Be more precise about navigating forward/backward through Eshell prompts

* lisp/eshell/em-prompt.el (eshell-next-prompt): Make N optional.
When navigating, first move to the end of the prompt.  This makes the
subsequent navigation more predictable.
(eshell-previous-prompt): Mane N optional.

* test/lisp/eshell/em-prompt-tests.el
(em-prompt-test--with-multiline): Move to "Code" section.
(em-prompt-test/next-previous-prompt-with): Rename to...
(em-prompt-test/next-previous-prompt-1): ... this, and add additional
test cases.  Update callers.
(em-prompt-test/forward-backward-matching-input-with): Rename to...
(em-prompt-test/forward-backward-matching-input-1): ... this, and
improve existing test cases.
This commit is contained in:
Jim Porter 2023-09-02 14:44:33 -07:00
parent 088fec0a1f
commit 5a430f90c1
2 changed files with 72 additions and 36 deletions

View file

@ -172,16 +172,27 @@ negative, find the Nth next match."
(interactive (eshell-regexp-arg "Backward input matching (regexp): "))
(eshell-forward-matching-input regexp (- arg)))
(defun eshell-next-prompt (n)
(defun eshell-next-prompt (&optional n)
"Move to end of Nth next prompt in the buffer."
(interactive "p")
(unless n (setq n 1))
;; First, move point to our starting position: the end of the
;; current prompt (aka the beginning of the input), if any. (The
;; welcome message and output from commands don't count as having a
;; current prompt.)
(pcase (get-text-property (point) 'field)
('command-output)
('prompt (goto-char (field-end)))
(_ (when-let ((match (text-property-search-backward 'field 'prompt t)))
(goto-char (prop-match-end match)))))
;; Now, move forward/backward to our destination prompt.
(if (natnump n)
(while (and (> n 0)
(text-property-search-forward 'field 'prompt t))
(setq n (1- n)))
(let (match this-match)
;; Don't count the current prompt.
(text-property-search-backward 'field 'prompt t)
;; Go to the beginning of the current prompt.
(goto-char (field-beginning (point) t))
(while (and (< n 0)
(setq this-match (text-property-search-backward
'field 'prompt t)))
@ -190,10 +201,10 @@ negative, find the Nth next match."
(when match
(goto-char (prop-match-end match))))))
(defun eshell-previous-prompt (n)
(defun eshell-previous-prompt (&optional n)
"Move to end of Nth previous prompt in the buffer."
(interactive "p")
(eshell-next-prompt (- n)))
(eshell-next-prompt (- (or n 1))))
(defun eshell-skip-prompt ()
"Skip past the text matching regexp `eshell-prompt-regexp'.

View file

@ -32,6 +32,11 @@
(file-name-directory (or load-file-name
default-directory))))
(defmacro em-prompt-test--with-multiline (&rest body)
"Execute BODY with a multiline Eshell prompt."
`(let ((eshell-prompt-function (lambda () "multiline prompt\n$ ")))
,@body))
;;; Tests:
(ert-deftest em-prompt-test/field-properties ()
@ -80,39 +85,56 @@ This tests the case when `eshell-highlight-prompt' is nil."
(apply #'propertize "hello\n"
eshell-command-output-properties)))))))
(defmacro em-prompt-test--with-multiline (&rest body)
"Execute BODY with a multiline Eshell prompt."
`(let ((eshell-prompt-function (lambda () "multiline prompt\n$ ")))
,@body))
(defun em-prompt-test/next-previous-prompt-with ()
(defun em-prompt-test/next-previous-prompt-1 ()
"Helper for checking forward/backward navigation of old prompts."
(with-temp-eshell
(eshell-insert-command "echo one")
(eshell-insert-command "echo two")
(eshell-insert-command "echo three")
(insert "echo fou") ; A partially-entered command.
;; Go back one prompt.
(eshell-previous-prompt 1)
(should (equal (eshell-get-old-input) "echo three"))
;; Go back two prompts, starting from the end of this line.
(end-of-line)
(eshell-previous-prompt 2)
(should (equal (eshell-get-old-input) "echo one"))
;; Go forward three prompts.
(eshell-next-prompt 3)
(should (equal (eshell-get-old-input) "echo fou"))))
(ert-info ("Go back one prompt")
(eshell-previous-prompt)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo three\n")))
(ert-info ("Go back two prompts, starting from the end of the input")
(end-of-line)
(eshell-previous-prompt 2)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo one\n")))
(ert-info ("Go to the current prompt, starting from the end of the input")
(end-of-line)
(eshell-previous-prompt 0)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo one\n")))
(ert-info ("Go forward one prompt")
(eshell-next-prompt)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo two\n")))
(ert-info ("Go forward two prompts")
(eshell-next-prompt 2)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo fou")))
(ert-info ("Go back one prompt, starting from the beginning of the line")
(forward-line 0)
(eshell-previous-prompt 1)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo three\n")))
(ert-info ("Go back one prompt, starting from the previous prompt's output")
(forward-line -1)
(eshell-previous-prompt 1)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo two\n")))))
(ert-deftest em-prompt-test/next-previous-prompt ()
"Check that navigating forward/backward through old prompts works correctly."
(em-prompt-test/next-previous-prompt-with))
(em-prompt-test/next-previous-prompt-1))
(ert-deftest em-prompt-test/next-previous-prompt-multiline ()
"Check old prompt forward/backward navigation for multiline prompts."
(em-prompt-test--with-multiline
(em-prompt-test/next-previous-prompt-with)))
(em-prompt-test/next-previous-prompt-1)))
(defun em-prompt-test/forward-backward-matching-input-with ()
(defun em-prompt-test/forward-backward-matching-input-1 ()
"Helper for checking forward/backward navigation via regexps."
(with-temp-eshell
(eshell-insert-command "echo one")
@ -120,24 +142,27 @@ This tests the case when `eshell-highlight-prompt' is nil."
(eshell-insert-command "echo two")
(eshell-insert-command "echo three")
(insert "echo fou") ; A partially-entered command.
;; Go back one prompt.
(eshell-backward-matching-input "echo" 1)
(should (equal (eshell-get-old-input) "echo three"))
;; Go back two prompts, starting from the end of this line.
(end-of-line)
(eshell-backward-matching-input "echo" 2)
(should (equal (eshell-get-old-input) "echo one"))
;; Go forward three prompts.
(eshell-forward-matching-input "echo" 3)
(should (equal (eshell-get-old-input) "echo fou"))))
(ert-info ("Go back one prompt")
(eshell-backward-matching-input "echo" 1)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo three\n")))
(ert-info ("Go back two prompts, starting from the end of this line")
(end-of-line)
(eshell-backward-matching-input "echo" 2)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo one\n")))
(ert-info ("Go forward three prompts")
(eshell-forward-matching-input "echo" 3)
(should (equal (point) (field-beginning)))
(should (equal (field-string) "echo fou")))))
(ert-deftest em-prompt-test/forward-backward-matching-input ()
"Check that navigating forward/backward via regexps works correctly."
(em-prompt-test/forward-backward-matching-input-with))
(em-prompt-test/forward-backward-matching-input-1))
(ert-deftest em-prompt-test/forward-backward-matching-input-multiline ()
"Check forward/backward regexp navigation for multiline prompts."
(em-prompt-test--with-multiline
(em-prompt-test/forward-backward-matching-input-with)))
(em-prompt-test/forward-backward-matching-input-1)))
;;; em-prompt-tests.el ends here