Improve the behavior of concatenating parts of Eshell arguments
Previously, concatenating a list to a string would first convert the list to a string. Now, the string is concatenated with the last element of the list. * lisp/eshell/esh-util.el (eshell-to-flat-string): Make obsolete. * lisp/eshell/esh-arg.el (eshell-concat, eshell-concat-1): New functions. (eshell-resolve-current-argument): Use 'eshell-concat'. * test/lisp/eshell/esh-var-tests.el (esh-var-test/interp-concat-cmd): Add check for concatenation of multiline output of subcommands. (esh-var-test/quoted-interp-concat-cmd): New test. * test/lisp/eshell/em-extpipe-tests.el (em-extpipe-test-13): Use 'eshell-concat'. * doc/misc/eshell.texi (Expansion): Document this behavior. * etc/NEWS: Announce the change (bug#55236).
This commit is contained in:
parent
06423b5d1e
commit
a3a7279a4a
6 changed files with 109 additions and 16 deletions
|
@ -1017,11 +1017,37 @@ parsers (such as @command{cpp} and @command{m4}), but in a command
|
|||
shell, they are less often used for constants, and usually for using
|
||||
variables and string manipulation.@footnote{Eshell has no
|
||||
string-manipulation expansions because the Elisp library already
|
||||
provides many functions for this.} For example, @code{$var} on a line
|
||||
expands to the value of the variable @code{var} when the line is
|
||||
provides many functions for this.} For example, @code{$@var{var}} on
|
||||
a line expands to the value of the variable @var{var} when the line is
|
||||
executed. Expansions are usually passed as arguments, but may also be
|
||||
used as commands.@footnote{E.g., entering just @samp{$var} at the prompt
|
||||
is equivalent to entering the value of @code{var} at the prompt.}
|
||||
used as commands.@footnote{E.g., entering just @samp{$@var{var}} at
|
||||
the prompt is equivalent to entering the value of @var{var} at the
|
||||
prompt.}
|
||||
|
||||
You can concatenate expansions with regular string arguments or even
|
||||
other expansions. In the simplest case, when the expansion returns a
|
||||
string value, this is equivalent to ordinary string concatenation; for
|
||||
example, @samp{$@{echo "foo"@}bar} returns @samp{foobar}. The exact
|
||||
behavior depends on the types of each value being concatenated:
|
||||
|
||||
@table @asis
|
||||
|
||||
@item both strings
|
||||
Concatenate both values together.
|
||||
|
||||
@item one or both numbers
|
||||
Concatenate the string representation of each value, converting back to
|
||||
a number if possible.
|
||||
|
||||
@item one or both (non-@code{nil}) lists
|
||||
Concatenate ``adjacent'' elements of each value (possibly converting
|
||||
back to a number as above). For example, @samp{$list("a" "b")c}
|
||||
returns @samp{("a" "bc")}.
|
||||
|
||||
@item anything else
|
||||
Concatenate the string represenation of each value.
|
||||
|
||||
@end table
|
||||
|
||||
@menu
|
||||
* Dollars Expansion::
|
||||
|
|
7
etc/NEWS
7
etc/NEWS
|
@ -1396,6 +1396,13 @@ If an Eshell expansion like '$FOO' is surrounded by double quotes, the
|
|||
result will always be a single string, no matter the type that would
|
||||
otherwise be returned.
|
||||
|
||||
+++
|
||||
*** Concatenating Eshell expansions now works more similarly to other shells.
|
||||
When concatenating an Eshell expansion that returns a list, "adjacent"
|
||||
elements of each operand are now concatenated together,
|
||||
e.g. '$list("a" "b")c' returns '("a" "bc")'. See the "(eshell)
|
||||
Expansion" node in the Eshell manual for more details.
|
||||
|
||||
+++
|
||||
*** Eshell subcommands with multiline numeric output return lists of numbers.
|
||||
If every line of the output of an Eshell subcommand like '${COMMAND}'
|
||||
|
|
|
@ -180,19 +180,63 @@ treated as a literal character."
|
|||
(add-text-properties 0 (length string) '(escaped t) string))
|
||||
string)
|
||||
|
||||
(defun eshell-concat (quoted &rest rest)
|
||||
"Concatenate all the arguments in REST and return the result.
|
||||
If QUOTED is nil, the resulting value(s) may be converted to
|
||||
numbers (see `eshell-concat-1').
|
||||
|
||||
If each argument in REST is a non-list value, the result will be
|
||||
a single value, as if (mapconcat #'eshell-stringify REST) had been
|
||||
called, possibly converted to a number.
|
||||
|
||||
If there is at least one (non-nil) list argument, the result will
|
||||
be a list, with \"adjacent\" elements of consecutive arguments
|
||||
concatenated as strings (again, possibly converted to numbers).
|
||||
For example, concatenating \"a\", (\"b\"), and (\"c\" \"d\")
|
||||
would produce (\"abc\" \"d\")."
|
||||
(let (result)
|
||||
(dolist (i rest result)
|
||||
(when i
|
||||
(cond
|
||||
((null result)
|
||||
(setq result i))
|
||||
((listp result)
|
||||
(let (curr-head curr-tail)
|
||||
(if (listp i)
|
||||
(setq curr-head (car i)
|
||||
curr-tail (cdr i))
|
||||
(setq curr-head i
|
||||
curr-tail nil))
|
||||
(setq result
|
||||
(append
|
||||
(butlast result 1)
|
||||
(list (eshell-concat-1 quoted (car (last result))
|
||||
curr-head))
|
||||
curr-tail))))
|
||||
((listp i)
|
||||
(setq result
|
||||
(cons (eshell-concat-1 quoted result (car i))
|
||||
(cdr i))))
|
||||
(t
|
||||
(setq result (eshell-concat-1 quoted result i))))))))
|
||||
|
||||
(defun eshell-concat-1 (quoted first second)
|
||||
"Concatenate FIRST and SECOND.
|
||||
If QUOTED is nil and either FIRST or SECOND are numbers, try to
|
||||
convert the result to a number as well."
|
||||
(let ((result (concat (eshell-stringify first) (eshell-stringify second))))
|
||||
(if (and (not quoted)
|
||||
(or (numberp first) (numberp second)))
|
||||
(eshell-convert-to-number result)
|
||||
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
|
||||
(let ((parts eshell-current-argument))
|
||||
(while parts
|
||||
(unless (stringp (car parts))
|
||||
(setcar parts
|
||||
(list 'eshell-to-flat-string (car parts))))
|
||||
(setq parts (cdr parts)))
|
||||
(setq eshell-current-argument
|
||||
(list 'eshell-convert
|
||||
(append (list 'concat) eshell-current-argument))))
|
||||
(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
|
||||
|
|
|
@ -293,6 +293,7 @@ Prepend remote identification of `default-directory', if any."
|
|||
|
||||
(defun eshell-to-flat-string (value)
|
||||
"Make value a string. If separated by newlines change them to spaces."
|
||||
(declare (obsolete nil "29.1"))
|
||||
(let ((text (eshell-stringify value)))
|
||||
(if (string-match "\n+\\'" text)
|
||||
(setq text (replace-match "" t t text)))
|
||||
|
|
|
@ -170,7 +170,7 @@
|
|||
|
||||
(em-extpipe-tests--deftest em-extpipe-test-13 "foo*|bar"
|
||||
(should-parse '(eshell-execute-pipeline
|
||||
'((eshell-named-command (concat "foo" "*"))
|
||||
'((eshell-named-command (eshell-concat nil "foo" "*"))
|
||||
(eshell-named-command "bar")))))
|
||||
|
||||
(em-extpipe-tests--deftest em-extpipe-test-14 "tac *<temp"
|
||||
|
|
|
@ -176,8 +176,17 @@
|
|||
(should (equal (eshell-test-command-result "+ $(+ 1 2)$(+ 1 2) 3") 36)))
|
||||
|
||||
(ert-deftest esh-var-test/interp-concat-cmd ()
|
||||
"Interpolate and concat command"
|
||||
(should (equal (eshell-test-command-result "+ ${+ 1 2}3 3") 36)))
|
||||
"Interpolate and concat command with literal"
|
||||
(should (equal (eshell-test-command-result "+ ${+ 1 2}3 3") 36))
|
||||
(should (equal (eshell-test-command-result "echo ${*echo \"foo\nbar\"}-baz")
|
||||
'("foo" "bar-baz")))
|
||||
;; Concatenating to a number in a list should produce a number...
|
||||
(should (equal (eshell-test-command-result "echo ${*echo \"1\n2\"}3")
|
||||
'(1 23)))
|
||||
;; ... but concatenating to a string that looks like a number in a list
|
||||
;; should produce a string.
|
||||
(should (equal (eshell-test-command-result "echo ${*echo \"hi\n2\"}3")
|
||||
'("hi" "23"))))
|
||||
|
||||
(ert-deftest esh-var-test/interp-concat-cmd2 ()
|
||||
"Interpolate and concat two commands"
|
||||
|
@ -326,6 +335,12 @@ inside double-quotes"
|
|||
"Interpolate command result redirected to temp file inside double-quotes"
|
||||
(should (equal (eshell-test-command-result "cat \"$<echo hi>\"") "hi")))
|
||||
|
||||
(ert-deftest esh-var-test/quoted-interp-concat-cmd ()
|
||||
"Interpolate and concat command with literal"
|
||||
(should (equal (eshell-test-command-result
|
||||
"echo \"${echo \\\"foo\nbar\\\"} baz\"")
|
||||
"foo\nbar baz")))
|
||||
|
||||
|
||||
;; Interpolated variable conversion
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue