diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 0b8d7d3b76d..8bf6ebc828b 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -1355,7 +1355,7 @@ Splitting Windows Examples of such windows are side windows (@pxref{Side Windows}) and atomic windows (@pxref{Atomic Windows}). -@defun split-window &optional window size side pixelwise +@defun split-window &optional window size side pixelwise refer This function creates a new live window next to the window @var{window}. If @var{window} is omitted or @code{nil}, it defaults to the selected window. That window is split, and reduced in @@ -1403,10 +1403,35 @@ Splitting Windows to interpret @var{size} in units of pixels, instead of lines and columns. -If @var{window} is a live window, the new window inherits various -properties from it, including margins and scroll bars. If -@var{window} is an internal window, the new window inherits the -properties of the window selected within @var{window}'s frame. +If the optional fifth argument @var{refer} is non-@code{nil}, it has to +denote either a deleted, former live window on the same frame as +@var{window} or an arbitrary live window. If @var{refer} is a deleted +window, this function does not make a new window but rather resurrects +@var{refer} and inserts it into the window tree at the position and with +the sizes the new window would have been given. Buffer, start and point +positions of @var{refer} are restored to the values they had immediately +before @var{refer} was deleted the last time. Decorations and +parameters remain unaltered from their values before @var{refer} was +deleted. An error is thrown if @var{refer}'s buffer has been deleted +after @var{refer} itself was deleted. + +If and only if @var{refer} is a deleted window and its former parent +window (@pxref{Windows and Frames}) is a deleted window too, this +function will also resurrect the old parent window and insert it into +the appropriate place in the window tree. + +Note that in order to resurrect a deleted window, that window must not +have been recycled by the garbage collector (@pxref{Garbage Collection}) +yet. Hence to make sure that a deleted window can get resurrected, you +should keep a reference to any such window in a variable until this +function returns. + +If @var{refer} is a live window, the new window will inherit properties +like buffer, start and point and some decorations from @var{refer}. If +@var{refer} is @code{nil} or omitted, then if @var{window} is live, any +such properties are inherited from @var{window}. If, however, +@var{window} is an internal window, the new window will inherit these +properties from the window selected on @var{window}'s frame. The behavior of this function may be altered by the window parameters of @var{window}, so long as the variable @@ -7126,13 +7151,16 @@ Window Hooks window. @defun window-old-buffer &optional window -This function returns the buffer shown in @var{window} at the last -time window change functions were run for @var{window}'s frame. If it -returns @code{nil}, @var{window} has been created after that. If it -returns @code{t}, @var{window} was not shown at that time but has been -restored from a previously saved window configuration afterwards. -Otherwise, the return value is the buffer shown by @var{window} at -that time. +This function returns the buffer shown in @var{window} at the last time +window change functions were run for @var{window}'s frame. If it +returns @code{nil}, @var{window} is either an internal window or has +been created after that. If it returns @code{t}, @var{window} was not +shown at that time but has been restored from a previously saved window +configuration afterwards. Otherwise, the return value is the buffer +shown by @var{window} at that time. As a special case, if @var{window} +has been deleted, this function returns the last buffer @var{window} had +shown at that time. @var{window} can be any window and defaults to the +selected one. @end defun @defun window-old-pixel-width &optional window diff --git a/lisp/window.el b/lisp/window.el index b50770cbd7e..2cf2c288452 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -5511,7 +5511,7 @@ window--combination-resizable (setq sibling (window-next-sibling sibling))) (/ size (1+ number)))) -(defun split-window (&optional window size side pixelwise) +(defun split-window (&optional window size side pixelwise refer) "Make a new window adjacent to WINDOW. WINDOW must be a valid window and defaults to the selected one. Return the new window which is always a live window. @@ -5554,11 +5554,30 @@ split-window root of that atomic window. The new window does not become a member of that atomic window. -If WINDOW is live, properties of the new window like margins and -scrollbars are inherited from WINDOW. If WINDOW is an internal -window, these properties as well as the buffer displayed in the -new window are inherited from the window selected on WINDOW's -frame. The selected window is not changed by this function." +If the optional fifth argument REFER is non-nil, it has to denote either +a deleted, former live window on the same frame as WINDOW or an +arbitrary live window. If REFER is a deleted window, do not make a new +window but rather make REFER live again and insert it into the window +tree at the position and with the sizes the new window would have been +given. Buffer, start and point positions of REFER are set to the values +they had immediately before REFER was deleted the last time. +Decorations and parameters remain unaltered from their values before +REFER was deleted. Throw an error if REFER's buffer has been deleted +after REFER itself was deleted. + +If and only if REFER is a deleted window and its previous parent window +is now a deleted window too, do not make a new parent window but reuse +the old aprent window, making it a valid window again. + +If REFER is a live window, the new window will inherit properties like +buffer, start and point and some decorations from REFER. If REFER is +nil or omitted, then if WINDOW is live, any such properties are +inherited from WINDOW. If, however, WINDOW is an internal window, the +new window will inherit these properties from the window selected on +WINDOW's frame. + +The selected window and the selected window on WINDOW's frame are +not changed by this function." (setq window (window-normalize-window window)) (let* ((side (cond ((not side) 'below) @@ -5598,14 +5617,14 @@ split-window ((and (window-parameter window 'window-atom) (setq atom-root (window-atom-root window)) (not (eq atom-root window))) - (throw 'done (split-window atom-root size side pixelwise))) + (throw 'done (split-window atom-root size side pixelwise refer))) ;; If WINDOW's frame has a side window and WINDOW specifies the ;; frame's root window, split the frame's main window instead ;; (Bug#73627). ((and (eq window (frame-root-window frame)) (window-with-parameter 'window-side nil frame)) (throw 'done (split-window (window-main-window frame) - size side pixelwise))) + size side pixelwise refer))) ;; If WINDOW is a side window or its first or last child is a ;; side window, throw an error unless `window-combination-resize' ;; equals 'side. @@ -5644,8 +5663,8 @@ split-window (window-combined-p window horizontal))) ;; 'old-pixel-size' is the current pixel size of WINDOW. (old-pixel-size (window-size window horizontal t)) - ;; 'new-size' is the specified or calculated size of the - ;; new window. + ;; 'new-pixel-size' is the specified or calculated size + ;; of the new window. new-pixel-size new-parent new-normal) (cond ((not pixel-size) @@ -5766,8 +5785,9 @@ split-window window (- (if new-parent 1.0 (window-normal-size window horizontal)) new-normal))) - (let* ((new (split-window-internal window new-pixel-size side new-normal))) - (window--pixel-to-total frame horizontal) + (let ((new (split-window-internal + window new-pixel-size side new-normal refer))) + (window--pixel-to-total frame horizontal) ;; Assign window-side parameters, if any. (cond ((eq window-combination-resize 'side) diff --git a/src/window.c b/src/window.c index 34968ac824f..5254f96ba89 100644 --- a/src/window.c +++ b/src/window.c @@ -652,15 +652,16 @@ DEFUN ("window-buffer", Fwindow_buffer, Swindow_buffer, 0, 1, 0, DEFUN ("window-old-buffer", Fwindow_old_buffer, Swindow_old_buffer, 0, 1, 0, doc: /* Return the old buffer displayed by WINDOW. -WINDOW must be a live window and defaults to the selected one. +WINDOW can be any window and defaults to the selected one. The return value is the buffer shown in WINDOW at the last time window -change functions were run. It is nil if WINDOW was created after -that. It is t if WINDOW has been restored from a window configuration -after that. */) +change functions were run or WINDOW is a former live window that was +deleted. It is nil if WINDOW was created after that. It is t if WINDOW +has been restored from a window configuration after that. It is always +nil if WINDOW is an internal window. */) (Lisp_Object window) { - struct window *w = decode_live_window (window); + struct window *w = decode_any_window (window); return (NILP (w->old_buffer) /* A new window. */ @@ -668,8 +669,8 @@ DEFUN ("window-old-buffer", Fwindow_old_buffer, Swindow_old_buffer, 0, 1, 0, : (w->change_stamp != WINDOW_XFRAME (w)->change_stamp) /* A window restored from a configuration. */ ? Qt - /* A window that was live the last time seen by window - change functions. */ + /* A window that was live the last time seen by window change + functions or was deleted. */ : w->old_buffer); } @@ -4491,45 +4492,6 @@ allocate_window (void) PVEC_WINDOW); } -/* Make new window, have it replace WINDOW in window-tree, and make - WINDOW its only vertical child (HORFLAG means make WINDOW its only - horizontal child). */ -static void -make_parent_window (Lisp_Object window, bool horflag) -{ - Lisp_Object parent; - register struct window *o, *p; - - o = XWINDOW (window); - p = allocate_window (); - memcpy ((char *) p + sizeof (union vectorlike_header), - (char *) o + sizeof (union vectorlike_header), - word_size * VECSIZE (struct window)); - /* P's buffer slot may change from nil to a buffer... */ - adjust_window_count (p, 1); - XSETWINDOW (parent, p); - - p->sequence_number = ++sequence_number; - - replace_window (window, parent, true); - - wset_next (o, Qnil); - wset_prev (o, Qnil); - wset_parent (o, parent); - /* ...but now P becomes an internal window. */ - wset_start (p, Qnil); - wset_pointm (p, Qnil); - wset_old_pointm (p, Qnil); - wset_buffer (p, Qnil); - wset_combination (p, horflag, window); - wset_combination_limit (p, Qnil); - /* Reset any previous and next buffers of p which have been installed - by the memcpy above. */ - wset_prev_buffers (p, Qnil); - wset_next_buffers (p, Qnil); - wset_window_parameters (p, Qnil); -} - /* Make new window from scratch. */ Lisp_Object make_window (void) @@ -5073,7 +5035,7 @@ resize_frame_windows (struct frame *f, int size, bool horflag) } -DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, 4, 4, 0, +DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, 4, 5, 0, doc: /* Split window OLD. Second argument PIXEL-SIZE specifies the number of pixels of the new window. It must be a positive integer. @@ -5088,32 +5050,33 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, the right side of WINDOW. SIDE `left' means the new window shall be located on the left of WINDOW. In both cases PIXEL-SIZE specifies the width of the new window including space reserved for fringes and the -scrollbar or a divider column. +scroll bar or a divider column. Fourth argument NORMAL-SIZE specifies the normal size of the new window -according to the SIDE argument. +according to the SIDE argument. Optional fifth argument REFER is as for +'split-window'. The new pixel and normal sizes of all involved windows must have been set correctly. See the code of `split-window' for how this is done. */) - (Lisp_Object old, Lisp_Object pixel_size, Lisp_Object side, Lisp_Object normal_size) -{ - /* OLD (*o) is the window we have to split. (*p) is either OLD's - parent window or an internal window we have to install as OLD's new - parent. REFERENCE (*r) must denote a live window, or is set to OLD - provided OLD is a leaf window, or to the frame's selected window. - NEW (*n) is the new window created with some parameters taken from - REFERENCE (*r). */ - Lisp_Object new, frame, reference; - struct window *o, *p, *n, *r, *c; - struct frame *f; + (Lisp_Object old, Lisp_Object pixel_size, Lisp_Object side, + Lisp_Object normal_size, Lisp_Object refer) +{ + /* OLD (*o) is the window to split. REFER (*r) is a reference window, + either an arbitrary live window or a former live, now deleted + window on the same frame as OLD. NEW (*n) is the new window + created anew or resurrected from REFER (*r), if specified. *p + refers either to OLD's parent window that will become NEW's parent + window too or to a new internal window that becomes OLD's and NEW's + new parent. */ + struct window *o = decode_valid_window (old); + Lisp_Object frame = WINDOW_FRAME (o); + struct frame *f = XFRAME (frame); + struct window *p, *n, *r, *c; bool horflag /* HORFLAG is true when we split side-by-side, false otherwise. */ = EQ (side, Qt) || EQ (side, Qleft) || EQ (side, Qright); - - CHECK_WINDOW (old); - o = XWINDOW (old); - frame = WINDOW_FRAME (o); - f = XFRAME (frame); + Lisp_Object new, parent = Qnil; + bool dead = false; CHECK_FIXNUM (pixel_size); EMACS_INT total_size @@ -5131,14 +5094,54 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, ? WINDOW_VERTICAL_COMBINATION_P (XWINDOW (o->parent)) : WINDOW_HORIZONTAL_COMBINATION_P (XWINDOW (o->parent)))); - /* We need a live reference window to initialize some parameters. */ - if (WINDOW_LIVE_P (old)) - /* OLD is live, use it as reference window. */ - reference = old; + /* Set up reference window. */ + if (NILP (refer)) + { + if (WINDOW_LIVE_P (old)) + /* OLD is live, use it as reference window. */ + refer = old; + else + /* Use the frame's selected window as reference window. */ + refer = FRAME_SELECTED_WINDOW (f); + + r = XWINDOW (refer); + } else - /* Use the frame's selected window as reference window. */ - reference = FRAME_SELECTED_WINDOW (f); - r = XWINDOW (reference); + { + if (CONSP (refer)) + { + parent = Fcdr (refer); + p = decode_any_window (parent); + refer = Fcar (refer); + + /* Note: PARENT is not necessarily REFER's parent at the time + REFER was deleted but maybe some earlier parent of REFER. */ + if (BUFFERP (p->old_buffer)) + error ("Deleted window in REFER was a live window (did show a buffer)"); + else if (!EQ (p->frame, frame)) + error ("Deleted window in REFER was not on same frame as the window to split"); + } + + r = decode_any_window (refer); + + if (NILP (r->contents)) + /* Presumably a deleted, former live window. Check whether its + contents can be used. */ + { + if (!BUFFERP (r->old_buffer)) + error ("Deleted window in REFER was not a live window (did not show a buffer)"); + else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer))) + error ("The buffer formerly shown by deleted window in REFER has been deleted"); + else if (!EQ (r->frame, frame)) + error ("Deleted window in REFER was not on same frame as the window to split"); + + dead = true; + } + else if (!NILP (parent)) + error ("REFER must not contain live and dead windows"); + else if (!WINDOW_LIVE_P (refer)) + error ("REFER is not a live window (does not show a buffer)"); + } /* The following bugs are caught by `split-window'. */ if (MINI_WINDOW_P (o)) @@ -5178,8 +5181,31 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, Lisp_Object new_normal = horflag ? o->normal_cols : o->normal_lines; - make_parent_window (old, horflag); - p = XWINDOW (o->parent); + /* This is the crux of the old make_parent_window. */ + if (dead && WINDOWP (parent) && !WINDOW_VALID_P (parent)) + /* Reuse REFER's cdr (provided by caller). */ + p = XWINDOW (parent); + else if (dead && WINDOWP (r->parent) && !WINDOW_VALID_P (r->parent)) + /* Reuse REFER's old parent (potentially unsafe). */ + { + parent = r->parent; + p = XWINDOW (parent); + } + else + /* Allocate new parent (classic style). */ + { + p = allocate_window (); + XSETWINDOW (parent, p); + p->sequence_number = ++sequence_number; + wset_frame (p, frame); + } + + replace_window (old, parent, true); + wset_next (o, Qnil); + wset_prev (o, Qnil); + wset_parent (o, parent); + wset_combination (p, horflag, old); + if (EQ (Vwindow_combination_limit, Qt)) /* Store t in the new parent's combination_limit slot to avoid that its children get merged into another window. */ @@ -5195,7 +5221,12 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, p = XWINDOW (o->parent); fset_redisplay (f); - new = make_window (); + + if (dead) + new = refer; + else + new = make_window (); + n = XWINDOW (new); wset_frame (n, frame); wset_parent (n, o->parent); @@ -5222,16 +5253,19 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, n->window_end_valid = false; n->last_cursor_vpos = 0; - /* Get special geometry settings from reference window. */ - n->left_margin_cols = r->left_margin_cols; - n->right_margin_cols = r->right_margin_cols; - n->left_fringe_width = r->left_fringe_width; - n->right_fringe_width = r->right_fringe_width; - n->fringes_outside_margins = r->fringes_outside_margins; - n->scroll_bar_width = r->scroll_bar_width; - n->scroll_bar_height = r->scroll_bar_height; - wset_vertical_scroll_bar_type (n, r->vertical_scroll_bar_type); - wset_horizontal_scroll_bar_type (n, r->horizontal_scroll_bar_type); + if (!dead) + { + /* Get special geometry settings from reference window. */ + n->left_margin_cols = r->left_margin_cols; + n->right_margin_cols = r->right_margin_cols; + n->left_fringe_width = r->left_fringe_width; + n->right_fringe_width = r->right_fringe_width; + n->fringes_outside_margins = r->fringes_outside_margins; + n->scroll_bar_width = r->scroll_bar_width; + n->scroll_bar_height = r->scroll_bar_height; + wset_vertical_scroll_bar_type (n, r->vertical_scroll_bar_type); + wset_horizontal_scroll_bar_type (n, r->horizontal_scroll_bar_type); + } /* Directly assign orthogonal coordinates and sizes. */ if (horflag) @@ -5260,6 +5294,7 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, sum = sum + XFIXNUM (c->new_total); c = NILP (c->next) ? 0 : XWINDOW (c->next); } + wset_new_total (n, make_fixnum ((horflag ? p->total_cols : p->total_lines) @@ -5267,10 +5302,30 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, wset_new_normal (n, normal_size); block_input (); + + if (dead) + { + /* Get dead window back its old buffer and markers. */ + wset_buffer (n, n->old_buffer); + set_marker_restricted + (n->start, make_fixnum (XMARKER (n->start)->charpos), n->contents); + set_marker_restricted + (n->pointm, make_fixnum (XMARKER (n->pointm)->charpos), n->contents); + set_marker_restricted + (n->old_pointm, make_fixnum (XMARKER (n->old_pointm)->charpos), + n->contents); + + Vwindow_list = Qnil; + /* Remove window from the table of dead windows. */ + Fremhash (make_fixnum (n->sequence_number), + window_dead_windows_table); + } + window_resize_apply (p, horflag); adjust_frame_glyphs (f); - /* Set buffer of NEW to buffer of reference window. */ + set_window_buffer (new, r->contents, true, true); + FRAME_WINDOW_CHANGE (f) = true; unblock_input (); @@ -5368,6 +5423,8 @@ DEFUN ("delete-window-internal", Fdelete_window_internal, Sdelete_window_interna } else { + /* Store WINDOW's buffer in old_buffer. */ + wset_old_buffer (w, w->contents); unshow_buffer (w); unchain_marker (XMARKER (w->pointm)); unchain_marker (XMARKER (w->old_pointm)); @@ -7712,6 +7769,8 @@ delete_all_child_windows (Lisp_Object window) } else if (BUFFERP (w->contents)) { + /* Store WINDOW's buffer in old_buffer. */ + wset_old_buffer (w, w->contents); unshow_buffer (w); unchain_marker (XMARKER (w->pointm)); unchain_marker (XMARKER (w->old_pointm)); @@ -9064,12 +9123,9 @@ syms_of_window (void) doc: /* Hash table of dead windows. Each entry in this table maps a window number to a window object. Entries are added by `delete-window-internal' and are removed by the -garbage collector. - -This table is maintained by code in window.c and is made visible in -Elisp for testing purposes only. */); +garbage collector. */); window_dead_windows_table - = CALLN (Fmake_hash_table, QCweakness, Qt); + = CALLN (Fmake_hash_table, QCweakness, Qvalue); defsubr (&Sselected_window); defsubr (&Sold_selected_window);