From: Jim Porter Date: Fri, 14 Jun 2024 04:26:53 +0000 (-0700) Subject: Ensure navigating by paragraphs in Eshell stops at prompts and paragraphs X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=39fdb835a9ebf4e3574fdee85094b404569a24a1;p=emacs.git Ensure navigating by paragraphs in Eshell stops at prompts and paragraphs The previous implementation in 6ae2b74ed20 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. (cherry picked from commit e22b072423a6764632328d4e0fecc06a6e7efe9b) --- diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el index b6556d29544..7de2bd4dc21 100644 --- a/lisp/eshell/em-prompt.el +++ b/lisp/eshell/em-prompt.el @@ -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." diff --git a/test/lisp/eshell/em-prompt-tests.el b/test/lisp/eshell/em-prompt-tests.el index 964609e6410..fbadade061f 100644 --- a/test/lisp/eshell/em-prompt-tests.el +++ b/test/lisp/eshell/em-prompt-tests.el @@ -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