1 ;;; elpy.el --- Emacs Python Development Environment -*- lexical-binding: t -*-
3 ;; Copyright (C) 2012-2019 Jorgen Schaefer
5 ;; Author: Jorgen Schaefer <contact@jorgenschaefer.de>, Gaby Launay <gaby.launay@protonmail.com>
6 ;; URL: https://github.com/jorgenschaefer/elpy
8 ;; Keywords: Python, IDE, Languages, Tools
9 ;; Package-Requires: ((company "0.9.10") (emacs "24.4") (highlight-indentation "0.7.0") (pyvenv "1.20") (yasnippet "0.13.0") (s "1.12.0"))
11 ;; This program is free software; you can redistribute it and/or
12 ;; modify it under the terms of the GNU General Public License
13 ;; as published by the Free Software Foundation; either version 3
14 ;; of the License, or (at your option) any later version.
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
26 ;; The Emacs Lisp Python Environment in Emacs
28 ;; Elpy is an Emacs package to bring powerful Python editing to Emacs.
29 ;; It combines a number of existing Emacs packages, both written in
30 ;; Emacs Lisp as well as Python.
32 ;; For more information, read the Elpy manual:
34 ;; https://elpy.readthedocs.io/en/latest/index.html
48 (require 'cl-lib) ; for `cl-every', `cl-copy-list', `cl-delete-if-not'
50 (require 'elpy-refactor)
51 (require 'elpy-django)
52 (require 'elpy-profile)
57 (defconst elpy-version "1.35.0"
58 "The version of the Elpy Lisp code.")
60 ;;;;;;;;;;;;;;;;;;;;;;
61 ;;; User customization
64 "The Emacs Lisp Python Environment."
68 (defcustom elpy-mode-hook nil
69 "Hook run when `elpy-mode' is enabled.
71 This can be used to enable minor modes for Python development."
73 :options '(subword-mode hl-line-mode)
76 (defcustom elpy-modules '(elpy-module-sane-defaults
80 elpy-module-highlight-indentation
84 "Which Elpy modules to use.
86 Elpy can use a number of modules for additional features, which
87 can be individually enabled or disabled."
88 :type '(set (const :tag "Inline code completion (company-mode)"
90 (const :tag "Show function signatures (ElDoc)"
92 (const :tag "Highlight syntax errors (Flymake)"
94 (const :tag "Code folding"
96 (const :tag "Show the virtualenv in the mode line (pyvenv)"
98 (const :tag "Display indentation markers (highlight-indentation)"
99 elpy-module-highlight-indentation)
100 (const :tag "Expand code snippets (YASnippet)"
101 elpy-module-yasnippet)
102 (const :tag "Django configurations (Elpy-Django)"
104 (const :tag "Automatically update documentation (Autodoc)."
106 (const :tag "Configure some sane defaults for Emacs"
107 elpy-module-sane-defaults))
110 (defcustom elpy-project-ignored-directories
111 '(".tox" "build" "dist" ".cask" ".ipynb_checkpoints")
112 "Directories ignored by functions working on the whole project.
113 This is in addition to `vc-directory-exclusion-list'
114 and `grep-find-ignored-directories', as appropriate."
115 :type '(repeat string)
117 (cl-every #'stringp val))
120 (defun elpy-project-ignored-directories ()
121 "Compute the list of ignored directories for project.
123 `elpy-project-ignored-directories'
124 `vc-directory-exclusion-list'
125 `grep-find-ignored-directories'"
127 (append elpy-project-ignored-directories
128 vc-directory-exclusion-list
129 (if (fboundp 'rgrep-find-ignored-directories)
130 (rgrep-find-ignored-directories (elpy-project-root))
131 (cl-delete-if-not #'stringp (cl-copy-list
132 grep-find-ignored-directories))))))
134 (defcustom elpy-project-root nil
135 "The root of the project the current buffer is in.
137 There is normally no use in setting this variable directly, as
138 Elpy tries to detect the project root automatically. See
139 `elpy-project-root-finder-functions' for a way of influencing
142 Setting this variable globally will override Elpy's automatic
143 project detection facilities entirely.
145 Alternatively, you can set this in file- or directory-local
146 variables using \\[add-file-local-variable] or
147 \\[add-dir-local-variable].
149 Do not use this variable in Emacs Lisp programs. Instead, call
150 the `elpy-project-root' function. It will do the right thing."
152 :safe 'file-directory-p
154 (make-variable-buffer-local 'elpy-project-root)
156 (defcustom elpy-project-root-finder-functions
157 '(elpy-project-find-projectile-root
158 elpy-project-find-python-root
159 elpy-project-find-git-root
160 elpy-project-find-hg-root
161 elpy-project-find-svn-root)
162 "List of functions to ask for the current project root.
164 These will be checked in turn. The first directory found is used."
165 :type '(set (const :tag "Projectile project root"
166 elpy-project-find-projectile-root)
167 (const :tag "Python project (setup.py, setup.cfg)"
168 elpy-project-find-python-root)
169 (const :tag "Git repository root (.git)"
170 elpy-project-find-git-root)
171 (const :tag "Mercurial project root (.hg)"
172 elpy-project-find-hg-root)
173 (const :tag "Subversion project root (.svn)"
174 elpy-project-find-svn-root)
175 (const :tag "Django project root (manage.py, django-admin.py)"
176 elpy-project-find-django-root))
179 (make-obsolete-variable 'elpy-company-hide-modeline
180 'elpy-remove-modeline-lighter
182 (defcustom elpy-remove-modeline-lighter t
183 "Non-nil if Elpy should remove most mode line display.
185 Modeline shows many minor modes currently active. For Elpy, this is mostly
186 uninteresting information, but if you rely on your modeline in other modes,
187 you might want to keep it."
191 (defcustom elpy-company-post-completion-function 'ignore
192 "Your preferred Company post completion function.
194 Elpy can automatically insert parentheses after completing
197 The heuristic on when to insert these parentheses can easily be
198 wrong, though, so this is disabled by default. Set this variable
199 to the function `elpy-company-post-complete-parens' to enable
201 :type '(choice (const :tag "Ignore post complete" ignore)
202 (const :tag "Complete callables with parens"
203 elpy-company-post-complete-parens)
204 (function :tag "Other function"))
207 (defcustom elpy-get-info-from-shell nil
208 "If t, use the shell to gather docstrings and completions.
210 Normally elpy provides completion and documentation using static code analysis (from jedi). With this option set to t, elpy will add the completion candidates and the docstrings from the associated python shell; This activates fallback completion candidates for cases when static code analysis fails."
214 (defcustom elpy-get-info-from-shell-timeout 1
215 "Timeout (in seconds) for gathering information from the shell."
219 (defcustom elpy-eldoc-show-current-function t
220 "If true, show the current function if no calltip is available.
222 When Elpy can not find the calltip of the function call at point,
223 it can show the name of the function or class and method being
224 edited instead. Setting this variable to nil disables this feature."
228 (defcustom elpy-test-runner 'elpy-test-discover-runner
229 "The test runner to use to run tests."
230 :type '(choice (const :tag "Unittest Discover" elpy-test-discover-runner)
231 (const :tag "Green" elpy-test-green-runner)
232 (const :tag "Django Discover" elpy-test-django-runner)
233 (const :tag "Nose" elpy-test-nose-runner)
234 (const :tag "py.test" elpy-test-pytest-runner)
235 (const :tag "Twisted Trial" elpy-test-trial-runner))
236 :safe 'elpy-test-runner-p
239 (defcustom elpy-test-discover-runner-command '("python-shell-interpreter" "-m" "unittest")
240 "The command to use for `elpy-test-discover-runner'.
241 If the string \"python-shell-interpreter\" is present, it will be replaced with
242 the value of `python-shell-interpreter'."
243 :type '(repeat string)
246 (defcustom elpy-test-green-runner-command '("green")
247 "The command to use for `elpy-test-green-runner'."
248 :type '(repeat string)
251 (defcustom elpy-test-nose-runner-command '("nosetests")
252 "The command to use for `elpy-test-nose-runner'."
253 :type '(repeat string)
256 (defcustom elpy-test-trial-runner-command '("trial")
257 "The command to use for `elpy-test-trial-runner'."
258 :type '(repeat string)
261 (defcustom elpy-test-pytest-runner-command '("py.test")
262 "The command to use for `elpy-test-pytest-runner'."
263 :type '(repeat string)
266 (defcustom elpy-test-compilation-function 'compile
267 "Function used by `elpy-test-run' to run a test command.
269 The function should behave similarly to `compile'. Another good
274 (defcustom elpy-rgrep-file-pattern "*.py"
275 "FILES to use for `elpy-rgrep-symbol'."
279 (defcustom elpy-disable-backend-error-display t
280 "Non-nil if Elpy should disable backend error display."
285 (defcustom elpy-formatter nil
286 "Auto formatter used by `elpy-format-code'.
288 if nil, use the first formatter found amongst
289 `yapf' , `autopep8' and `black'."
290 :type '(choice (const :tag "First one found" nil)
291 (const :tag "Yapf" yapf)
292 (const :tag "autopep8" autopep8)
293 (const :tag "Black" black))
296 (defcustom elpy-syntax-check-command "flake8"
297 "The command to use for `elpy-check'."
303 (defvar elpy-refactor-map
304 (let ((map (make-sparse-keymap "Refactor")))
305 (define-key map (kbd "i") (cons (format "%snline"
306 (propertize "i" 'face 'bold))
307 'elpy-refactor-inline))
308 (define-key map (kbd "f") (cons (format "%sunction extraction"
309 (propertize "f" 'face 'bold))
310 'elpy-refactor-extract-function))
311 (define-key map (kbd "v") (cons (format "%sariable extraction"
312 (propertize "v" 'face 'bold))
313 'elpy-refactor-extract-variable))
314 (define-key map (kbd "r") (cons (format "%sename"
315 (propertize "r" 'face 'bold))
316 'elpy-refactor-rename))
318 "Key map for the refactor command.")
320 (defvar elpy-mode-map
321 (let ((map (make-sparse-keymap)))
322 ;; Alphabetical order to make it easier to find free C-c C-X
323 ;; bindings in the future. Heh.
325 ;; (define-key map (kbd "<backspace>") 'python-indent-dedent-line-backspace)
326 ;; (define-key map (kbd "<backtab>") 'python-indent-dedent-line)
328 ;; (define-key map (kbd "C-M-x") 'python-shell-send-defun)
329 ;; (define-key map (kbd "C-c <") 'python-indent-shift-left)
330 ;; (define-key map (kbd "C-c >") 'python-indent-shift-right)
331 (define-key map (kbd "C-c RET") 'elpy-importmagic-add-import)
332 (define-key map (kbd "C-c C-b") 'elpy-nav-expand-to-indentation)
333 (define-key map (kbd "C-c C-c") 'elpy-shell-send-region-or-buffer)
334 (define-key map (kbd "C-c C-d") 'elpy-doc)
335 (define-key map (kbd "C-c C-e") 'elpy-multiedit-python-symbol-at-point)
336 (define-key map (kbd "C-c C-f") 'elpy-find-file)
337 (define-key map (kbd "C-c C-n") 'elpy-flymake-next-error)
338 (define-key map (kbd "C-c C-o") 'elpy-occur-definitions)
339 (define-key map (kbd "C-c C-p") 'elpy-flymake-previous-error)
340 (define-key map (kbd "C-c @ C-c") 'elpy-folding-toggle-at-point)
341 (define-key map (kbd "C-c @ C-b") 'elpy-folding-toggle-docstrings)
342 (define-key map (kbd "C-c @ C-m") 'elpy-folding-toggle-comments)
343 (define-key map (kbd "C-c @ C-f") 'elpy-folding-hide-leafs)
344 (define-key map (kbd "C-c C-s") 'elpy-rgrep-symbol)
345 (define-key map (kbd "C-c C-t") 'elpy-test)
346 (define-key map (kbd "C-c C-v") 'elpy-check)
347 (define-key map (kbd "C-c C-z") 'elpy-shell-switch-to-shell)
348 (define-key map (kbd "C-c C-k") 'elpy-shell-kill)
349 (define-key map (kbd "C-c C-K") 'elpy-shell-kill-all)
350 (define-key map (kbd "C-c C-r") elpy-refactor-map)
351 (define-key map (kbd "C-c C-x") elpy-django-mode-map)
353 (define-key map (kbd "<S-return>") 'elpy-open-and-indent-line-below)
354 (define-key map (kbd "<C-S-return>") 'elpy-open-and-indent-line-above)
356 (define-key map (kbd "<C-return>") 'elpy-shell-send-statement-and-step)
358 (define-key map (kbd "<C-down>") 'elpy-nav-forward-block)
359 (define-key map (kbd "<C-up>") 'elpy-nav-backward-block)
360 (define-key map (kbd "<C-left>") 'elpy-nav-backward-indent)
361 (define-key map (kbd "<C-right>") 'elpy-nav-forward-indent)
363 (define-key map (kbd "<M-down>") 'elpy-nav-move-line-or-region-down)
364 (define-key map (kbd "<M-up>") 'elpy-nav-move-line-or-region-up)
365 (define-key map (kbd "<M-left>") 'elpy-nav-indent-shift-left)
366 (define-key map (kbd "<M-right>") 'elpy-nav-indent-shift-right)
368 (unless (fboundp 'xref-find-definitions)
369 (define-key map (kbd "M-.") 'elpy-goto-definition))
370 (if (not (fboundp 'xref-find-definitions-other-window))
371 (define-key map (kbd "C-x 4 M-.") 'elpy-goto-definition-other-window)
372 (define-key map (kbd "C-x 4 M-.") 'xref-find-definitions-other-window))
373 (when (fboundp 'xref-pop-marker-stack)
374 (define-key map (kbd "M-*") 'xref-pop-marker-stack))
376 (define-key map (kbd "M-TAB") 'elpy-company-backend)
379 "Key map for the Emacs Lisp Python Environment.")
381 (defvar elpy-shell-map
382 (let ((map (make-sparse-keymap)))
383 (define-key map (kbd "e") 'elpy-shell-send-statement)
384 (define-key map (kbd "E") 'elpy-shell-send-statement-and-go)
385 (define-key map (kbd "s") 'elpy-shell-send-top-statement)
386 (define-key map (kbd "S") 'elpy-shell-send-top-statement-and-go)
387 (define-key map (kbd "f") 'elpy-shell-send-defun)
388 (define-key map (kbd "F") 'elpy-shell-send-defun-and-go)
389 (define-key map (kbd "c") 'elpy-shell-send-defclass)
390 (define-key map (kbd "C") 'elpy-shell-send-defclass-and-go)
391 (define-key map (kbd "o") 'elpy-shell-send-group)
392 (define-key map (kbd "O") 'elpy-shell-send-group-and-go)
393 (define-key map (kbd "w") 'elpy-shell-send-codecell)
394 (define-key map (kbd "W") 'elpy-shell-send-codecell-and-go)
395 (define-key map (kbd "r") 'elpy-shell-send-region-or-buffer)
396 (define-key map (kbd "R") 'elpy-shell-send-region-or-buffer-and-go)
397 (define-key map (kbd "b") 'elpy-shell-send-buffer)
398 (define-key map (kbd "B") 'elpy-shell-send-buffer-and-go)
399 (define-key map (kbd "C-e") 'elpy-shell-send-statement-and-step)
400 (define-key map (kbd "C-S-E") 'elpy-shell-send-statement-and-step-and-go)
401 (define-key map (kbd "C-s") 'elpy-shell-send-top-statement-and-step)
402 (define-key map (kbd "C-S-S") 'elpy-shell-send-top-statement-and-step-and-go)
403 (define-key map (kbd "C-f") 'elpy-shell-send-defun-and-step)
404 (define-key map (kbd "C-S-F") 'elpy-shell-send-defun-and-step-and-go)
405 (define-key map (kbd "C-c") 'elpy-shell-send-defclass-and-step)
406 (define-key map (kbd "C-S-C") 'elpy-shell-send-defclass-and-step-and-go)
407 (define-key map (kbd "C-o") 'elpy-shell-send-group-and-step)
408 (define-key map (kbd "C-S-O") 'elpy-shell-send-group-and-step-and-go)
409 (define-key map (kbd "C-w") 'elpy-shell-send-codecell-and-step)
410 (define-key map (kbd "C-S-W") 'elpy-shell-send-codecell-and-step-and-go)
411 (define-key map (kbd "C-r") 'elpy-shell-send-region-or-buffer-and-step)
412 (define-key map (kbd "C-S-R") 'elpy-shell-send-region-or-buffer-and-step-and-go)
413 (define-key map (kbd "C-b") 'elpy-shell-send-buffer-and-step)
414 (define-key map (kbd "C-S-B") 'elpy-shell-send-buffer-and-step-and-go)
416 "Key map for the shell related commands.")
417 (fset 'elpy-shell-map elpy-shell-map)
419 (defcustom elpy-shell-command-prefix-key "C-c C-y"
420 "Prefix key used to call elpy shell related commands.
422 This option need to bet set through `customize' or `customize-set-variable' to be taken into account."
427 (when (and (boundp var) (symbol-value var))
428 (define-key elpy-mode-map (kbd (symbol-value var)) nil))
430 (define-key elpy-mode-map (kbd key) 'elpy-shell-map)
434 (let ((map (make-sparse-keymap)))
435 (define-key map (kbd "d") 'elpy-pdb-debug-buffer)
436 (define-key map (kbd "p") 'elpy-pdb-break-at-point)
437 (define-key map (kbd "e") 'elpy-pdb-debug-last-exception)
438 (define-key map (kbd "b") 'elpy-pdb-toggle-breakpoint-at-point)
440 "Key map for the shell related commands.")
441 (fset 'elpy-pdb-map elpy-pdb-map)
442 (define-key elpy-mode-map (kbd "C-c C-u") 'elpy-pdb-map)
444 (easy-menu-define elpy-menu elpy-mode-map
447 ["Documentation" elpy-doc
448 :help "Get documentation for symbol at point"]
449 ["Run Tests" elpy-test
450 :help "Run test at point, or all tests in the project"]
451 ["Go to Definition" elpy-goto-definition
452 :help "Go to the definition of the symbol at point"]
453 ["Go to previous definition" pop-tag-mark
454 :active (if (version< emacs-version "25.1")
455 (not (ring-empty-p find-tag-marker-ring))
456 (> xref-marker-ring-length 0))
457 :help "Return to the position"]
458 ["Complete" elpy-company-backend
460 :help "Complete at point"]
461 ["Refactor" elpy-refactor
462 :help "Refactor options"]
464 ("Interactive Python"
465 ["Switch to Python Shell" elpy-shell-switch-to-shell
466 :help "Start and switch to the interactive Python"]
467 ["Send Region or Buffer" elpy-shell-send-region-or-buffer
468 :label (if (use-region-p)
469 "Send Region to Python"
470 "Send Buffer to Python")
471 :help "Send the current region or the whole buffer to Python"]
472 ["Send Definition" elpy-shell-send-defun
473 :help "Send current definition to Python"]
474 ["Kill Python shell" elpy-shell-kill
475 :help "Kill the current Python shell"]
476 ["Kill all Python shells" elpy-shell-kill-all
477 :help "Kill all Python shells"])
479 ["Debug buffer" elpy-pdb-debug-buffer
480 :help "Debug the current buffer using pdb"]
481 ["Debug at point" elpy-pdb-break-at-point
482 :help "Debug the current buffer and stop at the current position"]
483 ["Debug last exception" elpy-pdb-debug-last-exception
484 :help "Run post-mortem pdb on the last exception"]
485 ["Add/remove breakpoint" elpy-pdb-toggle-breakpoint-at-point
486 :help "Add or remove a breakpoint at the current position"])
488 ["Find File" elpy-find-file
489 :help "Interactively find a file in the current project"]
490 ["Find Symbol" elpy-rgrep-symbol
491 :help "Find occurrences of a symbol in the current project"]
492 ["Set Project Root" elpy-set-project-root
493 :help "Change the current project root"]
494 ["Set Project Variable" elpy-set-project-variable
495 :help "Configure a project-specific option"])
497 ["Check Syntax" elpy-check
498 :help "Check the syntax of the current file"]
499 ["Next Error" elpy-flymake-next-error
500 :help "Go to the next inline error, if any"]
501 ["Previous Error" elpy-flymake-previous-error
502 :help "Go to the previous inline error, if any"])
504 ["Hide/show at point" elpy-folding-toggle-at-point
505 :help "Hide or show the block or docstring at point"]
506 ["Hide/show all docstrings" elpy-folding-toggle-docstrings
507 :help "Hide or show all the docstrings"]
508 ["Hide/show all comments" elpy-folding-toggle-comments
509 :help "Hide or show all the comments"]
510 ["Hide leafs" elpy-folding-hide-leafs
511 :help "Hide all leaf blocks (blocks not containing other blocks)"])
512 ("Indentation Blocks"
513 ["Dedent" python-indent-shift-left
514 :help "Dedent current block or region"
515 :suffix (if (use-region-p) "Region" "Block")]
516 ["Indent" python-indent-shift-right
517 :help "Indent current block or region"
518 :suffix (if (use-region-p) "Region" "Block")]
519 ["Up" elpy-nav-move-line-or-region-up
520 :help "Move current block or region up"
521 :suffix (if (use-region-p) "Region" "Block")]
522 ["Down" elpy-nav-move-line-or-region-down
523 :help "Move current block or region down"
524 :suffix (if (use-region-p) "Region" "Block")])
526 ["Configure" elpy-config t]))
528 (defvar elpy-enabled-p nil
529 "Is Elpy enabled or not.")
532 (defun elpy-enable (&optional _ignored)
533 "Enable Elpy in all future Python buffers."
535 (unless elpy-enabled-p
536 (when (< emacs-major-version 24)
537 (error "Elpy requires Emacs 24 or newer"))
539 (warn "The argument to `elpy-enable' is deprecated, customize `elpy-modules' instead"))
540 (let ((filename (find-lisp-object-file-name 'python-mode
543 (string-match "/python-mode\\.el\\'"
545 (error (concat "You are using python-mode.el. "
546 "Elpy only works with python.el from "
547 "Emacs 24 and above"))))
548 (elpy-modules-global-init)
549 (define-key inferior-python-mode-map (kbd "C-c C-z") 'elpy-shell-switch-to-buffer)
550 (add-hook 'python-mode-hook 'elpy-mode)
551 (add-hook 'pyvenv-post-activate-hooks 'elpy-rpc--disconnect)
552 (add-hook 'pyvenv-post-deactivate-hooks 'elpy-rpc--disconnect)
553 (add-hook 'inferior-python-mode-hook 'elpy-shell--enable-output-filter)
554 (add-hook 'python-shell-first-prompt-hook 'elpy-shell--send-setup-code t)
555 ;; Add codecell boundaries highligting
556 (font-lock-add-keywords
558 `((,(replace-regexp-in-string "\\\\" "\\\\"
559 elpy-shell-cell-boundary-regexp)
560 0 'elpy-codecell-boundary prepend)))
561 ;; Enable Elpy-mode in the opened python buffer
562 (setq elpy-enabled-p t)
563 (dolist (buffer (buffer-list))
564 (and (not (string-match "^ ?\\*" (buffer-name buffer)))
565 (with-current-buffer buffer
566 (when (string= major-mode 'python-mode)
567 (python-mode) ;; update codecell fontification
571 (defun elpy-disable ()
572 "Disable Elpy in all future Python buffers."
574 (elpy-modules-global-stop)
575 (define-key inferior-python-mode-map (kbd "C-c C-z") nil)
576 (remove-hook 'python-mode-hook 'elpy-mode)
577 (remove-hook 'pyvenv-post-activate-hooks 'elpy-rpc--disconnect)
578 (remove-hook 'pyvenv-post-deactivate-hooks 'elpy-rpc--disconnect)
579 (remove-hook 'inferior-python-mode-hook 'elpy-shell--enable-output-filter)
580 (remove-hook 'python-shell-first-prompt-hook 'elpy-shell--send-setup-code)
581 ;; Remove codecell boundaries highligting
582 (font-lock-remove-keywords
584 `((,(replace-regexp-in-string "\\\\" "\\\\"
585 elpy-shell-cell-boundary-regexp)
586 0 'elpy-codecell-boundary prepend)))
587 (setq elpy-enabled-p nil))
590 (define-minor-mode elpy-mode
591 "Minor mode in Python buffers for the Emacs Lisp Python Environment.
593 This mode fully supports virtualenvs. Once you switch a
594 virtualenv using \\[pyvenv-workon], you can use
595 \\[elpy-rpc-restart] to make the elpy Python process use your
600 (unless (derived-mode-p 'python-mode 'python-ts-mode)
601 (error "Elpy only works with `python-mode'"))
602 (unless elpy-enabled-p
603 (error "Please enable Elpy with `(elpy-enable)` before using it"))
604 (when (boundp 'xref-backend-functions)
605 (add-hook 'xref-backend-functions #'elpy--xref-backend nil t))
606 ;; Set this for `elpy-check' command
607 (setq-local python-check-command elpy-syntax-check-command)
610 (elpy-modules-buffer-init))
612 (elpy-modules-buffer-stop))))
617 (defvar elpy-config--related-custom-groups
618 '(("Elpy" elpy "elpy-")
619 ("Python" python "python-")
620 ("Virtual Environments (Pyvenv)" pyvenv "pyvenv-")
621 ("Completion (Company)" company "company-")
622 ("Call Signatures (ElDoc)" eldoc "eldoc-")
623 ("Inline Errors (Flymake)" flymake "flymake-")
624 ("Code folding (hideshow)" hideshow "hs-")
625 ("Snippets (YASnippet)" yasnippet "yas-")
626 ("Directory Grep (rgrep)" grep "grep-")
627 ("Search as You Type (ido)" ido "ido-")
628 ("Django extension" elpy-django "elpy-django-")
629 ("Autodoc extension" elpy-autodoc "elpy-autodoc-")
630 ;; ffip does not use defcustom
631 ;; highlight-indent does not use defcustom, either. Its sole face
632 ;; is defined in basic-faces.
635 (defvar elpy-config--get-config "import json
639 warnings.filterwarnings('ignore', category=FutureWarning)
640 warnings.filterwarnings('ignore', category=DeprecationWarning)
641 warnings.filterwarnings('ignore', category=PendingDeprecationWarning)
643 from distutils.version import LooseVersion
646 import urllib2 as urllib
648 import urllib.request as urllib
651 # Check if we can connect to pypi quickly enough
653 response = urllib.urlopen('https://pypi.org/pypi', timeout=1)
654 CAN_CONNECT_TO_PYPI = True
656 CAN_CONNECT_TO_PYPI = False
659 def latest(package, version=None):
660 if not CAN_CONNECT_TO_PYPI:
663 response = urllib.urlopen('https://pypi.org/pypi/{package}/json'.format(package=package),
665 latest = json.loads(response)['info']['version']
666 if version is None or LooseVersion(version) < LooseVersion(latest):
675 config['can_connect_to_pypi'] = CAN_CONNECT_TO_PYPI
676 config['rpc_python_version'] = ('{major}.{minor}.{micro}'
677 .format(major=sys.version_info[0],
678 minor=sys.version_info[1],
679 micro=sys.version_info[2]))
683 config['elpy_version'] = elpy.__version__
685 config['elpy_version'] = None
689 if isinstance(jedi.__version__, tuple):
690 config['jedi_version'] = '.'.join(str(x) for x in jedi.__version__)
692 config['jedi_version'] = jedi.__version__
693 config['jedi_latest'] = latest('jedi', config['jedi_version'])
695 config['jedi_version'] = None
696 config['jedi_latest'] = latest('jedi')
700 config['autopep8_version'] = autopep8.__version__
701 config['autopep8_latest'] = latest('autopep8', config['autopep8_version'])
703 config['autopep8_version'] = None
704 config['autopep8_latest'] = latest('autopep8')
708 config['yapf_version'] = yapf.__version__
709 config['yapf_latest'] = latest('yapf', config['yapf_version'])
711 config['yapf_version'] = None
712 config['yapf_latest'] = latest('yapf')
716 config['black_version'] = black.__version__
717 config['black_latest'] = latest('black', config['black_version'])
719 config['black_version'] = None
720 config['black_latest'] = latest('black')
722 json.dump(config, sys.stdout)
725 (defun elpy-config-error (&optional fmt &rest args)
726 "Note a configuration problem.
728 FMT is the formating string.
730 This will show a message in the minibuffer that tells the user to
731 use \\[elpy-config]."
733 (apply #'format fmt args)
734 "Elpy is not properly configured")))
735 (error "%s; use M-x elpy-config to configure it" msg)))
738 (defun elpy-config ()
741 This function will pop up a configuration buffer, which is mostly
742 a customize buffer, but has some more options."
744 (let ((buf (custom-get-fresh-buffer "*Elpy Config*"))
745 (config (elpy-config--get-config))
746 (custom-search-field nil))
747 (with-current-buffer buf
748 (elpy-insert--header "Elpy Configuration")
750 (elpy-config--insert-configuration-table config)
753 (elpy-insert--header "Warnings")
755 (elpy-config--insert-configuration-problems config)
757 (elpy-insert--header "Options")
759 (let ((custom-buffer-style 'tree))
761 (elpy-config--insert-help)
762 (dolist (cust elpy-config--related-custom-groups)
763 (widget-create 'custom-group
765 :custom-state 'hidden
769 (goto-char (point-min))))
770 (pop-to-buffer-same-window buf)))
773 (defun elpy-version ()
774 "Display the version of Elpy."
776 (message "Elpy %s (use M-x elpy-config for details)" elpy-version))
778 (defun elpy-config--insert-help ()
779 "Insert the customization help."
780 (let ((start (point)))
781 ;; Help display from `customize-browse'
782 (widget-insert (format "\
783 %s buttons; type RET or click mouse-1
784 on a button to invoke its action.
785 Invoke [+] to expand a group, and [-] to collapse an expanded group.\n"
786 (if custom-raised-buttons
787 "`Raised' text indicates"
788 "Square brackets indicate")))
789 (if custom-browse-only-groups
791 Invoke the [Group] button below to edit that item in another window.\n\n")
792 (widget-insert "Invoke the ")
802 (widget-insert ", and ")
807 (widget-insert " buttons below to edit that
808 item in another window.\n\n")
810 (fill-region start (point)))))
812 (defun elpy-config--insert-configuration-problems (&optional config)
813 "Insert help text and widgets for configuration problems."
815 (setq config (elpy-config--get-config)))
816 (let* ((rpc-python-version (gethash "rpc_python_version" config)))
819 (unless (gethash "rpc_python_executable" config)
821 "Elpy can not find the configured Python interpreter. Please make "
822 "sure that the variable `elpy-rpc-python-command' points to a "
823 "command in your PATH. You can change the variable below.\n\n"))
826 (when (and (gethash "rpc_python_executable" config)
827 (not (gethash "virtual_env" config)))
829 "You have not activated a virtual env. It is not mandatory but"
830 " often a good idea to work inside a virtual env. You can use "
831 "`M-x pyvenv-activate` or `M-x pyvenv-workon` to activate one.\n\n"))
833 ;; No virtual env, but ~/.local/bin not in PATH
834 (when (and (not (memq system-type '(ms-dos windows-nt)))
835 (gethash "rpc_python_executable" config)
836 (not pyvenv-virtual-env)
837 (not (or (member (expand-file-name "~/.local/bin")
839 (member (expand-file-name "~/.local/bin/")
842 "The directory ~/.local/bin/ is not in your PATH. As there is "
843 "no active virtualenv, installing Python packages locally will "
844 "place executables in that directory, so Emacs won't find them. "
845 "If you are missing some commands, do add this directory to your "
846 "PATH -- and then do `elpy-rpc-restart'.\n\n"))
848 ;; Python found, but can't find the elpy module
849 (when (and (gethash "rpc_python_executable" config)
850 (not (gethash "elpy_version" config)))
852 "The Python interpreter could not find the elpy module. "
854 "https://github.com/jorgenschaefer/elpy/issues/new."
858 ;; Bad backend version
859 (when (and (gethash "elpy_version" config)
860 (not (equal (gethash "elpy_version" config)
862 (let ((elpy-python-version (gethash "elpy_version" config)))
864 "The Elpy backend is version " elpy-python-version " while "
865 "the Emacs package is " elpy-version ". This is incompatible. "
866 "Please report to: https://github.com/jorgenschaefer/elpy/issues/new."
869 ;; Otherwise unparseable output.
870 (when (gethash "error_output" config)
872 "There was an unexpected problem starting the RPC process. Please "
873 "check the following output to see if this makes sense to you. "
874 "To me, it doesn't.\n")
876 (gethash "error_output" config) "\n"
879 ;; Interactive python interpreter not in the current virtual env
880 (when (and pyvenv-virtual-env
881 (not (string-prefix-p (expand-file-name pyvenv-virtual-env)
883 python-shell-interpreter))))
885 "The python interactive interpreter (" python-shell-interpreter
886 ") is not installed on the current virtualenv ("
887 pyvenv-virtual-env "). The system binary ("
888 (executable-find python-shell-interpreter)
889 ") will be used instead."
892 (widget-create 'elpy-insert--pip-button
893 :package python-shell-interpreter :norpc t)
896 ;; Couldn't connect to pypi to check package versions
897 (when (not (gethash "can_connect_to_pypi" config))
899 "Elpy could not connect to Pypi (or at least not quickly enough) "
900 "and check if the python packages were up-to-date. "
901 "You can still try to update all of them:"
904 (widget-create 'elpy-insert--generic-button
905 :button-name "[Update python packages]"
906 :function (lambda () (with-elpy-rpc-virtualenv-activated
907 (elpy-rpc--install-dependencies))))
910 ;; Pip not available in the rpc virtualenv
912 (equal elpy-rpc-virtualenv-path 'default)
913 (elpy-rpc--pip-missing))
915 "Pip doesn't seem to be installed in the dedicated virtualenv "
916 "created by Elpy (" (elpy-rpc-get-virtualenv-path) "). "
917 "This may prevent some features from working properly"
918 " (completion, documentation, reformatting, ...). "
919 "You can try reinstalling the virtualenv. "
920 "If the problem persists, please report on Elpy's github page."
922 (widget-create 'elpy-insert--generic-button
923 :button-name "[Reinstall RPC virtualenv]"
924 :function (lambda () (elpy-rpc-reinstall-virtualenv)))
927 ;; Requested backend unavailable
928 (when (and (gethash "rpc_python_executable" config)
929 (not (gethash "jedi_version" config)))
931 "The Jedi package is not currently installed. "
932 "This package is needed for code completion, code navigation "
933 "and access to documentation.\n")
935 (widget-create 'elpy-insert--pip-button
939 ;; Newer version of Jedi available
940 (when (and (gethash "jedi_version" config)
941 (gethash "jedi_latest" config))
943 "There is a newer version of Jedi available.\n")
945 (widget-create 'elpy-insert--pip-button
946 :package "jedi" :upgrade t)
950 ;; No auto formatting tool available
952 (gethash "autopep8_version" config)
953 (gethash "yapf_version" config)
954 (gethash "black_version" config))
956 "No autoformatting package is currently installed. "
957 "At least one is needed (Autopep8, Yapf or Black) "
958 "to perform autoformatting (`C-c C-r f` in a python buffer).\n")
960 (widget-create 'elpy-insert--pip-button
963 (widget-create 'elpy-insert--pip-button
966 (widget-create 'elpy-insert--pip-button
970 ;; Newer version of autopep8 available
971 (when (and (gethash "autopep8_version" config)
972 (gethash "autopep8_latest" config))
974 "There is a newer version of the autopep8 package available.\n")
976 (widget-create 'elpy-insert--pip-button
977 :package "autopep8" :upgrade t)
980 ;; Newer version of yapf available
981 (when (and (gethash "yapf_version" config)
982 (gethash "yapf_latest" config))
984 "There is a newer version of the yapf package available.\n")
986 (widget-create 'elpy-insert--pip-button
987 :package "yapf" :upgrade t)
990 ;; Newer version of black available
991 (when (and (gethash "black_version" config)
992 (gethash "black_latest" config))
994 "There is a newer version of the black package available.\n")
996 (widget-create 'elpy-insert--pip-button
997 :package "black" :upgrade t)
1000 ;; Syntax checker not available
1001 (unless (executable-find (car (split-string elpy-syntax-check-command)))
1004 "The configured syntax checker (%s) could not be found. Elpy uses this "
1005 (car (split-string elpy-syntax-check-command)))
1006 "program to provide syntax checks of your code. You can either "
1007 "install it, or select another one using `elpy-syntax-check-command`.\n")
1009 (widget-create 'elpy-insert--pip-button :package "flake8" :norpc t)
1013 (defun elpy-config--package-available-p (package)
1014 "Check if PACKAGE is installed in the rpc."
1015 (with-elpy-rpc-virtualenv-activated
1016 (equal 0 (call-process elpy-rpc-python-command nil nil nil "-c"
1017 (format "import %s" package)))))
1019 (defun elpy-config--get-config ()
1020 "Return the configuration from `elpy-rpc-python-command'.
1022 This returns a hash table with the following keys (all strings):
1027 python_interactive_version
1028 python_interactive_executable
1030 rpc_virtualenv_short
1033 rpc_python_executable
1038 (let ((config (make-hash-table :test #'equal)))
1039 (puthash "emacs_version" emacs-version config)
1040 (let ((rpc-venv (elpy-rpc-get-or-create-virtualenv)))
1041 (puthash "rpc_virtualenv" rpc-venv config)
1043 (puthash "rpc_virtualenv_short"
1044 (file-name-nondirectory (directory-file-name rpc-venv))
1046 (puthash "rpc_virtualenv_short" nil config)))
1047 (with-elpy-rpc-virtualenv-activated
1048 (puthash "rpc_python" elpy-rpc-python-command config)
1049 (puthash "rpc_python_executable"
1050 (executable-find elpy-rpc-python-command)
1052 (let ((interactive-python (if (boundp 'python-python-command)
1053 python-python-command
1054 python-shell-interpreter)))
1055 (puthash "python_interactive"
1058 (puthash "python_interactive_version"
1059 (let ((pversion (shell-command-to-string
1060 (format "%s --version"
1061 python-shell-interpreter))))
1062 (when (string-match "[0-9.]+" pversion)
1063 (match-string 0 pversion)))
1065 (puthash "python_interactive_executable"
1066 (executable-find interactive-python)
1068 (let ((venv (getenv "VIRTUAL_ENV")))
1069 (puthash "virtual_env" venv config)
1071 (puthash "virtual_env_short" (file-name-nondirectory
1072 (directory-file-name venv))
1074 (puthash "virtual_env_short" nil config)))
1075 (with-elpy-rpc-virtualenv-activated
1076 (let ((return-value (ignore-errors
1077 (let ((process-environment
1078 (elpy-rpc--environment))
1079 (default-directory "/"))
1080 (call-process elpy-rpc-python-command
1085 elpy-config--get-config)))))
1087 (let ((data (ignore-errors
1088 (let ((json-array-type 'list)
1090 (json-encoding-pretty-print nil)) ;; Link to bug https://github.com/jorgenschaefer/elpy/issues/1521
1091 (goto-char (point-min))
1094 (puthash "error_output" (buffer-string) config)
1096 (puthash (symbol-name (car pair)) (cdr pair) config)))))))
1099 (defun elpy-config--insert-configuration-table (&optional config)
1100 "Insert a table describing the current Elpy config."
1102 (setq config (elpy-config--get-config)))
1103 (let ((emacs-version (gethash "emacs_version" config))
1104 (elpy-python-version (gethash "elpy_version" config))
1105 (virtual-env (gethash "virtual_env" config))
1106 (virtual-env-short (gethash "virtual_env_short" config))
1107 (python-interactive (gethash "python_interactive" config))
1108 (python-interactive-version (gethash "python_interactive_version" config))
1109 (python-interactive-executable (gethash "python_interactive_executable"
1111 (rpc-python (gethash "rpc_python" config))
1112 (rpc-python-executable (gethash "rpc_python_executable" config))
1113 (rpc-python-version (gethash "rpc_python_version" config))
1114 (rpc-virtualenv (gethash "rpc_virtualenv" config))
1115 (rpc-virtualenv-short (gethash "rpc_virtualenv_short" config))
1116 (jedi-version (gethash "jedi_version" config))
1117 (jedi-latest (gethash "jedi_latest" config))
1118 (autopep8-version (gethash "autopep8_version" config))
1119 (autopep8-latest (gethash "autopep8_latest" config))
1120 (yapf-version (gethash "yapf_version" config))
1121 (yapf-latest (gethash "yapf_latest" config))
1122 (black-version (gethash "black_version" config))
1123 (black-latest (gethash "black_latest" config))
1126 `(("Emacs" . ,emacs-version)
1128 ((and elpy-python-version elpy-version
1129 (equal elpy-python-version elpy-version))
1131 (elpy-python-version
1132 (format "%s (Python), %s (Emacs Lisp)"
1136 (format "Not found (Python), %s (Emacs Lisp)"
1138 (("Virtualenv" (lambda ()
1139 (call-interactively 'pyvenv-workon)
1141 . ,(if (gethash "virtual_env" config)
1146 (("Interactive Python" (lambda ()
1148 'python-shell-interpreter)))
1150 (python-interactive-executable
1151 (format "%s %s (%s)"
1153 python-interactive-version
1154 python-interactive-executable))
1156 (format "%s (not found)"
1157 python-interactive))
1161 . ,(format "%s (%s)"
1162 (if (or (eq elpy-rpc-virtualenv-path 'system)
1163 (eq elpy-rpc-virtualenv-path 'global)) ;; for backward compatibility
1165 rpc-virtualenv-short)
1167 ((" Python" (lambda ()
1169 'elpy-rpc-python-command)))
1171 (rpc-python-executable
1172 (format "%s %s (%s)"
1175 rpc-python-executable))
1176 (rpc-python-executable
1177 rpc-python-executable)
1179 (format "%s (not found)" rpc-python))
1181 (format "Not configured"))))
1182 (" Jedi" . ,(elpy-config--package-link "jedi"
1185 (" Autopep8" . ,(elpy-config--package-link "autopep8"
1188 (" Yapf" . ,(elpy-config--package-link "yapf"
1191 (" Black" . ,(elpy-config--package-link "black"
1194 (("Syntax checker" (lambda ()
1195 (customize-variable 'elpy-syntax-check-command)))
1197 . ,(let ((syntax-checker
1200 elpy-syntax-check-command)))))
1203 (file-name-nondirectory
1206 (format "Not found (%s)"
1207 elpy-syntax-check-command))))))
1211 (if (stringp (car row))
1212 (setq length (length (car row)))
1213 (setq length (length (car (car row)))))
1214 (when (> length maxwidth)
1215 (setq maxwidth length ))))
1217 (if (stringp (car row))
1219 (make-string (- maxwidth (length (car row)))
1221 (widget-create 'elpy-insert--generic-button
1222 :button-name (car (car row))
1223 :function (car (cdr (car row))))
1224 (insert (make-string (- maxwidth (length (car (car row))))
1230 (defun elpy-config--package-link (_name version latest)
1231 "Return a string detailing a Python package.
1233 NAME is the PyPI name of the package. VERSION is the currently
1234 installed version. LATEST is the latest-available version on
1235 PyPI, or nil if that's VERSION."
1237 ((and (not version) (not latest))
1242 (format "Not found (%s available)" latest))
1244 (format "%s (%s available)" version latest))))
1246 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1247 ;;; Elpy Formatted Insertion
1249 (defun elpy-insert--para (&rest messages)
1250 "Insert MESSAGES, a list of strings, and then fill it."
1251 (let ((start (point)))
1255 (insert (format "%s" obj))))
1257 (fill-region start (point))))
1259 (defun elpy-insert--header (&rest text)
1260 "Insert TEXT has a header for a buffer."
1261 (insert (propertize (mapconcat #'(lambda (x) x)
1268 (define-widget 'elpy-insert--generic-button 'item
1269 "A button that run a rgiven function."
1273 :value-create 'elpy-insert--generic-button-value-create
1274 :action 'elpy-insert--generic-button-action)
1276 (defun elpy-insert--generic-button-value-create (widget)
1277 "The :value-create option for the customize button widget."
1278 (insert (widget-get widget :button-name)))
1280 (defun elpy-insert--generic-button-action (widget &optional _event)
1281 "The :action option for the customize button widget."
1282 (funcall (widget-get widget :function)))
1284 (define-widget 'elpy-insert--pip-button 'item
1285 "A button that runs pip (or an alternative)."
1289 :value-create 'elpy-insert--pip-button-value-create
1290 :action 'elpy-insert--pip-button-action)
1292 (defun elpy-insert--pip-button-value-create (widget)
1293 "The :value-create option for the pip button widget."
1294 (let* ((python-package (widget-get widget :package))
1295 (do-upgrade (widget-get widget :upgrade))
1296 (upgrade-option (if do-upgrade
1300 ((= (call-process elpy-rpc-python-command
1302 "-m" "pip" "--help")
1304 (format "%s -m pip install %s%s"
1305 elpy-rpc-python-command
1308 ((executable-find "easy_install")
1309 (format "easy_install %s" python-package))
1311 (error "Neither easy_install nor pip found")))))
1312 (widget-put widget :command command)
1314 (insert (format "Update %s" python-package))
1315 (insert (format "Install %s" python-package)))))
1317 (defun elpy-insert--pip-button-action (widget &optional _event)
1318 "The :action option for the pip button widget."
1319 (let ((command (widget-get widget :command))
1320 (norpc (widget-get widget :norpc)))
1322 (async-shell-command command)
1323 (with-elpy-rpc-virtualenv-activated
1324 (async-shell-command command)))))
1329 (defvar elpy-project--variable-name-history nil
1330 "The history for `elpy-project--read-project-variable'.")
1332 (defun elpy-project-root ()
1333 "Return the root of the current buffer's project.
1335 This can very well be nil if the current file is not part of a
1338 See `elpy-project-root-finder-functions' for a way to configure
1339 how the project root is found. You can also set the variable
1340 `elpy-project-root' in, for example, .dir-locals.el to override
1342 (unless elpy-project-root
1343 (setq elpy-project-root
1344 (run-hook-with-args-until-success
1345 'elpy-project-root-finder-functions)))
1348 (defun elpy-set-project-root (new-root)
1349 "Set the Elpy project root to NEW-ROOT."
1350 (interactive "DNew project root: ")
1351 (setq elpy-project-root new-root))
1353 (defun elpy-project-find-python-root ()
1354 "Return the current Python project root, if any.
1356 This is marked with 'setup.py', 'setup.cfg' or 'pyproject.toml'."
1357 (or (locate-dominating-file default-directory "setup.py")
1358 (locate-dominating-file default-directory "setup.cfg")
1359 (locate-dominating-file default-directory "pyproject.toml")))
1361 (defun elpy-project-find-git-root ()
1362 "Return the current git repository root, if any."
1363 (locate-dominating-file default-directory ".git"))
1365 (defun elpy-project-find-hg-root ()
1366 "Return the current git repository root, if any."
1367 (locate-dominating-file default-directory ".hg"))
1369 (defun elpy-project-find-svn-root ()
1370 "Return the current git repository root, if any."
1371 (locate-dominating-file default-directory
1373 (and (file-directory-p (format "%s/.svn" dir))
1374 (not (file-directory-p (format "%s/../.svn"
1377 (defun elpy-project-find-projectile-root ()
1378 "Return the current project root according to projectile."
1379 ;; `ignore-errors' both to avoid an unbound function error as well
1380 ;; as ignore projectile saying there is no project root here.
1382 (projectile-project-root)))
1384 (defun elpy-library-root ()
1385 "Return the root of the Python package chain of the current buffer.
1387 That is, if you have /foo/package/module.py, it will return /foo,
1388 so that import package.module will pick up module.py."
1389 (locate-dominating-file default-directory
1392 (format "%s/__init__.py"
1395 (defun elpy-project--read-project-variable (prompt)
1396 "Prompt the user for a variable name to set project-wide using PROMPT."
1397 (let* ((prefixes (mapcar (lambda (cust)
1399 elpy-config--related-custom-groups))
1400 (var-regex (format "^%s" (regexp-opt prefixes))))
1406 (and (get sym 'safe-local-variable)
1407 (string-match var-regex (symbol-name sym))
1408 (get sym 'custom-type)))
1411 'elpy-project--variable-name-history))))
1413 (defun elpy-project--read-variable-value (prompt variable)
1414 "Read the value for VARIABLE from the user using PROMPT."
1415 (let ((custom-type (get variable 'custom-type)))
1417 (widget-prompt-value (if (listp custom-type)
1421 (if (boundp variable)
1423 (or (get variable 'custom-get)
1426 (not (boundp variable)))
1427 (eval-minibuffer prompt))))
1429 (defun elpy-set-project-variable (variable value)
1430 "Set or remove a variable in the project-wide .dir-locals.el.
1432 With prefix argument, remove the variable."
1434 (let* ((variable (elpy-project--read-project-variable
1435 (if current-prefix-arg
1436 "Remove project variable: "
1437 "Set project variable: ")))
1438 (value (if current-prefix-arg
1440 (elpy-project--read-variable-value (format "Value for %s: "
1443 (list variable value)))
1444 (with-current-buffer (find-file-noselect (format "%s/%s"
1447 (modify-dir-local-variable nil
1450 (if current-prefix-arg
1454 ;;;;;;;;;;;;;;;;;;;;;;;;
1455 ;;; Search Project Files
1457 (defun elpy-rgrep-symbol (regexp)
1458 "Search for REGEXP in the current project.
1460 REGEXP defaults to the symbol at point, or the current region if
1463 With a prefix argument, always prompt for a string to search for."
1468 (buffer-substring-no-properties (region-beginning)
1470 (thing-at-point 'symbol))))
1471 (if (and symbol (not current-prefix-arg))
1472 (concat "\\<" symbol "\\>")
1473 (read-from-minibuffer "Search in project for regexp: " symbol)))))
1474 (grep-compute-defaults)
1475 (let ((grep-find-ignored-directories (elpy-project-ignored-directories)))
1477 elpy-rgrep-file-pattern
1478 (or (elpy-project-root)
1479 default-directory)))
1480 (with-current-buffer next-error-last-buffer
1481 (let ((inhibit-read-only t))
1483 (goto-char (point-min))
1484 (when (re-search-forward "^find .*" nil t)
1485 (replace-match (format "Searching for '%s'\n"
1486 (regexp-quote regexp))))))))
1488 ;;;;;;;;;;;;;;;;;;;;;;
1489 ;;; Find Project Files
1491 (defcustom elpy-ffip-prune-patterns '()
1492 "Elpy-specific extension of `ffip-prune-patterns'.
1493 This is in addition to `elpy-project-ignored-directories'
1494 and `completion-ignored-extensions'.
1495 The final value of `ffip-prune-patterns' used is computed
1496 by the eponymous function `elpy-ffip-prune-patterns'."
1497 :type '(repeat string)
1499 (cl-every #'stringp val))
1502 (defun elpy-ffip-prune-patterns ()
1503 "Compute `ffip-prune-patterns' from other variables.
1505 `elpy-ffip-prune-patterns'
1506 `elpy-project-ignored-directories'
1507 `completion-ignored-extensions'
1508 `ffip-prune-patterns'."
1511 (mapcar (lambda (dir) (concat "*/" dir "/*"))
1512 elpy-project-ignored-directories)
1513 (mapcar (lambda (ext) (if (s-ends-with? "/" ext)
1514 (concat "*" ext "*")
1516 completion-ignored-extensions)
1517 (cl-copy-list elpy-ffip-prune-patterns)
1518 (cl-copy-list ffip-prune-patterns))))
1520 (defun elpy-find-file (&optional dwim)
1521 "Efficiently find a file in the current project.
1523 It necessitates `projectile' or `find-file-in-project' to be installed.
1525 With prefix argument (or DWIM non-nil), tries to guess what kind of
1526 file the user wants to open:
1527 - On an import line, it opens the file of that module.
1528 - Otherwise, it opens a test file associated with the current file,
1529 if one exists. A test file is named test_<name>.py if the current
1530 file is <name>.py, and is either in the same directory or a
1531 \"test\" or \"tests\" subdirectory."
1537 (goto-char (line-beginning-position))
1538 (or (looking-at "^ *import +\\([[:alnum:]._]+\\)")
1539 (looking-at "^ *from +\\([[:alnum:]._]+\\) +import +\\([[:alnum:]._]+\\)"))))
1540 (let* ((module (if (match-string 2)
1541 (format "%s.%s" (match-string 1) (match-string 2))
1543 (path (elpy-find--resolve-module module)))
1546 (elpy-find-file nil))))
1549 (let ((test-file (elpy-find--test-file)))
1551 (find-file test-file)
1552 (elpy-find-file nil))))
1553 ((fboundp 'projectile-find-file)
1554 (let ((projectile-globally-ignored-file-suffixes
1557 (cl-copy-list projectile-globally-ignored-file-suffixes)
1558 (cl-copy-list completion-ignored-extensions))))
1559 (projectile-globally-ignored-directories
1562 (cl-copy-list projectile-globally-ignored-directories)
1563 (cl-copy-list elpy-ffip-prune-patterns)
1564 (elpy-project-ignored-directories))))
1565 (projectile-project-root (or (elpy-project-root)
1566 default-directory)))
1567 (projectile-find-file)))
1568 ((fboundp 'find-file-in-project)
1569 (let ((ffip-prune-patterns (elpy-ffip-prune-patterns))
1570 (ffip-project-root (or (elpy-project-root)
1572 ;; Set up ido to use vertical file lists.
1573 (ffip-prefer-ido-mode t)
1574 (ido-decorations '("\n" "" "\n" "\n..."
1575 "[" "]" " [No match]" " [Matched]"
1576 " [Not readable]" " [Too big]"
1578 (ido-setup-hook (cons (lambda ()
1579 (define-key ido-completion-map (kbd "<down>")
1581 (define-key ido-completion-map (kbd "<up>")
1584 (find-file-in-project)))
1586 (error "`elpy-find-file' necessitates `projectile' or `find-file-in-project' to be installed"))))
1588 (defun elpy-find--test-file ()
1589 "Return the test file for the current file, if any.
1591 If this is a test file, return the non-test file.
1593 A test file is named test_<name>.py if the current file is
1594 <name>.py, and is either in the same directors or a \"test\" or
1595 \"tests\" subdirectory."
1596 (let* ((project-root (or (elpy-project-root) default-directory))
1597 (filename (file-name-base (buffer-file-name)))
1598 (impl-file (when (string-match "test_\\(.*\\)" filename)
1599 (match-string 1 filename)))
1602 ((fboundp 'projectile-find-file)
1603 (let ((projectile-project-root project-root))
1604 (projectile-current-project-files)))
1605 ((fboundp 'find-file-in-project)
1606 (let ((ffip-project-root project-root))
1607 (cl-map 'list 'cdr (ffip-project-search nil nil))))
1610 (cl-remove-if (lambda (file)
1613 (file-name-base file))))
1615 (cl-remove-if (lambda (file)
1617 (format "test_%s" filename)
1618 (file-name-base file))))
1621 (if (> (length file) 1)
1622 (setq file (completing-read "Which file: " file))
1623 (setq file (car file)))
1624 (if (file-name-absolute-p file)
1626 (concat (file-name-as-directory project-root) file)))))
1628 (defun elpy-find--module-path (module)
1629 "Return a directory path for MODULE.
1631 The resulting path is not guaranteed to exist. This simply
1632 resolves leading periods relative to the current directory and
1633 replaces periods in the middle of the string with slashes.
1635 Only works with absolute imports. Stop using implicit relative
1636 imports. They're a bad idea."
1637 (let* ((relative-depth (when(string-match "^\\.+" module)
1638 (length (match-string 0 module))))
1639 (base-directory (if relative-depth
1642 (mapconcat (lambda (_)
1644 (make-vector relative-depth
1647 (elpy-library-root)))
1648 (file-name (replace-regexp-in-string
1652 (substring module relative-depth)
1654 (expand-file-name (format "%s/%s" base-directory file-name))))
1656 (defun elpy-find--resolve-module (module)
1657 "Resolve MODULE relative to the current file and project.
1659 Returns a full path name for that module."
1661 (let ((path (elpy-find--module-path module)))
1662 (while (string-prefix-p (expand-file-name (elpy-library-root))
1664 (dolist (name (list (format "%s.py" path)
1665 (format "%s/__init__.py" path)))
1666 (when (file-exists-p name)
1667 (throw 'return name)))
1668 (if (string-match "/$" path)
1669 (setq path (substring path 0 -1))
1670 (setq path (file-name-directory path)))))
1676 (defun elpy-check (&optional whole-project-p)
1677 "Run `python-check-command' on the current buffer's file,
1679 or the project root if WHOLE-PROJECT-P is non-nil (interactively,
1680 with a prefix argument)."
1682 (unless (buffer-file-name)
1683 (error "Can't check a buffer without a file"))
1684 (save-some-buffers (not compilation-ask-about-save) nil)
1685 (let ((process-environment (python-shell-calculate-process-environment))
1686 (exec-path (python-shell-calculate-exec-path))
1687 (file-name-or-directory (expand-file-name
1689 (or (elpy-project-root)
1691 (buffer-file-name))))
1692 (extra-args (if whole-project-p
1694 (if (string-match "pylint$" python-check-command)
1697 (mapconcat #'identity
1698 (elpy-project-ignored-directories)
1701 (compilation-start (concat python-check-command
1703 (shell-quote-argument file-name-or-directory)
1706 (lambda (_mode-name)
1707 "*Python Check*"))))
1712 (defvar elpy-nav-expand--initial-position nil
1713 "Initial position before expanding to indentation.")
1714 (make-variable-buffer-local 'elpy-nav-expand--initial-position)
1716 (defun elpy-rpc-warn-if-jedi-not-available ()
1717 "Display a warning if jedi is not available in the current RPC."
1718 (unless elpy-rpc--jedi-available
1719 (error "This feature requires the `jedi` package to be installed. Please check `elpy-config` for more information.")))
1721 (defun elpy-goto-definition ()
1722 "Go to the definition of the symbol at point, if found."
1724 (elpy-rpc-warn-if-jedi-not-available)
1725 (let ((location (elpy-rpc-get-definition)))
1727 (elpy-goto-location (car location) (cadr location))
1728 (error "No definition found"))))
1730 (defun elpy-goto-assignment ()
1731 "Go to the assignment of the symbol at point, if found."
1733 (elpy-rpc-warn-if-jedi-not-available)
1734 (let ((location (elpy-rpc-get-assignment)))
1736 (elpy-goto-location (car location) (cadr location))
1737 (error "No assignment found"))))
1739 (defun elpy-goto-definition-other-window ()
1740 "Go to the definition of the symbol at point in other window, if found."
1742 (elpy-rpc-warn-if-jedi-not-available)
1743 (let ((location (elpy-rpc-get-definition)))
1745 (elpy-goto-location (car location) (cadr location) 'other-window)
1746 (error "No definition found"))))
1748 (defun elpy-goto-assignment-other-window ()
1749 "Go to the assignment of the symbol at point in other window, if found."
1751 (elpy-rpc-warn-if-jedi-not-available)
1752 (let ((location (elpy-rpc-get-assignment)))
1754 (elpy-goto-location (car location) (cadr location) 'other-window)
1755 (error "No assignment found"))))
1757 (defun elpy-goto-location (filename offset &optional other-window-p)
1758 "Show FILENAME at OFFSET to the user.
1760 If OTHER-WINDOW-P is non-nil, show the same in other window."
1762 ;; `find-tag-marker-ring' is marked as obsolete in 25.1
1763 ;; and not available in 27.
1764 (if (version< emacs-version "25.1")
1765 (ring-insert find-tag-marker-ring (point-marker))
1766 (xref-push-marker-stack))
1767 (let ((buffer (find-file-noselect filename)))
1769 (pop-to-buffer buffer t)
1770 (switch-to-buffer buffer))
1771 (goto-char (1+ offset))
1774 (defun elpy-nav-forward-block ()
1775 "Move to the next line indented like point.
1777 This will skip over lines and statements with different
1778 indentation levels."
1780 (let ((indent (current-column))
1783 (when (/= (% indent python-indent-offset)
1785 (setq indent (* (1+ (/ indent python-indent-offset))
1786 python-indent-offset)))
1787 (python-nav-forward-statement)
1788 (while (and (< indent (current-indentation))
1790 (when (equal (point) cur)
1791 (error "Statement does not finish"))
1793 (python-nav-forward-statement))
1794 (when (< (current-indentation)
1796 (goto-char start))))
1798 (defun elpy-nav-backward-block ()
1799 "Move to the previous line indented like point.
1801 This will skip over lines and statements with different
1802 indentation levels."
1804 (let ((indent (current-column))
1807 (when (/= (% indent python-indent-offset)
1809 (setq indent (* (1+ (/ indent python-indent-offset))
1810 python-indent-offset)))
1811 (python-nav-backward-statement)
1812 (while (and (< indent (current-indentation))
1814 (when (equal (point) cur)
1815 (error "Statement does not start"))
1817 (python-nav-backward-statement))
1818 (when (< (current-indentation)
1820 (goto-char start))))
1822 (defun elpy-nav-forward-indent ()
1823 "Move forward to the next indent level, or over the next word."
1825 (if (< (current-column) (current-indentation))
1826 (let* ((current (current-column))
1827 (next (* (1+ (/ current python-indent-offset))
1828 python-indent-offset)))
1829 (goto-char (+ (point-at-bol)
1831 (let ((eol (point-at-eol)))
1833 (when (> (point) eol)
1834 (goto-char (point-at-bol))))))
1836 (defun elpy-nav-backward-indent ()
1837 "Move backward to the previous indent level, or over the previous word."
1839 (if (and (<= (current-column) (current-indentation))
1840 (/= (current-column) 0))
1841 (let* ((current (current-column))
1842 (next (* (1- (/ current python-indent-offset))
1843 python-indent-offset)))
1844 (goto-char (+ (point-at-bol)
1848 (defun elpy-nav-move-line-or-region-down (&optional beg end)
1849 "Move the current line or active region down."
1852 (list (region-beginning) (region-end))
1855 (elpy--nav-move-region-vertically beg end 1)
1856 (elpy--nav-move-line-vertically 1)))
1858 (defun elpy-nav-move-line-or-region-up (&optional beg end)
1859 "Move the current line or active region down."
1862 (list (region-beginning) (region-end))
1865 (elpy--nav-move-region-vertically beg end -1)
1866 (elpy--nav-move-line-vertically -1)))
1868 (defun elpy--nav-move-line-vertically (dir)
1869 "Move the current line vertically in direction DIR."
1870 (let* ((beg (point-at-bol))
1871 (end (point-at-bol 2))
1872 (col (current-column))
1873 (region (delete-and-extract-region beg end)))
1877 (goto-char (+ (point) col))))
1879 (defun elpy--nav-move-region-vertically (beg end dir)
1880 "Move the current region vertically in direction DIR."
1881 (let* ((point-before-mark (< (point) (mark)))
1882 (beg (save-excursion
1885 (end (save-excursion
1890 (region (delete-and-extract-region beg end)))
1895 (if point-before-mark
1896 (set-mark (+ (point)
1899 (goto-char (+ (point)
1901 (setq deactivate-mark nil)))
1903 (defun elpy-open-and-indent-line-below ()
1904 "Open a line below the current one, move there, and indent."
1906 (move-end-of-line 1)
1907 (newline-and-indent))
1909 (defun elpy-open-and-indent-line-above ()
1910 "Open a line above the current one, move there, and indent."
1912 (move-beginning-of-line 1)
1915 (indent-according-to-mode))
1917 (defun elpy-nav-expand-to-indentation ()
1918 "Select surrounding lines with current indentation."
1920 (setq elpy-nav-expand--initial-position (point))
1921 (let ((indentation (current-indentation)))
1922 (if (= indentation 0)
1925 (push-mark (point-max) nil t)
1926 (goto-char (point-min)))
1927 (while (<= indentation (current-indentation))
1930 (push-mark (point) nil t)
1931 (while (<= indentation (current-indentation))
1935 (defadvice keyboard-quit (before collapse-region activate)
1936 "Abort elpy selection by indentation on quit."
1937 (when (eq last-command 'elpy-nav-expand-to-indentation)
1938 (goto-char elpy-nav-expand--initial-position)))
1940 (defun elpy-nav-normalize-region ()
1941 "If the first or last line are not fully selected, select them completely."
1942 (let ((beg (region-beginning))
1946 (push-mark (point) nil t)
1948 (unless (= (point) (line-beginning-position))
1951 (defun elpy-nav-indent-shift-right (&optional _count)
1952 "Shift current line by COUNT columns to the right.
1954 COUNT defaults to `python-indent-offset'.
1955 If region is active, normalize the region and shift."
1959 (elpy-nav-normalize-region)
1960 (python-indent-shift-right (region-beginning) (region-end) current-prefix-arg))
1961 (python-indent-shift-right (line-beginning-position) (line-end-position) current-prefix-arg)))
1963 (defun elpy-nav-indent-shift-left (&optional _count)
1964 "Shift current line by COUNT columns to the left.
1966 COUNT defaults to `python-indent-offset'.
1967 If region is active, normalize the region and shift."
1971 (elpy-nav-normalize-region)
1972 (python-indent-shift-left (region-beginning) (region-end) current-prefix-arg))
1973 (python-indent-shift-left (line-beginning-position) (line-end-position) current-prefix-arg)))
1979 (defvar elpy-set-test-runner-history nil
1980 "History variable for `elpy-set-test-runner'.")
1982 (defun elpy-test (&optional test-whole-project)
1983 "Run tests on the current test, or the whole project.
1985 If there is a test at point, run that test. If not, or if a
1986 prefix is given, run all tests in the current project."
1988 (let ((current-test (elpy-test-at-point)))
1989 (if test-whole-project
1990 ;; With prefix arg, test the whole project.
1991 (funcall elpy-test-runner
1994 ;; Else, run only this test
1995 (apply elpy-test-runner current-test))))
1997 (defun elpy-set-test-runner (test-runner)
1998 "Tell Elpy to use TEST-RUNNER to run tests.
2000 See `elpy-test' for how to invoke it."
2003 (let* ((runners (mapcar (lambda (value)
2006 (cdr (get 'elpy-test-runner 'custom-type))))
2007 (current (cdr (assq elpy-test-runner
2008 (mapcar (lambda (cell)
2009 (cons (cdr cell) (car cell)))
2011 (choice (completing-read (if current
2012 (format "Test runner (currently %s): "
2016 nil t nil 'elpy-set-test-runner-history)))
2017 (if (equal choice "")
2019 (cdr (assoc choice runners))))))
2020 (setq elpy-test-runner test-runner))
2022 (defun elpy-test-at-point ()
2023 "Return a list specifying the test at point, if any.
2025 This is used as the interactive
2027 This list has four elements.
2029 - Top level directory:
2030 All test files should be importable from here.
2032 The current file name.
2034 The module name, relative to the top level directory.
2036 The full name of the current test within the module, for
2037 example TestClass.test_method
2039 If there is no test at point, test name is nil.
2040 If the current buffer is not visiting a file, only the top level
2041 directory is not nil."
2042 (if (not buffer-file-name)
2045 (list (elpy-library-root) nil nil nil))
2046 (let* ((top (elpy-library-root))
2047 (file buffer-file-name)
2048 (module (elpy-test--module-name-for-file top file))
2049 (test (elpy-test--current-test-name)))
2050 (if (and file (string-match "test" (or module test "")))
2053 (list top file module test))
2055 (list top nil nil nil)))))
2057 (defun elpy-test--current-test-name ()
2058 "Return the name of the test at point."
2059 (let ((name (python-info-current-defun)))
2061 (string-match "\\`\\([^.]+\\.[^.]+\\)\\." name))
2062 (match-string 1 name)
2065 (defun elpy-test--module-name-for-file (top-level module-file)
2066 "Return the module name relative to TOP-LEVEL for MODULE-FILE.
2068 For example, for a top level of /project/root/ and a module file
2069 of /project/root/package/module.py, this would return
2070 \"package.module\"."
2071 (let* ((relative-name (file-relative-name module-file top-level))
2072 (no-extension (replace-regexp-in-string "\\.py\\'" "" relative-name))
2073 (no-init (replace-regexp-in-string "/__init__\\'" "" no-extension))
2074 (dotted (replace-regexp-in-string "/" "." no-init)))
2075 (if (string-match "^\\." dotted)
2076 (concat "." (replace-regexp-in-string (regexp-quote "...") "." dotted))
2079 (defun elpy-test-runner-p (obj)
2080 "Return t iff OBJ is a test runner.
2082 This uses the `elpy-test-runner-p' symbol property."
2083 (get obj 'elpy-test-runner-p))
2085 (defun elpy-test-run (working-directory command &rest args)
2086 "Run COMMAND with ARGS in WORKING-DIRECTORY as a test command."
2087 (let ((default-directory working-directory))
2088 (funcall elpy-test-compilation-function
2089 (mapconcat #'shell-quote-argument
2093 (defun elpy-test-get-discover-runner ()
2094 "Return the test discover runner from `elpy-test-discover-runner-command'."
2096 for string in elpy-test-discover-runner-command
2097 if (string= string "python-shell-interpreter")
2098 collect python-shell-interpreter
2102 (defun elpy-test-discover-runner (top _file module test)
2103 "Test the project using the python unittest discover runner.
2105 This requires Python 2.7 or later."
2106 (interactive (elpy-test-at-point))
2108 (test (format "%s.%s" module test))
2111 (apply #'elpy-test-run
2113 (append (elpy-test-get-discover-runner)
2115 (put 'elpy-test-discover-runner 'elpy-test-runner-p t)
2117 (defun elpy-test-green-runner (top _file module test)
2118 "Test the project using the green runner."
2119 (interactive (elpy-test-at-point))
2121 (test (format "%s.%s" module test))
2124 (append elpy-test-green-runner-command (list test))
2125 elpy-test-green-runner-command)))
2126 (apply #'elpy-test-run top command)))
2127 (put 'elpy-test-green-runner 'elpy-test-runner-p t)
2129 (defun elpy-test-nose-runner (top _file module test)
2130 "Test the project using the nose test runner.
2132 This requires the nose package to be installed."
2133 (interactive (elpy-test-at-point))
2135 (apply #'elpy-test-run
2137 (append elpy-test-nose-runner-command
2139 (format "%s:%s" module test)
2141 (apply #'elpy-test-run
2143 elpy-test-nose-runner-command)))
2144 (put 'elpy-test-nose-runner 'elpy-test-runner-p t)
2146 (defun elpy-test-trial-runner (top _file module test)
2147 "Test the project using Twisted's Trial test runner.
2149 This requires the twisted-core package to be installed."
2150 (interactive (elpy-test-at-point))
2152 (apply #'elpy-test-run
2154 (append elpy-test-trial-runner-command
2156 (format "%s.%s" module test)
2158 (apply #'elpy-test-run top elpy-test-trial-runner-command)))
2159 (put 'elpy-test-trial-runner 'elpy-test-runner-p t)
2161 (defun elpy-test-pytest-runner (top file module test)
2162 "Test the project using the py.test test runner.
2164 This requires the pytest package to be installed."
2165 (interactive (elpy-test-at-point))
2168 (let ((test-list (split-string test "\\.")))
2169 (apply #'elpy-test-run
2171 (append elpy-test-pytest-runner-command
2172 (list (mapconcat #'identity
2173 (cons file test-list)
2176 (apply #'elpy-test-run top (append elpy-test-pytest-runner-command
2179 (apply #'elpy-test-run top elpy-test-pytest-runner-command))))
2180 (put 'elpy-test-pytest-runner 'elpy-test-runner-p t)
2185 (defvar elpy-doc-history nil
2186 "History for the `elpy-doc' command.")
2189 "Show documentation for the symbol at point.
2191 If there is no documentation for the symbol at point, or if a
2192 prefix argument is given, prompt for a symbol from the user."
2195 (unless current-prefix-arg
2196 (setq doc (elpy-rpc-get-docstring))
2199 (python-nav-backward-up-list)
2200 (setq doc (elpy-rpc-get-docstring))))
2202 (setq doc (elpy-rpc-get-pydoc-documentation
2203 (elpy-doc--symbol-at-point))))
2206 (python-nav-backward-up-list)
2207 (setq doc (elpy-rpc-get-pydoc-documentation
2208 (elpy-doc--symbol-at-point))))))
2210 (setq doc (elpy-rpc-get-pydoc-documentation
2211 (elpy-doc--read-identifier-from-minibuffer
2212 (elpy-doc--symbol-at-point)))))
2214 (elpy-doc--show doc)
2215 (error "No documentation found"))))
2217 (defun elpy-doc--read-identifier-from-minibuffer (initial)
2218 "Read a pydoc-able identifier from the minibuffer."
2219 (completing-read "Pydoc for: "
2220 (completion-table-dynamic #'elpy-rpc-get-pydoc-completions)
2221 nil nil initial 'elpy-doc-history))
2223 (defun elpy-doc--show (documentation)
2224 "Show DOCUMENTATION to the user, replacing ^H with bold."
2225 (with-help-window "*Python Doc*"
2226 (with-current-buffer "*Python Doc*"
2228 (insert documentation)
2229 (goto-char (point-min))
2230 (while (re-search-forward "\\(.\\)
\b\\1" nil t)
2231 (replace-match (propertize (match-string 1)
2235 (defun elpy-doc--symbol-at-point ()
2236 "Return the Python symbol at point, including dotted paths."
2237 (with-syntax-table python-dotty-syntax-table
2238 (let ((symbol (symbol-at-point)))
2240 (symbol-name symbol)
2244 ;;; Buffer manipulation
2246 (defun elpy-buffer--replace-block (spec)
2247 "Replace a block. SPEC is (startline endline newblock)."
2248 (let ((start-line (nth 0 spec))
2249 (end-line (nth 1 spec))
2250 (new-block (nth 2 spec)))
2254 (goto-char (point-min))
2255 (forward-line start-line)
2257 (end (progn (forward-line (- end-line start-line)) (point))))
2258 ;; Avoid deleting and re-inserting when the blocks are equal.
2259 (unless (string-equal (buffer-substring beg end) new-block)
2260 (delete-region beg end)
2261 (insert new-block)))))))
2263 (defun elpy-buffer--replace-region (beg end rep)
2264 "Replace text in BUFFER in region (BEG END) with REP."
2265 (unless (string-equal (buffer-substring beg end) rep)
2269 (delete-region beg end))))
2271 ;;;;;;;;;;;;;;;;;;;;;
2272 ;;; Importmagic - make obsolete
2274 (defun elpy-importmagic-add-import ()
2276 (defun elpy-importmagic-fixup ()
2279 (make-obsolete 'elpy-importmagic-add-import "support for importmagic has been dropped." "1.17.0")
2280 (make-obsolete 'elpy-importmagic-fixup "support for importmagic has been dropped." "1.17.0")
2282 ;;;;;;;;;;;;;;;;;;;;;
2283 ;;; Code reformatting
2286 (defun elpy-format-code ()
2287 "Format code using the available formatter."
2289 (let ((elpy-formatter (or elpy-formatter
2291 (dolist (formatter '(yapf autopep8 black))
2292 (when (elpy-config--package-available-p
2294 (throw 'available formatter)))))))
2295 (unless elpy-formatter
2296 (error "No formatter installed, please install one using `elpy-config'"))
2297 (unless (elpy-config--package-available-p elpy-formatter)
2298 (error "The '%s' formatter is not installed, please install it using `elpy-config' or choose another one using `elpy-formatter'"
2300 (when (interactive-p) (message "Autoformatting code with %s."
2302 (funcall (intern (format "elpy-%s-fix-code" elpy-formatter)))))
2305 (defun elpy-yapf-fix-code ()
2306 "Automatically formats Python code with yapf.
2308 Yapf can be configured with a style file placed in the project
2311 (elpy--fix-code-with-formatter "fix_code_with_yapf"))
2313 (defun elpy-autopep8-fix-code ()
2314 "Automatically formats Python code to conform to the PEP 8 style guide.
2316 Autopep8 can be configured with a style file placed in the project
2319 (elpy--fix-code-with-formatter "fix_code"))
2321 (defun elpy-black-fix-code ()
2322 "Automatically formats Python code with black.
2323 Note: Requires 'toml' to be installed due to legacy reasons."
2325 (elpy--fix-code-with-formatter "fix_code_with_black"))
2327 (defun elpy--fix-code-with-formatter (method)
2328 "Common routine for formatting python code."
2329 (let ((line (line-number-at-pos))
2330 (col (current-column))
2331 (directory (if (elpy-project-root)
2332 (expand-file-name (elpy-project-root))
2333 default-directory)))
2335 (let ((new-block (elpy-rpc method
2336 (list (elpy-rpc--region-contents)
2338 (beg (region-beginning))
2340 (elpy-buffer--replace-region
2341 beg end (string-trim-right new-block))
2344 (let ((new-block (elpy-rpc method
2345 (list (elpy-rpc--buffer-contents)
2349 (elpy-buffer--replace-region beg end new-block)
2351 (forward-line (1- line))
2352 (forward-char col))))))
2357 (defvar elpy-multiedit-overlays nil
2358 "List of overlays currently being edited.")
2360 (defun elpy-multiedit-add-overlay (beg end)
2361 "Add an editable overlay between BEG and END.
2363 A modification in any of these overlays will modify all other
2366 (when (elpy-multiedit--overlays-in-p beg end)
2367 (error "Overlapping multiedit overlays are not allowed"))
2368 (let ((ov (make-overlay beg end nil nil :rear-advance)))
2369 (overlay-put ov 'elpy-multiedit t)
2370 (overlay-put ov 'face 'highlight)
2371 (overlay-put ov 'modification-hooks '(elpy-multiedit--overlay-changed))
2372 (overlay-put ov 'insert-in-front-hooks '(elpy-multiedit--overlay-changed))
2373 (overlay-put ov 'insert-behind-hooks '(elpy-multiedit--overlay-changed))
2374 (push ov elpy-multiedit-overlays)))
2376 (defun elpy-multiedit--overlays-in-p (beg end)
2377 "Return t iff there are multiedit overlays between beg and end."
2379 (dolist (ov (overlays-in beg end))
2380 (when (overlay-get ov 'elpy-multiedit)
2384 (defun elpy-multiedit-stop ()
2385 "Stop editing multiple places at once."
2387 (dolist (ov elpy-multiedit-overlays)
2388 (delete-overlay ov))
2389 (setq elpy-multiedit-overlays nil))
2391 (defun elpy-multiedit--overlay-changed (ov after-change _beg _end
2392 &optional _pre-change-length)
2393 "Called for each overlay that changes.
2395 This updates all other overlays."
2396 (when (and after-change
2397 (not undo-in-progress)
2398 (overlay-buffer ov))
2399 (let ((text (buffer-substring (overlay-start ov)
2401 (inhibit-modification-hooks t))
2402 (dolist (other-ov elpy-multiedit-overlays)
2403 (when (and (not (equal other-ov ov))
2404 (buffer-live-p (overlay-buffer other-ov)))
2405 (with-current-buffer (overlay-buffer other-ov)
2407 (goto-char (overlay-start other-ov))
2409 (delete-region (point) (overlay-end other-ov)))))))))
2411 (defun elpy-multiedit ()
2412 "Edit all occurences of the symbol at point, or the active region.
2414 If multiedit is active, stop it."
2416 (if elpy-multiedit-overlays
2417 (elpy-multiedit-stop)
2418 (let ((regex (if (use-region-p)
2419 (regexp-quote (buffer-substring (region-beginning)
2421 (format "\\_<%s\\_>" (regexp-quote
2423 (symbol-at-point))))))
2424 (case-fold-search nil))
2426 (goto-char (point-min))
2427 (while (re-search-forward regex nil t)
2428 (elpy-multiedit-add-overlay (match-beginning 0)
2431 (defun elpy-multiedit-python-symbol-at-point (&optional use-symbol-p)
2432 "Edit all usages of the the Python symbol at point.
2434 With prefix arg, edit all syntactic usages of the symbol at
2435 point. This might include unrelated symbols that just share the
2438 (if (or elpy-multiedit-overlays
2441 ;; If we are already doing a multiedit, or are explicitly told
2442 ;; to use the symbol at point, or if we are on an active region,
2443 ;; call the multiedit function that does just that already.
2444 (call-interactively 'elpy-multiedit)
2445 ;; Otherwise, fetch usages from backend.
2447 (let ((usages (condition-case err
2448 (elpy-rpc-get-usages)
2449 ;; This is quite the stunt, but elisp parses JSON
2450 ;; null as nil, which is indistinguishable from
2451 ;; the empty list, we stick to the error.
2453 (if (and (eq (car err) 'error)
2454 (stringp (cadr err))
2455 (string-match "not implemented" (cadr err)))
2457 (error (cadr err)))))))
2459 ((eq usages 'not-supported)
2460 (call-interactively 'elpy-multiedit)
2461 (message (concat "Using syntactic editing "
2462 "as current backend does not support get_usages.")))
2464 (call-interactively 'elpy-multiedit)
2465 (if elpy-multiedit-overlays
2466 (message (concat "Using syntactic editing as no usages of the "
2467 "symbol at point were found by the backend."))
2468 (message "No occurrences of the symbol at point found")))
2472 (elpy-multiedit--usages usages)))))))
2474 (defun elpy-multiedit--usages (usages)
2475 "Mark the usages in USAGES for editing."
2477 (locations (make-hash-table :test #'equal)))
2478 (dolist (usage usages)
2479 (let* ((filename (cdr (assq 'filename usage)))
2480 (this-name (cdr (assq 'name usage)))
2481 (offset (cdr (assq 'offset usage))))
2482 (setq name this-name)
2483 (with-current-buffer (if filename
2484 (find-file-noselect filename)
2486 (elpy-multiedit-add-overlay (+ offset 1)
2487 (+ offset 1 (length this-name)))
2489 (goto-char (+ offset 1))
2492 (buffer-substring (line-beginning-position)
2493 (line-end-position))
2495 (line-beginning-position))
2496 (- (+ (point) (length this-name))
2497 (line-beginning-position)))
2498 (gethash filename locations))
2500 (if (<= (hash-table-count locations)
2502 (message "Editing %s usages of '%s' in this buffer"
2503 (length usages) name)
2504 (with-current-buffer (get-buffer-create "*Elpy Edit Usages*")
2505 (let ((inhibit-read-only t)
2509 "The symbol '" name "' was found in multiple files. Editing "
2510 "all locations:\n\n")
2511 (maphash (lambda (key _value)
2512 (unless (member key filenames)
2513 (setq filenames (cons key filenames))))
2515 (dolist (filename (sort filenames #'string<))
2516 (elpy-insert--header filename)
2517 (dolist (location (sort (gethash filename locations)
2521 (let ((line (nth 1 location))
2522 (start (+ (line-beginning-position)
2524 (end (+ (line-end-position)
2526 ;; Insert the \n first, else we extend the overlay.
2528 (elpy-multiedit-add-overlay start end)))
2530 (goto-char (point-min))
2531 (display-buffer (current-buffer)
2535 ;;;;;;;;;;;;;;;;;;;;;
2536 ;;; Occur Definitions
2538 (defun elpy-occur-definitions ()
2539 "Display an occur buffer of all definitions in the current buffer.
2541 Also, switch to that buffer."
2543 (let ((list-matching-lines-face nil))
2544 (occur "^\s*\\(\\(async\s\\|\\)def\\|class\\)\s"))
2545 (let ((window (get-buffer-window "*Occur*")))
2547 (select-window window)
2548 (switch-to-buffer "*Occur*"))))
2553 (defun elpy--xref-backend ()
2554 "Return the name of the elpy xref backend."
2555 ;; If no rpc available, start one and assume jedi is available
2556 (if (or (and (not (elpy-rpc--process-buffer-p elpy-rpc--buffer))
2557 (elpy-rpc--get-rpc-buffer))
2558 elpy-rpc--jedi-available)
2562 (defvar elpy-xref--format-references
2563 (let ((str "%s:\t%s"))
2564 (put-text-property 0 4 'face 'compilation-line-number str)
2566 "String used to format references in xref buffers.")
2568 ;; Elpy location structure
2569 (when (featurep 'xref)
2570 (cl-defstruct (xref-elpy-location
2571 (:constructor xref-make-elpy-location (file pos)))
2572 "Location of a python symbol definition."
2575 (defun xref-make-elpy-location (file pos)
2576 "Return an elpy location structure.
2577 Points to file FILE, at position POS."
2578 (make-instance 'xref-etags-location
2579 :file (expand-file-name file)
2582 (cl-defmethod xref-location-marker ((l xref-elpy-location))
2583 (with-current-buffer (find-file-noselect (xref-elpy-location-file l))
2585 (goto-char (xref-elpy-location-pos l))
2588 (cl-defmethod xref-location-group ((l xref-elpy-location))
2589 (xref-elpy-location-file l))
2592 (cl-defmethod xref-backend-identifier-at-point ((_backend (eql elpy)))
2593 (elpy-xref--identifier-at-point))
2595 (defun elpy-xref--identifier-at-point ()
2596 "Return identifier at point.
2598 Is a string, formatted as \"LINE_NUMBER: VARIABLE_NAME\".
2600 (let* ((symb (symbol-at-point))
2601 (symb-str (substring-no-properties (symbol-name symb))))
2603 (format "%s: %s" (line-number-at-pos) symb-str))))
2605 (defun elpy-xref--identifier-name (id)
2606 "Return the identifier ID variable name."
2607 (string-match ".*: \\([^:]*\\)" id)
2608 (match-string 1 id))
2610 (defun elpy-xref--identifier-line (id)
2611 "Return the identifier ID line number."
2612 (string-match "\\([^:]*\\):.*" id)
2613 (string-to-number (match-string 1 id)))
2615 (defun elpy-xref--goto-identifier (id)
2616 "Goto the identifier ID in the current buffer.
2617 This is needed to get information on the identifier with jedi
2618 \(that work only on the symbol at point\)"
2619 (let ((case-fold-search nil))
2620 (goto-char (point-min))
2621 (forward-line (1- (elpy-xref--identifier-line id)))
2622 (search-forward (elpy-xref--identifier-name id) (line-end-position))
2623 (goto-char (match-beginning 0))))
2626 (cl-defmethod xref-backend-definitions ((_backend (eql elpy)) id)
2627 (elpy-xref--definitions id))
2629 (defun elpy-xref--definitions (id)
2630 "Return SYMBOL definition position as a xref object."
2632 (elpy-xref--goto-identifier id)
2633 (let* ((location (elpy-rpc-get-definition)))
2635 (error "No definition found")
2636 (let* ((file (expand-file-name (car location)))
2637 (pos (+ 1 (cadr location)))
2638 (line (with-current-buffer (find-file-noselect file)
2639 (goto-char (+ pos 1))
2640 (buffer-substring (line-beginning-position) (line-end-position))))
2641 (linenumber (with-current-buffer (find-file-noselect file)
2642 (line-number-at-pos pos)))
2643 (summary (format elpy-xref--format-references linenumber line))
2644 (loc (xref-make-elpy-location file pos)))
2645 (list (xref-make summary loc)))))))
2648 (cl-defmethod xref-backend-references ((_backend (eql elpy)) id)
2649 (elpy-xref--references id))
2651 (defun elpy-xref--references (id)
2652 "Return SYMBOL references as a list of xref objects."
2654 (elpy-xref--goto-identifier id)
2655 (let* ((references (elpy-rpc-get-usages)))
2657 for ref in references
2658 for file = (alist-get 'filename ref)
2659 for pos = (+ (alist-get 'offset ref) 1)
2660 for line = (when file
2661 (with-current-buffer (find-file-noselect file)
2663 (goto-char (+ pos 1))
2664 (buffer-substring (line-beginning-position) (line-end-position)))))
2665 for linenumber = (when file
2666 (with-current-buffer (find-file-noselect file)
2667 (line-number-at-pos pos)))
2668 for summary = (format elpy-xref--format-references linenumber line)
2669 for loc = (xref-make-elpy-location file pos)
2671 collect (xref-make summary loc)))))
2673 ;; Completion table (used when calling `xref-find-references`)
2674 (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql elpy)))
2675 (elpy-xref--get-completion-table))
2677 (defun elpy-xref--get-completion-table ()
2678 "Return the completion table for identifiers."
2680 for ref in (nreverse (elpy-rpc-get-names))
2681 for offset = (+ (alist-get 'offset ref) 1)
2682 for line = (line-number-at-pos offset)
2683 for id = (format "%s: %s" line (alist-get 'name ref))
2687 (cl-defmethod xref-backend-apropos ((_backend (eql elpy)) regex)
2688 (elpy-xref--apropos regex))
2690 (defun elpy-xref--apropos (regex)
2691 "Return identifiers matching REGEX."
2692 (let ((ids (elpy-rpc-get-names))
2693 (case-fold-search nil))
2696 for name = (alist-get 'name id)
2697 for filename = (alist-get 'filename id)
2698 for pos = (+ (alist-get 'offset id) 1)
2699 if (string-match regex name)
2700 collect (with-current-buffer (find-file-noselect filename)
2703 (let* ((linenumber (line-number-at-pos))
2704 (line (buffer-substring
2705 (line-beginning-position)
2706 (line-end-position)))
2707 (summary (format elpy-xref--format-references linenumber line))
2708 (loc (xref-make-elpy-location filename pos)))
2709 (xref-make summary loc)))))))
2715 (defvar elpy-modules-initialized-p nil
2716 "Boolean, set to true if modules were run with `global-init'.")
2718 (defun elpy-modules-run (command &rest args)
2719 "Run COMMAND with ARGS for all modules in `elpy-modules'."
2720 (dolist (module elpy-modules)
2721 (apply module command args)))
2723 (defun elpy-modules-global-init ()
2724 "Run the global-init method of Elpy modules.
2726 Make sure this only happens once."
2727 (unless elpy-modules-initialized-p
2728 (elpy-modules-run 'global-init)
2729 (setq elpy-modules-initialized-p t)))
2731 (defun elpy-modules-global-stop ()
2732 "Run the global-stop method of Elpy modules.
2734 Make sure this only happens once per global-init call."
2735 (when elpy-modules-initialized-p
2736 (elpy-modules-run 'global-stop)
2737 (setq elpy-modules-initialized-p nil)))
2739 (defun elpy-modules-buffer-init ()
2740 "Run the buffer-init method of Elpy modules.
2742 Make sure global-init is called first."
2743 (elpy-modules-global-init)
2744 (elpy-modules-run 'buffer-init))
2746 (defun elpy-modules-buffer-stop ()
2747 "Run the buffer-stop method of Elpy modules."
2748 (elpy-modules-run 'buffer-stop))
2750 (defun elpy-modules-remove-modeline-lighter (modename)
2751 "Remove the lighter for MODENAME.
2753 It should not be necessary to see (Python Elpy yas company ElDoc) all the
2756 If you need your modeline, you can set the variable `elpy-remove-modeline-lighter' to nil"
2759 (when elpy-remove-modeline-lighter
2761 ((eq modename 'eldoc-minor-mode)
2762 (setq eldoc-minor-mode-string nil))
2764 (let ((cell (assq modename minor-mode-alist)))
2769 ;;;;;;;;;;;;;;;;;;;;;;;;;
2770 ;;; Module: Sane Defaults
2772 (defun elpy-module-sane-defaults (command &rest _args)
2773 "Module for sane Emacs default for python."
2776 ;; Set `forward-sexp-function' to nil in python-mode. See
2777 ;; http://debbugs.gnu.org/db/13/13642.html
2778 (set (make-local-variable 'forward-sexp-function) nil)
2779 ;; PEP8 recommends two spaces in front of inline comments.
2780 (set (make-local-variable 'comment-inline-offset) 2))
2782 (kill-local-variable 'forward-sexp-function)
2783 (kill-local-variable 'comment-inline-offset))))
2788 (defun elpy-module-company (command &rest _args)
2789 "Module to support company-mode completions."
2793 (require 'company-capf)
2794 (elpy-modules-remove-modeline-lighter 'company-mode)
2795 (define-key company-active-map (kbd "C-d")
2796 'company-show-doc-buffer)
2797 (add-hook 'inferior-python-mode-hook
2799 ;; Workaround for company bug
2800 ;; (https://github.com/company-mode/company-mode/issues/759)
2801 (setq-local company-transformers
2802 (remove 'company-sort-by-occurrence
2803 company-transformers))
2804 ;; Be sure to trigger completion for one character variable
2806 (setq-local company-minimum-prefix-length 2))))
2809 ;; We want immediate completions from company.
2810 (set (make-local-variable 'company-idle-delay)
2812 ;; And annotations should be right-aligned.
2813 (set (make-local-variable 'company-tooltip-align-annotations)
2815 ;; Also, dabbrev in comments and strings is nice.
2816 (set (make-local-variable 'company-dabbrev-code-everywhere)
2818 ;; Add our own backend and remove a bunch of backends that
2819 ;; interfere in Python mode.
2820 (set (make-local-variable 'company-backends)
2821 (cons 'elpy-company-backend
2822 (delq 'company-semantic
2823 (delq 'company-ropemacs
2825 (mapcar #'identity company-backends))))))
2827 (when (> (buffer-size) elpy-rpc-ignored-buffer-size)
2829 (concat "Buffer %s larger than elpy-rpc-ignored-buffer-size (%d)."
2830 " Elpy will turn off completion.")
2831 (buffer-name) elpy-rpc-ignored-buffer-size)))
2834 (kill-local-variable 'company-idle-delay)
2835 (kill-local-variable 'company-tooltip-align-annotations)
2836 (kill-local-variable 'company-backends))
2839 (defvar elpy-company--cache nil
2840 "Buffer-local cache for candidate information.")
2841 (make-variable-buffer-local 'elpy-company--cache)
2843 (defun elpy-company--cache-clear ()
2844 "Clear and initialize the cache."
2845 (if elpy-company--cache
2846 (clrhash elpy-company--cache)
2847 (setq elpy-company--cache
2848 (make-hash-table :test #'equal))))
2850 (defun elpy-company--cache-annotation (name)
2851 "Return the cached annotation for NAME."
2852 (when elpy-company--cache
2853 (cdr (assq 'annotation (gethash name elpy-company--cache)))))
2855 (defun elpy-company--cache-meta (name)
2856 "Return the cached annotation for NAME."
2857 (when elpy-company--cache
2858 (cdr (assq 'meta (gethash name elpy-company--cache)))))
2860 (defun elpy-company--cache-name (name)
2861 "Return the cached name for NAME.
2863 Confused yet? We pass \"our\" name, that is, prefix + suffix,
2864 here, and return the \"name\" as used by the backend."
2865 (when elpy-company--cache
2866 (cdr (assq 'name (gethash name elpy-company--cache)))))
2868 (defun elpy-company--cache-completions (prefix result)
2869 "Store RESULT in the candidate cache and return candidates."
2870 (elpy-company--cache-clear)
2871 (mapcar (lambda (completion)
2872 (let* ((suffix (cdr (assq 'name completion)))
2873 (name (concat (s-chop-suffix (company-grab-symbol) prefix) suffix)))
2874 (puthash name completion elpy-company--cache)
2878 (defun elpy-company--python-exception-p (name)
2879 "Check whether NAME is a Python exception."
2880 (member name '("ArithmeticError"
2888 "ConnectionAbortedError"
2890 "ConnectionRefusedError"
2891 "ConnectionResetError"
2892 "DeprecationWarning"
2898 "FloatingPointError"
2911 "NotADirectoryError"
2912 "NotImplementedError"
2915 "PendingDeprecationWarning"
2917 "ProcessLookupError"
2924 "StopAsyncIteration"
2933 "UnicodeDecodeError"
2934 "UnicodeEncodeError"
2936 "UnicodeTranslateError"
2941 "ZeroDivisionError")))
2943 (defun elpy-company-post-complete-parens (annotation name)
2944 "Complete functions, classes, and callable instances with parentheses.
2946 Add parentheses in case ANNOTATION is \"class\", \"function\", or
2947 \"instance\",unless the completion is already looking at a left
2948 parenthesis,or unless NAME is a Python exception outside a reasonably
2949 formed raise statement,or unless NAME is no callable instance."
2950 (unless (looking-at-p "\(")
2951 (cond ((string= annotation "function")
2954 ((string= annotation "class")
2955 (cond ((elpy-company--python-exception-p name)
2956 (when (save-excursion
2958 (looking-at "\\_<raise\\_>"))
2963 (backward-char 1))))
2964 ((string= annotation "instance")
2965 ;; The jedi backend annotates some callables as instances (e.g. numpy
2966 ;; and scipy) and `elpy-company--cache' does not allow to identify
2967 ;; callable instances.
2968 ;; It looks easy to modify `elpy-company--cache' cheaply for the jedi
2969 ;; backend to eliminate the `elpy-rpc-get-calltip' call below.
2972 (unless (elpy-rpc-get-calltip)
2974 (delete-char 2))))))
2976 (defun elpy-company--add-interpreter-completions-candidates (candidates)
2977 "Add completions candidates from the shell to the list of candidates.
2979 Get completions candidates at point from the shell, normalize them to look
2980 like what elpy-company returns, merge them with the CANDIDATES list
2981 and return the list."
2982 ;; Check if prompt available
2983 (if (not (and elpy-get-info-from-shell
2984 (elpy-shell--check-if-shell-available)))
2986 ;; Completion need the cursor to be at the end of the shell buffer
2988 (with-current-buffer (process-buffer (python-shell-get-process))
2989 (goto-char (point-max)))
2990 ;; Try to get the info with timeout
2991 (let* ((new-candidates (with-timeout (elpy-get-info-from-shell-timeout
2993 (python-completion-complete-at-point)))
2994 (start (nth 0 new-candidates))
2995 (end (nth 1 new-candidates))
2996 (completion-list (nth 2 new-candidates)))
2997 (if (not (and start end))
2999 ;; Add the new candidates to the current ones
3000 (let ((candidate-names (cl-map 'list
3001 (lambda (el) (cdr (assoc 'name el)))
3003 (new-candidate-names (all-completions
3004 (buffer-substring start end)
3007 for pytel-cand in new-candidate-names
3008 for pytel-cand = (replace-regexp-in-string "($" "" pytel-cand)
3009 for pytel-cand = (replace-regexp-in-string "^.*\\." "" pytel-cand)
3010 for pytel-cand = (string-trim pytel-cand)
3011 unless (member pytel-cand candidate-names)
3012 do (push (list (cons 'name pytel-cand)) candidates)
3016 (defun elpy-company-backend (command &optional arg &rest ignored)
3017 "A company-mode backend for Elpy."
3018 (interactive (list 'interactive))
3021 (company-begin-backend 'elpy-company-backend))
3022 ;; init => Called once per buffer
3023 ;; prefix => return the prefix at point
3025 (when (and elpy-mode
3026 (not (company-in-string-or-comment)))
3027 (company-grab-symbol-cons "\\." 1)))
3028 ;; candidates <prefix> => return candidates for this prefix
3032 (elpy-rpc-get-completions
3034 ;; add completion candidates from python.el
3036 (elpy-company--add-interpreter-completions-candidates
3038 (elpy-company--cache-clear)
3042 ;; The backend returned something
3044 (elpy-company--cache-completions arg result))
3045 ;; Nothing from the backend, try dabbrev-code.
3046 ((> (length arg) company-minimum-prefix-length)
3047 (elpy--sort-and-strip-duplicates
3048 (company-dabbrev-code 'candidates arg)))
3049 ;; Well, ok, let's go meh.
3052 ;; sorted => t if the list is already sorted
3055 ;; duplicates => t if there could be duplicates
3058 ;; no-cache <prefix> => t if company shouldn't cache results
3059 ;; meta <candidate> => short docstring for minibuffer
3061 (let ((meta (elpy-company--cache-meta arg)))
3063 (string-match "\\`\\(.*\n.*\\)\n.*" meta))
3064 (setq meta (match-string 1 meta)))
3066 ;; annotation <candidate> => short docstring for completion buffer
3068 (elpy-company--cache-annotation arg))
3069 ;; doc-buffer <candidate> => put doc buffer in `company-doc-buffer'
3071 (let* ((name (elpy-company--cache-name arg))
3073 (elpy-rpc-get-completion-docstring name))))
3075 (company-doc-buffer doc))))
3076 ;; require-match => Never require a match, even if the user
3077 ;; started to interact with company. See `company-require-match'.
3080 ;; location <candidate> => (buffer . point) or (file .
3083 (let* ((name (elpy-company--cache-name arg))
3085 (elpy-rpc-get-completion-location name))))
3089 ;; match <candidate> => for non-prefix based backends
3090 ;; post-completion <candidate> => after insertion, for snippets
3092 (funcall elpy-company-post-completion-function
3093 (elpy-company--cache-annotation arg)
3094 (elpy-company--cache-name arg)))))
3096 (defun elpy--sort-and-strip-duplicates (seq)
3097 "Sort SEQ and remove any duplicates."
3098 (sort (delete-dups seq)
3105 (defun elpy-module-eldoc (command &rest _args)
3106 "Module to support ElDoc for Python files."
3110 (elpy-modules-remove-modeline-lighter 'eldoc-minor-mode))
3112 (eldoc-add-command-completions "python-indent-dedent-line-backspace")
3113 (set (make-local-variable 'company-frontends)
3114 (remq 'company-echo-metadata-frontend company-frontends))
3115 ;; New (Emacs >= 28) vs old eldoc API
3116 (if (and (version< "28.0.0" emacs-version)
3117 (boundp 'eldoc-documentation-functions))
3118 (add-hook 'eldoc-documentation-functions
3119 'elpy-eldoc-documentation nil t)
3120 (set (make-local-variable 'eldoc-documentation-function)
3121 'elpy-eldoc-documentation))
3125 (kill-local-variable 'eldoc-documentation-function))))
3127 (defun elpy-eldoc-documentation (&optional callback &rest _more)
3128 "Return some interesting information for the code at point.
3130 This function is meant to be added to `eldoc-documentation-functions'
3131 \(for Emacs >= 28) or set in `eldoc-documentation-function' (for older
3134 This will return flymake errors for the line at point if there are
3135 any. If not, this will do an asynchronous call to the RPC backend to
3136 get a call tip, and display that using `eldoc-message'. If the backend
3137 has no call tip, this will display the current class and method
3140 If specified, CALLBACK is the function called to display the
3141 documentation (only used for Emacs >= 28)."
3142 (let ((cb (or callback
3143 (lambda (doc &rest plist)
3144 (let ((thing (plist-get plist :thing))
3145 (face (plist-get plist :face)))
3147 (setq thing (format "%s: "
3150 'font-lock-function-name-face)))
3152 (if (version<= emacs-version "25")
3153 (format "%s%s" thing doc)
3154 (let ((eldoc-echo-area-use-multiline-p nil))
3155 (eldoc-docstring-format-sym-doc thing doc)))))
3156 (eldoc-message doc)))))
3157 (flymake-error (elpy-flymake-error-at-point)))
3160 (elpy-rpc-get-calltip-or-oneline-docstring
3163 ;; INFO is a string, just display it
3166 ;; INFO is a calltip
3167 ((string= (cdr (assq 'kind info)) "calltip")
3168 (let ((name (cdr (assq 'name info)))
3169 (index (cdr (assq 'index info)))
3170 (params (cdr (assq 'params info))))
3172 (setf (nth index params)
3173 (propertize (nth index params)
3175 'eldoc-highlight-function-argument)))
3176 (let* ((prefix (propertize name 'face
3177 'font-lock-function-name-face))
3178 (args (format "%s(%s)" prefix
3179 (mapconcat #'identity params ", "))))
3180 (funcall cb args))))
3181 ;; INFO is a oneline docstring
3182 ((string= (cdr (assq 'kind info)) "oneline_doc")
3183 (let ((name (cdr (assq 'name info)))
3184 (docs (cdr (assq 'doc info))))
3187 :face 'font-lock-function-name-face)))
3188 ;; INFO is nil, maybe display the current function
3190 (when elpy-eldoc-show-current-function
3191 (let ((current-defun (python-info-current-defun)))
3196 (format "%s()" current-defun)
3197 'face 'font-lock-function-name-face))))))))))
3199 ;; New protocol: return non-nil, non-string
3201 ;; Old protocol: return the last message until we're done
3202 eldoc-last-message))))
3208 (defun elpy-module-folding (command &rest _args)
3209 "Module allowing code folding in Python."
3213 (elpy-modules-remove-modeline-lighter 'hs-minor-mode))
3217 (setq-local hs-set-up-overlay 'elpy-folding--display-code-line-counts)
3218 (setq-local hs-allow-nesting t)
3219 (define-key elpy-mode-map [left-fringe mouse-1]
3220 'elpy-folding--click-fringe)
3221 (define-key elpy-mode-map (kbd "<mouse-1>") 'elpy-folding--click-text)
3222 (elpy-folding--mark-foldable-lines)
3223 (add-to-list 'after-change-functions 'elpy-folding--mark-foldable-lines))
3227 (kill-local-variable 'hs-set-up-overlay)
3228 (kill-local-variable 'hs-allow-nesting)
3229 (define-key elpy-mode-map [left-fringe mouse-1] nil)
3230 (define-key elpy-mode-map (kbd "<mouse-1>") nil)
3231 (remove 'elpy-folding--mark-foldable-lines after-change-functions)
3232 (cl-loop for prop in '(elpy-hs-folded elpy-hs-foldable elpy-hs-fringe)
3233 do (remove-overlays (point-min) (point-max) prop t)))))
3235 ;; Fringe and folding indicators
3236 (when (fboundp 'define-fringe-bitmap)
3237 (define-fringe-bitmap 'elpy-folding-fringe-marker
3247 (define-fringe-bitmap 'elpy-folding-fringe-foldable-marker
3257 (defcustom elpy-folding-fringe-face 'elpy-folding-fringe-face
3258 "Face for folding bitmaps appearing on the fringe."
3262 (defface elpy-folding-fringe-face
3263 '((t (:inherit 'font-lock-comment-face
3264 :box (:line-width 1 :style released-button))))
3265 "Face for folding bitmaps appearing on the fringe."
3268 (defcustom elpy-folding-face 'elpy-folding-face
3269 "Face for the folded region indicator."
3273 (defface elpy-folding-face
3274 '((t (:inherit 'font-lock-comment-face :box t)))
3275 "Face for the folded region indicator."
3278 (defcustom elpy-folding-fringe-indicators t
3279 "Non-nil if Elpy should display folding fringe indicators."
3280 :set (lambda (var val) ;
3281 (set-default var val)
3282 (dolist (buffer (buffer-list))
3283 (when (and (with-current-buffer buffer elpy-mode)
3284 (member 'elpy-folding--mark-foldable-lines
3285 after-change-functions))
3286 (elpy-folding--mark-foldable-lines)))))
3288 (defvar elpy-folding-docstring-regex "[uU]?[rR]?\"\"\""
3289 "Regular expression matching docstrings openings and closings.")
3291 (defvar elpy-docstring-block-start-regexp
3292 "^\\s-*[uU]?[rR]?\"\"\"\n?\\s-*"
3293 "Version of `hs-block-start-regexp' for docstrings.")
3295 (defface elpy-codecell-boundary '((t :inherit 'highlight))
3296 "Face for elpy codecell boundary."
3301 (defun elpy-folding--display-code-line-counts (ov)
3302 "Display a folded region indicator with the number of folded lines.
3304 Meant to be used as `hs-set-up-overlay'."
3305 (let* ((marker-string "*fringe-dummy*")
3306 (marker-length (length marker-string)))
3308 ((eq 'code (overlay-get ov 'hs))
3309 (let* ((nmb-line (count-lines (overlay-start ov) (overlay-end ov)))
3310 (display-string (format "(%d)..." nmb-line)))
3312 (when elpy-folding-fringe-indicators
3313 (put-text-property 0 marker-length 'display
3314 (list 'left-fringe 'elpy-folding-fringe-marker
3315 'elpy-folding-fringe-face)
3317 (overlay-put ov 'before-string marker-string)
3318 (overlay-put ov 'elpy-hs-fringe t))
3319 ;; folding indicator
3320 (put-text-property 0 (length display-string)
3321 'face 'elpy-folding-face display-string)
3322 (put-text-property 0 (length display-string)
3323 'mouse-face 'highlight display-string)
3324 (overlay-put ov 'display display-string)
3325 (overlay-put ov 'elpy-hs-folded t)))
3326 ;; for docstring and comments, we don't display the number of line
3327 ((or (eq 'docstring (overlay-get ov 'hs))
3328 (eq 'comment (overlay-get ov 'hs)))
3329 (let ((display-string "..."))
3330 (put-text-property 0 (length display-string)
3331 'mouse-face 'highlight display-string)
3332 (overlay-put ov 'display display-string)
3333 (overlay-put ov 'elpy-hs-folded t))))))
3335 (defun elpy-folding--mark-foldable-lines (&optional beg end rm-text-len)
3336 "Add a fringe indicator for foldable lines.
3338 Meant to be used as a hook to `after-change-functions'."
3339 (when elpy-folding-fringe-indicators
3344 (narrow-to-region (progn (goto-char beg)
3345 (line-beginning-position))
3346 (progn (goto-char end)
3347 (line-end-position))))
3348 (remove-overlays (point-min) (point-max) 'elpy-hs-foldable t)
3349 (goto-char (point-min))
3350 (while (re-search-forward
3351 python-nav-beginning-of-defun-regexp nil t)
3352 (let* ((beg (line-beginning-position))
3353 (end (line-end-position))
3354 (ov (make-overlay beg end))
3355 (marker-string "*fringe-dummy*")
3356 (marker-length (length marker-string)))
3357 (when (version<= "25.2" emacs-version)
3358 (put-text-property 0 marker-length
3362 'elpy-folding-fringe-foldable-marker
3363 'elpy-folding-fringe-face)
3365 (overlay-put ov 'before-string marker-string))
3366 (overlay-put ov 'elpy-hs-foldable t))))))))
3368 ;; Mouse interaction
3369 (defun elpy-folding--click-fringe (event)
3370 "Hide or show block on fringe click."
3373 (when elpy-folding-fringe-indicators
3374 (mouse-set-point event)
3375 (let* ((folded (save-excursion
3377 (cl-remove-if-not (lambda (ov)
3378 (overlay-get ov 'elpy-hs-folded))
3379 (overlays-at (point)))))
3380 (foldable (cl-remove-if-not (lambda (ov)
3381 (overlay-get ov 'elpy-hs-foldable))
3382 (overlays-at (point)))))
3386 (hs-hide-block)))))))
3388 (defun elpy-folding--click-text (event)
3389 "Show block on click."
3393 (let ((window (posn-window (event-end event)))
3394 (pos (posn-point (event-end event))))
3395 (with-current-buffer (window-buffer window)
3397 (when (hs-overlay-at (point))
3399 (deactivate-mark)))))))
3401 ;; Hidding docstrings
3402 (defun elpy-folding--hide-docstring-region (beg end)
3403 "Hide a region from BEG to END, marking it as a docstring.
3405 BEG and END have to be respectively on the first and last line
3406 of the docstring, their values are adapted to hide only the
3409 ;; do not fold oneliners
3410 (when (not (save-excursion
3414 (concat elpy-folding-docstring-regex
3416 elpy-folding-docstring-regex)
3417 (line-end-position) t)))
3418 ;; get begining position (do not fold first doc line)
3421 (when (save-excursion
3424 (concat elpy-folding-docstring-regex
3426 (line-end-position) t))
3429 (back-to-indentation)
3431 (setq ov-beg (line-end-position)))
3435 (setq end (line-beginning-position))
3436 (setq ov-end (line-end-position)))
3437 (hs-discard-overlays ov-beg ov-end)
3438 (hs-make-overlay ov-beg ov-end 'docstring (- beg ov-beg) (- end ov-end))
3439 (run-hooks 'hs-hide-hook)
3442 (defun elpy-folding--hide-docstring-at-point ()
3443 "Hide the docstring at point."
3445 (let ((hs-block-start-regexp elpy-docstring-block-start-regexp))
3446 (when (and (python-info-docstring-p) (not (hs-already-hidden-p)))
3447 (let (beg end line-beg line-end)
3448 ;; Get first doc line
3449 (if (not (save-excursion (forward-line -1)
3450 (python-info-docstring-p)))
3451 (setq beg (line-beginning-position))
3454 (re-search-backward (concat "^[[:space:]]*"
3455 elpy-folding-docstring-regex)
3457 (setq beg (line-beginning-position)))
3458 ;; Go to docstring opening (to be sure to be inside the docstring)
3459 (re-search-forward elpy-folding-docstring-regex nil t)
3460 (setq line-beg (line-number-at-pos))
3462 (if (not (save-excursion (forward-line 1)
3463 (python-info-docstring-p)))
3465 (setq end (line-end-position))
3466 (setq line-end (line-number-at-pos)))
3467 (re-search-forward elpy-folding-docstring-regex nil t)
3468 (setq end (line-end-position))
3469 (setq line-end (line-number-at-pos)))
3470 ;; hide the docstring
3471 (when (not (= line-end line-beg))
3472 (elpy-folding--hide-docstring-region beg end)))))))
3474 (defun elpy-folding--show-docstring-at-point ()
3475 "Show docstring at point."
3477 (let ((hs-block-start-regexp elpy-docstring-block-start-regexp))
3478 (when (python-info-docstring-p)
3481 (defvar-local elpy-folding-docstrings-hidden nil
3482 "If docstrings are globally hidden or not.")
3484 (defun elpy-folding-toggle-docstrings ()
3485 "Fold or unfold every docstrings in the current buffer."
3487 (if (not hs-minor-mode)
3488 (message "Please enable the 'Folding module' to use this functionality.")
3491 (goto-char (point-min))
3492 (while (python-nav-forward-defun)
3493 (search-forward-regexp ")\\s-*:" nil t)
3495 (when (and (python-info-docstring-p)
3498 (search-forward-regexp elpy-folding-docstring-regex
3501 (back-to-indentation)
3502 ;; be sure not to act on invisible docstrings
3503 (unless (and (hs-overlay-at (point))
3504 (not (eq (overlay-get (hs-overlay-at (point)) 'hs)
3506 (if elpy-folding-docstrings-hidden
3507 (elpy-folding--show-docstring-at-point)
3508 (elpy-folding--hide-docstring-at-point)))))))
3509 (setq elpy-folding-docstrings-hidden (not elpy-folding-docstrings-hidden))))
3512 (defvar-local elpy-folding-comments-hidden nil
3513 "If comments are globally hidden or not.")
3515 (defun elpy-folding-toggle-comments ()
3516 "Fold or unfold every comment blocks in the current buffer."
3518 (if (not hs-minor-mode)
3519 (message "Please enable the 'Folding module' to use this functionality.")
3522 (goto-char (point-min))
3523 (while (comment-search-forward (point-max) t)
3524 ;; be suse not to act on invisible comments
3525 (unless (and (hs-overlay-at (point))
3526 (not (eq (overlay-get (hs-overlay-at (point)) 'hs)
3528 (if (not elpy-folding-comments-hidden)
3529 ;; be sure to be on a multiline comment
3530 (when (save-excursion (forward-line)
3531 (comment-only-p (line-beginning-position)
3532 (line-end-position)))
3535 (python-util-forward-comment (buffer-size))))
3536 (setq elpy-folding-comments-hidden (not elpy-folding-comments-hidden)))))
3539 ;; taken from https://www.emacswiki.org/emacs/HideShow
3540 (defun elpy-folding--hide-leafs (beg end)
3541 "Hide blocks that do not contain others blocks in region (BEG END)."
3545 ;; Find the current block beginning and end
3546 (when (hs-find-block-beginning)
3547 (setq beg (1+ (point)))
3548 (funcall hs-forward-sexp-func 1)
3549 (setq end (1- (point))))
3550 ;; Show all branches if nesting not allowed
3551 (unless hs-allow-nesting
3552 (hs-discard-overlays beg end))
3553 ;; recursively treat current block leafs
3556 (forward-comment (buffer-size))
3557 (and (< (point) end)
3558 (re-search-forward hs-block-start-regexp end t)))
3559 (setq pos (match-beginning hs-block-start-mdata-select))
3560 (if (elpy-folding--hide-leafs pos end)
3563 (hs-hide-block-at-point t)))
3566 (run-hooks 'hs-hide-hook)
3569 (defun elpy-folding-hide-leafs ()
3570 "Hide all blocks that do not contain other blocks."
3572 (if (not hs-minor-mode)
3573 (message "Please enable the 'Folding module' to use this functionality.")
3575 (let ((beg (save-excursion
3576 (goto-char (if (use-region-p) (region-beginning) (point-min)))
3577 (line-beginning-position)))
3578 (end (save-excursion
3579 (goto-char (if (use-region-p) (region-end) (point-max)))
3580 (1+ (line-end-position)))))
3581 (elpy-folding--hide-leafs beg end)))))
3584 (defun elpy-folding--hide-region (beg end)
3585 "Hide the region betwwen BEG and END."
3588 (let ((beg-eol (progn (goto-char beg) (line-end-position)))
3589 (end-eol (progn (goto-char end) (line-end-position))))
3590 (hs-discard-overlays beg-eol end-eol)
3591 (hs-make-overlay beg-eol end-eol 'code (- beg beg-eol) (- end end-eol))
3592 (run-hooks 'hs-hide-hook)
3593 (deactivate-mark)))))
3595 (defun elpy-folding-toggle-at-point ()
3596 "Fold/Unfold the block, comment or docstring at point.
3598 If a region is selected, fold that region."
3600 (if (not hs-minor-mode)
3601 (message "Please enable the 'Folding module' to use this functionality.")
3603 ;; Use selected region
3605 (elpy-folding--hide-region (region-beginning) (region-end))
3606 ;; Adapt starting regexp if on a docstring
3607 (let ((hs-block-start-regexp
3608 (if (python-info-docstring-p)
3609 elpy-docstring-block-start-regexp
3610 hs-block-start-regexp)))
3613 ((hs-already-hidden-p)
3615 ((python-info-docstring-p)
3616 (elpy-folding--hide-docstring-at-point))
3618 (hs-hide-block))))))))
3623 (defun elpy-module-flymake (command &rest _args)
3624 "Enable Flymake support for Python."
3628 ;; flymake modeline is quite useful for emacs > 26.1
3629 (when (version< emacs-version "26.1")
3630 (elpy-modules-remove-modeline-lighter 'flymake-mode))
3631 ;; Add our initializer function.
3632 (unless (version<= "26.1" emacs-version)
3633 (add-to-list 'flymake-allowed-file-name-masks
3634 '("\\.py\\'" elpy-flymake-python-init))))
3637 ;; Avoid fringes clash between flymake and folding indicators
3638 (if (and (member 'elpy-module-folding elpy-modules)
3639 elpy-folding-fringe-indicators)
3640 (setq-local flymake-fringe-indicator-position 'right-fringe)
3641 (setq-local flymake-fringe-indicator-position 'left-fringe))
3642 ;; For emacs > 26.1, python.el natively supports flymake,
3643 ;; so we just tell python.el to use the wanted syntax checker
3644 (when (version<= "26.1" emacs-version)
3645 (setq-local python-flymake-command
3646 (let ((command (split-string elpy-syntax-check-command)))
3647 (if (string= (file-name-nondirectory (car command))
3649 (append command '("-"))
3652 ;; `flymake-no-changes-timeout': The original value of 0.5 is too
3653 ;; short for Python code, as that will result in the current line
3654 ;; to be highlighted most of the time, and that's annoying. This
3655 ;; value might be on the long side, but at least it does not, in
3656 ;; general, interfere with normal interaction.
3657 (set (make-local-variable 'flymake-no-changes-timeout)
3660 ;; `flymake-start-syntax-check-on-newline': This should be nil for
3661 ;; Python, as;; most lines with a colon at the end will mean the
3662 ;; next line is always highlighted as error, which is not helpful
3663 ;; and mostly annoying.
3664 (set (make-local-variable 'flymake-start-syntax-check-on-newline)
3667 ;; Enable warning faces for flake8 output.
3668 ;; Useless for emacs >= 26.1, as warning are handled fine
3669 ;; COMPAT: Obsolete variable as of 24.4
3671 ((version<= "26.1" emacs-version)
3672 (setq-default python-flymake-msg-alist
3673 '(("^W[0-9]+" . :warning)
3674 ("^E[0-9]+" . :error))))
3675 ((boundp 'flymake-warning-predicate)
3676 (set (make-local-variable 'flymake-warning-predicate) "^W[0-9]"))
3678 (set (make-local-variable 'flymake-warning-re) "^W[0-9]")))
3680 ;; for emacs >= 26.1, elpy relies on `python-flymake-command`, and
3681 ;; doesn't need `python-check-command` anymore.
3682 (when (and (buffer-file-name)
3683 (or (version<= "26.1" emacs-version)
3684 (executable-find python-check-command)))
3688 (kill-local-variable 'flymake-no-changes-timeout)
3689 (kill-local-variable 'flymake-start-syntax-check-on-newline)
3690 ;; Disable warning faces for flake8 output.
3691 ;; Useless for emacs >= 26.1, as warning are handled fine
3692 ;; COMPAT: Obsolete variable as of 24.4
3694 ((version<= "26.1" emacs-version) t)
3695 ((boundp 'flymake-warning-predicate)
3696 (kill-local-variable 'flymake-warning-predicate))
3698 (kill-local-variable 'flymake-warning-re))))))
3701 (defun elpy-flymake-python-init ()
3702 ;; Make sure it's not a remote buffer as flymake would not work
3703 (unless (file-remote-p buffer-file-name)
3704 (let* ((temp-file (flymake-init-create-temp-buffer-copy
3705 'flymake-create-temp-inplace)))
3706 (list python-check-command
3708 ;; Run flake8 from / to avoid import problems (#169)
3711 (defun elpy-flymake-next-error ()
3712 "Move forward to the next Flymake error and show a description."
3714 (flymake-goto-next-error)
3715 (elpy-flymake-show-error))
3717 (defun elpy-flymake-previous-error ()
3718 "Move backward to the previous Flymake error and show a description."
3720 (flymake-goto-prev-error)
3721 (elpy-flymake-show-error))
3723 (defun elpy-flymake-show-error ()
3724 "Show the flymake error message at point."
3726 (let ((error-message (elpy-flymake-error-at-point)))
3728 (message "%s" error-message))))
3730 (defun elpy-flymake-error-at-point ()
3731 "Return the flymake error at point, or nil if there is none."
3732 (cond ((boundp 'flymake-err-info) ; emacs < 26
3733 (let* ((lineno (line-number-at-pos))
3734 (err-info (car (flymake-find-err-info flymake-err-info
3737 (mapconcat #'flymake-ler-text
3740 ((and (fboundp 'flymake-diagnostic-text)
3741 (fboundp 'flymake-diagnostics)) ; emacs >= 26
3742 (let ((diag (flymake-diagnostics (point))))
3744 (mapconcat #'flymake-diagnostic-text diag ", "))))))
3746 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3747 ;;; Module: Highlight Indentation
3749 (defun elpy-module-highlight-indentation (command &rest _args)
3750 "Module to highlight indentation in Python files."
3753 (require 'highlight-indentation))
3755 (highlight-indentation-mode 1))
3757 (highlight-indentation-mode -1))))
3762 (defun elpy-module-pyvenv (command &rest _args)
3763 "Module to display the current virtualenv in the mode line."
3770 ;;;;;;;;;;;;;;;;;;;;;
3771 ;;; Module: Yasnippet
3773 (defun elpy-module-yasnippet (command &rest _args)
3774 "Module to enable YASnippet snippets."
3777 (require 'yasnippet)
3778 (elpy-modules-remove-modeline-lighter 'yas-minor-mode)
3780 ;; We provide some YASnippet snippets. Add them.
3782 ;; yas-snippet-dirs can be a string for a single directory. Make
3783 ;; sure it's a list in that case so we can add our own entry.
3784 (unless (listp yas-snippet-dirs)
3785 (setq yas-snippet-dirs (list yas-snippet-dirs)))
3786 (add-to-list 'yas-snippet-dirs
3787 (concat (file-name-directory (locate-library "elpy"))
3791 ;; Now load yasnippets.
3794 (setq yas-snippet-dirs
3795 (delete (concat (file-name-directory (locate-library "elpy"))
3801 (yas-minor-mode -1))))
3803 ;;;;;;;;;;;;;;;;;;;;;;;;;;;
3804 ;;; Module: Elpy-Django
3806 (defun elpy-module-django (command &rest _args)
3807 "Module to provide Django support."
3810 (add-to-list 'elpy-project-root-finder-functions
3811 'elpy-project-find-django-root t))
3813 (setq elpy-project-root-finder-functions
3814 (remove 'elpy-project-find-django-root
3815 elpy-project-root-finder-functions)))
3817 (elpy-django-setup))
3821 ;;;;;;;;;;;;;;;;;;;;;;;;;;;
3824 (defun elpy-module-autodoc (command &rest _args)
3825 "Module to automatically update documentation."
3828 (add-hook 'pre-command-hook 'elpy-autodoc--pre-command nil t)
3829 (add-hook 'post-command-hook 'elpy-autodoc--post-command nil t)
3830 (make-local-variable 'company-frontends)
3831 (add-to-list 'company-frontends 'elpy-autodoc--frontend :append))
3833 (remove-hook 'pre-command-hook 'elpy-autodoc--pre-command t)
3834 (remove-hook 'post-command-hook 'elpy-autodoc--post-command t)
3835 (setq company-frontends (remove 'elpy-autodoc--frontend company-frontends)))))
3838 ;; Auto refresh documentation on cursor motion
3839 (defvar elpy-autodoc--timer nil
3840 "Timer to refresh documentation.")
3842 (defcustom elpy-autodoc-delay .5
3843 "The idle delay in seconds until documentation is refreshed automatically."
3844 :type '(choice (const :tag "immediate (0)" 0)
3845 (number :tag "seconds"))
3847 :group 'elpy-autodoc)
3849 (defun elpy-autodoc--pre-command ()
3850 "Cancel autodoc timer on user action."
3851 (when elpy-autodoc--timer
3852 (cancel-timer elpy-autodoc--timer)
3853 (setq elpy-autodoc--timer nil)))
3855 (defun elpy-autodoc--post-command ()
3856 "Set up autodoc timer after user action."
3857 (when elpy-autodoc-delay
3858 (setq elpy-autodoc--timer
3859 (run-with-timer elpy-autodoc-delay nil
3860 'elpy-autodoc--refresh-doc))))
3862 (defun elpy-autodoc--refresh-doc ()
3863 "Refresh the doc asynchronously with the symbol at point."
3864 (when (get-buffer-window "*Python Doc*")
3865 (elpy-rpc-get-docstring 'elpy-autodoc--show-doc
3866 (lambda (_reason) nil))))
3868 (defun elpy-autodoc--show-doc (doc)
3869 "Display DOC (if any) but only if the doc buffer is currently visible."
3870 (when (and doc (get-buffer-window "*Python Doc*"))
3871 (elpy-doc--show doc)))
3873 ;; Auto refresh documentation in company candidate selection
3874 (defun elpy-autodoc--frontend (command)
3875 "Elpy autodoc front-end for refreshing documentation."
3878 (when elpy-autodoc-delay
3879 (when elpy-autodoc--timer
3880 (cancel-timer elpy-autodoc--timer))
3881 (setq elpy-autodoc--timer
3882 (run-with-timer elpy-autodoc-delay
3884 'elpy-autodoc--refresh-doc-from-company))))
3886 (when elpy-autodoc--timer
3887 (cancel-timer elpy-autodoc--timer)))))
3889 (defun elpy-autodoc--refresh-doc-from-company ()
3890 "Refresh the doc asynchronously using the current company candidate."
3891 (let* ((symbol (nth company-selection company-candidates))
3892 (doc (elpy-rpc-get-completion-docstring symbol)))
3893 (elpy-autodoc--show-doc doc)))
3895 ;;;;;;;;;;;;;;;;;;;;;;;;;;;
3896 ;;; Backwards compatibility
3898 ;; TODO Some/most of this compatibility code can now go, as minimum required
3899 ;; Emacs version is 24.4.
3901 ;; Functions for Emacs 24 before 24.3
3902 (unless (fboundp 'python-info-current-defun)
3903 (defalias 'python-info-current-defun 'python-current-defun))
3905 (unless (fboundp 'python-nav-forward-statement)
3906 (defun python-nav-forward-statement (&rest ignored)
3907 "Function added in Emacs 24.3"
3908 (error "Enhanced Python navigation only available in Emacs 24.3+")))
3910 (unless (fboundp 'python-nav-backward-up-list)
3911 (defun python-nav-backward-up-list ()
3912 "Compatibility function for older Emacsen"
3914 (backward-up-list))))
3916 (unless (fboundp 'python-shell-calculate-exec-path)
3917 (defun python-shell-calculate-exec-path ()
3918 "Compatibility function for older Emacsen."
3921 (unless (fboundp 'python-shell-calculate-process-environment)
3922 (defun python-shell-calculate-process-environment ()
3923 "Compatibility function for older Emacsen."
3924 process-environment))
3926 (unless (fboundp 'python-shell-get-process-name)
3927 (defun python-shell-get-process-name (_dedicated)
3928 "Compatibility function for older Emacsen."
3931 (unless (fboundp 'python-shell-parse-command)
3932 (defun python-shell-parse-command ()
3933 "Compatibility function for older Emacsen."
3934 python-python-command))
3936 (unless (fboundp 'python-shell-send-buffer)
3937 (defun python-shell-send-buffer (&optional _arg)
3938 (python-send-buffer)))
3940 (unless (fboundp 'python-shell-send-string)
3941 (defalias 'python-shell-send-string 'python-send-string))
3943 (unless (fboundp 'python-indent-shift-right)
3944 (defalias 'python-indent-shift-right 'python-shift-right))
3946 (unless (fboundp 'python-indent-shift-left)
3947 (defalias 'python-indent-shift-left 'python-shift-left))
3949 ;; Emacs 24.2 made `locate-dominating-file' accept a predicate instead
3950 ;; of a string. Simply overwrite the current one, it's
3951 ;; backwards-compatible. The code below is taken from Emacs 24.3.
3952 (when (or (< emacs-major-version 24)
3953 (and (= emacs-major-version 24)
3954 (<= emacs-minor-version 2)))
3955 (defun locate-dominating-file (file name)
3956 "Look up the directory hierarchy from FILE for a directory containing NAME.
3957 Stop at the first parent directory containing a file NAME,
3958 and return the directory. Return nil if not found.
3959 Instead of a string, NAME can also be a predicate taking one argument
3960 \(a directory) and returning a non-nil value if that directory is the one for
3961 which we're looking."
3962 ;; We used to use the above locate-dominating-files code, but the
3963 ;; directory-files call is very costly, so we're much better off doing
3964 ;; multiple calls using the code in here.
3966 ;; Represent /home/luser/foo as ~/foo so that we don't try to look for
3967 ;; `name' in /home or in /.
3968 (setq file (abbreviate-file-name file))
3970 ;; `user' is not initialized outside the loop because
3971 ;; `file' may not exist, so we may have to walk up part of the
3972 ;; hierarchy before we find the "initial UID". Note: currently unused
3975 (while (not (or root
3977 ;; FIXME: Disabled this heuristic because it is sometimes
3979 ;; As a heuristic, we stop looking up the hierarchy of
3980 ;; directories as soon as we find a directory belonging
3981 ;; to another user. This should save us from looking in
3982 ;; things like /net and /afs. This assumes that all the
3983 ;; files inside a project belong to the same user.
3984 ;; (let ((prev-user user))
3985 ;; (setq user (nth 2 (file-attributes file)))
3986 ;; (and prev-user (not (equal user prev-user))))
3987 (string-match locate-dominating-stop-dir-regexp file)))
3988 (setq try (if (stringp name)
3989 (file-exists-p (expand-file-name name file))
3990 (funcall name file)))
3991 (cond (try (setq root file))
3992 ((equal file (setq file (file-name-directory
3993 (directory-file-name file))))
3995 (if root (file-name-as-directory root))))
3998 ;; highlight-indentation 0.5 does not use modes yet
3999 (unless (fboundp 'highlight-indentation-mode)
4000 (defun highlight-indentation-mode (on-or-off)
4002 ((and (> on-or-off 0)
4003 (not highlight-indent-active))
4004 (highlight-indentation))
4005 ((and (<= on-or-off 0)
4006 highlight-indent-active)
4007 (highlight-indentation)))))
4009 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25753#44
4010 (when (version< emacs-version "25.2")
4011 (defun python-shell-completion-native-try ()
4012 "Return non-nil if can trigger native completion."
4013 (let ((python-shell-completion-native-enable t)
4014 (python-shell-completion-native-output-timeout
4015 python-shell-completion-native-try-output-timeout))
4016 (python-shell-completion-native-get-completions
4017 (get-buffer-process (current-buffer))
4020 ;; python.el in Emacs 24 does not have functions to determine buffer encoding.
4021 ;; In these versions, we fix the encoding to utf-8 (safe choice when no encoding
4022 ;; is defined since Python 2 uses ASCII and Python 3 UTF-8).
4023 (unless (fboundp 'python-info-encoding)
4024 (defun python-info-encoding ()
4027 ;; first-prompt-hook has been added in emacs 25.
4028 ;; for earlier versions, make sure Elpy's setup code is
4029 ;; still send to the python shell.
4030 (unless (boundp 'python-shell-first-prompt-hook)
4031 (add-hook 'inferior-python-mode-hook
4033 (when (elpy-project-root)
4034 (let ((process (get-buffer-process (current-buffer))))
4035 (python-shell-send-string
4036 (format "import sys;sys.path.append('%s');del sys"
4037 (elpy-project-root))
4042 ;; Added in Emacs 25
4043 (unless (fboundp 'python-shell-comint-end-of-output-p)
4044 (defun python-shell-comint-end-of-output-p (output)
4045 "Return non-nil if OUTPUT is ends with input prompt."
4047 ;; XXX: It seems on macOS an extra carriage return is attached
4048 ;; at the end of output, this handles that too.
4051 ;; Remove initial caret from calculated regexp
4052 (replace-regexp-in-string
4053 (rx string-start ?^) ""
4054 python-shell--prompt-calculated-input-regexp)
4058 (unless (fboundp 'python-info-docstring-p)
4059 (defun python-info-docstring-p (&optional syntax-ppss)
4060 "Return non-nil if point is in a docstring."
4062 (and (progn (python-nav-beginning-of-statement)
4063 (looking-at "\\(\"\\|'\\)"))
4064 (progn (forward-line -1)
4066 (python-info-looking-at-beginning-of-defun))))))
4068 (unless (fboundp 'alist-get)
4069 (defun alist-get (key alist &rest rest)
4070 (cdr (assoc key alist))))
4073 ;;; elpy.el ends here