From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Juri Linkov Newsgroups: gmane.emacs.bugs Subject: bug#69993: Wrap window buffers while cycling Date: Thu, 28 Mar 2024 09:54:31 +0200 Organization: LINKOV.NET Message-ID: <86cyreu78w.fsf@mail.linkov.net> References: <86h6gug41x.fsf@mail.linkov.net> <3419df35-1b96-4a64-8ed7-722a05c58742@gmx.at> <86le66ckhj.fsf@mail.linkov.net> <86h6gs2lk7.fsf@mail.linkov.net> <5c71ea64-0f97-4b90-af61-1156fe33f1ea@gmx.at> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="37913"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/30.0.50 (x86_64-pc-linux-gnu) Cc: 69993@debbugs.gnu.org To: martin rudalics Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Thu Mar 28 08:58:38 2024 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1rpkei-0009fl-Mg for geb-bug-gnu-emacs@m.gmane-mx.org; Thu, 28 Mar 2024 08:58:36 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rpkeG-00086j-7B; Thu, 28 Mar 2024 03:58:08 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rpkeD-00082a-3k for bug-gnu-emacs@gnu.org; Thu, 28 Mar 2024 03:58:05 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1rpkeC-0003D2-6p for bug-gnu-emacs@gnu.org; Thu, 28 Mar 2024 03:58:04 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1rpkeC-0004RJ-Ou for bug-gnu-emacs@gnu.org; Thu, 28 Mar 2024 03:58:04 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Juri Linkov Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 28 Mar 2024 07:58:04 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69993 X-GNU-PR-Package: emacs Original-Received: via spool by 69993-submit@debbugs.gnu.org id=B69993.171161264816900 (code B ref 69993); Thu, 28 Mar 2024 07:58:04 +0000 Original-Received: (at 69993) by debbugs.gnu.org; 28 Mar 2024 07:57:28 +0000 Original-Received: from localhost ([127.0.0.1]:38952 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rpkdb-0004OU-B2 for submit@debbugs.gnu.org; Thu, 28 Mar 2024 03:57:28 -0400 Original-Received: from relay1-d.mail.gandi.net ([217.70.183.193]:60331) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rpkdH-0004Ml-RH for 69993@debbugs.gnu.org; Thu, 28 Mar 2024 03:57:09 -0400 Original-Received: by mail.gandi.net (Postfix) with ESMTPSA id 640A5240009; Thu, 28 Mar 2024 07:57:00 +0000 (UTC) In-Reply-To: <5c71ea64-0f97-4b90-af61-1156fe33f1ea@gmx.at> (martin rudalics's message of "Wed, 27 Mar 2024 09:48:38 +0100") X-GND-Sasl: juri@linkov.net X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list 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-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:282186 Archived-At: --=-=-= Content-Type: text/plain >>> 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. > > What is the "fixed order"? When I use C-x b, that buffer becomes the > one most recently shown in that window. The fixed order is similar to how tabs work in web browsers. It would be unexpected when switching to a tab will always move it to the end of the tab line. This is why it's unexpected for C-x b to move tabs when tab-line-mode is used. >> 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. > > Then give 'record-window-buffer' a second argument - the new buffer to > be shown. I'm a bit reluctant to work in this area - the introduction > of 'push-window-buffer-onto-prev' has obfuscated the code considerably > for no apparent use (at least one that I could understand). Ok, let's add a second argument to 'record-window-buffer'. I'll do this in a separate feature request for a new option that will keep the fixed order for C-x b since it's quite different from the wrapping option. > I see no problems with it. After C-x b *foo* I want to return to *foo* > via 'previous-buffer' after switching to *bar* via a second C-x b. What > would you want to see instead? Maybe I still misunderstand you. Selecting a buffer via C-x b still uses the sorting order of buffers by the most-recently-used. So after C-x b *bar* you still can easily return to *foo* by C-x b RET. But the proposed change makes sense when using tab-line-mode where C-x b messes up buffer tabs. > I think 'switch-to-prev-buffer-wrap' already confuses things. Wrapping, > for me, means to wrap around like when navigating on a ring of buffers. > Whether this should include buffers never shown in the window before is > a different issue IMO. And whether C-x b should change the order is yet > another issue. So maybe we need three options instead of one... I can't imagine why anyone would need wrapping when C-x C-left will visit hundreds of buffers never shown in the window. So we need only two options: wrapping buffers shown in the window, and to keep the fixed order of C-x b. So I will create a new request for the fixed order of C-x b. And here is the final patch for wrapping: --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=switch-to-prev-buffer-wrap.patch diff --git a/lisp/window.el b/lisp/window.el index df55a7ca673..ff08b0bcfc9 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4542,6 +4546,22 @@ set-window-buffer-start-and-point (when point (set-window-point window point)))) +(defcustom switch-to-prev-buffer-wrap nil + "Wrap to the first or last window buffer while cycling. +The value t means wrapping around while cycling buffers appeared in the +window before. So when the commands that switch buffers in the selected +window `previous-buffer' and `next-buffer' reach the first or the last +buffer (these buffers are visible when using `tab-line-mode'), +then wrap around to another end of the list of previous/next buffers. + +When the value is `stop', stop at the first or last buffer +in the list of previous/next buffers, but don't wrap around." + :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 +4696,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 +4730,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 +4749,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 +4765,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 +4791,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 +4853,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 +4880,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 +4897,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 --=-=-=--