branch: externals/denote
commit 5203bc00cf3474f5c0604631cba728a5bfed376e
Author: Protesilaos Stavrou <[email protected]>
Commit: Protesilaos Stavrou <[email protected]>

    Ensure that denote-directories is not computed multiple times
---
 denote.el | 119 ++++++++++++++++++++++++++++++--------------------------------
 1 file changed, 57 insertions(+), 62 deletions(-)

diff --git a/denote.el b/denote.el
index e3ea13134e..dab0bfd583 100644
--- a/denote.el
+++ b/denote.el
@@ -1248,21 +1248,18 @@ what remains."
   (and (file-writable-p file)
        (denote-file-has-supported-extension-p file)))
 
-;; FIXME 2025-12-14: We are hardcoding the `denote-directories'.  What
-;; we need is a simpler function to get a relative path.  Otherwise we
-;; are probably computing the `denote-directories' multiple times.
-(defun denote-get-file-name-relative-to-denote-directory (file)
-  "Return name of FILE relative to the variable `denote-directory'.
-FILE must be an absolute path."
+(defun denote-get-file-name-relative-to-denote-directory (file &optional 
directories)
+  "Return FILE relative to the variable `denote-directory'.
+With optional DIRECTORIES, as a list of directories, return FILE
+relative to whichever one of those it belongs to."
   (unless (file-name-absolute-p file)
     (error "The file `%s' is not absolute" file))
-  (when-let* ((directories (denote-directories))
-              (file-name (expand-file-name file))
+  (when-let* ((dirs (or directories (denote-directories)))
               (directory (seq-find
                           (lambda (d)
-                            (string-prefix-p d file-name))
-                          directories)))
-    (substring-no-properties file-name (length directory))))
+                            (string-prefix-p d file))
+                          dirs)))
+    (substring-no-properties file (length directory))))
 
 (defun denote-extract-id-from-string (string)
   "Return existing Denote identifier in STRING, else nil."
@@ -1274,34 +1271,28 @@ FILE must be an absolute path."
   (and (stringp denote-excluded-directories-regexp)
        (string-match-p denote-excluded-directories-regexp file)))
 
-(defun denote--directory-files-recursively-predicate (file)
-  "Predicate used by `directory-files-recursively' on FILE.
-
-Return t if FILE is valid, else return nil."
-  (let ((rel (denote-get-file-name-relative-to-denote-directory file)))
-    (cond
-     ((string-match-p "\\`\\." rel) nil)
-     ((string-match-p "/\\." rel) nil)
-     ((denote--exclude-directory-regexp-p rel) nil)
-     ((file-readable-p file)))))
-
-;; FIXME 2025-12-14: The parameter should not be optional.  I am doing
-;; it like this for now because there are places where the function is
-;; called without an argument.
-(defun denote--directory-all-files-recursively (&optional directories)
+(defun denote--directory-all-files-recursively (directories)
   "Return list of all files in DIRECTORIES or `denote-directories'.
 Avoids traversing dotfiles (unconditionally) and whatever matches
 `denote-excluded-directories-regexp'."
-  (apply #'append
-         (mapcar
-          (lambda (directory)
-            (directory-files-recursively
-             directory
-             directory-files-no-dot-files-regexp
-             :include-directories
-             #'denote--directory-files-recursively-predicate
-             :follow-symlinks))
-          (or directories (denote-directories)))))
+  (let ((predicate-fn
+         (lambda (file)
+           (let ((rel (denote-get-file-name-relative-to-denote-directory file 
directories)))
+             (cond
+              ((string-match-p "\\`\\." rel) nil)
+              ((string-match-p "/\\." rel) nil)
+              ((denote--exclude-directory-regexp-p rel) nil)
+              ((file-readable-p file)))))))
+    (apply #'append
+           (mapcar
+            (lambda (directory)
+              (directory-files-recursively
+               directory
+               directory-files-no-dot-files-regexp
+               :include-directories
+               predicate-fn
+               :follow-symlinks))
+            directories))))
 
 (defun denote--file-excluded-p (file)
   "Return non-file if FILE matches `denote-excluded-files-regexp'."
@@ -1313,28 +1304,30 @@ Avoids traversing dotfiles (unconditionally) and 
whatever matches
   'denote-directory-get-files
   "4.1.0")
 
