all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Anders Lindgren <andlind@gmail.com>
To: Dmitry Antipov <dmantipov@yandex.ru>
Cc: 15957@debbugs.gnu.org
Subject: bug#15957: 24.3.50; Follow mode scrolling broken on Emacs trunk
Date: Tue, 26 Nov 2013 12:01:13 +0100	[thread overview]
Message-ID: <CABr8ebYDu=bnAw64i7r4foepHPeSgZ=yNQtmxqKwv9TXD9zw0Q@mail.gmail.com> (raw)
In-Reply-To: <529440AD.6060505@yandex.ru>


[-- Attachment #1.1: Type: text/plain, Size: 2769 bytes --]

Hi Dmitry!

The problem only occurs under certain circumstances. `window-end' must
called from a post-command hook and the command that started it must be
bound to a key (not using M-x). Also, sometimes it seems to work correctly,
sometimes not, like every second time. (Take "must" with a grain of salt,
it might be possible to trigger the bug under other circumstances as well.)

I have attached a piece of lisp code which basically is the follow-mode
code plus log commands, so that you clearly can see the return value of
different functions, including the call to `windows-end'. I'm sure it would
be possible to cut down the code to a minimal, now when we know that it's
`window-end' that returns the incorrect value. (The logs I referred to in
the previous mails were generated by this package.) I'm not familiar with
the bug tracking system, but feel free to add this code to it.

In case you are not familiar with follow-mode, it is a package that creates
the illusion that you have one large window spread out over several
side-by-side windows, you can move between the windows using the normal
cursor keys, and when you scroll one window, the others follow (hence the
name "Follow mode"). Technically, this is accomplished by a using a
post-command hook that performs all the repositioning, plus a handful of
new function performing operations that didn't exist before (like scrolling
X pages, where X equal the number of windows the buffer is shown in).

Do you think that you have everything that you need to find and fix the
bug, or is there anything else that I can help you with?

Sincerely,
    Anders Lindgren


On Tue, Nov 26, 2013 at 7:33 AM, Dmitry Antipov <dmantipov@yandex.ru> wrote:

> On 11/25/2013 08:42 PM, martin rudalics wrote:
>
>> Forwarding Anders Lindgren's mail to 15957@debbugs.gnu.org and Dmitry
>> Antipov.
>>
>>  Hi!
>>>
>>> I believe that I have found the problem. In `follow-calc-win-end', there
>>> is
>>> a call to `(window-end win t)'. The `t' means "compute the up-to-date
>>> position
>>> if it isn't already recorded." The return value from this function is
>>> clearly incorrect. The fact that the wrong window is selected is simply a
>>> consequence of this.
>>>
>>> In the older Emacs (up to bzr revision 113752), this seems to work
>>> correctly. However, in the newer (starting from bzt revision 113753) this
>>> is broken.
>>>
>>
> Hm...I just add debug printf() at the end of Fwindow_end, and see an
> identical output for 24.3 and r115239 for this simple example:
>
> ./src/emacs -Q -font 6x10 -geometry 200x65
> C-h t
> M-x follow-delete-other-windows-and-split
> C-c . C-v
>
> So if someone has a elisp code that clearly shows that window-end is
> broken,
> please attach it to this bug at least.
>
> Dmitry
>
>
>

[-- Attachment #1.2: Type: text/html, Size: 3922 bytes --]

[-- Attachment #2: check-follow-scroll-bug.el --]
[-- Type: application/octet-stream, Size: 9076 bytes --]

;; Quickn'dirty follow-mode debug log package.

(require 'follow)

(defun my-wrap-follow-scroll-up (&optional arg)
  (interactive "P")
  (setq my-f1 (selected-window))
  (follow-scroll-up arg)
  (setq my-f2 (selected-window)))

;; The problem obly manifests itself when bound to a key.
(global-set-key (kbd "C-z") 'my-wrap-follow-scroll-up)

(defvar my-post-log nil)
(defvar my-post-do-log nil)

(defun check-follow-log (what &rest extra)
  (if my-post-do-log
      (setq my-post-log (cons (cons what (cons (selected-window) extra)) my-post-log))))

(defun check-follow-report-log ()
  (interactive)
  (with-output-to-temp-buffer "*Follow log*"
    (dolist (x (reverse my-post-log))
      (princ x)
      (terpri))))

;; Modified to support logging.
(defun follow-calc-win-end (&optional win)
  "Calculate the end position for window WIN.
Return (END-POS END-OF-BUFFER).

Actually, the position returned is the start of the line after
the last fully-visible line in WIN.  If WIN is nil, the selected
window is used."
  (check-follow-log "follow-calc-win-end")
  (let ((win (or win (selected-window))))
    (let ((edges (window-inside-pixel-edges win)))
      (check-follow-log "  edges" edges)
      (let ((ht (- (nth 3 edges) (nth 1 edges))))
	(check-follow-log "  ht" ht)
	(let ((last-line-pos (posn-point (posn-at-x-y 0 (1- ht) win))))
	  (check-follow-log "  last-line-pos" last-line-pos)
	  (if (pos-visible-in-window-p last-line-pos win)
	      (progn
		(check-follow-log "  Pos visible")
		(let ((end (window-end win t)))
		  (check-follow-log "  end" end)
		  (list end (= end (point-max)))))
	    (check-follow-log "  Pos not visible")
	    (list last-line-pos nil)))))))

;; Modified to support logging.
(defun follow-post-command-hook ()
  "Ensure that the windows in Follow mode are adjacent after each command."
  (unless (input-pending-p)
    (let ((follow-inside-post-command-hook t)
	  (win (selected-window)))
      ;; Work in the selected window, not in the current buffer.
      (with-current-buffer (window-buffer win)
	(unless (and (symbolp this-command)
		     (get this-command 'follow-mode-use-cache))
	  (setq follow-windows-start-end-cache nil)))
      (setq my-post-do-log (eq this-command 'my-wrap-follow-scroll-up))
      (if my-post-do-log
	  (setq my-post-log '()))
      (check-follow-adjust-window win (point)))))

;; Special version of `follow-adjust-window'.
(defun check-follow-adjust-window (win dest)
  ;; Adjust the window WIN and its followers.
  (check-follow-log "Start")
  (with-current-buffer (window-buffer win)
    (check-follow-log "Start (inside w-c-b)")
    (when (and follow-mode
	       (not (window-minibuffer-p win)))
;      (check-follow-log "window-start" (window-start (car (follow-all-followers))))
;      (check-follow-log "window-end" (window-end (car (follow-all-followers))))
;      (check-follow-log "follow-calc-win-end" (follow-calc-win-end (car (follow-all-followers))))
;      (check-follow-log "follow-windows-start-end" (follow-windows-start-end (follow-all-followers)))
      (let* ((windows (follow-all-followers win))
	     (win-start-end (progn
			      (follow-update-window-start (car windows))
			      (follow-windows-start-end windows)))
	     (aligned (follow-windows-aligned-p win-start-end))
	     (visible (follow-pos-visible dest win win-start-end))
	     selected-window-up-to-date)
	(check-follow-log "Visible" visible)
	(check-follow-log "win-start-end" win-start-end)
	(unless (and aligned visible)
	  (setq follow-windows-start-end-cache nil))

	(check-follow-log "Before internal-forece-redisplay check")
	;; Select a window to display point.
	(unless follow-internal-force-redisplay
	  (if (eq dest (point-max))
	      ;; Be careful at point-max: the display can be aligned
	      ;; while DEST can be visible in several windows.
	      (cond
	       ;; Select the current window, but only when the display
	       ;; is correct. (When inserting characters in a tail
	       ;; window, the display is not correct, as they are
	       ;; shown twice.)
	       ;;
	       ;; Never stick to the current window after a deletion.
	       ;; Otherwise, when typing `DEL' in a window showing
	       ;; only the end of the file, a character would be
	       ;; removed from the window above, which is very
	       ;; unintuitive.
	       ((and visible
		     aligned
		     (not (memq this-command
				'(backward-delete-char
				  delete-backward-char
				  backward-delete-char-untabify
				  kill-region))))
		(check-follow-log "Case 1")
		(follow-debug-message "Max: same"))
	       ;; If the end is visible, and the window doesn't
	       ;; seems like it just has been moved, select it.
	       ((follow-select-if-end-visible win-start-end)
		(check-follow-log "Case 2")
		(follow-debug-message "Max: end visible")
		(setq visible t aligned nil)
		(goto-char dest))
	       ;; Just show the end...
	       (t
		(check-follow-log "Case 3")
		(follow-debug-message "Max: default")
		(select-window (car (last windows)))
		(goto-char dest)
		(setq visible nil aligned nil)))

	    ;; We're not at the end, here life is much simpler.
	    (cond
	     ;; This is the normal case!
	     ;; It should be optimized for speed.
	     ((and visible aligned)
	      (check-follow-log "Case 4")
	      (follow-debug-message "same"))
	     ;; Pick a position in any window.  If the display is ok,
	     ;; this picks the `correct' window.
	     ((follow-select-if-visible dest win-start-end)
	      (check-follow-log "Case 5")
	      (follow-debug-message "visible")
	      (goto-char dest)
	      ;; Perform redisplay, in case line is partially visible.
	      (setq visible nil))
	     ;; Not visible anywhere else, lets pick this one.
	     (visible
	      (check-follow-log "Case 6")
	      (follow-debug-message "visible in selected."))
	     ;; If DEST is before the first window start, select the
	     ;; first window.
	     ((< dest (nth 1 (car win-start-end)))
	      (check-follow-log "Case 7")
	      (follow-debug-message "before first")
	      (select-window (car windows))
	      (goto-char dest)
	      (setq visible nil aligned nil))
	     ;; If we can position the cursor without moving the first
	     ;; window, do it. This is the case that catches `RET' at
	     ;; the bottom of a window.
	     ((follow-select-if-visible-from-first dest windows)
	      (check-follow-log "Case 7")
	      (follow-debug-message "Below first")
	      (setq visible t aligned t))
	     ;; None of the above.  Stick to the selected window.
	     (t
	      (check-follow-log "Case 8")
	      (follow-debug-message "None")
	      (setq visible nil aligned nil))))

	  (check-follow-log "Before selected window check")
	  ;; If a new window was selected, make sure that the old is
	  ;; not scrolled when the point is outside the window.
	  (unless (eq win (selected-window))
	    (let ((p (window-point win)))
	      (set-window-start win (window-start win) nil)
	      (set-window-point win p))))

	(unless visible
	  ;; If point may not be visible in the selected window,
	  ;; perform a redisplay; this ensures scrolling.
	  (let ((opoint (point)))
	    (redisplay)
	    ;; If this `redisplay' moved point, we got clobbered by a
	    ;; previous call to `set-window-start'.  Try again.
	    (when (/= (point) opoint)
	      (goto-char opoint)
	      (redisplay)))

	  (setq selected-window-up-to-date t)
	  (check-follow-log "AAA")

	  (follow-avoid-tail-recenter)
	  (setq win-start-end (follow-windows-start-end windows)
		follow-windows-start-end-cache nil
		aligned nil))

	;; Now redraw the windows around the selected window.
	(check-follow-log "BBB")
	(unless (and (not follow-internal-force-redisplay)
		     (or aligned
			 (follow-windows-aligned-p win-start-end))
		     (follow-point-visible-all-windows-p win-start-end))
	  (setq follow-internal-force-redisplay nil)
	  (follow-redisplay windows (selected-window)
			    selected-window-up-to-date)
	  (setq win-start-end (follow-windows-start-end windows)
		follow-windows-start-end-cache nil)
	  ;; The point can ends up in another window when DEST is at
	  ;; the beginning of the buffer and the selected window is
	  ;; not the first.  It can also happen when long lines are
	  ;; used and there is a big difference between the width of
	  ;; the windows.  (When scrolling one line in a wide window
	  ;; which will cause a move larger that an entire small
	  ;; window.)
	  (unless (follow-pos-visible dest win win-start-end)
	    (follow-select-if-visible dest win-start-end)
	    (goto-char dest)))

	(check-follow-log "CCC")

	;; If the region is visible, make it look good when spanning
	;; multiple windows.

	;; FIXME: Why not use `use-region-p' here?
	(when (region-active-p)
	  (follow-maximize-region
	   (selected-window) windows win-start-end)))

      (check-follow-log "DDD")
      ;; Whether or not the buffer was in follow mode, update windows
      ;; displaying the tail so that Emacs won't recenter them.
      (follow-avoid-tail-recenter))
    (check-follow-log "End (inside w-c-b)"))
  (check-follow-log "End"))

  reply	other threads:[~2013-11-26 11:01 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-11-22 22:23 bug#15957: 24.3.50; Follow mode scrolling broken on Emacs trunk Anders Lindgren
2013-11-23 11:58 ` martin rudalics
2013-11-23 22:01   ` Anders Lindgren
2013-11-24 10:10     ` martin rudalics
2013-11-25  9:19       ` Anders Lindgren
     [not found]         ` <CABr8ebbnxDgVF-+pdk955Ux-SQuZDONw=OhHOVUdO9d=xsTcZg@mail.gmail.com>
2013-11-25 16:42           ` martin rudalics
2013-11-26  6:33             ` Dmitry Antipov
2013-11-26 11:01               ` Anders Lindgren [this message]
2013-11-26 14:12                 ` martin rudalics
2013-11-26 14:25                   ` Anders Lindgren
2013-11-26 16:19                     ` martin rudalics
2013-11-26 17:03                       ` Anders Lindgren
2013-11-26 17:21                         ` martin rudalics
2013-11-27  7:48                         ` martin rudalics
2014-01-04 14:12                           ` martin rudalics

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CABr8ebYDu=bnAw64i7r4foepHPeSgZ=yNQtmxqKwv9TXD9zw0Q@mail.gmail.com' \
    --to=andlind@gmail.com \
    --cc=15957@debbugs.gnu.org \
    --cc=dmantipov@yandex.ru \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.