Allow splicing Eshell globs in-place
This means that Eshell globs can now expand the same way as if the user had typed each matching file individually. * lisp/eshell/em-glob.el (eshell-glob-splice-results): New option. (eshell-no-command-globbing, eshell-add-glob-modifier): Handle spliced globs. (eshell-extended-glob): Always return a list when splicing. * lisp/eshell/em-pred.el (eshell-parse-arg-modifier): Ensure 'eshell-splice-args' is always at the end of the list of modifiers if present. * test/lisp/eshell/em-glob-tests.el (em-glob-test/expand/splice-results) (em-glob-test/expand/no-splice-results) (em-glob-test/expand/explicitly-splice-results) (em-glob-test/expand/explicitly-listify-results): New tests. (em-glob-test/no-matches): Check result when 'eshell-glob-splice-results' is nil/non-nil. * doc/misc/eshell.texi (Arguments): Expand explanation about argument flattening. (Globbing): Document splicing behavior of globs. * etc/NEWS: Announce this change.
This commit is contained in:
parent
82d8732505
commit
cf52cdb121
5 changed files with 128 additions and 25 deletions
|
@ -317,9 +317,10 @@ specify an argument of some other data type, you can use a Lisp form
|
|||
(1 2 3)
|
||||
@end example
|
||||
|
||||
Additionally, many built-in Eshell commands (@pxref{Built-ins}) will
|
||||
flatten the arguments they receive, so passing a list as an argument
|
||||
will ``spread'' the elements into multiple arguments:
|
||||
When calling external commands (and many built-in Eshell commands,
|
||||
too) Eshell will flatten the arguments the command receives, so
|
||||
passing a list as an argument will ``spread'' the elements into
|
||||
multiple arguments:
|
||||
|
||||
@example
|
||||
~ $ printnl (list 1 2) 3
|
||||
|
@ -1466,18 +1467,28 @@ other arguments around it. For example, if @var{numbers} is the list
|
|||
|
||||
@node Globbing
|
||||
@section Globbing
|
||||
@vindex eshell-glob-case-insensitive
|
||||
Eshell's globbing syntax is very similar to that of Zsh
|
||||
(@pxref{Filename Generation, , , zsh, The Z Shell Manual}). Users
|
||||
coming from Bash can still use Bash-style globbing, as there are no
|
||||
incompatibilities.
|
||||
|
||||
By default, globs are case sensitive, except on MS-DOS/MS-Windows
|
||||
@vindex eshell-glob-case-insensitive
|
||||
Globs are case sensitive by default, except on MS-DOS/MS-Windows
|
||||
systems. You can control this behavior via the
|
||||
@code{eshell-glob-case-insensitive} option. You can further customize
|
||||
the syntax and behavior of globbing in Eshell via the Customize group
|
||||
@code{eshell-glob} (@pxref{Easy Customization, , , emacs, The GNU
|
||||
Emacs Manual}).
|
||||
@code{eshell-glob-case-insensitive} option.
|
||||
|
||||
@vindex eshell-glob-splice-results
|
||||
By default, Eshell expands the results of a glob as a sublist into the
|
||||
list of arguments. You can change this to splice the results in-place
|
||||
by setting @code{eshell-glob-splice-results} to a non-@code{nil}
|
||||
value. If you want to splice a glob in-place for just one use, you
|
||||
can use a subcommand form like @samp{$@@@{listify @var{my-glob}@}}.
|
||||
(Conversely, you can explicitly expand a glob as a sublist via
|
||||
@samp{$@{listify @var{my-glob}@}}.)
|
||||
|
||||
You can further customize the syntax and behavior of globbing in
|
||||
Eshell via the Customize group @code{eshell-glob} (@pxref{Easy
|
||||
Customization, , , emacs, The GNU Emacs Manual}).
|
||||
|
||||
@table @samp
|
||||
|
||||
|
@ -2386,8 +2397,6 @@ be Eshell's job?
|
|||
This would be so that if a Lisp function calls @code{print}, everything
|
||||
will happen as it should (albeit slowly).
|
||||
|
||||
@item If a globbing pattern returns one match, should it be a list?
|
||||
|
||||
@item Make sure syntax table is correct in Eshell mode
|
||||
|
||||
So that @kbd{M-@key{DEL}} acts in a predictable manner, etc.
|
||||
|
|
7
etc/NEWS
7
etc/NEWS
|
@ -312,6 +312,13 @@ of arguments into a command, such as when defining aliases. For more
|
|||
information, see the "(eshell) Dollars Expansion" node in the Eshell
|
||||
manual.
|
||||
|
||||
+++
|
||||
*** You can now splice Eshell globs in-place into argument lists.
|
||||
By setting 'eshell-glob-splice-results' to a non-nil value, Eshell
|
||||
will expand glob results in-place as if you had typed each matching
|
||||
file name individually. For more information, see the "(eshell)
|
||||
Globbing" node in the Eshell manual.
|
||||
|
||||
+++
|
||||
*** Eshell now supports negative numbers and ranges for indices.
|
||||
Now, you can retrieve the last element of a list with '$my-list[-1]'
|
||||
|
|
|
@ -69,6 +69,15 @@ by zsh for filename generation."
|
|||
:type 'hook
|
||||
:group 'eshell-glob)
|
||||
|
||||
(defcustom eshell-glob-splice-results nil
|
||||
"If non-nil, the results of glob patterns will be spliced in-place.
|
||||
When splicing, the resulting command is as though the user typed
|
||||
each result individually. Otherwise, the glob results a single
|
||||
argument as a list."
|
||||
:version "30.1"
|
||||
:type 'boolean
|
||||
:group 'eshell-glob)
|
||||
|
||||
(defcustom eshell-glob-include-dot-files nil
|
||||
"If non-nil, glob patterns will match files beginning with a dot."
|
||||
:type 'boolean
|
||||
|
@ -139,12 +148,15 @@ This mimics the behavior of zsh if non-nil, but bash if nil."
|
|||
(defun eshell-no-command-globbing (terms)
|
||||
"Don't glob the command argument. Reflect this by modifying TERMS."
|
||||
(ignore
|
||||
(when (and (listp (car terms))
|
||||
(eq (caar terms) 'eshell-extended-glob))
|
||||
(setcar terms (cadr (car terms))))))
|
||||
(pcase (car terms)
|
||||
((or `(eshell-extended-glob ,term)
|
||||
`(eshell-splice-args (eshell-extended-glob ,term)))
|
||||
(setcar terms term)))))
|
||||
|
||||
(defun eshell-add-glob-modifier ()
|
||||
"Add `eshell-extended-glob' to the argument modifier list."
|
||||
(when eshell-glob-splice-results
|
||||
(add-to-list 'eshell-current-modifiers 'eshell-splice-args t))
|
||||
(add-to-list 'eshell-current-modifiers 'eshell-extended-glob))
|
||||
|
||||
(defun eshell-parse-glob-chars ()
|
||||
|
@ -326,7 +338,9 @@ regular expressions, and these cannot support the above constructs."
|
|||
(or (and eshell-glob-matches (sort eshell-glob-matches #'string<))
|
||||
(if eshell-error-if-no-glob
|
||||
(error "No matches found: %s" glob)
|
||||
glob))))
|
||||
(if eshell-glob-splice-results
|
||||
(list glob)
|
||||
glob)))))
|
||||
|
||||
;; FIXME does this really need to abuse eshell-glob-matches, message-shown?
|
||||
(defun eshell-glob-entries (path globs only-dirs)
|
||||
|
|
|
@ -301,16 +301,25 @@ This function is specially for adding onto `eshell-parse-argument-hook'."
|
|||
(modifiers (eshell-parse-modifiers))
|
||||
(preds (car modifiers))
|
||||
(mods (cdr modifiers)))
|
||||
(if (or preds mods)
|
||||
;; has to go at the end, which is only natural since
|
||||
;; syntactically it can only occur at the end
|
||||
(setq eshell-current-modifiers
|
||||
(append
|
||||
eshell-current-modifiers
|
||||
(list
|
||||
(lambda (lst)
|
||||
(eshell-apply-modifiers
|
||||
lst preds mods modifier-string))))))))
|
||||
(when (or preds mods)
|
||||
;; Has to go at the end, which is only natural since
|
||||
;; syntactically it can only occur at the end.
|
||||
(setq eshell-current-modifiers
|
||||
(append
|
||||
eshell-current-modifiers
|
||||
(list
|
||||
(lambda (lst)
|
||||
(eshell-apply-modifiers
|
||||
lst preds mods modifier-string)))))
|
||||
(when (memq 'eshell-splice-args eshell-current-modifiers)
|
||||
;; If splicing results, ensure that
|
||||
;; `eshell-splice-args' is the last modifier.
|
||||
;; Eshell's command parsing can't handle it anywhere
|
||||
;; else.
|
||||
(setq eshell-current-modifiers
|
||||
(append (delq 'eshell-splice-args
|
||||
eshell-current-modifiers)
|
||||
(list 'eshell-splice-args)))))))
|
||||
(goto-char (1+ end))
|
||||
(eshell-finish-arg))))))
|
||||
|
||||
|
|
|
@ -26,6 +26,13 @@
|
|||
(require 'ert)
|
||||
(require 'em-glob)
|
||||
|
||||
(require 'eshell-tests-helpers
|
||||
(expand-file-name "eshell-tests-helpers"
|
||||
(file-name-directory (or load-file-name
|
||||
default-directory))))
|
||||
|
||||
(defvar eshell-prefer-lisp-functions)
|
||||
|
||||
(defmacro with-fake-files (files &rest body)
|
||||
"Evaluate BODY forms, pretending that FILES exist on the filesystem.
|
||||
FILES is a list of file names that should be reported as
|
||||
|
@ -54,6 +61,60 @@ component ending in \"symlink\" is treated as a symbolic link."
|
|||
|
||||
;;; Tests:
|
||||
|
||||
(ert-deftest em-glob-test/expand/splice-results ()
|
||||
"Test that globs are spliced into the argument list when
|
||||
`eshell-glob-splice-results' is non-nil."
|
||||
(let ((eshell-prefer-lisp-functions t)
|
||||
(eshell-glob-splice-results t))
|
||||
(with-fake-files '("a.el" "b.el" "c.txt")
|
||||
;; Ensure the default expansion splices the glob.
|
||||
(eshell-command-result-equal "list *.el" '("a.el" "b.el"))
|
||||
(eshell-command-result-equal "list *.txt" '("c.txt"))
|
||||
(eshell-command-result-equal "list *.no" '("*.no")))))
|
||||
|
||||
(ert-deftest em-glob-test/expand/no-splice-results ()
|
||||
"Test that globs are treated as lists when
|
||||
`eshell-glob-splice-results' is nil."
|
||||
(let ((eshell-prefer-lisp-functions t)
|
||||
(eshell-glob-splice-results nil))
|
||||
(with-fake-files '("a.el" "b.el" "c.txt")
|
||||
;; Ensure the default expansion splices the glob.
|
||||
(eshell-command-result-equal "list *.el" '(("a.el" "b.el")))
|
||||
(eshell-command-result-equal "list *.txt" '(("c.txt")))
|
||||
;; The no-matches case is special here: the glob is just the
|
||||
;; string, not the list of results.
|
||||
(eshell-command-result-equal "list *.no" '("*.no")))))
|
||||
|
||||
(ert-deftest em-glob-test/expand/explicitly-splice-results ()
|
||||
"Test explicitly splicing globs works the same no matter the
|
||||
value of `eshell-glob-splice-results'."
|
||||
(let ((eshell-prefer-lisp-functions t))
|
||||
(dolist (eshell-glob-splice-results '(nil t))
|
||||
(ert-info ((format "eshell-glob-splice-results: %s"
|
||||
eshell-glob-splice-results))
|
||||
(with-fake-files '("a.el" "b.el" "c.txt")
|
||||
(eshell-command-result-equal "list $@{listify *.el}"
|
||||
'("a.el" "b.el"))
|
||||
(eshell-command-result-equal "list $@{listify *.txt}"
|
||||
'("c.txt"))
|
||||
(eshell-command-result-equal "list $@{listify *.no}"
|
||||
'("*.no")))))))
|
||||
|
||||
(ert-deftest em-glob-test/expand/explicitly-listify-results ()
|
||||
"Test explicitly listifying globs works the same no matter the
|
||||
value of `eshell-glob-splice-results'."
|
||||
(let ((eshell-prefer-lisp-functions t))
|
||||
(dolist (eshell-glob-splice-results '(nil t))
|
||||
(ert-info ((format "eshell-glob-splice-results: %s"
|
||||
eshell-glob-splice-results))
|
||||
(with-fake-files '("a.el" "b.el" "c.txt")
|
||||
(eshell-command-result-equal "list ${listify *.el}"
|
||||
'(("a.el" "b.el")))
|
||||
(eshell-command-result-equal "list ${listify *.txt}"
|
||||
'(("c.txt")))
|
||||
(eshell-command-result-equal "list ${listify *.no}"
|
||||
'(("*.no"))))))))
|
||||
|
||||
(ert-deftest em-glob-test/match-any-string ()
|
||||
"Test that \"*\" pattern matches any string."
|
||||
(with-fake-files '("a.el" "b.el" "c.txt" "dir/a.el")
|
||||
|
@ -191,6 +252,9 @@ component ending in \"symlink\" is treated as a symbolic link."
|
|||
(with-fake-files '("foo.el" "bar.el")
|
||||
(should (equal (eshell-extended-glob "*.txt")
|
||||
"*.txt"))
|
||||
(let ((eshell-glob-splice-results t))
|
||||
(should (equal (eshell-extended-glob "*.txt")
|
||||
'("*.txt"))))
|
||||
(let ((eshell-error-if-no-glob t))
|
||||
(should-error (eshell-extended-glob "*.txt")))))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue