From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.bugs Subject: Request for enhancement: Scrolling (etc.) in incremental search. Date: 24 Feb 2003 22:10:39 +0100 Organization: muc.de e.V. -- private internet access Sender: bug-gnu-emacs-bounces+gnu-bug-gnu-emacs=m.gmane.org@gnu.org Message-ID: NNTP-Posting-Host: main.gmane.org X-Trace: main.gmane.org 1046121491 14940 80.91.224.249 (24 Feb 2003 21:18:11 GMT) X-Complaints-To: usenet@main.gmane.org NNTP-Posting-Date: Mon, 24 Feb 2003 21:18:11 +0000 (UTC) Return-path: Original-Received: from monty-python.gnu.org ([199.232.76.173]) by main.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 18nPzE-0003sn-00 for ; Mon, 24 Feb 2003 22:18:08 +0100 Original-Received: from localhost ([127.0.0.1] helo=monty-python.gnu.org) by monty-python.gnu.org with esmtp (Exim 4.10.13) id 18nPz9-0005fQ-02 for gnu-bug-gnu-emacs@m.gmane.org; Mon, 24 Feb 2003 16:18:03 -0500 Original-Received: from list by monty-python.gnu.org with tmda-scanned (Exim 4.10.13) id 18nPyp-0005eD-00 for bug-gnu-emacs@gnu.org; Mon, 24 Feb 2003 16:17:43 -0500 Original-Received: from mail by monty-python.gnu.org with spam-scanned (Exim 4.10.13) id 18nPtx-0003M5-00 for bug-gnu-emacs@gnu.org; Mon, 24 Feb 2003 16:13:13 -0500 Original-Received: from fencepost.gnu.org ([199.232.76.164]) by monty-python.gnu.org with esmtp (Exim 4.10.13) id 18nPtw-0003Lz-00 for bug-gnu-emacs@gnu.org; Mon, 24 Feb 2003 16:12:40 -0500 Original-Received: from monty-python.gnu.org ([199.232.76.173]) by fencepost.gnu.org with esmtp (Exim 4.10) id 18nPtw-0007vV-01 for gnu-emacs-bug@prep.ai.mit.edu; Mon, 24 Feb 2003 16:12:40 -0500 Original-Received: from mail by monty-python.gnu.org with spam-scanned (Exim 4.10.13) id 18nPtP-00032m-00 for gnu-emacs-bug@prep.ai.mit.edu; Mon, 24 Feb 2003 16:12:39 -0500 Original-Received: from marvin.muc.de ([193.149.48.2]) by monty-python.gnu.org with smtp (Exim 4.10.13) id 18nPtP-00031Y-00 for gnu-emacs-bug@prep.ai.mit.edu; Mon, 24 Feb 2003 16:12:07 -0500 Original-Received: (qmail 49402 invoked by uid 8); 24 Feb 2003 21:10:40 -0000 Original-To: gnu-emacs-bug@prep.ai.mit.edu Original-Path: not-for-mail Original-Newsgroups: gnu.emacs.bug Original-Date: Mon, 24 Feb 2003 21:13:22 +0000 Original-Lines: 459 Original-NNTP-Posting-Host: acm.muc.de Original-X-Trace: marvin.muc.de 1046121039 49398 193.149.49.134 (24 Feb 2003 21:10:39 GMT) Original-X-Complaints-To: news-admin@muc.de Original-NNTP-Posting-Date: 24 Feb 2003 21:10:39 GMT User-Agent: tin/1.4.5-20010409 ("One More Nightmare") (UNIX) (Linux/2.0.35 (i686)) X-BeenThere: bug-gnu-emacs@gnu.org X-Mailman-Version: 2.1b5 Precedence: list List-Id: Bug reports for GNU Emacs, the Swiss army knife of text editors List-Help: List-Post: List-Subscribe: , List-Archive: List-Unsubscribe: , Errors-To: bug-gnu-emacs-bounces+gnu-bug-gnu-emacs=m.gmane.org@gnu.org Xref: main.gmane.org gmane.emacs.bugs:4510 X-Report-Spam: http://spam.gmane.org/gmane.emacs.bugs:4510 PROBLEM: While searching through a file with incremental search (C-s), I often find myself wanting to do things like this: 1: C-l (recenter) so that I can see what comes after the match; 2: C-M-l (reposition-window) so that I can see what defun I'm in; 3: (scroll-down) or (scroll-up), so that I can see the entire screenfull before/after the match; 4: C-M-v (scroll-other-window) or C-M-S-v (scroll-other-window-down) so that I can peruse, for example, an extensive *Help* buffer in the other window; 5: C-x 2 (split-window-vertically) so that I can "remember" where I got to (by using an other window) before searching further; 6: C-u 15 C-x ^ (enlarge-window) after the above, because the other window is taking up far too much screen space; 7: C-x C-b (list-buffers), so that I can check exactly which file it is I'm searching through. UNFORTUNATELY, as soon as I do any of the above, the search is terminated, and I find it more irritating than most people might believe to have to go C-s again to get the "lazy highlighting" back again. I would like to be able to do these things WHILST REMAINING IN THE SEARCH. The following patch enhances isearch-mode for precisely this purpose. Would the emacs maintainers please consider incorporating it into a future emacs version. Here is how it works: Each command which is to be usable as a "scrolling command" in isearch-mode is given a property iscroll-search with value t. Suitable commands are those which don't change the buffer's contents, don't switch to a different buffer, frame or window and don't mess with isearch-mode's state. There is nothing to stop anybody giving this property to an unsuitable command. The function isearch-other-meta-char looks up the binding of the key-sequence which invoked it, and if this binding is a scrolling command it is executed directly, any prefix-arg being passed into it. (Previously, the key-sequence was merely unread and isearch-mode terminated). Should the command have scrolled the current search-string out of the window, we scroll it back to the edge of the window. To enable prefix-args, isearch-other-meta-char is now (interactive "P"), and the univeral-argument functions (in simple.el) are amended to "bind" overriding-terminal-local-map rather than just setqing it. CURRENT LIMITATIONS: 1: Horizontal scrolling is not correctly handled - any horizontal scrolling done by a command is undone in the function isearch-update. 2: Commands bound to mouse events (in particular, scroll-bar-toolkit-scroll) are not correctly handled when given the isearch-scroll property. Here is a tentative first selection of scrolling commands: ************************************************************************* (put 'recenter 'isearch-scroll t) (put 'reposition-window 'isearch-scroll t) (put 'scroll-down 'isearch-scroll t) (put 'scroll-up 'isearch-scroll t) (put 'beginning-of-buffer-other-window 'isearch-scroll t) (put 'end-of-buffer-other-window 'isearch-scroll t) (put 'scroll-other-window 'isearch-scroll t) (put 'scroll-other-window-down 'isearch-scroll t) (put 'list-buffers 'isearch-scroll t) (put 'delete-other-windows 'isearch-scroll t) (put 'split-window-vertically 'isearch-scroll t) (put 'enlarge-window 'isearch-scroll t) (put 'balance-windows 'isearch-scroll t) (put 'universal-argument 'isearch-scroll t) (put 'negative-argument 'isearch-scroll t) (put 'digit-argument 'isearch-scroll t) ************************************************************************* Here is a change log entry: ************************************************************************* 2003-02-24 Alan Mackenzie * isearch.el: Add scrolling functionality: (isearch-allow-scroll): New customization variable. (isearch-string-out-of-window): New function. (isearch-back-into-window): New function. (isearch-reread-key-sequence-naturally): New function. (isearch-lookup-scroll-key): New function. (isearch-other-meta-char): Amended doc-string. Scroll command functionality added. (isearch-lazy-highlight-window-end): New variable. (isearch-lazy-highlight-new-loop): Add a check for change in window size. * simple.el: Enhance prefix-arg handling for isearch scrolling commands: (acm-universal-argument-map-is-bound): New variable. (acm-saved-overriding-map): New variable. (acm-ensure-universal-argument-map-is-bound): New function. (acm-restore-overriding-map): New function. (universal-argument): Function amended. (universal-argument-more): Function amended. (negative-argument): Function amended. (digit-argument): Function amended. (universal-argument-other-key): Function amended. ************************************************************************* Here is the patch for simple.el ************************************************************************* *** simple.1.593.el Mon Feb 24 18:34:57 2003 --- simple.1.593.acm.el Mon Feb 24 19:58:39 2003 *************** *** 1618,1623 **** --- 1618,1644 ---- `universal-argument-other-key' uses this to discard those events from (this-command-keys), and reread only the final command.") + (defvar acm-universal-argument-map-is-bound nil + "Is t when overriding-terminal-local-map is setqed to + universal-argument-map, nil otherwise.") + + (defvar acm-saved-overriding-map nil + "Holds \(for later restoration\) the previous value of + overriding-terminal-local-map whilst \"universal argument mode\" is active.") + + (defun acm-ensure-universal-argument-map-is-bound () + "\"Bind\" overriding-terminal-local-map to universal-argument map, if it + isn't already so bound." + (unless acm-universal-argument-map-is-bound + (setq acm-saved-overriding-map overriding-terminal-local-map) + (setq overriding-terminal-local-map universal-argument-map) + (setq acm-universal-argument-map-is-bound t))) + + (defun acm-restore-overriding-map () + "Restore overriding-terminal-local-map to its previous value." + (setq overriding-terminal-local-map acm-saved-overriding-map) + (setq acm-universal-argument-map-is-bound nil)) + (defun universal-argument () "Begin a numeric argument for the following command. Digits or minus sign following \\[universal-argument] make up the numeric argument. *************** *** 1631,1637 **** (interactive) (setq prefix-arg (list 4)) (setq universal-argument-num-events (length (this-command-keys))) ! (setq overriding-terminal-local-map universal-argument-map)) ;; A subsequent C-u means to multiply the factor by 4 if we've typed ;; nothing but C-u's; otherwise it means to terminate the prefix arg. --- 1652,1658 ---- (interactive) (setq prefix-arg (list 4)) (setq universal-argument-num-events (length (this-command-keys))) ! (acm-ensure-universal-argument-map-is-bound)) ;; A subsequent C-u means to multiply the factor by 4 if we've typed ;; nothing but C-u's; otherwise it means to terminate the prefix arg. *************** *** 1642,1648 **** (if (eq arg '-) (setq prefix-arg (list -4)) (setq prefix-arg arg) ! (setq overriding-terminal-local-map nil))) (setq universal-argument-num-events (length (this-command-keys)))) (defun negative-argument (arg) --- 1663,1669 ---- (if (eq arg '-) (setq prefix-arg (list -4)) (setq prefix-arg arg) ! (acm-restore-overriding-map))) (setq universal-argument-num-events (length (this-command-keys)))) (defun negative-argument (arg) *************** *** 1656,1662 **** (t (setq prefix-arg '-))) (setq universal-argument-num-events (length (this-command-keys))) ! (setq overriding-terminal-local-map universal-argument-map)) (defun digit-argument (arg) "Part of the numeric argument for the next command. --- 1677,1683 ---- (t (setq prefix-arg '-))) (setq universal-argument-num-events (length (this-command-keys))) ! (acm-ensure-universal-argument-map-is-bound)) (defun digit-argument (arg) "Part of the numeric argument for the next command. *************** *** 1675,1681 **** (t (setq prefix-arg digit)))) (setq universal-argument-num-events (length (this-command-keys))) ! (setq overriding-terminal-local-map universal-argument-map)) ;; For backward compatibility, minus with no modifiers is an ordinary ;; command if digits have already been entered. --- 1696,1702 ---- (t (setq prefix-arg digit)))) (setq universal-argument-num-events (length (this-command-keys))) ! (acm-ensure-universal-argument-map-is-bound)) ;; For backward compatibility, minus with no modifiers is an ordinary ;; command if digits have already been entered. *************** *** 1696,1702 **** (append (nthcdr universal-argument-num-events keylist) unread-command-events))) (reset-this-command-lengths) ! (setq overriding-terminal-local-map nil)) ;;;; Window system cut and paste hooks. --- 1717,1723 ---- (append (nthcdr universal-argument-num-events keylist) unread-command-events))) (reset-this-command-lengths) ! (acm-restore-overriding-map)) ;;;; Window system cut and paste hooks. ************************************************************************* The patch for isearch.el: ************************************************************************** *** isearch.1.218.el Fri Jan 24 20:29:48 2003 --- isearch.1.218.acm.el Mon Feb 24 20:17:09 2003 *************** *** 1225,1242 **** (goto-char isearch-barrier))) (isearch-process-search-char last-command-char)) (defalias 'isearch-other-control-char 'isearch-other-meta-char) ! (defun isearch-other-meta-char () ! "Exit the search normally and reread this key sequence. ! But only if `search-exit-option' is non-nil, the default. ! If it is the symbol `edit', the search string is edited in the minibuffer ! and the meta character is unread so that it applies to editing the string." ! (interactive) ! (let* ((key (this-command-keys)) (main-event (aref key 0)) ! (keylist (listify-key-sequence key))) (cond ((and (= (length key) 1) (let ((lookup (lookup-key function-key-map key))) (not (or (null lookup) (integerp lookup) --- 1225,1334 ---- (goto-char isearch-barrier))) (isearch-process-search-char last-command-char)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; scrolling within isearch-mode. Alan Mackenzie (acm@muc.de), 2003/2/24 + ;; + ;; The idea here is that certain scrolling commands (like C-l (recenter)) + ;; should be usable WITHIN isearch mode. For a command to be suitable, it + ;; must NOT alter the buffer, swap to another buffer or frame, tamper with + ;; isearch's state, or anything like that. It is unacceptable for the search + ;; string to be scrolled out of the current window. If this happens, we + ;; scroll it back again. + ;; + ;; We implement this feature with a property called isearch-scroll. If a + ;; command's symbol has the value t for this property it is a scrolling + ;; command. + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (defcustom isearch-allow-scroll t + "If non-nil, certain scrolling commands are allowed during incremental search." + :type 'boolean + :group 'isearch) + + (defun isearch-string-out-of-window (isearch-point) + "Is the search string currently outside of the window? Return nil if it's + completely visible, or if point is visible, together with as much of the + search string as will fit; 'above if we need to scroll the text downwards; + 'below, if upwards." + (let ((w-start (window-start)) + (w-end (window-end nil t)) + (w-L1 (save-excursion (move-to-window-line 1) (point))) + (w-L-1 (save-excursion (move-to-window-line -1) (point))) + start end) ; start and end of search string in buffer + (if isearch-forward + (setq end isearch-point start (or isearch-other-end isearch-point)) + (setq start isearch-point end (or isearch-other-end isearch-point))) + (cond ((or (and (>= start w-start) (<= end w-end)) + (if isearch-forward + (and (>= isearch-point w-L-1) (< isearch-point w-end)) ; point on Line -1 + (and (>= isearch-point w-start) (< isearch-point w-L1)))) ; point on Line 0 + nil) + ((and (< start w-start) + (< isearch-point w-L-1)) + 'above) + (t 'below)))) + + (defun isearch-back-into-window (above isearch-point) + "Scroll the window to bring the search string back into view, restoring + point to ISEARCH-POINT in the process. ABOVE is t when the search string is + above the top of the window, nil when it is beneath the bottom." + (let (start end) + (if isearch-forward + (setq end isearch-point start (or isearch-other-end isearch-point)) + (setq start isearch-point end (or isearch-other-end isearch-point))) + (if above + (progn + (goto-char start) + (recenter 0) + (when (>= isearch-point (window-end nil t)) + (goto-char isearch-point) + (recenter -1))) + (goto-char end) + (recenter -1) + (when (< isearch-point (window-start)) + (goto-char isearch-point) + (recenter 0)))) + (goto-char isearch-point)) + + (defun isearch-reread-key-sequence-naturally (keylist) + "Reread the current key sequence with isearch-mode's own keymap deactivated. + Return the key sequence as a string/vector." + (apply 'isearch-unread keylist) + (let (overriding-terminal-local-map) + (read-key-sequence nil))) ; This will go through function-key-map, if nec. + + (defun isearch-lookup-scroll-key (key) + "If the supplied key sequence, KEY, is bound to a scrolling command, return + this command (always a symbol), otherwise nil." + (let* ((overriding-terminal-local-map nil) + (binding (key-binding key))) + (and binding (symbolp binding) (commandp binding) + (eq (get binding 'isearch-scroll) t) + binding))) (defalias 'isearch-other-control-char 'isearch-other-meta-char) ! (defun isearch-other-meta-char (&optional arg) ! "See if the current key-sequence can be converted to something usable in ! isearch-mode, either by converting it with the function-key-map, downcasing a ! key with C-, or finding a \"scrolling command\" bound to it. \(In ! the last case, we may have to read more events.\) If so, either unread the ! converted sequence or execute the command. ! ! Otherwise, if `search-exit-option' is non-nil (the default) unread the ! key-sequence and exit the search normally. If it is the symbol `edit', the ! search string is edited in the minibuffer and the meta character is unread so ! that it applies to editing the string. ! ! ARG is the prefix argument. It will be transmitted through to the scrolling ! command or to the command which exits isearch-mode." ! (interactive "P") ! (let* ((key (if current-prefix-arg ! (substring (this-command-keys) universal-argument-num-events) ! (this-command-keys))) (main-event (aref key 0)) ! (keylist (listify-key-sequence key)) ! scroll-function isearch-point) (cond ((and (= (length key) 1) (let ((lookup (lookup-key function-key-map key))) (not (or (null lookup) (integerp lookup) *************** *** 1288,1293 **** --- 1380,1402 ---- ((eq search-exit-option 'edit) (apply 'isearch-unread keylist) (isearch-edit-string)) + ;; Handle a scrolling function. + ((and isearch-allow-scroll + (progn (setq key (isearch-reread-key-sequence-naturally keylist)) + (setq keylist (listify-key-sequence key)) + (setq main-event (aref key 0)) + (setq scroll-function (isearch-lookup-scroll-key key)))) + ;; From this point onwards, KEY, KEYLIST and MAIN-EVENT hold a + ;; complete key sequence, possibly as modified by function-key-map, + ;; not merely the one or two event fragment which invoked + ;; isearch-other-meta-char in the first place. + (setq isearch-point (point)) + (setq prefix-arg arg) + (command-execute scroll-function) + (let ((ab-bel (isearch-string-out-of-window isearch-point))) + (if ab-bel + (isearch-back-into-window (eq ab-bel 'above) isearch-point))) + (isearch-update)) (search-exit-option (let (window) (cancel-kbd-macro-events) *************** *** 1334,1341 **** (isearch-done) (isearch-clean-overlays)) (isearch-done) ! (isearch-clean-overlays)))) ! (t;; otherwise nil (isearch-process-search-string key key))))) (defun isearch-quote-char () --- 1443,1451 ---- (isearch-done) (isearch-clean-overlays)) (isearch-done) ! (isearch-clean-overlays) ! (setq prefix-arg arg)))) ! (t;; otherwise nil (isearch-process-search-string key key))))) (defun isearch-quote-char () *************** *** 1996,2001 **** --- 2106,2112 ---- (defvar isearch-lazy-highlight-last-string nil) (defvar isearch-lazy-highlight-window nil) (defvar isearch-lazy-highlight-window-start nil) + (defvar isearch-lazy-highlight-window-end nil) ; ACM, 2003/2/21 (defvar isearch-lazy-highlight-case-fold-search nil) (defvar isearch-lazy-highlight-regexp nil) *************** *** 2030,2041 **** (not (eq isearch-lazy-highlight-regexp isearch-regexp)) (not (= (window-start) ! isearch-lazy-highlight-window-start)))) ;; something important did indeed change (isearch-lazy-highlight-cleanup t) ;kill old loop & remove overlays (when (not isearch-invalid-regexp) (setq isearch-lazy-highlight-window (selected-window) isearch-lazy-highlight-window-start (window-start) isearch-lazy-highlight-start (point) isearch-lazy-highlight-end (point) isearch-lazy-highlight-last-string isearch-string --- 2141,2155 ---- (not (eq isearch-lazy-highlight-regexp isearch-regexp)) (not (= (window-start) ! isearch-lazy-highlight-window-start)) ! (not (= (window-end) ; Window may have been split/joined. ! isearch-lazy-highlight-window-end)))) ; ACM, 2003/2/21 ;; something important did indeed change (isearch-lazy-highlight-cleanup t) ;kill old loop & remove overlays (when (not isearch-invalid-regexp) (setq isearch-lazy-highlight-window (selected-window) isearch-lazy-highlight-window-start (window-start) + isearch-lazy-highlight-window-end (window-end) ; ACM, 2003/2/21 isearch-lazy-highlight-start (point) isearch-lazy-highlight-end (point) isearch-lazy-highlight-last-string isearch-string ************************************************************************* -- Alan Mackenzie (Munich, Germany) Email: aacm@muuc.dee; to decode, wherever there is a repeated letter (like "aa"), remove half of them (leaving, say, "a").