aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/python.el
diff options
context:
space:
mode:
authorLiu Hui2024-01-18 12:00:00 +0800
committerEli Zaretskii2024-02-08 14:09:42 +0200
commit0b9c7148fd681c8ad63fd0eb3895db44403e9f8c (patch)
treed454370049b289722ba06e2777e6006fcf762917 /lisp/progmodes/python.el
parentebf4ef2022a5f0a69cdd881eb41104e7b59d698e (diff)
downloademacs-0b9c7148fd681c8ad63fd0eb3895db44403e9f8c.tar.gz
emacs-0b9c7148fd681c8ad63fd0eb3895db44403e9f8c.zip
Respect the delimiter of completer in Python shell completion
* lisp/progmodes/python.el: (python-shell-completion-setup-code): Fix the completion code of IPython. Change the return value to JSON string and ... (python-shell-completion-get-completions): ... simplify parsing. (inferior-python-mode): Update docstring. (python-shell-readline-completer-delims): New variable indicating the word delimiters of readline completer. (python-shell-completion-native-setup): Set the completer delimiter. (python-shell-completion-native-get-completions): Convert output string to completions properly. (python-shell--get-multiline-input) (python-shell--extra-completion-context) (python-shell-completion-extra-context): New functions. (python-shell-completion-at-point): Send text beginning from the line start if the completion backend does not need word splitting. Remove the detection of import statement because it is not needed anymore. Create proper completion table based on completions returned from different backends. * test/lisp/progmodes/python-tests.el (python-tests--completion-module) (python-tests--completion-parameters) (python-tests--completion-extra-context): New helper functions. (python-shell-completion-at-point-jedi-completer) (python-shell-completion-at-point-ipython): New tests. (bug#68559)
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r--lisp/progmodes/python.el220
1 files changed, 172 insertions, 48 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 9d840efb9da..b1654b6a5aa 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -5,7 +5,7 @@
5;; Author: Fabián E. Gallina <fgallina@gnu.org> 5;; Author: Fabián E. Gallina <fgallina@gnu.org>
6;; URL: https://github.com/fgallina/python.el 6;; URL: https://github.com/fgallina/python.el
7;; Version: 0.28 7;; Version: 0.28
8;; Package-Requires: ((emacs "24.4") (compat "28.1.2.1") (seq "2.23")) 8;; Package-Requires: ((emacs "24.4") (compat "29.1.1.0") (seq "2.23"))
9;; Maintainer: emacs-devel@gnu.org 9;; Maintainer: emacs-devel@gnu.org
10;; Created: Jul 2010 10;; Created: Jul 2010
11;; Keywords: languages 11;; Keywords: languages
@@ -128,9 +128,9 @@
128;; receiving escape sequences (with some limitations, i.e. completion 128;; receiving escape sequences (with some limitations, i.e. completion
129;; in blocks does not work). The code executed for the "fallback" 129;; in blocks does not work). The code executed for the "fallback"
130;; completion can be found in `python-shell-completion-setup-code' and 130;; completion can be found in `python-shell-completion-setup-code' and
131;; `python-shell-completion-string-code' variables. Their default 131;; `python-shell-completion-get-completions'. Their default values
132;; values enable completion for both CPython and IPython, and probably 132;; enable completion for both CPython and IPython, and probably any
133;; any readline based shell (it's known to work with PyPy). If your 133;; readline based shell (it's known to work with PyPy). If your
134;; Python installation lacks readline (like CPython for Windows), 134;; Python installation lacks readline (like CPython for Windows),
135;; installing pyreadline (URL `https://ipython.org/pyreadline.html') 135;; installing pyreadline (URL `https://ipython.org/pyreadline.html')
136;; should suffice. To troubleshoot why you are not getting any 136;; should suffice. To troubleshoot why you are not getting any
@@ -141,6 +141,12 @@
141;; If you see an error, then you need to either install pyreadline or 141;; If you see an error, then you need to either install pyreadline or
142;; setup custom code that avoids that dependency. 142;; setup custom code that avoids that dependency.
143 143
144;; By default, the "native" completion uses the built-in rlcompleter.
145;; To use other readline completer (e.g. Jedi) or a custom one, you just
146;; need to set it in the PYTHONSTARTUP file. You can set an
147;; Emacs-specific completer by testing the environment variable
148;; INSIDE_EMACS.
149
144;; Shell virtualenv support: The shell also contains support for 150;; Shell virtualenv support: The shell also contains support for
145;; virtualenvs and other special environment modifications thanks to 151;; virtualenvs and other special environment modifications thanks to
146;; `python-shell-process-environment' and `python-shell-exec-path'. 152;; `python-shell-process-environment' and `python-shell-exec-path'.
@@ -3604,7 +3610,6 @@ interpreter is run. Variables
3604`python-shell-prompt-block-regexp', 3610`python-shell-prompt-block-regexp',
3605`python-shell-font-lock-enable', 3611`python-shell-font-lock-enable',
3606`python-shell-completion-setup-code', 3612`python-shell-completion-setup-code',
3607`python-shell-completion-string-code',
3608`python-eldoc-setup-code', 3613`python-eldoc-setup-code',
3609`python-ffap-setup-code' can 3614`python-ffap-setup-code' can
3610customize this mode for different Python interpreters. 3615customize this mode for different Python interpreters.
@@ -4244,8 +4249,9 @@ def __PYTHON_EL_get_completions(text):
4244 completions = [] 4249 completions = []
4245 completer = None 4250 completer = None
4246 4251
4252 import json
4247 try: 4253 try:
4248 import readline 4254 import readline, re
4249 4255
4250 try: 4256 try:
4251 import __builtin__ 4257 import __builtin__
@@ -4256,16 +4262,29 @@ def __PYTHON_EL_get_completions(text):
4256 4262
4257 is_ipython = ('__IPYTHON__' in builtins or 4263 is_ipython = ('__IPYTHON__' in builtins or
4258 '__IPYTHON__active' in builtins) 4264 '__IPYTHON__active' in builtins)
4259 splits = text.split() 4265
4260 is_module = splits and splits[0] in ('from', 'import') 4266 if is_ipython and 'get_ipython' in builtins:
4261 4267 def filter_c(prefix, c):
4262 if is_ipython and is_module: 4268 if re.match('_+(i?[0-9]+)?$', c):
4263 from IPython.core.completerlib import module_completion 4269 return False
4264 completions = module_completion(text.strip()) 4270 elif c[0] == '%' and not re.match('[%a-zA-Z]+$', prefix):
4265 elif is_ipython and '__IP' in builtins: 4271 return False
4266 completions = __IP.complete(text) 4272 return True
4267 elif is_ipython and 'get_ipython' in builtins: 4273
4268 completions = get_ipython().Completer.all_completions(text) 4274 import IPython
4275 try:
4276 if IPython.version_info[0] >= 6:
4277 from IPython.core.completer import provisionalcompleter
4278 with provisionalcompleter():
4279 completions = [
4280 [c.text, c.start, c.end, c.type or '?', c.signature or '']
4281 for c in get_ipython().Completer.completions(text, len(text))
4282 if filter_c(text, c.text)]
4283 else:
4284 part, matches = get_ipython().Completer.complete(line_buffer=text)
4285 completions = [text + m[len(part):] for m in matches if filter_c(text, m)]
4286 except:
4287 pass
4269 else: 4288 else:
4270 # Try to reuse current completer. 4289 # Try to reuse current completer.
4271 completer = readline.get_completer() 4290 completer = readline.get_completer()
@@ -4288,7 +4307,7 @@ def __PYTHON_EL_get_completions(text):
4288 finally: 4307 finally:
4289 if getattr(completer, 'PYTHON_EL_WRAPPED', False): 4308 if getattr(completer, 'PYTHON_EL_WRAPPED', False):
4290 completer.print_mode = True 4309 completer.print_mode = True
4291 return completions" 4310 return json.dumps(completions)"
4292 "Code used to setup completion in inferior Python processes." 4311 "Code used to setup completion in inferior Python processes."
4293 :type 'string) 4312 :type 'string)
4294 4313
@@ -4329,6 +4348,10 @@ When a match is found, native completion is disabled."
4329 :version "25.1" 4348 :version "25.1"
4330 :type 'float) 4349 :type 'float)
4331 4350
4351(defvar python-shell-readline-completer-delims nil
4352 "Word delimiters used by the readline completer.
4353It is automatically set by Python shell.")
4354
4332(defvar python-shell-completion-native-redirect-buffer 4355(defvar python-shell-completion-native-redirect-buffer
4333 " *Python completions redirect*" 4356 " *Python completions redirect*"
4334 "Buffer to be used to redirect output of readline commands.") 4357 "Buffer to be used to redirect output of readline commands.")
@@ -4467,6 +4490,10 @@ def __PYTHON_EL_native_completion_setup():
4467__PYTHON_EL_native_completion_setup()" process))) 4490__PYTHON_EL_native_completion_setup()" process)))
4468 (when (string-match-p "python\\.el: native completion setup loaded" 4491 (when (string-match-p "python\\.el: native completion setup loaded"
4469 output) 4492 output)
4493 (setq-local python-shell-readline-completer-delims
4494 (string-trim-right
4495 (python-shell-send-string-no-output
4496 "import readline; print(readline.get_completer_delims())")))
4470 (python-shell-completion-native-try)))) 4497 (python-shell-completion-native-try))))
4471 4498
4472(defun python-shell-completion-native-turn-off (&optional msg) 4499(defun python-shell-completion-native-turn-off (&optional msg)
@@ -4534,6 +4561,8 @@ With argument MSG show activation/deactivation message."
4534 (let* ((original-filter-fn (process-filter process)) 4561 (let* ((original-filter-fn (process-filter process))
4535 (redirect-buffer (get-buffer-create 4562 (redirect-buffer (get-buffer-create
4536 python-shell-completion-native-redirect-buffer)) 4563 python-shell-completion-native-redirect-buffer))
4564 (sep (if (string= python-shell-readline-completer-delims "")
4565 "[\n\r]+" "[ \f\t\n\r\v()]+"))
4537 (trigger "\t") 4566 (trigger "\t")
4538 (new-input (concat input trigger)) 4567 (new-input (concat input trigger))
4539 (input-length 4568 (input-length
@@ -4576,28 +4605,80 @@ With argument MSG show activation/deactivation message."
4576 process python-shell-completion-native-output-timeout 4605 process python-shell-completion-native-output-timeout
4577 comint-redirect-finished-regexp) 4606 comint-redirect-finished-regexp)
4578 (re-search-backward "0__dummy_completion__" nil t) 4607 (re-search-backward "0__dummy_completion__" nil t)
4579 (cl-remove-duplicates 4608 (let ((str (buffer-substring-no-properties
4580 (split-string 4609 (line-beginning-position) (point-min))))
4581 (buffer-substring-no-properties 4610 ;; The readline completer is allowed to return a list
4582 (line-beginning-position) (point-min)) 4611 ;; of (text start end type signature) as a JSON
4583 "[ \f\t\n\r\v()]+" t) 4612 ;; string. See the return value for IPython in
4584 :test #'string=)))) 4613 ;; `python-shell-completion-setup-code'.
4614 (if (string= "[" (substring str 0 1))
4615 (condition-case nil
4616 (python--parse-json-array str)
4617 (t (cl-remove-duplicates (split-string str sep t)
4618 :test #'string=)))
4619 (cl-remove-duplicates (split-string str sep t)
4620 :test #'string=))))))
4585 (set-process-filter process original-filter-fn))))) 4621 (set-process-filter process original-filter-fn)))))
4586 4622
4587(defun python-shell-completion-get-completions (process input) 4623(defun python-shell-completion-get-completions (process input)
4588 "Get completions of INPUT using PROCESS." 4624 "Get completions of INPUT using PROCESS."
4589 (with-current-buffer (process-buffer process) 4625 (with-current-buffer (process-buffer process)
4590 (let ((completions 4626 (python--parse-json-array
4591 (python-util-strip-string 4627 (python-shell-send-string-no-output
4592 (python-shell-send-string-no-output 4628 (format "%s\nprint(__PYTHON_EL_get_completions(%s))"
4593 (format
4594 "%s\nprint(';'.join(__PYTHON_EL_get_completions(%s)))"
4595 python-shell-completion-setup-code 4629 python-shell-completion-setup-code
4596 (python-shell--encode-string input)) 4630 (python-shell--encode-string input))
4597 process)))) 4631 process))))
4598 (when (> (length completions) 2) 4632
4599 (split-string completions 4633(defun python-shell--get-multiline-input ()
4600 "^'\\|^\"\\|;\\|'$\\|\"$" t))))) 4634 "Return lines at a multi-line input in Python shell."
4635 (save-excursion
4636 (let ((p (point)) lines)
4637 (when (progn
4638 (beginning-of-line)
4639 (looking-back python-shell-prompt-block-regexp (pos-bol)))
4640 (push (buffer-substring-no-properties (point) p) lines)
4641 (while (progn (comint-previous-prompt 1)
4642 (looking-back python-shell-prompt-block-regexp (pos-bol)))
4643 (push (buffer-substring-no-properties (point) (pos-eol)) lines))
4644 (push (buffer-substring-no-properties (point) (pos-eol)) lines))
4645 lines)))
4646
4647(defun python-shell--extra-completion-context ()
4648 "Get extra completion context of current input in Python shell."
4649 (let ((lines (python-shell--get-multiline-input))
4650 (python-indent-guess-indent-offset nil))
4651 (when (not (zerop (length lines)))
4652 (with-temp-buffer
4653 (delay-mode-hooks
4654 (insert (string-join lines "\n"))
4655 (python-mode)
4656 (python-shell-completion-extra-context))))))
4657
4658(defun python-shell-completion-extra-context (&optional pos)
4659 "Get extra completion context at position POS in Python buffer.
4660If optional argument POS is nil, use current position.
4661
4662Readline completers could use current line as the completion
4663context, which may be insufficient. In this function, extra
4664context (e.g. multi-line function call) is found and reformatted
4665as one line, which is required by native completion."
4666 (let (bound p)
4667 (save-excursion
4668 (and pos (goto-char pos))
4669 (setq bound (pos-bol))
4670 (python-nav-up-list -1)
4671 (when (and (< (point) bound)
4672 (or
4673 (looking-back
4674 (python-rx (group (+ (or "." symbol-name)))) (pos-bol) t)
4675 (progn
4676 (forward-line 0)
4677 (looking-at "^[ \t]*\\(from \\)"))))
4678 (setq p (match-beginning 1))))
4679 (when p
4680 (replace-regexp-in-string
4681 "\n[ \t]*" "" (buffer-substring-no-properties p (1- bound))))))
4601 4682
4602(defvar-local python-shell--capf-cache nil 4683(defvar-local python-shell--capf-cache nil
4603 "Variable to store cached completions and invalidation keys.") 4684 "Variable to store cached completions and invalidation keys.")
@@ -4612,21 +4693,26 @@ using that one instead of current buffer's process."
4612 ;; Working on a shell buffer: use prompt end. 4693 ;; Working on a shell buffer: use prompt end.
4613 (cdr (python-util-comint-last-prompt)) 4694 (cdr (python-util-comint-last-prompt))
4614 (line-beginning-position))) 4695 (line-beginning-position)))
4615 (import-statement 4696 (no-delims
4616 (when (string-match-p 4697 (and (not (if is-shell-buffer
4617 (rx (* space) word-start (or "from" "import") word-end space) 4698 (eq 'font-lock-comment-face
4618 (buffer-substring-no-properties line-start (point))) 4699 (get-text-property (1- (point)) 'face))
4619 (buffer-substring-no-properties line-start (point)))) 4700 (python-syntax-context 'comment)))
4701 (with-current-buffer (process-buffer process)
4702 (if python-shell-completion-native-enable
4703 (string= python-shell-readline-completer-delims "")
4704 (string-match-p "ipython[23]?\\'" python-shell-interpreter)))))
4620 (start 4705 (start
4621 (if (< (point) line-start) 4706 (if (< (point) line-start)
4622 (point) 4707 (point)
4623 (save-excursion 4708 (save-excursion
4624 (if (not (re-search-backward 4709 (if (or no-delims
4625 (python-rx 4710 (not (re-search-backward
4626 (or whitespace open-paren close-paren 4711 (python-rx
4627 string-delimiter simple-operator)) 4712 (or whitespace open-paren close-paren
4628 line-start 4713 string-delimiter simple-operator))
4629 t 1)) 4714 line-start
4715 t 1)))
4630 line-start 4716 line-start
4631 (forward-char (length (match-string-no-properties 0))) 4717 (forward-char (length (match-string-no-properties 0)))
4632 (point))))) 4718 (point)))))
@@ -4666,18 +4752,56 @@ using that one instead of current buffer's process."
4666 (t #'python-shell-completion-native-get-completions)))) 4752 (t #'python-shell-completion-native-get-completions))))
4667 (prev-prompt (car python-shell--capf-cache)) 4753 (prev-prompt (car python-shell--capf-cache))
4668 (re (or (cadr python-shell--capf-cache) regexp-unmatchable)) 4754 (re (or (cadr python-shell--capf-cache) regexp-unmatchable))
4669 (prefix (buffer-substring-no-properties start end))) 4755 (prefix (buffer-substring-no-properties start end))
4756 (prefix-offset 0)
4757 (extra-context (when no-delims
4758 (if is-shell-buffer
4759 (python-shell--extra-completion-context)
4760 (python-shell-completion-extra-context))))
4761 (extra-offset (length extra-context)))
4762 (unless (zerop extra-offset)
4763 (setq prefix (concat extra-context prefix)))
4670 ;; To invalidate the cache, we check if the prompt position or the 4764 ;; To invalidate the cache, we check if the prompt position or the
4671 ;; completion prefix changed. 4765 ;; completion prefix changed.
4672 (unless (and (equal prev-prompt (car prompt-boundaries)) 4766 (unless (and (equal prev-prompt (car prompt-boundaries))
4673 (string-match re prefix)) 4767 (string-match re prefix)
4768 (setq prefix-offset (- (length prefix) (match-end 1))))
4674 (setq python-shell--capf-cache 4769 (setq python-shell--capf-cache
4675 `(,(car prompt-boundaries) 4770 `(,(car prompt-boundaries)
4676 ,(if (string-empty-p prefix) 4771 ,(if (string-empty-p prefix)
4677 regexp-unmatchable 4772 regexp-unmatchable
4678 (concat "\\`" (regexp-quote prefix) "\\(?:\\sw\\|\\s_\\)*\\'")) 4773 (concat "\\`\\(" (regexp-quote prefix) "\\)\\(?:\\sw\\|\\s_\\)*\\'"))
4679 ,@(funcall completion-fn process (or import-statement prefix))))) 4774 ,@(funcall completion-fn process prefix))))
4680 (list start end (cddr python-shell--capf-cache)))) 4775 (let ((cands (cddr python-shell--capf-cache)))
4776 (cond
4777 ((stringp (car cands))
4778 (if no-delims
4779 ;; Reduce completion candidates due to long prefix.
4780 (if-let ((Lp (length prefix))
4781 ((string-match "\\(\\sw\\|\\s_\\)+\\'" prefix))
4782 (L (match-beginning 0)))
4783 ;; If extra-offset is not zero:
4784 ;; start end
4785 ;; o------------------o---------o-------o
4786 ;; |<- extra-offset ->|
4787 ;; |<----------- L ------------>|
4788 ;; new-start
4789 (list (+ start L (- extra-offset)) end
4790 (mapcar (lambda (s) (substring s L)) cands))
4791 (list end end (mapcar (lambda (s) (substring s Lp)) cands)))
4792 (list start end cands)))
4793 ;; python-shell-completion(-native)-get-completions may produce a
4794 ;; list of (text start end type signature) for completion.
4795 ((consp (car cands))
4796 (list (+ start (nth 1 (car cands)) (- extra-offset))
4797 ;; Candidates may be cached, so the end position should
4798 ;; be adjusted according to current completion prefix.
4799 (+ start (nth 2 (car cands)) (- extra-offset) prefix-offset)
4800 cands
4801 :annotation-function
4802 (lambda (c) (concat " " (nth 3 (assoc c cands))))
4803 :company-docsig
4804 (lambda (c) (nth 4 (assoc c cands)))))))))
4681 4805
4682(define-obsolete-function-alias 4806(define-obsolete-function-alias
4683 'python-shell-completion-complete-at-point 4807 'python-shell-completion-complete-at-point