From 82c76e3aeb2465d1d1e66eae5db13ba53e38ed84 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 31 Jan 2021 19:27:10 -0500 Subject: * lisp/eshell/em-cmpl.el: Try and fix bug#41423 (eshell--complete-commands-list): Rename from `eshell-complete-commands-list`. Return a (dynamic) completion table rather than a list of completions. Use `dolist` and `push`. --- lisp/eshell/em-cmpl.el | 118 +++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el index 0200631da66..e0b3ab1ecf4 100644 --- a/lisp/eshell/em-cmpl.el +++ b/lisp/eshell/em-cmpl.el @@ -211,7 +211,7 @@ to writing a completion function." (defcustom eshell-command-completion-function (lambda () - (pcomplete-here (eshell-complete-commands-list))) + (pcomplete-here (eshell--complete-commands-list))) (eshell-cmpl--custom-variable-docstring 'pcomplete-command-completion-function) :type (get 'pcomplete-command-completion-function 'custom-type) :group 'eshell-cmpl) @@ -403,64 +403,66 @@ to writing a completion function." args) posns))) -(defun eshell-complete-commands-list () +(defun eshell--complete-commands-list () "Generate list of applicable, visible commands." - (let ((filename (pcomplete-arg)) glob-name) - (if (file-name-directory filename) - (if eshell-force-execution - (pcomplete-dirs-or-entries nil #'file-readable-p) - (pcomplete-executables)) - (if (and (> (length filename) 0) - (eq (aref filename 0) eshell-explicit-command-char)) - (setq filename (substring filename 1) - pcomplete-stub filename - glob-name t)) - (let* ((paths (eshell-get-path)) - (cwd (file-name-as-directory - (expand-file-name default-directory))) - (path "") (comps-in-path ()) - (file "") (filepath "") (completions ())) - ;; Go thru each path in the search path, finding completions. - (while paths - (setq path (file-name-as-directory - (expand-file-name (or (car paths) "."))) - comps-in-path - (and (file-accessible-directory-p path) - (file-name-all-completions filename path))) - ;; Go thru each completion found, to see whether it should - ;; be used. - (while comps-in-path - (setq file (car comps-in-path) - filepath (concat path file)) - (if (and (not (member file completions)) ; - (or (string-equal path cwd) - (not (file-directory-p filepath))) - (if eshell-force-execution - (file-readable-p filepath) - (file-executable-p filepath))) - (setq completions (cons file completions))) - (setq comps-in-path (cdr comps-in-path))) - (setq paths (cdr paths))) - ;; Add aliases which are currently visible, and Lisp functions. - (pcomplete-uniquify-list - (if glob-name - completions - (setq completions - (append (if (fboundp 'eshell-alias-completions) - (eshell-alias-completions filename)) - (eshell-winnow-list - (mapcar - (lambda (name) - (substring name 7)) - (all-completions (concat "eshell/" filename) - obarray #'functionp)) - nil '(eshell-find-alias-function)) - completions)) - (append (and (or eshell-show-lisp-completions - (and eshell-show-lisp-alternatives - (null completions))) - (all-completions filename obarray #'functionp)) - completions))))))) + ;; Building the commands list can take quite a while, especially over Tramp + ;; (bug#41423), so do it lazily. + (completion-table-dynamic + (lambda (filename) + (if (file-name-directory filename) + (if eshell-force-execution + (pcomplete-dirs-or-entries nil #'file-readable-p) + (pcomplete-executables)) + (let (glob-name) + (if (and (> (length filename) 0) + (eq (aref filename 0) eshell-explicit-command-char)) + ;; FIXME: Shouldn't we handle this `*' outside of the + ;; `pcomplete-here' in `eshell-command-completion-function'? + (setq filename (substring filename 1) + pcomplete-stub filename + glob-name t)) + (let* ((paths (eshell-get-path)) + (cwd (file-name-as-directory + (expand-file-name default-directory))) + (filepath "") (completions ())) + ;; Go thru each path in the search path, finding completions. + (dolist (path paths) + (setq path (file-name-as-directory + (expand-file-name (or path ".")))) + ;; Go thru each completion found, to see whether it should + ;; be used. + (dolist (file (and (file-accessible-directory-p path) + (file-name-all-completions filename path))) + (setq filepath (concat path file)) + (if (and (not (member file completions)) ; + (or (string-equal path cwd) + (not (file-directory-p filepath))) + ;; FIXME: Those repeated file tests end up + ;; very costly over Tramp, we should cache the result. + (if eshell-force-execution + (file-readable-p filepath) + (file-executable-p filepath))) + (push file completions)))) + ;; Add aliases which are currently visible, and Lisp functions. + (pcomplete-uniquify-list + (if glob-name + completions + (setq completions + (append (if (fboundp 'eshell-alias-completions) + (eshell-alias-completions filename)) + (eshell-winnow-list + (mapcar + (lambda (name) + (substring name 7)) + (all-completions (concat "eshell/" filename) + obarray #'functionp)) + nil '(eshell-find-alias-function)) + completions)) + (append (and (or eshell-show-lisp-completions + (and eshell-show-lisp-alternatives + (null completions))) + (all-completions filename obarray #'functionp)) + completions))))))))) (define-obsolete-function-alias 'eshell-pcomplete #'completion-at-point "27.1") -- cgit v1.2.3