bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Request for enhancement: Scrolling (etc.) in incremental search.


From: Alan Mackenzie
Subject: Request for enhancement: Scrolling (etc.) in incremental search.
Date: 24 Feb 2003 22:10:39 +0100
Date: Mon, 24 Feb 2003 21:13:22 +0000
User-agent: tin/1.4.5-20010409 ("One More Nightmare") (UNIX) (Linux/2.0.35 (i686))

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: <prior> (scroll-down) or <next> (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 <up> 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  <acm@muc.de>

        * 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-<upper case>, 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").





reply via email to

[Prev in Thread] Current Thread [Next in Thread]