(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 "./"))))))
This commit is contained in:
Herbert Jones
2020-02-25 09:06:40 -06:00
committed by GitHub
parent d780b6ffd1
commit b4d89c6a0c
9 changed files with 389 additions and 127 deletions

View File

@ -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)))

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,3 @@
#+TITLE: Multi-File 2
This file has no links.

View File

@ -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]]

View File

@ -0,0 +1 @@
no title in this file :O

View File

@ -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]]

View File

@ -0,0 +1,3 @@
#+TITLE: Nested Multi-File 2
Link to [[file:mf1.org][Nested Multi-File 1]]

View File

@ -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)))