From b4d89c6a0c90072a3c36f40f3c4e082df11b00c6 Mon Sep 17 00:00:00 2001 From: Herbert Jones Date: Tue, 25 Feb 2020 09:06:40 -0600 Subject: [PATCH] (feature): allow multiple org-roam directories (#178) Each org-roam-directory gets its own cache. One can override the `org-roam-directory` variable locally via dir-locals: ((org-mode . ((eval . (setq-local org-roam-directory (expand-file-name "./")))))) --- org-roam-utils.el | 1 + org-roam.el | 251 ++++++++++++++-------- tests/roam-files-multi/mf1.org | 7 + tests/roam-files-multi/mf2.org | 3 + tests/roam-files-multi/mf3.org | 5 + tests/roam-files-multi/multi-no-title.org | 1 + tests/roam-files-multi/nested/mf1.org | 4 + tests/roam-files-multi/nested/mf2.org | 3 + tests/test-org-roam.el | 241 +++++++++++++++++---- 9 files changed, 389 insertions(+), 127 deletions(-) create mode 100644 tests/roam-files-multi/mf1.org create mode 100644 tests/roam-files-multi/mf2.org create mode 100644 tests/roam-files-multi/mf3.org create mode 100644 tests/roam-files-multi/multi-no-title.org create mode 100644 tests/roam-files-multi/nested/mf1.org create mode 100644 tests/roam-files-multi/nested/mf2.org diff --git a/org-roam-utils.el b/org-roam-utils.el index fdbe8c8..c34b886 100644 --- a/org-roam-utils.el +++ b/org-roam-utils.el @@ -160,6 +160,7 @@ ITEM is of the form: (:from from-path :to to-path :properties (:content preview- (puthash file title file-titles))) org-roam-files)) (list + :directory dir :forward forward-links :backward backward-links :titles file-titles))) diff --git a/org-roam.el b/org-roam.el index 182c822..decea26 100644 --- a/org-roam.el +++ b/org-roam.el @@ -42,6 +42,7 @@ (require 's) (require 'f) (require 'org-roam-utils) +(require 'eieio) ;;; Customizations (defgroup org-roam nil @@ -159,29 +160,77 @@ If called interactively, then PARENTS is non-nil." (make-directory paren-dir parents))) (write-region "" nil filename nil 0))) +;;; Classes + +(defclass org-roam-cache () + ((initialized :initarg :initialized + :documentation "Is cache valid?") + (forward-links :initarg :forward-links + :documentation "Cache containing forward links.") + (backward-links :initarg :backward-links + :documentation "Cache containing backward links.") + (titles :initarg :titles + :documentation "Cache containing titles for org-roam files.")) + "All cache for an org-roam directory.") + ;;; Dynamic variables -(defvar org-roam-cache-initialized nil - "Boolean value indicating whether the cache is initialized.") - -(defvar org-roam-forward-links-cache (make-hash-table :test #'equal) - "Cache containing forward links.") - -(defvar org-roam-backward-links-cache (make-hash-table :test #'equal) - "Cache containing backward-links.") - -(defvar org-roam-titles-cache (make-hash-table :test #'equal) - "Cache containing titles for org-roam files.") - (defvar org-roam--current-buffer nil "Currently displayed file in `org-roam' buffer.") (defvar org-roam-last-window nil "Last window `org-roam' was called from.") +(defvar org-roam--cache nil + "The list of cache separated by directory.") + ;;; Utilities +(defun org-roam-directory-normalized () + "Get the org-roam-directory normalized so that it can be used +as a unique key." + (directory-file-name (file-truename org-roam-directory))) + +(defmacro org-roam--get-local (name) + "Get a variable that is local to the current org-roam-directory." + `(alist-get (org-roam-directory-normalized) ,name nil nil #'equal)) + +(defmacro org-roam--set-local (name value) + "Set a variable that is local to the current org-roam-directory." + `(setf (alist-get (org-roam-directory-normalized) ,name nil nil #'equal) + ,value)) + +(defun org-roam--get-directory-cache () + "Get the cache object for the current org-roam-directory." + (let* ((cache (org-roam--get-local org-roam--cache))) + (if cache + cache + (let ((new-cache (org-roam--default-cache))) + (org-roam--set-local org-roam--cache new-cache) + new-cache)))) + +(defun org-roam--set-directory-cache (data) + "Set the cache object for the current org-roam-directory." + (setf (alist-get (org-roam-directory-normalized) + org-roam--cache nil nil #'equal) data)) + +(defun org-roam--cache-initialized-p () + "Is cache valid?" + (oref (org-roam--get-directory-cache) initialized)) + +(defun org-roam--forward-links-cache () + "Cache containing forward links." + (oref (org-roam--get-directory-cache) forward-links)) + +(defun org-roam--backward-links-cache () + "Cache containing backward links." + (oref (org-roam--get-directory-cache) backward-links)) + +(defun org-roam--titles-cache () + "Cache containing titles for org-roam files." + (oref (org-roam--get-directory-cache) titles)) + (defun org-roam--ensure-cache-built () "Ensures that org-roam cache is built." - (unless org-roam-cache-initialized + (unless (org-roam--cache-initialized-p) (org-roam--build-cache-async) (user-error "Your Org-Roam cache isn't built yet! Please wait"))) @@ -196,9 +245,9 @@ If called interactively, then PARENTS is non-nil." (defun org-roam--get-title-from-cache (file) "Return title of `FILE' from the cache." - (or (gethash file org-roam-titles-cache) + (or (gethash file (org-roam--titles-cache)) (progn - (unless org-roam-cache-initialized + (unless (org-roam--cache-initialized-p) (user-error "The Org-Roam caches aren't built! Please run org-roam--build-cache-async")) nil))) @@ -366,37 +415,44 @@ If PREFIX, downcase the title before insertion." build has completed.") ;;; Building the org-roam cache -(defun org-roam--build-cache-async () +(defun org-roam--build-cache-async (&optional on-success) "Builds the caches asychronously." (interactive) - (unless (and (processp org-roam--ongoing-async-build) - (not (async-ready org-roam--ongoing-async-build))) - (setq org-roam--ongoing-async-build - (async-start - `(lambda () - (setq load-path ',load-path) - (package-initialize) - (require 'org-roam-utils) - ,(async-inject-variables "org-roam-directory") - (cons org-roam-directory (org-roam--build-cache org-roam-directory))) - (lambda (pair) - (let ((directory (car pair)) - (cache (cdr pair))) - (setq org-roam-directory directory ;; ensure dir matches cache - org-roam-forward-links-cache (plist-get cache :forward) - org-roam-backward-links-cache (plist-get cache :backward) - org-roam-titles-cache (plist-get cache :titles) - org-roam-cache-initialized t) - (unless org-roam-mute-cache-build - (message "Org-roam cache built!")))))))) + (let ((existing (org-roam--get-local org-roam--ongoing-async-build))) + (unless (and (processp existing) + (not (async-ready existing))) + (org-roam--set-local + org-roam--ongoing-async-build + (async-start + `(lambda () + (setq load-path ',load-path) + (package-initialize) + (require 'org-roam-utils) + ,(async-inject-variables "org-roam-directory") + (org-roam--build-cache org-roam-directory)) + (lambda (cache) + (let ((org-roam-directory (plist-get cache :directory))) + (org-roam--set-directory-cache + (org-roam-cache :initialized t + :forward-links (plist-get cache :forward) + :backward-links (plist-get cache :backward) + :titles (plist-get cache :titles))) + (unless org-roam-mute-cache-build + (message "Org-roam cache built!")) + (when on-success + (funcall on-success))))))))) (defun org-roam--clear-cache () "Clears all entries in the caches." (interactive) - (setq org-roam-cache-initialized nil) - (setq org-roam-forward-links-cache (make-hash-table :test #'equal)) - (setq org-roam-backward-links-cache (make-hash-table :test #'equal)) - (setq org-roam-titles-cache (make-hash-table :test #'equal))) + (org-roam--set-directory-cache (org-roam--default-cache))) + +(defun org-roam--default-cache () + "A default, uninitialized cache object." + (org-roam-cache :initialized nil + :forward-links (make-hash-table :test #'equal) + :backward-links (make-hash-table :test #'equal) + :titles (make-hash-table :test #'equal))) (defun org-roam--clear-file-from-cache (&optional filepath) "Remove any related links to the file. @@ -406,23 +462,23 @@ This is equivalent to removing the node from the graph." (buffer-file-name (current-buffer)))) (file (file-truename path))) ;; Step 1: Remove all existing links for file - (when-let ((forward-links (gethash file org-roam-forward-links-cache))) + (when-let ((forward-links (gethash file (org-roam--forward-links-cache)))) ;; Delete backlinks to file (dolist (link forward-links) - (when-let ((backward-links (gethash link org-roam-backward-links-cache))) + (when-let ((backward-links (gethash link (org-roam--backward-links-cache)))) (remhash file backward-links) - (puthash link backward-links org-roam-backward-links-cache))) + (puthash link backward-links (org-roam--backward-links-cache)))) ;; Clean out forward links - (remhash file org-roam-forward-links-cache)) + (remhash file (org-roam--forward-links-cache))) ;; Step 2: Remove from the title cache - (remhash file org-roam-titles-cache))) + (remhash file (org-roam--titles-cache)))) (defun org-roam--update-cache-title () "Insert the title of the current buffer into the cache." (when-let ((title (org-roam--extract-title))) (puthash (file-truename (buffer-file-name (current-buffer))) title - org-roam-titles-cache))) + (org-roam--titles-cache)))) (defun org-roam--update-cache () "Update org-roam caches for the current buffer file." @@ -435,8 +491,8 @@ This is equivalent to removing the node from the graph." (dolist (item items) (org-roam--insert-item item - :forward org-roam-forward-links-cache - :backward org-roam-backward-links-cache))) + :forward (org-roam--forward-links-cache) + :backward (org-roam--backward-links-cache)))) ;; Rerender buffer (org-roam--maybe-update-buffer :redisplay t))) @@ -497,42 +553,46 @@ Bindings: (defun org-roam-update (file-path) "Show the backlinks for given org file for file at `FILE-PATH'." - (org-roam--ensure-cache-built) - (let ((buffer-title (org-roam--get-title-or-slug file-path))) - (with-current-buffer org-roam-buffer - ;; Locally overwrite the file opening function to re-use the - ;; last window org-roam was called from - (setq-local - org-link-frame-setup - (cons '(file . org-roam--find-file) org-link-frame-setup)) - (let ((inhibit-read-only t)) - (erase-buffer) - (when (not (eq major-mode 'org-roam-backlinks-mode)) - (org-roam-backlinks-mode)) - (make-local-variable 'org-return-follows-link) - (setq org-return-follows-link t) - (insert - (propertize buffer-title 'font-lock-face 'org-document-title)) - (if-let ((backlinks (gethash file-path org-roam-backward-links-cache))) - (progn - (insert (format "\n\n* %d Backlinks\n" - (hash-table-count backlinks))) - (maphash (lambda (file-from contents) - (insert (format "** [[file:%s][%s]]\n" - file-from - (org-roam--get-title-or-slug file-from))) - (dolist (properties contents) - (let ((content (propertize - (s-trim (s-replace "\n" " " - (plist-get properties :content))) - 'font-lock-face 'org-block - 'help-echo "mouse-1: visit backlinked note" - 'file-from file-from - 'file-from-point (plist-get properties :point)))) - (insert (format "%s \n\n" content))))) - backlinks)) - (insert "\n\n* No backlinks!"))) - (read-only-mode 1)))) + (let* ((source-org-roam-directory org-roam-directory)) + (org-roam--ensure-cache-built) + (let ((buffer-title (org-roam--get-title-or-slug file-path))) + (with-current-buffer org-roam-buffer + ;; When dir-locals.el is used to override org-roam-directory, + ;; org-roam-buffer may have a different local org-roam-directory. + (let ((org-roam-directory source-org-roam-directory)) + ;; Locally overwrite the file opening function to re-use the + ;; last window org-roam was called from + (setq-local + org-link-frame-setup + (cons '(file . org-roam--find-file) org-link-frame-setup)) + (let ((inhibit-read-only t)) + (erase-buffer) + (when (not (eq major-mode 'org-roam-backlinks-mode)) + (org-roam-backlinks-mode)) + (make-local-variable 'org-return-follows-link) + (setq org-return-follows-link t) + (insert + (propertize buffer-title 'font-lock-face 'org-document-title)) + (if-let ((backlinks (gethash file-path (org-roam--backward-links-cache)))) + (progn + (insert (format "\n\n* %d Backlinks\n" + (hash-table-count backlinks))) + (maphash (lambda (file-from contents) + (insert (format "** [[file:%s][%s]]\n" + file-from + (org-roam--get-title-or-slug file-from))) + (dolist (properties contents) + (let ((content (propertize + (s-trim (s-replace "\n" " " + (plist-get properties :content))) + 'font-lock-face 'org-block + 'help-echo "mouse-1: visit backlinked note" + 'file-from file-from + 'file-from-point (plist-get properties :point)))) + (insert (format "%s \n\n" content))))) + backlinks)) + (insert "\n\n* No backlinks!"))) + (read-only-mode 1)))))) ;;; Building the Graphviz graph (defun org-roam-build-graph () @@ -557,7 +617,7 @@ Bindings: (insert (format " \"%s\" -> \"%s\";\n" (org-roam--get-title-or-slug from-link) (org-roam--get-title-or-slug to-link))))) - org-roam-forward-links-cache) + (org-roam--forward-links-cache)) (insert "}") (buffer-string))) @@ -588,6 +648,7 @@ This needs to be quick/infrequent, because this is run at (when (and (or redisplay (not (eq org-roam--current-buffer buffer))) (eq 'visible (org-roam--current-visibility)) + (org-roam--cache-initialized-p) (buffer-local-value 'buffer-file-truename buffer)) (setq org-roam--current-buffer buffer) (org-roam-update (expand-file-name @@ -609,9 +670,21 @@ Applies `org-roam-link-face' if PATH correponds to a Roam file." (defun org-roam--find-file-hook-function () "Called by `find-file-hook' when `org-roam-mode' is on." (when (org-roam--org-roam-file-p) - (org-link-set-parameters "file" :face 'org-roam--roam-link-face) + (setq org-roam-last-window (get-buffer-window)) (add-hook 'post-command-hook #'org-roam--maybe-update-buffer nil t) - (add-hook 'after-save-hook #'org-roam--update-cache nil t))) + (add-hook 'after-save-hook #'org-roam--update-cache nil t) + (if (org-roam--cache-initialized-p) + (org-roam--setup-found-file) + (org-roam--build-cache-async + (let ((buf (buffer-name))) + #'(lambda () + (with-current-buffer buf + (org-roam--setup-found-file)))))))) + +(defun org-roam--setup-found-file () + "Setup a buffer recognized via the \"find-file-hook\"." + (org-link-set-parameters "file" :face 'org-roam--roam-link-face) + (org-roam--maybe-update-buffer :redisplay nil)) (defvar org-roam-mode-map (make-sparse-keymap) @@ -629,7 +702,7 @@ Applies `org-roam-link-face' if PATH correponds to a Roam file." (org-roam--ensure-cache-built) (org-roam--clear-file-from-cache file) - (let* ((files (gethash file org-roam-backward-links-cache nil)) + (let* ((files (gethash file (org-roam--backward-links-cache) nil)) (path (file-truename file)) (new-path (file-truename new-file)) (slug (org-roam--get-title-or-slug file)) @@ -678,7 +751,7 @@ If ARG is `toggle', toggle `org-roam-mode'. Otherwise, behave as if called inter :global t (cond (org-roam-mode - (unless org-roam-cache-initialized + (unless (org-roam--cache-initialized-p) (org-roam--build-cache-async)) (add-hook 'find-file-hook #'org-roam--find-file-hook-function) (advice-add 'rename-file :after #'org-roam--rename-file-advice) diff --git a/tests/roam-files-multi/mf1.org b/tests/roam-files-multi/mf1.org new file mode 100644 index 0000000..c09f809 --- /dev/null +++ b/tests/roam-files-multi/mf1.org @@ -0,0 +1,7 @@ +#+TITLE: Multi-File 1 + +link to [[file:nested/mf1.org][Nested Multi-File 1]] +link to [[file:mf2.org][Multi-File 2]] + +Arbitrary [[https://google.com][HTML]] link +Arbitrary text diff --git a/tests/roam-files-multi/mf2.org b/tests/roam-files-multi/mf2.org new file mode 100644 index 0000000..256b073 --- /dev/null +++ b/tests/roam-files-multi/mf2.org @@ -0,0 +1,3 @@ +#+TITLE: Multi-File 2 + +This file has no links. diff --git a/tests/roam-files-multi/mf3.org b/tests/roam-files-multi/mf3.org new file mode 100644 index 0000000..a43de84 --- /dev/null +++ b/tests/roam-files-multi/mf3.org @@ -0,0 +1,5 @@ +#+TITLE: Multi-File 3 + +This file has a link to an file with no title. + +[[file:multi-no-title.org][multi-no-title]] diff --git a/tests/roam-files-multi/multi-no-title.org b/tests/roam-files-multi/multi-no-title.org new file mode 100644 index 0000000..d751e65 --- /dev/null +++ b/tests/roam-files-multi/multi-no-title.org @@ -0,0 +1 @@ +no title in this file :O diff --git a/tests/roam-files-multi/nested/mf1.org b/tests/roam-files-multi/nested/mf1.org new file mode 100644 index 0000000..e0e7fde --- /dev/null +++ b/tests/roam-files-multi/nested/mf1.org @@ -0,0 +1,4 @@ +#+TITLE: Nested Multi-File 1 + +Link to [[file:mf2.org][Nested Multi-File 2]] +Link to [[file:../mf1.org][Mulit-File 1]] diff --git a/tests/roam-files-multi/nested/mf2.org b/tests/roam-files-multi/nested/mf2.org new file mode 100644 index 0000000..803a17b --- /dev/null +++ b/tests/roam-files-multi/nested/mf2.org @@ -0,0 +1,3 @@ +#+TITLE: Nested Multi-File 2 + +Link to [[file:mf1.org][Nested Multi-File 1]] diff --git a/tests/test-org-roam.el b/tests/test-org-roam.el index 6d74c58..e871497 100644 --- a/tests/test-org-roam.el +++ b/tests/test-org-roam.el @@ -42,6 +42,9 @@ (defvar org-roam--tests-directory (file-truename (concat default-directory "tests/roam-files")) "Directory containing org-roam test org files.") +(defvar org-roam--tests-multi (file-truename (concat default-directory "tests/roam-files-multi")) + "Directory containing org-roam test org files.") + (defun org-roam--test-init () (let ((original-dir org-roam--tests-directory) (new-dir (expand-file-name (make-temp-name "org-roam") temporary-file-directory))) @@ -50,37 +53,52 @@ (setq org-roam-mute-cache-build t)) (org-roam-mode +1)) +(defun org-roam--test-multi-init () + (let ((original-dir-1 org-roam--tests-directory) + (original-dir-2 org-roam--tests-multi) + (new-dir-1 (expand-file-name (make-temp-name "org-roam") temporary-file-directory)) + (new-dir-2 (expand-file-name (make-temp-name "org-roam") temporary-file-directory))) + (copy-directory original-dir-1 new-dir-1) + (copy-directory original-dir-2 new-dir-2) + (setq org-roam-directory new-dir-1) + (setq org-roam-directory2 new-dir-2) + (setq org-roam-mute-cache-build t)) + (org-roam-mode +1)) + (defun org-roam--test-build-cache () "Builds the caches synchronously." (let ((cache (org-roam--build-cache org-roam-directory))) - (setq org-roam-forward-links-cache (plist-get cache :forward)) - (setq org-roam-backward-links-cache (plist-get cache :backward)) - (setq org-roam-titles-cache (plist-get cache :titles)) - (setq org-roam-cache-initialized t))) + (org-roam--set-directory-cache + (org-roam-cache :initialized t + :forward-links (plist-get cache :forward) + :backward-links (plist-get cache :backward) + :titles (plist-get cache :titles))))) ;;; Tests (describe "org-roam--build-cache-async" (it "initializes correctly" (org-roam--test-init) - (expect org-roam-cache-initialized :to-be nil) - (expect (hash-table-count org-roam-forward-links-cache) :to-be 0) - (expect (hash-table-count org-roam-backward-links-cache) :to-be 0) - (expect (hash-table-count org-roam-titles-cache) :to-be 0) + (expect (org-roam--cache-initialized-p) :to-be nil) + (expect (hash-table-count (org-roam--forward-links-cache)) :to-be 0) + (expect (hash-table-count (org-roam--backward-links-cache)) :to-be 0) + (expect (hash-table-count (org-roam--titles-cache)) :to-be 0) (org-roam--build-cache-async) (sleep-for 3) ;; Because it's async ;; Caches should be populated - (expect org-roam-cache-initialized :to-be t) - (expect (hash-table-count org-roam-forward-links-cache) :to-be 4) - (expect (hash-table-count org-roam-backward-links-cache) :to-be 5) - (expect (hash-table-count org-roam-titles-cache) :to-be 5) + (expect (org-roam--cache-initialized-p) :to-be t) + (expect (hash-table-count (org-roam--forward-links-cache)) :to-be 4) + (expect (hash-table-count (org-roam--backward-links-cache)) :to-be 5) + (expect (hash-table-count (org-roam--titles-cache)) :to-be 5) ;; Forward cache - (let ((f1 (gethash (abs-path "f1.org") org-roam-forward-links-cache)) - (f2 (gethash (abs-path "f2.org") org-roam-forward-links-cache)) - (nested-f1 (gethash (abs-path "nested/f1.org") org-roam-forward-links-cache)) - (nested-f2 (gethash (abs-path "nested/f2.org") org-roam-forward-links-cache)) + (let ((f1 (gethash (abs-path "f1.org") (org-roam--forward-links-cache))) + (f2 (gethash (abs-path "f2.org") (org-roam--forward-links-cache))) + (nested-f1 (gethash (abs-path "nested/f1.org") + (org-roam--forward-links-cache))) + (nested-f2 (gethash (abs-path "nested/f2.org") + (org-roam--forward-links-cache))) (expected-f1 (list (abs-path "nested/f1.org") (abs-path "f2.org"))) (expected-nested-f1 (list (abs-path "nested/f2.org") @@ -93,10 +111,14 @@ (expect nested-f2 :to-have-same-items-as expected-nested-f2)) ;; Backward cache - (let ((f1 (hash-table-keys (gethash (abs-path "f1.org") org-roam-backward-links-cache))) - (f2 (hash-table-keys (gethash (abs-path "f2.org") org-roam-backward-links-cache))) - (nested-f1 (hash-table-keys(gethash (abs-path "nested/f1.org") org-roam-backward-links-cache))) - (nested-f2 (hash-table-keys (gethash (abs-path "nested/f2.org") org-roam-backward-links-cache))) + (let ((f1 (hash-table-keys (gethash (abs-path "f1.org") + (org-roam--backward-links-cache)))) + (f2 (hash-table-keys (gethash (abs-path "f2.org") + (org-roam--backward-links-cache)))) + (nested-f1 (hash-table-keys(gethash (abs-path "nested/f1.org") + (org-roam--backward-links-cache)))) + (nested-f2 (hash-table-keys (gethash (abs-path "nested/f2.org") + (org-roam--backward-links-cache)))) (expected-f1 (list (abs-path "nested/f1.org"))) (expected-f2 (list (abs-path "f1.org"))) (expected-nested-f1 (list (abs-path "nested/f2.org") @@ -108,11 +130,153 @@ (expect nested-f2 :to-have-same-items-as expected-nested-f2)) ;; Titles Cache - (expect (gethash (abs-path "f1.org") org-roam-titles-cache) :to-equal "File 1") - (expect (gethash (abs-path "f2.org") org-roam-titles-cache) :to-equal "File 2") - (expect (gethash (abs-path "nested/f1.org") org-roam-titles-cache) :to-equal "Nested File 1") - (expect (gethash (abs-path "nested/f2.org") org-roam-titles-cache) :to-equal "Nested File 2") - (expect (gethash (abs-path "no-title.org") org-roam-titles-cache) :to-be nil))) + (expect (gethash (abs-path "f1.org") (org-roam--titles-cache)) :to-equal "File 1") + (expect (gethash (abs-path "f2.org") (org-roam--titles-cache)) :to-equal "File 2") + (expect (gethash (abs-path "nested/f1.org") (org-roam--titles-cache)) :to-equal "Nested File 1") + (expect (gethash (abs-path "nested/f2.org") (org-roam--titles-cache)) :to-equal "Nested File 2") + (expect (gethash (abs-path "no-title.org") (org-roam--titles-cache)) :to-be nil))) + +(describe "org-roam--build-cache-async-multi" + (it "initializes correctly" + (org-roam--clear-cache) + (org-roam--test-multi-init) + (expect (org-roam--cache-initialized-p) :to-be nil) + (expect (hash-table-count (org-roam--forward-links-cache)) :to-be 0) + (expect (hash-table-count (org-roam--backward-links-cache)) :to-be 0) + (expect (hash-table-count (org-roam--titles-cache)) :to-be 0) + + (org-roam--build-cache-async) + (sleep-for 3) ;; Because it's async + + ;; Caches should be populated + (expect (org-roam--cache-initialized-p) :to-be t) + (expect (hash-table-count (org-roam--forward-links-cache)) :to-be 4) + (expect (hash-table-count (org-roam--backward-links-cache)) :to-be 5) + (expect (hash-table-count (org-roam--titles-cache)) :to-be 5) + + ;; Forward cache + (let ((f1 (gethash (abs-path "f1.org") + (org-roam--forward-links-cache))) + (f2 (gethash (abs-path "f2.org") + (org-roam--forward-links-cache))) + (nested-f1 (gethash (abs-path "nested/f1.org") + (org-roam--forward-links-cache))) + (nested-f2 (gethash (abs-path "nested/f2.org") + (org-roam--forward-links-cache))) + (expected-f1 (list (abs-path "nested/f1.org") + (abs-path "f2.org"))) + (expected-nested-f1 (list (abs-path "nested/f2.org") + (abs-path "f1.org"))) + (expected-nested-f2 (list (abs-path "nested/f1.org")))) + + (expect f1 :to-have-same-items-as expected-f1) + (expect f2 :to-be nil) + (expect nested-f1 :to-have-same-items-as expected-nested-f1) + (expect nested-f2 :to-have-same-items-as expected-nested-f2)) + + ;; Backward cache + (let ((f1 (hash-table-keys (gethash (abs-path "f1.org") + (org-roam--backward-links-cache)))) + (f2 (hash-table-keys (gethash (abs-path "f2.org") + (org-roam--backward-links-cache)))) + (nested-f1 (hash-table-keys + (gethash (abs-path "nested/f1.org") + (org-roam--backward-links-cache)))) + (nested-f2 (hash-table-keys + (gethash (abs-path "nested/f2.org") + (org-roam--backward-links-cache)))) + (expected-f1 (list (abs-path "nested/f1.org"))) + (expected-f2 (list (abs-path "f1.org"))) + (expected-nested-f1 (list (abs-path "nested/f2.org") + (abs-path "f1.org"))) + (expected-nested-f2 (list (abs-path "nested/f1.org")))) + (expect f1 :to-have-same-items-as expected-f1) + (expect f2 :to-have-same-items-as expected-f2) + (expect nested-f1 :to-have-same-items-as expected-nested-f1) + (expect nested-f2 :to-have-same-items-as expected-nested-f2)) + + ;; Titles Cache + (expect (gethash (abs-path "f1.org") + (org-roam--titles-cache)) :to-equal "File 1") + (expect (gethash (abs-path "f2.org") + (org-roam--titles-cache)) :to-equal "File 2") + (expect (gethash (abs-path "nested/f1.org") + (org-roam--titles-cache)) :to-equal "Nested File 1") + (expect (gethash (abs-path "nested/f2.org") + (org-roam--titles-cache)) :to-equal "Nested File 2") + (expect (gethash (abs-path "no-title.org") + (org-roam--titles-cache)) :to-be nil) + + ;; Multi + (let ((org-roam-directory org-roam-directory2)) + (org-roam--build-cache-async) + (sleep-for 3) ;; Because it's async + + ;; Caches should be populated + (expect (org-roam--cache-initialized-p) :to-be t) + (expect (hash-table-count (org-roam--forward-links-cache)) :to-be 4) + (expect (hash-table-count (org-roam--backward-links-cache)) :to-be 5) + (expect (hash-table-count (org-roam--titles-cache)) :to-be 5) + + ;; Forward cache + (let ((mf1 (gethash (abs-path "mf1.org") + (org-roam--forward-links-cache))) + (mf2 (gethash (abs-path "mf2.org") + (org-roam--forward-links-cache))) + (nested-mf1 (gethash (abs-path "nested/mf1.org") + (org-roam--forward-links-cache))) + (nested-mf2 (gethash (abs-path "nested/mf2.org") + (org-roam--forward-links-cache))) + (expected-mf1 (list (abs-path "nested/mf1.org") + (abs-path "mf2.org"))) + (expected-nested-mf1 (list (abs-path "nested/mf2.org") + (abs-path "mf1.org"))) + (expected-nested-mf2 (list (abs-path "nested/mf1.org")))) + + (expect mf1 :to-have-same-items-as expected-mf1) + (expect mf2 :to-be nil) + (expect nested-mf1 :to-have-same-items-as expected-nested-mf1) + (expect nested-mf2 :to-have-same-items-as expected-nested-mf2)) + + ;; Backward cache + (let ((mf1 (hash-table-keys + (gethash (abs-path "mf1.org") + (org-roam--backward-links-cache)))) + (mf2 (hash-table-keys + (gethash (abs-path "mf2.org") + (org-roam--backward-links-cache)))) + (nested-mf1 (hash-table-keys + (gethash (abs-path "nested/mf1.org") + (org-roam--backward-links-cache)))) + (nested-mf2 (hash-table-keys + (gethash (abs-path "nested/mf2.org") + (org-roam--backward-links-cache)))) + (expected-mf1 (list (abs-path "nested/mf1.org"))) + (expected-mf2 (list (abs-path "mf1.org"))) + (expected-nested-mf1 (list (abs-path "nested/mf2.org") + (abs-path "mf1.org"))) + (expected-nested-mf2 (list (abs-path "nested/mf1.org")))) + (expect mf1 :to-have-same-items-as expected-mf1) + (expect mf2 :to-have-same-items-as expected-mf2) + (expect nested-mf1 :to-have-same-items-as expected-nested-mf1) + (expect nested-mf2 :to-have-same-items-as expected-nested-mf2)) + + ;; Titles Cache + (expect (gethash (abs-path "mf1.org") + (org-roam--titles-cache)) + :to-equal "Multi-File 1") + (expect (gethash (abs-path "mf2.org") + (org-roam--titles-cache)) + :to-equal "Multi-File 2") + (expect (gethash (abs-path "nested/mf1.org") + (org-roam--titles-cache)) + :to-equal "Nested Multi-File 1") + (expect (gethash (abs-path "nested/mf2.org") + (org-roam--titles-cache)) + :to-equal "Nested Multi-File 2") + (expect (gethash (abs-path "no-title.org") + (org-roam--titles-cache)) + :to-be nil)))) (describe "org-roam-insert" (before-each @@ -162,16 +326,16 @@ (rename-file (abs-path "f1.org") (abs-path "new_f1.org")) ;; Cache should be cleared of old file - (expect (gethash (abs-path "f1.org") org-roam-forward-links-cache) :to-be nil) - (expect (->> org-roam-backward-links-cache + (expect (gethash (abs-path "f1.org") (org-roam--forward-links-cache)) :to-be nil) + (expect (->> (org-roam--backward-links-cache) (gethash (abs-path "nested/f1.org")) (hash-table-keys) (member (abs-path "f1.org"))) :to-be nil) - (expect (->> org-roam-forward-links-cache + (expect (->> (org-roam--forward-links-cache) (gethash (abs-path "new_f1.org"))) :not :to-be nil) - (expect (->> org-roam-forward-links-cache + (expect (->> (org-roam--forward-links-cache) (gethash (abs-path "new_f1.org")) (member (abs-path "nested/f1.org"))) :not :to-be nil) ;; Links are updated @@ -184,8 +348,8 @@ (rename-file (abs-path "f1.org") (abs-path "f1 with spaces.org")) ;; Cache should be cleared of old file - (expect (gethash (abs-path "f1.org") org-roam-forward-links-cache) :to-be nil) - (expect (->> org-roam-backward-links-cache + (expect (gethash (abs-path "f1.org") (org-roam--forward-links-cache)) :to-be nil) + (expect (->> (org-roam--backward-links-cache) (gethash (abs-path "nested/f1.org")) (hash-table-keys) (member (abs-path "f1.org"))) :to-be nil) @@ -198,14 +362,15 @@ (rename-file (abs-path "no-title.org") (abs-path "meaningful-title.org")) ;; File has no forward links - (expect (gethash (abs-path "no-title.org") org-roam-forward-links-cache) :to-be nil) - (expect (gethash (abs-path "meaningful-title.org") org-roam-forward-links-cache) :to-be nil) + (expect (gethash (abs-path "no-title.org") (org-roam--forward-links-cache)) :to-be nil) + (expect (gethash (abs-path "meaningful-title.org") + (org-roam--forward-links-cache)) :to-be nil) - (expect (->> org-roam-forward-links-cache + (expect (->> (org-roam--forward-links-cache) (gethash (abs-path "f3.org")) (member (abs-path "no-title.org"))) :to-be nil) - (expect (->> org-roam-forward-links-cache + (expect (->> (org-roam--forward-links-cache) (gethash (abs-path "f3.org")) (member (abs-path "meaningful-title.org"))) :not :to-be nil) @@ -221,11 +386,11 @@ (org-roam--test-build-cache)) (it "delete f1" (delete-file (abs-path "f1.org")) - (expect (->> org-roam-forward-links-cache + (expect (->> (org-roam--forward-links-cache) (gethash (abs-path "f1.org"))) :to-be nil) - (expect (->> org-roam-backward-links-cache + (expect (->> (org-roam--backward-links-cache) (gethash (abs-path "nested/f1.org")) (gethash (abs-path "f1.org"))) :to-be nil) - (expect (->> org-roam-backward-links-cache + (expect (->> (org-roam--backward-links-cache) (gethash (abs-path "nested/f1.org")) (gethash (abs-path "nested/f2.org"))) :not :to-be nil)))