Merge remote-tracking branch 'origin/master' into feature/android
This commit is contained in:
commit
338f4be900
9 changed files with 154 additions and 34 deletions
|
@ -741,18 +741,24 @@ if none)."
|
|||
;; The structure of the following macros is very important to
|
||||
;; `eshell-do-eval' [Iterative evaluation]:
|
||||
;;
|
||||
;; @ Don't use forms that conditionally evaluate their arguments, such
|
||||
;; as `setq', `if', `while', `let*', etc. The only special forms
|
||||
;; that can be used are `let', `condition-case' and
|
||||
;; `unwind-protect'.
|
||||
;; @ Don't use special forms that conditionally evaluate their
|
||||
;; arguments, such as `let*', unless Eshell explicitly supports
|
||||
;; them. Eshell supports the following special forms: `catch',
|
||||
;; `condition-case', `if', `let', `prog1', `progn', `quote', `setq',
|
||||
;; `unwind-protect', and `while'.
|
||||
;;
|
||||
;; @ The main body of a `let' can contain only one form. Use `progn'
|
||||
;; if necessary.
|
||||
;; @ When using `if' or `while', first let-bind `eshell-test-body' and
|
||||
;; `eshell-command-body' to '(nil). Eshell uses these variables to
|
||||
;; handle conditional evaluation.
|
||||
;;
|
||||
;; @ The two `special' variables are `eshell-current-handles' and
|
||||
;; `eshell-current-subjob-p'. Bind them locally with a `let' if you
|
||||
;; need to change them. Change them directly only if your intention
|
||||
;; is to change the calling environment.
|
||||
;;
|
||||
;; These rules likewise apply to any other code that generates forms
|
||||
;; that `eshell-do-eval' will evaluated, such as command rewriting
|
||||
;; hooks (see `eshell-rewrite-command-hook' and friends).
|
||||
|
||||
(defmacro eshell-do-subjob (object)
|
||||
"Evaluate a command OBJECT as a subjob.
|
||||
|
@ -1095,9 +1101,17 @@ produced by `eshell-parse-command'."
|
|||
(eshell-debug-command ,(concat "done " (eval tag)) form))))
|
||||
|
||||
(defun eshell-do-eval (form &optional synchronous-p)
|
||||
"Evaluate form, simplifying it as we go.
|
||||
"Evaluate FORM, simplifying it as we go.
|
||||
Unless SYNCHRONOUS-P is non-nil, throws `eshell-defer' if it needs to
|
||||
be finished later after the completion of an asynchronous subprocess."
|
||||
be finished later after the completion of an asynchronous subprocess.
|
||||
|
||||
As this function evaluates FORM, it will gradually replace
|
||||
subforms with the (quoted) result of evaluating them. For
|
||||
example, a function call is replaced with the result of the call.
|
||||
This allows us to resume evaluation of FORM after something
|
||||
inside throws `eshell-defer' simply by calling this function
|
||||
again. Any forms preceding one that throw `eshell-defer' will
|
||||
have been replaced by constants."
|
||||
(cond
|
||||
((not (listp form))
|
||||
(list 'quote (eval form)))
|
||||
|
@ -1161,21 +1175,48 @@ be finished later after the completion of an asynchronous subprocess."
|
|||
(setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p))
|
||||
(eval form))
|
||||
((eq (car form) 'let)
|
||||
(if (not (eq (car (cadr args)) 'eshell-do-eval))
|
||||
(eshell-manipulate "evaluating let args"
|
||||
(dolist (letarg (car args))
|
||||
(if (and (listp letarg)
|
||||
(not (eq (cadr letarg) 'quote)))
|
||||
(setcdr letarg
|
||||
(list (eshell-do-eval
|
||||
(cadr letarg) synchronous-p)))))))
|
||||
(when (not (eq (car (cadr args)) 'eshell-do-eval))
|
||||
(eshell-manipulate "evaluating let args"
|
||||
(dolist (letarg (car args))
|
||||
(when (and (listp letarg)
|
||||
(not (eq (cadr letarg) 'quote)))
|
||||
(setcdr letarg
|
||||
(list (eshell-do-eval
|
||||
(cadr letarg) synchronous-p)))))))
|
||||
(cl-progv
|
||||
(mapcar (lambda (binding) (if (consp binding) (car binding) binding))
|
||||
(mapcar (lambda (binding)
|
||||
(if (consp binding) (car binding) binding))
|
||||
(car args))
|
||||
;; These expressions should all be constants now.
|
||||
(mapcar (lambda (binding) (if (consp binding) (eval (cadr binding))))
|
||||
(mapcar (lambda (binding)
|
||||
(when (consp binding) (eval (cadr binding))))
|
||||
(car args))
|
||||
(eshell-do-eval (macroexp-progn (cdr args)) synchronous-p)))
|
||||
(let (deferred result)
|
||||
;; Evaluate the `let' body, catching `eshell-defer' so we
|
||||
;; can handle it below.
|
||||
(setq deferred
|
||||
(catch 'eshell-defer
|
||||
(ignore (setq result (eshell-do-eval
|
||||
(macroexp-progn (cdr args))
|
||||
synchronous-p)))))
|
||||
;; If something threw `eshell-defer', we need to update
|
||||
;; the let-bindings' values so that those values are
|
||||
;; correct when we resume evaluation of this form.
|
||||
(when deferred
|
||||
(eshell-manipulate "rebinding let args after `eshell-defer'"
|
||||
(let ((bindings (car args)))
|
||||
(while bindings
|
||||
(let ((binding (if (consp (car bindings))
|
||||
(caar bindings)
|
||||
(car bindings))))
|
||||
(setcar bindings
|
||||
(list binding
|
||||
(list 'quote (symbol-value binding)))))
|
||||
(pop bindings))))
|
||||
(throw 'eshell-defer deferred))
|
||||
;; If we get here, there was no `eshell-defer' thrown, so
|
||||
;; just return the `let' body's result.
|
||||
result)))
|
||||
((memq (car form) '(catch condition-case unwind-protect))
|
||||
;; `condition-case' and `unwind-protect' have to be
|
||||
;; handled specially, because we only want to call
|
||||
|
|
|
@ -525,9 +525,7 @@ Putting this function on `eshell-pre-command-hook' will mimic Plan 9's
|
|||
(defun eshell-interactive-print (string)
|
||||
"Print STRING to the eshell display buffer."
|
||||
(when string
|
||||
(add-text-properties 0 (length string)
|
||||
'(field command-output rear-nonsticky (field))
|
||||
string)
|
||||
(eshell--mark-as-output 0 (length string) string)
|
||||
(eshell-interactive-filter nil string)))
|
||||
|
||||
(defsubst eshell-begin-on-new-line ()
|
||||
|
@ -891,7 +889,7 @@ If USE-CURRENT-REGION is non-nil, return the current region."
|
|||
(let ((inhibit-field-text-motion)
|
||||
(end (point)))
|
||||
(beginning-of-line)
|
||||
(buffer-substring (point) end)))))
|
||||
(buffer-substring-no-properties (point) end)))))
|
||||
|
||||
(defun eshell-copy-old-input ()
|
||||
"Insert after prompt old input at point as new input to be edited."
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
;;; Code:
|
||||
|
||||
(require 'esh-io)
|
||||
(require 'esh-util)
|
||||
|
||||
(defgroup eshell-proc nil
|
||||
"When Eshell invokes external commands, it always does so
|
||||
|
@ -411,9 +412,7 @@ Used only on systems which do not support async subprocesses.")
|
|||
"Send the output from PROCESS (STRING) to the interactive display.
|
||||
This is done after all necessary filtering has been done."
|
||||
(when string
|
||||
(add-text-properties 0 (length string)
|
||||
'(field command-output rear-nonsticky (field))
|
||||
string)
|
||||
(eshell--mark-as-output 0 (length string) string)
|
||||
(require 'esh-mode)
|
||||
(declare-function eshell-interactive-filter "esh-mode" (buffer string))
|
||||
(eshell-interactive-filter (if process (process-buffer process)
|
||||
|
|
|
@ -132,6 +132,19 @@ function `string-to-number'.")
|
|||
(defvar eshell-user-timestamp nil
|
||||
"A timestamp of when the user file was read.")
|
||||
|
||||
(defvar eshell-command-output-properties
|
||||
`( field command-output
|
||||
front-sticky (field)
|
||||
rear-nonsticky (field)
|
||||
;; Text inserted by a user in the middle of process output
|
||||
;; should be marked as output. This is needed for commands
|
||||
;; such as `yank' or `just-one-space' which don't use
|
||||
;; `insert-and-inherit' and thus bypass default text property
|
||||
;; inheritance.
|
||||
insert-in-front-hooks (,#'eshell--mark-as-output
|
||||
,#'eshell--mark-yanked-as-output))
|
||||
"A list of text properties to apply to command output.")
|
||||
|
||||
;;; Obsolete variables:
|
||||
|
||||
(define-obsolete-variable-alias 'eshell-host-names
|
||||
|
@ -157,6 +170,27 @@ Otherwise, evaluates FORM with no error handling."
|
|||
,@handlers)
|
||||
form))
|
||||
|
||||
(defun eshell--mark-as-output (start end &optional object)
|
||||
"Mark the text from START to END as Eshell output.
|
||||
OBJECT can be a buffer or string. If nil, mark the text in the
|
||||
current buffer."
|
||||
(with-silent-modifications
|
||||
(add-text-properties start end eshell-command-output-properties
|
||||
object)))
|
||||
|
||||
(defun eshell--mark-yanked-as-output (start end)
|
||||
"Mark yanked text from START to END as Eshell output."
|
||||
;; `yank' removes the field text property from the text it inserts
|
||||
;; due to `yank-excluded-properties', so arrange for this text
|
||||
;; property to be reapplied in the `after-change-functions'.
|
||||
(letrec ((hook
|
||||
(lambda (start1 end1 _len1)
|
||||
(remove-hook 'after-change-functions hook t)
|
||||
(when (and (= start start1)
|
||||
(= end end1))
|
||||
(eshell--mark-as-output start1 end1)))))
|
||||
(add-hook 'after-change-functions hook nil t)))
|
||||
|
||||
(defun eshell-find-delimiter
|
||||
(open close &optional bound reverse-p backslash-p)
|
||||
"From point, find the CLOSE delimiter corresponding to OPEN.
|
||||
|
|
|
@ -686,11 +686,13 @@ If it's on, just add the vertical display."
|
|||
Should be run via minibuffer `post-command-hook'.
|
||||
See `icomplete-mode' and `minibuffer-setup-hook'."
|
||||
(when (and icomplete-mode
|
||||
;; Check if still in the right buffer (bug#61308)
|
||||
(or (window-minibuffer-p) completion-in-region--data)
|
||||
(icomplete-simple-completing-p)) ;Shouldn't be necessary.
|
||||
(let ((saved-point (point)))
|
||||
(save-excursion
|
||||
(goto-char (icomplete--field-end))
|
||||
; Insert the match-status information:
|
||||
;; Insert the match-status information:
|
||||
(when (and (or icomplete-show-matches-on-no-input
|
||||
(not (equal (icomplete--field-string)
|
||||
icomplete--initial-input)))
|
||||
|
|
|
@ -1126,7 +1126,9 @@ GROUP is a string for decoration purposes and XREF is an
|
|||
maximize (xref-location-line
|
||||
(xref-item-location xref)))
|
||||
for line-format = (and max-line
|
||||
(format "%%%dd:" (1+ (floor (log max-line 10)))))
|
||||
(format
|
||||
#("%%%dd:" 0 4 (face xref-line-number) 5 6 (face shadow))
|
||||
(1+ (floor (log max-line 10)))))
|
||||
with item-text-props = (list 'mouse-face 'highlight
|
||||
'keymap xref--button-map
|
||||
'help-echo
|
||||
|
@ -1146,8 +1148,7 @@ GROUP is a string for decoration purposes and XREF is an
|
|||
((and (equal line prev-line)
|
||||
(equal prev-group group))
|
||||
"")
|
||||
(t (propertize (format line-format line)
|
||||
'face 'xref-line-number)))))
|
||||
(t (format line-format line)))))
|
||||
;; Render multiple matches on the same line, together.
|
||||
(when (and (equal prev-group group)
|
||||
(or (null line)
|
||||
|
|
|
@ -54,8 +54,8 @@
|
|||
(should (equal last-input "echo hello\n"))
|
||||
(should (equal-including-properties
|
||||
last-output
|
||||
(propertize "hello\n" 'rear-nonsticky '(field)
|
||||
'field 'command-output))))))
|
||||
(apply #'propertize "hello\n"
|
||||
eshell-command-output-properties))))))
|
||||
|
||||
(ert-deftest em-prompt-test/field-properties/no-highlight ()
|
||||
"Check that field properties are properly set on Eshell output/prompts.
|
||||
|
@ -77,8 +77,8 @@ This tests the case when `eshell-highlight-prompt' is nil."
|
|||
(should (equal last-input "echo hello\n"))
|
||||
(should (equal-including-properties
|
||||
last-output
|
||||
(propertize "hello\n" 'rear-nonsticky '(field)
|
||||
'field 'command-output)))))))
|
||||
(apply #'propertize "hello\n"
|
||||
eshell-command-output-properties)))))))
|
||||
|
||||
(ert-deftest em-prompt-test/next-previous-prompt ()
|
||||
"Check that navigating forward/backward through old prompts works correctly."
|
||||
|
|
|
@ -73,6 +73,23 @@ Test that trailing arguments outside the subcommand are ignored.
|
|||
e.g. \"{(+ 1 2)} 3\" => 3"
|
||||
(eshell-command-result-equal "{(+ 1 2)} 3" 3))
|
||||
|
||||
(ert-deftest esh-cmd-test/let-rebinds-after-defer ()
|
||||
"Test that let-bound values are properly updated after `eshell-defer'.
|
||||
When inside a `let' block in an Eshell command form, we need to
|
||||
ensure that deferred commands update any let-bound variables so
|
||||
they have the correct values when resuming evaluation. See
|
||||
bug#59469."
|
||||
(skip-unless (executable-find "echo"))
|
||||
(with-temp-eshell
|
||||
(eshell-match-command-output
|
||||
(concat "{"
|
||||
" export LOCAL=value; "
|
||||
" echo \"$LOCAL\"; "
|
||||
" *echo external; " ; This will throw `eshell-defer'.
|
||||
" echo \"$LOCAL\"; "
|
||||
"}")
|
||||
"value\nexternal\nvalue\n")))
|
||||
|
||||
|
||||
;; Lisp forms
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
(file-name-directory (or load-file-name
|
||||
default-directory))))
|
||||
|
||||
(defvar eshell-test-value nil)
|
||||
|
||||
;;; Tests:
|
||||
|
||||
(ert-deftest eshell-test/pipe-headproc ()
|
||||
|
@ -160,6 +162,32 @@ insert the queued one at the next prompt, and finally run it."
|
|||
(beginning-of-line))
|
||||
(should (string= (eshell-get-old-input) "echo alpha"))))
|
||||
|
||||
(ert-deftest eshell-test/get-old-input/rerun-command ()
|
||||
"Test that we can rerun an old command when point is on it."
|
||||
(with-temp-eshell
|
||||
(let ((eshell-test-value "first"))
|
||||
(eshell-match-command-output "echo $eshell-test-value" "first"))
|
||||
;; Go to the previous prompt.
|
||||
(forward-line -2)
|
||||
(let ((inhibit-field-text-motion t))
|
||||
(end-of-line))
|
||||
;; Rerun the command, but with a different variable value.
|
||||
(let ((eshell-test-value "second"))
|
||||
(eshell-send-input))
|
||||
(eshell-match-output "second")))
|
||||
|
||||
(ert-deftest eshell-test/get-old-input/run-output ()
|
||||
"Test that we can run a line of output as a command when point is on it."
|
||||
(with-temp-eshell
|
||||
(eshell-match-command-output "echo \"echo there\"" "echo there")
|
||||
;; Go to the output, and insert "hello" after "echo".
|
||||
(forward-line -1)
|
||||
(forward-word)
|
||||
(insert " hello")
|
||||
;; Run the line as a command.
|
||||
(eshell-send-input)
|
||||
(eshell-match-output "(\"hello\" \"there\")")))
|
||||
|
||||
(provide 'eshell-tests)
|
||||
|
||||
;;; eshell-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue