GNU bug report logs - #32825
27.0.50; Deterministic window management

Previous Next

Package: emacs;

Reported by: Juri Linkov <juri <at> linkov.net>

Date: Mon, 24 Sep 2018 19:15:02 UTC

Severity: minor

Tags: moreinfo

Found in version 27.0.50

Fixed in version 29.0.50

Done: Juri Linkov <juri <at> linkov.net>

Bug is archived. No further changes may be made.

Full log


Message #263 received at 32825 <at> debbugs.gnu.org (full text, mbox):

From: martin rudalics <rudalics <at> gmx.at>
To: Juri Linkov <juri <at> linkov.net>
Cc: Michael Heerdegen <michael_heerdegen <at> web.de>, 32825 <at> debbugs.gnu.org
Subject: Re: bug#32825: 27.0.50; Deterministic window management
Date: Tue, 13 Nov 2018 10:12:23 +0100
>> Maybe better to represent a list of prev/next buffers as a tree?
>> Then inserting a new buffer inside it will create a leaf that
>> can be ignored for navigation.  An analogy of this is undo-tree.
>
> More precisely, navigating in the tree trunk and ignoring branches.

Note that 'display-buffer-in-previous-window' doesn't care about the
order in which a buffer appears in a window's previous buffers list.
It only wants to know whether it's there and traversing a tree for
that purpose looks like overkill.

OTOH we would still have to decide what to do with a window's buffer
when displaying another buffer in that window via 'display-buffer':
Make it the first buffer we would want to switch to in the next
'switch-to-prev-buffer' call or make it insignificant (a leaf in your
parlance).

