From 39fe1f15f0804e608891f158afc49e806f969e98 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Sun, 15 Jan 2023 16:44:23 -0800 Subject: [PATCH 2/2] Properly parse Eshell variable splices for interactive completion Previously, the code simply ignored the splice operator, which usually worked, but isn't actually correct. * lisp/eshell/em-cmpl.el (eshell-complete-eval-argument-form): New function. (eshell-complete-parse-arguments): Properly parse variable splices. * test/lisp/eshell/em-cmpl-tests.el (em-cmpl-test/parse-arguments/variable-splice): New test. --- lisp/eshell/em-cmpl.el | 56 ++++++++++++++++++++----------- test/lisp/eshell/em-cmpl-tests.el | 8 +++++ 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el index 4206ad048fa..a67fe53ed46 100644 --- a/lisp/eshell/em-cmpl.el +++ b/lisp/eshell/em-cmpl.el @@ -306,6 +306,12 @@ eshell--pcomplete-insert-tab (insert-and-inherit "\t") (throw 'pcompleted t))) +(defun eshell-complete-eval-argument-form (arg) + "Evaluate a single Eshell argument form ARG for the purposes of completion." + (let ((result (eshell-do-eval `(eshell-commands ,arg) t))) + (cl-assert (eq (car result) 'quote)) + (cadr result))) + (defun eshell-complete-parse-arguments () "Parse the command line arguments for `pcomplete-argument'." (when (and eshell-no-completion-during-jobs @@ -344,11 +350,6 @@ eshell-complete-parse-arguments (cl-assert (= (length args) (length posns))) (let ((a args) (i 0) new-start) (while a - ;; Remove any top-level `eshell-splice-args' sigils. These - ;; are meant to be rewritten and can't actually be called. - (when (and (consp (car a)) - (eq (caar a) 'eshell-splice-args)) - (setcar a (cadar a))) ;; If there's an unreplaced `eshell-operator' sigil, consider ;; the token after it the new start of our arguments. (when (and (consp (car a)) @@ -364,23 +365,38 @@ eshell-complete-parse-arguments (not (eq (char-before (1- end)) ?\\))) (nconc args (list "")) (nconc posns (list (point)))) + ;; Evaluate and expand Eshell forms. + (let (evaled-args evaled-posns) + (cl-mapc + (lambda (arg posn) + (pcase arg + (`(eshell-splice-args ,val) + (dolist (subarg (eshell-complete-eval-argument-form val)) + (push subarg evaled-args) + (push posn evaled-posns))) + ((pred listp) + (push (eshell-complete-eval-argument-form arg) evaled-args) + (push posn evaled-posns)) + (_ + (push arg evaled-args) + (push posn evaled-posns)))) + args posns) + (setq args (nreverse evaled-args) + posns (nreverse evaled-posns))) + ;; Convert arguments to forms that Pcomplete can understand. (cons (mapcar (lambda (arg) - (let ((val - (if (listp arg) - (let ((result - (eshell-do-eval - (list 'eshell-commands arg) t))) - (cl-assert (eq (car result) 'quote)) - (cadr result)) - arg))) - (cond ((numberp val) - (setq val (number-to-string val))) - ;; expand .../ etc that only eshell understands to - ;; standard ../../ - ((and (stringp val)) (string-match "\\.\\.\\.+/" val) - (setq val (eshell-expand-multiple-dots val)))) - (or val ""))) + (cond + ((numberp arg) + (number-to-string arg)) + ;; Expand ".../" etc that only Eshell understands to the + ;; standard "../../". + ((and (stringp arg) (string-match "\\.\\.\\.+/" arg)) + (eshell-expand-multiple-dots arg)) + ((null arg) + "") + (t + arg))) args) posns))) diff --git a/test/lisp/eshell/em-cmpl-tests.el b/test/lisp/eshell/em-cmpl-tests.el index 837af9d1061..2460f58c25c 100644 --- a/test/lisp/eshell/em-cmpl-tests.el +++ b/test/lisp/eshell/em-cmpl-tests.el @@ -62,6 +62,14 @@ em-cmpl-test/parse-arguments/variable (should (equal (car (eshell-complete-parse-arguments)) `("echo" ("foo" "bar"))))))) +(ert-deftest em-cmpl-test/parse-arguments/variable-splice () + "Test parsing arguments with a spliced variable interpolation." + (with-temp-eshell + (let ((eshell-test-value '("foo" "bar"))) + (insert "echo $@eshell-test-value") + (should (equal (car (eshell-complete-parse-arguments)) + `("echo" "foo" "bar")))))) + (ert-deftest em-cmpl-test/file-completion/unique () "Test completion of file names when there's a unique result." (with-temp-eshell -- 2.25.1