[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
feature/android 13e1fb4c3f7 2/2: Merge remote-tracking branch 'origin/ma
From: |
Po Lu |
Subject: |
feature/android 13e1fb4c3f7 2/2: Merge remote-tracking branch 'origin/master' into feature/android |
Date: |
Tue, 28 Mar 2023 20:42:38 -0400 (EDT) |
branch: feature/android
commit 13e1fb4c3f7f969f45ccbec1cfc001d76690d680
Merge: af6af86d08e 2002ac376c9
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Merge remote-tracking branch 'origin/master' into feature/android
---
lisp/eshell/em-cmpl.el | 60 ++++++++++++++++---------
lisp/eshell/em-dirs.el | 11 ++++-
lisp/eshell/em-glob.el | 10 -----
lisp/eshell/esh-cmd.el | 31 +++++++++----
lisp/eshell/esh-var.el | 95 ++++++++++++++++++++-------------------
lisp/progmodes/ruby-ts-mode.el | 41 ++++++++++-------
test/lisp/eshell/em-cmpl-tests.el | 86 ++++++++++++++++++++++++++++++-----
test/lisp/eshell/esh-var-tests.el | 2 +-
8 files changed, 224 insertions(+), 112 deletions(-)
diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el
index b65652019d4..732bbb3f1fa 100644
--- a/lisp/eshell/em-cmpl.el
+++ b/lisp/eshell/em-cmpl.el
@@ -306,9 +306,24 @@ to writing a completion function."
(defun eshell-complete--eval-argument-form (arg)
"Evaluate a single Eshell argument form ARG for the purposes of completion."
- (let ((result (eshell-do-eval `(eshell-commands ,arg) t)))
- (cl-assert (eq (car result) 'quote))
- (cadr result)))
+ (condition-case err
+ (let* (;; Don't allow running commands; they could have
+ ;; arbitrary side effects, which we don't want when we're
+ ;; just performing completions!
+ (eshell-allow-commands)
+ ;; Handle errors ourselves so that we can properly catch
+ ;; `eshell-commands-forbidden'.
+ (eshell-handle-errors)
+ (result (eshell-do-eval `(eshell-commands ,arg) t)))
+ (cl-assert (eq (car result) 'quote))
+ (cadr result))
+ (eshell-commands-forbidden
+ (propertize "\0" 'eshell-argument-stub
+ (intern (format "%s-command" (cadr err)))))
+ (error
+ (lwarn 'eshell :error
+ "Failed to evaluate argument form during completion: %S" arg)
+ (propertize "\0" 'eshell-argument-stub 'error))))
(defun eshell-complete-parse-arguments ()
"Parse the command line arguments for `pcomplete-argument'."
@@ -325,23 +340,28 @@ to writing a completion function."
(if (= begin end)
(end-of-line))
(setq end (point-marker)))
- (if (setq delim
- (catch 'eshell-incomplete
- (ignore
- (setq args (eshell-parse-arguments begin end)))))
- (cond ((member (car delim) '("{" "${" "$<"))
- (setq begin (1+ (cadr delim))
- args (eshell-parse-arguments begin end)))
- ((member (car delim) '("$'" "$\"" "#<"))
- ;; Add the (incomplete) argument to our arguments, and
- ;; note its position.
- (setq args (append (nth 2 delim) (list (car delim)))
- incomplete-arg t)
- (push (- (nth 1 delim) 2) posns))
- ((member (car delim) '("(" "$("))
- (throw 'pcompleted (elisp-completion-at-point)))
- (t
- (eshell--pcomplete-insert-tab))))
+ ;; Don't expand globs when parsing arguments; we want to pass any
+ ;; globs to Pcomplete unaltered.
+ (declare-function eshell-parse-glob-chars "em-glob" ())
+ (let ((eshell-parse-argument-hook (remq #'eshell-parse-glob-chars
+ eshell-parse-argument-hook)))
+ (if (setq delim
+ (catch 'eshell-incomplete
+ (ignore
+ (setq args (eshell-parse-arguments begin end)))))
+ (cond ((member (car delim) '("{" "${" "$<"))
+ (setq begin (1+ (cadr delim))
+ args (eshell-parse-arguments begin end)))
+ ((member (car delim) '("$'" "$\"" "#<"))
+ ;; Add the (incomplete) argument to our arguments, and
+ ;; note its position.
+ (setq args (append (nth 2 delim) (list (car delim)))
+ incomplete-arg t)
+ (push (- (nth 1 delim) 2) posns))
+ ((member (car delim) '("(" "$("))
+ (throw 'pcompleted (elisp-completion-at-point)))
+ (t
+ (eshell--pcomplete-insert-tab)))))
(when (get-text-property (1- end) 'comment)
(eshell--pcomplete-insert-tab))
(let ((pos (1- end)))
diff --git a/lisp/eshell/em-dirs.el b/lisp/eshell/em-dirs.el
index 4bc6342d422..5284df9ab59 100644
--- a/lisp/eshell/em-dirs.el
+++ b/lisp/eshell/em-dirs.el
@@ -253,12 +253,21 @@ Thus, this does not include the current directory.")
(throw 'eshell-replace-command
(eshell-parse-command "cd" (flatten-tree args)))))
-(defun eshell-expand-user-reference (file)
+(defun eshell-expand-user-reference-1 (file)
"Expand a user reference in FILE to its real directory name."
(replace-regexp-in-string
(rx bos (group "~" (*? anychar)) (or "/" eos))
#'expand-file-name file))
+(defun eshell-expand-user-reference (file)
+ "Expand a user reference in FILE to its real directory name.
+FILE can be either a string or a list of strings to expand."
+ ;; If the argument was a glob pattern, then FILE is a list, so
+ ;; expand each element of the glob's resulting list.
+ (if (listp file)
+ (mapcar #'eshell-expand-user-reference-1 file)
+ (eshell-expand-user-reference-1 file)))
+
(defun eshell-parse-user-reference ()
"An argument beginning with ~ is a filename to be expanded."
(when (and (not eshell-current-argument)
diff --git a/lisp/eshell/em-glob.el b/lisp/eshell/em-glob.el
index 8a2ba13b2ad..9402df43065 100644
--- a/lisp/eshell/em-glob.el
+++ b/lisp/eshell/em-glob.el
@@ -145,16 +145,6 @@ This mimics the behavior of zsh if non-nil, but bash if
nil."
(defun eshell-add-glob-modifier ()
"Add `eshell-extended-glob' to the argument modifier list."
- (when (memq 'expand-file-name eshell-current-modifiers)
- (setq eshell-current-modifiers
- (delq 'expand-file-name eshell-current-modifiers))
- ;; if this is a glob pattern than needs to be expanded, then it
- ;; will need to expand each member of the resulting glob list
- (add-to-list 'eshell-current-modifiers
- (lambda (list)
- (if (listp list)
- (mapcar 'expand-file-name list)
- (expand-file-name list)))))
(add-to-list 'eshell-current-modifiers 'eshell-extended-glob))
(defun eshell-parse-glob-chars ()
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el
index 93f2616020c..d5237ee1f04 100644
--- a/lisp/eshell/esh-cmd.el
+++ b/lisp/eshell/esh-cmd.el
@@ -293,6 +293,17 @@ CDR are the same process.
When the process in the CDR completes, resume command evaluation.")
+(defvar eshell-allow-commands t
+ "If non-nil, allow evaluating command forms (including Lisp forms).
+If you want to forbid command forms, you can let-bind this to a
+non-nil value before calling `eshell-do-eval'. Then, any command
+forms will signal `eshell-commands-forbidden'. This is useful
+if, for example, you want to evaluate simple expressions like
+variable expansions, but not fully-evaluate the command. See
+also `eshell-complete-parse-arguments'.")
+
+(define-error 'eshell-commands-forbidden "Commands forbidden")
+
;;; Functions:
(defsubst eshell-interactive-process-p ()
@@ -675,13 +686,13 @@ This means an exit code of 0."
(or (= (point-max) (1+ (point)))
(not (eq (char-after (1+ (point))) ?\}))))
(let ((end (eshell-find-delimiter ?\{ ?\})))
- (if (not end)
- (throw 'eshell-incomplete "{")
- (when (eshell-arg-delimiter (1+ end))
- (prog1
- `(eshell-as-subcommand
- ,(eshell-parse-command (cons (1+ (point)) end)))
- (goto-char (1+ end))))))))
+ (unless end
+ (throw 'eshell-incomplete "{"))
+ (when (eshell-arg-delimiter (1+ end))
+ (prog1
+ `(eshell-as-subcommand
+ ,(eshell-parse-command (cons (1+ (point)) end)))
+ (goto-char (1+ end)))))))
(defun eshell-parse-lisp-argument ()
"Parse a Lisp expression which is specified as an argument."
@@ -1168,7 +1179,7 @@ have been replaced by constants."
(setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p))
(eval form))
((eq (car form) 'let)
- (when (not (eq (car (cadr args)) 'eshell-do-eval))
+ (unless (eq (car-safe (cadr args)) 'eshell-do-eval)
(eshell-manipulate "evaluating let args"
(dolist (letarg (car args))
(when (and (listp letarg)
@@ -1328,6 +1339,8 @@ have been replaced by constants."
(defun eshell-named-command (command &optional args)
"Insert output from a plain COMMAND, using ARGS.
COMMAND may result in an alias being executed, or a plain command."
+ (unless eshell-allow-commands
+ (signal 'eshell-commands-forbidden '(named)))
(setq eshell-last-arguments args
eshell-last-command-name (eshell-stringify command))
(run-hook-with-args 'eshell-prepare-command-hook)
@@ -1465,6 +1478,8 @@ via `eshell-errorn'."
(defun eshell-lisp-command (object &optional args)
"Insert Lisp OBJECT, using ARGS if a function."
+ (unless eshell-allow-commands
+ (signal 'eshell-commands-forbidden '(lisp)))
(catch 'eshell-external ; deferred to an external command
(setq eshell-last-command-status 0
eshell-last-arguments args)
diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el
index 5d6299af564..7dcaff1e24f 100644
--- a/lisp/eshell/esh-var.el
+++ b/lisp/eshell/esh-var.el
@@ -507,55 +507,56 @@ Possible variable references are:
(cond
((eq (char-after) ?{)
(let ((end (eshell-find-delimiter ?\{ ?\})))
- (if (not end)
- (throw 'eshell-incomplete "${")
- (forward-char)
- (prog1
- `(eshell-apply-indices
- (eshell-convert
- (eshell-command-to-value
- (eshell-as-subcommand
- ,(let ((subcmd (or (eshell-unescape-inner-double-quote end)
- (cons (point) end)))
- (eshell-current-quoted nil))
- (eshell-parse-command subcmd))))
- ;; If this is a simple double-quoted form like
- ;; "${COMMAND}" (i.e. no indices after the subcommand
- ;; and no `#' modifier before), ensure we convert to a
- ;; single string. This avoids unnecessary work
- ;; (e.g. splitting the output by lines) when it would
- ;; just be joined back together afterwards.
- ,(when (and (not modifier-p) eshell-current-quoted)
- '(not indices)))
- indices ,eshell-current-quoted)
- (goto-char (1+ end))))))
+ (unless end
+ (throw 'eshell-incomplete "${"))
+ (forward-char)
+ (prog1
+ `(eshell-apply-indices
+ (eshell-convert
+ (eshell-command-to-value
+ (eshell-as-subcommand
+ ,(let ((subcmd (or (eshell-unescape-inner-double-quote end)
+ (cons (point) end)))
+ (eshell-current-quoted nil))
+ (eshell-parse-command subcmd))))
+ ;; If this is a simple double-quoted form like
+ ;; "${COMMAND}" (i.e. no indices after the subcommand and
+ ;; no `#' modifier before), ensure we convert to a single
+ ;; string. This avoids unnecessary work (e.g. splitting
+ ;; the output by lines) when it would just be joined back
+ ;; together afterwards.
+ ,(when (and (not modifier-p) eshell-current-quoted)
+ '(not indices)))
+ indices ,eshell-current-quoted)
+ (goto-char (1+ end)))))
((eq (char-after) ?\<)
(let ((end (eshell-find-delimiter ?\< ?\>)))
- (if (not end)
- (throw 'eshell-incomplete "$<")
- (let* ((temp (make-temp-file temporary-file-directory))
- (cmd (concat (buffer-substring (1+ (point)) end)
- " > " temp)))
- (prog1
- `(let ((eshell-current-handles
- (eshell-create-handles ,temp 'overwrite)))
- (progn
- (eshell-as-subcommand
- ,(let ((eshell-current-quoted nil))
- (eshell-parse-command cmd)))
- (ignore
- (nconc eshell-this-command-hook
- ;; Quote this lambda; it will be evaluated
- ;; by `eshell-do-eval', which requires very
- ;; particular forms in order to work
- ;; properly. See bug#54190.
- (list (function
- (lambda ()
- (delete-file ,temp)
- (when-let ((buffer (get-file-buffer
,temp)))
- (kill-buffer buffer)))))))
- (eshell-apply-indices ,temp indices
,eshell-current-quoted)))
- (goto-char (1+ end)))))))
+ (unless end
+ (throw 'eshell-incomplete "$<"))
+ (forward-char)
+ (let* ((temp (make-temp-file temporary-file-directory))
+ (subcmd (or (eshell-unescape-inner-double-quote end)
+ (cons (point) end))))
+ (prog1
+ `(let ((eshell-current-handles
+ (eshell-create-handles ,temp 'overwrite)))
+ (progn
+ (eshell-as-subcommand
+ ,(let ((eshell-current-quoted nil))
+ (eshell-parse-command subcmd)))
+ (ignore
+ (nconc eshell-this-command-hook
+ ;; Quote this lambda; it will be evaluated by
+ ;; `eshell-do-eval', which requires very
+ ;; particular forms in order to work
+ ;; properly. See bug#54190.
+ (list (function
+ (lambda ()
+ (delete-file ,temp)
+ (when-let ((buffer (get-file-buffer ,temp)))
+ (kill-buffer buffer)))))))
+ (eshell-apply-indices ,temp indices ,eshell-current-quoted)))
+ (goto-char (1+ end))))))
((eq (char-after) ?\()
(condition-case nil
`(eshell-apply-indices
diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el
index 97c7cf6480c..1a085b669ee 100644
--- a/lisp/progmodes/ruby-ts-mode.el
+++ b/lisp/progmodes/ruby-ts-mode.el
@@ -1114,21 +1114,32 @@ leading double colon is not added."
(setq-local treesit-defun-type-regexp ruby-ts--method-regex)
(setq-local treesit-sexp-type-regexp
- (regexp-opt '("class"
- "module"
- "method"
- "argument_list"
- "array"
- "hash"
- "parenthesized_statements"
- "if"
- "case"
- "when"
- "block"
- "do_block"
- "begin"
- "binary"
- "assignment")))
+ (rx bol
+ (or "class"
+ "module"
+ "method"
+ "array"
+ "hash"
+ "parenthesized_statements"
+ "if"
+ "case"
+ "when"
+ "block"
+ "do_block"
+ "begin"
+ "integer"
+ "identifier"
+ "constant"
+ "simple_symbol"
+ "symbol_array"
+ "hash_key_symbol"
+ "string"
+ "string_array"
+ "heredoc_body"
+ "regex"
+ "argument_list"
+ )
+ eol))
;; AFAIK, Ruby can not nest methods
(setq-local treesit-defun-prefer-top-level nil)
diff --git a/test/lisp/eshell/em-cmpl-tests.el
b/test/lisp/eshell/em-cmpl-tests.el
index ea907f1945d..29a41625d5e 100644
--- a/test/lisp/eshell/em-cmpl-tests.el
+++ b/test/lisp/eshell/em-cmpl-tests.el
@@ -69,11 +69,10 @@ ACTUAL and EXPECTED should both be lists of strings."
(ert-deftest em-cmpl-test/parse-arguments/pipeline ()
"Test that parsing arguments for completion discards earlier commands."
(with-temp-eshell
- (let ((eshell-test-value '("foo" "bar")))
- (insert "echo hi | cat")
- (should (eshell-arguments-equal
- (car (eshell-complete-parse-arguments))
- '("cat"))))))
+ (insert "echo hi | cat")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ '("cat")))))
(ert-deftest em-cmpl-test/parse-arguments/multiple-dots ()
"Test parsing arguments with multiple dots like \".../\"."
@@ -123,6 +122,45 @@ ACTUAL and EXPECTED should both be lists of strings."
(car (eshell-complete-parse-arguments))
'("echo" "foo" "bar"))))))
+(ert-deftest em-cmpl-test/parse-arguments/unevaluated-subcommand ()
+ "Test that subcommands return a stub when parsing for completion."
+ (with-temp-eshell
+ (insert "echo {echo hi}")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'named-command)))))
+ (with-temp-eshell
+ (insert "echo ${echo hi}")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'named-command))))))
+
+(ert-deftest em-cmpl-test/parse-arguments/unevaluated-lisp-form ()
+ "Test that Lisp forms return a stub when parsing for completion."
+ (with-temp-eshell
+ (insert "echo (concat \"hi\")")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'lisp-command)))))
+ (with-temp-eshell
+ (insert "echo $(concat \"hi\")")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'lisp-command))))))
+
+(ert-deftest em-cmpl-test/parse-arguments/unevaluated-inner-subcommand ()
+ "Test that nested subcommands return a stub when parsing for completion."
+ (with-temp-eshell
+ (insert "echo $exec-path[${echo 0}]")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'named-command))))))
+
(ert-deftest em-cmpl-test/file-completion/unique ()
"Test completion of file names when there's a unique result."
(with-temp-eshell
@@ -150,14 +188,39 @@ ACTUAL and EXPECTED should both be lists of strings."
(forward-line -1)
(should (looking-at "Complete, but not unique")))))))
+(ert-deftest em-cmpl-test/file-completion/glob ()
+ "Test completion of file names using a glob."
+ (with-temp-eshell
+ (ert-with-temp-directory default-directory
+ (write-region nil nil (expand-file-name "file.txt"))
+ (write-region nil nil (expand-file-name "file.el"))
+ (should (equal (eshell-insert-and-complete "echo fi*.el")
+ "echo file.el ")))))
+
(ert-deftest em-cmpl-test/file-completion/after-list ()
"Test completion of file names after previous list arguments.
See bug#59956."
(with-temp-eshell
- (ert-with-temp-directory default-directory
- (write-region nil nil (expand-file-name "file.txt"))
- (should (equal (eshell-insert-and-complete "echo (list 1 2) fi")
- "echo (list 1 2) file.txt ")))))
+ (let ((eshell-test-value '("foo" "bar")))
+ (ert-with-temp-directory default-directory
+ (write-region nil nil (expand-file-name "file.txt"))
+ (should (equal (eshell-insert-and-complete "echo $eshell-test-value fi")
+ "echo $eshell-test-value file.txt "))))))
+
+(ert-deftest em-cmpl-test/command-completion ()
+ "Test completion of command names like \"command\"."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "listif")
+ "listify "))))
+
+(ert-deftest em-cmpl-test/subcommand-completion ()
+ "Test completion of command names like \"{command}\"."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "{ listif")
+ "{ listify ")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo ${ listif")
+ "echo ${ listify "))))
(ert-deftest em-cmpl-test/lisp-symbol-completion ()
"Test completion of Lisp forms like \"#'symbol\" and \"`symbol\".
@@ -174,7 +237,10 @@ See <lisp/eshell/esh-cmd.el>."
See <lisp/eshell/esh-cmd.el>."
(with-temp-eshell
(should (equal (eshell-insert-and-complete "echo (eshell/ech")
- "echo (eshell/echo"))))
+ "echo (eshell/echo")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo $(eshell/ech")
+ "echo $(eshell/echo"))))
(ert-deftest em-cmpl-test/special-ref-completion/type ()
"Test completion of the start of special references like \"#<buffer\".
diff --git a/test/lisp/eshell/esh-var-tests.el
b/test/lisp/eshell/esh-var-tests.el
index 6767d9289f9..771bd5a419c 100644
--- a/test/lisp/eshell/esh-var-tests.el
+++ b/test/lisp/eshell/esh-var-tests.el
@@ -454,7 +454,7 @@ nil, use FUNCTION instead."
(let ((temporary-file-directory
(file-name-as-directory (make-temp-file "esh-vars-tests" t))))
(unwind-protect
- (eshell-command-result-equal "cat \"$<echo hi>\"" "hi")
+ (eshell-command-result-equal "cat \"$<echo \\\"hi\\\">\"" "hi")
(delete-directory temporary-file-directory t))))
(ert-deftest esh-var-test/quoted-interp-concat-cmd ()