Ensure navigating by paragraphs in Eshell stops at prompts and paragraphs

The previous implementation in 6ae2b74ed2 only stopped at prompts,
which isn't the right behavior (bug#61545).

* lisp/eshell/em-prompt.el (eshell-forward-paragraph)
(eshell-backward-paragraph): Reimplement to handle prompts and
paragraphs (the latter by calling the original 'forward-paragraph').

* test/lisp/eshell/em-prompt-tests.el
(em-prompt-test/next-previous-prompt/multiline): Rename.
(em-prompt-test/forward-backward-paragraph-1): New function.
(em-prompt-test/forward-backward-paragraph)
(em-prompt-test/forward-backward-paragraph/multiline): New tests.
This commit is contained in:
Jim Porter 2024-06-13 21:26:53 -07:00
parent 1a55e957ae
commit e22b072423
2 changed files with 75 additions and 6 deletions

View file

@ -167,17 +167,39 @@ negative, find the Nth next match."
(defun eshell-forward-paragraph (&optional n)
"Move to the beginning of the Nth next prompt in the buffer.
Like `forward-paragraph', but navigates using fields."
Like `forward-paragraph', but also stops at the beginning of each prompt."
(interactive "p")
(eshell-next-prompt n)
(goto-char (field-beginning (point) t)))
(unless n (setq n 1))
(let (;; We'll handle the "paragraph" starts ourselves.
(paragraph-start regexp-unmatchable)
(inhibit-field-text-motion t))
(cond
((> n 0)
(while (and (> n 0) (< (point) (point-max)))
(let ((next-paragraph (save-excursion (forward-paragraph) (point)))
(next-prompt (save-excursion
(if-let ((match (text-property-search-forward
'field 'prompt t t)))
(prop-match-beginning match)
(point-max)))))
(goto-char (min next-paragraph next-prompt)))
(setq n (1- n))))
((< n 0)
(while (and (< n 0) (> (point) (point-min)))
(let ((prev-paragraph (save-excursion (backward-paragraph) (point)))
(prev-prompt (save-excursion
(if (text-property-search-backward
'field 'prompt t)
(point)
(point-min)))))
(goto-char (max prev-paragraph prev-prompt)))
(setq n (1+ n)))))))
(defun eshell-backward-paragraph (&optional n)
"Move to the beginning of the Nth previous prompt in the buffer.
Like `backward-paragraph', but navigates using fields."
(interactive "p")
(eshell-previous-prompt n)
(goto-char (field-beginning (point) t)))
(eshell-forward-paragraph (- (or n 1))))
(defun eshell-next-prompt (&optional n)
"Move to end of Nth next prompt in the buffer."

View file

@ -39,6 +39,9 @@
;;; Tests:
;; Prompt output
(ert-deftest em-prompt-test/field-properties ()
"Check that field properties are properly set on Eshell output/prompts."
(with-temp-eshell
@ -104,6 +107,9 @@ This tests the case when `eshell-highlight-prompt' is nil."
'front-sticky '(read-only field font-lock-face)
'rear-nonsticky '(read-only field font-lock-face)))))))
;; Prompt navigation
(defun em-prompt-test/next-previous-prompt-1 ()
"Helper for checking forward/backward navigation of old prompts."
(with-temp-eshell
@ -150,11 +156,52 @@ This tests the case when `eshell-highlight-prompt' is nil."
"Check that navigating forward/backward through old prompts works correctly."
(em-prompt-test/next-previous-prompt-1))
(ert-deftest em-prompt-test/next-previous-prompt-multiline ()
(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-1)))
(defun em-prompt-test/forward-backward-paragraph-1 ()
"Helper for checking forward/backward navigation by paragraphs."
(with-temp-eshell
(cl-flet ((at-prompt-for-command-p (command)
(and (equal (point) (field-beginning))
(equal (get-text-property (point) 'field) 'prompt)
(save-excursion
(goto-char (field-end))
(equal (field-string) command)))))
(eshell-insert-command "echo 'high five'")
(eshell-insert-command "echo 'up high\n\ndown low'")
(eshell-insert-command "echo 'too slow'")
(insert "echo goodby") ; A partially-entered command.
(ert-info ("Go back to the last prompt")
(eshell-backward-paragraph)
(should (at-prompt-for-command-p "echo goodby")))
(ert-info ("Go back to the paragraph break")
(eshell-backward-paragraph 2)
(should (looking-at "\ndown low\n")))
(ert-info ("Go forward to the third prompt")
(eshell-forward-paragraph)
(should (at-prompt-for-command-p "echo 'too slow'\n")))
(ert-info ("Go backward to before the first prompt")
(eshell-backward-paragraph 5)
(should (looking-back "Welcome to the Emacs shell\n")))
(ert-info ("Go backward to the beginning of the buffer")
(eshell-backward-paragraph)
(should (bobp)))
(ert-info ("Go forward to the second prompt")
(eshell-forward-paragraph 3)
(should (at-prompt-for-command-p "echo 'up high\n\ndown low'\n"))))))
(ert-deftest em-prompt-test/forward-backward-paragraph ()
"Check that navigating forward/backward through paragraphs works correctly."
(em-prompt-test/forward-backward-paragraph-1))
(ert-deftest em-prompt-test/forward-backward-paragraph/multiline ()
"Check paragraph forward/backward navigation for multiline prompts."
(em-prompt-test--with-multiline
(em-prompt-test/forward-backward-paragraph-1)))
(defun em-prompt-test/forward-backward-matching-input-1 ()
"Helper for checking forward/backward navigation via regexps."
(with-temp-eshell