(fix)org-id: gracefully handle absence of org-id-locations-file (#1769)

This should be more useful than telling the user to run something
like (org-id-update-id-locations (directory-files-recursively ..))

org-id normally stores the org-id-locations-file in
user-emacs-directory, which should always exist if Emacs is installed.
If the path to the location exists, org-id will be able to create the
file on its own, however, configurations often change the location of
this file to a different one, in which case its path might not be
constructed of existing directories (on the file system) and org-id
won't be able to laydown this path for the user.

This causes org-id to throw unhelpful errors (like in #1734, #1700,
\#1688) that don't allow Org-roam to complete capture process or
properly finish migration, and possibly add malfunctions at other
layers.

Ideally this problem should be handled by org-id itself, but for now
this will be only patched in Org-roam.

Fixes #1700 and fixes #1638.

Co-authored-by: Jethro Kuan <jethrokuan95@gmail.com>
Co-authored-by: David Wilson <david@daviwil.com>
This commit is contained in:
Wetlize
2021-08-16 16:38:47 +03:00
committed by GitHub
parent 8a21131d9f
commit 8d4de78fac
2 changed files with 83 additions and 0 deletions

View File

@ -95,6 +95,67 @@ recursion."
(push (concat dir "/" file) files)))))
(nconc result (nreverse files))))
;;; Compatibility hacks and patches
(advice-add #'org-id-add-location :around #'org-roam--handle-absent-org-id-locations-file-a)
(defun org-roam--handle-absent-org-id-locations-file-a (fn &rest args)
"Gracefully handle errors related to absence of `org-id-locations-file'.
FN is `org-id-locations-file' that comes from advice and ARGS are
passed to it."
(let (result)
;; Use `unwind-protect' over `condition-case' because `org-id' can produce various other errors, but all
;; of its errors are generic ones, so trapping all of them isn't a good idea and preserving the correct
;; backtrace is valuable.
(unwind-protect (setq result (apply fn args))
(unless result
(unless org-id-locations
;; Pre-allocate the hash table to avoid weird access related errors during the regeneration.
(setq org-id-locations (make-hash-table :type 'equal)))
;; `org-id' makes the assumption that `org-id-locations-file' will be stored in `user-emacs-directory'
;; which always exist if you have Emacs, so it uses `with-temp-file' to write to the file. However,
;; the users *do* change the path to this file and `with-temp-file' unable to create the file, if the
;; path to it consists of directories that don't exist. We'll have to handle this ourselves.
(unless (file-exists-p (file-truename org-id-locations-file))
;; If permissions allow that, try to create the user specified directory path to
;; `org-id-locations-file' ourselves.
(condition-case _err
(progn (org-roam-message (concat "`org-id-locations-file' (%s) doesn't exist. "
"Trying to regenerate it (this may take a while)...")
org-id-locations-file)
(make-directory (file-name-directory (file-truename org-id-locations-file)))
(org-roam-update-org-id-locations)
(apply fn args))
;; In case of failure (lack of permissions), we'll patch it to at least handle the current session
;; without errors.
(file-error (org-roam-message "Failed to regenerate `org-id-locations-file'")
(lwarn 'org-roam :error "
--------
WARNING: `org-id-locations-file' (%s) doesn't exist!
Org-roam is unable to create it for you.
--------
This happens when Emacs doesn't have permissions to create the
path to your `org-id-locations-file'. Org-roam will now fallback
storing the file in your current `org-roam-directory', but the
warning will keep popup with each new session.
To stop this warning from popping up, set `org-id-locations-file'
to the location you want and ensure that the path exists on your
filesystem, then run M-x `org-roam-update-org-id-locations'.
Note: While Org-roam doesn't depend on `org-id-locations-file' to
lookup IDs for the nodes that are stored in the database, it
still tries to keep it updated so IDs work across other files in
Org-mode, so the IDs used in your `org-roam-directory' would be
able to cross-reference outside of `org-roam-directory'. It also
allows to keep linking with \"id:\" links within the current
`org-roam-directory' to headings and files that are excluded from
identification (e.g. with \"ROAM_EXCLUDE\" property) as Org-roam
nodes." org-id-locations-file)
(setq org-id-locations-file
(expand-file-name ".orgids" (file-truename org-roam-directory)))
(apply fn args)))))
result)))
;;; Obsolete aliases (remove after next major release)
(define-obsolete-function-alias
'org-roam-setup

View File

@ -847,6 +847,28 @@ first encapsulating ID."
(when (org-roam-db-node-p)
(org-id-get))))
;;;###autoload
(defun org-roam-update-org-id-locations (&rest directories)
"Scan Org-roam files to update `org-id' related state.
This is like `org-id-update-id-locations', but will automatically
use the currently bound `org-directory' and `org-roam-directory'
along with DIRECTORIES (if any), where the lookup for files in
these directories will be always recursive.
Note: Org-roam doesn't have hard dependency on
`org-id-locations-file' to lookup IDs for nodes that are stored
in the database, but it still tries to properly integrates with
`org-id'. This allows the user to cross-reference IDs outside of
the current `org-roam-directory', and also link with \"id:\"
links to headings/files within the current `org-roam-directory'
that are excluded from identification in Org-roam as
`org-roam-node's, e.g. with \"ROAM_EXCLUDE\" property."
(interactive)
(cl-loop with files for dir in (cons org-roam-directory directories)
for org-roam-directory = dir
nconc (org-roam-list-files) into files
finally (org-id-update-id-locations files org-roam-verbose)))
;;; Refs
;;;; Completing-read interface
(defun org-roam-ref-read (&optional initial-input filter-fn)