(feat): rename link descriptions on title changes (#1071)

Previously the rename file advice was responsible for trying to update
the link descriptions as well. This was brittle and didn't work in some
cases.

The rename-file-advice has now been altered to solely deal with breaking
links. We use the before and after-save-hooks to determine whether the
title has changed, and update the link descriptions accordingly.
This commit is contained in:
Jethro Kuan
2020-08-26 17:24:58 +08:00
committed by GitHub
parent fe5566c0dc
commit b8b180d60d
2 changed files with 63 additions and 40 deletions

View File

@@ -10,6 +10,7 @@ In this release we support fuzzy links of the form `[[Title]]`, `[[*Headline]]`
### Features ### Features
- [#1071](https://github.com/org-roam/org-roam/pull/1071) Update link descriptions on title changes, and clean-up rename file advice
- [#1061](https://github.com/org-roam/org-roam/pull/1061) Speed up the extraction of file properties, headlines, and titles - [#1061](https://github.com/org-roam/org-roam/pull/1061) Speed up the extraction of file properties, headlines, and titles
- [#1046](https://github.com/org-roam/org-roam/pull/1046) New user option to exclude files from Org-roam - [#1046](https://github.com/org-roam/org-roam/pull/1046) New user option to exclude files from Org-roam
- [#1032](https://github.com/org-roam/org-roam/pull/1032) File changes are now propagated to the database on idle timer. This prevents large wait times during file saves. - [#1032](https://github.com/org-roam/org-roam/pull/1032) File changes are now propagated to the database on idle timer. This prevents large wait times during file saves.

View File

@@ -1472,6 +1472,7 @@ during the next idle slot."
(when (org-roam--org-roam-file-p) (when (org-roam--org-roam-file-p)
(setq org-roam-last-window (get-buffer-window)) (setq org-roam-last-window (get-buffer-window))
(run-hooks 'org-roam-file-setup-hook) ; Run user hooks (run-hooks 'org-roam-file-setup-hook) ; Run user hooks
(org-roam--setup-title-auto-update)
(add-hook 'post-command-hook #'org-roam-buffer--update-maybe nil t) (add-hook 'post-command-hook #'org-roam-buffer--update-maybe nil t)
(add-hook 'before-save-hook #'org-roam--replace-fuzzy-link-on-save nil t) (add-hook 'before-save-hook #'org-roam--replace-fuzzy-link-on-save nil t)
(add-hook 'after-save-hook #'org-roam--queue-file-for-update nil t) (add-hook 'after-save-hook #'org-roam--queue-file-for-update nil t)
@@ -1484,33 +1485,30 @@ during the next idle slot."
(org-roam--org-roam-file-p file)) (org-roam--org-roam-file-p file))
(org-roam-db--clear-file (file-truename file)))) (org-roam-db--clear-file (file-truename file))))
(defun org-roam--replace-link (file old-path new-path &optional old-desc new-desc) (defun org-roam--replace-link (old-path new-path &optional old-desc new-desc)
"Replace Org-roam file links in FILE with path OLD-PATH to path NEW-PATH. "Replace Org-roam file links with path OLD-PATH to path NEW-PATH.
If OLD-DESC is passed, and is not the same as the link If OLD-DESC is passed, and is not the same as the link
description, it is assumed that the user has modified the description, it is assumed that the user has modified the
description, and the description will not be updated. Else, description, and the description will not be updated. Else,
update with NEW-DESC." update with NEW-DESC."
(with-current-buffer (or (find-buffer-visiting file) (save-excursion
(find-file-noselect file)) (goto-char (point-min))
(save-excursion (while (re-search-forward org-link-any-re nil t)
(goto-char (point-min)) (when-let ((link (org-element-lineage (org-element-context) '(link) t)))
(while (re-search-forward org-link-any-re nil t) (let ((type (org-element-property :type link))
(when-let ((link (org-element-lineage (org-element-context) '(link) t))) (path (org-element-property :path link)))
(let ((type (org-element-property :type link)) (when (and (string-equal (file-truename path) old-path)
(path (org-element-property :path link))) (org-in-regexp org-link-bracket-re 1))
(when (and (string-equal (file-truename path) old-path) (let* ((label (if (match-end 2)
(org-in-regexp org-link-bracket-re 1)) (match-string-no-properties 2)
(let* ((label (if (match-end 2) (org-link-unescape (match-string-no-properties 1))))
(match-string-no-properties 2) (new-label (if (string-equal label old-desc)
(org-link-unescape (match-string-no-properties 1)))) new-desc
(new-label (if (string-equal label old-desc) label)))
new-desc (replace-match (org-roam-link-make-string
label))) (concat type ":"
(replace-match (org-roam-link-make-string (file-relative-name new-path (file-name-directory (buffer-file-name))))
(concat type ":" new-label)))))))))
(file-relative-name new-path (file-name-directory (buffer-file-name))))
new-label))))))))
(save-buffer)))
(defun org-roam--fix-relative-links (old-path) (defun org-roam--fix-relative-links (old-path)
"Fix file-relative links in current buffer. "Fix file-relative links in current buffer.
@@ -1529,9 +1527,38 @@ replaced links are made relative to the current buffer."
(replace-match (concat type ":" new-path) (replace-match (concat type ":" new-path)
nil t nil 1)))))))) nil t nil 1))))))))
(defvar-local org-roam-current-title nil
"The current title of the Org-roam file.")
(defun org-roam--setup-title-auto-update ()
"Setup automatic link description update on title change."
(setq-local org-roam-current-title (car (org-roam--extract-titles)))
(add-hook 'after-save-hook #'org-roam--update-links-on-title-change nil t))
(defun org-roam--update-links-on-title-change ()
"Update the link description of other Org-roam files on title change.
This function is to be called in `after-save-hook'. If the title
of the Org-roam file has changed, it will iterate over all
Org-roam files that link to the current file, and replace the
link descriptions with the new title if applicable."
(let ((new-title (car (org-roam--extract-titles)))
(old-title org-roam-current-title))
(unless (string-equal old-title new-title)
(let* ((current-path (file-truename (buffer-file-name)))
(files-affected (org-roam-db-query [:select :distinct [from]
:from links
:where (= to $s1)]
current-path)))
(dolist (file files-affected)
(with-current-buffer (or (find-buffer-visiting (car file))
(find-file-noselect (car file)))
(org-roam--replace-link current-path current-path old-title new-title)
(save-buffer)))))
(setq-local org-roam-current-title new-title)))
(defun org-roam--rename-file-advice (old-file new-file-or-dir &rest _args) (defun org-roam--rename-file-advice (old-file new-file-or-dir &rest _args)
"Rename backlinks of OLD-FILE to refer to NEW-FILE-OR-DIR." "Rename backlinks of OLD-FILE to refer to NEW-FILE-OR-DIR.
;; When rename-file is passed a directory as an argument, compute the new name When NEW-FILE-OR-DIR is a directory, we use it to compute the new file path."
(let ((new-file (if (directory-name-p new-file-or-dir) (let ((new-file (if (directory-name-p new-file-or-dir)
(expand-file-name (file-name-nondirectory old-file) new-file-or-dir) (expand-file-name (file-name-nondirectory old-file) new-file-or-dir)
new-file-or-dir))) new-file-or-dir)))
@@ -1543,30 +1570,25 @@ replaced links are made relative to the current buffer."
(org-roam-db--ensure-built) (org-roam-db--ensure-built)
(let* ((old-path (file-truename old-file)) (let* ((old-path (file-truename old-file))
(new-path (file-truename new-file)) (new-path (file-truename new-file))
(old-slug (org-roam--get-title-or-slug old-file))
(old-desc (org-roam--format-link-title old-slug))
(new-slug (or (org-roam-db--get-titles old-path)
(org-roam--path-to-slug new-path)))
(new-desc (org-roam--format-link-title new-slug))
(new-buffer (or (find-buffer-visiting new-path) (new-buffer (or (find-buffer-visiting new-path)
(find-file-noselect new-path))) (find-file-noselect new-path)))
(files-to-rename (org-roam-db-query [:select :distinct [from] (files-affected (org-roam-db-query [:select :distinct [from]
:from links :from links
:where (= to $s1)] :where (= to $s1)]
old-path))) old-path)))
;; Remove database entries for old-file.org ;; Remove database entries for old-file.org
(org-roam-db--clear-file old-file) (org-roam-db--clear-file old-file)
;; Insert new headlines locations in new-file.org after removing the previous IDs
(with-current-buffer new-buffer
(org-roam-db--update-headlines))
;; Replace links from old-file.org -> new-file.org in all Org-roam files with these links ;; Replace links from old-file.org -> new-file.org in all Org-roam files with these links
(mapc (lambda (file) (mapc (lambda (file)
(setq file (if (string-equal (file-truename (car file)) old-path) (setq file (if (string-equal (file-truename (car file)) old-path)
new-path new-path
(car file))) (car file)))
(org-roam--replace-link file old-path new-path old-desc new-desc) (with-current-buffer (or (find-buffer-visiting file)
(org-roam-db--update-file file)) (find-file-noselect file))
files-to-rename) (org-roam--replace-link old-path new-path)
(save-buffer)
(org-roam-db--update-file)))
files-affected)
;; If the new path is in a different directory, relative links ;; If the new path is in a different directory, relative links
;; will break. Fix all file-relative links: ;; will break. Fix all file-relative links:
(unless (string= (file-name-directory old-path) (unless (string= (file-name-directory old-path)