diff elpa/elpy-1.14.1/elpy.el @ 156:c745e2cc79ee

elpy: update along with direct deps
author Jordi GutiƩrrez Hermoso <jordigh@octave.org>
date Mon, 27 Feb 2017 12:17:38 -0500 (2017-02-27)
parents elpa/elpy-1.12.0/elpy.el@55ceabc58fcc
children
line wrap: on
line diff
copy from elpa/elpy-1.12.0/elpy.el
copy to elpa/elpy-1.14.1/elpy.el
--- a/elpa/elpy-1.12.0/elpy.el
+++ b/elpa/elpy-1.14.1/elpy.el
@@ -4,9 +4,9 @@
 
 ;; Author: Jorgen Schaefer <contact@jorgenschaefer.de>
 ;; URL: https://github.com/jorgenschaefer/elpy
-;; Version: 1.12.0
+;; Version: 1.14.1
 ;; Keywords: Python, IDE, Languages, Tools
-;; Package-Requires: ((company "0.8.2") (find-file-in-project "3.3")  (highlight-indentation "0.5.0") (pyvenv "1.3") (yasnippet "0.8.0"))
+;; Package-Requires: ((company "0.8.2") (find-file-in-project "3.3")  (highlight-indentation "0.5.0") (pyvenv "1.3") (yasnippet "0.8.0") (s "1.10.0"))
 
 ;; This program is free software; you can redistribute it and/or
 ;; modify it under the terms of the GNU General Public License
