diff options
| author | Fabián Ezequiel Gallina | 2015-08-22 18:07:26 -0300 |
|---|---|---|
| committer | Fabián Ezequiel Gallina | 2015-08-22 18:07:26 -0300 |
| commit | 34f58ce5d7ae8b716f8c9eee0f33d46dbcb2d9ef (patch) | |
| tree | f478c28629efdda2dd899b7ecc8d416ad564571e | |
| parent | b0fe29ed920e65510ed062c5293c4db3b9a02ab6 (diff) | |
| download | emacs-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.el | 93 |
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." | |||
| 3252 | def __PYTHON_EL_native_completion_setup(): | 3252 | def __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) |