aboutsummaryrefslogtreecommitdiff
path: root/modules/org-refile-config.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-24 07:21:44 -0500
committerCraig Jennings <c@cjennings.net>2026-05-24 07:21:44 -0500
commit12fb0108ba217f06fb9d40da8431d49540650402 (patch)
tree4d2c6630b3a6bc8f13de45697bc88ba89a257389 /modules/org-refile-config.el
parentbc9652752c231e8634a90c875ed6d206ff172458 (diff)
downloaddotemacs-12fb0108ba217f06fb9d40da8431d49540650402.tar.gz
dotemacs-12fb0108ba217f06fb9d40da8431d49540650402.zip
fix(org): surface directory-scan failures instead of crashing or hiding them
The refile target scan caught permission-denied and silently dropped the directory, and would crash outright on a missing root (only permission-denied was caught, so a missing code-dir/projects-dir raised file-missing and aborted the whole build). The agenda build had the same crash: cj/add-files-to-org-agenda-files-list called directory-files on projects-dir with no existence check. Extracted cj/--org-refile-scan-dir, which warns (display-warning) and returns nil for a missing, unreadable, or permission-denied root so the rest of the scan continues. Guarded the agenda scan the same way. Both now log a concise warning naming the skipped directory rather than failing silently or fatally. Also fixed a latent bug surfaced here: org-refile-targets was never declared special, so under make compile cj/org-refile-in-file let-bound it lexically and the scoped targets never reached org-refile. Added (defvar org-refile-targets) so the binding stays dynamic when byte-compiled. Tests cover the helper (missing/permission-denied/normal) and the agenda missing-dir guard.
Diffstat (limited to 'modules/org-refile-config.el')
-rw-r--r--modules/org-refile-config.el51
1 files changed, 40 insertions, 11 deletions
diff --git a/modules/org-refile-config.el b/modules/org-refile-config.el
index 16b37bf9..3c3cc7da 100644
--- a/modules/org-refile-config.el
+++ b/modules/org-refile-config.el
@@ -16,6 +16,13 @@
(require 'system-lib)
(require 'cj-cache-lib)
+;; Forward-declare org-refile's dynamic var so byte-compiled code treats our
+;; `let'/`setq' on it as dynamic. Without this, compiling the module turns
+;; `cj/org-refile-in-file's (let ((org-refile-targets ...)) ...) into a
+;; lexical binding that never reaches `org-refile', silently breaking
+;; in-file refiling under `make compile'.
+(defvar org-refile-targets)
+
;; ----------------------------- Org Refile Targets ----------------------------
;; sets refile targets
;; - adds project files in org-roam to the refile targets
@@ -43,6 +50,34 @@ This prevents issues where:
(org-mode)))
buf))
+(defun cj/--org-refile-scan-dir (dir)
+ "Return the todo.org files under DIR, or nil with a warning if unusable.
+A missing, unreadable, or permission-denied DIR is non-fatal: it logs a
+`display-warning' and returns nil so the rest of the refile-target scan
+continues, rather than silently swallowing the failure or crashing the
+whole scan on a missing directory."
+ (cond
+ ((not (file-directory-p dir))
+ (display-warning 'org-refile
+ (format "Refile scan: directory missing, skipped: %s" dir)
+ :warning)
+ nil)
+ ((not (file-readable-p dir))
+ (display-warning 'org-refile
+ (format "Refile scan: directory unreadable, skipped: %s" dir)
+ :warning)
+ nil)
+ (t
+ (condition-case _
+ (directory-files-recursively
+ dir "^[Tt][Oo][Dd][Oo]\\.[Oo][Rr][Gg]$" nil
+ (lambda (d) (not (string-match-p "airootfs" d))))
+ (permission-denied
+ (display-warning 'org-refile
+ (format "Refile scan: permission denied, skipped: %s" dir)
+ :warning)
+ nil)))))
+
(defun cj/--org-refile-scan-targets ()
"Scan disk for the refile-targets list. Pure-ish: no caching, no logging.
Returns the list to assign to `org-refile-targets'. Slow -- walks
@@ -61,17 +96,11 @@ Returns the list to assign to `org-refile-targets'. Slow -- walks
(dolist (file project-and-topic-files)
(unless (assoc file new-files)
(push (cons file file-rule) new-files)))))
- (dolist (dir (list user-emacs-directory code-dir projects-dir))
- (condition-case nil
- (let* ((todo-files (directory-files-recursively
- dir "^[Tt][Oo][Dd][Oo]\\.[Oo][Rr][Gg]$"
- nil
- (lambda (d) (not (string-match-p "airootfs" d)))))
- (file-rule '(:maxlevel . 1)))
- (dolist (file todo-files)
- (unless (assoc file new-files)
- (push (cons file file-rule) new-files))))
- (permission-denied nil)))
+ (let ((file-rule '(:maxlevel . 1)))
+ (dolist (dir (list user-emacs-directory code-dir projects-dir))
+ (dolist (file (cj/--org-refile-scan-dir dir))
+ (unless (assoc file new-files)
+ (push (cons file file-rule) new-files)))))
(nreverse new-files)))
(defun cj/build-org-refile-targets (&optional force-rebuild)