-;; FIXME 2025-12-14: This should accept DIRECTORIES, which it would
-;; pass to `denote--directory-all-files-recursively'.  I have a
-;; relevant FIXME for `denote--directory-all-files-recursively'.
-(defun denote-directory-get-files ()
+(defun denote-directory-get-files (&optional directories)
   "Return list with full path of valid files in variable `denote-directory'.
 Consider files that satisfy `denote-file-has-denoted-filename-p' and
-are not backups."
-  (mapcar
-   #'expand-file-name
-   (seq-filter
-    (lambda (file)
-      (and (file-regular-p file)
-           (denote-file-has-denoted-filename-p file)
-           (not (denote--file-excluded-p file))
-           (not (backup-file-name-p file))))
-    (denote--directory-all-files-recursively (denote-directories)))))
+are not backups.
+
+With optional DIRECTORIES, as a list of directories, perform the
+operation therein."
+  (when-let* ((dirs (or directories (denote-directories))))
+    (seq-filter
+     (lambda (file)
+       (and (file-regular-p file)
+            (denote-file-has-denoted-filename-p file)
+            (not (denote--file-excluded-p file))
+            (not (backup-file-name-p file))))
+     (denote--directory-all-files-recursively dirs))))
 
 (defvar denote-directory-get-files-function #'denote-directory-get-files
   "Function to return list of Denote files.
 Each file is a string representing an absolute file system path.  This
 is intended for use in the function `denote-directory-files'.")
 
+;; NOTE 2025-12-22: The HAS-IDENTIFIER is there because we provide the
+;; `denote-directory-get-files-function'.  For core Denote, the
+;; `denote-directory-get-files' already does `denote-file-has-identifier-p'.
 (defun denote-directory-files (&optional files-matching-regexp omit-current 
text-only exclude-regexp has-identifier)
   "Return list of absolute file paths in variable `denote-directory'.
 Files that match `denote-excluded-files-regexp' are excluded from the
@@ -1378,21 +1371,18 @@ files that have an identifier."
                    files)))
     files))
 
-;; FIXME 2025-12-14: The parameter should not be optional.  Also see
-;; the same kind of comment for `denote-directory-get-files' and
-;; `denote--directory-all-files-recursively'.
 (defun denote-directory-subdirectories (&optional directories)
   "Return list of subdirectories in DIRECTORIES or variable `denote-directory'.
 Omit dotfiles (such as .git) unconditionally.  Also exclude
 whatever matches `denote-excluded-directories-regexp'."
   (seq-remove
    (lambda (filename)
-     (let ((rel (denote-get-file-name-relative-to-denote-directory filename)))
+     (let ((rel (denote-get-file-name-relative-to-denote-directory filename 
directories)))
        (or (not (file-directory-p filename))
            (string-match-p "\\`\\." rel)
            (string-match-p "/\\." rel)
            (denote--exclude-directory-regexp-p rel))))
-   (denote--directory-all-files-recursively directories)))
+   (denote--directory-all-files-recursively (or directories 
(denote-directories)))))
 
 ;; TODO 2023-01-24: Perhaps there is a good reason to make this a user
 ;; option, but I am keeping it as a generic variable for now.
@@ -1568,7 +1558,10 @@ Return the absolute path to the matching file."
                  (or denote-file-prompt-use-files-matching-regexp 
files-matching-regexp)
                  :omit-current nil nil has-identifier))
          (relative-files (if single-dir-p
-                             (mapcar 
#'denote-get-file-name-relative-to-denote-directory files)
+                             (mapcar
+                              (lambda (file)
+                                
(denote-get-file-name-relative-to-denote-directory file roots))
+                              files)
                            files))
          (prompt (if single-dir-p
                      (format "%s: " (or prompt-text "Select FILE"))
@@ -3575,10 +3568,6 @@ a value that can be parsed by `decode-time' or nil."
 (defalias 'denote--subdir-history 'denote-subdirectory-history
   "Compatibility alias for `denote-subdirectory-history'.")
 
-;; NOTE 2025-12-14: I have written several FIXME comments about how we
-;; get files and directories.  We should only be computing the target
-;; directories once.
-;;
 ;; TODO 2025-12-14: Explore if we can have relative paths here.  The
 ;; problem is that we also return the root `denote-directory', so how
 ;; should that be presented?  Maybe as "."?
@@ -5456,7 +5445,10 @@ the generic one."
   (let* ((roots (denote-directories))
          (single-dir-p (null (cdr roots)))
          (file-names (if single-dir-p
-                         (mapcar 
#'denote-get-file-name-relative-to-denote-directory files)
+                         (mapcar
+                          (lambda (file)
+                            (denote-get-file-name-relative-to-denote-directory 
file roots))
+                          files)
                        files))
          (selected (completing-read
                     (format-prompt (or prompt-text "Select file among files") 
nil)
@@ -6614,7 +6606,10 @@ contents, not file names.  Optional ID-ONLY has the same 
meaning as in
   (let* ((roots (denote-directories))
          (single-dir-p (null (cdr roots)))
          (file-names (if single-dir-p
-                         (mapcar 
#'denote-get-file-name-relative-to-denote-directory buffer-file-names)
+                         (mapcar
+                          (lambda (file)
+                            (denote-get-file-name-relative-to-denote-directory 
file roots))
+                          buffer-file-names)
                        buffer-file-names))
          (selected (completing-read
                     "Select open note to add links to: "

Reply via email to