my-emacs-d/elpa/django-manage-20160818.1912/django-manage.el

320 lines
12 KiB
EmacsLisp

;;; django-manage.el --- Django minor mode for commanding manage.py
;; Copyright (C) 2015 Daniel Gopar
;; Author: Daniel Gopar <gopardaniel@yahoo.com>
;; Package-Requires: ((hydra "0.13.2"))
;; Package-Version: 20160818.1912
;; Version: 0.1
;; Keywords: languages
;; This file is NOT part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Simple package to be to be able to control `manage.py', the standard
;; file that every Django project comes with. You are able to call any
;; command with `django-manage-command' plus it comes with code
;; completion so third party plugins will also be completed.
;;; Code:
(condition-case nil
(require 'python)
(error
(require 'python-mode)))
(require 'hydra)
(defcustom django-manage-shell-preference 'pyshell
"What shell to use."
:type 'symbol
:options '(eshell term pyshell)
:group 'shell)
(defcustom django-manage-server-ipaddr "127.0.0.1"
"What address Django will use when running the dev server."
:type 'string
:group 'server)
(defcustom django-manage-server-port "8000"
"What port Django will use when running the dev server."
:type 'string
:group 'server)
(defcustom django-manage-prompt-for-command nil
"When non-nil will ask for confirmation on command.
Will affect every function other than `django-manage-command'."
:type 'boolean
:group 'django-manage)
(defcustom django-manage-root ""
"The directory where 'manage.py' lives."
:type 'string
:group 'django-manage)
(make-local-variable 'django-manage-root)
(defun django-manage-root ()
"Return the root directory of Django project."
;; Check if projectile is in use, and if it is. Return root directory
(if (not (string= django-manage-root ""))
django-manage-root
(if (fboundp 'projectile-project-root)
(projectile-project-root)
;; Try looking for the directory holding 'manage.py'
(locate-dominating-file default-directory "manage.py"))))
(defun django-manage-python-command ()
"Return Python version to use with args."
(if (boundp 'python-shell-interpreter)
(concat python-shell-interpreter " " python-shell-interpreter-args)
;; For old python.el
(mapconcat 'identity (cons python-python-command python-python-command-args) " ")))
(defun django-manage-get-commands ()
"Return list of django commands."
(let ((help-output
(shell-command-to-string (concat python-shell-interpreter " "
(shell-quote-argument (django-manage-root)) "manage.py -h"))))
(setq dj-commands-str
(with-temp-buffer
(progn
(insert help-output)
(beginning-of-buffer)
(delete-region (point) (search-forward "Available subcommands:" nil nil nil))
;; cleanup [auth] and stuff
(beginning-of-buffer)
(save-excursion
(replace-regexp "\\[.*\\]" ""))
(buffer-string))))
;; get a list of commands from the output of manage.py -h
;; What would be the pattern to optimize this ?
(setq dj-commands-str (s-split "\n" dj-commands-str))
(setq dj-commands-str (-remove (lambda (x) (string= x "")) dj-commands-str))
(setq dj-commands-str (mapcar (lambda (x) (s-trim x)) dj-commands-str))
(sort dj-commands-str 'string-lessp)))
(defun django-manage-command (command &optional no-prompt)
"Allow to run any `manage.py' command.
Argument COMMAND command for django to run.
Optional Argument NO-PROMPT if non-nil will *not* ask if you wish to pass extra arguments."
;; nil nil: enable user to exit with any command. Still, he can not edit a completed choice.
(interactive (list (completing-read "Command: " (django-manage-get-commands) nil nil)))
(if (not no-prompt)
;; Now ask to edit the command. How to do the two actions at once ?
(setq command (read-shell-command "Run command like this: " command)))
(compile (concat (django-manage-python-command) " "
(shell-quote-argument (django-manage-root)) "manage.py " command)))
(defun django-manage-makemigrations (&optional app-name)
"Run \"makemigrations app-name\", will prompt for \"app-name\".
You can leave blank to simply run \"makemigrations\".
To choose arguments call `django-manage-command'.
Optional argument APP-NAME name of django app create migrations."
(interactive "sName: ")
(django-manage-command (concat "makemigrations " app-name)
(not django-manage-prompt-for-command)))
(defun django-manage-flush ()
"Run \"flush --noinput\". To choose arguments call `django-manage-command'."
(interactive)
(django-manage-command "flush --noinput"
(not django-manage-prompt-for-command)))
(defun django-manage-runserver ()
"Start the development server. To change what address and port to use,
customize `django-manage-server-ipaddr' and `django-manage-server-port'
If you want to pass arguments, then call `django-manage-command'"
(interactive)
(let ((parent-dir (file-name-base (substring (django-manage-root) 0 -1))))
(compile (concat (django-manage-python-command) " "
(shell-quote-argument (django-manage-root)) "manage.py runserver "
django-manage-server-ipaddr ":" django-manage-server-port))
(with-current-buffer "*compilation*"
(rename-buffer (format "*runserver[%s]*" parent-dir)))))
(defun django-manage-migrate ()
"Run \"migrate\". To choose arguments call `django-manage-command'."
(interactive)
(django-manage-command "migrate"
(not django-manage-prompt-for-command)))
(defun django-manage-assets-rebuild ()
"Run \"assets rebuild\". To choose arguments call `django-manage-command'."
(interactive)
(django-manage-command "assets rebuild"
(not django-manage-prompt-for-command)))
(defun django-manage-startapp (name)
"Run \"startapp name\". Will prompt for name of app.
To choose arguments call `django-manage-command'.
Argument NAME name of app to create."
(interactive "sName:")
(django-manage-command (concat "startapp " name)
(not django-manage-prompt-for-command)))
(defun django-manage-makemessages ()
"Run \"makemessages --all --symlinks\".
To pass arguments call `django-manage-command'."
(interactive)
(django-manage-command "makemessages --all --symlinks"
(not django-manage-prompt-for-command)))
(defun django-manage-compilemessages ()
"Run \"compilemessages\". To pass arguments call `django-manage-command'."
(interactive)
(django-manage-command "compilemessages"
(not django-manage-prompt-for-command)))
(defun django-manage-test (name)
"Run \"test name\". Will prompt for Django app name to test.
To pass arguments call `django-manage-command'.
Argument NAME name of django app to test."
(interactive "sTest app:")
(django-manage-command (concat "test " name)
(not django-manage-prompt-for-command)))
(defun django-manage--prep-shell (pref-shell)
"Prepare the shell with users preference.
Argument PREF-SHELL users shell of choice"
;; If a preexisting shell buffer exists return that one. If not create it
(let* ((parent-dir (file-name-base (substring (django-manage-root) 0 -1)))
(default-directory (django-manage-root))
(buffer-shell-name
(format (if (string= pref-shell "shell") "*Django Shell[%s]*" "*Django DBshell[%s]*") parent-dir)))
;; If it exists return it
(if (get-buffer buffer-shell-name)
(switch-to-buffer buffer-shell-name)
;; Shell didn't exist, so let's create it
(if (eq 'term django-manage-shell-preference)
(term (concat (django-manage-python-command) " "
(shell-quote-argument (django-manage-root)) "manage.py " pref-shell)))
(if (eq 'eshell django-manage-shell-preference)
(progn
(unless (get-buffer eshell-buffer-name)
(eshell))
(insert (concat (django-manage-python-command) " "
(shell-quote-argument (django-manage-root)) "manage.py " pref-shell))
(eshell-send-input)))
(if (eq 'pyshell django-manage-shell-preference)
(let ((setup-code "os.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"%s.settings\")")
(cmd ";from django.core.management import execute_from_command_line")
(exe (if (string= pref-shell "shell")
";import django;django.setup()"
(format ";execute_from_command_line(['manage.py', '%s'])" pref-shell))))
(run-python (python-shell-parse-command))
(python-shell-send-string (concat (format setup-code parent-dir) cmd exe))
(switch-to-buffer (python-shell-get-buffer))))
(rename-buffer buffer-shell-name))))
(defun django-manage-shell ()
"Start Python shell with Django already configured."
(interactive)
(django-manage--prep-shell "shell"))
(defun django-manage-dbshell ()
"Start Database shell."
(interactive)
(django-manage--prep-shell "dbshell"))
(defun django-manage-insert-transpy (from to &optional buffer)
"Wraps highlighted region in _(...) for i18n.
Argument FROM start point TO wrap.
Optional argument BUFFER end point to wrap."
;; From http://garage.pimentech.net/libcommonDjango_django_emacs/
;; Modified a little
(interactive "*r")
(save-excursion
(save-restriction
(narrow-to-region from to)
(goto-char from)
(iso-iso2sgml from to)
(insert "_(")
(goto-char (point-max))
(insert ")")
(point-max))))
(defhydra django-manage-hydra (:color blue
:hint nil)
"
Manage.py
--------------------------------------------------
_mm_: Enter manage.py commnand _r_: runserver _f_: Flush _t_: Run rest
_ma_: Makemigrations _sa_: Start new app _i_: Insert transpy
_mg_: Migrate _ss_: Run shell _a_: Rebuild Assets
_me_: Make messages _sd_: Run DB Shell _c_: Compile messages
_q_: Cancel
"
("mm" django-manage-command)
("ma" django-manage-makemigrations)
("mg" django-manage-migrate)
("me" django-manage-makemessages)
("r" django-manage-runserver "Start server")
("sa" django-manage-startapp)
("ss" django-manage-shell)
("sd" django-manage-dbshell)
("f" django-manage-flush)
("a" django-manage-assets-rebuild)
("c" django-manage-compilemessages)
("t" django-manage-test)
("i" django-manage-insert-transpy)
("q" nil "cancel"))
(defvar django-manage-map
(let ((map (make-keymap)))
(define-key map (kbd "C-c C-x") 'django-manage-hydra/body)
map))
(defun django-manage-setup ()
"Determine whether to start minor mode or not."
(when (and (stringp buffer-file-name)
(locate-dominating-file default-directory "manage.py"))
(django-manage)))
;;;###autoload
(define-minor-mode django-manage
"Minor mode for handling Django's manage.py"
:lighter " Manage"
:keymap django-manage-map)
(easy-menu-define django-manage-menu django-manage-map "Django menu"
'("Django"
["Start an app" django-manage-startapp t]
["Run tests" django-manage-test t]
["Make migrations" django-manage-makemigrations t]
["Flush database" django-manage-flush t]
["Runserver" django-manage-runserver t]
["Run database migrations" django-manage-migrate t]
["Rebuild assets" django-manage-assets-rebuild t]
["Make translations" django-manage-makemessages t]
["Compile translations" django-manage-compilemessages t]
["Open Python shell" django-manage-shell t]
["Open database shell" django-manage-dbshell t]
["Run other command" django-manage-command t]
"-"
["Insert translation mark" django-manage-insert-transpy t]))
(easy-menu-add django-manage-menu django-manage-map)
(provide 'django-manage)
;;; django-manage.el ends here