From 8d4de78facccc95f6c3627f0134ae3d602d6b983 Mon Sep 17 00:00:00 2001 From: Wetlize Date: Mon, 16 Aug 2021 16:38:47 +0300 Subject: [PATCH] (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 Co-authored-by: David Wilson --- org-roam-compat.el | 61 ++++++++++++++++++++++++++++++++++++++++++++++ org-roam-node.el | 22 +++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/org-roam-compat.el b/org-roam-compat.el index edff0de..fba6f82 100644 --- a/org-roam-compat.el +++ b/org-roam-compat.el @@ -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 diff --git a/org-roam-node.el b/org-roam-node.el index 1f90926..231b0ed 100644 --- a/org-roam-node.el +++ b/org-roam-node.el @@ -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)