aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabián Ezequiel Gallina2015-08-22 18:07:26 -0300
committerFabián Ezequiel Gallina2015-08-22 18:07:26 -0300
commit34f58ce5d7ae8b716f8c9eee0f33d46dbcb2d9ef (patch)
treef478c28629efdda2dd899b7ecc8d416ad564571e
parentb0fe29ed920e65510ed062c5293c4db3b9a02ab6 (diff)
downloademacs-34f58ce5d7ae8b716f8c9eee0f33d46dbcb2d9ef.tar.gz
emacs-34f58ce5d7ae8b716f8c9eee0f33d46dbcb2d9ef.zip
python.el: Increase native completion robustness.
* lisp/progmodes/python.el (python-shell-completion-native-setup): Make completer print real candidates and just return dummy ones to avoid input modification. (python-shell-completion-native-get-completions): Set comint-redirect-insert-matching-regexp to non-nil and make comint-redirect-finished-regexp match the last dummy candidate. Use python-shell-accept-process-output to wait for the full list of candidates.
-rw-r--r--lisp/progmodes/python.el93
1 files changed, 60 insertions, 33 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index d442dae9f3b..7b755c6f538 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -3252,48 +3252,78 @@ When a match is found, native completion is disabled."
3252def __PYTHON_EL_native_completion_setup(): 3252def __PYTHON_EL_native_completion_setup():
3253 try: 3253 try:
3254 import readline 3254 import readline
3255
3255 try: 3256 try:
3256 import __builtin__ 3257 import __builtin__
3257 except ImportError: 3258 except ImportError:
3258 # Python 3 3259 # Python 3
3259 import builtins as __builtin__ 3260 import builtins as __builtin__
3261
3260 builtins = dir(__builtin__) 3262 builtins = dir(__builtin__)
3261 is_ipython = ('__IPYTHON__' in builtins or 3263 is_ipython = ('__IPYTHON__' in builtins or
3262 '__IPYTHON__active' in builtins) 3264 '__IPYTHON__active' in builtins)
3265
3263 class __PYTHON_EL_Completer: 3266 class __PYTHON_EL_Completer:
3267 '''Completer wrapper that prints candidates to stdout.
3268
3269 It wraps an existing completer function and changes its behavior so
3270 that the user input is unchanged and real candidates are printed to
3271 stdout.
3272
3273 Returned candidates are '0__dummy_completion__' and
3274 '1__dummy_completion__' in that order ('0__dummy_completion__' is
3275 returned repeatedly until all possible candidates are consumed).
3276
3277 The real candidates are printed to stdout so that they can be
3278 easily retrieved through comint output redirect trickery.
3279 '''
3280
3264 PYTHON_EL_WRAPPED = True 3281 PYTHON_EL_WRAPPED = True
3282
3265 def __init__(self, completer): 3283 def __init__(self, completer):
3266 self.completer = completer 3284 self.completer = completer
3267 self.last_completion = None 3285 self.last_completion = None
3286
3268 def __call__(self, text, state): 3287 def __call__(self, text, state):
3269 if state == 0: 3288 if state == 0:
3270 # The first completion is always a dummy completion. This 3289 # Set the first dummy completion.
3271 # ensures proper output for sole completions and a current
3272 # input safeguard when no completions are available.
3273 self.last_completion = None 3290 self.last_completion = None
3274 completion = '0__dummy_completion__' 3291 completion = '0__dummy_completion__'
3275 else: 3292 else:
3276 completion = self.completer(text, state - 1) 3293 completion = self.completer(text, state - 1)
3294
3277 if not completion: 3295 if not completion:
3278 if state == 1: 3296 if self.last_completion != '1__dummy_completion__':
3279 # When no completions are available, two non-sharing 3297 # When no more completions are available, returning a
3280 # prefix strings are returned just to ensure output 3298 # dummy with non-sharing prefix allow to ensure output
3281 # while preventing changes to current input. 3299 # while preventing changes to current input.
3300 # Coincidentally it's also the end of output.
3282 completion = '1__dummy_completion__' 3301 completion = '1__dummy_completion__'
3283 elif self.last_completion != '~~~~__dummy_completion__':
3284 # This marks the end of output.
3285 completion = '~~~~__dummy_completion__'
3286 elif completion.endswith('('): 3302 elif completion.endswith('('):
3287 # Remove parens on callables as it breaks completion on 3303 # Remove parens on callables as it breaks completion on
3288 # arguments (e.g. str(Ari<tab>)). 3304 # arguments (e.g. str(Ari<tab>)).
3289 completion = completion[:-1] 3305 completion = completion[:-1]
3290 self.last_completion = completion 3306 self.last_completion = completion
3291 return completion 3307
3308 if completion in (
3309 '0__dummy_completion__', '1__dummy_completion__'):
3310 return completion
3311 elif completion:
3312 # For every non-dummy completion, return a repeated dummy
3313 # one and print the real candidate so it can be retrieved
3314 # by comint output filters.
3315 print (completion)
3316 return '0__dummy_completion__'
3317 else:
3318 return completion
3319
3292 completer = readline.get_completer() 3320 completer = readline.get_completer()
3321
3293 if not completer: 3322 if not completer:
3294 # Used as last resort to avoid breaking customizations. 3323 # Used as last resort to avoid breaking customizations.
3295 import rlcompleter 3324 import rlcompleter
3296 completer = readline.get_completer() 3325 completer = readline.get_completer()
3326
3297 if completer and not getattr(completer, 'PYTHON_EL_WRAPPED', False): 3327 if completer and not getattr(completer, 'PYTHON_EL_WRAPPED', False):
3298 # Wrap the existing completer function only once. 3328 # Wrap the existing completer function only once.
3299 new_completer = __PYTHON_EL_Completer(completer) 3329 new_completer = __PYTHON_EL_Completer(completer)
@@ -3308,15 +3338,18 @@ def __PYTHON_EL_native_completion_setup():
3308 # function into the existing instance directly: 3338 # function into the existing instance directly:
3309 instance = getattr(completer, 'im_self', completer.__self__) 3339 instance = getattr(completer, 'im_self', completer.__self__)
3310 instance.rlcomplete = new_completer 3340 instance.rlcomplete = new_completer
3341
3311 if readline.__doc__ and 'libedit' in readline.__doc__: 3342 if readline.__doc__ and 'libedit' in readline.__doc__:
3312 readline.parse_and_bind('bind ^I rl_complete') 3343 readline.parse_and_bind('bind ^I rl_complete')
3313 else: 3344 else:
3314 readline.parse_and_bind('tab: complete') 3345 readline.parse_and_bind('tab: complete')
3315 # Require just one tab to send output. 3346 # Require just one tab to send output.
3316 readline.parse_and_bind('set show-all-if-ambiguous on') 3347 readline.parse_and_bind('set show-all-if-ambiguous on')
3348
3317 print ('python.el: readline is available') 3349 print ('python.el: readline is available')
3318 except IOError: 3350 except IOError:
3319 print ('python.el: readline not available') 3351 print ('python.el: readline not available')
3352
3320__PYTHON_EL_native_completion_setup()" 3353__PYTHON_EL_native_completion_setup()"
3321 process) 3354 process)
3322 (python-shell-accept-process-output process) 3355 (python-shell-accept-process-output process)
@@ -3395,7 +3428,6 @@ completion."
3395 (original-filter-fn (process-filter process)) 3428 (original-filter-fn (process-filter process))
3396 (redirect-buffer (get-buffer-create 3429 (redirect-buffer (get-buffer-create
3397 python-shell-completion-native-redirect-buffer)) 3430 python-shell-completion-native-redirect-buffer))
3398 (separators (python-rx (or whitespace open-paren close-paren)))
3399 (trigger "\t") 3431 (trigger "\t")
3400 (new-input (concat input trigger)) 3432 (new-input (concat input trigger))
3401 (input-length 3433 (input-length
@@ -3408,18 +3440,17 @@ completion."
3408 (unwind-protect 3440 (unwind-protect
3409 (with-current-buffer redirect-buffer 3441 (with-current-buffer redirect-buffer
3410 ;; Cleanup the redirect buffer 3442 ;; Cleanup the redirect buffer
3411 (delete-region (point-min) (point-max)) 3443 (erase-buffer)
3412 ;; Mimic `comint-redirect-send-command', unfortunately it 3444 ;; Mimic `comint-redirect-send-command', unfortunately it
3413 ;; can't be used here because it expects a newline in the 3445 ;; can't be used here because it expects a newline in the
3414 ;; command and that's exactly what we are trying to avoid. 3446 ;; command and that's exactly what we are trying to avoid.
3415 (let ((comint-redirect-echo-input nil) 3447 (let ((comint-redirect-echo-input nil)
3416 (comint-redirect-verbose nil) 3448 (comint-redirect-completed nil)
3417 (comint-redirect-perform-sanity-check nil) 3449 (comint-redirect-perform-sanity-check nil)
3418 (comint-redirect-insert-matching-regexp nil) 3450 (comint-redirect-insert-matching-regexp t)
3419 ;; Feed it some regex that will never match. 3451 (comint-redirect-finished-regexp
3420 (comint-redirect-finished-regexp "^\\'$") 3452 "1__dummy_completion__[[:space:]]*\n")
3421 (comint-redirect-output-buffer redirect-buffer) 3453 (comint-redirect-output-buffer redirect-buffer))
3422 (current-time (float-time)))
3423 ;; Compatibility with Emacs 24.x. Comint changed and 3454 ;; Compatibility with Emacs 24.x. Comint changed and
3424 ;; now `comint-redirect-filter' gets 3 args. This 3455 ;; now `comint-redirect-filter' gets 3 args. This
3425 ;; checks which version of `comint-redirect-filter' is 3456 ;; checks which version of `comint-redirect-filter' is
@@ -3433,21 +3464,17 @@ completion."
3433 (set-process-filter process #'comint-redirect-filter)) 3464 (set-process-filter process #'comint-redirect-filter))
3434 (process-send-string process input-to-send) 3465 (process-send-string process input-to-send)
3435 ;; Grab output until our dummy completion used as 3466 ;; Grab output until our dummy completion used as
3436 ;; output end marker is found. Output is accepted 3467 ;; output end marker is found.
3437 ;; *very* quickly to keep the shell super-responsive. 3468 (when (python-shell-accept-process-output
3438 (while (and (not (re-search-backward "~~~~__dummy_completion__" nil t)) 3469 process python-shell-completion-native-output-timeout
3439 (< (- (float-time) current-time) 3470 comint-redirect-finished-regexp)
3440 python-shell-completion-native-output-timeout)) 3471 (re-search-backward "0__dummy_completion__" nil t)
3441 (accept-process-output process 0.01)) 3472 (cl-remove-duplicates
3442 (cl-remove-duplicates 3473 (split-string
3443 (cl-remove-if 3474 (buffer-substring-no-properties
3444 (lambda (c) 3475 (line-beginning-position) (point-min))
3445 (string-match "__dummy_completion__" c)) 3476 "[ \f\t\n\r\v()]+" t)
3446 (split-string 3477 :test #'string=))))
3447 (buffer-substring-no-properties
3448 (point-min) (point-max))
3449 separators t))
3450 :test #'string=)))
3451 (set-process-filter process original-filter-fn)))))) 3478 (set-process-filter process original-filter-fn))))))
3452 3479
3453(defun python-shell-completion-get-completions (process import input) 3480(defun python-shell-completion-get-completions (process import input)