branch: elpa/extmap commit 02f1dfeab15e3691eac37561ea2cd93e3b590c9d Author: Paul Pogonyshev <pogonys...@gmail.com> Commit: Paul Pogonyshev <pogonys...@gmail.com>
Make `extmap-init' lazy by default in that it won't even preload extmap metadata. --- extmap.el | 63 ++++++++++++++++++++++++++++++++++------------------- test/extmap-test.el | 41 ++++++++++++++++++++++++---------- 2 files changed, 71 insertions(+), 33 deletions(-) diff --git a/extmap.el b/extmap.el index 5e92cacbd3..20a3f5e241 100644 --- a/extmap.el +++ b/extmap.el @@ -113,11 +113,50 @@ OPTIONS can be a list of the following keyword arguments: developing, when you'd often re-generate disk files, though nothing precludes using it in end-code either. + :preload-metadata + + Immediately parse the header of the extmap file. Otherwise + this is done lazily, on first access. For consistency, it is + still an error if FILENAME points to an unreadable file. + The file must remain accessible in case `extmap-get' needs to load a value later. There is no need to somehow close a map: just stop using it." + (let ((extmap (list (cons filename (if (plist-get options :auto-reload) :auto-reload :not-initialized)) + nil (when (plist-get options :weak-data) (make-hash-table :test #'eq :weakness 'value))))) + (if (plist-get options :preload-metadata) + (extmap--do-reload-if-needed extmap) + ;; Still check that `filename' exists and is readable. + (unless (file-regular-p filename) + (signal 'file-error (list "Not a regular file" filename)))) + extmap)) + +;; 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-then (cdr (nth 0 extmap))) + ;; Fifth element of `file-attributes' result is the modification date. + ;; `file-attribute-modification-time' doesn't exist in Emacs 25. + (modtime-now (nth 5 (file-attributes filename)))) + (cond ((keywordp modtime-then) + ;; Means the map has not been initialized yet. + (progn (extmap--do-initialize extmap) + (setf (cdr (nth 0 extmap)) (when (eq modtime-then :auto-reload) modtime-now)))) + ((not (equal modtime-now modtime-then)) + (let ((reloaded-extmap (extmap-init filename :auto-reload t :weak-data (nth 2 extmap) :preload-metadata t))) + (setf (car extmap) (car reloaded-extmap)) + (setf (cdr extmap) (cdr reloaded-extmap))))))) + +(defun extmap--do-initialize (extmap) (with-temp-buffer - (let* ((header-length (bindat-length extmap--header-bindat-spec nil)) + (let* ((filename (car (nth 0 extmap))) + (header-length (bindat-length extmap--header-bindat-spec nil)) (item-short-header-length (bindat-length extmap--item-short-bindat-spec nil)) (item-header-length (bindat-length extmap--item-bindat-spec nil)) (read-as (insert-file-contents-literally filename nil 0 header-length)) @@ -152,27 +191,7 @@ just stop using it." (unless (= type 0) (error "Corrupted extmap file"))) (puthash key (cons t value) items)))))) - ;; Fifth element of `file-attributes' result is the modification date. - ;; `file-attribute-modification-time' doesn't exist in Emacs 25. - (list (cons filename (when (plist-get options :auto-reload) (nth 5 (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)))) - ;; Fifth element of `file-attributes' result is the modification date. - ;; `file-attribute-modification-time' doesn't exist in Emacs 25. - (unless (equal (nth 5 (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)))))) + (setf (nth 1 extmap) items)))) (defun extmap-get (extmap key &optional no-error) diff --git a/test/extmap-test.el b/test/extmap-test.el index e30f69b975..14cf427b67 100644 --- a/test/extmap-test.el +++ b/test/extmap-test.el @@ -22,23 +22,35 @@ (defconst extmap--test-directory (file-name-directory (or load-file-name (buffer-file-name)))) -(defvar extmap--test-filename nil) +(defun extmap--test-file (&optional filename) + (expand-file-name (or filename "test.extmap") extmap--test-directory)) (defun extmap--test-alist (data &rest options) - (let ((filename (concat extmap--test-directory (or extmap--test-filename "test.extmap")))) + (let ((filename (extmap--test-file)) + extmap) (apply #'extmap-from-alist filename data :overwrite t options) - (let ((extmap (extmap-init filename))) - (should (equal (sort (mapcar #'car data) #'string<) (sort (extmap-keys extmap) #'string<))) - (dolist (entry data) - (should (extmap-contains-key extmap (car entry))) - (should (extmap--equal-including-properties (extmap-get extmap (car entry)) (cdr entry))) - (should (extmap-value-loaded extmap (car entry)))) - extmap))) + ;; By default, test both without and with preloading metadata. + (dolist (preload-metadata (or (plist-get options :preload-metadata) '(nil t))) + (setq extmap (extmap--do-test-alist filename data preload-metadata))) + extmap)) + +(defun extmap--do-test-alist (filename data preload-metadata) + (let ((extmap (extmap-init filename :preload-metadata preload-metadata))) + ;; It's fine to access internals in our own tests. + (if preload-metadata + (should (hash-table-p (nth 1 extmap))) + (should (null (nth 1 extmap)))) + (should (equal (sort (mapcar #'car data) #'string<) (sort (extmap-keys extmap) #'string<))) + (dolist (entry data) + (should (extmap-contains-key extmap (car entry))) + (should (extmap--equal-including-properties (extmap-get extmap (car entry)) (cdr entry))) + (should (extmap-value-loaded extmap (car entry)))) + extmap)) (defun extmap--test-compare (data1 data2 &optional keys-to-ignore &rest options) - (let* ((filename1 (concat extmap--test-directory (or extmap--test-filename "test1.extmap"))) - (filename2 (concat extmap--test-directory (or extmap--test-filename "test2.extmap")))) + (let* ((filename1 (extmap--test-file "test1.extmap")) + (filename2 (extmap--test-file "test2.extmap"))) (apply #'extmap-from-alist filename1 data1 :overwrite t options) (apply #'extmap-from-alist filename2 data2 :overwrite t options) (extmap-equal-p filename1 filename2 keys-to-ignore))) @@ -102,6 +114,13 @@ (should (eq (nth 0 foo) (nth 1 foo))))) +(ert-deftest extmap-init-signals-for-non-existing-file () + (should-error (extmap-init (extmap--test-file "this-file-shouldnt-exist-or-test-will-fail.extmap")) + :type 'file-error) + (should-error (extmap-init (extmap--test-file "this-file-shouldnt-exist-or-test-will-fail.extmap") :preload-metadata t) + :type 'file-error)) + + (ert-deftest extmap-equal-p-1 () (should (extmap--test-compare `((foo . 1)) `((foo . 1))))