I currently tend to favor a solution like the below (we could also use
the 'buffer-predicate' frame parameter for that purpose but that would
affect 'other-buffer' as well, something I'd like to avoid).

The solution below should behave as follows: Assume TAGS is shown in
the selected window.  Then doing 'switch-to-prev-buffer' followed by
'switch-to-next-buffer' there should get me back to TAGS.  Doing
‘display-buffer’ there followed by 'switch-to-prev-buffer' should get
me to the buffer shown there before TAGS.

Then I could set the default of 'debugger-bury-or-kill' to 'append'
and *Backtrace* would not show up accidentally via
'switch-to-prev-buffer' but still remain on the list of previous
buffers so 'display-buffer-in-previous-window' will find it.  WDYT?

martin


(defcustom switch-to-prev-buffer-skip-regexp
  "\\*Backtrace\\*\\|TAGS"
  "Regexp matching names of buffer `switch-to-prev-buffer' should skip.
The default matches the *Backtrace* and the TAGS buffers."
  :type '(repeat (regexp :format "%v"))
  :group 'windows)

(defun switch-to-prev-buffer (&optional window bury-or-kill)
  "In WINDOW switch to previous buffer.
WINDOW must be a live window and defaults to the selected one.
Return the buffer switched to, nil if no suitable buffer could be
found.

Optional argument BURY-OR-KILL non-nil means the buffer currently
shown in WINDOW is about to be buried or killed and consequently
shall not be switched to in future invocations of this command.

As a special case, if BURY-OR-KILL equals `append', this means to
move the buffer to the end of WINDOW's previous buffers list so a
future invocation of `switch-to-prev-buffer' less likely switches
to it."
  (interactive)
  (let* ((window (window-normalize-window window t))
	 (frame (window-frame window))
         (window-side (window-parameter window 'window-side))
	 (old-buffer (window-buffer window))
	 ;; Save this since it's destroyed by `set-window-buffer'.
	 (next-buffers (window-next-buffers window))
         (pred (frame-parameter frame 'buffer-predicate))
	 entry new-buffer killed-buffers visible)
    (when (window-minibuffer-p window)
      ;; Don't switch in minibuffer window.
      (unless (setq window (minibuffer-selected-window))
	(error "Window %s is a minibuffer window" window)))

    (unless (memq (window-dedicated-p window) '(nil side))
      ;; Don't switch in dedicated window.
      (error "Window %s is dedicated to buffer %s" window old-buffer))

    (catch 'found
      ;; Scan WINDOW's previous buffers first, skipping entries of next
      ;; buffers.
      (dolist (entry (window-prev-buffers window))
	(when (and (setq new-buffer (car entry))
		   (or (buffer-live-p new-buffer)
		       (not (setq killed-buffers
				  (cons new-buffer killed-buffers))))
		   (not (eq new-buffer old-buffer))
                   (not (string-match-p
                         switch-to-prev-buffer-skip-regexp
                         (buffer-name new-buffer)))
                   (or (null pred) (funcall pred new-buffer))
		   ;; When BURY-OR-KILL is nil, avoid switching to a
		   ;; buffer in WINDOW's next buffers list.
		   (or bury-or-kill (not (memq new-buffer next-buffers))))
	  (if (and (not switch-to-visible-buffer)
		   (get-buffer-window new-buffer frame))
	      ;; Try to avoid showing a buffer visible in some other
	      ;; window.
	      (setq visible new-buffer)
	    (set-window-buffer-start-and-point
	     window new-buffer (nth 1 entry) (nth 2 entry))
	    (throw 'found t))))
      ;; Scan reverted buffer list of WINDOW's frame next, skipping
      ;; entries of next buffers.  Note that when we bury or kill a
      ;; buffer we don't reverse the global buffer list to avoid showing
      ;; a buried buffer instead.  Otherwise, we must reverse the global
      ;; buffer list in order to make sure that switching to the
      ;; previous/next buffer traverse it in opposite directions.  Skip
      ;; this step for side windows.
      (unless window-side
        (dolist (buffer (if bury-or-kill
                            (buffer-list frame)
                          (nreverse (buffer-list frame))))
          (when (and (buffer-live-p buffer)
                     (not (eq buffer old-buffer))
                     (not (string-match-p
                           switch-to-prev-buffer-skip-regexp
                           (buffer-name new-buffer)))
                     (or (null pred) (funcall pred buffer))
                     (not (eq (aref (buffer-name buffer) 0) ?\s))
                     ;; Don't show a buffer shown in a side window before.
                     (not (buffer-local-value 'window--sides-shown buffer))
                     (or bury-or-kill (not (memq buffer next-buffers))))
            (if (and (not switch-to-visible-buffer)
                     (get-buffer-window buffer frame))
                ;; Try to avoid showing a buffer visible in some other window.
                (unless visible
                  (setq visible buffer))
              (setq new-buffer buffer)
              (set-window-buffer-start-and-point window new-buffer)
              (throw 'found t)))))
      (unless bury-or-kill
	;; Scan reverted next buffers last (must not use nreverse
	;; here!).
	(dolist (buffer (reverse next-buffers))
	  ;; Actually, buffer _must_ be live here since otherwise it
	  ;; would have been caught in the scan of previous buffers.
	  (when (and (or (buffer-live-p buffer)
			 (not (setq killed-buffers
				    (cons buffer killed-buffers))))
		     (not (eq buffer old-buffer))
                     (not (string-match-p
                           switch-to-prev-buffer-skip-regexp
                           (buffer-name new-buffer)))
                     (or (null pred) (funcall pred buffer))
		     (setq entry (assq buffer (window-prev-buffers window))))
	    (setq new-buffer buffer)
	    (set-window-buffer-start-and-point
	     window new-buffer (nth 1 entry) (nth 2 entry))
	    (throw 'found t))))

      ;; Show a buffer visible in another window.
      (when visible
	(setq new-buffer visible)
	(set-window-buffer-start-and-point window new-buffer)))

    (if bury-or-kill
	(let ((entry (and (eq bury-or-kill 'append)
			  (assq old-buffer (window-prev-buffers window)))))
	  ;; Remove `old-buffer' from WINDOW's previous and (restored list
	  ;; of) next buffers.
	  (set-window-prev-buffers
	   window (assq-delete-all old-buffer (window-prev-buffers window)))
	  (set-window-next-buffers window (delq old-buffer next-buffers))
	  (when entry
	    ;; Append old-buffer's entry to list of WINDOW's previous
	    ;; buffers so it's less likely to get switched to soon but
	    ;; `display-buffer-in-previous-window' can nevertheless find
	    ;; it.
	    (set-window-prev-buffers
	     window (append (window-prev-buffers window) (list entry)))))
      ;; Move `old-buffer' to head of WINDOW's restored list of next
      ;; buffers.
      (set-window-next-buffers
       window (cons old-buffer (delq old-buffer next-buffers))))

    ;; Remove killed buffers from WINDOW's previous and next buffers.
    (when killed-buffers
      (dolist (buffer killed-buffers)
	(set-window-prev-buffers
	 window (assq-delete-all buffer (window-prev-buffers window)))
	(set-window-next-buffers
	 window (delq buffer (window-next-buffers window)))))

    ;; Return new-buffer.
    new-buffer))





This bug report was last modified 3 years and 24 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.