From: martin rudalics via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
To: Juri Linkov <juri@linkov.net>
Cc: 68235@debbugs.gnu.org
Subject: bug#68235: 29.1.90; Switching tabs stops following process output in selected window
Date: Tue, 6 Feb 2024 11:34:54 +0100 [thread overview]
Message-ID: <39fe71c5-db40-49d1-b22b-37b52ed13865@gmx.at> (raw)
In-Reply-To: <86wmrjl8m3.fsf@mail.linkov.net>
[-- Attachment #1: Type: text/plain, Size: 1957 bytes --]
> Is 'keep-windows' doable for 'window-state-put' as well?
I attach a patch that adds a fourth argument to 'window-state-put'. It
and a new 'set-window-configuration' now use a new function I called
'marker-last-position' that returns the last position of a marker even
after its buffer was killed.
The patch also fixes a bug in 'window--state-put-2' that can be
reproduced with the following simple scenario
(let ((buffer (get-buffer-create "*foo*"))
state)
(pop-to-buffer buffer)
(setq state (window-state-get))
(kill-buffer buffer)
(window-state-put state))
Did you never see it?
> So maybe the same option 'keep-windows' could call the same hook
> 'post-set-window-configuration-functions' from 'window-state-put' too?
I added a new hook called 'window-state-put-keep-window-functions' with
the same arguments as 'post-set-window-configuration-functions'. Maybe
people wanted to keep them apart. If you think the hook should be also
run when there are no "kept" windows, we can do that as well.
I tested it here with
(defun foo (frame windows)
(while windows
(let* ((quad (car windows))
(window (car quad))
(buffer (find-file-noselect
(buffer-file-name (nth 1 quad)))))
(when buffer
(set-window-buffer window buffer)
(set-window-point window (nth 3 quad))
(set-window-start window (nth 2 quad) t)))
(setq windows (cdr windows))))
(add-hook 'window-state-put-keep-window-functions 'foo)
(let ((window (selected-window))
(buffer (pop-to-buffer
(find-file-noselect "...")))
(window-1 (split-window))
(window-2 (split-window nil nil t))
state)
(set-window-point window-1 5000)
(set-window-point window-2 10000)
(setq state (window-state-get))
(y-or-n-p "State saved ...")
(delete-other-windows window)
(kill-buffer buffer)
(y-or-n-p "State reset ...")
(window-state-put state nil nil t)
(message "State restored"))
martin
[-- Attachment #2: keep-windows-2.diff --]
[-- Type: text/x-patch, Size: 14106 bytes --]
diff --git a/lisp/window.el b/lisp/window.el
index e100f25526b..3fa06b0708c 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -6168,12 +6168,25 @@ window-state-get
(min-pixel-width-safe . ,(window-min-size window t 'safe t)))
(window--state-get-1 window writable)))
+(defvar window-state-put-keep-window-functions nil
+ "List of functions run by `window-state-put' for windows to keep.
+These functions are called when `window-state-put' has been
+invoked with KEEP-WINDOWS non-nil. `window-state-put' runs them
+after it has restored STATE, for each window whose buffer has
+been deleted since STATE was saved. Each function is called with
+two arguments. The frame on which the window was restored and a
+quadruple of four entries - the window, its old buffer and that
+buffer's start and point positions.")
+
(defvar window-state-put-list nil
"Helper variable for `window-state-put'.")
(defvar window-state-put-stale-windows nil
"Helper variable for `window-state-put'.")
+(defvar window-state-put-kept-windows nil
+ "Helper variable for `window-state-put'.")
+
(defun window--state-put-1 (state &optional window ignore totals pixelwise)
"Helper function for `window-state-put'."
(let ((type (car state)))
@@ -6258,7 +6271,7 @@ window--state-put-1
;; Continue with the last window split off.
(setq window new))))))))
-(defun window--state-put-2 (ignore pixelwise)
+(defun window--state-put-2 (ignore pixelwise keep-windows)
"Helper function for `window-state-put'."
(dolist (item window-state-put-list)
(let ((window (car item))
@@ -6278,9 +6291,10 @@ window--state-put-2
(set-window-parameter window (car parameter) (cdr parameter))))
;; Process buffer related state.
(when state
- (let ((buffer (get-buffer (car state)))
- (state (cdr state)))
- (if buffer
+ (let* ((old-buffer-or-name (car state))
+ (buffer (get-buffer old-buffer-or-name))
+ (state (cdr state)))
+ (if (buffer-live-p buffer)
(with-current-buffer buffer
(set-window-buffer window buffer)
(set-window-hscroll window (cdr (assq 'hscroll state)))
@@ -6375,9 +6389,22 @@ window--state-put-2
;; save the window with the intention of deleting it later
;; if possible.
(switch-to-prev-buffer window)
- (push window window-state-put-stale-windows)))))))
-
-(defun window-state-put (state &optional window ignore)
+ (if keep-windows
+ (let* ((start (cdr (assq 'start state)))
+ ;; Handle both - marker positions from writable
+ ;; states and markers from non-writable states.
+ (start-pos (if (markerp start)
+ (marker-last-position start)
+ start))
+ (point (cdr (assq 'point state)))
+ (point-pos (if (markerp point)
+ (marker-last-position point)
+ point)))
+ (push (list window old-buffer-or-name start-pos point-pos)
+ window-state-put-kept-windows))
+ (push window window-state-put-stale-windows))))))))
+
+(defun window-state-put (state &optional window ignore keep-windows)
"Put window state STATE into WINDOW.
STATE should be the state of a window returned by an earlier
invocation of `window-state-get'. Optional argument WINDOW must
@@ -6388,8 +6415,19 @@ window-state-put
Optional argument IGNORE non-nil means ignore minimum window
sizes and fixed size restrictions. IGNORE equal `safe' means
windows can get as small as `window-safe-min-height' and
-`window-safe-min-width'."
+`window-safe-min-width'.
+
+Optional argument KEEP-WINDOWS non-nil means to never delete any
+windows saved by STATE whose buffers were deleted since STATE was
+saved. `window-state-put' records such windows in a list and
+calls the functions in `window-state-put-keep-window-functions'
+with a list of two arguments: the frame where STATE was put and a
+list of entries for each such window. Each entry contains four
+elements - the window, its old buffer and the last positions of
+`window-start' and `window-point' for the buffer in that window.
+KEEP-WINDOWS nil means such windows are deleted, if possible."
(setq window-state-put-stale-windows nil)
+ (setq window-state-put-kept-windows nil)
;; When WINDOW is internal or nil, reduce it to a live one,
;; then create a new window on the same frame to put STATE into.
@@ -6489,15 +6527,21 @@ window-state-put
(with-temp-buffer
(set-window-buffer window (current-buffer))
(window--state-put-1 state window nil totals pixelwise)
- (window--state-put-2 ignore pixelwise))
+ (window--state-put-2 ignore pixelwise keep-windows))
(while window-state-put-stale-windows
(let ((window (pop window-state-put-stale-windows)))
- ;; Avoid that 'window-deletable-p' throws an error if window
+ ;; Avoid that 'window-deletable-p' throws an error if window
;; was already deleted when exiting 'with-temp-buffer' above
;; (Bug#54028).
(when (and (window-valid-p window)
(eq (window-deletable-p window) t))
(delete-window window))))
+ (when window-state-put-kept-windows
+ (when window-state-put-keep-window-functions
+ (run-hook-with-args
+ 'window-state-put-keep-window-functions
+ frame window-state-put-kept-windows))
+ (setq window-state-put-kept-windows nil))
(window--check frame))))
(defun window-state-buffers (state)
diff --git a/src/keyboard.c b/src/keyboard.c
index e1d738dd6ef..b6ba3c57ff8 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -2281,7 +2281,7 @@ read_char_help_form_unwind (void)
Lisp_Object window_config = XCAR (help_form_saved_window_configs);
help_form_saved_window_configs = XCDR (help_form_saved_window_configs);
if (!NILP (window_config))
- Fset_window_configuration (window_config, Qnil, Qnil);
+ Fset_window_configuration (window_config, Qnil, Qnil, Qnil);
}
#define STOP_POLLING \
diff --git a/src/marker.c b/src/marker.c
index 377f6fbe8db..14b9f63f0cd 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -458,6 +458,18 @@ DEFUN ("marker-position", Fmarker_position, Smarker_position, 1, 1, 0,
return Qnil;
}
+DEFUN ("marker-last-position", Fmarker_last_position, Smarker_last_position, 1, 1, 0,
+ doc: /* Return last position of MARKER in its buffer.
+This is like `marker-position' with one exception: If the buffer of
+MARKER is dead, it returns the last position of MARKER in that buffer
+before it was killed. */)
+ (Lisp_Object marker)
+{
+ CHECK_MARKER (marker);
+
+ return make_fixnum (XMARKER (marker)->charpos);
+}
+
/* Change M so it points to B at CHARPOS and BYTEPOS. */
static void
@@ -825,6 +837,7 @@ verify_bytepos (ptrdiff_t charpos)
syms_of_marker (void)
{
defsubr (&Smarker_position);
+ defsubr (&Smarker_last_position);
defsubr (&Smarker_buffer);
defsubr (&Sset_marker);
defsubr (&Scopy_marker);
diff --git a/src/window.c b/src/window.c
index 3a54f7ce7b1..cb37983b648 100644
--- a/src/window.c
+++ b/src/window.c
@@ -7079,7 +7079,7 @@ DEFUN ("window-configuration-frame", Fwindow_configuration_frame, Swindow_config
}
DEFUN ("set-window-configuration", Fset_window_configuration,
- Sset_window_configuration, 1, 3, 0,
+ Sset_window_configuration, 1, 4, 0,
doc: /* Set the configuration of windows and buffers as specified by CONFIGURATION.
CONFIGURATION must be a value previously returned
by `current-window-configuration' (which see).
@@ -7090,16 +7090,35 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
the mini-window of the frame doesn't get set to the corresponding element
of CONFIGURATION.
+Normally, this function will try to delete any dead window in
+CONFIGURATION whose buffer has been deleted since CONFIGURATION was
+made. However, if KEEP-WINDOWS is non-nil, it will preserve such a
+window in the restored layout and show another buffer in it.
+
+After restoring the frame layout, this function runs the abnormal hook
+'post-set-window-configuration-functions' with two arguments - the frame
+whose layout is has restored and, provided KEEP-WINDOWS is non-nil, a
+list of entries for each window whose buffer has been found dead when it
+tried to restore CONFIGURATION: Each entry is a list of four elements
+<window, buffer, start, point> where `window' denotes the window whose
+buffer was found dead, `buffer' denotes the dead buffer, and `start' and
+`point' denote the positions of `window-start' and `window-point' of
+that window at the time CONFIGURATION was made. Note that these
+positions are no markers and may be no more accurate if the buffer has
+been modified afterwards. If KEEP-WINDOWS is nil, the second argument
+is nil.
+
If CONFIGURATION was made from a frame that is now deleted,
only frame-independent values can be restored. In this case,
the return value is nil. Otherwise the value is t. */)
(Lisp_Object configuration, Lisp_Object dont_set_frame,
- Lisp_Object dont_set_miniwindow)
+ Lisp_Object dont_set_miniwindow, Lisp_Object keep_windows)
{
register struct save_window_data *data;
struct Lisp_Vector *saved_windows;
Lisp_Object new_current_buffer;
Lisp_Object frame;
+ Lisp_Object kept_windows = Qnil;
Lisp_Object old_frame = selected_frame;
struct frame *f;
ptrdiff_t old_point = -1;
@@ -7340,6 +7359,11 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
BUF_PT (XBUFFER (w->contents)),
BUF_PT_BYTE (XBUFFER (w->contents)));
w->start_at_line_beg = true;
+ if (!NILP (keep_windows))
+ kept_windows = Fcons (list4 (window, p->buffer,
+ Fmarker_last_position (p->start),
+ Fmarker_last_position (p->pointm)),
+ kept_windows);
}
else if (!NILP (w->start))
/* Leaf window has no live buffer, get one. */
@@ -7360,6 +7384,11 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
dead_windows = Fcons (window, dead_windows);
/* Make sure window is no more dedicated. */
wset_dedicated (w, Qnil);
+ if (!NILP (keep_windows))
+ kept_windows = Fcons (list4 (window, p->buffer,
+ Fmarker_last_position (p->start),
+ Fmarker_last_position (p->pointm)),
+ kept_windows);
}
}
@@ -7411,12 +7440,13 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
unblock_input ();
/* Scan dead buffer windows. */
- for (; CONSP (dead_windows); dead_windows = XCDR (dead_windows))
- {
- window = XCAR (dead_windows);
- if (WINDOW_LIVE_P (window) && !EQ (window, FRAME_ROOT_WINDOW (f)))
- delete_deletable_window (window);
- }
+ if (NILP (keep_windows))
+ for (; CONSP (dead_windows); dead_windows = XCDR (dead_windows))
+ {
+ window = XCAR (dead_windows);
+ if (WINDOW_LIVE_P (window) && !EQ (window, FRAME_ROOT_WINDOW (f)))
+ delete_deletable_window (window);
+ }
/* Record the selected window's buffer here. The window should
already be the selected one from the call above. */
@@ -7463,6 +7493,11 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
minibuf_selected_window = data->minibuf_selected_window;
SAFE_FREE ();
+
+ if (!NILP (Vrun_hooks) && !NILP (Vpost_set_window_configuration_functions))
+ run_hook_with_args_2 (Qpost_set_window_configuration_functions, frame,
+ kept_windows);
+
return FRAME_LIVE_P (f) ? Qt : Qnil;
}
@@ -7472,12 +7507,12 @@ restore_window_configuration (Lisp_Object configuration)
if (CONSP (configuration))
Fset_window_configuration (XCAR (configuration),
Fcar_safe (XCDR (configuration)),
- Fcar_safe (Fcdr_safe (XCDR (configuration))));
+ Fcar_safe (Fcdr_safe (XCDR (configuration))),
+ Qnil);
else
- Fset_window_configuration (configuration, Qnil, Qnil);
+ Fset_window_configuration (configuration, Qnil, Qnil, Qnil);
}
-
/* If WINDOW is an internal window, recursively delete all child windows
reachable via the next and contents slots of WINDOW. Otherwise setup
WINDOW to not show any buffer. */
@@ -7690,6 +7725,7 @@ save_window_save (Lisp_Object window, struct Lisp_Vector *vector, ptrdiff_t i)
BUF_PT_BYTE (XBUFFER (w->contents)));
else
p->pointm = Fcopy_marker (w->pointm, Qnil);
+
p->old_pointm = Fcopy_marker (w->old_pointm, Qnil);
XMARKER (p->pointm)->insertion_type = window_point_insertion_type;
XMARKER (p->old_pointm)->insertion_type = window_point_insertion_type;
@@ -8460,6 +8496,8 @@ syms_of_window (void)
DEFSYM (Qheader_line_format, "header-line-format");
DEFSYM (Qtab_line_format, "tab-line-format");
DEFSYM (Qno_other_window, "no-other-window");
+ DEFSYM (Qpost_set_window_configuration_functions,
+ "post-set-window-configuration-functions");
DEFVAR_LISP ("temp-buffer-show-function", Vtemp_buffer_show_function,
doc: /* Non-nil means call as function to display a help buffer.
@@ -8617,6 +8655,21 @@ syms_of_window (void)
call is performed with the frame temporarily selected. */);
Vwindow_configuration_change_hook = Qnil;
+ DEFVAR_LISP ("post-set-window-configuration-functions",
+ Vpost_set_window_configuration_functions,
+ doc: /* Functions called after restoring a window configuration.
+The value should be a list of functions that take two arguments.
+
+This function is called by `set-window-configuration' after it has
+restored the layout of a frame. The first argument specifies the frame
+whose configuration has been restored. The second argument, if non-nil,
+specifies a list of entries for each window whose buffer has been found
+dead at the time 'set-window-configuration' tried to restore it in that
+window. Each entry is a list of four values - the window whose buffer
+was found dead, the dead buffer, and the positions of start and point of
+the buffer in that window. */);
+ Vpost_set_window_configuration_functions = Qnil;
+
DEFVAR_LISP ("recenter-redisplay", Vrecenter_redisplay,
doc: /* Non-nil means `recenter' redraws entire frame.
If this option is non-nil, then the `recenter' command with a nil
next prev parent reply other threads:[~2024-02-06 10:34 UTC|newest]
Thread overview: 85+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-01-03 20:48 bug#68235: 29.1.90; Switching tabs stops following process output in selected window Dan McCarthy
2024-01-04 6:09 ` Eli Zaretskii
2024-01-04 10:23 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-04 10:42 ` Eli Zaretskii
2024-01-04 17:07 ` Juri Linkov
2024-01-04 17:48 ` Eli Zaretskii
2024-01-05 9:24 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-06 17:36 ` Juri Linkov
2024-01-07 14:54 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-07 16:45 ` Juri Linkov
2024-01-08 8:55 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-09 17:23 ` Juri Linkov
2024-01-07 16:49 ` Juri Linkov
2024-01-09 17:25 ` Juri Linkov
2024-01-10 8:37 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-10 17:08 ` Juri Linkov
2024-01-11 9:14 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-12 7:37 ` Juri Linkov
2024-01-13 10:38 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-13 15:02 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-13 18:20 ` Juri Linkov
2024-01-14 8:13 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-14 18:53 ` Juri Linkov
2024-01-15 10:24 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-15 17:53 ` Juri Linkov
2024-01-16 10:19 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-16 16:30 ` Juri Linkov
2024-01-17 11:42 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-17 16:36 ` Juri Linkov
2024-01-18 10:47 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-18 16:50 ` Juri Linkov
2024-01-20 9:44 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-22 7:43 ` Juri Linkov
2024-01-23 9:30 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-24 7:54 ` Juri Linkov
2024-01-25 9:39 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-25 17:46 ` Juri Linkov
2024-01-26 9:56 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-27 17:58 ` Juri Linkov
2024-01-28 10:06 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-02-05 7:17 ` Juri Linkov
2024-02-06 10:34 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors [this message]
2024-02-06 18:03 ` Juri Linkov
2024-02-15 7:34 ` Juri Linkov
2024-02-16 9:40 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-02-18 7:35 ` Juri Linkov
2024-02-19 9:42 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-02-20 17:44 ` Juri Linkov
2024-03-04 9:40 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-05 17:27 ` Juri Linkov
2024-03-05 17:45 ` Eli Zaretskii
2024-03-06 18:03 ` Juri Linkov
2024-03-09 8:35 ` Eli Zaretskii
2024-03-17 17:57 ` Juri Linkov
2024-03-05 17:37 ` Juri Linkov
2024-03-06 10:19 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-06 17:57 ` Juri Linkov
2024-03-08 9:21 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-10 17:23 ` Juri Linkov
2024-03-11 9:13 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-09 6:53 ` Juri Linkov
2024-04-09 7:36 ` Eli Zaretskii
2024-04-09 9:22 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-09 16:40 ` Juri Linkov
2024-04-10 8:47 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-10 17:35 ` Juri Linkov
2024-04-11 9:16 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-12 6:30 ` Juri Linkov
2024-04-12 8:18 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-12 16:20 ` Juri Linkov
2024-04-15 9:21 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-21 6:59 ` Juri Linkov
2024-04-21 8:56 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-04-22 6:46 ` Juri Linkov
2024-04-21 9:27 ` Eli Zaretskii
2024-04-22 6:40 ` Juri Linkov
2024-04-22 7:00 ` Eli Zaretskii
2024-04-22 16:36 ` Juri Linkov
2024-04-22 19:22 ` Eli Zaretskii
2024-03-15 9:38 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-17 17:47 ` Juri Linkov
2024-03-18 10:13 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-03-15 10:11 ` Andreas Schwab
2024-03-15 10:56 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-02-04 17:27 ` 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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=39fe71c5-db40-49d1-b22b-37b52ed13865@gmx.at \
--to=bug-gnu-emacs@gnu.org \
--cc=68235@debbugs.gnu.org \
--cc=juri@linkov.net \
--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 external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.