From: Juri Linkov <juri@linkov.net>
To: martin rudalics <rudalics@gmx.at>
Cc: 69993@debbugs.gnu.org
Subject: bug#69993: Wrap window buffers while cycling
Date: Wed, 27 Mar 2024 09:20:32 +0200 [thread overview]
Message-ID: <86h6gs2lk7.fsf@mail.linkov.net> (raw)
In-Reply-To: <c2258795-4352-46af-85d3-0a4f1ba3d261@gmx.at> (martin rudalics's message of "Tue, 26 Mar 2024 10:56:24 +0100")
[-- Attachment #1: Type: text/plain, Size: 1974 bytes --]
>> There is a remaining problem, and I can't find a way to fix it.
>> When a buffer already appeared in the window before,
>> then switching to that buffer with e.g. 'C-x b'
>> moves it to the end of the list. Technically
>> this means that window-next-buffers is set to nil.
>
> I'm not sure I understand. IIRC 'window-next-buffers' always returns
> nil unless you invoked 'switch-to-prev-buffer' before. It serves to
> "navigate" a window's buffer list, in particular, to "undo" preceding
> 'previous-buffer' calls when overshooting. 'switch-to-buffer' is not
> part of such a scenario.
A new option should always keep the fixed order, even when users use C-x b
to visit a buffer that appeared in the window before.
The problem is that there is no function that is called after
set-window-buffer to reset the order of prev/next-buffers.
set-window-buffer works that way that before changing the window buffer
it calls record-window-buffer. But record-window-buffer has
no information about new-buffer. So it can't reorder prev/next-buffers
based on new-buffer that will be displayed in this window.
Then later set-window-buffer sets window's buffer,
but after that it doesn't call any function like
record-window-buffer that could reorder prev/next-buffers.
Then maybe possible to add such reordering after calling
set-window-buffer? I mean such places as after calling
set-window-buffer in window--display-buffer, and after calling
set-window-buffer in switch-to-buffer.
>> However, I can't find code that does this. Could you help
>> to find it? I already found one occurrence of
>> (set-window-next-buffers window nil) in record-window-buffer.
>> But after adding a condition with switch-to-prev-buffer-wrap
>> it still moves the switched buffer to the end.
>
> If you get me the patch you currently use and a scenario, I'll try.
Ok, here is the current patch that supports the fixed order
for 'C-x C-left' and 'C-x C-right' but still not for 'C-x b':
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: switch-to-prev-buffer-wrap.patch --]
[-- Type: text/x-diff, Size: 10723 bytes --]
diff --git a/lisp/window.el b/lisp/window.el
index df55a7ca673..5fc346571a8 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -4475,24 +4475,26 @@ push-window-buffer-onto-prev
(let* ((window (window-normalize-window window t))
(buffer (window-buffer window))
(w-list (window-prev-buffers window))
- (entry (assq buffer w-list)))
- (when entry
- (setq w-list (assq-delete-all buffer w-list)))
- (let ((start (window-start window))
- (point (window-point window)))
- (setq entry
- (cons buffer
- (with-current-buffer buffer
- (if entry
- ;; We have an entry, update marker positions.
- (list (set-marker (nth 1 entry) start)
- (set-marker (nth 2 entry) point))
- (list (copy-marker start)
- (copy-marker
- ;; Preserve window-point-insertion-type
- ;; (Bug#12855)
- point window-point-insertion-type))))))
- (set-window-prev-buffers window (cons entry w-list)))))
+ (entry (assq buffer w-list))
+ (start (window-start window))
+ (point (window-point window))
+ (start-point
+ (with-current-buffer buffer
+ (if entry
+ ;; We have an entry, update marker positions.
+ (list (set-marker (nth 1 entry) start)
+ (set-marker (nth 2 entry) point))
+ (list (copy-marker start)
+ (copy-marker
+ ;; Preserve window-point-insertion-type
+ ;; (Bug#12855)
+ point window-point-insertion-type))))))
+ (if (and switch-to-prev-buffer-wrap entry)
+ (setf (alist-get buffer w-list) start-point)
+ (when entry
+ (setq w-list (assq-delete-all buffer w-list)))
+ (set-window-prev-buffers
+ window (cons (cons buffer start-point) w-list)))))
(defun record-window-buffer (&optional window)
"Record WINDOW's buffer.
@@ -4501,7 +4503,9 @@ record-window-buffer
(buffer (window-buffer window)))
;; Reset WINDOW's next buffers. If needed, they are resurrected by
;; `switch-to-prev-buffer' and `switch-to-next-buffer'.
- (set-window-next-buffers window nil)
+ (unless (and switch-to-prev-buffer-wrap
+ (assq buffer (window-prev-buffers window)))
+ (set-window-next-buffers window nil))
;; Don't record insignificant buffers.
(when (not (eq (aref (buffer-name buffer) 0) ?\s))
@@ -4542,6 +4546,16 @@ set-window-buffer-start-and-point
(when point
(set-window-point window point))))
+(defcustom switch-to-prev-buffer-wrap nil
+ "Wrap to the first/last window-local buffer while cycling.
+When t, wrap to the first/last buffer.
+When the value is `stop', stop at the first/last buffer."
+ :type '(choice (const :tag "Never wrap" nil)
+ (const :tag "Stop at window-local buffers" stop)
+ (const :tag "Wrap to window-local buffers" t))
+ :version "30.1"
+ :group 'windows)
+
(defcustom switch-to-visible-buffer t
"If non-nil, allow switching to an already visible buffer.
If this variable is non-nil, `switch-to-prev-buffer' and
@@ -4676,7 +4690,7 @@ switch-to-prev-buffer
((or switch-to-prev-buffer-skip
(not switch-to-visible-buffer))
frame)))
- entry new-buffer killed-buffers skipped)
+ entry new-buffer killed-buffers skipped wrapped)
(when (window-minibuffer-p window)
;; Don't switch in minibuffer window.
(unless (setq window (minibuffer-selected-window))
@@ -4710,8 +4724,8 @@ switch-to-prev-buffer
;; 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
+ ;; this step for side windows or when wrapping.
+ (unless (or window-side switch-to-prev-buffer-wrap)
(dolist (buffer (if bury-or-kill
(buffer-list frame)
(nreverse (buffer-list frame))))
@@ -4729,7 +4743,9 @@ switch-to-prev-buffer
(set-window-buffer-start-and-point window new-buffer)
(throw 'found t)))))
- (unless bury-or-kill
+ (when (eq switch-to-prev-buffer-wrap 'stop)
+ (setq wrapped 'stop))
+ (unless (or bury-or-kill (eq switch-to-prev-buffer-wrap 'stop))
;; Scan reverted next buffers last (must not use nreverse
;; here!).
(dolist (buffer (reverse next-buffers))
@@ -4743,12 +4759,13 @@ switch-to-prev-buffer
(setq entry (assq buffer (window-prev-buffers window))))
(if (switch-to-prev-buffer-skip-p skip window buffer bury-or-kill)
(setq skipped (or skipped buffer))
- (setq new-buffer buffer)
+ (setq new-buffer buffer wrapped t)
(set-window-buffer-start-and-point
window new-buffer (nth 1 entry) (nth 2 entry))
(throw 'found t)))))
- (when (and skipped (not (functionp switch-to-prev-buffer-skip)))
+ (when (and skipped (not (functionp switch-to-prev-buffer-skip))
+ (not wrapped))
;; Show first skipped buffer, unless skip was a function.
(setq new-buffer skipped)
(set-window-buffer-start-and-point window new-buffer)))
@@ -4768,10 +4785,28 @@ switch-to-prev-buffer
;; 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))))
+ (if (not (and switch-to-prev-buffer-wrap wrapped))
+ ;; 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)))
+ (if (eq wrapped 'stop)
+ (setq new-buffer nil)
+ ;; Restore the right order of previous buffers.
+ (let ((prev-buffers (window-prev-buffers window)))
+ ;; Use the same sorting order as was in next-buffers
+ ;; with old-buffer at the bottom.
+ (setq prev-buffers
+ (sort prev-buffers
+ (lambda (a b)
+ (cond
+ ((eq (car a) old-buffer) nil)
+ ((eq (car b) old-buffer) t)
+ (t (< (length (memq (car a) next-buffers))
+ (length (memq (car b) next-buffers))))))))
+ (set-window-prev-buffers window prev-buffers)
+ ;; When record-window-buffer doesn't reset next-buffers.
+ (set-window-next-buffers window nil)))))
;; Remove killed buffers from WINDOW's previous and next buffers.
(when killed-buffers
@@ -4812,7 +4847,7 @@ switch-to-next-buffer
((or switch-to-prev-buffer-skip
(not switch-to-visible-buffer))
frame)))
- new-buffer entry killed-buffers skipped)
+ new-buffer entry killed-buffers skipped wrapped)
(when (window-minibuffer-p window)
;; Don't switch in minibuffer window.
(unless (setq window (minibuffer-selected-window))
@@ -4839,7 +4874,7 @@ switch-to-next-buffer
(throw 'found t))))
;; Scan the buffer list of WINDOW's frame next, skipping previous
;; buffers entries. Skip this step for side windows.
- (unless window-side
+ (unless (or window-side switch-to-prev-buffer-wrap)
(dolist (buffer (buffer-list frame))
(when (and (buffer-live-p buffer)
(not (eq buffer old-buffer))
@@ -4856,27 +4891,38 @@ switch-to-next-buffer
(throw 'found t)))))
;; Scan WINDOW's reverted previous buffers last (must not use
;; nreverse here!)
- (dolist (entry (reverse (window-prev-buffers window)))
- (when (and (not (eq new-buffer (car entry)))
- (not (eq old-buffer (car entry)))
- (setq new-buffer (car entry))
- (or (buffer-live-p new-buffer)
- (not (setq killed-buffers
- (cons new-buffer killed-buffers))))
- (or (null pred) (funcall pred new-buffer)))
- (if (switch-to-prev-buffer-skip-p skip window new-buffer)
- (setq skipped (or skipped new-buffer))
- (set-window-buffer-start-and-point
- window new-buffer (nth 1 entry) (nth 2 entry))
- (throw 'found t))))
+ (if (eq switch-to-prev-buffer-wrap 'stop)
+ (setq wrapped 'stop)
+ (dolist (entry (reverse (window-prev-buffers window)))
+ (when (and (not (eq new-buffer (car entry)))
+ (not (eq old-buffer (car entry)))
+ (setq new-buffer (car entry))
+ (or (buffer-live-p new-buffer)
+ (not (setq killed-buffers
+ (cons new-buffer killed-buffers))))
+ (or (null pred) (funcall pred new-buffer)))
+ (if (switch-to-prev-buffer-skip-p skip window new-buffer)
+ (setq skipped (or skipped new-buffer))
+ (setq wrapped t)
+ (set-window-buffer-start-and-point
+ window new-buffer (nth 1 entry) (nth 2 entry))
+ (throw 'found t)))))
- (when (and skipped (not (functionp switch-to-prev-buffer-skip)))
+ (when (and skipped (not (functionp switch-to-prev-buffer-skip))
+ (not wrapped))
;; Show first skipped buffer, unless skip was a function.
(setq new-buffer skipped)
(set-window-buffer-start-and-point window new-buffer)))
- ;; Remove `new-buffer' from and restore WINDOW's next buffers.
- (set-window-next-buffers window (delq new-buffer next-buffers))
+ (if (not (and switch-to-prev-buffer-wrap wrapped))
+ ;; Remove `new-buffer' from and restore WINDOW's next buffers.
+ (set-window-next-buffers window (delq new-buffer next-buffers))
+ (if (eq wrapped 'stop)
+ (setq new-buffer nil)
+ (let ((prev-buffers (window-prev-buffers window)))
+ (setq prev-buffers
+ (nreverse (delq new-buffer (mapcar #'car prev-buffers))))
+ (set-window-next-buffers window prev-buffers))))
;; Remove killed buffers from WINDOW's previous and next buffers.
(when killed-buffers
next prev parent reply other threads:[~2024-03-27 7:20 UTC|newest]
Thread overview: 45+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-03-25 7:42 bug#69993: Wrap window buffers while cycling Juri Linkov
2024-03-25 9:41 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-25 17:16 ` Juri Linkov
2024-03-26 9:56 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-27 7:20 ` Juri Linkov [this message]
2024-03-27 8:48 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28 7:54 ` Juri Linkov
2024-03-28 9:19 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-28 17:57 ` Juri Linkov
2024-03-29 8:45 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-29 16:35 ` Juri Linkov
2024-03-30 9:37 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-30 18:24 ` Juri Linkov
2024-03-31 8:32 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-02 6:37 ` Juri Linkov
2024-04-02 8:22 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-02 16:28 ` Juri Linkov
2024-04-03 8:24 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-03 17:45 ` Juri Linkov
2024-04-04 8:03 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-05 6:45 ` Juri Linkov
2024-04-05 9:08 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-05 16:32 ` Juri Linkov
2024-04-06 18:43 ` Juri Linkov
2024-04-07 8:23 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-09 6:35 ` Juri Linkov
2024-04-09 9:04 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-09 16:37 ` Juri Linkov
2024-04-10 8:46 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-10 17:45 ` Juri Linkov
2024-04-11 9:18 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-12 6:35 ` Juri Linkov
2024-04-12 8:37 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-12 16:23 ` Juri Linkov
2024-04-14 16:15 ` Juri Linkov
2024-04-16 6:38 ` Juri Linkov
2024-04-17 17:56 ` Juri Linkov
2024-04-24 16:39 ` Juri Linkov
2024-04-25 8:31 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-25 17:40 ` Juri Linkov
2024-04-02 16:34 ` Juri Linkov
2024-04-03 8:24 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-03 17:44 ` Juri Linkov
2024-04-04 6:22 ` Juri Linkov
2024-04-02 16:40 ` Juri Linkov
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
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=86h6gs2lk7.fsf@mail.linkov.net \
--to=juri@linkov.net \
--cc=69993@debbugs.gnu.org \
--cc=rudalics@gmx.at \
/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 public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).