branch: externals/denote commit 62c6853480d62b00e4c838e148730d1fedf6235f Merge: 94a3ffd442 d7dc32cb42 Author: Protesilaos Stavrou <i...@protesilaos.com> Commit: Protesilaos Stavrou <i...@protesilaos.com>
MAJOR UPDATE towards version 2.0.0: Merge branch 'signature' Signatures are an optional extension to Denote's file-naming scheme. They are arbitrary strings of alphanumerical characters that can be used to establish sequential relations between files at the level of their file name (e.g. 1, 1a, 1b, 1b1, 1b2, ...). Files that have the signature follow this scheme (though read the documentation of 'denote-prompts' for possible permutations): DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION As a reminder, Denote's default file-naming scheme is: DATE--TITLE__KEYWORDS.EXTENSION [ Denote can be used to rename any file, not just to create notes. As such, the file-naming scheme is a powerful, yet low-tech invention to facilitate searching and filtering. ] For the time being, signatures are not added to a file's front matter and are not shown anywhere else beside the file name. This is done on purpose to simplify the implementation and make sure we define clear use-cases for this feature (it is easier to add new functionality than refactor/break existing one). Users can create files with signatures either by (i) modifying the 'denote-prompts' user option to affect the standard 'denote' command, or (ii) by generating such files on demand with the command 'denote-signature' (alias 'denote-create-note-using-signature'). Signatures are treated as quasi-identifiers when renaming files that include them. This means that they are not touched by Denote. The user must manually update the signature which, in theory, should not be done if notes already have a predefined sequence. Signatures are backward-compatible, meaning that existing users are not impacted by their inclusion. The signature extension was discussed at length on the GitHub mirror in issue 115: <https://github.com/protesilaos/denote/issues/115>. Thanks to Stefan Thesing, Mirko Hernandez, Noboru Ota (nobiot), Xiaoxing Hu, nbehrnd, Elias Storms, and 101scholar for helping me reason about this feature, understand its scope, and prototype its implementation. The inclusion of the 'signature' branch into 'main' does not mean that we are done with the development of this feature. We are simply making it available to more users while preparing for the release of version 2.0.0 of Denote. Watch the video series "Denote as a Zettelkasten" produced by Stefan Thesing, which makes use of these signatures: <https://www.thesing-online.de/blog/denote-as-a-zettelkasten>. --- README.org | 148 ++++++++++++++++++++++++++++++++++++++++++++++------------- denote.el | 152 +++++++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 231 insertions(+), 69 deletions(-) diff --git a/README.org b/README.org index 7b227e52ca..6990d1d354 100644 --- a/README.org +++ b/README.org @@ -6,7 +6,7 @@ #+startup: content #+macro: stable-version 1.2.0 #+macro: release-date 2022-12-16 -#+macro: development-version 1.3.0-dev +#+macro: development-version 2.0.0-dev #+export_file_name: denote.texi #+texinfo_filename: denote.info #+texinfo_dir_category: Emacs misc features @@ -149,11 +149,16 @@ Obligations, Tasks, and Engagements. #+findex: denote-date #+findex: denote-subdirectory #+findex: denote-template +#+findex: denote-signature There are five ways to write a note with Denote: invoke the ~denote~, -~denote-type~, ~denote-date~, ~denote-subdirectory~, ~denote-template~ -commands, or leverage the ~org-capture-templates~ by setting up a -template which calls the function ~denote-org-capture~. We explain all -of those in the subsequent sections. +~denote-type~, ~denote-date~, ~denote-subdirectory~, +~denote-template~, ~denote-signature~ commands, or leverage the +~org-capture-templates~ by setting up a template which calls the +function ~denote-org-capture~. We explain all of those in the +subsequent sections. + +[ The ~denote-signature~ and related are part of +{{{development-version}}}. ] ** Standard note creation :PROPERTIES: @@ -237,6 +242,16 @@ The value is a list of symbols, which includes any of the following: of that KEY is used to populate the new note with content, which is added after the front matter ([[#h:f635a490-d29e-4608-9372-7bd13b34d56c][The denote-templates option]]). +- =signature=: - Prompts for an arbitrary string that can be used to + establish a sequential relationship between files (e.g. 1, 1a, 1b, + 1b1, 1b2, ...). Signatures have no strictly defined function and + are up to the user to apply as they see fit. One use-case is to + implement Niklas Luhmann's Zettelkasten system for a sequence of + notes (Folgezettel). Signatures are not included in a file's front + matter and are not shown in the description of a link. They are + reserved solely for creating a sequence in a file listing, at least + for the time being. [Part of {{{development-version}}}.] + The prompts occur in the given order. If the value of this user option is nil, no prompts are used. The @@ -262,8 +277,9 @@ Finally, this user option only affects the interactive use of the ~denote~ command (advanced users can call it from Lisp). For ad-hoc interactive actions that do not change the default behaviour of the ~denote~ command, users can invoke these convenience commands: -~denote-type~, ~denote-subdirectory~, ~denote-date~. They are described -in the subsequent section ([[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]]). +~denote-type~, ~denote-subdirectory~, ~denote-date~, +~denote-signature~. They are described in the subsequent section +([[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]]). *** The ~denote-templates~ option :PROPERTIES: @@ -396,6 +412,18 @@ commands for note creation: The ~denote-create-note-with-template~ is an alias of the command ~denote-template~, meant to help with discoverability. ++ Create note with a signature :: The ~denote-signature~ command first + prompts for an arbitrary string to use in the optional =SIGNATURE= + field of the file name and then asks for a title and keywords. + Signatures are arbitrary strings of alphanumeric characters which + can be used to establish sequential relations between file at the + level of their file name (e.g. 1, 1a, 1b, 1b1, 1b2, ...). [Part of + {{{development-version}}}.] + + The ~denote-create-note-using-signature~ is an alias of the command + ~denote-signature~ intended to make the functionality more + discoverable. [Also part of {{{development-version}}}.] + **** Write your own convenience commands :PROPERTIES: :CUSTOM_ID: h:11946562-7eb0-4925-a3b5-92d75f1f5895 @@ -1119,14 +1147,15 @@ difference between old and new file names. :END: #+vindex: denote-directory -Notes are stored the ~denote-directory~. The default path is +Notes are stored in the ~denote-directory~. The default path is =~/Documents/notes=. The ~denote-directory~ can be a flat listing, meaning that it has no subdirectories, or it can be a directory tree. Either way, Denote takes care to only consider "notes" as valid candidates in the relevant operations and will omit other files or directories. -Every note produced by Denote follows this pattern ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]): +Every note produced by Denote follows this pattern by default +([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]): : DATE--TITLE__KEYWORDS.EXTENSION @@ -1134,7 +1163,7 @@ The =DATE= field represents the date in year-month-day format followed by the capital letter =T= (for "time") and the current time in hour-minute-second notation. The presentation is compact: =20220531T091625=. The =DATE= serves as the unique identifier of each -note. +note and, as such, is also known as the file's ID or identifier. The =TITLE= field is the title of the note, as provided by the user. It automatically gets downcased and hyphenated. An entry about "Economics @@ -1180,10 +1209,41 @@ invoking =M-x re-builder=). [[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]. +As an optional extension to the above, file names can include a string +of alphanumeric characters in the =SIGNATURE= field. Signatures have +no clearly defined purpose and are up to the user to define. One +use-case is to use them to establish sequential relations between +files (e.g. 1, 1a, 1b, 1b1, 1b2, ...). A full file name with a +signature looks like this: + +: DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION + +The =SIGNATURE= field is anchored by the equals sign and thus retains +the aforementioned searching/anchoring feature of =--= and =__=. + +Signatures are an optional extension to Denote's file-naming scheme. +They can be added to newly created files on demand, with the command +~denote-signature~, or by modifying the value of the user option +~denote-prompts~. + +The ~denote-prompts~ can be configured in such ways to yield the +following file name permutations: + +: DATE.EXT +: DATE--TITLE.EXT +: DATE__KEYWORDS.EXT +: DATE==SIGNATURE.EXT +: DATE==SIGNATURE--TITLE.EXT +: DATE==SIGNATURE--TITLE__KEYWORDS.EXT +: DATE==SIGNATURE__KEYWORDS.EXT + +When in doubt, stick to the default design. + While Denote is an Emacs package, notes should work long-term and not depend on the functionality of a specific program. The file-naming scheme we apply guarantees that a listing is readable in a variety of -contexts. +contexts. The Denote file-naming scheme is, in essence, an effective, +low-tech invention. ** Sluggified title and keywords :PROPERTIES: @@ -1208,30 +1268,38 @@ holds the relevant value. In simple terms: :CUSTOM_ID: h:1a953736-86c2-420b-b566-fb22c97df197 :END: -File names have three fields and two sets of field delimiters between -them: +By default, file names have three fields and two sets of field +delimiters between them: : DATE--TITLE__KEYWORDS.EXTENSION -The first field delimiter is the double hyphen, while the second is the -double underscore. These practically serve as anchors for easier -searching. Consider this example: +When a signature is present, this becomes: + +: DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION + +[ Signatures are part of {{{development-version}}}. ] -: 20220621T062327--introduction-to-denote__denote_emacs.txt +Field delimiters practically serve as anchors for easier searching. +Consider this example: -You will notice that there are two matches for the word =denote=: one in -the title field and another in the keywords' field. Because of the -distinct field delimiters, if we search for =-denote= we only match the -first instance while =_denote= targets the second one. When sorting -through your notes, this kind of specificity is invaluable---and you get -it for free from the file names alone! +: 20220621T062327==1a2--introduction-to-denote__denote_emacs.txt -Users can get a lot of value out of this simple arrangement, even if -they have no knowledge of regular expressions. One thing to consider, -for maximum effect, is to avoid using multi-word keywords as those get -hyphenated like the title and will thus interfere with the above: either -set the user option ~denote-allow-multi-word-keywords~ to nil or simply -insert single words at the relevant prompts. +You will notice that there are two matches for the word =denote=: one +in the title field and another in the keywords' field. Because of the +distinct field delimiters, if we search for =-denote= we only match +the first instance while =_denote= targets the second one. When +sorting through your notes, this kind of specificity is +invaluable---and you get it for free from the file names alone! +Similarly, a search for ==1= will show all notes that are related to +each other by virtue of their signature. + +Users can get a lot of value out of this simple yet effective +arrangement, even if they have no knowledge of regular expressions. +One thing to consider, for maximum effect, is to avoid using +multi-word keywords as those get hyphenated like the title and will +thus interfere with the above: either set the user option +~denote-allow-multi-word-keywords~ to nil or simply insert single +words at the relevant prompts. * Front matter :PROPERTIES: @@ -2801,6 +2869,7 @@ Everything is in place to set up the package. (define-key map (kbd "C-c n n") #'denote) (define-key map (kbd "C-c n N") #'denote-type) (define-key map (kbd "C-c n d") #'denote-date) + (define-key map (kbd "C-c n z") #'denote-signature) ; "zettelkasten" mnemonic (define-key map (kbd "C-c n s") #'denote-subdirectory) (define-key map (kbd "C-c n t") #'denote-template) ;; If you intend to use Denote with a variety of file types, it is @@ -2868,13 +2937,17 @@ might change them without further notice. + Variable ~denote-id-regexp~ :: Regular expression to match ~denote-id-format~. +#+vindex: denote-signature-regexp ++ Variable ~denote-signature-regexp~ :: Regular expression to match + the =SIGNATURE= field in a file name. + #+vindex: denote-title-regexp + Variable ~denote-title-regexp~ :: Regular expression to match the - TITLE field in a file name ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). + =TITLE= field in a file name ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). #+vindex: denote-keywords-regexp + Variable ~denote-keywords-regexp~ :: Regular expression to match the - KEYWORDS field in a file name ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). + =KEYWORDS= field in a file name ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). #+vindex: denote-excluded-punctuation-regexp + Variable ~denote-excluded-punctuation-regexp~ :: Punctionation that @@ -2900,6 +2973,10 @@ might change them without further notice. + Function ~denote-file-has-identifier-p~ :: Return non-nil if =FILE= has a Denote identifier. +#+findex: denote-file-has-signature-p ++ Function ~denote-file-has-signature-p~ :: Return non-nil if =FILE= + has a signature. [Part of {{{development-version}}}.] + #+findex: denote-file-has-supported-extension-p + Function ~denote-file-has-supported-extension-p~ :: Return non-nil if =FILE= has supported extension. Also account for the possibility @@ -3019,6 +3096,11 @@ might change them without further notice. one, refer to the ~denote-retrieve-or-create-file-identifier~ function. +#+findex: denote-retrieve-filename-signature ++ Function ~denote-retrieve-filename-signature~ :: Extract signature + from =FILE= name, if present, else return nil. [Part of + {{{development-version}}}.] + #+findex: denote-retrieve-or-create-file-identifier + Function ~denote-retrieve-or-create-file-identifier~ :: Return =FILE= identifier, generating one if appropriate. The conditions @@ -3059,6 +3141,10 @@ might change them without further notice. + Function ~denote-retrieve-keywords-line~ :: Return keywords line from =FILE= front matter per =FILE-TYPE=. +#+findex: denote-signature-prompt ++ Function ~denote-signature-prompt~ :: Prompt for signature + string. [Part of {{{development-version}}}.] + #+findex: denote-file-prompt + Function ~denote-file-prompt~ :: Prompt for file with identifier in variable ~denote-directory~. With optional =INITIAL-TEXT=, use it diff --git a/denote.el b/denote.el index da9955c9a8..9573540c36 100644 --- a/denote.el +++ b/denote.el @@ -223,6 +223,16 @@ The value is a list of symbols, which includes any of the following: value of that KEY is used to populate the new note with content, which is added after the front matter. +- `signature': Prompts for an arbitrary string that can be used + to establish a sequential relationship between files (e.g. 1, + 1a, 1b, 1b1, 1b2, ...). Signatures have no strictly defined + function and are up to the user to apply as they see fit. One + use-case is to implement Niklas Luhmann's Zettelkasten system + for a sequence of notes (Folgezettel). Signatures are not + included in a file's front matter and are not shown in the + description of a link. They are reserved solely for creating a + sequence in a file listing, at least for the time being. + The prompts occur in the given order. If the value of this user option is nil, no prompts are used. @@ -235,24 +245,29 @@ follows (read the manual for the technicalities): DATE--TITLE__KEYWORDS.EXT -If either or both of the `title' and `keywords' prompts are not -included in the value of this variable, file names will be any of -those permutations: +Depending on the inclusion of the `title', `keywords', and +`signature' prompts, file names will be any of those +permutations: DATE.EXT DATE--TITLE.EXT DATE__KEYWORDS.EXT + DATE==SIGNATURE.EXT + DATE==SIGNATURE--TITLE.EXT + DATE==SIGNATURE--TITLE__KEYWORDS.EXT + DATE==SIGNATURE__KEYWORDS.EXT -When in doubt, always include the `title' and `keywords' prompts. +When in doubt, always include the `title' and `keywords' +prompts (the default style). Finally, this user option only affects the interactive use of the `denote' command (advanced users can call it from Lisp). For ad-hoc interactive actions that do not change the default behaviour of the `denote' command, users can invoke these convenience commands: `denote-type', `denote-subdirectory', -`denote-date', `denote-template'." +`denote-date', `denote-template', `denote-signature'." :group 'denote - :package-version '(denote . "0.5.0") + :package-version '(denote . "2.0.0") :link '(info-link "(denote) The denote-prompts option") :type '(radio (const :tag "Use no prompts" nil) (set :tag "Available prompts" :greedy t @@ -261,7 +276,8 @@ convenience commands: `denote-type', `denote-subdirectory', (const :tag "Date" date) (const :tag "File type extension" file-type) (const :tag "Subdirectory" subdirectory) - (const :tag "Template" template)))) + (const :tag "Template" template) + (const :tag "Signature" signature)))) (defcustom denote-sort-keywords t "Whether to sort keywords in new files. @@ -444,6 +460,9 @@ The note's ID is derived from the date and time of its creation.") (defconst denote-id-regexp "\\([0-9]\\{8\\}\\)\\(T[0-9]\\{6\\}\\)" "Regular expression to match `denote-id-format'.") +(defconst denote-signature-regexp "==\\([[:alnum:][:nonascii:]]*\\)" + "Regular expression to match the SIGNATURE field in a file name.") + (define-obsolete-variable-alias 'denote--title-regexp 'denote-title-regexp @@ -604,6 +623,12 @@ and use one of the extensions implied by `denote-file-type'." 'denote-file-has-identifier-p "1.0.0") +(defun denote-file-has-signature-p (file) + "Return non-nil if FILE has a Denote identifier." + (when file + (string-match-p denote-signature-regexp + (file-name-nondirectory file)))) + (defun denote-file-directory-p (file) "Return non-nil if FILE is a directory. Omit FILE if it matches the value of user option @@ -1246,6 +1271,12 @@ To only return an existing identifier, refer to the function 'denote-retrieve-or-create-file-identifier "1.0.0") +(defun denote-retrieve-filename-signature (file) + "Extract signature from FILE name, if present, else return nil." + (when (denote-file-has-signature-p file) + (string-match denote-signature-regexp file) + (match-string 1 file))) + (defun denote-retrieve-filename-title (file) "Extract title from FILE name, else return `file-name-base'. Run `denote-desluggify' on title if the extraction is sucessful." @@ -1337,13 +1368,14 @@ Run `denote-desluggify' on title if the extraction is sucessful." ;;;;; Common helpers for new notes -(defun denote-format-file-name (path id keywords title-slug extension) +(defun denote-format-file-name (path id keywords title-slug extension &optional signature) "Format file name. -PATH, ID, KEYWORDS, TITLE-SLUG are expected to be supplied by -`denote' or equivalent: they will all be converted into a single -string. EXTENSION is the file type extension, as a string." +PATH, ID, KEYWORDS, TITLE-SLUG, EXTENSION and optional SIGNATURE +are expected to be supplied by `denote' or equivalent command." (let ((kws (denote--keywords-combine keywords)) (file-name (concat path id))) + (when (and signature (not (string-empty-p signature))) + (setq file-name (concat file-name "==" signature))) (when (and title-slug (not (string-empty-p title-slug))) (setq file-name (concat file-name "--" title-slug))) (when (and keywords (not (string-blank-p kws))) @@ -1378,13 +1410,17 @@ provided by `denote'. FILETYPE is one of the values of (kws (denote--format-front-matter-keywords keywords filetype))) (if fm (format fm title date kws id) ""))) -(defun denote--path (title keywords dir id file-type) - "Return path to new file with ID, TITLE, KEYWORDS and FILE-TYPE in DIR." +(defun denote--path (title keywords dir id file-type &optional signature) + "Return path to new file. +Use ID, TITLE, KEYWORDS, FILE-TYPE and optional SIGNATURE to +construct path to DIR." (denote-format-file-name dir id (denote-sluggify-keywords keywords) (denote-sluggify title) - (denote--file-extension file-type))) + (denote--file-extension file-type) + (when signature + (denote--slug-no-punct signature)))) ;; Adapted from `org-hugo--org-date-time-to-rfc3339' in the `ox-hugo' ;; package: <https://github.com/kaushalmodi/ox-hugo>. @@ -1428,12 +1464,13 @@ provided by `denote'. FILETYPE is one of the values of (t (denote-date-org-timestamp date))))) -(defun denote--prepare-note (title keywords date id directory file-type template) +(defun denote--prepare-note (title keywords date id directory file-type template &optional signature) "Prepare a new note file. Arguments TITLE, KEYWORDS, DATE, ID, DIRECTORY, FILE-TYPE, -and TEMPLATE should be valid for note creation." - (let* ((path (denote--path title keywords directory id file-type)) +TEMPLATE, and optional SIGNATURE should be valid for note +creation." + (let* ((path (denote--path title keywords directory id file-type signature)) (buffer (find-file path)) (header (denote--format-front-matter title (denote--date date file-type) keywords @@ -1515,7 +1552,7 @@ where the former does not read dates without a time component." ;;;;; The `denote' command and its prompts ;;;###autoload -(defun denote (&optional title keywords file-type subdirectory date template) +(defun denote (&optional title keywords file-type subdirectory date template signature) "Create a new note with the appropriate metadata and file name. When called interactively, the metadata and file name are prompted @@ -1542,9 +1579,11 @@ When called from Lisp, all arguments are optional. - TEMPLATE is a symbol which represents the key of a cons cell in the user option `denote-templates'. The value of that key is - inserted to the newly created buffer after the front matter." + inserted to the newly created buffer after the front matter. + +- SIGNATURE is a string or a function returning a string." (interactive - (let ((args (make-vector 6 nil))) + (let ((args (make-vector 7 nil))) (dolist (prompt denote-prompts) (pcase prompt ('title (aset args 0 (denote-title-prompt @@ -1556,7 +1595,8 @@ When called from Lisp, all arguments are optional. ('file-type (aset args 2 (denote-file-type-prompt))) ('subdirectory (aset args 3 (denote-subdirectory-prompt))) ('date (aset args 4 (denote-date-prompt))) - ('template (aset args 5 (denote-template-prompt))))) + ('template (aset args 5 (denote-template-prompt))) + ('signature (aset args 6 (denote-signature-prompt))))) (append args nil))) (let* ((title (or title "")) (file-type (denote--valid-file-type (or file-type denote-file-type))) @@ -1572,9 +1612,10 @@ When called from Lisp, all arguments are optional. (denote-directory))) (template (if (stringp template) template - (or (alist-get template denote-templates) "")))) + (or (alist-get template denote-templates) ""))) + (signature (or signature ""))) (denote-barf-duplicate-id id) - (denote--prepare-note title kws date id directory file-type template) + (denote--prepare-note title kws date id directory file-type template signature) (denote--keywords-add-to-history keywords))) (defvar denote--title-history nil @@ -1691,6 +1732,13 @@ packages such as `marginalia' and `embark')." 'denote-template-prompt "1.0.0") +(defvar denote--signature-history nil + "Minibuffer history of `denote-signature-prompt'.") + +(defun denote-signature-prompt () + "Prompt for signature string." + (read-string "Provide signature: " nil 'denote--signature-history)) + ;;;;; Convenience commands as `denote' variants (defalias 'denote-create-note 'denote @@ -1764,6 +1812,20 @@ set to \\='(template title keywords)." (defalias 'denote-create-note-with-template 'denote-template "Alias of `denote-template' command.") +;;;###autoload +(defun denote-signature () + "Create note while prompting for a file signature. + +This is the equivalent to calling `denote' when `denote-prompts' +is set to \\='(signature title keywords)." + (declare (interactive-only t)) + (interactive) + (let ((denote-prompts '(signature title keywords))) + (call-interactively #'denote))) + +(defalias 'denote-create-note-using-signature 'denote-signature + "Alias of `denote-signature' command.") + ;;;;; Other convenience commands (defun denote--extract-title-from-file-history () @@ -2122,10 +2184,11 @@ files)." current-prefix-arg))) (let* ((dir (file-name-directory file)) (id (denote-retrieve-or-create-file-identifier file date)) + (signature (denote-retrieve-filename-signature file)) (extension (file-name-extension file t)) (file-type (denote-filetype-heuristics file)) (new-name (denote-format-file-name - dir id keywords (denote-sluggify title) extension)) + dir id keywords (denote-sluggify title) extension signature)) (max-mini-window-height 0.33)) ; allow minibuffer to be resized (when (denote-rename-file-prompt file new-name) (denote-rename-file-and-buffer file new-name) @@ -2212,11 +2275,12 @@ The operation does the following: (dolist (file marks) (let* ((dir (file-name-directory file)) (id (denote-retrieve-or-create-file-identifier file)) + (signature (denote-retrieve-filename-signature file)) (file-type (denote-filetype-heuristics file)) (title (denote--retrieve-title-or-filename file file-type)) (extension (file-name-extension file t)) (new-name (denote-format-file-name - dir id keywords (denote-sluggify title) extension))) + dir id keywords (denote-sluggify title) extension signature))) (denote-rename-file-and-buffer file new-name) (when (denote-file-is-writable-and-supported-p new-name) (if (denote--edit-front-matter-p new-name file-type) @@ -2259,9 +2323,10 @@ proceed with the renaming." (keywords (denote-retrieve-keywords-value file file-type)) (extension (file-name-extension file t)) (id (denote-retrieve-or-create-file-identifier file)) + (signature (denote-retrieve-filename-signature file)) (dir (file-name-directory file)) (new-name (denote-format-file-name - dir id keywords (denote-sluggify title) extension))) + dir id keywords (denote-sluggify title) extension signature))) (when (or auto-confirm (denote-rename-file-prompt file new-name)) (denote-rename-file-and-buffer file new-name) @@ -2306,12 +2371,13 @@ their respective front matter." (dolist (file marks) (let* ((dir (file-name-directory file)) (id (denote-retrieve-or-create-file-identifier file)) + (signature (denote-retrieve-filename-signature file)) (file-type (denote-filetype-heuristics file)) (title (denote-retrieve-title-value file file-type)) (keywords (denote-retrieve-keywords-value file file-type)) (extension (file-name-extension file t)) (new-name (denote-format-file-name - dir id keywords (denote-sluggify title) extension))) + dir id keywords (denote-sluggify title) extension signature))) (denote-rename-file-and-buffer file new-name))) (revert-buffer)) (user-error "No marked files; aborting"))) @@ -2407,6 +2473,11 @@ and seconds." :group 'denote-faces :package-version '(denote . "0.1.0")) +(defface denote-faces-signature '((t :inherit font-lock-warning-face)) + "Face for file name signature in Dired buffers." + :group 'denote-faces + :package-version '(denote . "2.0.0")) + (defface denote-faces-delimiter '((((class color) (min-colors 88) (background light)) :foreground "gray70") @@ -2420,9 +2491,10 @@ and seconds." ;; For character classes, evaluate: (info "(elisp) Char Classes") (defvar denote-faces--file-name-regexp (concat "\\(?1:[0-9]\\{8\\}\\)\\(?2:T[0-9]\\{6\\}\\)" - "\\(?:\\(?3:--\\)\\(?4:[[:alnum:][:nonascii:]-]*\\)\\)?" - "\\(?:\\(?5:__\\)\\(?6:[[:alnum:][:nonascii:]_-]*\\)\\)?" - "\\(?7:\\..*\\)?$") + "\\(?:\\(?3:==\\)\\(?4:[[:alnum:][:nonascii:]]*?\\)\\)?" + "\\(?:\\(?5:--\\)\\(?6:[[:alnum:][:nonascii:]-]*?\\)\\)?" + "\\(?:\\(?7:__\\)\\(?8:[[:alnum:][:nonascii:]_-]*?\\)\\)?" + "\\(?9:\\..*\\)?$") "Regexp of file names for fontification.") (defconst denote-faces-file-name-keywords @@ -2430,22 +2502,26 @@ and seconds." (1 'denote-faces-date) (2 'denote-faces-time) (3 'denote-faces-delimiter nil t) - (4 'denote-faces-title nil t) + (4 'denote-faces-signature nil t) (5 'denote-faces-delimiter nil t) - (6 'denote-faces-keywords nil t) - (7 'denote-faces-extension nil t ))) + (6 'denote-faces-title nil t) + (7 'denote-faces-delimiter nil t) + (8 'denote-faces-keywords nil t) + (9 'denote-faces-extension nil t ))) "Keywords for fontification of file names.") (defconst denote-faces-file-name-keywords-for-backlinks - `((,(concat "^\\(?8:.*/\\)?" denote-faces--file-name-regexp) - (8 'denote-faces-subdirectory nil t) + `((,(concat "^\\(?10:.*/\\)?" denote-faces--file-name-regexp) + (10 'denote-faces-subdirectory nil t) (1 'denote-faces-date) (2 'denote-faces-time) (3 'denote-faces-delimiter nil t) - (4 'denote-faces-title nil t) + (4 'denote-faces-signature nil t) (5 'denote-faces-delimiter nil t) - (6 'denote-faces-keywords nil t) - (7 'denote-faces-extension nil t))) + (6 'denote-faces-title nil t) + (7 'denote-faces-delimiter nil t) + (8 'denote-faces-keywords nil t) + (9 'denote-faces-extension nil t ))) "Keywords for fontification of file names in the backlinks buffer.") ;;;; Fontification in Dired