Subject: Re: bug#10348: 24.0.92; Save and load window states

From: martin rudalics <[email-masked]>
Date: Thu, 29 Dec 2011 12:39:13 +0100

> I don't understand: you first seem to say you disagree with me, but then
 > what you say afterwards seems to agree with me.

Indeed.

 > Indeed, but otherwise, as you pointed out, it makes the semantics rather
 > tricky if window-persistent-parameters is changed between the save and
 > the restore (which is something that is very likely to happen sometimes
 > since the restore may happen years after the save).

I resolved the semantics by saving always all parameters unless I do
`window-state-get' with IGNORE non-nil and the parameter is in
`window-state-ignored-parameters'.  When restoring parameters via
`set-window-configuration'/`window-state-put', I overwrite/install a
value only if the parameter is in `window-persistent-parameters'.

martin
=== modified file 'doc/lispref/windows.texi'
--- doc/lispref/windows.texi	2011-12-13 13:37:48 +0000
+++ doc/lispref/windows.texi	2011-12-29 10:34:38 +0000
@@ -3104,7 +3104,7 @@
 @defun current-window-configuration &optional frame
 This function returns a new object representing @var{frame}'s current
 window configuration.  The default for @var{frame} is the selected
-frame.
+frame.  This function copies the value of all window parameters.
 @end defun
 
 @defun set-window-configuration configuration
@@ -3121,6 +3121,9 @@
 know how to tell whether the new configuration actually differs from the
 old one.
 
+This function restores the values of all window parameters listed by
+@code{window-persistent-parameters}, see below.
+
 If the frame which @var{configuration} was saved from is dead, all this
 function does is restore the three variables @code{window-min-height},
 @code{window-min-width} and @code{minibuffer-scroll-window}. In this
@@ -3140,6 +3143,18 @@
 @end example
 @end defun
 
+@defvar window-persistent-parameters
+This variable lists all window parameters that are restored by
+@code{set-window-configuration}, see above.  This means that the value
+of any parameter listed by this variable and changed within the body of
+@code{save-window-excursion} is restored to its previous value when the
+window excursion exits.  Parameters not listed by this variable are left
+alone when the window excursion terminates.
+
+Parameters not listed by this variable are effectively not installed by
+the function @code{window-state-put}, see below.
+@end defvar
+
 @defspec save-window-excursion forms@dots{}
 This special form records the window configuration, executes @var{forms}
 in sequence, then restores the earlier window configuration.  The window
@@ -3209,18 +3224,25 @@
 configuration on disk and read it back in another Emacs session the
 following two functions can be used.
 
-@defun window-state-get &optional window markers
+@defun window-state-get &optional window ignore
 This function returns the state of @var{window} as a Lisp object.  The
 argument @var{window} can be any window and defaults to the root window
 of the selected frame.
 
-The optional argument @var{markers} non-@code{nil} means to use markers
-for sampling positions like @code{window-point} or @code{window-start}.
-This argument should be non-@code{nil} only if the value is used for
-putting the state back in the same session since markers slow down
-processing.
+If the optional argument @var{ignore} is non-@code{nil}, this means to
+not use markers for sampling positions like @code{window-point} or
+@code{window-start} and to not save values of parameters in the list
+@code{window-state-ignored-parameters}, see below.  This argument should
+be non-@code{nil} when the state shall be written on disk and read back
+in another session.
 @end defun
 
+@defvar window-state-ignored-parameters
+This variable lists all parameters whose value is not recorded by the
+function @code{window-state-get} when its @var{ignore} argument is
+non-@code{nil}, see above.
+@end defvar
+
 The value returned by @code{window-state-get} can be converted by using
 one of the functions defined by Desktop Save Mode (@pxref{Desktop Save
 Mode}) to an object that can be written to a file.  Such objects can be
@@ -3239,6 +3261,10 @@
 minimum window sizes and fixed size restrictions.  If @var{ignore}
 equals @code{safe}, this means windows can get as small as one line
 and/or two columns.
+
+This function installs the values of all window parameters listed by
+@code{window-persistent-parameters}, see above, provided their values
+are stored in @var{state}.
 @end defun

=== modified file 'lisp/window.el'
--- lisp/window.el	2011-12-24 19:16:53 +0000
+++ lisp/window.el	2011-12-29 10:11:05 +0000
@@ -3568,10 +3568,12 @@
     ))
 
 ;;; Window states, how to get them and how to put them in a window.
-(defvar window-state-ignored-parameters '(quit-restore)
-  "List of window parameters ignored by `window-state-get'.")
+(defvar window-state-ignored-parameters '(quit-restore clone-of)
+  "Window parameters ignored by `window-state-get'.
+Parameters in this list are not saved by `window-state-get' when
+its IGNORE argument is non-nil.")
 
-(defun window--state-get-1 (window &optional markers)
+(defun window--state-get-1 (window &optional ignore)
   "Helper function for `window-state-get'."
   (let* ((type
 	  (cond
@@ -3590,11 +3592,15 @@
             (combination-limit . ,(window-combination-limit window))
             ,@(let (list)
                 (dolist (parameter (window-parameters window))
-                  (unless (memq (car parameter)
-                                window-state-ignored-parameters)
-                    (setq list (cons parameter list))))
-                (unless (window-parameter window 'clone-of)
-                  ;; Make a clone-of parameter.
+		  ;; When IGNORE is non-nil, add a parameter if and only
+		  ;; if it is not in `window-state-ignored-parameters'.
+		  (unless (and ignore
+			       (memq (car parameter)
+				     window-state-ignored-parameters))
+                    (setq list (cons (cons (car parameter) (cdr parameter))
+				     list))))
+                (unless (or ignore (window-parameter window 'clone-of))
+                  ;; Make `clone-of' parameter unless IGNORE is non-nil.
                   (setq list (cons (cons 'clone-of window) list)))
                 (when list
                   `((parameters . ,list))))
@@ -3616,30 +3622,32 @@
                        (scroll-bars . ,(window-scroll-bars window))
                        (vscroll . ,(window-vscroll window))
                        (dedicated . ,(window-dedicated-p window))
-                       (point . ,(if markers (copy-marker point) point))
-                       (start . ,(if markers (copy-marker start) start))
+                       (point . ,(if ignore point (copy-marker point)))
+                       (start . ,(if ignore start (copy-marker start)))
                        ,@(when mark
-                           `((mark . ,(if markers
-                                          (copy-marker mark) mark)))))))))))
+                           `((mark . ,(if ignore
+                                          mark (copy-marker mark))))))))))))
 	 (tail
 	  (when (memq type '(vc hc))
 	    (let (list)
 	      (setq window (window-child window))
 	      (while window
-		(setq list (cons (window--state-get-1 window markers) list))
+		(setq list (cons (window--state-get-1 window ignore) list))
 		(setq window (window-right window)))
 	      (nreverse list)))))
     (append head tail)))
 
-(defun window-state-get (&optional window markers)
+(defun window-state-get (&optional window ignore)
   "Return state of WINDOW as a Lisp object.
 WINDOW can be any window and defaults to the root window of the
 selected frame.
 
-Optional argument MARKERS non-nil means use markers for sampling
-positions like `window-point' or `window-start'.  MARKERS should
-be non-nil only if the value is used for putting the state back
-in the same session (note that markers slow down processing).
+Optional argument IGNORE non-nil means do not use markers for
+sampling positions like `window-point' or `window-start' and do
+not record ignored window parameters as specified by
+`window-state-ignored-parameters'.  IGNORE should be non-nil when
+the return value shall be written to a file and read back in
+another session.
 
 The return value can be used as argument for `window-state-put'
 to put the state recorded here into an arbitrary window.  The
@@ -3665,7 +3673,7 @@
      ;; These are probably not needed.
      ,@(when (window-size-fixed-p window) `((fixed-height . t)))
      ,@(when (window-size-fixed-p window t) `((fixed-width . t))))
-   (window--state-get-1 window markers)))
+   (window--state-get-1 window ignore)))
 
 (defvar window-state-put-list nil
   "Helper variable for `window-state-put'.")