@@ -44,9 +44,10 @@
 (require 'python)
 
 (require 'elpy-refactor)
+(require 'elpy-django)
 (require 'pyvenv)
 
-(defconst elpy-version "1.12.0"
+(defconst elpy-version "1.14.1"
   "The version of the Elpy lisp code.")
 
 ;;;;;;;;;;;;;;;;;;;;;;
@@ -71,7 +72,8 @@
                           elpy-module-flymake
                           elpy-module-highlight-indentation
                           elpy-module-pyvenv
-                          elpy-module-yasnippet)
+                          elpy-module-yasnippet
+                          elpy-module-django)
   "Which Elpy modules to use.
 
 Elpy can use a number of modules for additional features, which
@@ -88,6 +90,8 @@
                      elpy-module-highlight-indentation)
               (const :tag "Expand code snippets (YASnippet)"
                      elpy-module-yasnippet)
+              (const :tag "Django configurations (Elpy-Django)"
+                     elpy-module-django)
               (const :tag "Configure some sane defaults for Emacs"
                      elpy-module-sane-defaults))
   :group 'elpy)
@@ -153,7 +157,7 @@
 you might want to keep it."
   :type 'boolean
   :group 'elpy)
-  
+
 (defcustom elpy-dedicated-shells nil
   "Non-nil if Elpy should use dedicated shells.
 
@@ -315,6 +319,18 @@
   :type '(repeat string)
   :group 'elpy)
 
+(defcustom elpy-test-django-runner-manage-command '("manage.py" "test"
+                                                    "--noinput")
+  "The command to use for `elpy-test-django-runner' in case we want to use manage.py."
+  :type '(repeat string)
+  :group 'elpy)
+
+(defcustom elpy-test-django-with-manage nil
+  "Set to nil, elpy will use `elpy-test-django-runner-command',
+set to t elpy will use `elpy-test-django-runner-manage-command' and set the project root accordingly."
+  :type 'boolean
+  :group 'elpy)
+
 (defcustom elpy-test-nose-runner-command '("nosetests")
   "The command to use for `elpy-test-nose-runner'."
   :type '(repeat string)
@@ -340,8 +356,15 @@
   :type 'boolean
   :group 'elpy)
 
+(defcustom elpy-syntax-check-command "flake8"
+  "The command to use for `elpy-check'."
+  :type 'string
+  :group 'elpy)
+
 ;;;;;;;;;;;;;
 ;;; Elpy Mode
+(defvar elpy--shell-last-py-buffer nil
+  "Help keep track of python buffer when changing to pyshell.")
 
 (defvar elpy-refactor-map
   (let ((map (make-sparse-keymap "Refactor")))
@@ -381,7 +404,10 @@
     (define-key map (kbd "C-c C-t") 'elpy-test)
     (define-key map (kbd "C-c C-v") 'elpy-check)
     (define-key map (kbd "C-c C-z") 'elpy-shell-switch-to-shell)
+    (define-key map (kbd "C-c C-k") 'elpy-shell-kill)
+    (define-key map (kbd "C-c C-K") 'elpy-shell-kill-all)
     (define-key map (kbd "C-c C-r") elpy-refactor-map)
+    (define-key map (kbd "C-c C-x") elpy-django-mode-map)
 
     (define-key map (kbd "<S-return>") 'elpy-open-and-indent-line-below)
     (define-key map (kbd "<C-S-return>") 'elpy-open-and-indent-line-above)
@@ -432,7 +458,11 @@
                "Send Buffer to Python")
       :help "Send the current region or the whole buffer to Python"]
      ["Send Definition" python-shell-send-defun
-      :help "Send current definition to Python"])
+      :help "Send current definition to Python"]
+     ["Kill Python shell" elpy-shell-kill
+      :help "Kill the current Python shell"]
+     ["Kill all Python shells" elpy-shell-kill-all
+      :help "Kill all Python shells"])
     ("Project"
      ["Find File" elpy-find-file
       :help "Interactively find a file in the current project"]
@@ -539,6 +569,7 @@
     ("Snippets (YASnippet)" yasnippet "yas-")
     ("Directory Grep (rgrep)" grep "grep-")
     ("Search as You Type (ido)" ido "ido-")
+    ("Django Extension" elpy-django "elpy-django-")
     ;; ffip does not use defcustom
     ;; highlight-indent does not use defcustom, either. Its sole face
     ;; is defined in basic-faces.
@@ -811,7 +842,8 @@
                     (not (gethash "rope_version" config))
                     (not (gethash "jedi_version" config))))
       (elpy-insert--para
-       "There is no backend available. Please install either Rope or Jedi.\n")
+       "There is no backend available. Please install either Rope or Jedi."
+       "See https://github.com/jorgenschaefer/elpy/wiki/FAQ#q-should-i-use-rope-or-jedi for guidance.\n")
       (insert "\n")
       (widget-create 'elpy-insert--pip-button :package rope-pypi-package)
       (insert "\n")
@@ -898,8 +930,8 @@
                      :package "yapf" :upgrade t)
       (insert "\n\n"))
 
-    ;; flake8, the default syntax checker, not found
-    (when (not (executable-find python-check-command))
+    ;; Syntax checker not available
+    (when (not (executable-find elpy-syntax-check-command))
       (elpy-insert--para
        "The configured syntax checker could not be found. Elpy uses this "
        "program to provide syntax checks of your programs, so you might "
@@ -1049,14 +1081,14 @@
                                                   yapf-latest))
             ("Syntax checker" . ,(let ((syntax-checker
                                         (executable-find
-                                         python-check-command)))
+                                         elpy-syntax-check-command)))
                                    (if  syntax-checker
                                        (format "%s (%s)"
                                                (file-name-nondirectory
                                                 syntax-checker)
                                                syntax-checker)
                                      (format "Not found (%s)"
-                                             python-check-command))))))
+                                             elpy-syntax-check-command))))))
     (setq maxwidth 0)
     (dolist (row table)
       (when (> (length (car row))
@@ -1601,30 +1633,83 @@
 (defun elpy-shell-switch-to-shell ()
   "Switch to inferior Python process buffer."
   (interactive)
+  (setq elpy--shell-last-py-buffer (buffer-name))
   (pop-to-buffer (process-buffer (elpy-shell-get-or-create-process))))
 
 (defun elpy-shell-switch-to-buffer ()
   "Switch from inferior Python process buffer to recent Python buffer."
   (interactive)
-  (pop-to-buffer (other-buffer (current-buffer) 1)))
+  (pop-to-buffer elpy--shell-last-py-buffer))
+
+(defun elpy-shell-kill (&optional kill-buff)
+  "Kill the current python shell.
+
+If `elpy-dedicated-shells' is non-nil,
+kill the current buffer dedicated shell.
+
+If KILL-BUFF is non-nil, also kill the associated buffer."
+  (interactive)
+  (let ((shell-buffer (python-shell-get-buffer)))
+    (cond
+     (shell-buffer
+      (delete-process shell-buffer)
+      (when kill-buff
+	(kill-buffer shell-buffer))
+      (message "Killed %s shell" shell-buffer))
+     (t
+      (message "No python shell to kill")))))
+
+(defun elpy-shell-kill-all (&optional kill-buffers ask-for-each-one)
+  "Kill all active python shells.
+
+If KILL-BUFFERS is non-nil, also kill the associated buffers.
+If ASK-FOR-EACH-ONE is non-nil, ask before killing each python process.
+"
+  (interactive)
+  (let ((python-buffer-list ()))
+    ;; Get active python shell buffers and kill inactive ones (if asked)
+    (loop for buffer being the buffers do
+	  (when (string-match "^\*Python\\\[.*\\]\*$" (buffer-name buffer))
+	    (if (get-buffer-process buffer)
+		(push buffer python-buffer-list)
+	      (when kill-buffers
+		(kill-buffer buffer)))))
+    (cond
+     ;; Ask for each buffers and kill
+     ((and python-buffer-list ask-for-each-one)
+      (loop for buffer in python-buffer-list do
+	    (when (y-or-n-p (format "Kill %s ?" buffer))
+		(delete-process buffer)
+		(when kill-buffers
+		  (kill-buffer buffer)))))
+     ;; Ask and kill every buffers
+     (python-buffer-list
+      (if (y-or-n-p (format "Kill %s python shells ?" (length python-buffer-list)))
+	  (loop for buffer in python-buffer-list do
+		(delete-process buffer)
+		(when kill-buffers
+		  (kill-buffer buffer)))))
+     ;; No shell to close
+     (t
+      (message "No python shell to close")))))
 
 (defun elpy-shell-get-or-create-process ()
   "Get or create an inferior Python process for current buffer and return it."
   (let* ((bufname (format "*%s*" (python-shell-get-process-name nil)))
-	 (dedbufname (format "*%s*" (python-shell-get-process-name t)))
-	 (proc (get-buffer-process bufname))
-	 (dedproc (get-buffer-process dedbufname)))
+         (dedbufname (format "*%s*" (python-shell-get-process-name t)))
+         (proc (get-buffer-process bufname))
+         (dedproc (get-buffer-process dedbufname)))
     (if elpy-dedicated-shells
-	(if dedproc
-	    dedproc
-	  (run-python (python-shell-parse-command) t)
-	  (get-buffer-process dedbufname))
+        (if dedproc
+            dedproc
+          (run-python (python-shell-parse-command) t)
+          (get-buffer-process dedbufname))
       (if dedproc
-	  dedproc
-	(if proc
-	    proc
-	  (run-python (python-shell-parse-command))
-	  (get-buffer-process bufname))))))
+          dedproc
+        (if proc
+            proc
+          (run-python (python-shell-parse-command))
+          (get-buffer-process bufname))))))
 
 (defun elpy-shell--region-without-indentation (beg end)
   "Return the current region as a string, but without indentation."
@@ -1672,10 +1757,13 @@
                                          (buffer-file-name))
                                    (buffer-file-name))))
         (extra-args (if whole-project-p
-                        (concat " --exclude="
-                                (mapconcat #'identity
-                                           elpy-project-ignored-directories
-                                           ","))
+                        (concat
+                         (if (equal python-check-command "pylint")
+                             " --ignore="
+                           " --exclude=")
+                         (mapconcat #'identity
+                                    elpy-project-ignored-directories
+                                    ","))
                       "")))
     (compilation-start (concat python-check-command
                                " "
@@ -2041,17 +2129,28 @@
 (put 'elpy-test-discover-runner 'elpy-test-runner-p t)
 
 (defun elpy-test-django-runner (top file module test)
-  "Test the project using the Django discover runner.
+  "Test the project using the Django discover runner,
+or with manage.py if elpy-test-django-with-manage is true.
 
 This requires Django 1.6 or the django-discover-runner package."
   (interactive (elpy-test-at-point))
   (if module
       (apply #'elpy-test-run
              top
-             (append elpy-test-django-runner-command
-                     (list (if test
-                               (format "%s.%s" module test)
-                             module))))
+             (append
+              ;; if we want to use manage.py, get the root directory where it is.
+              (if elpy-test-django-with-manage
+                  (append (list (concat (expand-file-name
+                                         (locate-dominating-file
+                                          (elpy-project-root)
+                                          (car elpy-test-django-runner-manage-command)))
+                                        (car elpy-test-django-runner-manage-command)))
+                          (cdr elpy-test-django-runner-manage-command))
+                ;; or the default:
+                elpy-test-django-runner-command)
+              (list (if test
+                        (format "%s.%s" module test)
+                      module))))
     (apply #'elpy-test-run
            top
            elpy-test-django-runner-command)))
@@ -2204,7 +2303,7 @@
 ;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Import manipulation
 
-(defun elpy-importmagic--add-import-read-args (&optional object prompt)
+(defun elpy-importmagic--add-import-read-args (&optional object prompt ask-for-alias)
   (let* ((default-object (save-excursion
                            (let ((bounds (with-syntax-table python-dotty-syntax-table
                                            (bounds-of-thing-at-point 'symbol))))
@@ -2217,24 +2316,29 @@
     (cond
      ;; An elpy warning (i.e. index not ready) is returned as a string.
      ((stringp possible-imports)
-      (list ""))
+      "")
      ;; If there is no candidate, we exit immediately.
      ((null possible-imports)
       (message "No import candidate found")
-      (list ""))
+      "")
      ;; We have some candidates, let the user choose one.
      (t
-      (let ((first-choice (car possible-imports))
-            (user-choice (completing-read statement-prompt possible-imports)))
-        (list (if (equal user-choice "") first-choice user-choice)))))))
-
-(defun elpy-importmagic-add-import (statement)
-  "Prompt to import thing at point, show possbile imports and add selected import."
-  (interactive (elpy-importmagic--add-import-read-args))
-  (unless (equal statement "")
-    (let* ((res (elpy-rpc "add_import" (list buffer-file-name
-                                             (elpy-rpc--buffer-contents)
-                                             statement))))
+      (let* ((first-choice (car possible-imports))
+	     (user-choice (completing-read statement-prompt possible-imports))
+	     (alias (if ask-for-alias (read-string (format "Import \"%s\" as: " object-to-import)) "")))
+	(concat (if (equal user-choice "") first-choice user-choice)
+		(if (not (or (equal alias "") (equal alias object-to-import))) (concat " as " alias))))))))
+
+(defun elpy-importmagic-add-import (&optional statement ask-for-alias)
+  "Prompt to import thing at point, show possible imports and add selected import.
+
+With prefix arg, also ask for an import alias."
+  (interactive "i\nP")
+  (let* ((statement (or statement (elpy-importmagic--add-import-read-args nil nil ask-for-alias)))
+	 (res (elpy-rpc "add_import" (list buffer-file-name
+					   (elpy-rpc--buffer-contents)
+					   statement))))
+    (unless (equal statement "")
       (elpy-buffer--replace-block res))))
 
 (defun elpy-importmagic-fixup ()
@@ -2244,13 +2348,24 @@
   (interactive)
   ;; get all unresolved names, and interactively add imports for them
   (let* ((res (elpy-rpc "get_unresolved_symbols" (list buffer-file-name
-                                                       (elpy-rpc--buffer-contents)))))
+						       (elpy-rpc--buffer-contents))))
+	 (unresolved-aliases (list)))
     (unless (stringp res)
       (if (null res) (message "No imports to add."))
       (dolist (object res)
         (let* ((prompt (format "How to import \"%s\": " object))
-               (choice (elpy-importmagic--add-import-read-args object prompt)))
-          (elpy-importmagic-add-import (car choice))))))
+               (choice (elpy-importmagic--add-import-read-args object prompt nil)))
+	  (when (equal choice "")
+	    (add-to-list 'unresolved-aliases (car (split-string object "\\."))))
+	  (elpy-importmagic-add-import choice nil))))
+    ;; ask for unresolved aliases real names and add import for them
+    (dolist (alias unresolved-aliases)
+      (let* ((object (read-string (format "Real name of \"%s\" alias: " alias nil)))
+	     (prompt (format "How to import \"%s\": " object))
+	     (choice (concat
+		      (elpy-importmagic--add-import-read-args object prompt nil)
+		      (concat " as " alias))))
+	(elpy-importmagic-add-import choice nil))))
   ;; now get a new import statement block (this also sorts)
   (let* ((res (elpy-rpc "remove_unreferenced_imports" (list buffer-file-name
                                                             (elpy-rpc--buffer-contents)))))
@@ -2288,14 +2403,18 @@
     (if (use-region-p)
         (let ((new-block (elpy-rpc method (list (elpy-rpc--region-contents))))
               (beg (region-beginning)) (end (region-end)))
-          (elpy-buffer--replace-region beg end new-block))
+          (elpy-buffer--replace-region
+           beg end
+           (replace-regexp-in-string "\n$" "" new-block))
+          (goto-char end)
+          (deactivate-mark))
       ;; Vector instead of list, json.el in Emacs 24.3 and before
       ;; breaks for single-element lists of alists.
       (let ((new-block (elpy-rpc method (vector (elpy-rpc--buffer-contents))))
             (beg (point-min)) (end (point-max)))
-        (elpy-buffer--replace-region beg end new-block)))
-    (forward-line (1- line))
-    (forward-char col)))
+        (elpy-buffer--replace-region beg end new-block)
+        (forward-line (1- line))
+        (forward-char col)))))
 
 ;;;;;;;;;;;;;;
 ;;; Multi-Edit
@@ -3218,7 +3337,7 @@
   "Remove the lighter for MODE-NAME.
 
 It should not be necessary to see (Python Elpy yas company ElDoc) all the
-time. 
+time.
 
 If you need your modeline, you can set the variable `elpy-remove-modeline-lighter' to nil
 "
@@ -3571,10 +3690,7 @@
      (require 'flymake)
      (elpy-modules-remove-modeline-lighter 'flymake-mode)
      ;; Flymake support using flake8, including warning faces.
-     (when (and (executable-find "flake8")
-                (equal python-check-command
-                       (elpy-flymake--standard-value 'python-check-command)))
-       (setq python-check-command "flake8"))
+     (setq python-check-command elpy-syntax-check-command)
 
      ;; Add our initializer function
      (add-to-list 'flymake-allowed-file-name-masks
@@ -3655,13 +3771,6 @@
                    err-info
                    ", ")))))
 
-(defun elpy-flymake--standard-value (var)
-  "Return the standard value of the given variable."
-  (let ((sv (get var 'standard-value)))
-    (when (consp sv)
-      (ignore-errors
-        (eval (car sv))))))
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Module: Highlight Indentation
 
@@ -3720,6 +3829,17 @@
      (yas-minor-mode -1))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Module: Elpy-Django
+
+(defun elpy-module-django (command &rest args)
+  "Module to provide Django support."
+  (pcase command
+    (`buffer-init
+     (elpy-django-setup))
+    (`buffer-stop
+     (elpy-django -1))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Backwards compatibility
 
 ;; Functions for Emacs 24 before 24.3