diff options
Diffstat (limited to 'lisp/eshell')
-rw-r--r-- | lisp/eshell/em-alias.el | 4 | ||||
-rw-r--r-- | lisp/eshell/em-cmpl.el | 26 | ||||
-rw-r--r-- | lisp/eshell/em-unix.el | 12 | ||||
-rw-r--r-- | lisp/eshell/esh-arg.el | 115 | ||||
-rw-r--r-- | lisp/eshell/esh-cmd.el | 43 | ||||
-rw-r--r-- | lisp/eshell/esh-io.el | 125 | ||||
-rw-r--r-- | lisp/eshell/esh-opt.el | 4 | ||||
-rw-r--r-- | lisp/eshell/esh-util.el | 6 | ||||
-rw-r--r-- | lisp/eshell/esh-var.el | 35 |
9 files changed, 250 insertions, 120 deletions
diff --git a/lisp/eshell/em-alias.el b/lisp/eshell/em-alias.el index 9ad218d5988..9b75c7a1237 100644 --- a/lisp/eshell/em-alias.el +++ b/lisp/eshell/em-alias.el @@ -183,7 +183,9 @@ file named by `eshell-aliases-file'.") (pcomplete-here (eshell-alias-completions pcomplete-stub))) (defun eshell-read-aliases-list () - "Read in an aliases list from `eshell-aliases-file'." + "Read in an aliases list from `eshell-aliases-file'. +This is useful after manually editing the contents of the file." + (interactive) (let ((file eshell-aliases-file)) (when (file-readable-p file) (setq eshell-command-aliases-list diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el index ac82e3f225c..2c721eb9e31 100644 --- a/lisp/eshell/em-cmpl.el +++ b/lisp/eshell/em-cmpl.el @@ -342,17 +342,23 @@ to writing a completion function." (setq pos (1+ pos)))) (setq posns (cdr posns)) (cl-assert (= (length args) (length posns))) - (let ((a args) - (i 0) - l) + (let ((a args) (i 0) new-start) (while a - (if (and (consp (car a)) - (eq (caar a) 'eshell-operator)) - (setq l i)) - (setq a (cdr a) i (1+ i))) - (and l - (setq args (nthcdr (1+ l) args) - posns (nthcdr (1+ l) posns)))) + ;; 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)) + (eq (caar a) 'eshell-operator)) + (setq new-start i)) + (setq a (cdr a) + i (1+ i))) + (when new-start + (setq args (nthcdr (1+ new-start) args) + posns (nthcdr (1+ new-start) posns)))) (cl-assert (= (length args) (length posns))) (when (and args (eq (char-syntax (char-before end)) ? ) (not (eq (char-before (1- end)) ?\\))) diff --git a/lisp/eshell/em-unix.el b/lisp/eshell/em-unix.el index 4b5e4dd53ed..3f7ec618a33 100644 --- a/lisp/eshell/em-unix.el +++ b/lisp/eshell/em-unix.el @@ -786,10 +786,14 @@ external command." (defun eshell-complete-host-reference () "If there is a host reference, complete it." - (let ((arg (pcomplete-actual-arg)) - index) - (when (setq index (string-match "@[a-z.]*\\'" arg)) - (setq pcomplete-stub (substring arg (1+ index)) + (let ((arg (pcomplete-actual-arg))) + (when (string-match + (rx ;; Match an "@", but not immediately following a "$". + (or string-start (not "$")) "@" + (group (* (any "a-z."))) + string-end) + arg) + (setq pcomplete-stub (substring arg (match-beginning 1)) pcomplete-last-completion-raw t) (throw 'pcomplete-completions (pcomplete-read-host-names))))) diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el index 48ac3e2bd4d..87121196715 100644 --- a/lisp/eshell/esh-arg.el +++ b/lisp/eshell/esh-arg.el @@ -238,13 +238,53 @@ convert the result to a number as well." (eshell-convert-to-number result) result))) +(defun eshell-concat-groups (quoted &rest args) + "Concatenate groups of arguments in ARGS and return the result. +QUOTED is passed to `eshell-concat' (which see) and, if non-nil, +allows values to be converted to numbers where appropriate. + +ARGS should be a list of lists of arguments, such as that +produced by `eshell-prepare-slice'. \"Adjacent\" values of +consecutive arguments will be passed to `eshell-concat'. For +example, if ARGS is + + ((list a) (list b) (list c d e) (list f g)), + +then the result will be: + + ((eshell-concat QUOTED a b c) + d + (eshell-concat QUOTED e f) + g)." + (let (result current-arg) + (dolist (arg args) + (when arg + (push (car arg) current-arg) + (when (length> arg 1) + (push (apply #'eshell-concat quoted (nreverse current-arg)) + result) + (dolist (inner (butlast (cdr arg))) + (push inner result)) + (setq current-arg (list (car (last arg))))))) + (when current-arg + (push (apply #'eshell-concat quoted (nreverse current-arg)) + result)) + (nreverse result))) + (defun eshell-resolve-current-argument () "If there are pending modifications to be made, make them now." (when eshell-current-argument (when eshell-arg-listified - (setq eshell-current-argument - (append (list 'eshell-concat eshell-current-quoted) - eshell-current-argument)) + (if-let ((grouped-terms (eshell-prepare-splice + eshell-current-argument))) + (setq eshell-current-argument + `(eshell-splice-args + (eshell-concat-groups ,eshell-current-quoted + ,@grouped-terms))) + ;; If no terms are spliced, use a simpler command form. + (setq eshell-current-argument + (append (list 'eshell-concat eshell-current-quoted) + eshell-current-argument))) (setq eshell-arg-listified nil)) (while eshell-current-modifiers (setq eshell-current-argument @@ -261,7 +301,8 @@ argument list in place of the value of the current argument." (setq eshell-current-argument (car arguments)) (cl-assert (and (not eshell-arg-listified) (not eshell-current-modifiers))) - (setq eshell-current-argument (cons 'eshell-flatten-args arguments)))) + (setq eshell-current-argument + (cons 'eshell-splice-immediately arguments)))) (throw 'eshell-arg-done t)) (defun eshell-quote-argument (string) @@ -302,7 +343,8 @@ Point is left at the end of the arguments." (buffer-substring here (point-max)))) (when arg (nconc args - (if (eq (car-safe arg) 'eshell-flatten-args) + (if (eq (car-safe arg) + 'eshell-splice-immediately) (cdr arg) (list arg)))))))) (throw 'eshell-incomplete (if (listp delim) @@ -348,6 +390,10 @@ Point is left at the end of the arguments." "A stub function that generates an error if a floating operator is found." (error "Unhandled operator in input text")) +(defsubst eshell-splice-args (&rest _args) + "A stub function that generates an error if a floating splice is found." + (error "Splice operator is not permitted in this context")) + (defsubst eshell-looking-at-backslash-return (pos) "Test whether a backslash-return sequence occurs at POS." (and (eq (char-after pos) ?\\) @@ -377,20 +423,24 @@ after are both returned." (when (eshell-looking-at-backslash-return (point)) (throw 'eshell-incomplete ?\\)) (forward-char 2) ; Move one char past the backslash. - (if (eq (char-before) ?\n) - ;; Escaped newlines are extra-special: they expand to an empty - ;; token to allow for continuing Eshell commands across - ;; multiple lines. - 'eshell-empty-token - ;; If the char is in a quote, backslash only has special meaning - ;; if it is escaping a special char. - (if eshell-current-quoted - (if (memq (char-before) eshell-special-chars-inside-quoting) - (list 'eshell-escape-arg (char-to-string (char-before))) - (concat "\\" (char-to-string (char-before)))) - (if (memq (char-before) eshell-special-chars-outside-quoting) - (list 'eshell-escape-arg (char-to-string (char-before))) - (char-to-string (char-before))))))) + (let ((special-chars (if eshell-current-quoted + eshell-special-chars-inside-quoting + eshell-special-chars-outside-quoting))) + (cond + ;; Escaped newlines are extra-special: they expand to an empty + ;; token to allow for continuing Eshell commands across + ;; multiple lines. + ((eq (char-before) ?\n) + 'eshell-empty-token) + ((memq (char-before) special-chars) + (list 'eshell-escape-arg (char-to-string (char-before)))) + ;; If the char is in a quote, backslash only has special + ;; meaning if it is escaping a special char. Otherwise, the + ;; result is the literal string "\c". + (eshell-current-quoted + (concat "\\" (char-to-string (char-before)))) + (t + (char-to-string (char-before))))))) (defun eshell-parse-literal-quote () "Parse a literally quoted string. Nothing has special meaning!" @@ -496,5 +546,32 @@ If the form has no `type', the syntax is parsed as if `type' were (char-to-string (char-after))))) (goto-char end))))))) +(defun eshell-prepare-splice (args) + "Prepare a list of ARGS for splicing, if any arg requested a splice. +This looks for `eshell-splice-args' as the CAR of each argument, +and if found, returns a grouped list like: + + ((list arg-1) (list arg-2) spliced-arg-3 ...) + +This allows callers of this function to build the final spliced +list by concatenating each element together, e.g. with (apply +#'append grouped-list). + +If no argument requested a splice, return nil." + (let* ((splicep nil) + ;; Group each arg like ((list arg-1) (list arg-2) ...), + ;; splicing in `eshell-splice-args' args. This lets us + ;; apply spliced args correctly elsewhere. + (grouped-args + (mapcar (lambda (i) + (if (eq (car-safe i) 'eshell-splice-args) + (progn + (setq splicep t) + (cadr i)) + `(list ,i))) + args))) + (when splicep + grouped-args))) + (provide 'esh-arg) ;;; esh-arg.el ends here diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 4a41bbe8fa1..79957aeb416 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -418,8 +418,12 @@ hooks should be run before and after the command." (eshell-separate-commands terms "[&;]" nil 'eshell--sep-terms)))) (let ((cmd commands)) (while cmd - (if (cdr cmd) - (setcar cmd `(eshell-commands ,(car cmd)))) + ;; Copy I/O handles so each full statement can manipulate them + ;; if they like. As a small optimization, skip this for the + ;; last top-level one; we won't use these handles again + ;; anyway. + (when (or (not toplevel) (cdr cmd)) + (setcar cmd `(eshell-with-copied-handles ,(car cmd)))) (setq cmd (cdr cmd)))) (if toplevel `(eshell-commands (progn @@ -480,11 +484,16 @@ hooks should be run before and after the command." (let ((sym (if eshell-in-pipeline-p 'eshell-named-command* 'eshell-named-command)) - (cmd (car terms)) - (args (cdr terms))) - (if args - (list sym cmd `(list ,@(cdr terms))) - (list sym cmd)))) + (grouped-terms (eshell-prepare-splice terms))) + (cond + (grouped-terms + `(let ((terms (nconc ,@grouped-terms))) + (,sym (car terms) (cdr terms)))) + ;; If no terms are spliced, use a simpler command form. + ((cdr terms) + (list sym (car terms) `(list ,@(cdr terms)))) + (t + (list sym (car terms)))))) (defvar eshell-command-body) (defvar eshell-test-body) @@ -783,16 +792,15 @@ this grossness will be made to disappear by using `call/cc'..." (defvar eshell-output-handle) ;Defined in esh-io.el. (defvar eshell-error-handle) ;Defined in esh-io.el. -(defmacro eshell-copy-handles (object) +(defmacro eshell-with-copied-handles (object) "Duplicate current I/O handles, so OBJECT works with its own copy." `(let ((eshell-current-handles - (eshell-create-handles - (car (aref eshell-current-handles - eshell-output-handle)) nil - (car (aref eshell-current-handles - eshell-error-handle)) nil))) + (eshell-duplicate-handles eshell-current-handles))) ,object)) +(define-obsolete-function-alias 'eshell-copy-handles + #'eshell-with-copied-handles "30.1") + (defmacro eshell-protect (object) "Protect I/O handles, so they aren't get closed after eval'ing OBJECT." `(progn @@ -803,7 +811,7 @@ this grossness will be made to disappear by using `call/cc'..." "Execute the commands in PIPELINE, connecting each to one another. This macro calls itself recursively, with NOTFIRST non-nil." (when (setq pipeline (cadr pipeline)) - `(eshell-copy-handles + `(eshell-with-copied-handles (progn ,(when (cdr pipeline) `(let ((nextproc @@ -875,11 +883,8 @@ This is used on systems where async subprocesses are not supported." (progn ,(if (fboundp 'make-process) `(eshell-do-pipelines ,pipeline) - `(let ((tail-handles (eshell-create-handles - (car (aref eshell-current-handles - ,eshell-output-handle)) nil - (car (aref eshell-current-handles - ,eshell-error-handle)) nil))) + `(let ((tail-handles (eshell-duplicate-handles + eshell-current-handles))) (eshell-do-pipelines-synchronously ,pipeline))) (eshell-process-identity (cons (symbol-value headproc) (symbol-value tailproc)))))) diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index d223be680f9..f2bc87374c1 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -116,16 +116,22 @@ from executing while Emacs is redisplaying." :group 'eshell-io) (defcustom eshell-virtual-targets - '(("/dev/eshell" eshell-interactive-print nil) + '(;; The literal string "/dev/null" is intentional here. It just + ;; provides compatibility so that users can redirect to + ;; "/dev/null" no matter the actual value of `null-device'. + ("/dev/null" (lambda (_mode) (throw 'eshell-null-device t)) t) + ("/dev/eshell" eshell-interactive-print nil) ("/dev/kill" (lambda (mode) - (if (eq mode 'overwrite) - (kill-new "")) - 'eshell-kill-append) t) + (when (eq mode 'overwrite) + (kill-new "")) + #'eshell-kill-append) + t) ("/dev/clip" (lambda (mode) - (if (eq mode 'overwrite) - (let ((select-enable-clipboard t)) - (kill-new ""))) - 'eshell-clipboard-append) t)) + (when (eq mode 'overwrite) + (let ((select-enable-clipboard t)) + (kill-new ""))) + #'eshell-clipboard-append) + t)) "Map virtual devices name to Emacs Lisp functions. If the user specifies any of the filenames above as a redirection target, the function in the second element will be called. @@ -138,10 +144,8 @@ function. The output function is then called repeatedly with single strings, which represents successive pieces of the output of the command, until nil -is passed, meaning EOF. - -NOTE: /dev/null is handled specially as a virtual target, and should -not be added to this variable." +is passed, meaning EOF." + :version "30.1" :type '(repeat (list (string :tag "Target") function @@ -291,25 +295,42 @@ describing the mode, e.g. for using with `eshell-get-target'.") (defun eshell-create-handles (stdout output-mode &optional stderr error-mode) "Create a new set of file handles for a command. -The default location for standard output and standard error will go to -STDOUT and STDERR, respectively. -OUTPUT-MODE and ERROR-MODE are either `overwrite', `append' or `insert'; -a nil value of mode defaults to `insert'." +The default target for standard output and standard error will +go to STDOUT and STDERR, respectively. OUTPUT-MODE and +ERROR-MODE are either `overwrite', `append' or `insert'; a nil +value of mode defaults to `insert'. + +The result is a vector of file handles. Each handle is of the form: + + (TARGETS DEFAULT REF-COUNT) + +TARGETS is a list of destinations for output. DEFAULT is non-nil +if handle has its initial default value (always t after calling +this function). REF-COUNT is the number of references to this +handle (initially 1); see `eshell-protect-handles' and +`eshell-close-handles'." (let* ((handles (make-vector eshell-number-of-handles nil)) - (output-target (eshell-get-target stdout output-mode)) + (output-target (eshell-get-targets stdout output-mode)) (error-target (if stderr - (eshell-get-target stderr error-mode) + (eshell-get-targets stderr error-mode) output-target))) - (aset handles eshell-output-handle (cons output-target 1)) - (aset handles eshell-error-handle (cons error-target 1)) + (aset handles eshell-output-handle (list output-target t 1)) + (aset handles eshell-error-handle (list error-target t 1)) handles)) +(defun eshell-duplicate-handles (handles) + "Create a duplicate of the file handles in HANDLES. +This will copy the targets of each handle in HANDLES, setting the +DEFAULT field to t (see `eshell-create-handles')." + (eshell-create-handles + (car (aref handles eshell-output-handle)) nil + (car (aref handles eshell-error-handle)) nil)) + (defun eshell-protect-handles (handles) "Protect the handles in HANDLES from a being closed." (dotimes (idx eshell-number-of-handles) - (when (aref handles idx) - (setcdr (aref handles idx) - (1+ (cdr (aref handles idx)))))) + (when-let ((handle (aref handles idx))) + (setcar (nthcdr 2 handle) (1+ (nth 2 handle))))) handles) (defun eshell-close-handles (&optional exit-code result handles) @@ -330,8 +351,8 @@ the value already set in `eshell-last-command-result'." (let ((handles (or handles eshell-current-handles))) (dotimes (idx eshell-number-of-handles) (when-let ((handle (aref handles idx))) - (setcdr handle (1- (cdr handle))) - (when (= (cdr handle) 0) + (setcar (nthcdr 2 handle) (1- (nth 2 handle))) + (when (= (nth 2 handle) 0) (dolist (target (ensure-list (car (aref handles idx)))) (eshell-close-target target (= eshell-last-command-status 0))) (setcar handle nil)))))) @@ -340,23 +361,17 @@ the value already set in `eshell-last-command-result'." "Set handle INDEX for the current HANDLES to point to TARGET using MODE. If HANDLES is nil, use `eshell-current-handles'." (when target - (let ((handles (or handles eshell-current-handles))) - (if (and (stringp target) - ;; The literal string "/dev/null" is intentional here. - ;; It just provides compatibility so that users can - ;; redirect to "/dev/null" no matter the actual value - ;; of `null-device'. - (string= target "/dev/null")) - (aset handles index nil) - (let ((where (eshell-get-target target mode)) - (current (car (aref handles index)))) - (if (listp current) - (unless (member where current) - (setq current (append current (list where)))) - (setq current (list where))) - (if (not (aref handles index)) - (aset handles index (cons nil 1))) - (setcar (aref handles index) current)))))) + (let* ((handles (or handles eshell-current-handles)) + (handle (or (aref handles index) + (aset handles index (list nil nil 1)))) + (defaultp (cadr handle)) + (current (unless defaultp (car handle)))) + (catch 'eshell-null-device + (let ((where (eshell-get-target target mode))) + (unless (member where current) + (setq current (append current (list where)))))) + (setcar handle current) + (setcar (cdr handle) nil)))) (defun eshell-copy-output-handle (index index-to-copy &optional handles) "Copy the handle INDEX-TO-COPY to INDEX for the current HANDLES. @@ -486,6 +501,13 @@ it defaults to `insert'." (error "Invalid redirection target: %s" (eshell-stringify target))))) +(defun eshell-get-targets (targets &optional mode) + "Convert TARGETS into valid output targets. +TARGETS can be a single raw target or a list thereof. MODE is either +`overwrite', `append' or `insert'; if it is omitted or nil, it +defaults to `insert'." + (mapcar (lambda (i) (eshell-get-target i mode)) (ensure-list targets))) + (defun eshell-interactive-output-p (&optional index handles) "Return non-nil if the specified handle is bound for interactive display. HANDLES is the set of handles to check; if nil, use @@ -497,9 +519,9 @@ INDEX is the handle index to check. If nil, check (let ((handles (or handles eshell-current-handles)) (index (or index eshell-output-handle))) (if (eq index 'all) - (and (eq (car (aref handles eshell-output-handle)) t) - (eq (car (aref handles eshell-error-handle)) t)) - (eq (car (aref handles index)) t)))) + (and (equal (car (aref handles eshell-output-handle)) '(t)) + (equal (car (aref handles eshell-error-handle)) '(t))) + (equal (car (aref handles index)) '(t))))) (defvar eshell-print-queue nil) (defvar eshell-print-queue-count -1) @@ -606,15 +628,10 @@ Returns what was actually sent, or nil if nothing was sent." If HANDLE-INDEX is nil, output to `eshell-output-handle'. HANDLES is the set of file handles to use; if nil, use `eshell-current-handles'." - (let ((target (car (aref (or handles eshell-current-handles) - (or handle-index eshell-output-handle))))) - (if (listp target) - (while target - (eshell-output-object-to-target object (car target)) - (setq target (cdr target))) - (eshell-output-object-to-target object target) - ;; Explicitly return nil to match the list case above. - nil))) + (let ((targets (car (aref (or handles eshell-current-handles) + (or handle-index eshell-output-handle))))) + (dolist (target targets) + (eshell-output-object-to-target object target)))) (provide 'esh-io) ;;; esh-io.el ends here diff --git a/lisp/eshell/esh-opt.el b/lisp/eshell/esh-opt.el index f52b70fe7a6..551317d8339 100644 --- a/lisp/eshell/esh-opt.el +++ b/lisp/eshell/esh-opt.el @@ -132,7 +132,7 @@ This code doesn't really need to be macro expanded everywhere." (setq args (eshell--process-args name args options)) nil)))) (when usage-msg - (error "%s" usage-msg)))))) + (user-error "%s" usage-msg)))))) (if ext-command (throw 'eshell-external (eshell-external-command ext-command orig-args)) @@ -237,7 +237,7 @@ remaining characters in SWITCH to be processed later as further short options. If no matching handler is found, and an :external command is defined -(and available), it will be called; otherwise, an error will be +\(and available), it will be called; otherwise, an error will be triggered to say that the switch is unrecognized." (let ((switch (eshell--split-switch switch kind)) (opts options) diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el index 0ec11e8a0b3..aceca28befb 100644 --- a/lisp/eshell/esh-util.el +++ b/lisp/eshell/esh-util.el @@ -362,9 +362,13 @@ Prepend remote identification of `default-directory', if any." "Convert each element of ARGS into a string value." (mapcar #'eshell-stringify args)) +(defsubst eshell-list-to-string (list) + "Convert LIST into a single string separated by spaces." + (mapconcat #'eshell-stringify list " ")) + (defsubst eshell-flatten-and-stringify (&rest args) "Flatten and stringify all of the ARGS into a single string." - (mapconcat #'eshell-stringify (flatten-tree args) " ")) + (eshell-list-to-string (flatten-tree args))) (defsubst eshell-directory-files (regexp &optional directory) "Return a list of files in the given DIRECTORY matching REGEXP." diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el index 57ea42f4933..61e9af01a4d 100644 --- a/lisp/eshell/esh-var.el +++ b/lisp/eshell/esh-var.el @@ -86,6 +86,13 @@ ;; Returns the length of the value of $EXPR. This could also be ;; done using the `length' Lisp function. ;; +;; $@EXPR +;; +;; Splices the value of $EXPR in-place into the current list of +;; arguments. This is analogous to the `,@' token in Elisp +;; backquotes, and works as if the user typed '$EXPR[0] $EXPR[1] +;; ... $EXPR[N]'. +;; ;; There are also a few special variables defined by Eshell. '$$' is ;; the value of the last command (t or nil, in the case of an external ;; command). This makes it possible to chain results: @@ -320,10 +327,9 @@ copied (a.k.a. \"exported\") to the environment of created subprocesses." "Parse a variable interpolation. This function is explicit for adding to `eshell-parse-argument-hook'." (when (and (eq (char-after) ?$) - (/= (1+ (point)) (point-max))) + (/= (1+ (point)) (point-max))) (forward-char) - (list 'eshell-escape-arg - (eshell-parse-variable)))) + (eshell-parse-variable))) (defun eshell/define (var-alias definition) "Define a VAR-ALIAS using DEFINITION." @@ -453,6 +459,8 @@ Its purpose is to call `eshell-parse-variable-ref', and then to process any indices that come after the variable reference." (let* ((get-len (when (eq (char-after) ?#) (forward-char) t)) + (splice (when (eq (char-after) ?@) + (forward-char) t)) value indices) (setq value (eshell-parse-variable-ref get-len) indices (and (not (eobp)) @@ -464,7 +472,13 @@ process any indices that come after the variable reference." (when get-len (setq value `(length ,value))) (when eshell-current-quoted - (setq value `(eshell-stringify ,value))) + (if splice + (setq value `(eshell-list-to-string ,value) + splice nil) + (setq value `(eshell-stringify ,value)))) + (setq value `(eshell-escape-arg ,value)) + (when splice + (setq value `(eshell-splice-args ,value))) value)) (defun eshell-parse-variable-ref (&optional modifier-p) @@ -751,12 +765,13 @@ For example, to retrieve the second element of a user's record in (defun eshell-complete-variable-reference () "If there is a variable reference, complete it." - (let ((arg (pcomplete-actual-arg)) index) - (when (setq index - (string-match - (concat "\\$\\(" eshell-variable-name-regexp - "\\)?\\'") arg)) - (setq pcomplete-stub (substring arg (1+ index))) + (let ((arg (pcomplete-actual-arg))) + (when (string-match + (rx "$" (? (or "#" "@")) + (? (group (regexp eshell-variable-name-regexp))) + string-end) + arg) + (setq pcomplete-stub (substring arg (match-beginning 1))) (throw 'pcomplete-completions (eshell-variables-list))))) (defun eshell-variables-list () |