(feature): update cache on file rename (#124)

This commit is contained in:
Daniel Koudouna
2020-02-20 00:32:54 +00:00
committed by GitHub
parent 2522b9d2fa
commit bdc13cd6bf
4 changed files with 126 additions and 10 deletions

View File

@ -10,6 +10,7 @@
* [#103][gh-103] Change `org-roam-file-format` to a function: `org-roam-file-name-function` to allow more flexible file name customizaton. Also changes `org-roam-use-timestamp-as-filename` to `org-roam-filename-noconfirm` to better describe what it does.
### New Features
* [#124](https://github.com/jethrokuan/org-roam/pull/124) Maintain cache consistency on file rename
* [#87][gh-87], [#90][gh-90] Support encrypted Org files
* [#110][gh-110] Add prefix to `org-roam-insert`, for inserting titles down-cased
* [#99](https://github.com/jethrokuan/org-roam/pull/99) Add keybinding so that `<return>` or `mouse-1` in the backlinks buffer visits the source file of the backlink at point

View File

@ -162,11 +162,12 @@ If called interactively, then PARENTS is non-nil."
(org-roam--build-cache-async)
(user-error "Your Org-Roam cache isn't built yet! Please wait")))
(defun org-roam--org-roam-file-p ()
"Return t if file is part of org-roam system, false otherwise."
(and (buffer-file-name (current-buffer))
(f-descendant-of-p (file-truename (buffer-file-name (current-buffer)))
(file-truename org-roam-directory))))
(defun org-roam--org-roam-file-p (&optional file)
"Return t if FILE is part of org-roam system, defaulting to the name of the current buffer. Else, return nil."
(let ((path (or file
(buffer-file-name (current-buffer)))))
(f-descendant-of-p (file-truename path)
(file-truename org-roam-directory))))
(defun org-roam--get-title-from-cache (file)
"Return title of `FILE' from the cache."
@ -340,11 +341,13 @@ If PREFIX, downcase the title before insertion."
(setq org-roam-backward-links-cache (make-hash-table :test #'equal))
(setq org-roam-titles-cache (make-hash-table :test #'equal)))
(defun org-roam--clear-file-from-cache ()
(defun org-roam--clear-file-from-cache (&optional filepath)
"Remove any related links to the file.
This is equivalent to removing the node from the graph."
(let ((file (file-truename (buffer-file-name (current-buffer)))))
(let* ((path (or filepath
(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)))
;; Delete backlinks to file
@ -589,6 +592,50 @@ This needs to be quick/infrequent, because this is run at
(call-process org-roam-graphviz-executable nil 0 nil temp-dot "-Tsvg" "-o" temp-graph)
(call-process org-roam-graph-viewer nil 0 nil temp-graph)))
(defun org-roam--rename-file-links (file new-file &rest args)
"Rename backlinks of FILE to refer to NEW-FILE."
(when (and (not (auto-save-file-name-p file))
(not (auto-save-file-name-p new-file))
(org-roam--org-roam-file-p new-file))
(org-roam--ensure-cache-built)
(org-roam--clear-file-from-cache file)
(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))
(old-title (format org-roam-link-title-format slug))
(new-slug (or (org-roam--get-title-from-cache path)
(org-roam--get-title-or-slug new-path)))
(new-title (format org-roam-link-title-format new-slug)))
(when files
(maphash (lambda (file-from props)
(let* ((file-dir (file-name-directory file-from))
(relative-path (file-relative-name new-path file-dir))
(old-relative-path (file-relative-name path file-dir))
(slug-regex (regexp-quote (format "[[file:%s][%s]]" old-relative-path old-title)))
(named-regex (concat
(regexp-quote (format "[[file:%s][" old-relative-path))
"\\(.*\\)"
(regexp-quote "]]"))))
(with-temp-file file-from
(insert-file-contents file-from)
(while (re-search-forward slug-regex nil t)
(replace-match (format "[[file:%s][%s]]" relative-path new-title)))
(goto-char (point-min))
(while (re-search-forward named-regex nil t)
(replace-match (format "[[file:%s][\\1]]" relative-path))))
(save-window-excursion
(find-file file-from)
(org-roam--update-cache))))
files))
(save-window-excursion
(find-file new-path)
(org-roam--update-cache)))))
(advice-add 'rename-file :after 'org-roam--rename-file-links)
(provide 'org-roam)
;;; org-roam.el ends here

5
tests/roam-files/f3.org Normal file
View File

@ -0,0 +1,5 @@
#+TITLE: File 3
This file has a link to an file with no title.
[[file:no-title.org][no-title]]

View File

@ -29,6 +29,7 @@
(require 'buttercup)
(require 'with-simulated-input)
(require 'org-roam)
(require 'dash)
(defun abs-path (file-path)
(file-truename (expand-file-name file-path org-roam-directory)))
@ -69,9 +70,9 @@
;; Caches should be populated
(expect org-roam-cache-initialized :to-be t)
(expect (hash-table-count org-roam-forward-links-cache) :to-be 3)
(expect (hash-table-count org-roam-backward-links-cache) :to-be 4)
(expect (hash-table-count org-roam-titles-cache) :to-be 4)
(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))
@ -148,3 +149,65 @@
"Nested SPC File SPC 1 RET"
(org-roam-insert nil))))
(expect (buffer-string) :to-match (regexp-quote "file:../../nested/f1.org"))))
(describe "rename file updates cache"
(before-each
(org-roam--test-init)
(org-roam--clear-cache)
(org-roam--test-build-cache))
(it "f1 -> new_f1"
(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
(gethash (abs-path "nested/f1.org"))
(hash-table-keys)
(member (abs-path "f1.org"))) :to-be nil)
(expect (->> org-roam-forward-links-cache
(gethash (abs-path "new_f1.org"))) :not :to-be nil)
(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
(expect (with-temp-buffer
(insert-file-contents (abs-path "nested/f1.org"))
(buffer-string)) :to-match (regexp-quote "[[file:../new_f1.org][File 1]]")))
(it "f1 -> f1 with spaces"
(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
(gethash (abs-path "nested/f1.org"))
(hash-table-keys)
(member (abs-path "f1.org"))) :to-be nil)
;; Links are updated
(expect (with-temp-buffer
(insert-file-contents (abs-path "nested/f1.org"))
(buffer-string)) :to-match (regexp-quote "[[file:../f1 with spaces.org][File 1]]")))
(it "no-title -> meaningful-title"
(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 (->> 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
(gethash (abs-path "f3.org"))
(member (abs-path "meaningful-title.org"))) :not :to-be nil)
;; Links are updated with the appropriate name
(expect (with-temp-buffer
(insert-file-contents (abs-path "f3.org"))
(buffer-string)) :to-match (regexp-quote "[[file:meaningful-title.org][meaningful-title]]"))))