unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* savehist and multiple Emacs sessions
@ 2005-12-20 18:55 Stefan Monnier
  2005-12-21  7:52 ` Hrvoje Niksic
  0 siblings, 1 reply; 3+ messages in thread
From: Stefan Monnier @ 2005-12-20 18:55 UTC (permalink / raw)
  Cc: emacs-devel

It turns out that I almost always have 2 Emacs sessions open at the same
time and the longer-lived one is the Gnus one which hence tends to "win"
w.r.t savehist: when I log in the .emacs.d/history file only holds the
history info from my last Gnus session, which lacks a lot of history info.

So I think it's worth it to make savehist.el handle such situations better.
The patch below does just that.  With it, Emacs will automatically notice
when the file has been changed by some other process and reload the history
file, merging the new data without losing the old one.

This also makes it safe (in the sense that it won't lose history data) to
call savehist-load at any point in an Emacs session.

It's only be lightly tested.  It also includes the changes to the minor mode
definition that make it handle Custom setting slightly better and which make
it run the minor mode hook in the same way as all other minor modes defined
by define-minor-mode.


        Stefan


--- orig/lisp/savehist.el
+++ mod/lisp/savehist.el
@@ -170,8 +169,9 @@
 
 (defvar savehist-loaded nil
   "Whether the history has already been loaded.
-This prevents toggling savehist-mode from destroying existing
-minibuffer history.")
+This prevents toggling `savehist-mode' from destroying existing
+minibuffer history.  If non-nil, holds the modtime when the file and
+Emacs's internal variables where last synchronized.")
 
 (when (featurep 'xemacs)
   ;; Must declare this under XEmacs, which doesn't have built-in
@@ -198,53 +198,75 @@
 	  (> (prefix-numeric-value arg) 0)))
   (if (not savehist-mode)
       (savehist-uninstall)
-    (when (and (not savehist-loaded)
-	       (file-exists-p savehist-file))
-      (condition-case errvar
-	  (progn
-	    ;; Don't set coding-system-for-read -- we rely on the
-	    ;; coding cookie to convey that information.  That way, if
-	    ;; the user changes the value of savehist-coding-system,
-	    ;; we can still correctly load the old file.
-	    (load savehist-file nil (not (interactive-p)))
-	    (setq savehist-loaded t))
-	(error
-	 ;; Don't install the mode if reading failed.  Doing so would
-	 ;; effectively destroy the user's data at the next save.
-	 (setq savehist-mode nil)
-	 (savehist-uninstall)
-	 (signal (car errvar) (cdr errvar)))))
-    (savehist-install)
-    (run-hooks 'savehist-mode-hook))
+    (savehist-load)
+    (savehist-install))
+  (if (interactive-p) (customize-mark-as-set 'savehist-mode))
+  (run-hooks 'savehist-mode-hook)
   ;; Return the new setting.
   savehist-mode)
-(add-minor-mode 'savehist-mode "")
+(add-minor-mode 'savehist-mode nil)
 
 (defun savehist-load ()
   "Obsolete function provided for transition from old versions of savehist.
 Don't call this from new code, use (savehist-mode 1) instead.
 
 This function loads the variables stored in `savehist-file' and turns on
-savehist-mode.  If savehist-file is in the old format that doesn't record
+`savehist-mode'.  If savehist-file is in the old format that doesn't record
 the value of `savehist-minibuffer-history-variables', that value is
 deducted from the contents of the file."
-  (savehist-mode 1)
-  ;; Old versions of savehist distributed with XEmacs didn't save
-  ;; savehist-minibuffer-history-variables.  If that variable is nil
-  ;; after loading the file, try to intuit the intended value.
-  (when (null savehist-minibuffer-history-variables)
-    (setq savehist-minibuffer-history-variables
-          (with-temp-buffer
-	    (ignore-errors
-	      (insert-file-contents savehist-file))
-            (let ((vars ()) form)
-              (while (setq form (condition-case nil
-				    (read (current-buffer)) (error nil)))
-		;; Each form read is of the form (setq VAR VALUE).
-		;; Collect VAR, i.e. (nth form 1).
-                (push (nth 1 form) vars))
-              vars)))))
-(make-obsolete 'savehist-load 'savehist-mode)
+  (let ((savehist-old-minibuffer-history-variables
+         (mapcar (lambda (s) (and (boundp s) (cons s (symbol-value s))))
+                 (cons 'savehist-minibuffer-history-variables
+                       savehist-minibuffer-history-variables))))
+    (when (and (file-exists-p savehist-file)
+               (not (equal savehist-loaded
+                           (nth 5 (file-attributes savehist-file)))))
+      (condition-case errvar
+          (progn
+            ;; Don't set coding-system-for-read -- we rely on the
+            ;; coding cookie to convey that information.  That way, if
+            ;; the user changes the value of savehist-coding-system,
+            ;; we can still correctly load the old file.
+            (load savehist-file nil (not (interactive-p)))
+            (setq savehist-loaded (nth 5 (file-attributes savehist-file))))
+        (error
+         ;; Don't install the mode if reading failed.  Doing so would
+         ;; effectively destroy the user's data at the next save.
+         (setq savehist-mode nil)
+         (savehist-uninstall)
+         (signal (car errvar) (cdr errvar)))))
+
+    (when (null savehist-minibuffer-history-variables)
+      ;; Old versions of savehist distributed with XEmacs didn't save
+      ;; savehist-minibuffer-history-variables.  If that variable is nil
+      ;; after loading the file, try to intuit the intended value.
+      (setq savehist-minibuffer-history-variables
+            (with-temp-buffer
+              (ignore-errors
+                (insert-file-contents savehist-file))
+              (let ((vars ()) form)
+                (while (setq form (ignore-errors (read (current-buffer))))
+                  ;; Each form read is of the form (setq VAR VALUE).
+                  ;; Collect VAR, i.e. (nth form 1).
+                  (push (nth 1 form) vars))
+                vars))))
+
+    ;; In case we're loading the file late, there was info in the history
+    ;; variables that may have been overwritten by the info extracted from
+    ;; the file, so put it back in.
+    (dolist (sv savehist-old-minibuffer-history-variables)
+      ;; For each histvar that we knew about, make sure all the entries that
+      ;; were there before are still here now and in the same order.
+      (ignore-errors            ; Maybe some var is not a list or something.
+        (let ((s (car sv))
+              (v (cdr sv))
+              (newv (symbol-value s)))
+          (unless (equal newv v)       ;Simple optimization.
+            (dolist (x v) (setq newv (delete x newv)))
+            (set s (append v newv)))))))
+
+  ;; If it's called the old obsolete way, turn on savehist-mode.
+  (unless savehist-mode (savehist-mode 1)))
 
 (defun savehist-install ()
   "Hook savehist into Emacs.
@@ -283,6 +305,12 @@
 If AUTO-SAVE is non-nil, compare the saved contents to the one last saved,
  and don't save the buffer if they are the same."
   (interactive)
+  (unless (equal savehist-loaded (nth 5 (file-attributes savehist-file)))
+    ;; The file has been changed since last time we touched it.
+    ;; Probably some other Emacs session.  Load the corresponding info so we
+    ;; don't end up throwing it away by blindly overwriting it.  There's
+    ;; still a race-condition, but I don't think it's that important.
+    (savehist-load))
   (with-temp-buffer
     (insert
      (format ";; -*- mode: emacs-lisp; coding: %s -*-\n" savehist-coding-system)
@@ -328,6 +356,7 @@
 			(unless (interactive-p) 'quiet)))
 	(when savehist-file-modes
 	  (set-file-modes savehist-file savehist-file-modes))
+        (setq savehist-loaded (nth 5 (file-attributes savehist-file)))
 	(setq savehist-last-checksum checksum)))))
 
 (defun savehist-autosave ()

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: savehist and multiple Emacs sessions
  2005-12-20 18:55 savehist and multiple Emacs sessions Stefan Monnier
@ 2005-12-21  7:52 ` Hrvoje Niksic
  2005-12-21 17:01   ` Stefan Monnier
  0 siblings, 1 reply; 3+ messages in thread
From: Hrvoje Niksic @ 2005-12-21  7:52 UTC (permalink / raw)
  Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> So I think it's worth it to make savehist.el handle such situations
> better.

I agree.  I don't have time to thoroughly review the patch right now,
but I trust that you got it right.

The only thing I disagree with is the resurrection of `savehist-load'.
The docstring of that function declares it as obsolete, yet you
improve it and call it.  `savehist-load' is supposed to only migrate
the history data of old XEmacs users of savehist, who have
(savehist-load) in their init files.  The new way to load the history
is supposed to be `(savehist-mode 1)'.

BTW with the new semantics of savehist-loaded, you might want to
rename it to savehist-load-time or something like that.

> It's only be lightly tested.  It also includes the changes to the
> minor mode definition that make it handle Custom setting slightly
> better and which make it run the minor mode hook in the same way as
> all other minor modes defined by define-minor-mode.

This breaks savehist under XEmacs and Emacs 21.4, which I use.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: savehist and multiple Emacs sessions
  2005-12-21  7:52 ` Hrvoje Niksic
@ 2005-12-21 17:01   ` Stefan Monnier
  0 siblings, 0 replies; 3+ messages in thread
From: Stefan Monnier @ 2005-12-21 17:01 UTC (permalink / raw)
  Cc: emacs-devel

>> So I think it's worth it to make savehist.el handle such
>> situations better.
> I agree.  I don't have time to thoroughly review the patch right now,
> but I trust that you got it right.

> The only thing I disagree with is the resurrection of `savehist-load'.

Well, the code is long enough to warrant a function of its own, and it
seemed like the most natural name for it.  But if you prefer to keep it
separate, I can call it something else, e.g. savehist-reload.

> BTW with the new semantics of savehist-loaded, you might want to
> rename it to savehist-load-time or something like that.

In that case I'd name it savehist-file-sync-time, since it can also be the
time it was last saved.

>> It's only be lightly tested.  It also includes the changes to the
>> minor mode definition that make it handle Custom setting slightly
>> better and which make it run the minor mode hook in the same way as
>> all other minor modes defined by define-minor-mode.

> This breaks savehist under XEmacs and Emacs 21.4, which I use.

Oh yes, now I remember.  Sorry 'bout that.  This new patch should fix all
the above.


        Stefan


--- orig/lisp/savehist.el
+++ mod/lisp/savehist.el
@@ -168,10 +168,8 @@
 as possible, ideally simply exposing the internal representation of
 buffer text.")
 
-(defvar savehist-loaded nil
-  "Whether the history has already been loaded.
-This prevents toggling savehist-mode from destroying existing
-minibuffer history.")
+(defvar savehist-file-sync-modtime nil
+  "Modtime of the `savehist-file' when we last sync'd up with it.")
 
 (when (featurep 'xemacs)
   ;; Must declare this under XEmacs, which doesn't have built-in
@@ -198,34 +196,21 @@
 	  (> (prefix-numeric-value arg) 0)))
   (if (not savehist-mode)
       (savehist-uninstall)
-    (when (and (not savehist-loaded)
-	       (file-exists-p savehist-file))
-      (condition-case errvar
-	  (progn
-	    ;; Don't set coding-system-for-read -- we rely on the
-	    ;; coding cookie to convey that information.  That way, if
-	    ;; the user changes the value of savehist-coding-system,
-	    ;; we can still correctly load the old file.
-	    (load savehist-file nil (not (interactive-p)))
-	    (setq savehist-loaded t))
-	(error
-	 ;; Don't install the mode if reading failed.  Doing so would
-	 ;; effectively destroy the user's data at the next save.
-	 (setq savehist-mode nil)
-	 (savehist-uninstall)
-	 (signal (car errvar) (cdr errvar)))))
-    (savehist-install)
-    (run-hooks 'savehist-mode-hook))
+    (savehist-reload)
+    (savehist-install))
+  (if (and (fboundp 'customize-mark-as-set) (interactive-p))
+      (customize-mark-as-set 'savehist-mode))
+  (run-hooks 'savehist-mode-hook)
   ;; Return the new setting.
   savehist-mode)
-(add-minor-mode 'savehist-mode "")
+(add-minor-mode 'savehist-mode nil)
 
 (defun savehist-load ()
   "Obsolete function provided for transition from old versions of savehist.
 Don't call this from new code, use (savehist-mode 1) instead.
 
 This function loads the variables stored in `savehist-file' and turns on
-savehist-mode.  If savehist-file is in the old format that doesn't record
+`savehist-mode'.  If `savehist-file' is in the old format that doesn't record
 the value of `savehist-minibuffer-history-variables', that value is
 deducted from the contents of the file."
   (savehist-mode 1)
@@ -238,14 +223,52 @@
 	    (ignore-errors
 	      (insert-file-contents savehist-file))
             (let ((vars ()) form)
-              (while (setq form (condition-case nil
-				    (read (current-buffer)) (error nil)))
+              (while (setq form (ignore-errors (read (current-buffer))))
 		;; Each form read is of the form (setq VAR VALUE).
 		;; Collect VAR, i.e. (nth form 1).
                 (push (nth 1 form) vars))
               vars)))))
 (make-obsolete 'savehist-load 'savehist-mode)
 
+(defun savehist-reload ()
+  "Load the history data from `savehist-file' while preserving the current history data."
+  (let ((savehist-old-minibuffer-history-variables
+         (mapcar (lambda (s) (and (boundp s) (cons s (symbol-value s))))
+                 (cons 'savehist-minibuffer-history-variables
+                       savehist-minibuffer-history-variables))))
+    (when (and (file-exists-p savehist-file)
+               (not (equal savehist-file-sync-modtime
+                           (nth 5 (file-attributes savehist-file)))))
+      (condition-case errvar
+          (progn
+            ;; Don't set coding-system-for-read -- we rely on the
+            ;; coding cookie to convey that information.  That way, if
+            ;; the user changes the value of savehist-coding-system,
+            ;; we can still correctly load the old file.
+            (load savehist-file nil (not (interactive-p)))
+            (setq savehist-file-sync-modtime
+                  (nth 5 (file-attributes savehist-file))))
+        (error
+         ;; Don't install the mode if reading failed.  Doing so would
+         ;; effectively destroy the user's data at the next save.
+         (setq savehist-mode nil)
+         (savehist-uninstall)
+         (signal (car errvar) (cdr errvar)))))
+
+    ;; In case we're loading the file late, there was info in the history
+    ;; variables that may have been overwritten by the info extracted from
+    ;; the file, so put it back in.
+    (dolist (sv savehist-old-minibuffer-history-variables)
+      ;; For each histvar that we knew about, make sure all the entries that
+      ;; were there before are still here now and in the same order.
+      (ignore-errors            ; Maybe some var is not a list or something.
+        (let* ((s (car sv))
+               (v (cdr sv))
+               (newv (symbol-value s)))
+          (unless (equal newv v)       ;Simple optimization.
+            (dolist (x v) (setq newv (delete x newv)))
+            (set s (append v newv))))))))
+
 (defun savehist-install ()
   "Hook savehist into Emacs.
 Normally invoked by calling `savehist-mode' to set the minor mode.
@@ -283,6 +306,13 @@
 If AUTO-SAVE is non-nil, compare the saved contents to the one last saved,
  and don't save the buffer if they are the same."
   (interactive)
+  (unless (equal savehist-file-sync-modtime
+                 (nth 5 (file-attributes savehist-file)))
+    ;; The file has been changed since last time we touched it.
+    ;; Probably some other Emacs session.  Load the corresponding info so we
+    ;; don't end up throwing it away by blindly overwriting it.  There's
+    ;; still a race-condition, but I don't think it's that important.
+    (savehist-reload))
   (with-temp-buffer
     (insert
      (format ";; -*- mode: emacs-lisp; coding: %s -*-\n" savehist-coding-system)
@@ -328,6 +358,8 @@
 			(unless (interactive-p) 'quiet)))
 	(when savehist-file-modes
 	  (set-file-modes savehist-file savehist-file-modes))
+        (setq savehist-file-sync-modtime
+              (nth 5 (file-attributes savehist-file)))
 	(setq savehist-last-checksum checksum)))))
 
 (defun savehist-autosave ()

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2005-12-21 17:01 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-12-20 18:55 savehist and multiple Emacs sessions Stefan Monnier
2005-12-21  7:52 ` Hrvoje Niksic
2005-12-21 17:01   ` Stefan Monnier

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).