Fix 'set-window-configuration' and 'window-state-put'
Fix some bugs with 'window-state-put' (Bug#69093). Add new hook 'window-kept-windows-functions' (Bug#68235). * doc/lispref/windows.texi (Window Configurations): Mention 'window-kept-windows-functions'. (Window Hooks): Describe new abnormal hook 'window-kept-windows-functions'. * src/marker.c (Fmarker_last_position): New function to return the last position of a marker even if its buffer is now dead. * src/window.c (Fset_window_configuration): If 'window-kept-windows-functions' is non-nil, do not delete any window whose buffer is now dead but remember all such windows in a list to pass to 'window-kept-windows-functions'. Run 'window-kept-windows-functions' if it is non-nil. (Vwindow_kept_windows_functions): New abnormal hook run by Fset_window_configuration and 'window-state-put' with two arguments - the frame whose configuration is restored and a list of entries for each window whose buffer was found dead during restoration. Each entry is a list of four elements, the window, the dead buffer, and the last know positions of the start and point of that window. * lisp/window.el (window-state-put-kept-windows) (window-state-put-selected-window): New variables. (window--state-put-2): Make sure buffer is live before restoring its state. Set 'window-state-put-selected-window' to state's selected window. If 'window-kept-windows-functions' is non-nil, do not delete any windows whose buffer is found dead but remember all such windows in a list to pass to 'window-kept-windows-functions'. (window-state-put): Run 'window-kept-windows-functions' if it is non-nil. Select window recorded in 'window-state-put-selected-window'.
This commit is contained in:
parent
6dacb60bb1
commit
912e37b811
4 changed files with 173 additions and 16 deletions
|
@ -6266,9 +6266,13 @@ this function does is to restore the value of the variable
|
|||
|
||||
If the buffer of a window of @var{configuration} has been killed since
|
||||
@var{configuration} was made, that window is, as a rule, removed from
|
||||
the restored configuration. However, if that window is the last
|
||||
window remaining in the restored configuration, another live buffer is
|
||||
shown in it.
|
||||
the restored configuration. However, if that window is the last window
|
||||
remaining in the restored configuration, another live buffer is shown in
|
||||
it. Also, if the variable @var{window-kept-windows-functions} is
|
||||
non-@code{nil}, any window whose buffer is now dead is not deleted.
|
||||
Rather, this function will show another live buffer in that window and
|
||||
include an entry for that window when calling any function in
|
||||
@var{window-kept-windows-functions} (@pxref{Window Hooks}).
|
||||
|
||||
Here is a way of using this function to get the same effect as
|
||||
@code{save-window-excursion}:
|
||||
|
@ -6357,6 +6361,15 @@ a live window, it is replaced by a new live window created on the same
|
|||
frame before putting @var{state} into it. If @var{window} is @code{nil},
|
||||
it puts the window state into a new window.
|
||||
|
||||
If the buffer of any window recorded in @var{state} has been killed
|
||||
since @var{state} was made, that window is, as a rule, not restored.
|
||||
However, if that window is the only window in @var{state}, another live
|
||||
buffer will be shown in it. Also, if the variable
|
||||
@var{window-kept-windows-functions} is non-@code{nil}, any window whose
|
||||
buffer is now dead is restored. This function will show another live
|
||||
buffer in it and include an entry for that window when calling a
|
||||
function in @var{window-kept-windows-functions} (@pxref{Window Hooks}).
|
||||
|
||||
If the optional argument @var{ignore} is non-@code{nil}, it means to ignore
|
||||
minimum window sizes and fixed-size restrictions. If @var{ignore}
|
||||
is @code{safe}, this means windows can get as small as one line
|
||||
|
@ -6623,6 +6636,27 @@ Lock fontification function, which will be called whenever parts of a
|
|||
buffer are (re)fontified because a window was scrolled or its size
|
||||
changed. @xref{Other Font Lock Variables}.
|
||||
|
||||
@cindex window kept windows functions
|
||||
@defvar window-kept-windows-functions
|
||||
This variable holds a list of functions that Emacs will call after
|
||||
restoring a window configuration via @code{set-window-configuration} or
|
||||
state via @code{window-state-put} (@pxref{Window Configurations}). When
|
||||
the value of this variable is non-@code{nil}, these functions will not
|
||||
delete any window whose buffer has been killed since the corresponding
|
||||
configuration or state was saved, but show some live buffer in it.
|
||||
|
||||
The value should be a list of functions that take two arguments. The
|
||||
first argument specifies the frame whose windows have been restored.
|
||||
The second argument specifies a list of entries for each window whose
|
||||
buffer has been found dead at the time @code{set-window-configuration}
|
||||
or @code{window-state-put} tried to restore it. Each entry is a list of
|
||||
four values - the window whose buffer was found dead, the dead buffer,
|
||||
and the last known positions of start and point of the buffer in that
|
||||
window. Any function run by this hook should check that the window is
|
||||
live since another function run by this hook may have deleted it in the
|
||||
meantime.
|
||||
@end defvar
|
||||
|
||||
@cindex window change functions
|
||||
The remainder of this section covers six hooks that are called
|
||||
during redisplay provided a significant, non-scrolling change of a
|
||||
|
|
|
@ -6174,6 +6174,12 @@ value can be also stored on disk and read back in a new session."
|
|||
(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'.")
|
||||
|
||||
(defvar window-state-put-selected-window 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)))
|
||||
|
@ -6278,9 +6284,10 @@ value can be also stored on disk and read back in a new session."
|
|||
(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)))
|
||||
|
@ -6348,7 +6355,18 @@ value can be also stored on disk and read back in a new session."
|
|||
(set-window-point window (cdr (assq 'point state))))
|
||||
;; Select window if it's the selected one.
|
||||
(when (cdr (assq 'selected state))
|
||||
(select-window window))
|
||||
;; This used to call 'select-window' which, however,
|
||||
;; can be partially undone because the current buffer
|
||||
;; may subsequently change twice: When leaving the
|
||||
;; present 'with-current-buffer' and when leaving the
|
||||
;; containing 'with-temp-buffer' form (Bug#69093).
|
||||
;; 'window-state-put-selected-window' should now work
|
||||
;; around that bug but we leave this 'select-window'
|
||||
;; in since some code run before the part that fixed
|
||||
;; it might still refer to this window as the selected
|
||||
;; one.
|
||||
(select-window window)
|
||||
(setq window-state-put-selected-window window))
|
||||
(set-window-next-buffers
|
||||
window
|
||||
(delq nil (mapcar (lambda (buffer)
|
||||
|
@ -6375,7 +6393,20 @@ value can be also stored on disk and read back in a new session."
|
|||
;; save the window with the intention of deleting it later
|
||||
;; if possible.
|
||||
(switch-to-prev-buffer window)
|
||||
(push window window-state-put-stale-windows)))))))
|
||||
(if window-kept-windows-functions
|
||||
(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)
|
||||
"Put window state STATE into WINDOW.
|
||||
|
@ -6388,8 +6419,20 @@ If WINDOW is nil, create a new window before putting STATE into it.
|
|||
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'.
|
||||
|
||||
If the abnormal hook `window-kept-windows-functions' is non-nil,
|
||||
do not delete any windows saved by STATE whose buffers were
|
||||
deleted since STATE was saved. Rather, show some live buffer in
|
||||
them and call the functions in `window-kept-windows-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.
|
||||
Always check the window for liveness because another function run
|
||||
by this hook may have deleted it."
|
||||
(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.
|
||||
|
@ -6482,6 +6525,7 @@ windows can get as small as `window-safe-min-height' and
|
|||
(error "Window %s too small to accommodate state" window)
|
||||
(setq state (cdr state))
|
||||
(setq window-state-put-list nil)
|
||||
(setq window-state-put-selected-window nil)
|
||||
;; Work on the windows of a temporary buffer to make sure that
|
||||
;; splitting proceeds regardless of any buffer local values of
|
||||
;; `window-size-fixed'. Release that buffer after the buffers of
|
||||
|
@ -6490,14 +6534,21 @@ windows can get as small as `window-safe-min-height' and
|
|||
(set-window-buffer window (current-buffer))
|
||||
(window--state-put-1 state window nil totals pixelwise)
|
||||
(window--state-put-2 ignore pixelwise))
|
||||
(when (window-live-p window-state-put-selected-window)
|
||||
(select-window window-state-put-selected-window))
|
||||
(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-kept-windows-functions
|
||||
(run-hook-with-args
|
||||
'window-kept-windows-functions
|
||||
frame window-state-put-kept-windows)
|
||||
(setq window-state-put-kept-windows nil))
|
||||
(window--check frame))))
|
||||
|
||||
(defun window-state-buffers (state)
|
||||
|
|
13
src/marker.c
13
src/marker.c
|
@ -463,6 +463,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
|
||||
|
@ -830,6 +842,7 @@ void
|
|||
syms_of_marker (void)
|
||||
{
|
||||
defsubr (&Smarker_position);
|
||||
defsubr (&Smarker_last_position);
|
||||
defsubr (&Smarker_buffer);
|
||||
defsubr (&Sset_marker);
|
||||
defsubr (&Scopy_marker);
|
||||
|
|
71
src/window.c
71
src/window.c
|
@ -7109,6 +7109,24 @@ current at the start of the function. If DONT-SET-MINIWINDOW is non-nil,
|
|||
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 the abnormal hook `window-kept-windows-functions' 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
|
||||
`window-kept-windows-functions' with two arguments - the frame whose
|
||||
layout it has restored and 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 last known
|
||||
positions of `window-start' and `window-point' of the buffer in that
|
||||
window. Any function run by this hook should check such a window for
|
||||
liveness because another function run by this hook may have deleted it
|
||||
in the meantime."
|
||||
|
||||
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. */)
|
||||
|
@ -7119,6 +7137,7 @@ the return value is nil. Otherwise the value is t. */)
|
|||
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;
|
||||
|
@ -7359,6 +7378,11 @@ the return value is nil. Otherwise the value is t. */)
|
|||
BUF_PT (XBUFFER (w->contents)),
|
||||
BUF_PT_BYTE (XBUFFER (w->contents)));
|
||||
w->start_at_line_beg = true;
|
||||
if (!NILP (Vwindow_kept_windows_functions))
|
||||
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. */
|
||||
|
@ -7379,6 +7403,11 @@ the return value is nil. Otherwise the value is t. */)
|
|||
dead_windows = Fcons (window, dead_windows);
|
||||
/* Make sure window is no more dedicated. */
|
||||
wset_dedicated (w, Qnil);
|
||||
if (!NILP (Vwindow_kept_windows_functions))
|
||||
kept_windows = Fcons (list4 (window, p->buffer,
|
||||
Fmarker_last_position (p->start),
|
||||
Fmarker_last_position (p->pointm)),
|
||||
kept_windows);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7430,12 +7459,13 @@ the return value is nil. Otherwise the value is t. */)
|
|||
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 (Vwindow_kept_windows_functions))
|
||||
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. */
|
||||
|
@ -7482,6 +7512,11 @@ the return value is nil. Otherwise the value is t. */)
|
|||
minibuf_selected_window = data->minibuf_selected_window;
|
||||
|
||||
SAFE_FREE ();
|
||||
|
||||
if (!NILP (Vrun_hooks) && !NILP (Vwindow_kept_windows_functions))
|
||||
run_hook_with_args_2 (Qwindow_kept_windows_functions, frame,
|
||||
kept_windows);
|
||||
|
||||
return FRAME_LIVE_P (f) ? Qt : Qnil;
|
||||
}
|
||||
|
||||
|
@ -8479,6 +8514,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 (Qwindow_kept_windows_functions,
|
||||
"window-kept-windows-functions");
|
||||
|
||||
DEFVAR_LISP ("temp-buffer-show-function", Vtemp_buffer_show_function,
|
||||
doc: /* Non-nil means call as function to display a help buffer.
|
||||
|
@ -8636,6 +8673,28 @@ its buffer or its total or body size since the last redisplay. Each
|
|||
call is performed with the frame temporarily selected. */);
|
||||
Vwindow_configuration_change_hook = Qnil;
|
||||
|
||||
DEFVAR_LISP ("window-kept-windows-functions",
|
||||
Vwindow_kept_windows_functions,
|
||||
doc: /* Functions run after restoring a window configuration or state.
|
||||
These functions are called by `set-window-configuration' and
|
||||
`window-state-put'. When the value of this variable is non-nil, these
|
||||
functions restore any window whose buffer has been deleted since the
|
||||
corresponding configuration or state was saved. Rather than deleting
|
||||
such a window, `set-window-configuration' and `window-state-put' show
|
||||
some live buffer in it.
|
||||
|
||||
The value should be a list of functions that take two arguments. 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' or `window-state-put' 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. Note that the window may be already
|
||||
dead since another function on this list may have deleted it in the
|
||||
meantime. */);
|
||||
Vwindow_kept_windows_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
|
||||
|
|
Loading…
Add table
Reference in a new issue