mirror of
https://github.com/org-roam/org-roam
synced 2025-08-01 12:17:21 -05:00
(feature): update cache on file rename (#124)
This commit is contained in:
@ -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
|
||||
|
61
org-roam.el
61
org-roam.el
@ -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
5
tests/roam-files/f3.org
Normal 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]]
|
@ -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]]"))))
|
||||
|
Reference in New Issue
Block a user