branch: externals/denote
commit a7c562e4bcb16054ae8df4993d092a9e289a2be3
Merge: 7707c2681d e8fa213409
Author: Protesilaos Stavrou <i...@protesilaos.com>
Commit: GitHub <nore...@github.com>

    Merge pull request #625 from jeanphilippegg/cleanup
    
    Various adjustments
---
 denote.el | 121 +++++++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 72 insertions(+), 49 deletions(-)

diff --git a/denote.el b/denote.el
index b683fc3852..14366e6771 100644
--- a/denote.el
+++ b/denote.el
@@ -418,6 +418,12 @@ in the front matter template."
           (const :tag "Date" date)
           (const :tag "Identifier" identifier)))
 
+(defcustom denote-identifier-delimiter-always-present-in-file-name nil
+  "Specify if file names always contain the identifier delimiter."
+  :group 'denote
+  :package-version '(denote . "4.1.0")
+  :type 'boolean)
+
 (defcustom denote-sort-keywords t
   "Whether to sort keywords in new files.
 
@@ -787,7 +793,8 @@ even an empty string to not have any such prefix."
   :group 'denote)
 
 (defcustom denote-file-name-slug-functions
-  '((title . denote-sluggify-title)
+  '((identifier . identity)
+    (title . denote-sluggify-title)
     (signature . denote-sluggify-signature)
     (keyword . denote-sluggify-keyword))
   "Specify the method Denote uses to format the components of the file name.
@@ -795,9 +802,9 @@ even an empty string to not have any such prefix."
 The value is an alist where each element is a cons cell of the
 form (COMPONENT . METHOD).
 
-- The COMPONENT is an unquoted symbol among `title', `signature',
-  `keyword' (notice the absence of `s', see below), which
-  refers to the corresponding component of the file name.
+- The COMPONENT is an unquoted symbol among `identifier', `title',
+  `signature', `keyword' (notice the absence of `s', see below),
+  which refers to the corresponding component of the file name.
 
 - The METHOD is the function to be used to format the given
   component.  This function should take a string as its parameter
@@ -810,8 +817,8 @@ form (COMPONENT . METHOD).
 Note that the `keyword' function is also applied to the keywords
 of the front matter.
 
-By default, if a function is not specified for a component, we
-use `denote-sluggify-title', `denote-sluggify-keyword' and
+By default, if a function is not specified for a component, we use
+`identity', `denote-sluggify-title', `denote-sluggify-keyword' and
 `denote-sluggify-signature'.
 
 Remember that deviating from the default file-naming scheme of Denote
@@ -1018,19 +1025,13 @@ returns the first directory."
 
 (make-obsolete 'denote-directory 'denote-directories "4.1.0")
 
-;; TODO: Review and fix the features listed in the docstring below before
-;; making this a user option.
 (defvar denote-generate-identifier-automatically t
   "Make creation and renaming commands automatically create and identifier.
 
 This applies when a note is created or renamed.  The default is to
 always create an identifier automatically.
 
-Valid values are: t, nil, `on-creation', and `on-rename'.
-
-IMPORTANT: Some features may not work with notes that do not have an
-identifier.  For example, backlinks do not contain files without an
-identifier.")
+Valid values are: t, nil, `on-creation', and `on-rename'.")
 
 (defvar denote-accept-nil-date nil
   "Make creation and renaming commands use `current-time' when date is nil.")
@@ -1118,11 +1119,11 @@ a single one in str, if necessary according to 
COMPONENT."
   "Make STR an appropriate slug for file name COMPONENT.
 
 Apply the function specified in `denote-file-name-slug-function' to
-COMPONENT which is one of `title', `signature', `keyword'.  If the
-resulting string still contains consecutive -, _, =, or @, they are
-replaced by a single occurence of the character, if necessary according
-to COMPONENT.  If COMPONENT is `keyword', remove underscores from STR as
-they are used as the keywords separator in file names.
+COMPONENT which is one of `identifier', `title', `signature', `keyword'.
+If the resulting string still contains consecutive -, _, =, or @, they
+are replaced by a single occurence of the character, if necessary
+according to COMPONENT.  If COMPONENT is `keyword', remove underscores
+from STR as they are used as the keywords separator in file names.
 
 Also enforce the rules of the file-naming scheme."
   (let* ((slug-function (alist-get component denote-file-name-slug-functions))
@@ -1133,7 +1134,8 @@ Also enforce the rules of the file-naming scheme."
                            "_" ""
                            (funcall (or slug-function 
#'denote-sluggify-keyword) str)))
                          ((eq component 'identifier)
-                          (denote--valid-identifier str))
+                          (denote--valid-identifier
+                           (funcall (or slug-function #'identity) str)))
                          ((eq component 'signature)
                           (funcall (or slug-function 
#'denote-sluggify-signature) str)))))
     (denote--trim-right-token-characters
@@ -1218,6 +1220,9 @@ For our purposes, a note must satisfy `file-regular-p' and
 `denote-filename-is-note-p'."
   (and (file-regular-p file) (denote-filename-is-note-p file)))
 
+(make-obsolete 'denote-filename-is-note-p nil "4.1.0")
+(make-obsolete 'denote-file-is-note-p nil "4.1.0")
+
 (defun denote-file-has-denoted-filename-p (file)
   "Return non-nil if FILE respects the file-naming scheme of Denote.
 
@@ -1316,14 +1321,14 @@ Avoids traversing dotfiles (unconditionally) and 
whatever matches
 
 (defun denote-directory-get-files ()
   "Return list with full path of valid files in variable `denote-directory'.
-Consider files that satisfy `denote-file-has-identifier-p' and
+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-identifier-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))))
@@ -1333,7 +1338,7 @@ are not backups."
 Each file is a string representing an absolute file system path.  This
 is intended for use in the function `denote-directory-files'.")
 
-(defun denote-directory-files (&optional files-matching-regexp omit-current 
text-only exclude-regexp)
+(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
 list.
@@ -1353,7 +1358,10 @@ text files that satisfy 
`denote-file-has-supported-extension-p'.
 
 With optional EXCLUDE-REGEXP exclude the files that match the given
 regular expression.  This is done after FILES-MATCHING-REGEXP and
-OMIT-CURRENT have been applied."
+OMIT-CURRENT have been applied.
+
+With optional HAS-IDENTIFIER as a non-nil value, limit the results to
+files that have an identifier."
   (let ((files (funcall denote-directory-get-files-function)))
     (when (and omit-current buffer-file-name (denote-file-has-identifier-p 
buffer-file-name))
       (setq files (delete buffer-file-name files)))
@@ -1364,6 +1372,8 @@ OMIT-CURRENT have been applied."
                    files)))
     (when text-only
       (setq files (seq-filter #'denote-file-has-supported-extension-p files)))
+    (when has-identifier
+      (setq files (seq-filter #'denote-file-has-identifier-p files)))
     (when exclude-regexp
       (setq files (seq-remove
                    (lambda (file)
@@ -1426,7 +1436,7 @@ something like .org even if the actual file extension is
          (seq-filter
           (lambda (file)
             (string= id (denote-retrieve-filename-identifier file)))
-          (denote-directory-files))))
+          (denote-directory-files nil nil nil nil :has-identifier))))
     (if (length< files 2)
         (car files)
       (seq-find
@@ -1461,7 +1471,7 @@ the title prompt of `denote-open-or-create' and related 
commands.")
 Only ever `let' bind this, otherwise the restriction will always be
 there.")
 
-(defun denote-file-prompt (&optional files-matching-regexp prompt-text 
no-require-match)
+(defun denote-file-prompt (&optional files-matching-regexp prompt-text 
no-require-match has-identifier)
   "Prompt for file in variable `denote-directory'.
 Files that match `denote-excluded-files-regexp' are excluded from the
 list.
@@ -1474,13 +1484,16 @@ select a file.
 
 With optional NO-REQUIRE-MATCH, accept the given input as-is.
 
+With optional HAS-IDENTIFIER, only show candidates that have an
+identifier.
+
 Return the absolute path to the matching file."
   (let* (;; Some external program may use `default-directory' with the
          ;; relative file paths of the completion candidates.
          (default-directory (car (denote-directories)))
          (files (denote-directory-files
                  (or denote-file-prompt-use-files-matching-regexp 
files-matching-regexp)
-                 :omit-current))
+                 :omit-current nil nil has-identifier))
          (relative-files (mapcar
                           #'denote-get-file-name-relative-to-denote-directory
                           files))
@@ -2858,8 +2871,9 @@ which case it is not added to the base file name."
       (error "There should be at least one file name component"))
     (setq file-name (concat file-name extension))
     ;; Do not prepend identifier with @@ if it is the first component and has 
the format 00000000T000000.
-    (when (and (string-prefix-p "@@" file-name)
-               (string-match-p (concat "\\`" denote-date-identifier-regexp 
"\\'") id)) ; This is always true for now.
+    (when (and (not denote-identifier-delimiter-always-present-in-file-name)
+               (string-prefix-p "@@" file-name)
+               (string-match-p (concat "\\`" denote-date-identifier-regexp 
"\\'") id))
       (setq file-name (substring file-name 2)))
     (concat dir-path file-name)))
 
@@ -2995,7 +3009,10 @@ If DATE is nil or an empty string, return nil."
          (lambda (buffer)
            (when-let* (((buffer-live-p buffer))
                        (file (buffer-file-name buffer))
-                       ((denote-filename-is-note-p file)))
+                       ((denote-file-is-in-denote-directory-p file))
+                       ((denote-file-has-supported-extension-p file))
+                       ((denote-file-has-denoted-filename-p file))
+                       ((denote-file-has-identifier-p file)))
              file))
          (buffer-list))))
 
@@ -3005,7 +3022,7 @@ It checks files in variable `denote-directory' and active 
buffer files."
   (let* ((ids (make-hash-table :test 'equal))
          (file-names (mapcar
                       (lambda (file) (file-name-nondirectory file))
-                      (denote-directory-files)))
+                      (denote-directory-files nil nil nil nil 
:has-identifier)))
          (names (append file-names (denote--buffer-file-names))))
     (dolist (name names)
       (when-let* ((id (denote-retrieve-filename-identifier name)))
@@ -3052,15 +3069,9 @@ This is a reference function for 
`denote-get-identifier-function'."
 (defun denote--find-first-unused-id-as-number (id)
   "Return the first unused id starting at ID.
 If ID is already used, increment it until an available id is found."
-  (let ((current-id id)
-        (iteration 0))
-    (while (gethash current-id denote-used-identifiers)
-      ;; Prevent infinite loop
-      (setq iteration (1+ iteration))
-      (when (>= iteration 10000)
-        (user-error "A unique identifier could not be found"))
-      (setq current-id (number-to-string (1+ (string-to-number current-id)))))
-    current-id))
+  (while (gethash id denote-used-identifiers)
+    (setq id (number-to-string (1+ (string-to-number id)))))
+  id)
 
 (defun denote-generate-identifier-as-number (initial-identifier _date)
   "Generate an increasing number identifier.
@@ -3072,13 +3083,21 @@ Else, use the first unused number starting from 1.
 
 This is a reference function for `denote-get-identifier-function'."
   (let ((denote-used-identifiers (or denote-used-identifiers 
(denote--get-all-used-ids))))
-    (cond ((and initial-identifier
+    (cond (;; Always use the supplied initial-identifier if possible,
+           ;; regardless of format.
+           (and initial-identifier
                 (not (gethash initial-identifier denote-used-identifiers)))
            initial-identifier)
-          ((and initial-identifier
+          (;; If the supplied initial-identifier is already used, but
+           ;; it has the right format, make is unique.
+           (and initial-identifier
                 (string-match-p "[1-9][0-9]*" initial-identifier))
            (denote--find-first-unused-id-as-number initial-identifier))
-          (t
+          (;; Else, the supplied initial-identifier is nil or it is
+           ;; already used or it does not match the supplied
+           ;; format. Ignore it and generate a valid identifier with
+           ;; the right format.
+           t
            (denote--find-first-unused-id-as-number "1")))))
 
 (defvar denote-command-prompt-history nil
@@ -5311,7 +5330,7 @@ path.  FILE-TYPE is a symbol as described in the user 
option
 `denote-file-type'.  DESCRIPTION is a string.  Whether the caller treats
 the active region specially, is up to it."
   (interactive
-   (let* ((file (denote-file-prompt nil "Link to FILE"))
+   (let* ((file (denote-file-prompt nil "Link to FILE" nil :has-identifier))
           (file-type (denote-filetype-heuristics buffer-file-name))
           (description (when (file-exists-p file)
                          (denote-get-link-description file))))
@@ -5384,7 +5403,7 @@ Also see `denote-get-backlinks'."
               ((denote-file-has-supported-extension-p current-file))
               (file-type (denote-filetype-heuristics current-file))
               (regexp (denote--link-in-context-regexp file-type))
-              (files (or files (denote-directory-files)))
+              (files (or files (denote-directory-files nil nil nil nil 
:has-identifier)))
               (file-identifiers
                (with-temp-buffer
                  (insert-file-contents current-file)
@@ -5475,7 +5494,7 @@ With optional ID-ONLY as a prefix argument create a link 
that
 consists of just the identifier.  Else try to also include the
 file's title.  This has the same meaning as in `denote-link'."
   (interactive
-   (let* ((target (denote-file-prompt nil "Select file (RET on no match to 
create it)" :no-require-match)))
+   (let* ((target (denote-file-prompt nil "Select file (RET on no match to 
create it)" :no-require-match :has-identifier)))
      (unless (and target (file-exists-p target))
        (setq target (denote--command-with-features #'denote 
:use-file-prompt-as-def-title :ignore-region :save :in-background)))
      (list target current-prefix-arg)))
@@ -6320,7 +6339,9 @@ To be used as a `thing-at' provider."
   "Enable `denote-fontify-links-mode' in a denote file unless in `org-mode'."
   (when (and buffer-file-name
              (not (derived-mode-p 'org-mode))
-             (denote-file-is-note-p buffer-file-name))
+             (denote-file-is-in-denote-directory-p buffer-file-name)
+             (denote-file-has-supported-extension-p buffer-file-name)
+             (denote-file-has-denoted-filename-p buffer-file-name))
     (denote-fontify-links-mode)))
 
 ;;;###autoload
@@ -6405,7 +6426,7 @@ inserts links with just the identifier."
               (and buffer-file-name (denote-file-has-supported-extension-p 
buffer-file-name)))
     (user-error "The current file type is not recognized by Denote"))
   (let ((file-type (denote-filetype-heuristics (buffer-file-name))))
-    (if-let* ((files (denote-directory-files regexp :omit-current)))
+    (if-let* ((files (denote-directory-files regexp :omit-current nil nil 
:has-identifier)))
         (denote-link--insert-links files file-type id-only)
       (message "No links matching `%s'" regexp))))
 
@@ -6699,7 +6720,7 @@ Uses the function `denote-directory' to establish the 
path to the file."
   "Like `denote-link' but for Org integration.
 This lets the user complete a link through the `org-insert-link'
 interface by first selecting the `denote:' hyperlink type."
-  (if-let* ((file (denote-file-prompt)))
+  (if-let* ((file (denote-file-prompt nil nil nil :has-identifier)))
       (concat "denote:" (denote-retrieve-filename-identifier file))
     (user-error "No files in `denote-directory'")))
 
@@ -6740,7 +6761,9 @@ Optional INTERACTIVE? is used by `org-store-link'.
 Also see the user option `denote-org-store-link-to-heading'."
   (when interactive?
     (when-let* ((file (buffer-file-name))
-                ((denote-file-is-note-p file))
+                ((file-regular-p file))
+                ((denote-file-is-in-denote-directory-p file))
+                ((denote-file-has-denoted-filename-p file))
                 (file-id (denote-retrieve-filename-identifier file))
                 (description (denote-get-link-description file)))
       (let ((heading-links (and denote-org-store-link-to-heading

Reply via email to