@@ -3744,10 +3752,15 @@
 	  (state (cdr (assq 'buffer item))))
       (when combination-limit
 	(set-window-combination-limit window combination-limit))
-      ;; Process parameters.
+      ;; nil out values of parameters in `window-persistent-parameters'.
+      (dolist (parameter (window-parameters window))
+	(when (memq (car parameter) window-persistent-parameters)
+	  (set-window-parameter window (car parameter) nil)))
+      ;; Assign persistent window parameters.
       (when parameters
 	(dolist (parameter parameters)
-	  (set-window-parameter window (car parameter) (cdr parameter))))
+	  (when (memq (car parameter) window-persistent-parameters)
+	    (set-window-parameter window (car parameter) (cdr parameter)))))
       ;; Process buffer related state.
       (when state
 	;; We don't want to raise an error here so we create a buffer if

=== modified file 'src/window.c'
--- src/window.c	2011-12-13 13:58:20 +0000
+++ src/window.c	2011-12-29 11:21:42 +0000
@@ -5349,6 +5349,7 @@
   (Lisp_Object configuration)
 {
   register struct save_window_data *data;
+  register Lisp_Object tem, car;
   struct Lisp_Vector *saved_windows;
   Lisp_Object new_current_buffer;
   Lisp_Object frame;
@@ -5543,7 +5544,26 @@
 	  w->vertical_scroll_bar_type = p->vertical_scroll_bar_type;
 	  w->dedicated = p->dedicated;
 	  w->combination_limit = p->combination_limit;
-	  w->window_parameters = p->window_parameters;
+	  /* nil out values of persistent window parameters.  */
+	  if (!NILP (w->window_parameters))
+	    for (tem = w->window_parameters; CONSP (tem); tem = XCDR (tem))
+	      {
+		car = XCAR (tem);
+		if (CONSP (car)
+		    && !NILP (Fmemq (XCAR (car), Vwindow_persistent_parameters)))
+		  Fsetcdr (car, Qnil);
+	      }
+
+	  /* Restore persistent window parameters.  */
+	  if (!NILP (p->window_parameters))
+	    for (tem = p->window_parameters; CONSP (tem); tem = XCDR (tem))
+	      {
+		car = XCAR (tem);
+		if (CONSP (car)
+		    && !NILP (Fmemq (XCAR (car), Vwindow_persistent_parameters)))
+		  Fset_window_parameter (window, XCAR (car), XCDR (car));
+	      }
+
 	  XSETFASTINT (w->last_modified, 0);
 	  XSETFASTINT (w->last_overlay_modified, 0);
 
@@ -5810,7 +5830,7 @@
 {
   register struct saved_window *p;
   register struct window *w;
-  register Lisp_Object tem;
+  register Lisp_Object tem, car;
 
   for (;!NILP (window); window = w->next)
     {
@@ -5838,7 +5858,11 @@
       p->vertical_scroll_bar_type = w->vertical_scroll_bar_type;
       p->dedicated = w->dedicated;
       p->combination_limit = w->combination_limit;
-      p->window_parameters = w->window_parameters;
+      /* Store copies of all window parameters.  When
+	 Fset_window_configuration later considers a parameter as
+	 persistent it will take it from the copy stored here.  */
+      p->window_parameters = Fcopy_alist (w->window_parameters);
+
       if (!NILP (w->buffer))
 	{
 	  /* Save w's value of point in the window configuration.
@@ -6542,6 +6566,15 @@
 function `set-window-combination-limit'.  */);
   Vwindow_combination_limit = Qnil;
 
+  DEFVAR_LISP ("window-persistent-parameters", Vwindow_persistent_parameters,
+	       doc: /* List of persistent window parameters.
+
+The parameters in this list are restored to their previous values by the
+functions `set-window-configuration' and `window-state-put'.  Any
+parameterts not listed here are left alone by `set-window-configuration'
+respectively not installed by `window-state-put'.  */);
+  Vwindow_persistent_parameters = Qnil;
+
   defsubr (&Sselected_window);
   defsubr (&Sminibuffer_window);
   defsubr (&Swindow_minibuffer_p);