From e2496d2ff3b740a24e52618b1e9599784ca84bb1 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Sat, 9 Apr 2021 23:08:22 +0000 Subject: [PATCH] New user options to move between isearch matches * lisp/isearch.el (isearch-allow-motion, isearch-motion-changes-direction): New user options. (isearch-pre-command-hook): Handle the new options. * etc/NEWS: Mention the new user options. * doc/emacs/search.texi: Document the new user options. --- doc/emacs/search.texi | 12 ++++++++++ etc/NEWS | 13 +++++++++++ lisp/isearch.el | 52 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/doc/emacs/search.texi b/doc/emacs/search.texi index 38430a2ab1..de644505f7 100644 --- a/doc/emacs/search.texi +++ b/doc/emacs/search.texi @@ -595,6 +595,18 @@ or the selected window and frame. The command must not itself attempt an incremental search. This feature is disabled if @code{isearch-allow-scroll} is @code{nil} (which it is by default). +@vindex isearch-allow-motion +@vindex isearch-motion-changes-direction + Likewise, if you change the variable @code{isearch-allow-motion} +to a non-@code{nil} value, this enables the use of the keyboard motion +commands @kbd{M-<}, @kbd{M->}, @kbd{C-v} and @kbd{M-v}, to move +respectively to the first occurrence of the current search string in +the buffer, the last one, the first one after the current window, +and the last one before the current window. The search direction +does not change when these motion commands are used, unless you change +the variable @code{isearch-motion-changes-direction} to a non-@code{nil} +value. + @item Motion Commands @cindex motion commands, during incremental search When @code{isearch-yank-on-move} is customized to @code{shift}, diff --git a/etc/NEWS b/etc/NEWS index a0f05d8cf1..b8921f1405 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -373,6 +373,19 @@ When this option is set, direction changes in Isearch move to another search match, if there is one, instead of moving point to the other end of the current match. ++++ +** New user options 'isearch-allow-motion' and +'isearch-motion-changes-direction'. +When 'isearch-allow-motion' is set, the commands 'beginning-of-buffer', +'end-of-buffer', 'scroll-up-command' and 'scroll-down-command', when +invoked during I-search, move respectively to the first occurrence of +the current search string in the buffer, the last one, the first one +after the current window, and the last one before the current window. +Additionally, users can change the meaning of other motion commands +during I-search by using their 'isearch-motion' property. When +'isearch-motion-changes-direction' is set, the motion commands also +change the direction of the search. + ** Outline +++ diff --git a/lisp/isearch.el b/lisp/isearch.el index 5efac4c78f..6df31af589 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -2882,12 +2882,45 @@ If non-nil, scrolling commands can be used in Isearch mode. However, you cannot scroll far enough that the current match is no longer visible (is off screen). But if the value is `unlimited' that limitation is removed and you can scroll any distance off screen. -If nil, scrolling commands exit Isearch mode." +If nil, scrolling commands exit Isearch mode. +See also the related option `isearch-allow-motion'." :type '(choice (const :tag "Scrolling exits Isearch" nil) (const :tag "Scrolling with current match on screen" t) (const :tag "Scrolling with current match off screen" unlimited)) :group 'isearch) +(put 'beginning-of-buffer 'isearch-motion + '((lambda () (goto-char (point-min))) . forward)) +(put 'end-of-buffer 'isearch-motion + '((lambda () (goto-char (point-max))) . backward)) +(put 'scroll-up-command 'isearch-motion + '((lambda () (goto-char (window-end))) . forward)) +(put 'scroll-down-command 'isearch-motion + '((lambda () (goto-char (window-start)) (recenter nil t)) . backward)) + +(defcustom isearch-allow-motion nil + "Whether to allow movement between isearch matches by cursor motion commands. +If non-nil, the four motion commands `beginning-of-buffer', `end-of-buffer', +`scroll-up-command' and `scroll-down-command', when invoked during Isearch, +move respectively to the first occurrence of the current search string in the +buffer, the last one, the first one after the current window, and the last one +before the current window. +If nil, these motion commands normally exit Isearch and are executed. +See also the related option `isearch-allow-scroll'." + :type '(choice (const :tag "Off" nil) + (const :tag "On" t)) + :group 'isearch + :version "28.1") + +(defcustom isearch-motion-changes-direction nil + "Whether motion commands during incremental search change search direction. +If nil, the search direction (forward or backward) does not change when +motion commands are used during incremental search, except when wrapping." + :type '(choice (const :tag "Off" nil) + (const :tag "On" t)) + :group 'isearch + :version "28.1") + (defcustom isearch-allow-prefix t "Whether prefix arguments are allowed during incremental search. If non-nil, entering a prefix argument will not terminate the @@ -2989,6 +3022,23 @@ See more for options in `search-exit-option'." ;; Optionally edit the search string instead of exiting. ((eq search-exit-option 'edit) (setq this-command 'isearch-edit-string)) + ;; Handle motion command functions. + ((and isearch-allow-motion + (symbolp this-command) + (get this-command 'isearch-motion)) + (let* ((property (get this-command 'isearch-motion)) + (function (car property)) + (current-direction (if isearch-forward 'forward 'backward)) + (direction (or (cdr property) + (if isearch-forward 'forward 'backward)))) + (funcall function) + (setq isearch-just-started t) + (isearch-repeat direction) + (when (and isearch-success (not isearch-motion-changes-direction)) + (unless (eq direction current-direction) + (let ((isearch-repeat-on-direction-change nil)) + (isearch-repeat current-direction)))) + (setq this-command 'ignore))) ;; Handle a scrolling function or prefix argument. ((or (and isearch-allow-prefix (memq this-command '(universal-argument universal-argument-more -- 2.30.2