branch: elpa/extmap
commit eabe5f6f267d4e9e2cd0aed1d175302be1d53dcb
Author: Paul Pogonyshev <pogonys...@gmail.com>
Commit: Paul Pogonyshev <pogonys...@gmail.com>

    Implement auto-reloading maps, largely to simplify development for users of 
the package.
---
 extmap.el | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 54 insertions(+), 10 deletions(-)

diff --git a/extmap.el b/extmap.el
index c0c3557068..c85a685be8 100644
--- a/extmap.el
+++ b/extmap.el
@@ -55,17 +55,37 @@
                                            (offset    u32)))
 
 
-(defun extmap-init (filename &optional weak-data)
+(defun extmap-init (filename &rest options)
   "Load metadata of a previously created map from FILENAME.
 
 Loaded metadata can be further passed to `extmap-get' and other
 functions.  It must be treated as an opaque object: you must not
 alter it or make any assumptions about its contents.
 
-If WEAK-DATA is non-nil, loaded values are stored in a weak
-hashmap and can be garbage-collected by Emacs if no longer used.
-This allows to potentially reduce memory usage at the cost of
-more disk operations.
+OPTIONS can be a list of the following keyword arguments:
+
+  :weak-data
+
+    If non-nil, loaded values are stored in a weak hashmap and
+    can be garbage-collected by Emacs if no longer used.  This
+    allows to potentially reduce memory usage at the cost of more
+    disk operations.
+
+  :auto-reload
+
+    If the backing file is changed, automatically reset the map.
+    By default, backing file is supposed to remain constant and
+    if it changes, that results in undefined map behavior.
+
+    Reloading doesn't affect any already retrieved values.
+    Backing file is considered changed only if its modification
+    time is different compared to the previous check, actual
+    contents is not checked.
+
+    Using this option slows a map down a little, since it has to
+    check file modification time often.  It exists largely for
+    developing, when you'd often re-generate disk files, though
+    nothing precludes using it in end-code either.
 
 The file must remain accessible in case `extmap-get' needs to
 load a value later.  There is no need to somehow close a map:
@@ -101,7 +121,24 @@ just stop using it."
             (let ((item-header (bindat-unpack extmap--item-bindat-spec 
(encode-coding-string (buffer-substring-no-properties (point) (+ (point) 
item-header-length)) 'no-conversion))))
               (goto-char (+ (point) item-header-length))
               (puthash key (cons nil (cons type (cons (bindat-get-field 
item-header 'offset) length))) items)))))
-      (list filename items (when weak-data (make-hash-table :test #'eq 
:weakness 'value))))))
+      (list (cons filename (when (plist-get options :auto-reload) 
(file-attribute-modification-time (file-attributes filename))))
+            items (when (plist-get options :weak-data) (make-hash-table :test 
#'eq :weakness 'value))))))
+
+;; After a call to this, any value in `extmap' other than the filename
+;; might change.
+(defsubst extmap--reload-if-needed (extmap)
+  (let ((modtime (cdr (nth 0 extmap))))
+    (when modtime
+      (extmap--do-reload-if-needed extmap))))
+
+(defun extmap--do-reload-if-needed (extmap)
+  (let ((filename (car (nth 0 extmap)))
+        (modtime  (cdr (nth 0 extmap))))
+    (unless (equal (file-attribute-modification-time (file-attributes 
filename)) modtime)
+      (let ((reloaded-extmap (extmap-init filename :auto-reload t :weak-data 
(nth 2 extmap))))
+        (setcar extmap (car reloaded-extmap))
+        (setcdr extmap (cdr reloaded-extmap))))))
+
 
 (defun extmap-get (extmap key &optional no-error)
   "Get value associated with KEY from the map.
@@ -110,6 +147,7 @@ EXTMAP must be a result of a previous call to 
`extmap-init'.  KEY
 should be a symbol present in the map.  If it is not, function
 signals an error, unless NO-ERROR is specified, in which case it
 returns nil."
+  (extmap--reload-if-needed extmap)
   (let* ((items     (nth 1 extmap))
          (weak-data (nth 2 extmap))
          ;; A key cannot be mapped to `items' table itself, we use
@@ -127,7 +165,7 @@ returns nil."
                      (let ((coding-system-for-read 'utf-8)
                            (offset                 (cadr (cdr value))))
                        (with-temp-buffer
-                         (insert-file-contents (nth 0 extmap) nil offset (+ 
offset (cddr (cdr value))))
+                         (insert-file-contents (car (nth 0 extmap)) nil offset 
(+ offset (cddr (cdr value))))
                          (let ((new-value (if (= (cadr value) 2) 
(buffer-string) (read (current-buffer)))))
                            (if weak-data
                                (puthash key new-value weak-data)
@@ -139,6 +177,7 @@ returns nil."
 
 (defun extmap-contains-key (extmap key)
   "Determine if there is a mapping for given KEY in EXTMAP."
+  (extmap--reload-if-needed extmap)
   (consp (gethash key (nth 1 extmap))))
 
 (defun extmap-value-loaded (extmap key)
@@ -146,9 +185,10 @@ returns nil."
 If there is no mapping for KEY, this function always returns
 nil.
 
-In case the map has been initialized with WEEK-DATA argument, it
+In case the map has been initialized with `:weak-data' option, it
 may happen that this function returns t, but value for the KEY
 has to be loaded again in the future."
+  (extmap--reload-if-needed extmap)
   (or (car-safe (gethash key (nth 1 extmap)))
       (let ((weak-data (nth 2 extmap)))
         (when weak-data
@@ -159,6 +199,7 @@ has to be loaded again in the future."
 The list is in no particular order.
 
 EXTMAP must be a result of a previous call to `extmap-init'."
+  (extmap--reload-if-needed extmap)
   (let (keys)
     (maphash (lambda (key _value) (push key keys)) (nth 1 extmap))
     keys))
@@ -176,6 +217,7 @@ Note that unless CALLBACK exits non-locally (with `throw' 
or by
 signalling an error), this will result in loading all values into
 memory.  If you just need to enumerate the keys, use
 `extmap-keys' instead."
+  (extmap--reload-if-needed extmap)
   (maphash (lambda (key _value) (funcall callback key (extmap-get extmap 
key))) (nth 1 extmap)))
 
 (defun extmap-mapcar (extmap callback)
@@ -186,6 +228,7 @@ Returned list corresponds to the order in which keys have 
been
 passed to CALLBACK.  However, that order can be arbitrary.
 
 See `extmap-mapc' for more information."
+  (extmap--reload-if-needed extmap)
   (let (result)
     (maphash (lambda (key _value) (push (funcall callback key (extmap-get 
extmap key)) result)) (nth 1 extmap))
     (nreverse result)))
@@ -203,14 +246,15 @@ the following items:
 In some cases maps can report loaded values right after
 initialization.  This is because of value inlining and typically
 happens for small values.  In case the map has been initialized
-with WEEK-DATA argument, `num-loaded' should be seen as an upper
+with `:weak-data' option, `num-loaded' should be seen as an upper
 limit only, as (some) loaded values can be garbage-collected at
 any time."
+  (extmap--reload-if-needed extmap)
   (let ((items      (nth 1 extmap))
         (weak-data  (nth 2 extmap))
         (num-loaded 0))
     (maphash (lambda (_key value) (when (car value) (setq num-loaded (1+ 
num-loaded)))) items)
-    `((filename   . ,(nth 0 extmap))
+    `((filename   . ,(car (nth 0 extmap)))
       (num-items  . ,(hash-table-count items))
       (num-loaded . ,(+ num-loaded (if weak-data (hash-table-count weak-data) 
0))))))
 

Reply via email to