aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Porter2023-01-15 16:44:23 -0800
committerJim Porter2023-01-30 17:49:11 -0800
commitcc5a2ed457eb34543bb7aaf6b39663af2599805d (patch)
tree21f3755be4249b38a49b68429cd7538821ea1411
parent79154f625cc4f1db3cd2b9df1a3d88def27e0d56 (diff)
downloademacs-cc5a2ed457eb34543bb7aaf6b39663af2599805d.tar.gz
emacs-cc5a2ed457eb34543bb7aaf6b39663af2599805d.zip
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.
-rw-r--r--lisp/eshell/em-cmpl.el56
-rw-r--r--test/lisp/eshell/em-cmpl-tests.el8
2 files changed, 44 insertions, 20 deletions
diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el
index 4206ad048fa..d1c7e81090a 100644
--- a/lisp/eshell/em-cmpl.el
+++ b/lisp/eshell/em-cmpl.el
@@ -306,6 +306,12 @@ to writing a completion function."
306 (insert-and-inherit "\t") 306 (insert-and-inherit "\t")
307 (throw 'pcompleted t))) 307 (throw 'pcompleted t)))
308 308
309(defun eshell-complete--eval-argument-form (arg)
310 "Evaluate a single Eshell argument form ARG for the purposes of completion."
311 (let ((result (eshell-do-eval `(eshell-commands ,arg) t)))
312 (cl-assert (eq (car result) 'quote))
313 (cadr result)))
314
309(defun eshell-complete-parse-arguments () 315(defun eshell-complete-parse-arguments ()
310 "Parse the command line arguments for `pcomplete-argument'." 316 "Parse the command line arguments for `pcomplete-argument'."
311 (when (and eshell-no-completion-during-jobs 317 (when (and eshell-no-completion-during-jobs
@@ -344,11 +350,6 @@ to writing a completion function."
344 (cl-assert (= (length args) (length posns))) 350 (cl-assert (= (length args) (length posns)))
345 (let ((a args) (i 0) new-start) 351 (let ((a args) (i 0) new-start)
346 (while a 352 (while a
347 ;; Remove any top-level `eshell-splice-args' sigils. These
348 ;; are meant to be rewritten and can't actually be called.
349 (when (and (consp (car a))
350 (eq (caar a) 'eshell-splice-args))
351 (setcar a (cadar a)))
352 ;; If there's an unreplaced `eshell-operator' sigil, consider 353 ;; If there's an unreplaced `eshell-operator' sigil, consider
353 ;; the token after it the new start of our arguments. 354 ;; the token after it the new start of our arguments.
354 (when (and (consp (car a)) 355 (when (and (consp (car a))
@@ -364,23 +365,38 @@ to writing a completion function."
364 (not (eq (char-before (1- end)) ?\\))) 365 (not (eq (char-before (1- end)) ?\\)))
365 (nconc args (list "")) 366 (nconc args (list ""))
366 (nconc posns (list (point)))) 367 (nconc posns (list (point))))
368 ;; Evaluate and expand Eshell forms.
369 (let (evaled-args evaled-posns)
370 (cl-mapc
371 (lambda (arg posn)
372 (pcase arg
373 (`(eshell-splice-args ,val)
374 (dolist (subarg (eshell-complete--eval-argument-form val))
375 (push subarg evaled-args)
376 (push posn evaled-posns)))
377 ((pred listp)
378 (push (eshell-complete--eval-argument-form arg) evaled-args)
379 (push posn evaled-posns))
380 (_
381 (push arg evaled-args)
382 (push posn evaled-posns))))
383 args posns)
384 (setq args (nreverse evaled-args)
385 posns (nreverse evaled-posns)))
386 ;; Convert arguments to forms that Pcomplete can understand.
367 (cons (mapcar 387 (cons (mapcar
368 (lambda (arg) 388 (lambda (arg)
369 (let ((val 389 (cond
370 (if (listp arg) 390 ((numberp arg)
371 (let ((result 391 (number-to-string arg))
372 (eshell-do-eval 392 ;; Expand ".../" etc that only Eshell understands to the
373 (list 'eshell-commands arg) t))) 393 ;; standard "../../".
374 (cl-assert (eq (car result) 'quote)) 394 ((and (stringp arg) (string-match "\\.\\.\\.+/" arg))
375 (cadr result)) 395 (eshell-expand-multiple-dots arg))
376 arg))) 396 ((null arg)
377 (cond ((numberp val) 397 "")
378 (setq val (number-to-string val))) 398 (t
379 ;; expand .../ etc that only eshell understands to 399 arg)))
380 ;; standard ../../
381 ((and (stringp val)) (string-match "\\.\\.\\.+/" val)
382 (setq val (eshell-expand-multiple-dots val))))
383 (or val "")))
384 args) 400 args)
385 posns))) 401 posns)))
386 402
diff --git a/test/lisp/eshell/em-cmpl-tests.el b/test/lisp/eshell/em-cmpl-tests.el
index 32b0781dd75..3f8f890f6e5 100644
--- a/test/lisp/eshell/em-cmpl-tests.el
+++ b/test/lisp/eshell/em-cmpl-tests.el
@@ -85,6 +85,14 @@
85 (should (equal (car (eshell-complete-parse-arguments)) 85 (should (equal (car (eshell-complete-parse-arguments))
86 '("echo" ("foo" "bar"))))))) 86 '("echo" ("foo" "bar")))))))
87 87
88(ert-deftest em-cmpl-test/parse-arguments/variable/splice ()
89 "Test parsing arguments with a spliced variable interpolation."
90 (with-temp-eshell
91 (let ((eshell-test-value '("foo" "bar")))
92 (insert "echo $@eshell-test-value")
93 (should (equal (car (eshell-complete-parse-arguments))
94 '("echo" "foo" "bar"))))))
95
88(ert-deftest em-cmpl-test/file-completion/unique () 96(ert-deftest em-cmpl-test/file-completion/unique ()
89 "Test completion of file names when there's a unique result." 97 "Test completion of file names when there's a unique result."
90 (with-temp-eshell 98 (with-temp-eshell