diff options
| author | Jim Porter | 2022-02-27 21:04:30 -0800 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2022-03-03 14:59:33 +0100 |
| commit | cccee7e840102488e01f9bb7c2220392d358f4f0 (patch) | |
| tree | 1077dd929cf7908fd8c76b63d135b6fe1834bb60 | |
| parent | ae1acb601764009fc2551819f9193aa6e9441be4 (diff) | |
| download | emacs-cccee7e840102488e01f9bb7c2220392d358f4f0.tar.gz emacs-cccee7e840102488e01f9bb7c2220392d358f4f0.zip | |
Fix Eshell dollar interpolation inside of double-quotes
For example,
echo "${echo hi}"
previously tried to run the program named 'echo hi', instead of 'echo'
with the argument 'hi'.
* lisp/eshell/esh-arg.el (eshell-parse-inner-double-quote):
New function.
* lisp/eshell/esh-var.el (eshell-parse-variable-ref): Support parsing
when wrapped in double-quiotes.
* test/lisp/eshell/esh-var-tests.el (esh-var-test/interp-var)
(esh-var-test/interp-quoted-var)
(esh-var-test/interp-quoted-var-concat)
(esh-var-test/quoted-interp-var)
(esh-var-test/quoted-interp-quoted-var)
(esh-var-test/quoted-interp-lisp, esh-var-test/quoted-interp-cmd)
(esh-var-test/quoted-interp-temp-cmd): New tests.
| -rw-r--r-- | lisp/eshell/esh-arg.el | 24 | ||||
| -rw-r--r-- | lisp/eshell/esh-var.el | 27 | ||||
| -rw-r--r-- | test/lisp/eshell/esh-var-tests.el | 49 |
3 files changed, 91 insertions, 9 deletions
diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el index 1a2f2a57e8e..e19481c4ba9 100644 --- a/lisp/eshell/esh-arg.el +++ b/lisp/eshell/esh-arg.el | |||
| @@ -354,6 +354,30 @@ after are both returned." | |||
| 354 | (list 'eshell-escape-arg arg)))) | 354 | (list 'eshell-escape-arg arg)))) |
| 355 | (goto-char (1+ end))))))) | 355 | (goto-char (1+ end))))))) |
| 356 | 356 | ||
| 357 | (defun eshell-parse-inner-double-quote (bound) | ||
| 358 | "Parse the inner part of a double quoted string. | ||
| 359 | The string to parse starts at point and ends at BOUND. | ||
| 360 | |||
| 361 | If Eshell is currently parsing a quoted string and there are any | ||
| 362 | backslash-escaped characters, this will return the unescaped | ||
| 363 | string, updating point to BOUND. Otherwise, this returns nil and | ||
| 364 | leaves point where it was." | ||
| 365 | (when eshell-current-quoted | ||
| 366 | (let (strings | ||
| 367 | (start (point)) | ||
| 368 | (special-char | ||
| 369 | (rx-to-string | ||
| 370 | `(seq "\\" (group (any ,@eshell-special-chars-inside-quoting)))))) | ||
| 371 | (while (re-search-forward special-char bound t) | ||
| 372 | (push (concat (buffer-substring start (match-beginning 0)) | ||
| 373 | (match-string 1)) | ||
| 374 | strings) | ||
| 375 | (setq start (match-end 0))) | ||
| 376 | (when strings | ||
| 377 | (push (buffer-substring start bound) strings) | ||
| 378 | (goto-char bound) | ||
| 379 | (apply #'concat (nreverse strings)))))) | ||
| 380 | |||
| 357 | (defun eshell-parse-special-reference () | 381 | (defun eshell-parse-special-reference () |
| 358 | "Parse a special syntax reference, of the form `#<args>'. | 382 | "Parse a special syntax reference, of the form `#<args>'. |
| 359 | 383 | ||
diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el index ee3ffbc6475..24fdbde3cfb 100644 --- a/lisp/eshell/esh-var.el +++ b/lisp/eshell/esh-var.el | |||
| @@ -440,18 +440,16 @@ Possible options are: | |||
| 440 | (let ((end (eshell-find-delimiter ?\{ ?\}))) | 440 | (let ((end (eshell-find-delimiter ?\{ ?\}))) |
| 441 | (if (not end) | 441 | (if (not end) |
| 442 | (throw 'eshell-incomplete ?\{) | 442 | (throw 'eshell-incomplete ?\{) |
| 443 | (forward-char) | ||
| 443 | (prog1 | 444 | (prog1 |
| 444 | `(eshell-convert | 445 | `(eshell-convert |
| 445 | (eshell-command-to-value | 446 | (eshell-command-to-value |
| 446 | (eshell-as-subcommand | 447 | (eshell-as-subcommand |
| 447 | ,(eshell-parse-command (cons (1+ (point)) end))))) | 448 | ,(let ((subcmd (or (eshell-parse-inner-double-quote end) |
| 449 | (cons (point) end))) | ||
| 450 | (eshell-current-quoted nil)) | ||
| 451 | (eshell-parse-command subcmd))))) | ||
| 448 | (goto-char (1+ end)))))) | 452 | (goto-char (1+ end)))))) |
| 449 | ((memq (char-after) '(?\' ?\")) | ||
| 450 | (let ((name (if (eq (char-after) ?\') | ||
| 451 | (eshell-parse-literal-quote) | ||
| 452 | (eshell-parse-double-quote)))) | ||
| 453 | (if name | ||
| 454 | `(eshell-get-variable ,(eval name) indices)))) | ||
| 455 | ((eq (char-after) ?\<) | 453 | ((eq (char-after) ?\<) |
| 456 | (let ((end (eshell-find-delimiter ?\< ?\>))) | 454 | (let ((end (eshell-find-delimiter ?\< ?\>))) |
| 457 | (if (not end) | 455 | (if (not end) |
| @@ -463,7 +461,9 @@ Possible options are: | |||
| 463 | `(let ((eshell-current-handles | 461 | `(let ((eshell-current-handles |
| 464 | (eshell-create-handles ,temp 'overwrite))) | 462 | (eshell-create-handles ,temp 'overwrite))) |
| 465 | (progn | 463 | (progn |
| 466 | (eshell-as-subcommand ,(eshell-parse-command cmd)) | 464 | (eshell-as-subcommand |
| 465 | ,(let ((eshell-current-quoted nil)) | ||
| 466 | (eshell-parse-command cmd))) | ||
| 467 | (ignore | 467 | (ignore |
| 468 | (nconc eshell-this-command-hook | 468 | (nconc eshell-this-command-hook |
| 469 | ;; Quote this lambda; it will be evaluated | 469 | ;; Quote this lambda; it will be evaluated |
| @@ -478,9 +478,18 @@ Possible options are: | |||
| 478 | (condition-case nil | 478 | (condition-case nil |
| 479 | `(eshell-command-to-value | 479 | `(eshell-command-to-value |
| 480 | (eshell-lisp-command | 480 | (eshell-lisp-command |
| 481 | ',(read (current-buffer)))) | 481 | ',(read (or (eshell-parse-inner-double-quote (point-max)) |
| 482 | (current-buffer))))) | ||
| 482 | (end-of-file | 483 | (end-of-file |
| 483 | (throw 'eshell-incomplete ?\()))) | 484 | (throw 'eshell-incomplete ?\()))) |
| 485 | ((looking-at (rx (or "'" "\"" "\\\""))) | ||
| 486 | (eshell-with-temp-command (or (eshell-parse-inner-double-quote (point-max)) | ||
| 487 | (cons (point) (point-max))) | ||
| 488 | (let ((name (if (eq (char-after) ?\') | ||
| 489 | (eshell-parse-literal-quote) | ||
| 490 | (eshell-parse-double-quote)))) | ||
| 491 | (when name | ||
| 492 | `(eshell-get-variable ,(eval name) indices))))) | ||
| 484 | ((assoc (char-to-string (char-after)) | 493 | ((assoc (char-to-string (char-after)) |
| 485 | eshell-variable-aliases-list) | 494 | eshell-variable-aliases-list) |
| 486 | (forward-char) | 495 | (forward-char) |
diff --git a/test/lisp/eshell/esh-var-tests.el b/test/lisp/eshell/esh-var-tests.el index 8d803e5ca49..7ec6a975198 100644 --- a/test/lisp/eshell/esh-var-tests.el +++ b/test/lisp/eshell/esh-var-tests.el | |||
| @@ -37,6 +37,25 @@ | |||
| 37 | 37 | ||
| 38 | ;; Variable interpolation | 38 | ;; Variable interpolation |
| 39 | 39 | ||
| 40 | (ert-deftest esh-var-test/interp-var () | ||
| 41 | "Interpolate variable" | ||
| 42 | (should (equal (eshell-test-command-result "echo $user-login-name") | ||
| 43 | user-login-name))) | ||
| 44 | |||
| 45 | (ert-deftest esh-var-test/interp-quoted-var () | ||
| 46 | "Interpolate quoted variable" | ||
| 47 | (should (equal (eshell-test-command-result "echo $'user-login-name'") | ||
| 48 | user-login-name)) | ||
| 49 | (should (equal (eshell-test-command-result "echo $\"user-login-name\"") | ||
| 50 | user-login-name))) | ||
| 51 | |||
| 52 | (ert-deftest esh-var-test/interp-quoted-var-concat () | ||
| 53 | "Interpolate and concat quoted variable" | ||
| 54 | (should (equal (eshell-test-command-result "echo $'user-login-name'-foo") | ||
| 55 | (concat user-login-name "-foo"))) | ||
| 56 | (should (equal (eshell-test-command-result "echo $\"user-login-name\"-foo") | ||
| 57 | (concat user-login-name "-foo")))) | ||
| 58 | |||
| 40 | (ert-deftest esh-var-test/interp-lisp () | 59 | (ert-deftest esh-var-test/interp-lisp () |
| 41 | "Interpolate Lisp form evaluation" | 60 | "Interpolate Lisp form evaluation" |
| 42 | (should (equal (eshell-test-command-result "+ $(+ 1 2) 3") 6))) | 61 | (should (equal (eshell-test-command-result "+ $(+ 1 2) 3") 6))) |
| @@ -79,6 +98,36 @@ | |||
| 79 | (eshell-command-result-p "echo ${echo hi}-${*echo there}" | 98 | (eshell-command-result-p "echo ${echo hi}-${*echo there}" |
| 80 | "hi-there\n"))) | 99 | "hi-there\n"))) |
| 81 | 100 | ||
| 101 | (ert-deftest esh-var-test/quoted-interp-var () | ||
| 102 | "Interpolate variable inside double-quotes" | ||
| 103 | (should (equal (eshell-test-command-result "echo \"$user-login-name\"") | ||
| 104 | user-login-name))) | ||
| 105 | |||
| 106 | (ert-deftest esh-var-test/quoted-interp-quoted-var () | ||
| 107 | "Interpolate quoted variable inside double-quotes" | ||
| 108 | (should (equal (eshell-test-command-result | ||
| 109 | "echo \"hi, $'user-login-name'\"") | ||
| 110 | (concat "hi, " user-login-name))) | ||
| 111 | (should (equal (eshell-test-command-result | ||
| 112 | "echo \"hi, $\\\"user-login-name\\\"\"") | ||
| 113 | (concat "hi, " user-login-name)))) | ||
| 114 | |||
| 115 | (ert-deftest esh-var-test/quoted-interp-lisp () | ||
| 116 | "Interpolate Lisp form evaluation inside double-quotes" | ||
| 117 | (should (equal (eshell-test-command-result | ||
| 118 | "echo \"hi $(concat \\\"the\\\" \\\"re\\\")\"") | ||
| 119 | "hi there"))) | ||
| 120 | |||
| 121 | (ert-deftest esh-var-test/quoted-interp-cmd () | ||
| 122 | "Interpolate command result inside double-quotes" | ||
| 123 | (should (equal (eshell-test-command-result | ||
| 124 | "echo \"hi ${echo \\\"there\\\"}\"") | ||
| 125 | "hi there"))) | ||
| 126 | |||
| 127 | (ert-deftest esh-var-test/quoted-interp-temp-cmd () | ||
| 128 | "Interpolate command result redirected to temp file inside double-quotes" | ||
| 129 | (should (equal (eshell-test-command-result "cat \"$<echo hi>\"") "hi"))) | ||
| 130 | |||
| 82 | 131 | ||
| 83 | ;; Built-in variables | 132 | ;; Built-in variables |
| 84 | 133 | ||