Subject: Re: bug#54227: 29.0.50; [PATCH] Inconsistencies with Eshell variable interpolation

From: Jim Porter <[email-masked]>
Date: Sat, 5 Mar 2022 12:06:00 -0800

On 3/3/2022 11:50 AM, Eli Zaretskii wrote:
> Thanks, this now LGTM.

Thanks.

I found one more issue with the code though: the subscript operator 
doesn't work on subcommands. For example, from "emacs -Q --eval '(eshell)'":

   ~ $ echo ${*echo -e "hi\nbye"}[0]
   ("hi" "bye")

Since `${COMMAND}' forms split the output line-by-line, you'd expect 
this to say "hi", but the subscript operator is a no-op in this case. 
This was previously documented to work in the docstring for 
`eshell-apply-indices':

   For example, to retrieve the second element of a user's record in
   '/etc/passwd', the variable reference would look like:

     ${grep johnw /etc/passwd}[: 2]

I also updated the manual to indicate that this is possible (though I 
didn't provide any direct examples), since I thought this already worked 
based on that docstring.

Attached is a patch with some tests for this.

Just a note: using subscript on `$<COMMAND>' forms is probably not 
super-useful (at least not currently), though I added support for it 
anyway for consistency and future improvement. Since the result of that 
form is the name of a temp file, there's not much reason to do something 
like `$<COMMAND>[0]'. However in the future, if the subscript operator 
were more advanced, you could do something like `$<COMMAND>[/ *]' to 
split the file name by directory separators. (The "*" is a suggested 
feature in the "Bugs and ideas" section to return the whole list.)
From 1e7bb60551612b16ac5b9490603eb93d5db1921d Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Sat, 5 Mar 2022 11:45:49 -0800
Subject: [PATCH] Support applying indices to more Eshell dollar expansions

For example, '${echo -e "hi\nbye"}[1]' should expand to "bye".

* lisp/eshell/esh-var.el (eshell-parse-variable-ref): Support applying
indices to '${}', '$()', and '$<>' forms.

* lisp/eshell/esh-var-tests.el (esh-var-test/interp-lisp-indices)
(esh-var-test/interp-cmd-indices)
(esh-var-test/interp-cmd-external-indices)
(esh-var-test/quoted-interp-lisp-indices)
(esh-var-test/quoted-interp-cmd-indices): New tests.
---
 lisp/eshell/esh-var.el            | 28 ++++++++++++++++------------
 test/lisp/eshell/esh-var-tests.el | 23 +++++++++++++++++++++++
 2 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el
index af89e35f55..e8df66d999 100644
--- a/lisp/eshell/esh-var.el
+++ b/lisp/eshell/esh-var.el
@@ -434,13 +434,15 @@ eshell-parse-variable-ref
           (throw 'eshell-incomplete ?\{)
         (forward-char)
         (prog1
-            `(eshell-convert
-              (eshell-command-to-value
-               (eshell-as-subcommand
-                ,(let ((subcmd (or (eshell-parse-inner-double-quote end)
-                                   (cons (point) end)))
-                       (eshell-current-quoted nil))
-                   (eshell-parse-command subcmd)))))
+            `(eshell-apply-indices
+              (eshell-convert
+               (eshell-command-to-value
+                (eshell-as-subcommand
+                 ,(let ((subcmd (or (eshell-parse-inner-double-quote end)
+                                    (cons (point) end)))
+                        (eshell-current-quoted nil))
+                    (eshell-parse-command subcmd)))))
+              indices)
           (goto-char (1+ end))))))
    ((eq (char-after) ?\<)
     (let ((end (eshell-find-delimiter ?\< ?\>)))
@@ -464,14 +466,16 @@ eshell-parse-variable-ref
                            ;; properly.  See bug#54190.
                            (list (function (lambda ()
                                    (delete-file ,temp))))))
-                   (quote ,temp)))
+                   (eshell-apply-indices ,temp indices)))
             (goto-char (1+ end)))))))
    ((eq (char-after) ?\()
     (condition-case nil
-        `(eshell-command-to-value
-          (eshell-lisp-command
-           ',(read (or (eshell-parse-inner-double-quote (point-max))
-                       (current-buffer)))))
+        `(eshell-apply-indices
+          (eshell-command-to-value
+           (eshell-lisp-command
+            ',(read (or (eshell-parse-inner-double-quote (point-max))
+                        (current-buffer)))))
+          indices)
       (end-of-file
        (throw 'eshell-incomplete ?\())))
    ((looking-at (rx (or "'" "\"" "\\\"")))
diff --git a/test/lisp/eshell/esh-var-tests.el b/test/lisp/eshell/esh-var-tests.el
index d09dd614de..1d051d681a 100644
--- a/test/lisp/eshell/esh-var-tests.el
+++ b/test/lisp/eshell/esh-var-tests.el
@@ -137,10 +137,18 @@ esh-var-test/interp-lisp
   "Interpolate Lisp form evaluation"
   (should (equal (eshell-test-command-result "+ $(+ 1 2) 3") 6)))
 
+(ert-deftest esh-var-test/interp-lisp-indices ()
+  "Interpolate Lisp form evaluation with index"
+  (should (equal (eshell-test-command-result "+ $(list 1 2)[1] 3") 5)))
+
 (ert-deftest esh-var-test/interp-cmd ()
   "Interpolate command result"
   (should (equal (eshell-test-command-result "+ ${+ 1 2} 3") 6)))
 
+(ert-deftest esh-var-test/interp-cmd-indices ()
+  "Interpolate command result with index"
+  (should (equal (eshell-test-command-result "+ ${list 1 2}[1] 3") 5)))
+
 (ert-deftest esh-var-test/interp-cmd-external ()
   "Interpolate command result from external command"
   (skip-unless (executable-find "echo"))
@@ -148,6 +156,13 @@ esh-var-test/interp-cmd-external
    (eshell-command-result-p "echo ${*echo hi}"
                             "hi\n")))
 
+(ert-deftest esh-var-test/interp-cmd-external-indices ()
+  "Interpolate command result from external command with index"
+  (skip-unless (executable-find "echo"))
+  (with-temp-eshell
+   (eshell-command-result-p "echo ${*echo \"hi\nbye\"}[1]"
+                            "bye\n")))
+
 (ert-deftest esh-var-test/interp-temp-cmd ()
   "Interpolate command result redirected to temp file"
   (should (equal (eshell-test-command-result "cat $<echo hi>") "hi")))
@@ -282,12 +297,20 @@ esh-var-test/quoted-interp-lisp
                   "echo \"hi $(concat \\\"the\\\" \\\"re\\\")\"")
                  "hi there")))
 
+(ert-deftest esh-var-test/quoted-interp-lisp-indices ()
+  "Interpolate Lisp form evaluation with index"
+  (should (equal (eshell-test-command-result "+ \"$(list 1 2)[1]\" 3") 5)))
+
 (ert-deftest esh-var-test/quoted-interp-cmd ()
   "Interpolate command result inside double-quotes"
   (should (equal (eshell-test-command-result
                   "echo \"hi ${echo \\\"there\\\"}\"")
                  "hi there")))
 
+(ert-deftest esh-var-test/quoted-interp-cmd-indices ()
+  "Interpolate command result with index inside double-quotes"
+  (should (equal (eshell-test-command-result "+ \"${list 1 2}[1]\" 3") 5)))
+
 (ert-deftest esh-var-test/quoted-interp-temp-cmd ()
   "Interpolate command result redirected to temp file inside double-quotes"
   (should (equal (eshell-test-command-result "cat \"$<echo hi>\"") "hi")))
-- 
2.25.1