]> git.eshelyaron.com Git - emacs.git/commitdiff
For minibuffer windows record minibuffers only (Bug#72487)
authorMartin Rudalics <rudalics@gmx.at>
Wed, 11 Sep 2024 08:36:14 +0000 (10:36 +0200)
committerEshel Yaron <me@eshelyaron.com>
Sat, 14 Sep 2024 20:21:58 +0000 (22:21 +0200)
* src/minibuf.c (zip_minibuffer_stacks): Use wset type
functions.  Call 'record-window-buffer' instead of
'push-window-buffer-onto-prev' to handle all sorts of buffers
shown in minibuffer windows in a uniform way.
(read_minibuf): Call 'record-window-buffer' instead of
'push-window-buffer-onto-prev' for same reason as previous.
* lisp/calculator.el (calculator-update-display)
(calculator-save-and-quit): Make sure calculator buffer is live
before operating on it.
* lisp/window.el (record-window-buffer): Handle case where
WINDOW is a minibuffer window: Unconditionally remove WINDOW's
buffer from WINDOW's list of previous buffers and push it if
and only if it is a live minibuffer (Bug#72487).  Do not run
'buffer-list-update-hook' if WINDOW is a minibuffer window.
(push-window-buffer-onto-prev): Make it an alias of
'record-window-buffer' so it will run the latter's checks.
(replace-buffer-in-windows): Handle minibuffer windows and
rewrite doc-string accordingly.
* doc/lispref/windows.texi (Buffers and Windows): Explain
handling of minibuffer windows in 'replace-buffer-in-windows'.

(cherry picked from commit fc3a7f45292b9a7be95fdefd24fedb7e8f564d1c)

doc/lispref/windows.texi
lisp/window.el
src/minibuf.c
src/window.c

index 656a44dfcbf319577f01cdb5adeba52f40acd7b1..541c91ddae25d1c9c070d9c91c2f43f672e4ab6c 100644 (file)
@@ -2353,6 +2353,12 @@ buffers (@pxref{Window History}) of all windows (including dead windows
 that are only referenced by window configurations) and remove any
 @code{quit-restore} or @code{quit-restore-prev} parameters
 (@pxref{Window Parameters}) referencing that buffer.
+
+This function does not replace the buffer specified by
+@var{buffer-or-name} in any minibuffer window showing it, nor does it
+delete minibuffer windows or minibuffer frames.  It removes, however,
+that buffer from the lists of previous and next buffers of all
+minibuffer windows.
 @end deffn
 
 By default, @code{replace-buffer-in-windows} deletes only windows
index cc7a6a00eb5a36391527714998a7ccc3aeeaaabe..3966fb1073fb8ecdf00c2cf52411cb9b8c007eb4 100644 (file)
@@ -4468,75 +4468,86 @@ This may be a useful alternative binding for \\[delete-other-windows]
 
 ;;; Windows and buffers.
 
-;; `prev-buffers' and `next-buffers' are two reserved window slots used
+;; 'prev-buffers' and 'next-buffers' are two reserved window slots used
 ;; for (1) determining which buffer to show in the window when its
 ;; buffer shall be buried or killed and (2) which buffer to show for
-;; `switch-to-prev-buffer' and `switch-to-next-buffer'.
+;; 'switch-to-prev-buffer' and 'switch-to-next-buffer'.
 
-;; `prev-buffers' consists of <buffer, window-start, window-point>
+;; 'prev-buffers' consists of <buffer, window-start, window-point>
 ;; triples.  The entries on this list are ordered by the time their
 ;; buffer has been removed from the window, the most recently removed
 ;; buffer's entry being first.  The window-start and window-point
-;; components are `window-start' and `window-point' at the time the
+;; components are 'window-start' and 'window-point' at the time the
 ;; buffer was removed from the window which implies that the entry must
-;; be added when `set-window-buffer' removes the buffer from the window.
+;; be added when 'set-window-buffer' removes the buffer from the window.
 
-;; `next-buffers' is the list of buffers that have been replaced
-;; recently by `switch-to-prev-buffer'.  These buffers are the least
-;; preferred candidates of `switch-to-prev-buffer' and the preferred
-;; candidates of `switch-to-next-buffer' to switch to.  This list is
+;; 'next-buffers' is the list of buffers that have been replaced
+;; recently by 'switch-to-prev-buffer'.  These buffers are the least
+;; preferred candidates of 'switch-to-prev-buffer' and the preferred
+;; candidates of 'switch-to-next-buffer' to switch to.  This list is
 ;; reset to nil by any action changing the window's buffer with the
-;; exception of `switch-to-prev-buffer' and `switch-to-next-buffer'.
-;; `switch-to-prev-buffer' pushes the buffer it just replaced on it,
-;; `switch-to-next-buffer' pops the last pushed buffer from it.
-
-;; Both `prev-buffers' and `next-buffers' may reference killed buffers
-;; if such a buffer was killed while the window was hidden within a
-;; window configuration.  Such killed buffers get removed whenever
-;; `switch-to-prev-buffer' or `switch-to-next-buffer' encounter them.
-
-;; The following function is called by `set-window-buffer' _before_ it
-;; replaces the buffer of the argument window with the new buffer.
-(defun push-window-buffer-onto-prev (&optional window)
-  "Push entry for WINDOW's buffer onto WINDOW's prev-buffers list.
-WINDOW must be a live window and defaults to the selected one.
-
-Any duplicate entries for the buffer in the list are removed."
+;; exception of 'switch-to-prev-buffer' and 'switch-to-next-buffer'.
+;; 'switch-to-prev-buffer' pushes the buffer it just replaced on it,
+;; 'switch-to-next-buffer' pops the last pushed buffer from it.
+
+;; The following function is called by 'set-window-buffer' _before_ it
+;; replaces the buffer of the argument window with the new buffer.  It
+;; does not record a non-minibuffer buffer (like the one created by
+;; 'calculator' in Electric mode) in a minibuffer window since the code
+;; in minibuf.c cannot handle that.  The minibuf.c code calls this
+;; function exclusively to arrange minibuffers shown in minibuffer
+;; windows.
+(defun record-window-buffer (&optional window)
+  "Record WINDOW's buffer.
+Add the buffer currently shown in WINDOW to the list of WINDOW's
+previous buffers.  WINDOW must be a live window and defaults to the
+selected one.
+
+If WINDOW is not a minibuffer window, do not record insignificant
+buffers (buffers whose name starts with a space).  If WINDOW is a
+minibuffer window, record its buffer if and only if that buffer is a
+live minibuffer (`minibufferp' with LIVE argument non-nil must return
+non-nil for it).
+
+Run `buffer-list-update-hook' if and only if WINDOW is not a minibuffer
+window."
   (let* ((window (window-normalize-window window t))
+        (mini (window-minibuffer-p window))
          (buffer (window-buffer window))
-         (w-list (window-prev-buffers window))
-         (entry (assq buffer w-list)))
+         (prev-buffers (window-prev-buffers window))
+         (entry (assq buffer prev-buffers)))
     (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)))))
+      (setq prev-buffers (assq-delete-all buffer prev-buffers)))
 
-(defun record-window-buffer (&optional window)
-  "Record WINDOW's buffer.
-WINDOW must be a live window and defaults to the selected one."
-  (let* ((window (window-normalize-window window t))
-         (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)
 
-    ;; Don't record insignificant buffers.
-    (when (not (eq (aref (buffer-name buffer) 0) ?\s))
-      (push-window-buffer-onto-prev window)
-      (run-hooks 'buffer-list-update-hook))))
+    ;; For minibuffer windows record live minibuffers only.  For normal
+    ;; windows do not record insignificant buffers.
+    (when (if mini
+             (minibufferp buffer t)
+           (not (eq (aref (buffer-name buffer) 0) ?\s)))
+      (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 prev-buffers))
+
+       (unless mini
+         (run-hooks 'buffer-list-update-hook))))))
+
+(defalias 'push-window-buffer-onto-prev 'record-window-buffer)
 
 (defun unrecord-window-buffer (&optional window buffer all)
   "Unrecord BUFFER in WINDOW.
@@ -5159,10 +5170,19 @@ parameters naming it."
        ;; If a window doesn't show BUFFER, unrecord BUFFER in it.
        (unrecord-window-buffer window buffer t)))))
 
+;; Conceptually, 'replace-buffer-in-windows' would not have to touch the
+;; list of previous buffers of a minibuffer window: As a rule,
+;; minibuffers are never deleted and any other buffers shown in a
+;; minibuffer window are not recorded by 'record-window'.  To be on the
+;; safe side, 'replace-buffer-in-windows' now scans minibuffer windows
+;; too to make sure that any killed buffer gets removed from all lists
+;; of previous and next buffers.  'replace-buffer-in-windows' still does
+;; _not_ replace the buffer itself in any minibuffer window showing it.
+;; That case is still handled only in 'kill-buffer' itself.
 (defun replace-buffer-in-windows (&optional buffer-or-name)
   "Replace BUFFER-OR-NAME with some other buffer in all windows showing it.
 BUFFER-OR-NAME may be a buffer or the name of an existing buffer and
-defaults to the current buffer.  Minibuffer windows are not considered.
+defaults to the current buffer.
 
 If the option `kill-buffer-quit-windows' is nil, behave as follows: With
 the exception of side windows, when a window showing BUFFER-OR-NAME is
@@ -5179,21 +5199,29 @@ In either case, remove the buffer denoted by BUFFER-OR-NAME from the
 lists of previous and next buffers of all windows and remove any
 `quit-restore' or `quit-restore-prev' parameters mentioning it.
 
+This function does not replace the buffer specified by BUFFER-OR-NAME in
+any minibuffer window showing it, nor does it delete minibuffer windows
+or minibuffer frames.  It removes, however, that buffer from the lists
+of previous and next buffers of all minibuffer windows.
+
 If, for any window showing BUFFER-OR-NAME running the abnormal hook
 `window-deletable-functions' returns nil, do not delete that window but
 show some other buffer in that window.
 
-This function is called by `kill-buffer' which kills the buffer
-specified by `buffer-or-name' afterwards.  It never kills a buffer by
-itself."
+This function is called by `kill-buffer' which effectively kills the
+buffer specified by `buffer-or-name' afterwards.  It never kills a
+buffer by itself."
   (interactive "bBuffer to replace: ")
   (let ((buffer (window-normalize-buffer buffer-or-name)))
-    ;; Scan all windows.  We have to unrecord BUFFER-OR-NAME in those
-    ;; not showing it.
-    (dolist (window (window-list-1 nil nil t))
+    ;; Scan all windows including minibuffer windows.  We have to
+    ;; unrecord BUFFER-OR-NAME even in those not showing it.
+    (dolist (window (window-list-1 nil t t))
       (when (eq (window-buffer window) buffer)
-       (if kill-buffer-quit-windows
-           (quit-restore-window window 'killing)
+       (cond
+        ((window-minibuffer-p window))
+        (kill-buffer-quit-windows
+         (quit-restore-window window 'killing))
+        (t
          (let ((dedicated-side (eq (window-dedicated-p window) 'side)))
             (when (or dedicated-side (not (window--delete window t 'kill)))
              ;; Switch to another buffer in that window.
@@ -5211,7 +5239,7 @@ itself."
          ;; element of the parameter, 'quit-restore-window' cannot
          ;; possibly show BUFFER instead; so this parameter becomes
          ;; useless too.
-         (unrecord-window-buffer window buffer t)))))
+         (unrecord-window-buffer window buffer t))))))
 
 (put 'replace-buffer-in-windows 'minibuffer-action 'display-buffer)
 
index 2865e3ede570434b3794b13f88fffeadb8bb3c58..5ef7b1f71b155cfcb06b2642a6b6e14af1bb9975 100644 (file)
@@ -151,16 +151,15 @@ zip_minibuffer_stacks (Lisp_Object dest_window, Lisp_Object source_window)
       set_window_buffer (dest_window, sw->contents, 0, 0);
       Fset_window_start (dest_window, Fwindow_start (source_window), Qnil);
       Fset_window_point (dest_window, Fwindow_point (source_window));
-      dw->prev_buffers = sw->prev_buffers;
+      wset_prev_buffers (dw, sw->prev_buffers);
       set_window_buffer (source_window, nth_minibuffer (0), 0, 0);
-      sw->prev_buffers = Qnil;
+      wset_prev_buffers (sw, Qnil);
       return;
     }
 
-  if (live_minibuffer_p (dw->contents))
-    call1 (Qpush_window_buffer_onto_prev, dest_window);
-  if (live_minibuffer_p (sw->contents))
-    call1 (Qpush_window_buffer_onto_prev, source_window);
+  call1 (Qrecord_window_buffer, dest_window);
+  call1 (Qrecord_window_buffer, source_window);
+
   acc = merge_c (dw->prev_buffers, sw->prev_buffers, minibuffer_ent_greater);
 
   if (!NILP (acc))
@@ -171,8 +170,9 @@ zip_minibuffer_stacks (Lisp_Object dest_window, Lisp_Object source_window)
       Fset_window_start (dest_window, Fcar (Fcdr (d_ent)), Qnil);
       Fset_window_point (dest_window, Fcar (Fcdr (Fcdr (d_ent))));
     }
-  dw->prev_buffers = acc;
-  sw->prev_buffers = Qnil;
+
+  wset_prev_buffers (dw, acc);
+  wset_prev_buffers (sw, Qnil);
   set_window_buffer (source_window, nth_minibuffer (0), 0, 0);
 }
 
@@ -655,8 +655,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial,
                                    Fframe_first_window (MB_frame), Qnil);
     }
   MB_frame = XWINDOW (XFRAME (selected_frame)->minibuffer_window)->frame;
-  if (live_minibuffer_p (XWINDOW (minibuf_window)->contents))
-    call1 (Qpush_window_buffer_onto_prev, minibuf_window);
+
+  call1 (Qrecord_window_buffer, minibuf_window);
 
   record_unwind_protect_void (minibuffer_unwind);
 
index 35092ddd582eb7e103b46029597e1012307813cd..34968ac824fee4db0a59910f54e07588880d31d5 100644 (file)
@@ -3647,9 +3647,17 @@ replace_buffer_in_windows (Lisp_Object buffer)
     call1 (Qreplace_buffer_in_windows, buffer);
 }
 
-/* If BUFFER is shown in a window, safely replace it with some other
-   buffer in all windows of all frames, even those on other keyboards.  */
-
+/** If BUFFER is shown in any window, safely replace it with some other
+    buffer in all windows of all frames, even those on other keyboards.
+    Do not delete any window.
+
+    This function is called by Fkill_buffer when it detects that
+    replacing BUFFER in some window showing BUFFER has failed.  It
+    assumes that ‘replace-buffer-in-windows’ has removed any entry
+    referencing BUFFER from any window's lists of previous and next
+    buffers and that window's ‘quit-restore’ and 'quit-restore-prev'
+    parameters.
+*/
 void
 replace_buffer_in_windows_safely (Lisp_Object buffer)
 {