From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.bugs Subject: bug#17453: Isearch doesn't work properly with Follow Mode. Date: Fri, 9 May 2014 22:44:58 +0000 Message-ID: <20140509224458.GA4205@acm.acm> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1399675846 17249 80.91.229.3 (9 May 2014 22:50:46 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Fri, 9 May 2014 22:50:46 +0000 (UTC) To: 17453@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sat May 10 00:50:38 2014 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1Witck-0004Lf-Cq for geb-bug-gnu-emacs@m.gmane.org; Sat, 10 May 2014 00:50:38 +0200 Original-Received: from localhost ([::1]:54753 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Witcj-0003un-Tn for geb-bug-gnu-emacs@m.gmane.org; Fri, 09 May 2014 18:50:37 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:55729) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WitcO-0003tC-2U for bug-gnu-emacs@gnu.org; Fri, 09 May 2014 18:50:25 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WitcE-0006uv-4M for bug-gnu-emacs@gnu.org; Fri, 09 May 2014 18:50:16 -0400 Original-Received: from debbugs.gnu.org ([140.186.70.43]:40048) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WitcE-0006oE-1Q for bug-gnu-emacs@gnu.org; Fri, 09 May 2014 18:50:06 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.80) (envelope-from ) id 1WitcA-0008Ju-S0 for bug-gnu-emacs@gnu.org; Fri, 09 May 2014 18:50:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Alan Mackenzie Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 09 May 2014 22:50:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 17453 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.139967578531952 (code B ref -1); Fri, 09 May 2014 22:50:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 9 May 2014 22:49:45 +0000 Original-Received: from localhost ([127.0.0.1]:57399 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1Witbr-0008JF-Tt for submit@debbugs.gnu.org; Fri, 09 May 2014 18:49:45 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:53936) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1Witbp-0008J2-4j for submit@debbugs.gnu.org; Fri, 09 May 2014 18:49:42 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WitbZ-0006fk-Gx for submit@debbugs.gnu.org; Fri, 09 May 2014 18:49:35 -0400 Original-Received: from lists.gnu.org ([208.118.235.17]:45459) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WitbZ-0006fg-Do for submit@debbugs.gnu.org; Fri, 09 May 2014 18:49:25 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:55651) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WitbR-0003SF-Lt for bug-gnu-emacs@gnu.org; Fri, 09 May 2014 18:49:25 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WitbK-0006eC-2w for bug-gnu-emacs@gnu.org; Fri, 09 May 2014 18:49:17 -0400 Original-Received: from colin.muc.de ([193.149.48.1]:56704 helo=mail.muc.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WitbJ-0006e4-Lp for bug-gnu-emacs@gnu.org; Fri, 09 May 2014 18:49:10 -0400 Original-Received: (qmail 29276 invoked by uid 3782); 9 May 2014 22:49:05 -0000 Original-Received: from acm.muc.de (pD9519D80.dip0.t-ipconnect.de [217.81.157.128]) by colin.muc.de (tmda-ofmipd) with ESMTP; Sat, 10 May 2014 00:49:04 +0200 Original-Received: (qmail 4310 invoked by uid 1000); 9 May 2014 22:44:58 -0000 Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-Delivery-Agent: TMDA/1.1.12 (Macallan) X-Primary-Address: acm@muc.de X-detected-operating-system: by eggs.gnu.org: FreeBSD 8.x X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 140.186.70.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:88843 Archived-At: Hi, Emacs. Isearch doesn't work properly with Follow Mode. There follows a patch which is a first attempt to fix this. There are three main problems (there may be others): 1. Isearch commands stay in the same window when they ought to move across the entire screen. This is only actually a problem with lazy highlighting enabled. 2. Lazy highlighting is only applied to the current follow window, not all of them. 3. Scroll commands cannot scroll out of the current window. They ought to be able to scroll through any follow window. Most of the exercise was straightforward. The only trouble came where isearch-message (or isearch-message-function) was called from isearch-search. There are circumstances (see comments in the code) where calling isearch-message with follow-mode enabled can cause unwanted vertical scrolling. This happens when point is temporarily moved to isearch-other-end and then calling isearch-search. To get round this I refactored isearch-message out of isearch-search and inserted into the places just before isearch-search is called. Here's the patch: --- emacs/emacs.bzr/trunk/lisp/isearch.el 2014-03-03 11:50:56.000000000 +0000 +++ /home/acm/isearch.el 2014-05-09 22:10:29.000000000 +0000 @@ -166,6 +166,21 @@ :type 'boolean :group 'isearch) +(defvar isearch-windows nil + "List of windows active in the incremental search. +This is either the ordered list of active windows administered by +`follow-mode' or a list of the single window involved in the +search.") + +(defmacro isearch-windows-start (&optional wins) + "Get the start point of the windows involved in the isearch." + `(window-start (car ,(if wins wins 'isearch-windows)))) + +(defmacro isearch-windows-end (&optional wins update) + "Get the end point of the windows involved in the isearch." + `(window-end (car (last ,(if wins wins 'isearch-windows))) + ,@(if update `(,update)))) + (defvar isearch-mode-hook nil "Function(s) to call after starting up an incremental search.") @@ -184,6 +199,11 @@ "Function to call to display the search prompt. If nil, use function `isearch-message'.") +(defmacro isearch-call-message (&optional cqh ellip) + `(if isearch-message-function + (funcall isearch-message-function ,cqh ,ellip) + (isearch-message ,cqh ,ellip))) + (defvar isearch-wrap-function nil "Function to call to wrap the search when search is failed. If nil, move point to the beginning of the buffer for a forward search, @@ -203,7 +223,7 @@ `isearch-message-prefix' advice property to specify the prefix string displayed in the search message.") -;; Search ring. +;;; Search ring. (defvar search-ring nil "List of search string sequences.") @@ -860,6 +880,9 @@ isearch-regexp regexp isearch-word word isearch-op-fun op-fun + isearch-windows (if (and (boundp 'follow-mode) follow-mode) + (follow-all-followers) + (list (selected-window))) isearch-last-case-fold-search isearch-case-fold-search isearch-case-fold-search case-fold-search isearch-invisible search-invisible @@ -1254,13 +1277,6 @@ (unwind-protect (progn ,@body) - ;; Set point at the start (end) of old match if forward (backward), - ;; so after exiting minibuffer isearch resumes at the start (end) - ;; of this match and can find it again. - (if (and old-other-end (eq old-point (point)) - (eq isearch-forward isearch-new-forward)) - (goto-char old-other-end)) - ;; Always resume isearching by restarting it. (isearch-mode isearch-forward isearch-regexp @@ -1273,7 +1289,17 @@ isearch-message isearch-new-message isearch-forward isearch-new-forward isearch-word isearch-new-word - isearch-case-fold-search isearch-new-case-fold)) + isearch-case-fold-search isearch-new-case-fold) + + ;; Restore the minibuffer message before moving point. + (isearch-call-message nil t) + + ;; Set point at the start (end) of old match if forward (backward), + ;; so after exiting minibuffer isearch resumes at the start (end) + ;; of this match and can find it again. + (if (and old-other-end (eq old-point (point)) + (eq isearch-forward isearch-new-forward)) + (goto-char old-other-end))) ;; Empty isearch-string means use default. (when (= 0 (length isearch-string)) @@ -1417,16 +1443,20 @@ (isearch-ring-adjust1 nil)) ;; If already have what to search for, repeat it. (or isearch-success - (progn - ;; Set isearch-wrapped before calling isearch-wrap-function - (setq isearch-wrapped t) - (if isearch-wrap-function - (funcall isearch-wrap-function) - (goto-char (if isearch-forward (point-min) (point-max))))))) + ;; Set isearch-wrapped before calling isearch-wrap-function + (setq isearch-wrapped t))) ;; C-s in reverse or C-r in forward, change direction. (setq isearch-forward (not isearch-forward) isearch-success t)) + (isearch-call-message nil t) ; Call before moving point. + (if (and (eq isearch-forward (eq direction 'forward)) + (not (equal isearch-string "")) + (not isearch-success)) + (if isearch-wrap-function + (funcall isearch-wrap-function) + (goto-char (if isearch-forward (point-min) (point-max))))) + (setq isearch-barrier (point)) ; For subsequent \| if regexp. (if (equal isearch-string "") @@ -1867,6 +1897,7 @@ (length isearch-string)))) isearch-message (mapconcat 'isearch-text-char-description isearch-string ""))) + (isearch-call-message nil t) ; Do this before moving point. ;; Use the isearch-other-end as new starting point to be able ;; to find the remaining part of the search string again. ;; This is like what `isearch-search-and-update' does, @@ -2041,6 +2072,7 @@ (setq isearch-case-fold-search (isearch-no-upper-case-p isearch-string isearch-regexp)))) ;; Not regexp, not reverse, or no match at point. + (isearch-call-message nil t) ; Do this before moving point. (if (and isearch-other-end (not isearch-adjusted)) (goto-char (if isearch-forward isearch-other-end (min isearch-opoint @@ -2207,10 +2239,12 @@ together with as much of the search string as will fit; the symbol `above' if we need to scroll the text downwards; the symbol `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))) + (let ((w-start (isearch-windows-start)) + (w-end (isearch-windows-end nil t)) + (w-L1 (with-selected-window (car isearch-windows) + (save-excursion (move-to-window-line 1) (point)))) + (w-L-1 (with-selected-window (car (last isearch-windows)) + (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)) @@ -2236,14 +2270,18 @@ (setq start isearch-point end (or isearch-other-end isearch-point))) (if above (progn - (goto-char start) + (select-window (car isearch-windows)) + (goto-char start) (recenter 0) - (when (>= isearch-point (window-end nil t)) - (goto-char isearch-point) + (when (>= isearch-point (isearch-windows-end nil t)) + (select-window (car (last isearch-windows))) + (goto-char isearch-point) (recenter -1))) + (select-window (car (last isearch-windows))) (goto-char end) (recenter -1) - (when (< isearch-point (window-start)) + (when (< isearch-point (isearch-windows-start)) + (select-window (car isearch-windows)) (goto-char isearch-point) (recenter 0)))) (goto-char isearch-point)) @@ -2390,6 +2428,7 @@ (isearch-ring-adjust1 advance) (if search-ring-update (progn + (isearch-call-message nil t) (isearch-search) (isearch-push-state) (isearch-update)) @@ -2462,6 +2501,13 @@ (defun isearch-message (&optional c-q-hack ellipsis) ;; Generate and print the message string. + + ;; N.B.: This function should always be called with point at the + ;; search point, because in certain (rare) circumstances, undesired + ;; scrolling can happen when point is elsewhere. These + ;; circumstances are when follow-mode is active, the search string + ;; spans two (or several) windows, and the message about to be + ;; displayed will cause the echo area to expand. (let ((cursor-in-echo-area ellipsis) (m isearch-message) (fail-pos (isearch-fail-pos t))) @@ -2630,9 +2676,6 @@ (defun isearch-search () ;; Do the search with the current search string. - (if isearch-message-function - (funcall isearch-message-function nil t) - (isearch-message nil t)) (if (and (eq isearch-case-fold-search t) search-upper-case) (setq isearch-case-fold-search (isearch-no-upper-case-p isearch-string isearch-regexp))) @@ -2936,8 +2979,9 @@ (defvar isearch-lazy-highlight-timer nil) (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) +(defvar isearch-lazy-highlight-windows nil) +(defvar isearch-lazy-highlight-windows-start 0) +(defvar isearch-lazy-highlight-windows-end 0) (defvar isearch-lazy-highlight-case-fold-search nil) (defvar isearch-lazy-highlight-regexp nil) (defvar isearch-lazy-highlight-lax-whitespace nil) @@ -2972,11 +3016,15 @@ search string to change or the window to scroll). It is also used by other Emacs features." (when (and (null executing-kbd-macro) - (sit-for 0) ;make sure (window-start) is credible + ;; make sure (window-start) is credible + (if (and (boundp 'follow-mode) follow-mode) + (progn (follow-adjust-window (selected-window)) + t) + (sit-for 0)) (or (not (equal isearch-string isearch-lazy-highlight-last-string)) - (not (eq (selected-window) - isearch-lazy-highlight-window)) + (not (memq (selected-window) + isearch-lazy-highlight-windows)) (not (eq isearch-lazy-highlight-case-fold-search isearch-case-fold-search)) (not (eq isearch-lazy-highlight-regexp @@ -2987,10 +3035,10 @@ isearch-lax-whitespace)) (not (eq isearch-lazy-highlight-regexp-lax-whitespace isearch-regexp-lax-whitespace)) - (not (= (window-start) - isearch-lazy-highlight-window-start)) - (not (= (window-end) ; Window may have been split/joined. - isearch-lazy-highlight-window-end)) + (not (= (isearch-windows-start isearch-lazy-highlight-windows) + isearch-lazy-highlight-windows-start)) + (not (= (isearch-windows-end isearch-lazy-highlight-windows) ; Window may have been split/joined. + isearch-lazy-highlight-windows-end)) (not (eq isearch-forward isearch-lazy-highlight-forward)) ;; In case we are recovering from an error. @@ -3005,8 +3053,14 @@ (setq isearch-lazy-highlight-start-limit beg isearch-lazy-highlight-end-limit end) (setq isearch-lazy-highlight-window (selected-window) - isearch-lazy-highlight-window-start (window-start) - isearch-lazy-highlight-window-end (window-end) + isearch-lazy-highlight-windows + (if (and (boundp 'follow-mode) follow-mode) + (follow-all-followers) + (list (selected-window))) + isearch-lazy-highlight-windows-start + (isearch-windows-start isearch-lazy-highlight-windows) + isearch-lazy-highlight-windows-end + (isearch-windows-end isearch-lazy-highlight-windows) ;; Start lazy-highlighting at the beginning of the found ;; match (`isearch-other-end'). If no match, use point. ;; One of the next two variables (depending on search direction) @@ -3048,11 +3102,11 @@ (min (or isearch-lazy-highlight-end-limit (point-max)) (if isearch-lazy-highlight-wrapped isearch-lazy-highlight-start - (window-end))) + isearch-lazy-highlight-windows-end)) (max (or isearch-lazy-highlight-start-limit (point-min)) (if isearch-lazy-highlight-wrapped isearch-lazy-highlight-end - (window-start)))))) + isearch-lazy-highlight-windows-start))))) ;; Use a loop like in `isearch-search'. (while retry (setq success (isearch-search-string @@ -3068,6 +3122,24 @@ success) (error nil))) +(defun isearch-lazy-highlight-put-overlays (mb me) + "Put a highlighting overlay on the buffer for each pertinent window. + +An overlay is put over the positions (MB ME) in each window in +`isearch-lazy-highlight-windows' which (at least partially) contains them." + ;; non-zero-length match + (mapc (lambda (w) + (if (and (< mb (window-end w)) + (> me (window-start w))) + (let ((ov (make-overlay mb me))) + (push ov isearch-lazy-highlight-overlays) + ;; 1000 is higher than ediff's 100+, + ;; but lower than isearch main overlay's 1001 + (overlay-put ov 'priority 1000) + (overlay-put ov 'face lazy-highlight-face) + (overlay-put ov 'window w)))) + isearch-lazy-highlight-windows)) + (defun isearch-lazy-highlight-update () "Update highlighting of other matches for current search." (let ((max lazy-highlight-max-at-a-time) @@ -3096,23 +3168,15 @@ (if isearch-lazy-highlight-forward (if (= mb (if isearch-lazy-highlight-wrapped isearch-lazy-highlight-start - (window-end))) + isearch-lazy-highlight-windows-end)) (setq found nil) (forward-char 1)) (if (= mb (if isearch-lazy-highlight-wrapped isearch-lazy-highlight-end - (window-start))) + isearch-lazy-highlight-windows-start)) (setq found nil) (forward-char -1))) - - ;; non-zero-length match - (let ((ov (make-overlay mb me))) - (push ov isearch-lazy-highlight-overlays) - ;; 1000 is higher than ediff's 100+, - ;; but lower than isearch main overlay's 1001 - (overlay-put ov 'priority 1000) - (overlay-put ov 'face lazy-highlight-face) - (overlay-put ov 'window (selected-window)))) + (isearch-lazy-highlight-put-overlays mb me)) ;; Remember the current position of point for ;; the next call of `isearch-lazy-highlight-update' ;; when `lazy-highlight-max-at-a-time' is too small. @@ -3128,12 +3192,12 @@ (setq isearch-lazy-highlight-wrapped t) (if isearch-lazy-highlight-forward (progn - (setq isearch-lazy-highlight-end (window-start)) + (setq isearch-lazy-highlight-end isearch-lazy-highlight-windows-start) (goto-char (max (or isearch-lazy-highlight-start-limit (point-min)) - (window-start)))) - (setq isearch-lazy-highlight-start (window-end)) + isearch-lazy-highlight-windows-start))) + (setq isearch-lazy-highlight-start isearch-lazy-highlight-windows-end) (goto-char (min (or isearch-lazy-highlight-end-limit (point-max)) - (window-end)))))))) + isearch-lazy-highlight-windows-end))))))) (unless nomore (setq isearch-lazy-highlight-timer (run-at-time lazy-highlight-interval nil @@ -3151,6 +3215,7 @@ (setq isearch-string string isearch-message message isearch-case-fold-search case-fold) + (isearch-call-message nil t) (isearch-search) (isearch-update)) -- Alan Mackenzie (Nuremberg, Germany).