aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoão Távora2020-07-08 22:47:00 +0100
committerJoão Távora2020-07-08 22:47:10 +0100
commit91041920c6f80d7a6b4ae53d27d844018710d7f3 (patch)
tree20a9950c15d91995a142304c941d10a1b370a695
parent9a7aab2d9e5f5a8f15c6f60130cae6be32b11f48 (diff)
downloademacs-scratch/python-eldoc-async.tar.gz
emacs-scratch/python-eldoc-async.zip
Have Python mode cooperate asynchronously with Eldocscratch/python-eldoc-async
When combined with Flymake mode, which also adds a value to eldoc-documentation-functions, Python-mode users can now experiment with different eldoc-documentation-strategy values. Also, this shoulda allow us to write automatic tests for this particular Eldoc functionality. * lisp/progmodes/python.el (inferior-python-mode): Set coming-preoutput-filter-functions. (python--shell-output-filter-in-progress) (python--shell-output-filter-buffer): Rename from python- variant. (python-shell-output-filter): Rework to support async operation. (python-eldoc--get-doc-at-point): Rework to support async. (python-eldoc-function): Use callback.
-rw-r--r--lisp/progmodes/python.el188
1 files changed, 119 insertions, 69 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 165463aef59..e135ee76f60 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -2809,6 +2809,8 @@ variable.
2809 python-shell-comint-watch-for-first-prompt-output-filter 2809 python-shell-comint-watch-for-first-prompt-output-filter
2810 python-comint-postoutput-scroll-to-bottom 2810 python-comint-postoutput-scroll-to-bottom
2811 comint-watch-for-password-prompt)) 2811 comint-watch-for-password-prompt))
2812 (setq comint-preoutput-filter-functions
2813 '(python-shell-output-filter))
2812 (set (make-local-variable 'compilation-error-regexp-alist) 2814 (set (make-local-variable 'compilation-error-regexp-alist)
2813 python-shell-compilation-regexp-alist) 2815 python-shell-compilation-regexp-alist)
2814 (add-hook 'completion-at-point-functions 2816 (add-hook 'completion-at-point-functions
@@ -3021,8 +3023,10 @@ t when called interactively."
3021 (string-match "\n[ \t].*\n?\\'" string)) 3023 (string-match "\n[ \t].*\n?\\'" string))
3022 (comint-send-string process "\n"))))) 3024 (comint-send-string process "\n")))))
3023 3025
3024(defvar python-shell-output-filter-in-progress nil) 3026;;; Three variables used by `python-shell-output-filter'.
3025(defvar python-shell-output-filter-buffer nil) 3027(defvar python--shell-output-filter-in-progress nil)
3028(defvar python--shell-output-filter-buffer nil)
3029(defvar python--shell-output-filter-callback nil)
3026 3030
3027(defun python-shell-output-filter (string) 3031(defun python-shell-output-filter (string)
3028 "Filter used in `python-shell-send-string-no-output' to grab output. 3032 "Filter used in `python-shell-send-string-no-output' to grab output.
@@ -3030,48 +3034,71 @@ STRING is the output received to this point from the process.
3030This filter saves received output from the process in 3034This filter saves received output from the process in
3031`python-shell-output-filter-buffer' and stops receiving it after 3035`python-shell-output-filter-buffer' and stops receiving it after
3032detecting a prompt at the end of the buffer." 3036detecting a prompt at the end of the buffer."
3033 (setq 3037 (cond (python--shell-output-filter-in-progress
3034 string (ansi-color-filter-apply string) 3038 (setq
3035 python-shell-output-filter-buffer 3039 string (ansi-color-filter-apply string)
3036 (concat python-shell-output-filter-buffer string)) 3040 python--shell-output-filter-buffer
3037 (when (python-shell-comint-end-of-output-p 3041 (concat python--shell-output-filter-buffer string))
3038 python-shell-output-filter-buffer) 3042 (when (python-shell-comint-end-of-output-p
3039 ;; Output ends when `python-shell-output-filter-buffer' contains 3043 python--shell-output-filter-buffer)
3040 ;; the prompt attached at the end of it. 3044 ;; Output ends when `python-shell-output-filter-buffer' contains
3041 (setq python-shell-output-filter-in-progress nil 3045 ;; the prompt attached at the end of it.
3042 python-shell-output-filter-buffer 3046 (setq python--shell-output-filter-in-progress nil
3043 (substring python-shell-output-filter-buffer 3047 python--shell-output-filter-buffer
3044 0 (match-beginning 0))) 3048 (substring python--shell-output-filter-buffer
3045 (when (string-match 3049 0 (match-beginning 0)))
3046 python-shell--prompt-calculated-output-regexp 3050 (when (string-match
3047 python-shell-output-filter-buffer) 3051 python-shell--prompt-calculated-output-regexp
3048 ;; Some shells, like IPython might append a prompt before the 3052 python--shell-output-filter-buffer)
3049 ;; output, clean that. 3053 ;; Some shells, like IPython might append a prompt before the
3050 (setq python-shell-output-filter-buffer 3054 ;; output, clean that.
3051 (substring python-shell-output-filter-buffer (match-end 0))))) 3055 (setq python--shell-output-filter-buffer
3052 "") 3056 (substring python--shell-output-filter-buffer (match-end 0))))
3053 3057 (when python--shell-output-filter-callback
3054(defun python-shell-send-string-no-output (string &optional process) 3058 (funcall python--shell-output-filter-callback
3059 python--shell-output-filter-buffer)
3060 (setq python--shell-output-filter-callback nil
3061 python--shell-output-filter-buffer nil)))
3062 "")
3063 (t string)))
3064
3065(defvar python--async-comint-timeout 1
3066 "Defaults to 1, but can be bound dynamically.
3067Namely by callers of `python-shell-send-string-no-output'.")
3068
3069(defun python-shell-send-string-no-output (string &optional process async)
3055 "Send STRING to PROCESS and inhibit output. 3070 "Send STRING to PROCESS and inhibit output.
3056Return the output." 3071Return the output string, unless ASYNC is non-nil, in which case
3072nil is returned. If ASYNC is non-nil it should be a function of
3073one argument. It is called with the output string if the comint
3074subjob finished successfully or with nil if it didn't."
3057 (let ((process (or process (python-shell-get-process-or-error))) 3075 (let ((process (or process (python-shell-get-process-or-error)))
3058 (comint-preoutput-filter-functions
3059 '(python-shell-output-filter))
3060 (python-shell-output-filter-in-progress t)
3061 (inhibit-quit t)) 3076 (inhibit-quit t))
3062 (or 3077 (with-current-buffer (process-buffer process)
3063 (with-local-quit 3078 (setq python--shell-output-filter-in-progress t)
3064 (python-shell-send-string string process) 3079 (cond (async
3065 (while python-shell-output-filter-in-progress 3080 (python-shell-send-string string process)
3066 ;; `python-shell-output-filter' takes care of setting 3081 (let ((timer (run-with-timer
3067 ;; `python-shell-output-filter-in-progress' to NIL after it 3082 python--async-comint-timeout
3068 ;; detects end of output. 3083 nil async nil)))
3069 (accept-process-output process)) 3084 (setq python--shell-output-filter-callback
3070 (prog1 3085 (lambda (string)
3071 python-shell-output-filter-buffer 3086 (funcall async string)
3072 (setq python-shell-output-filter-buffer nil))) 3087 (cancel-timer timer)))))
3073 (with-current-buffer (process-buffer process) 3088 (t
3074 (comint-interrupt-subjob))))) 3089 (or
3090 (with-local-quit
3091 (python-shell-send-string string process)
3092 (while python--shell-output-filter-in-progress
3093 ;; `python-shell-output-filter' takes care of
3094 ;; setting `python--shell-output-filter-in-progress'
3095 ;; to NIL after it detects end of output.
3096 (accept-process-output process))
3097 (prog1
3098 python--shell-output-filter-buffer
3099 (setq python--shell-output-filter-buffer nil)))
3100 (with-current-buffer (process-buffer process)
3101 (comint-interrupt-subjob))))))))
3075 3102
3076(defun python-shell-internal-send-string (string) 3103(defun python-shell-internal-send-string (string)
3077 "Send STRING to the Internal Python interpreter. 3104 "Send STRING to the Internal Python interpreter.
@@ -4532,27 +4559,46 @@ Returns the current symbol handling point within arguments."
4532 (python-util-forward-comment -1))) 4559 (python-util-forward-comment -1)))
4533 (python-info-current-symbol t))) 4560 (python-info-current-symbol t)))
4534 4561
4535(defun python-eldoc--get-doc-at-point (&optional force-input force-process) 4562(defun python-eldoc--get-doc-at-point (&optional force-input force-process async)
4536 "Internal implementation to get documentation at point. 4563 "Internal implementation to get documentation at point.
4537If not FORCE-INPUT is passed then what `python-eldoc--get-symbol-at-point' 4564If not FORCE-INPUT is passed then what `python-eldoc--get-symbol-at-point'
4538returns will be used. If not FORCE-PROCESS is passed what 4565returns will be used. If not FORCE-PROCESS is passed what
4539`python-shell-get-process' returns is used." 4566`python-shell-get-process' returns is used.
4567
4568If ASYNC is non nil, should be a function accepting the
4569documentation one or nil if there's no such thing. In that case,
4570the function returns t.
4571"
4540 (let ((process (or force-process (python-shell-get-process)))) 4572 (let ((process (or force-process (python-shell-get-process))))
4541 (when process 4573 (when process
4542 (let* ((input (or force-input 4574 (let* ((input (or force-input
4543 (python-eldoc--get-symbol-at-point))) 4575 (python-eldoc--get-symbol-at-point)))
4544 (docstring 4576 (command (and input
4545 (when input 4577 (concat
4546 ;; Prevent resizing the echo area when iPython is 4578 python-eldoc-setup-code
4547 ;; enabled. Bug#18794. 4579 "\nprint(" (format python-eldoc-string-code input) ")"))))
4548 (python-util-strip-string 4580 (when command
4581 (cond (async
4549 (python-shell-send-string-no-output 4582 (python-shell-send-string-no-output
4550 (concat 4583 command
4551 python-eldoc-setup-code 4584 process
4552 "\nprint(" (format python-eldoc-string-code input) ")") 4585 (lambda (string)
4553 process))))) 4586 ;; Prevent resizing the echo area when iPython is
4554 (unless (zerop (length docstring)) 4587 ;; enabled. Bug#18794.
4555 docstring))))) 4588 (when string
4589 (setq string (python-util-strip-string string)))
4590 (funcall async
4591 (and (not (zerop (length string)))
4592 string))))
4593 t)
4594 (t
4595 (let ((res
4596 (python-util-strip-string
4597 (python-shell-send-string-no-output
4598 command
4599 process))))
4600 (unless (zerop (length res))
4601 res)))))))))
4556 4602
4557(defvar-local python-eldoc-get-doc t 4603(defvar-local python-eldoc-get-doc t
4558 "Non-nil means eldoc should fetch the documentation 4604 "Non-nil means eldoc should fetch the documentation
@@ -4573,28 +4619,32 @@ returns will be used. If not FORCE-PROCESS is passed what
4573 :type 'boolean 4619 :type 'boolean
4574 :version "25.1") 4620 :version "25.1")
4575 4621
4576(defun python-eldoc-function (&rest _ignored) 4622(defun python-eldoc-function (callback &rest _ignored)
4577 "`eldoc-documentation-function' for Python. 4623 "A member of `eldoc-documentation-functions' for Python.
4578For this to work as best as possible you should call 4624For this to work as best as possible you should call
4579`python-shell-send-buffer' from time to time so context in 4625`python-shell-send-buffer' from time to time so context in
4580inferior Python process is updated properly. 4626inferior Python process is updated properly.
4581 4627
4582If `python-eldoc-function-timeout' seconds elapse before this 4628In systems with `eldoc-documentation-functions', CALLBACK is
4583function returns then if 4629handled accordingly, otherwise this function is acceptable for
4630`eldoc-documentation-function'.
4631
4632In the latter case if `python-eldoc-function-timeout' seconds
4633elapse before this -function returns then if
4584`python-eldoc-function-timeout-permanent' is non-nil 4634`python-eldoc-function-timeout-permanent' is non-nil
4585`python-eldoc-get-doc' will be set to nil and eldoc will no 4635`python-eldoc-get-doc' will be set to nil and eldoc will no
4586longer return the documentation at the point automatically. 4636longer return the documentation at the point automatically."
4587
4588Set `python-eldoc-get-doc' to t to reenable eldoc documentation
4589fetching."
4590 (when python-eldoc-get-doc 4637 (when python-eldoc-get-doc
4591 (with-timeout (python-eldoc-function-timeout 4638 (if (boundp 'eldoc-documentation-strategy)
4592 (if python-eldoc-function-timeout-permanent 4639 (python-eldoc--get-doc-at-point nil nil callback)
4593 (progn 4640 (with-timeout
4594 (message "Eldoc echo-area display muted in this buffer, see `python-eldoc-function'") 4641 (python-eldoc-function-timeout
4595 (setq python-eldoc-get-doc nil)) 4642 (if python-eldoc-function-timeout-permanent
4596 (message "`python-eldoc-function' timed out, see `python-eldoc-function-timeout'"))) 4643 (progn
4597 (python-eldoc--get-doc-at-point)))) 4644 (message "Eldoc echo-area display muted in this buffer, see `python-eldoc-function'")
4645 (setq python-eldoc-get-doc nil))
4646 (message "`python-eldoc-function' timed out, see `python-eldoc-function-timeout'")))
4647 (python-eldoc--get-doc-at-point)))))
4598 4648
4599(defun python-eldoc-at-point (symbol) 4649(defun python-eldoc-at-point (symbol)
4600 "Get help on SYMBOL using `help'. 4650 "Get help on SYMBOL using `help'.