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