branch: externals/denote commit c73c4cf143852f0fc51ba8bb7824858ac9afe42b Merge: 9a82b7d313 33ca6c25e3 Author: Protesilaos Stavrou <i...@protesilaos.com> Commit: GitHub <nore...@github.com>
Merge pull request #360 from jeanphilippegg/denote-file-name-components-order Add `denote-file-name-components-order` to allow reordering --- denote.el | 90 ++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/denote.el b/denote.el index cec061818e..e54bd4045a 100644 --- a/denote.el +++ b/denote.el @@ -303,6 +303,32 @@ Also see `denote-history-completion-in-prompts'." (const :tag "Template" template) (const :tag "Signature" signature)))) +(defvar denote-file-name-components-order '(identifier signature title keywords) + "Specify the order of the file name components of new notes. + +The value is a list of the following symbols: + +- `identifier': An identifier in a file name looks like + \"20240101T111111\" if it appears as the first component. + Else, it is prepended with \"@@\" if it appears anywhere else + in the file name. + +- `signature': A signature in a file name is an arbitrary string + that combines \"==\" with the signature of + `denote-signature-prompt'. + +- `title': A title in a file name is an arbitrary string that + combines \"--\" with the title of `denote-title-prompt'. + +- `keywords': Keywords in a file name are represented as a string + that combines \"__\" with the keywords of + `denote-keywords-prompt' separated with an underscore. + +All the symbols above must appear exactly once. Any symbol +missing will be added automatically. + +See also `denote-prompts'.") + (defcustom denote-sort-keywords t "Whether to sort keywords in new files. @@ -825,35 +851,34 @@ leading and trailing hyphen." (replace-regexp-in-string "\\." "" str)) (defun denote--trim-right-token-characters (str component) - "Remove =, - and _ from the end of STR. + "Remove =, -, _ and @ from the end of STR. The removal is done only if necessary according to COMPONENT." (if (eq component 'title) - (string-trim-right str "[=_]+") - (string-trim-right str "[=_-]+"))) + (string-trim-right str "[=@_]+") + (string-trim-right str "[=@_-]+"))) (defun denote--replace-consecutive-token-characters (str component) "Replace consecutive characters with a single one in STR. -Hyphens, underscores and equal signs are replaced with a single -one in str, if necessary according to COMPONENT." - ;; -- are allowed in titles - (if (eq component 'title) - (replace-regexp-in-string - "_\\{2,\\}" "_" - (replace-regexp-in-string - "=\\{2,\\}" "=" str)) - (replace-regexp-in-string - "-\\{2,\\}" "-" - (replace-regexp-in-string - "_\\{2,\\}" "_" +Hyphens, underscores, equal signs and at signs are replaced with +a single one in str, if necessary according to COMPONENT." + (let ((str (replace-regexp-in-string + "_\\{2,\\}" "_" + (replace-regexp-in-string + "=\\{2,\\}" "=" + (replace-regexp-in-string + "@\\{2,\\}" "@" str))))) + ;; -- are allowed in titles + (if (eq component 'title) + str (replace-regexp-in-string - "=\\{2,\\}" "=" str))))) + "-\\{2,\\}" "-" str)))) (defun denote-sluggify (component str) "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 +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 @@ -1642,8 +1667,10 @@ contain the newline." To create a new one, refer to the function `denote-create-unique-file-identifier'." (let ((filename (file-name-nondirectory file))) - (if (string-match (concat "\\`" denote-id-regexp) filename) - (match-string-no-properties 0 filename)))) + (cond ((string-match (concat "\\`" denote-id-regexp) filename) + (match-string-no-properties 0 filename)) + ((string-match (concat "@@\\(?1:" denote-id-regexp "\\)") filename) + (match-string-no-properties 1 filename))))) ;; TODO 2023-12-08: Maybe we can only use ;; `denote-retrieve-filename-identifier' and remove this function. @@ -1856,14 +1883,23 @@ which case it is not added to the base file name." (error "ID must not be an empty string")) ((not (string-match-p denote-id-regexp id)) (error "ID `%s' does not match `denote-id-regexp'" id))) - (let ((file-name (concat dir-path id))) - (when (and signature (not (string-empty-p signature))) - (setq file-name (concat file-name "==" (denote-sluggify 'signature signature)))) - (when (and title (not (string-empty-p title))) - (setq file-name (concat file-name "--" (denote-sluggify 'title title)))) - (when keywords - (setq file-name (concat file-name "__" (denote-keywords-combine (denote-sluggify-keywords keywords))))) - (concat file-name extension))) + (let ((file-name "") + (components (seq-union denote-file-name-components-order + '(identifier signature title keywords)))) + (dolist (component components) + (cond ((and (eq component 'identifier) id (not (string-empty-p id))) + (setq file-name (concat file-name "@@" id))) + ((and (eq component 'title) title (not (string-empty-p title))) + (setq file-name (concat file-name "--" (denote-sluggify 'title title)))) + ((and (eq component 'keywords) keywords) + (setq file-name (concat file-name "__" (denote-keywords-combine (denote-sluggify-keywords keywords))))) + ((and (eq component 'signature) signature (not (string-empty-p signature))) + (setq file-name (concat file-name "==" (denote-sluggify 'signature signature)))))) + (setq file-name (concat file-name extension)) + ;; Do not prepend identifier with @@ if it is the first component. + (when (string-prefix-p "@@" file-name) + (setq file-name (substring file-name 2))) + (concat dir-path file-name))) (defun denote--format-front-matter-title (title file-type) "Format TITLE according to FILE-TYPE for the file's front matter."