commit - 2b7a791c1f2ee8221fd8c891bb137bdb0a7ae588
commit + 984e27bda33195f6f907a564679017b5a3ec9388
blob - /dev/null
blob + 1f7c4b790168032cdf15e155f3d3f3419b908ce0 (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/.dir-locals.el
+;;; Directory Local Variables
+;;; For more information see (info "(emacs) Directory Variables")
+
+((emacs-lisp-mode . ((indent-tabs-mode . nil))))
blob - /dev/null
blob + 4f545859df79e2432e5f20cc98e1f5a13b2791f2 (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/CHANGELOG.org
+#+title: Change log of Denote
+#+author: Protesilaos Stavrou
+#+email: info@protesilaos.com
+#+language: en
+#+options: ':t toc:nil author:t email:t num:t
+#+startup: content
+
+This document contains the release notes for each tagged commit on the
+project's main git repository: <https://github.com/protesilaos/denote>.
+
+The newest release is at the top. For further details, please consult
+the manual: <https://protesilaos.com/emacs/denote>.
+
+#+toc: headlines 1 insert TOC here, with one headline level
+
+* Version 4.0.0 on 2025-04-15
+:PROPERTIES:
+:CUSTOM_ID: h:8a134846-72cc-4fbf-830d-6ca9fd0f9ec8
+:END:
+
+This is a massive release. There is one breaking change, which should
+be easy to adapt to: this pertains to the reorganisation of the
+project to separate the "core" of Denote from its "extensions". The
+core is the ~denote~ package. Each extension now has its own package
+(details below).
+
+Other than that, this version includes lots of new features for
+searching and linking as well as quality-of-life refinements. We have
+generalised the infrastructure for performing queries in the
+~denote-directory~ and made the buffers with the search results more
+useful.
+
+Take your time to read through this publication. I am writing it for
+you. Also remember that the most up-to-date resource for anything
+related to Denote is its manual. You are always welcome to contact me:
+<https://protesilaos.com/contact>. Or join the development on the Git
+repository.
+
+As usual, special thanks to Jean-Philippe Gagné Guay for making high
+quality contributions to Denote since the beginning of the project ~3
+years ago. Those will not always be headline features, but are
+important improvements to the underlying code base.
+
+I mention contributions from Jean-Philippe and others in its context.
+Though I do not cover implementation details, otherwise this document
+will be the size of a book. This does not mean that they are no
+important though. Please consult the Git commit log for all the
+technicalities.
+
+** All the "extras" are in separate packages, including the Org dynamic blocks
+:PROPERTIES:
+:CUSTOM_ID: h:26ed2af1-60c8-4217-93b3-bbe344e4eb7b
+:END:
+
+In previous versions of Denote, we included some optional extensions
+as part of the ~denote~ package. These included the files
+=denote-org-extras.el= (Org dynamic blocks, among others),
+=denote-journal-extras.el= (streamlined for journaling),
+=denote-silo-extras.el= (working with multiple Denote silos).
+
+The files =denote-md-extras.el= (Markdown extras) and
+=denote-sequence.el= (sequence notes, including Luhmann-style
+alphanumeric sequences) were also part of the project during the last
+development cycle, though they never made it into a tagged release.
+
+All these are now available as standalone packages on the official GNU
+ELPA archive:
+
+- ~denote-org~ :: In the Emacs configuration file, replace all
+ instances of =denote-org-extras= with =denote-org=.
+
+- ~denote-journal~ :: Replace =denote-journal-extras= with =denote-journal=.
+
+- ~denote-silo~ :: Replace =denote-silo-extras= with =denote-silo=.
+
+- ~denote-markdown~ :: Replace =denote-md-extras= with =denote-markdown=.
+
+- ~denote-sequence~ :: No changes to any of the defined symbols.
+ Simply get the new package.
+
+I will document each of these packages further below. The plan, going
+forward, is to maintain all the packages and coordinate their new
+versions.
+
+** More things in "core"
+:PROPERTIES:
+:CUSTOM_ID: h:3820e9cf-f034-4c3c-a4ed-1e7d11f1cd23
+:END:
+
+While the extras are moved out to their own code repositories, all
+other features are merged into =denote.el=. Those include everything
+that was in =denote-sort.el= and =denote-rename-buffer.el=.
+
+- The "sort" mechanism is mostly for package developers. We use it
+ extensively in our Org dynamic blocks, which are now part of the
+ ~denote-org~ package.
+
+- The ~denote-dired~ command (alias ~denote-sort-dired~) is the only
+ user-facing "sort" command we have always provided. It produces a
+ fully fledged Dired buffer showing the results of the given search
+ for file names. The matching files are sorted according to the
+ user's expressed preference. The details are described in the
+ manual.
+
+- The ~denote-rename-buffer-mode~ and all of its user options are
+ unchanged. This mode automatically renames the buffer of a given
+ Denote file so that it is easier to read it. Again, the manual
+ covers the technicalities.
+
+Users do not need to make changes, unless they are explicitly loading
+=denote-sort-dired= and =denote-rename-buffer=. In that case, they may
+just remove those calls: only ~denote~ needs to be loaded.
+
+** The ~denote-query-mode~
+:PROPERTIES:
+:CUSTOM_ID: h:36f305e2-310d-4327-941a-ca0570b473d2
+:END:
+
+Many of the features I will describe below produce search results via
+the built-in Xref mechanism. Xref performs a search with a Grep or
+Grep-like program, subject to the user option ~xref-search-program~.
+The buffer those search results are displayed in runs the
+~denote-query-mode~. It supersedes ~denote-backlinks-mode~.
+
+The ~denote-query-mode~ supports the following:
+
+- Results are shown in the context, with the exact match in highlight.
+- Matches are grouped by file. Each file is a "heading".
+- Headings can be folded with =TAB=, just how it is done in Org buffers.
+- The results can be used for further queries. Type =C-h m=
+ (~describe-mode~) to learn about all the relevant commands.
+
+We have had support for Xref since the original version of Denote. It
+now is more generalised to cover backlinks, query links, and
+~denote-grep~ (more below).
+
+** Use query links for file contents or file names
+:PROPERTIES:
+:CUSTOM_ID: h:c217a37a-db73-46bd-ab5f-3f9c54f9d53b
+:END:
+
+Denote has always provided the option to link directly to a file with
+a given name by referencing its identifier. This can be done with the
+command ~denote-link~, among a few others like it (always consult the
+manual of Denote).
+
+In addition to these "direct links", we also support "query links".
+Those do not point to a file but instead trigger a search. The results
+are placed in a buffer that uses the appropriate major mode.
+
+There are two types of query links:
+
+- Query file contents :: Use the command ~denote-query-contents-link~
+ to insert a query link at point for "file contents". It perform a
+ search inside files in the ~denote-directory~ and put the results in
+ a ~denote-query-mode~ buffer.
+
+- Query file names :: Use the ~denote-query-filenames-link~ to insert
+ a query link for "file names". It performs the query against file
+ names (not contents!) and puts the results in a ~dired~ buffer.
+
+The display of the buffer with the query link results is controlled by
+the user option ~denote-query-links-display-buffer-action~.
+
+Query links are styled a little bit differently than direct links.
+Compare the ~denote-faces-link~ with ~denote-faces-query-link~. Both
+should look okay with most themes.
+
+Denote query links are supported as part of the =denote:= hyperlink
+type. They are available in all file types we define (per the user
+option ~denote-file-type~) and should, in principle, work in any
+custom file type (advanced users can check the variable ~denote-file-types~).
+
+** Backlinks now always show their context
+:PROPERTIES:
+:CUSTOM_ID: h:8ebc6ae8-1087-46fa-a0ec-464749f0ac4d
+:END:
+
+In the past, the command ~denote-backlinks~ would produce a bespoke
+buffer showing a list of file names that included links to the current
+file (any file with the Denote file-naming scheme can have backlinks,
+by the way, including PDFs, videos, etc.). This buffer did not provide
+any additional functionality. We used to support the option to show
+results in their context via ~denote-backlinks-show-context~. Those
+would be rendered in a standard Xref buffer.
+
+The contextual results are now the default and sole option. This is
+because we have expanded the functionality of those buffers to use the
+~denote-query-mode~, as explained above. Plus, it makes our code base
+simpler.
+
+Users will notice how backlikns look just like a query link for file
+contents. This is because backlinks are the original query links since
+day one of Denote.
+
+** Direct links to a file with matching contents
+:PROPERTIES:
+:CUSTOM_ID: h:a1a7b766-328d-4883-93b2-c68b49bd1aa3
+:END:
+
+The command ~denote-link-to-file-with-contents~ allows users to
+produce a direct link to a file whose contents (not file name!)
+includes the given query.
+
+Similarly, the command ~denote-link-to-all-files-with-contents~
+generates a typographic list (bullet list) to all files whose contents
+match the given query.
+
+The manual covers all linking commands in depth.
+
+** The essence of ~denote-search~ is part of ~denote~
+:PROPERTIES:
+:CUSTOM_ID: h:5d2ac378-304c-4ea8-bbfb-b3f7b649b27d
+:END:
+
+The ~denote-search~ package by Lucas Quintana uses the infrastructure
+of Denote to perform searches in file contents. We now provide its
+feature set as part of core ~denote~.
+
+We decided to do this since query links already introduced all of the
+requisite generalisations to ~denote-query-mode~.
+
+Users can rely on the commands ~denote-grep~, ~denote-grep-marked-dired-files~,
+and ~denote-grep-files-referenced-in-region~.
+
+The placement of these buffers is subject to the user option
+~denote-grep-display-buffer-action~.
+
+This functionality was introduced in two pull requests by Lucas
+Quintana, 571 and 573, with further changes by me:
+
+- <https://github.com/protesilaos/denote/pull/571>.
+- <https://github.com/protesilaos/denote/pull/573>.
+
+Lucas has assigned copyright to the Free Software Foundation.
+
+I think this was a much-needed addition to the core of Denote. It
+complements ~denote-dired~ and query links.
+
+** Formatting of links with ~denote-link-description-format~
+:PROPERTIES:
+:CUSTOM_ID: h:635b7f04-891a-4a8b-b420-4e0d9dadc232
+:END:
+
+The old user option ~denote-link-description-function~ is deprecated
+and superseded by the new ~denote-link-description-format~. The new
+user option still accepts a custom function as its value, so the old
+behaviour should be retained.
+
+What the new ~denote-link-description-format~ supports is an easier
+way to customise the description of a link by using format specifiers
+for common options. For example, users who only want to see the title
+of the linked file can do this:
+
+#+begin_src emacs-lisp
+(setq denote-link-description-format "%t")
+#+end_src
+
+The documentation of this user option covers all the format specifiers
+and further details.
+
+** Miscellaneous changes for all users
+:PROPERTIES:
+:CUSTOM_ID: h:76a56ab5-c44b-4c67-8048-25dd0dd88dcf
+:END:
+
+- The command ~denote-add-front-matter~ is superseded by
+ ~denote-rename-file~ and related. Those renaming commands will add
+ missing front matter or rewrite the modified lines of existing front
+ matter. This is due to refinements made by Jean-Philippe Gagné Guay
+ to the file renaming mechanism. We discussed this deprecation in
+ issue 498: <https://github.com/protesilaos/denote/issues/498>. Also
+ thanks to Samuel Flint for reporting an earlier problem with file
+ name signatures: <https://github.com/protesilaos/denote/issues/492>.
+
+- The user option ~denote-open-link-function~ specifies the function
+ used by Denote to open the file of a direct link.
+
+- The user option ~denote-org-store-link-to-heading~ can now be set to
+ form generic context links without a =PROPERTIES= drawer and
+ corresponding =CUSTOM_ID=. Set the value of this variable to
+ ='context=. Read its documentation for further details.
+
+- Also about ~denote-org-store-link-to-heading~, we have changed its
+ default value to ~nil~, which is what we were doing for most of
+ Denote's history. This means that, by default, ~org-store-link~ and
+ anything building on top of it will create a link only to the
+ current Denote file, like =denote:IDENTIFIER=, but not to the
+ current heading within that file. To create links to the
+ file+heading, set the value of this variable to ='id=.
+
+- The command ~denote-dired-link-marked-notes~ is an alias for
+ ~denote-link-dired-marked-notes~.
+
+- The user option ~denote-sort-dired-extra-prompts~ control what
+ ~denote-dired~ (alias ~denote-sort-dired~) prompts for. It accepts
+ either a nil value or a list of symbols among ~sort-by-component~,
+ ~reverse-sort~, and ~exclude-regexp~. The order those symbols appear
+ in the list is significant, with the leftmost coming first.
+
+- There is a new ~denote-sort-identifier-comparison-function~ variable
+ which determines how identifier-based sorting should be done by
+ default. It complements the existing ~denote-sort-title-comparison-function~,
+ ~denote-sort-keywords-comparison-function~, ~denote-sort-signature-comparison-function~.
+ Thanks to Maikol Solís for the contribution in pull request 517:
+ <https://github.com/protesilaos/denote/pull/517>. The change is
+ small, meaning that Maikol does not need to assign copyright to the
+ Free Software Foundation (though I believe the paperwork is done, anyway).
+
+- Lots of refinements to the doc strings of individual variables
+ and/or functions as well as the manual.
+
+- Lots of other contributions to discussions and questions on the Git
+ repository. Granted, these are not "changes" per se but are part of
+ the development effort nonetheless.
+
+- Made ~denote-get-path-by-id~ use ~denote-get-file-extension-sans-encryption~
+ instead of ~denote-get-file-extension~. This fixes a bug where the
+ extension is duplicated if it has an encryption component. Thanks to
+ eum3l for the patch in pull request 562: <https://github.com/protesilaos/denote/pull/562>.
+ The change is small, meaning that the author does not need to assign
+ copyright to the Free Software Foundation.
+
+- Same as above for ~denote--rename-file~, which was done in pull
+ request 557: <https://github.com/protesilaos/denote/pull/557>.
+
+** For developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:7eaf43a6-7d62-440e-bf7c-8d9536c7d36e
+:END:
+
+The following have been added or modified.
+
++ NEW Function ~denote-file-has-denoted-filename-p~ :: Return non-nil
+ if =FILE= respects the file-naming scheme of Denote. This tests the
+ rules of Denote's file-naming scheme. Sluggification is ignored. It
+ is done by removing all file name components and validating what
+ remains. Thanks to Jean-Philippe Gagné Guay for the pull request
+ 515: <https://github.com/protesilaos/denote/pull/515>.
+
++ NEW Functions ~denote-infer-keywords-from-files~ :: Return list of
+ keywords in ~denote-directory-files~. With optional
+ =FILES-MATCHING-REGEXP=, only extract keywords from the matching
+ files. Otherwise, do it for all files. Keep any duplicates. Users
+ who do not want duplicates should refer to the functions
+ ~denote-keywords~.
+
++ MODIFIED Function ~denote-keywords~ :: Returns an appropriate list
+ of keyword candidates, while accounting for the value of the user
+ option ~denote-infer-keywords~. It now also accepts the optional
+ =FILES-MATCHING-REGEXP= parameter.
+
++ MODIFIED Function ~denote-directory-files~ :: Returns a list of
+ absolute file paths in variable ~denote-directory~. It now accepts
+ the optional =EXCLUDE-REGEXP= parameter.
+
++ MODIFIED Function ~denote-format-file-name~ :: Formats a file name.
+ The way it treats its =ID= parameter has changed. Please read its
+ doc string. Thanks to Jean-Philippe Gagné Guay for the pull request
+ 496: <https://github.com/protesilaos/denote/pull/496>.
+
++ ALIAS Function ~denote-retrieve-filename-keywords-as-list~ :: This
+ is a name that is easier to discover than ~denote-extract-keywords-from-path~,
+ because of the many other functions with the =denote-retrieve-*= prefix.
+
++ MODIFIED Function ~denote-retrieve-filename-identifier~ :: Extracts
+ the identifier from =FILE= name, if present, else returns nil. To
+ create a new one from a date, refer to the ~denote-get-identifier~
+ function. Thanks to Jean-Philippe Gagné Guay for the pull request
+ 476: <https://github.com/protesilaos/denote/pull/476>.
+
++ MODIFIED Function ~denote-get-identifier~ :: Converts =DATE= into a
+ Denote identifier using ~denote-id-format~. If =DATE= is nil, it
+ returns an empty string as the identifier. Also by Jean-Philippe in
+ pull request 476 mentioned right above.
+
++ MODIFIED Function ~denote-date-prompt~ :: Prompts for a date,
+ expecting =YYYY-MM-DD= or that plus =HH:MM= (or even =HH:MM:SS=).
+ Can also use Org's more advanced date selection utility if the user
+ option ~denote-date-prompt-use-org-read-date~ is non-nil. It now has
+ the optional parameters =INITIAL-DATE= and =PROMPT-TEXT=. Thanks to
+ Jean-Philippe Gagné Guay for the pull request 576:
+ <https://github.com/protesilaos/denote/pull/576>.
+
+- NEW Function ~denote-retrieve-groups-xref-query~ :: Accesses the
+ location of xrefs for =QUERY= and group them per file. Limit the
+ search to text files.
+
+- NEW Function ~denote-retrieve-files-xref-query~ :: Returns sorted,
+ deduplicated file names with matches for =QUERY= in their contents.
+ Limits the search to text files.
+
+- NEW Function ~denote-retrieve-xref-alist~ :: Returns xref alist of
+ files with the location of matches for =QUERY=. With optional
+ =FILES-MATCHING-REGEXP=, it limits the list of files accordingly
+ (per ~denote-directory-files~). At all times, it limits the search
+ to text files.
+
++ NEW Function ~denote-prepend-front-matter~ :: Prepend front matter
+ to =FILE=. The =TITLE=, =KEYWORDS=, =DATE=, =ID=, =SIGNATURE=, and
+ =FILE-TYPE= are passed from the renaming command and are used to
+ construct a new front matter block if appropriate.
+
++ MODIFIED Function ~denote-rewrite-front-matter~ :: Rewrites front
+ matter of note after ~denote-rename-file~ (or related). The =FILE=,
+ =TITLE=, =KEYWORDS=, =SIGNATURE=, =DATE=, =IDENTIFIER=, and
+ =FILE-TYPE= arguments are given by the renaming command and are used
+ to construct new front matter values if appropriate. If
+ ~denote-rename-confirmations~ contains ~rewrite-front-matter~,
+ prompt to confirm the rewriting of the front matter. Otherwise
+ produce a ~y-or-n-p~ prompt to that effect. Thanks to
+ Jean-Philippe Gagné Guay for the pull request 558:
+ <https://github.com/protesilaos/denote/pull/558>.
+
+** Denote "extensions" that are not in the ~denote~ package anymore
+:PROPERTIES:
+:CUSTOM_ID: h:40e030cd-f462-44ce-add9-ab1525359ae6
+:END:
+
+*** ~denote-journal~ integrates nicely with =M-x calendar=
+:PROPERTIES:
+:CUSTOM_ID: h:f8ab710d-852f-4d8b-b0f8-9a24c5c83808
+:END:
+
+The ~calendar~ can now highlight days that have journal entry. It may
+also be used as a date picker to view or write a journal entry for
+that day.
+
+- Thanks to Alan Schmitt for reporting an issue with the calendar
+ integration during development:
+ <https://github.com/protesilaos/denote-journal/issues/8>.
+
+- Thanks to Vineet C. Kulkarni for tweaking the identification of the
+ journal keyword to be more robust:
+ <https://github.com/protesilaos/denote-journal/pull/4>.
+
+- Thanks to Honza Pokorny for fixing two small issues with the path
+ expansion:
+
+ - <https://github.com/protesilaos/denote-journal/pull/5>
+ - <https://github.com/protesilaos/denote-journal/pull/7>
+
+Other than that, the package is providing the same functionality as
+the discontinued =denote-journal-extras.el=.
+
+- Manual: <https://protesilaos.com/emacs/denote-journal>.
+- GitHub: <https://github.com/protesilaos/denote-journal>.
+
+*** ~denote-org~ is almost the same as the discontinued =denote-org-extras.el=
+:PROPERTIES:
+:CUSTOM_ID: h:db491fe0-0c96-4c2e-9320-dc2697106e12
+:END:
+
+The only addition to dynamic blocks the optional =:not-regexp= parameter.
+This is a regular expression that can further filter the results of a
+search, such that the matching items are removed from the output.
+
+The official manual of ~denote-org~ covers the technicalities.
+
+- Manual: <https://protesilaos.com/emacs/denote-org>.
+- GitHub: <https://github.com/protesilaos/denote-org>.
+
+Also thanks to Elias Storms for fixing a small issue with the "missing
+links" Org dynamic block, in pull request 486: <https://github.com/protesilaos/denote/pull/486>
+
+*** ~denote-silo~ is the same as the discontinued =denote-silo-extras.el=
+:PROPERTIES:
+:CUSTOM_ID: h:ed7c77f5-9b44-4e76-9ada-80ff0ed6d5f6
+:END:
+
+I have only made small tweaks to it, but nothing that changes the user
+experience.
+
++ Manual: <https://protesilaos.com/emacs/denote-silo>
++ GitHub: <https://github.com/protesilaos/denote-silo>
+
+*** ~denote-markdown~ for some Markdown-specific extras
+:PROPERTIES:
+:CUSTOM_ID: h:e01d236c-fb50-488f-9fb2-15e866fa122a
+:END:
+
+This package provides some convenience functions to better integrate
+Markdown with Denote. This is mostly about converting links from one
+type to another so that they can work in different applications
+(because Markdown does not have a standardised way to define custom
+link types). It also defines an "Obsidian" file type which does not
+have any front matter but only uses a level 1 heading for the title of
+the note.
+
+The code of ~denote-markdown~ used to be bundled up with the ~denote~
+package before version =4.0.0= of the latter and was available in the
+file =denote-md-extras.el=. Users of the old code will need to adapt
+their setup to use the ~denote-markdown~ package. This can be done by
+replacing all instances of =denote-md-extras= with =denote-markdown=
+across their configuration.
+
++ Manual: <https://protesilaos.com/emacs/denote-markdown>
++ GitHub: <https://github.com/protesilaos/denote-markdown>
+
+*** Write sequence notes (or "folgezettel") with ~denote-sequence~
+:PROPERTIES:
+:CUSTOM_ID: h:6181df9e-790f-4fcf-8093-cefbba324cb5
+:END:
+
+Users who want their notes to have an inherent structure can use
+~denote-sequence~. The idea is to have thoughts that naturally form
+sequences and are named accordingly. The sequence scheme is either
+numeric or alphanumeric. The manual of the package explains all the
+details.
+
++ Manual: <https://protesilaos.com/emacs/denote-sequence>
++ GitHub: <https://github.com/protesilaos/denote-sequence>
+
+I had a lot of fun developing this comprehensive package during the
+winter holidays.
+
+Thanks to Claudio Migliorelli, Kierin Bell, Mirko Hernandez for
+helping me fix some issues during development:
+
+- <https://github.com/protesilaos/denote/pull/518>.
+- <https://github.com/protesilaos/denote/pull/528>.
+- <https://github.com/protesilaos/denote/pull/540>.
+- <https://github.com/protesilaos/denote/pull/541>.
+- <https://github.com/protesilaos/denote-sequence/issues/2>.
+
+** The ~consult-denote~ also gets a small update
+:PROPERTIES:
+:CUSTOM_ID: h:90a9287d-a7dd-4d65-9214-4be6ebdf5943
+:END:
+
+This has always been a standalone package. I made the function
+~consult-denote-file-prompt~ read the special-purpose variable
+~denote-file-prompt-use-files-matching-regexp~. This is related to
+commit =e0f1d47= in denote.git, about issue 536 as reported by Alan
+Schmitt: <https://github.com/protesilaos/denote/issues/536>. The
+variable =denote-file-prompt-use-files-matching-regexp= is meant to be
+~let~ bound and is for advanced users or developers.
+
+** Feature freeze at least until the end of April 2025
+:PROPERTIES:
+:CUSTOM_ID: h:8624e698-90cd-429e-a072-b0fa2df76662
+:END:
+
+I will not develop new features or accept pull request for a couple of
+weeks. The idea is to focus on fixing any bug reports. We can then
+publish point releases quickly.
+
+New features can be included after we are confident that the packages
+we have are okay.
+
+** Git commits
+:PROPERTIES:
+:CUSTOM_ID: h:5191b423-6dc5-4ca7-9bcc-39797be5707c
+:END:
+
+This is just an overview of the Git commits, though remember that
+there is more that goes into a project, such as the reporting of
+inconsistencies, discussion of new ideas, et cetera. Thanks to
+everybody involved! Plus, some commits are large while others are
+tiny.
+
+#+begin_src
+~/Git/Projects/denote $ git shortlog 3.1.0..4.0.0 --summary --numbered
+ 470 Protesilaos Stavrou
+ 90 Jean-Philippe Gagné Guay
+ 6 Kierin Bell
+ 4 Alan Schmitt
+ 3 eum3l
+ 2 Claudio Migliorelli
+ 2 Lucas Quintana
+ 2 grtcdr
+ 1 Elias Storms
+ 1 Laurent Gatto
+ 1 Maikol Solís
+ 1 Octavian
+ 1 TomoeMami
+#+end_src
+
+The following are not accurate because they only reflect the changes
+after the reorganisation I made. But we have to start from somewhere.
+
+#+begin_src
+~/Git/Projects/denote-journal $ git shortlog --summary --numbered
+ 54 Protesilaos Stavrou
+ 2 Honza Pokorny
+ 1 Vineet C. Kulkarni
+#+end_src
+
+#+begin_src
+~/Git/Projects/denote-sequence $ git shortlog --summary --numbered
+ 22 Protesilaos Stavrou
+#+end_src
+
+#+begin_src
+~/Git/Projects/denote-silo $ git shortlog --summary --numbered
+ 17 Protesilaos Stavrou
+#+end_src
+
+#+begin_src
+~/Git/Projects/denote-org $ git shortlog --summary --numbered
+ 15 Protesilaos Stavrou
+#+end_src
+
+#+begin_src
+~/Git/Projects/denote-markdown $ git shortlog --summary --numbered
+ 11 Protesilaos Stavrou
+#+end_src
+
+* Version 3.1.0 on 2024-09-04
+:PROPERTIES:
+:CUSTOM_ID: h:f089ab11-4ad7-4fd9-9bf3-2deb2e070297
+:END:
+
+Denote is stable and reliable though we keep adding minor refinements
+to it. Remember that many---if not all---of these are intended for
+experienced users who have developed their own workflow and want to
+adapt Denote to its particularities. We may call them "power users".
+
+New users do not need to know about every single feature. A basic
+configuration is enough and is why the original video I did about
+Denote (from even before I published version =0.1.0=) is still relevant.
+For example:
+
+#+begin_src emacs-lisp
+;; Start with something like this.
+(use-package denote
+ :ensure t
+ :bind
+ (("C-c n n" . denote)
+ ("C-c n r" . denote-rename-file)
+ ("C-c n i" . denote-link) ; "insert" mnemonic
+ ("C-c n b" . denote-backlinks))
+ :config
+ (setq denote-directory (expand-file-name "~/Documents/notes/")))
+#+end_src
+
+And here is the same idea with a little bit more convenience:
+
+#+begin_src emacs-lisp
+;; Another basic setup with a little more to it.
+(use-package denote
+ :ensure t
+ :hook (dired-mode . denote-dired-mode)
+ :bind
+ (("C-c n n" . denote)
+ ("C-c n r" . denote-rename-file)
+ ("C-c n l" . denote-link)
+ ("C-c n b" . denote-backlinks))
+ :config
+ (setq denote-directory (expand-file-name "~/Documents/notes/"))
+
+ ;; Automatically rename Denote buffers when opening them so that
+ ;; instead of their long file name they have a literal "[D]"
+ ;; followed by the file's title. Read the doc string of
+ ;; `denote-rename-buffer-format' for how to modify this.
+ (denote-rename-buffer-mode 1))
+#+end_src
+
+** The ~denote-sort-dired~ command is more configurable
+:PROPERTIES:
+:CUSTOM_ID: h:717765ae-f76f-4b41-96c0-895fe131a83d
+:END:
+
+The ~denote-sort-dired~ command asks for a literal string or regular
+expression and then produces a fully fledged Dired listing of matching
+files in the ~denote-directory~. Combined with the efficient Denote
+file-naming scheme, this is a killer feature to collect your relevant
+files in a consolidated view and have the full power of Dired available.
+
+By default ~denote-sort-dired~ prompts for the file name component to
+sort by and then asks whether to reverse the sorting or not. Users who
+want a more streamlined experience can configure the user option
+~denote-sort-dired-extra-prompts~.
+
+It is possible to skip the prompts altogether and use the default
+values for (i) which component to sort by and (ii) whether to reverse
+the sort. To this end, users can have something like this in their
+configuration:
+
+#+begin_src emacs-lisp
+;; Do not issue any extra prompts. Always sort by the `title' file
+;; name component and never do a reverse sort.
+(setq denote-sort-dired-extra-prompts nil)
+(setq denote-sort-dired-default-sort-component 'title)
+(setq denote-sort-dired-default-reverse-sort nil)
+#+end_src
+
+For me, Dired is one of the best things about Emacs and I like how it
+combines so nicely with Denote file names (this is the cornerstone of
+Denote, after all).
+
+** The ~denote-sort-dired~ sorting functions are customisable
+:PROPERTIES:
+:CUSTOM_ID: h:7c4824c0-7f9b-46f5-98ea-4ebbab092193
+:END:
+
+Power users may want to control how the sorting works and what it is
+matching on a per file-name-component basis. The user options are
+these:
+
+- ~denote-sort-title-comparison-function~.
+- ~denote-sort-keywords-comparison-function~.
+- ~denote-sort-signature-comparison-function~.
+
+One use-case is to match specific patterns inside of file names, such
+as Luhmann-style signatures. I wrote about this in the manual as well
+as on my blog (with screenshots):
+<https://protesilaos.com/codelog/2024-08-01-emacs-denote-luhmann-signature-sort/>.
+
+Thanks to Riccardo Giannitrapani for discussing this with me and
+helping me understand the use-case better. This was done via a private
+channel and I am sharing it with permission.
+
+** Show the date of each linked file in Org dynamic blocks
+:PROPERTIES:
+:CUSTOM_ID: h:ad222eb0-06db-4416-820c-c60f31169f66
+:END:
+
+All our Org dynamic blocks that produce links to files now read the
+parameter =:include-date=. When it is set to =t=, the listed files
+will include their corresponding date inside of parentheses after the
+file's title.
+
+Thanks to Sergio Rey for describing this idea to me. This was done via
+a private channel and the information is shared with permission.
+
+** Exclude specific directories from Org dynamic blocks
+:PROPERTIES:
+:CUSTOM_ID: h:5c0b76fc-2758-4a33-875a-fa9eee705d83
+:END:
+
+The optional Org dynamic blocks we define let users collect links to
+other files (and more) in a quick and effective way. Each block
+accepts parameters which control its output, such as how to sort
+files.
+
+All our dynamic blocks now accept the =:excluded-dirs-regexp=. This is
+a regular expression which is matched against directory file system
+paths. Matching directories and their files are not included in the
+data handled by the dynamic block.
+
+Note that we have the user option ~denote-excluded-punctuation-regexp~
+which defines a global preference along the same lines.
+
+I did a video about this feature:
+<https://protesilaos.com/codelog/2024-07-30-emacs-denote-exclude-dirs-org-blocks/>.
+
+Thanks to Claudio Migliorelli for discussing this idea with me. It was
+done via a private channel and this information is shared with permission.
+
+** New dynamic block to insert files as headings
+:PROPERTIES:
+:CUSTOM_ID: h:0cae3fdb-ba83-46f0-9006-13d0073ae092
+:END:
+
+We already had an Org dynamic block that would insert file contents.
+Though that one inserts files as they are, optionally without their
+front matter. However, users may have a workflow where they want to
+eventually copy some of the block's output into the main file they are
+editing, at which point it is easier for the entire inserted file to
+appear as a series of headings. The =#+title= of the inserted file
+becomes a top-level heading and every other heading is pushed deeper
+one level.
+
+To this end, we provide the Org dynamic block known as ~denote-files-as-headings~.
+Insert it with the command ~denote-org-extras-dblock-insert-files-as-headings~
+or select it with the minibuffer after calling Org's own command
+~org-dynamic-block-insert-dblock~.
+
+The top-level headings (those that were the =#+title=) can optionally
+link back to the original file. Though please read the manual for all
+the parameters this dynamic block takes.
+
+** The dynamic block for backlinks can be about the current heading only
+:PROPERTIES:
+:CUSTOM_ID: h:6e6deff6-c02d-4157-85c0-fc405f64ad34
+:END:
+
+The Org dynamic block for backlinks can now read the optional
+=:this-heading-only= parameter. When it is set to =t=, the block will
+only include links that point to the specific heading inside of the
+current file. Otherwise, backlinks are about the whole file.
+
+To insert such a dynamic block, use the command
+~denote-org-extras-dblock-insert-backlinks~.
+
+** Toggle the detailed view in backlinks buffers
+:PROPERTIES:
+:CUSTOM_ID: h:01b4dfb0-3883-4cc8-ac41-8a7b55e42fe8
+:END:
+
+By default, the buffer produced by the command ~denote-backlinks~ has
+a compact view of showing the file names linking to the current file.
+With the user option ~denote-backlinks-show-context~ set to a non-nil
+value, the backlinks buffer produces a detailed listing of matching
+results, where the links are shown in their original context.
+
+Users can now choose to have this on-demand by calling the command
+~denote-backlinks-toggle-context~ which switches between the detailed
+and compact views.
+
+This blog post I wrote about it include screenshots:
+<https://protesilaos.com/codelog/2024-07-25-emacs-denote-backlinks-context-toggle/>.
+
+** Templates can have a function that returns a string
+:PROPERTIES:
+:CUSTOM_ID: h:8d0ac1eb-d057-4fce-bf60-98d5de932a0d
+:END:
+
+The ~denote-templates~ variable allows the user to specify one or more
+named templates which can then be inserted during the creation of a
+new note. One way to be prompted for a template among those specified
+is to modify the ~denote-prompts~ user option and then use the regular
+~denote~ command. Another way is to use the command ~denote-template~
+(alias ~denote-create-note-with-template~), which will prompt for the
+template to use.
+
+Templates ordinarily have a string as their value, though now their
+value can also be the symbol of a function. This function takes no
+arguments and is expected to return a string. Denote takes care to
+insert that below the front matter of the new note.
+
+So it can look like this:
+
+#+begin_src emacs-lisp
+(setq denote-templates
+ `((report . "* Some heading\n\n* Another heading") ; A string with newline characters
+ (blog . my-denote-template-function-for-blog) ; the symbol of a function that will return a string
+ (memo . ,(concat "* Some heading" ; expand this `concat' into a string
+ "\n\n"
+ "* Another heading"
+ "\n\n"))))
+#+end_src
+
+Thanks to skissue (Ad) for the contribution in pull request 398:
+<https://github.com/protesilaos/denote/pull/398>. The change is small,
+meaning that its author does not need to assign copyright to the Free
+Software Foundation.
+
+Also thanks to Jean-Philippe Gagné Guay for extending this to
+~denote-org-capture~. Done in pull request 399:
+<https://github.com/protesilaos/denote/pull/399>. Jean-Philippe is a
+long-time contributor who has assigned copyright to the Free Software
+Foundation.
+
+** The ~denote-rename-buffer-mode~ can now show if a file has backlinks
+:PROPERTIES:
+:CUSTOM_ID: h:b163982b-3fea-48c8-90a0-d8358e066951
+:END:
+
+This global minor mode takes care to rename the buffers of Denote
+files to a pattern that is easier for users to read. As with
+everything, it is highly configurable. The default value now includes
+an indicator that shows if the current file has backlinks (other files
+linking to it).
+
+The exact characters used in this indicator are specified in the new
+user option ~denote-rename-buffer-backlinks-indicator~. The default
+value is ="<-->"=, which hopefully communicates the idea of a link
+(but, yeah, symbolism is hard). Users may want to modify this to add
+some fancier Unicode character.
+
+Thanks to Ashton Wiersdorf for the original contribution in pull
+request 392: <https://github.com/protesilaos/denote/pull/392>. Ashton
+has assigned copyright to the Free Software Foundation.
+
+** The ~denote-rename-buffer-format~ has changed
+:PROPERTIES:
+:CUSTOM_ID: h:8913785e-5c87-48ab-9df2-dafbbb9b1a5d
+:END:
+
+In the same theme as above, the user option ~denote-rename-buffer-format~
+has a new default value. Before, it would only show the title of the
+file. Now it shows the aforementioned ~denote-rename-buffer-backlinks-indicator~,
+if there are backlinks, plus the title, plus a literal ="[D]"= prefix.
+The prefix should make it easier to spot Denote files in a buffer
+listing.
+
+Read the documentation of ~denote-rename-buffer-format~ for how to
+tweak this to your liking.
+
+** New user option ~denote-kill-buffers~
+:PROPERTIES:
+:CUSTOM_ID: h:14177204-6269-48c1-bc65-de72b59d4e84
+:END:
+
+This controls whether and when Denote should automatically kill any
+buffer it generates while creating a new note or renaming an existing
+file. The manual describes the details.
+
+By default, Denote does not kill any buffers to give users the chance
+to review what is on display and confirm any changes or revert them
+accordingly.
+
+Thanks to Jean-Philippe Gagné Guay for the contribution in pull
+request 426: <https://github.com/protesilaos/denote/pull/426>. This is
+related to issues 273 and 413, so also thanks to Vineet C. Kulkarni
+and mentalisttraceur for their participation and/or questions.
+
+** The ~denote-journal-extras-new-or-existing-entry~ handles any filename component order
+:PROPERTIES:
+:CUSTOM_ID: h:0b570a97-3e0c-488f-9c9d-02d2f8ac786f
+:END:
+
+Version =3.0.0= of Denote introduced a new option to rearrange the
+file name components. All Denote commands should respect it. We did,
+however, have a problem with the command ~denote-journal-extras-new-or-existing-entry~
+which was not recognising the date properly.
+
+Thanks to Jakub Szczerbowski for the contribution in pull request 395:
+<https://github.com/protesilaos/denote/pull/395>. The change is small,
+meaning that Jakub does not need to assign copyright to the Free
+Software Foundation.
+
+While I am documenting this here, users should already have the fix as
+I published a minor release for it in July (in fact, there were 8
+minor releases in the aftermath of the =3.0.0= release, which
+addressed several small issues).
+
+** The ~denote-rename-file-using-front-matter~ recognises the file-at-point in Dired
+:PROPERTIES:
+:CUSTOM_ID: h:01f652c5-5713-4884-8a01-ee39f1388a12
+:END:
+
+This makes it consistent with how ~denote-rename-file~ works. I am
+implemented this in response to issue 401 where Alp Eren Kose assumed
+it was the default behaviour: <https://github.com/protesilaos/denote/issues/401>.
+
+I think it makes sense to have it this way to avoid such confusion.
+Still, it seems easier to edit the file and call ~denote-rename-file-using-front-matter~
+directly, rather do an intermediate step through Dired.
+
+** The ~denote-rename-file-using-front-matter~ does not ask to rewrite front matter
+:PROPERTIES:
+:CUSTOM_ID: h:337c5640-8e75-408e-b5cf-4a37ac8e249b
+:END:
+
+The workflow for this command is that the user modifies the front
+matter, invokes the command, and Denote takes care to rename the file
+accordingly. We had a regression were this would happen as expected,
+but Denote would still prompt if it was okay to update the front
+matter. That made no sense.
+
+As with the change mentioned above, this was also fixed in a minor
+release so that users would not have to wait all this time.
+
+** The ~denote-add-links~ and ~denote-find-link~ commands always works inside a silo
+:PROPERTIES:
+:CUSTOM_ID: h:9c343ddb-a31a-41d5-90c2-ce54622558fa
+:END:
+
+This was always the intended behaviour, though there was an issue with
+the implementation that prevented the directory-local value from being
+read.
+
+Thanks to yetanotherfossman for reporting the problem with
+~denote-add-links~ in issue 386 and to Kolmas for doing the same for
+~denote-find-link~:
+
+- <https://github.com/protesilaos/denote/issues/386>.
+- <https://github.com/protesilaos/denote/issues/416>.
+
+Also thanks to Jean-Philippe Gagné Guay for following up with a change
+to the code that should address the underlying problem with temporary
+buffers. This was done in pull request 419:
+<https://github.com/protesilaos/denote/pull/419>.
+
+** Denote commands should work in more special Org buffers
+:PROPERTIES:
+:CUSTOM_ID: h:e33bea49-f2b4-43ee-96ac-d24630c9bcd7
+:END:
+
+A case we already handled was ~org-capture~ buffers. Another one is
+the buffer produced by the command ~org-tree-to-indirect-buffer~.
+
+Thanks to coherentstate for bringing this matter to my attention in
+issue 418: <https://github.com/protesilaos/denote/issues/418>.
+
+Also thanks to skissue for noting another edge case that prevented
+~denote-rename-buffer-mode~ from doing the right thing. This was
+reported in issue 393: <https://github.com/protesilaos/denote/issues/393>.
+
+** Denote will not create a =CUSTOM_ID= via ~org-capture~ if not necessary
+:PROPERTIES:
+:CUSTOM_ID: h:c9c04d9a-776d-4b6b-826a-cd9a56f31643
+:END:
+
+If the ~org-capture~ template does not include one of the specifiers
+which produce a link, then we take care to not include a =CUSTOM_ID=
+in the properties of the current heading. We do this to make it
+possible to link directly to a heading inside of a file (a feature
+that is documented in the manual).
+
+Before, we were creating the =CUSTOM_ID= unconditionally, which was
+not the desired behaviour. Thanks to Jonas Großekathöfer for bringing
+this matter to my attention in issue 404:
+<https://github.com/protesilaos/denote/issues/404>.
+
+** The prompt for selecting a silo has the appropriate metadata
+:PROPERTIES:
+:CUSTOM_ID: h:cc5951b1-ee87-4126-b3f2-91c32e3431ae
+:END:
+
+All the Denote minibuffer prompts have the appropriate completion
+metadata to integrate with core Emacs functionalities and with
+third-party packages that leverage them. One such case pertains to the
+completion category our prompts report. This is used by a package such
+as ~embark~ to infer the set of relevant actions to perform or by the
+~marginalia~ package to produce the appropriate annotations.
+
+Users will now notice a difference while using commands such as
+~denote-silo-extras-create-note~ if they have ~marginalia-mode~
+enabled: all completion candidates will have file-related annotations.
+
+This is a small change which goes to show how the little things
+contribute to a more refined experience.
+
+** New name for option that controls where backlinks buffers are displayed
+:PROPERTIES:
+:CUSTOM_ID: h:3e0e2242-8802-4c63-adce-0e20d3f27ad9
+:END:
+
+The user option is now called ~denote-backlinks-display-buffer-action~.
+The old name ~denote-link-backlinks-display-buffer-action~ is an alias
+for it and will thus work the same way. Though you are encouraged to
+rename it in your configuration as I will eventually remove those
+obsolete symbols from the Denote code base.
+
+** The ~revert-buffer~ should do the right thing in backlinks buffers
+:PROPERTIES:
+:CUSTOM_ID: h:2982cfcd-6bef-452d-aa0d-d8ab4e721b25
+:END:
+
+I made several tweaks to the underlying code to ensure that reverting
+a backlinks buffer will always reuse the original parameters that
+generated it. Backlinks buffers are produced by the ~denote-backlinks~
+command, among others.
+
+** Lots of new entries in the manual with custom code
+:PROPERTIES:
+:CUSTOM_ID: h:991d980b-1353-4b53-8ae4-ae09450cce05
+:END:
+
+The manual of Denote is a rich resource of knowledge for how to use
+this package and how to extend it with custom code. I have written the
+following entries to further help you improve your productivity:
+
+- A custom ~denote-region~ that references the source
+- Custom sluggification to remove non-ASCII characters
+- Sort signatures that include Luhmann-style sequences
+- Why are some Org links opening outside Emacs?
+
+** More functions for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:2e41930a-1907-47f2-956e-fef8b2459461
+:END:
+
+The following functions are now public, meaning that they are safe to
+be used in the code of other packages or incorporated in user
+configurations:
+
+- ~denote-identifier-p~.
+
+- ~denote-get-identifier-at-point~. I am implementing this in response
+ to a question by Alan Schmitt in issue 400: <https://github.com/protesilaos/denote/issues/400>.
+
+- ~denote-org-extras-outline-prompt~.
+
+- ~denote-silo-extras-directory-prompt~.
+
+Consult their respective doc strings for the technicalities.
+
+Note that the Elisp convention is that private functions (intended for
+use only inside the package) have a double dash (=--=) in their name.
+In principle, these are undocumented and can change at any moment
+without any notice. I do try to avoid such cases and even add warnings
+when I make changes to them. Still, you should not use private
+functions without understanding the risks involved.
+
+** Miscellaneous
+:PROPERTIES:
+:CUSTOM_ID: h:be41d902-8d65-4012-aad2-d66507d34f78
+:END:
+
+- Wrote more unit tests for various functions.
+- Improve the doc strings of several symbols (everything in the Denote
+ code base is documented).
+- Fix some typos thanks to Nicolas Semrau and bryanrinders:
+ - <https://github.com/protesilaos/denote/commit/e7cfd48bd63e0815718cd9f1f0465d8c1c4f4a84>.
+ - <https://github.com/protesilaos/denote/pull/425>.
+- Commented on all sorts of issues on the GitHub repository and many
+ more in private.
+
+** New release cycle starts in mid-September
+:PROPERTIES:
+:CUSTOM_ID: h:2b55dd84-6ebe-438d-aba8-97dd329ec34e
+:END:
+
+I have many ideas for how to further refine Denote. Maybe you do too.
+Though we must all wait a couple of weeks in case someone reports a
+bug. This way, it is easy to fix it and publish a new minor version.
+Otherwise, we may have to bundle the fix with some in-development
+feature that we have not fully tested yet.
+
+** Git commits
+:PROPERTIES:
+:CUSTOM_ID: h:c0da6de0-c683-4029-9d95-06c27102dc4a
+:END:
+
+This is just an overview of the Git commits, though remember that
+there is more that goes into a project, such as the reporting of
+inconsistencies, discussion of new ideas, etc.. Thanks to everybody
+involved!
+
+#+begin_src
+~/Git/Projects/denote $ git shortlog 3.0.0..3.1.0 --summary --numbered
+ 104 Protesilaos Stavrou
+ 7 Jean-Philippe Gagné Guay
+ 3 Ashton Wiersdorf
+ 1 Ad
+ 1 Jakub Szczerbowski
+ 1 bryanrinders
+#+end_src
+
+* Version 3.0.0 on 2024-06-30
+:PROPERTIES:
+:CUSTOM_ID: h:bf5e869d-548f-4c77-bf1c-b7dcf6d1d4da
+:END:
+
+This major release comes about two years after the first version of
+Denote, which was published on 2022-06-27. A lot of technicalities
+have changed in the meantime, though the core idea remains the same.
+In fact, the original video presentation I did is still relevant,
+especially for those looking to get started with Denote (but remember
+to consult the latest documentation for up-to-date information---and
+ask me if you have any questions).
+
+Version 3 iterates on refinements that we made over the life cycle of
+version 2. Existing users will find that their workflow remains the
+same, though they now have even more options at their disposal.
+
+As usual, my release notes are detailed. Please take your time to read
+them: they are here for you.
+
+Special thanks to Jean-Philippe Gagné Guay, a long-time contributor to
+the project, for working on some of the items covered herein. I am not
+covering everything, as many important changes are not user-facing.
+Please consult the Git log for further details.
+
+** File name components can be written in any order
+:PROPERTIES:
+:CUSTOM_ID: h:fa0ffaf5-f762-4667-abe2-350f4ea4aac5
+:END:
+
+[ Relevant blog post: <https://protesilaos.com/codelog/2024-05-19-emacs-denote-reorder-file-name-components/>.]
+
+Users can now change the variable ~denote-file-name-components-order~
+to affect how Denote file names are constructed. By default, file
+names are written using this scheme (consult the manual for the
+details):
+
+: IDENTIFIER--TITLE__KEYWORDS.EX
+
+An optional =SIGNATURE= field can be added, thus:
+
+: IDENTIFIER==SIGNATURE--TITLE__KEYWORDS.EXT
+
+By modifying the ~denote-file-name-components-order~, users can
+produce file names like these:
+
+: --TITLE__KEYWORDS@@IDENTIFIER.EXT
+: __SIGNATURE--TITLE__KEYWORDS@@IDENTIFIER.EXT
+: __SIGNATURE--TITLE@@IDENTIFIER__KEYWORDS.EXT
+
+Note that when the =DATE= is not the first component, it gets the =@@=
+prefix to (i) remain unambiguous and (ii) make it easy to target it
+directly for search purposes.
+
+Thanks to Jean-Philippe Gagné Guay for the contribution in pull
+request 360: <https://github.com/protesilaos/denote/pull/360>.
+
+We discussed the possible delimiters for the =IDENTIFIER= in issue
+332: <https://github.com/protesilaos/denote/issues/332>. Thanks to
+Jean-Philippe, Nick Bell, Maikol Solis, and mentalisttraceur for their
+insights. Our concern was to use characters that are stylistically
+fine, while they are not special symbol in regular expressions (as
+those make searching a bit less convenient).
+
+Please remember that the file-naming scheme is the cornerstone of
+Denote. If you do change how your notes are named, make sure to be
+consistent throughout, otherwise you will likely make it harder for
+yourself to find what you need.
+
+** Exclude certain files from all prompts
+:PROPERTIES:
+:CUSTOM_ID: h:1c751e58-2f57-4aa8-9990-4ecb73054262
+:END:
+
+Sometimes users keep files in their ~denote-directory~ that they do
+not want to interactive with. These can, for example, be what Org
+produces when exporting to another file format or when archiving a
+heading.
+
+The user option ~denote-excluded-files-regexp~ makes is possible to
+omit all those files from the relevant Denote prompts.
+
+This is in response to requests for such a user option done by Samuel
+W. Flint and zadca123 in issues 376 and 384, respectively:
+
+- <https://github.com/protesilaos/denote/issues/376>
+- <https://github.com/protesilaos/denote/issues/384>
+
+[ Please let me know if you need this feature but do not know how to
+ write a regular expression. I can include concrete examples in the
+ manual, though I need to know about them first. ]
+
+** Links in plain text and Markdown files are buttonised differently
+:PROPERTIES:
+:CUSTOM_ID: h:9f9b978f-3c2b-4b27-9ff5-ec0ac24a76d0
+:END:
+
+Before we were using the function ~denote-link-buttonize-buffer~,
+which would create "buttons" for all the =denote:= links it would.
+Users probably had something like this in their configuration:
+
+#+begin_src emacs-lisp
+;; DEPRECATED method
+(add-hook 'text-mode-hook #'denote-link-buttonize-buffer)
+#+end_src
+
+We now provide an approach that is technically better by using Emacs'
+fontification mechanism. All the user needs is to add this to their
+configuration:
+
+#+begin_src emacs-lisp
+(add-hook 'text-mode-hook #'denote-fontify-links-mode-maybe)
+#+end_src
+
+The notion of "maybe" in the symbol of that function is because this
+will take care to be activated only in the right context.
+
+Thanks to Abdul-Lateef Haji-Ali for the contribution in pull request
+344 (further changes by me): <https://github.com/protesilaos/denote/pull/344>.
+
+Abdul-Lateef has assigned copyright to the Free Software Foundation.
+
+** How to make Org export work in a Denote silo
+:PROPERTIES:
+:CUSTOM_ID: h:dd39bb26-a2c0-47ad-8d54-61f1ba82d3d5
+:END:
+
+[ Relevant blog post: <https://protesilaos.com/codelog/2024-06-18-emacs-denote-silos-org-export/>. ]
+
+This is not a change in Denote per se, though I have added the
+relevant details in the manual. Basically, the Org export machinery
+dismisses directory-local variables, thus breaking how Denote silos
+work. We can work around this by having an extra =#+bind= directive in
+the front matter of each file. The manual, or the aforementioned blog
+post, describe the technicalities.
+
+** Org headings can have their own backlinks
+:PROPERTIES:
+:CUSTOM_ID: h:2464625e-e041-467c-a4fd-5744e3bb79c3
+:END:
+
+[ Relevant blog: <https://protesilaos.com/codelog/2024-04-21-emacs-denote-heading-backlinks/>. ]
+
+Denote could already link to an Org heading directly. Now it can also
+generate a backlinks buffer for the current heading, using the
+command ~denote-org-extras-backlinks-for-heading~.
+
+This is part of the optional extension =denote-org-extras.el= (it is
+part of the Denote package, but not loaded by default if you use
+something like =(require 'denote)=).
+
+I am providing this as an option for those who absolutely need it,
+though in my opinion it is better to have atomic notes, such that each
+file contains information that is relevant as a whole. In this
+workflow, individual headings can be added or removed, but the big
+picture idea of the file remain intact.
+
+At any rate, this change is possible due to the requisite refactoring
+of the code that handles the backlinks. We can technically produce
+backlinks to any pattern in files, though this may be more of interest
+to developers rather than foreshadow future features in core Denote.
+
+** Finer control over confirmations while renaming
+:PROPERTIES:
+:CUSTOM_ID: h:b3dc71a6-fa0b-48d1-b8a7-2842bf725092
+:END:
+
+The ~denote-rename-no-confirm~ is deprecated and superseded by the
+more flexible user option ~denote-rename-confirmations~.
+
+The command ~denote-rename-file~ (and others like it) prompts for
+confirmation before changing the name of a file and updating its front
+matter. The user option ~denote-rename-confirmations~ controls what
+the user is prompted for, if anything. Please consult its
+documentation for the technicalities.
+
+Thanks to Jean-Philippe Gagné Guay for the contribution in pull
+request 324: <https://github.com/protesilaos/denote/pull/324>.
+
+** The user option ~denote-save-buffer-after-creation~ is renamed to ~denote-save-buffers~
+:PROPERTIES:
+:CUSTOM_ID: h:10883c47-0f5e-4267-a122-86906bf25a61
+:END:
+
+Please update your configuration accordingly, if you were using the
+old name.
+
+** The commands ~denote-keywords-add~ and ~denote-keywords-remove~ are replaced by ~denote-rename-file-keywords~
+:PROPERTIES:
+:CUSTOM_ID: h:042699ec-5516-44ca-93ee-60b32e599029
+:END:
+
+The new command can add or remove keywords. It does this by
+prepopulating the minibuffer prompt with the existing keywords. Users
+can then use the ~crm-separator~ (normally a comma), to write new
+keywords or edit what is in the prompt to rewrite them accordingly. An
+empty input means to remove all keywords.
+
+[ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the empty
+ minibuffer contents as they are, though popular packages like
+ ~vertico~ use the first available completion candidate instead. For
+ ~vertico~, the user must either move one up to select the prompt and
+ then type =RET= there with empty contents, or use the command
+ ~vertico-exit-input~ with empty contents. That Vertico command is
+ bound to =M-RET= as of this writing on 2024-06-30 10:37 +0300. ]
+
+Technically, ~denote-rename-file-keywords~ is a wrapper for
+~denote-rename-file~, doing all the things that does.
+
+** The commands ~denote-rename-file-title~ and ~denote-rename-file-signature~
+:PROPERTIES:
+:CUSTOM_ID: h:2c2c0f6f-413b-4492-b6a2-062034692c4c
+:END:
+
+These are like the ~denote-rename-file-keywords~ we just covered.
+There are wrappers of the ~denote-rename-file~ command, which are used
+to change on the file name component they reference.
+
+If that component exists, its text is included in the minibuffer. The
+user can then modify it accordingly. If there is no text, the user is
+adding a new one. An empty input means to remove the title/signature
+from the file altogether (again, check your minibuffer for how to
+provide an empty input).
+
+** More commands to add/remove keywords in bulk from Dired
+:PROPERTIES:
+:CUSTOM_ID: h:01770801-c511-46c0-9a05-890276374b63
+:END:
+
+Two new specialised commands are available to help users add or remove
+keywords from many files at once. These are:
+
+- ~denote-dired-rename-marked-files-add-keywords~
+- ~denote-dired-rename-marked-files-remove-keywords~.
+
+They complement the ~denote-dired-rename-marked-files-with-keywords~,
+which we have had for a long time already, and which rewrites all the
+keywords (instead of only adding/removing from the list).
+
+All three of those commands operate only on the =KEYWORDS= component
+of the file name, leaving everything else as-is (while respecting the
+aforementioned ~denote-file-name-components-order~).
+
+Thanks to Vedang Manerikar for the contribution in pull request 316:
+<https://github.com/protesilaos/denote/pull/316>. Vedang has already
+assigned copyright to the Free Software Foundation.
+
+** The ~denote-org-extras-convert-links-to-file-type~ can return relative paths
+:PROPERTIES:
+:CUSTOM_ID: h:5f97d3f5-294d-467d-858b-281e46060625
+:END:
+
+The previous implementation would always return an absolute file path,
+ignoring the Org user option ~org-link-file-path-type~. Whereas now it
+will return a relative path if that user option is set to a value of
+either ='adaptive= or ='relative=.
+
+Thanks to Alexandre Rousseau for the contribution in pull request 325:
+<https://github.com/protesilaos/denote/pull/325>. The change is small,
+meaning that Alexandre does not need to assign copyright to the Free
+Software Foundation.
+
+** For developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:6911a5d6-6de3-4e04-b9d2-12a6526b4e97
+:END:
+
+*** The ~denote-add-prompts~ is made public
+:PROPERTIES:
+:CUSTOM_ID: h:28bfa74b-5a38-4f3c-a5fe-bf5a697385db
+:END:
+
+This is used to ~let~ bind any additional prompts that should be used
+by the ~denote~ command. Check the source code for how we are using
+this function.
+
+*** The ~denote-select-linked-file-prompt~ is now public
+:PROPERTIES:
+:CUSTOM_ID: h:669f8eee-bdab-406a-aada-73ee8de9caf2
+:END:
+
+This is used internally but the commands ~denote-find-link~,
+~denote-find-backlink~. Refer to the implementation of those commands
+to get an idea of how to use this prompt.
+
+*** The ~denote-retrieve-title-or-filename~ is just a wrapper
+:PROPERTIES:
+:CUSTOM_ID: h:414cfd70-1850-44bf-b0fe-fb3809af302e
+:END:
+
+It simply calls the ~denote-retrieve-front-matter-title-value~ or
+~denote-retrieve-filename-title~. We do not want it to return the
+~file-name-base~, as it used to, because this will duplicate the text
+of the file name when there is no =TITLE= component, as demonstrated by
+duli in issue 347: <https://github.com/protesilaos/denote/issues/347>.
+
+*** The ~denote-file-prompt~ is more robust
+:PROPERTIES:
+:CUSTOM_ID: h:f7fb9e1f-e7b9-4dd1-a517-79069f28dcfe
+:END:
+
+We have made this function show relative file paths for the
+convenience of the user, but we take care to internally return and
+store the full file path (which is unambiguous). Thanks to Alan
+Schmitt for noting that the history was not working properly. This was
+done in issue 339: <https://github.com/protesilaos/denote/issues/339>.
+A series of commits dealt with the implementation details, including a
+contribution by Jean-Philippe Gagné Guay in pull request 342:
+<https://github.com/protesilaos/denote/pull/342>. Also read 353 for a
+further set of tweaks from my side: <https://github.com/protesilaos/denote/discussions/353>.
+
+As part of these changes, the ~denote-file-prompt~ now takes a
+=NO-REQUIRE-MATCH= argument. It also respects the aforementioned user
+option of ~denote-excluded-files-regexp~.
+
+*** Relevant functions conform with the ~denote-rename-confirmations~
+:PROPERTIES:
+:CUSTOM_ID: h:b056abdf-b9d9-4799-8e24-ae6507b4780e
+:END:
+
+These include the ~denote-rename-file-prompt~ and
+~denote-rewrite-front-matter~, as well as the new
+~denote-add-front-matter-prompt~.
+
+This has the meaning of what I mentioned above. Commands that need to
+deviate from the user option ~denote-rename-confirmations~ can ~let~
+bind it accordingly: we even do this for some commands in =denote.el=,
+because certain prompts do not make sense there.
+
+*** All file name components can be ~let~ bound
+:PROPERTIES:
+:CUSTOM_ID: h:0118ad59-5fc2-4a19-8946-324410adb107
+:END:
+
+We define a new series of variables which can be set to a lexically
+scoped value to control what the ~denote~ function parses. These are:
+
+- ~denote-use-date~
+- ~denote-use-directory~
+- ~denote-use-file-type~
+- ~denote-use-keywords~
+- ~denote-use-signature~
+- ~denote-use-template~
+- ~denote-use-title~
+
+Employ those for custom extensions you may have.
+
+Thanks to Jean-Philippe Gagné Guay for adding those in pull request
+365: <https://github.com/protesilaos/denote/pull/365>.
+
+** Miscellaneous
+:PROPERTIES:
+:CUSTOM_ID: h:1fc6d700-e8aa-4d05-bbae-5623a9565922
+:END:
+
+- All the Org dynamic blocks defined by Denote in the optional
+ =denote-org-extras.el= are now autoloaded. This means that
+ evaluating such a code block will work even if the user has not
+ explicitly used something like =(require 'denote-org-extras)=.
+ Thanks to Julian Hoch for asking for a relevant clarification in
+ issue 337: <https://github.com/protesilaos/denote/issues/337>.
+ Thanks to Kolmas for reporting some missing autoloads in issue 371:
+ <https://github.com/protesilaos/denote/issues/371>.
+
+- The value of the user option ~denote-link-backlinks-display-buffer-action~
+ is slightly modified to (i) make the buffer dedicated to its window
+ and (ii) try to preserve its size during automatic recombinations of
+ the frame's layout.
+
+- There was a regression in version =2.3.0= relative to =2.2.0= where
+ the ~denote-link~ command would fail in Org capture buffers. Thanks
+ to Sven Seebeck for reporting this bug in issue 298:
+ <https://github.com/protesilaos/denote/issues/298>.
+
+- The ~denote-filetype-heuristics~ function no longer chokes if it
+ gets a nil value (such as in Org capture buffers).
+
+- The ~denote-journal-extras-directory~ (part of the optional
+ =denote-journal-extras= file) falls back to ~denote-directory~ if
+ its value is nil. This is what the user option
+ ~denote-journal-extras-directory~ promises in its doc string.
+
+- All prompts should have their scope of application in all capital
+ letters, such as =Select TEMPLATE key=. The idea is to make it
+ easier for the user to quickly spot for the prompt is about.
+
+- The user option ~denote-link-description-function~ is documented in
+ the manual. Thanks to Sven Seebeck for noticing that we did not
+ document this for the =2.3.0= release. Thanks to Jean-Philippe Gagné
+ Guay for helping me refine the code. This was all done in issue 298:
+ <https://github.com/protesilaos/denote/issues/298>.
+
+- As part of internal changes to how our various "rename" commands
+ work, Kolmas reported a regression with wrongly assigned file
+ extensions. This was done in issue 343:
+ <https://github.com/protesilaos/denote/issues/343>.
+
+- In the =denote-org-extras.el= we now always jump to the correct Org
+ heading line, instead of missing it by 1 under certain conditions.
+ Thanks to kilesduli for bringing this matter to my attention in
+ issue 354: <https://github.com/protesilaos/denote/issues/354>.
+
+** Policy for the aftermath of this release
+:PROPERTIES:
+:CUSTOM_ID: h:250e8abe-8e8d-41b9-a9dc-b2951a07d5bd
+:END:
+
+The next few days or weeks are reserved for bug fixes. We first want
+to make sure that the current code base is rock solid, before making
+any further changes. Any bugs will be addressed outright and new point
+releases will be published (though those are not accompanied by a
+change log entry).
+
+** Git commits
+:PROPERTIES:
+:CUSTOM_ID: h:e9ea8288-99d1-407d-919a-b6024d35a501
+:END:
+
+Just an overview of what we did. Thanks again to everyone involved.
+
+#+begin_src sh
+~/Git/Projects/denote $ git shortlog 2.3.0..3.0.0 --summary --numbered
+ 169 Protesilaos Stavrou
+ 52 Jean-Philippe Gagné Guay
+ 3 Al Haji-Ali
+ 2 Alan Schmitt
+ 1 Alexandre Rousseau
+ 1 Jianwei Hou
+ 1 Vedang Manerikar
+#+end_src
+
+* Version 2.3.0 on 2024-03-24
+:PROPERTIES:
+:CUSTOM_ID: h:e9d3ebdb-8a69-47a9-a5a2-619abc44b7d2
+:END:
+
+This release brings a host of user-facing refinements to an already
+stable base, as well as some impressive new features. There is a lot
+to cover, so take your time reading these notes.
+
+Special thanks to Jean-Philippe Gagné Guay for the numerous
+refinements to parts of the code base. Some of these are not directly
+visible to users, but are critical regardless. In the interest of
+brevity, I will not be covering the most technical parts here. I
+mention Jean-Philippe's contributions at the outset for this reason.
+Though the Git commit log is there for interested parties to study
+things further.
+
+** Check out the ~denote-explore~ package by Peter Prevos
+:PROPERTIES:
+:CUSTOM_ID: h:3e49dd9d-59db-40e5-9116-ce678231b08d
+:END:
+
+This package provides several neat extensions that help you make
+better sense of your knowledge base, while keeping it in good order.
+The ~denote-explore~ package has commands to summarise the usage of
+keywords, visualise connections between notes, spot infrequently used
+keywords, and jump to previous historical entries.
+
+- Git repository: <https://github.com/pprevos/denote-explore>.
+- Documentation: <https://lucidmanager.org/productivity/denote-explore>.
+
+Now on to Denote version =2.3.0=!
+
+** Link to a heading inside a Denote Org file
+:PROPERTIES:
+:CUSTOM_ID: h:ca7baf4f-04af-4467-a1e6-20403357280f
+:END:
+
+Denote creates links to files by using their unique identifier. As Org
+provides the =CUSTOM_ID= property for per-heading identifiers, we now
+leverage this infrastructure to compose links that point to a file and
+then to a heading therein. This only works for Org, as no other plain
+text major mode has a concept of heading identifiers (and it is not
+Denote's job to create such a feature).
+
+I demonstrated the functionality in a video:
+<https://protesilaos.com/codelog/2024-01-20-emacs-denote-link-org-headings/>
+
+Technically, the =denote:= link type has the same implementation
+details as Org's standard =file:= and has always had this potential to
+jump to a section inside the given file.
+
+*** The ~denote-org-store-link-to-heading~ user option
+:PROPERTIES:
+:CUSTOM_ID: h:a7864660-5b4c-4467-a252-9140baedeb1a
+:END:
+
+The user option ~denote-org-store-link-to-heading~ determines whether
+~org-store-link~ links to the current Org heading (such links are
+merely "stored" and need to be inserted afterwards with the command
+~org-insert-link~). Note that the ~org-capture~ command uses the
+~org-link~ internally if it has to store a link.
+
+When its value is non-nil, ~org-store-link~ stores a link to the
+current Org heading inside the Denote Org file. If the heading does
+not have a =CUSTOM_ID=, it creates it and includes it in the heading's
+=PROPERTIES= drawer. If a =CUSTOM_ID= exists, ~org-store-link~ use it
+as-is.
+
+This makes the resulting link a combination of the =denote:= link type,
+pointing to the identifier of the current file, plus the value of the
+heading's =CUSTOM_ID=, such as:
+
+- =[[denote:20240118T060608][Some test]]=
+- =[[denote:20240118T060608::#h:eed0fb8e-4cc7-478f-acb6-f0aa1a8bffcd][Some test::Heading text]]=
+
+Both lead to the same Denote file, but the latter jumps to the heading
+with the given =CUSTOM_ID=. Notice that the link to the heading also
+has a different description, which includes the heading text.
+
+The value of the =CUSTOM_ID= is determined by the Org user option
+~org-id-method~. The sample shown above uses the default UUID
+infrastructure.
+
+If ~denote-org-store-link-to-heading~ is set to a nil value, the
+command ~org-store-link~ only stores links to the Denote file (using
+its identifier), but not to the given heading. This is what Denote was
+doing in all versions prior to =2.3.0=.
+
+Thanks to Kristoffer Balintona for discussing with me how
+~org-capture~ interfaces with ~org-store-link~. I updated the
+documentation accordingly. This was done in issue 267:
+<https://github.com/protesilaos/denote/issues/267>.
+
+*** Insert link to an Org file with a further pointer to a heading
+:PROPERTIES:
+:CUSTOM_ID: h:dd054536-8d20-4251-b23d-77fec7d7d036
+:END:
+
+As part of the optional =denote-org-extras.el= extension that comes
+with the ~denote~ package, the command ~denote-org-extras-link-to-heading~
+prompts for a link to an Org file and then asks for a heading therein,
+using minibuffer completion. Once the user provides input at the two
+prompts, the command inserts a link at point which has the following
+pattern: =[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading text]]=.
+
+Because only Org files can have links to individual headings, the
+command ~denote-org-extras-link-to-heading~ prompts only for Org files
+(i.e. files which include the =.org= extension). Remember that Denote
+works with many file types.
+
+This feature is similar to the concept of the aforementioned user
+option ~denote-org-store-link-to-heading~. It is, however, interactive
+and differs in the directionality of the action. With that user
+option, the command ~org-store-link~ will generate a =CUSTOM_ID= for
+the current heading (or capture the value of one as-is), giving the
+user the option to then call ~org-insert-link~ wherever they see fit.
+By contrast, the command ~denote-org-extras-link-to-heading~ prompts
+for a file, then a heading, and inserts the link at point.
+
+** Refinements galore to minibuffer prompts
+:PROPERTIES:
+:CUSTOM_ID: h:e509402b-a58f-4a10-b364-b158b31d1ee5
+:END:
+
+*** All commands that affect file names conform with ~denote-prompts~
+:PROPERTIES:
+:CUSTOM_ID: h:11f0fc1e-552b-4a02-bf01-9d8508ce68c8
+:END:
+
+The scope of the ~denote-prompts~ user option is broadened to make it
+more useful. In the past, this variable would only affect the
+behaviour of the ~denote~ command. For example, the user would make
+the command prompt for a subdirectory, then keywords, then a title.
+But all other commands were not following this setting, as they were
+hardcoding the prompts for title and keywords.
+
+Take the ~denote-subdirectory~ command as an example. It would first
+prompt for a subdirectory to place the new note in, then for a title,
+and then for keywords. Whereas now, it prepends the =subdirectory=
+prompt to the list of ~denote-prompts~. So if the user has configured
+their ~denote-prompts~ to, for example, ask for a signature and a file
+type, the ~denote-subdirectory~ will do just that with the addition of
+the =subdirectory= prompt.
+
+Same idea for all commands that either create or modify file names,
+wherever conformity with ~denote-prompts~ makes sense. For example,
+the ~denote-rename-file~ will never ask for a =subdirectory= because
+our renaming policy is to always rename in place (to avoid
+mistakes---you can always move the file afterwards).
+
+This also means that the ~denote-rename-file~ and its multi-file
+counterpart, ~denote-dired-rename-files~, will only prompt for a
+signature if it is part of the ~denote-prompts~. Whereas in the
+previous version this was unconditional, thus burdening users who do
+not need the =SIGNATURE= file name component (more about renaming
+further into the release notes).
+
+Lots of Git commits went into this redesign, per my initiave in issue
+247: <https://github.com/protesilaos/denote/issues/247>. Thanks to
+Vedang Manerikar for the changes to the convenience wrappers of the
+~denote~ command (like ~denote-subdirectory~), which were done in pull
+request 248: <https://github.com/protesilaos/denote/pull/248>.
+
+Vedang has assigned copyright to the Free Software Foundation.
+
+Also thanks to Max Brieiev for joining the technical discussion
+therein.
+
+The renaming commands are more intuitive now, which addresses a
+discussion point raised by user babusri in issue 204:
+<https://github.com/protesilaos/denote/issues/204>.
+
+*** A simple tweak for more informative minibuffer prompts
+:PROPERTIES:
+:CUSTOM_ID: h:a502217d-8eff-4a6f-b66a-33e5e7ecda9d
+:END:
+
+The text of each prompt now has all capital letters for the word
+referencing its scope of its application, like =TITLE=, =KEYWORDS=,
+=SIGNATURE=. The idea is to make it easier to quickly scan the text,
+especially while working through multiple prompts. For example, the
+prompt for a title now reads:
+
+: New file TITLE:
+
+This paradigm is followed by all prompts. It is a small yet effective
+tweak to get a better sense of context.
+
+*** The file prompt uses relative names once again
+:PROPERTIES:
+:CUSTOM_ID: h:8f182ad3-c97f-45dc-a451-c552f2a7957c
+:END:
+
+In previous versions of Denote, the minibuffer prompt to pick a file
+(such as a file to link to) would show relative file names: the name
+without the full file system path. The functionality depended on the
+built-in =project.el= library, which did not allow us to do everything
+we wanted with our prompts, such as to have a dedicated minibuffer
+history or to easily enable the workflow of commands like
+~denote-open-or-create~.
+
+In the previous version, I made the decision to remove the
+=project.el= dependency and the concomitant presentation of relative
+names in order to add the functionality we want. I did it with the
+intention to find a better solution down the line. Et voilá! Relative
+file names are back. We now have all the functionality we need. Sorry
+if in the meantime you had to deal with those longer names! It was a
+necessary intermediate arrangement for the greater good.
+
+For the technicalities, refer to the source code of the function
+~denote-title-prompt~.
+
+*** Completion using previous inputs is now optional
+:PROPERTIES:
+:CUSTOM_ID: h:bcf382e4-bd00-49f3-859a-3f86e9770b77
+:END:
+
+All our minibuffer prompts have their dedicated history (you can
+persist histories with the built-in ~savehist-mode~). They store
+previous values, giving the user easy access to their past input
+values. Some of our commands not only record a history, but also
+leverage it to provide completion. These commands are named in the
+variable ~denote-prompts-with-history-as-completion~. As of this
+writing, they are:
+
+- ~denote-title-prompt~
+- ~denote-signature-prompt~
+- ~denote-files-matching-regexp-prompt~
+
+Users who do not want to use completion for those can set the new user
+option ~denote-history-completion-in-prompts~ to a nil value.
+
+** Renaming files got better all-round
+:PROPERTIES:
+:CUSTOM_ID: h:747e126a-b966-4ac8-a8ec-cf900012e37e
+:END:
+
+One of the pillars of the ~denote~ package is its ability to rename
+any file to use the efficient Denote file-naming scheme (makes file
+names predictable and easy to retrieve even with rudimentary tools).
+To this end, we provide several commands that affect file names,
+beside the commands that create new files.
+
+As noted above, the commands which rename files to follow the Denote
+file-naming scheme now conform with the user option ~denote-prompts~,
+but there is more!
+
+*** A broadened scope for the ~denote-rename-no-confirm~ option
+:PROPERTIES:
+:CUSTOM_ID: h:f93b8075-de2d-416e-9275-7225d03678ad
+:END:
+
+The implementation of this user option is redone (i) to save the
+underlying buffer outright if the user does not want to provide their
+confirmation for a rename each time and (ii) to cover all relevant
+commands that perform a rename operation. The assumption is that the
+user who opts in to this feature is familiar with the Denote renaming
+modalities and knows they are reliable.
+
+The default is still the same: Denote always asks for confirmation
+before renaming a file, showing the difference between the old and new
+names, as well as any changes to the file's contents. In this light,
+buffers are not saved to give the user the chance to further inspect
+the changes (such as by running ~diff-buffer-with-file~).
+
+Commands that will now skip all confirmation prompts to rename the file
+and, where relevant, save the corresponding buffer outright:
+
+- ~denote-rename-file~
+- ~denote-dired-rename-files~
+- ~denote-dired-rename-marked-files-with-keywords~
+- ~denote-rename-file-using-front-matter~
+- ~denote-rename-add-keywords~
+- ~denote-rename-remove-keywords~
+- ~denote-rename-add-signature~ (new, more below)
+- ~denote-rename-remove-signature~ (new, more below)
+
+*** Rename a file by adding or removing a =SIGNATURE= component
+:PROPERTIES:
+:CUSTOM_ID: h:01ab0277-b4d4-433e-bd25-b9a0357412f6
+:END:
+
+The =SIGNATURE= is an optional free-form field that is part of a
+Denote file name. A common use-case is to write sequence notes with
+it, though Denote does not enforce any particular convention (you may
+prefer to have it as a special kind of keyword for certain files that
+simply stands out more due to its placement).
+
+[ Besides, the ~denote-sort-dired~ command lets you filter and sort
+ files while putting them in a fully fledged Dired buffer, so
+ manually sequencing notes via their signature may not be needed. ]
+
+We now provide two commands to add or remove a signature from file
+names:
+
+- The ~denote-rename-add-signature~ prompts for a file and a
+ signature. The default value for the file prompt is the file of the
+ currently open buffer or the file-at-point in a Dired buffer. The
+ signature is an ordinary string, defaulting to the selected file's
+ signature, if any.
+
+- The ~denote-rename-remove-signature~ uses the same file prompt as
+ above. It performs its action only if the selected file has a
+ signature. Otherwise, it does nothing.
+
+Files that do not have a Denote file name are renamed accordingly.
+Though for such cases it is better to use ~denote-rename-file~ or
+~denote-dired-rename-files~ as they are more general.
+
+*** Use the ~denote-after-rename-file-hook~ for optional post-rename operations
+:PROPERTIES:
+:CUSTOM_ID: h:57f4f60c-7873-4542-a7a5-5c997cdbd137
+:END:
+
+All renaming commands run the ~denote-after-rename-file-hook~ after a
+successful operation. This is meant for users who want to do something
+specific after the renaming is done.
+
+** More optional features of the =denote-org-extras.el=
+:PROPERTIES:
+:CUSTOM_ID: h:a0a2753e-5be9-4776-9f3f-e3b7556c13c1
+:END:
+
+I already covered the ~denote-org-extras-link-to-heading~, though the
+file =denote-org-extras.el= has some more optional goodies for those
+who work with Org files.
+
+*** Create a note from the current Org subtree
+:PROPERTIES:
+:CUSTOM_ID: h:fbf1e574-e9aa-4c67-8034-27341d7a5536
+:END:
+
+In Org parlance, an entry with all its subheadings and other contents
+is a "subtree". Denote can operate on the subtree to extract it from
+the current file and create a new file out of it. One such workflow is
+to collect thoughts in a single document and produce longer standalone
+notes out of them upon review.
+
+The command ~denote-org-extras-extract-org-subtree~ (part of the
+optional =denote-org-extras.el= extension) is used for this purpose.
+It creates a new Denote note using the current Org subtree. In doing
+so, it removes the subtree from its current file and moves its
+contents into a new file.
+
+The text of the subtree's heading becomes the =#+title= of the new
+note. Everything else is inserted as-is.
+
+Read the documentation string of ~denote-org-extras-extract-org-subtree~
+or consult the manual for further details.
+
+*** Convert =denote:= links to =file:= links
+:PROPERTIES:
+:CUSTOM_ID: h:042e26e8-e3e0-4c57-9855-6b363671ae9a
+:END:
+
+Sometimes the user needs to translate all =denote:= link types to
+their =file:= equivalent. This may be because some other tool does not
+recognise =denote:= links (or other custom links types---which are a
+standard feature of Org, by the way). The user thus needs to (i)
+either make a copy of their Denote note or edit the existing one, and
+(ii) convert all links to the generic =file:= link type that
+external/other programs understand.
+
+The optional extension =denote-org-extras.el= contains two commands
+that are relevant for this use-case:
+
++ Convert =denote:= links to =file:= links :: The command
+ ~denote-org-extras-convert-links-to-file-type~ goes through the
+ buffer to find all =denote:= links. It gets the identifier of the
+ link and resolves it to the actual file system path. It then
+ replaces the match so that the link is written with the =file:= type
+ and then the file system path. The optional search terms and/or link
+ description are preserved.
+
++ Convert =file:= links to =denote:= links :: The command
+ ~denote-org-extras-convert-links-to-denote-type~ behaves like the
+ one above. The difference is that it finds the file system path and
+ converts it into its identifier.
+
+*** The Denote Org dynamic blocks are now in =denote-org-extras.el=
+:PROPERTIES:
+:CUSTOM_ID: h:51d72c47-d434-4954-98d6-2db7a7ea6812
+:END:
+
+As part of this version, all our dynamic blocks are defined in the
+file =denote-org-extras.el=. The file which once contained these block
+definitions, =denote-org-dblock.el=, now only has aliases for the new
+function names and dipslays a warning about its deprecation.
+
+There is no need to ~require~ the ~denote-org-extras~ feature because
+all of Denote's Org dynamic blocks are autoloaded (meaning that they
+work as soon as they are used). For backward compatibility, all
+dynamic blocks retain their original names as an alias for the newer
+one.
+
+We will not remove =denote-org-dblock.el= anytime soon to avoid any
+potential breakage with people's existing notes. Though if you are new
+to this functionality, you better avoid the deprecated symbols.
+
+*** Org dynamic block to only insert missing links
+:PROPERTIES:
+:CUSTOM_ID: h:45176e63-c609-40f6-a11d-1cc0c28460dd
+:END:
+
+The =denote-missing-links= block is available with the command
+~denote-org-extras-dblock-insert-missing-links~. It is like the
+=denote-links= block (documented at length in the manual), except it
+only lists links to files that are not present in the current buffer.
+The parameters are otherwise the same:
+
+: #+BEGIN: denote-missing-links :regexp "YOUR REGEXP HERE" :sort-by-component nil :reverse-sort nil :id-only nil
+:
+: #+END:
+
+Remember to type =C-c C-x C-u= (~org-dblock-update~) with point on the
+=#+BEGIN= line to update the block.
+
+This brings back a feature that was deprecated in version 2.2.0, but
+makes changes to it so that (i) it is more limited in scope and (ii)
+available as a standalone Org dynamic block.
+
+Thanks to Stephen R. Kifer, Peter Prevos, and Elias Storms for the
+discussion which made it clear to me that users do have a need for
+such functionality. This was done in the now-defunct mailing list:
+<https://lists.sr.ht/~protesilaos/denote/%3C1db2104e-70bd-47f9-a7ed-b8d4bb370a7f%40app.fastmail.com%3E>.
+
+Also thanks to Vedang Manerikar for fixing an edge case bug. This was
+done in pull request 260: <https://github.com/protesilaos/denote/pull/260>.
+
+Org dynamic blocks are a powerful feature which also showcases how far
+we can go with Denote's efficient file-naming scheme.
+
+** Quality-of-life improvements
+:PROPERTIES:
+:CUSTOM_ID: h:08f27f36-0ed2-4a5e-b02b-f0075c6e904f
+:END:
+
+Here I include other changes we made to existing functionality.
+
+*** BREAKING User-defined sluggification of file name components
+:PROPERTIES:
+:CUSTOM_ID: h:240b80e7-242c-46fb-83d2-1ba36bdcaf66
+:END:
+
+In the previous version, we introduced the user option
+~denote-file-name-letter-casing~. This was used to control the letter
+casing of file name components, but was ultimately not flexible enough
+for our purposes. We are thus retiring it and replacing it with the
+more powerful, but also more advanced, user option
+~denote-file-name-slug-functions~.
+
+For existing users of the deprecated functionality, you can still
+preserve the input of a prompt verbatim with something like this:
+
+#+begin_src emacs-lisp
+(setq denote-file-name-slug-functions
+ '((title . denote-sluggify-title)
+ (keyword . identity)
+ (signature . denote-sluggify-signature)))
+#+end_src
+
+The manual explains the details and shows ready-to-use code samples.
+
+Remember that deviating from the default file-naming scheme of Denote
+will make things harder to use in the future, as files will have
+permutations that create uncertainty. The sluggification scheme and
+concomitant restrictions we impose by default are there for a very
+good reason: they are the distillation of years of experience. Here we
+give you what you wish, but bear in mind it may not be what you need.
+You have been warned.
+
+Thanks to Jean-Philippe Gagné Guay for introducing this variable,
+among other tweaks, in pull request 217: <https://github.com/protesilaos/denote/pull/217>.
+Jean-Philippe has assigned copyright to the Free Software Foundation.
+
+*** Option to automatically save the buffer of a new note
+:PROPERTIES:
+:CUSTOM_ID: h:3e1249f1-ac26-4187-9ddd-7391b4e5131f
+:END:
+
+The user option ~denote-save-buffer-after-creation~ controls whether
+commands that create new notes save their buffer right away.
+
+The default behaviour of commands such as ~denote~ (or related) is to
+not save the buffer they create. This gives the user the chance to
+review the text before writing it to a file. The user may choose to
+delete the unsaved buffer, thus not creating a new file on disk.
+
+If ~denote-save-buffer-after-creation~ is set to a non-nil value, such
+buffers are saved automatically and so the file is written to disk.
+
+*** The ~denote-menu-bar-mode~ and the placement of the Denote submenu
+:PROPERTIES:
+:CUSTOM_ID: h:c8336927-cf6b-4770-b041-123bf9186e57
+:END:
+
+The command ~denote-menu-bar-mode~ toggles the inclusion of the
+submenu with the Denote entries in the Emacs menu bar (which is on
+display when ~menu-bar-mode~ is enabled).
+
+This submenu is now shown after the =Tools= entry.
+
+Thanks to Joseph Turner for sending me the relevant patches. Joseph
+has assigned copyright to the Free Software Foundation.
+
+*** The =C-c C-o= works in ~markdown-mode~ for Denote links
+:PROPERTIES:
+:CUSTOM_ID: h:1c884b19-7ab7-4eb5-a332-815d25f7373c
+:END:
+
+In files whose major mode is ~markdown-mode~, the default key binding
+=C-c C-o= (which calls the command ~markdown-follow-thing-at-point~)
+correctly resolves =denote:= links. This method works in addition to
+the =RET= key, which is made available by the buttonization that we
+also provide. Interested users can refer to the function
+~denote-link-markdown-follow~ for the implementation details.
+
+Thanks to user pmenair for noting a case where this was breaking
+general Markdown linking functionality. This was done in issue 290:
+<https://github.com/protesilaos/denote/issues/290>.
+
+*** More fine-grained control of Denote faces for dates/identifiers
+:PROPERTIES:
+:CUSTOM_ID: h:c6f739ef-ea26-41b8-84e6-c87c4622cdba
+:END:
+
+We now define more faces for fine-grained control of the identifier in
+Dired. Thanks to mentalisttraceur for suggesting the idea in issue
+276: <https://github.com/protesilaos/denote/issues/276>.
+
+Before you ask, no, none of my themes will cover those faces because
+extra colouration is something only the user can decide if they want
+or not. In the above link I provide a sample with a screenshot (apart
+from the ~modus-themes~, my ~ef-themes~ and ~standard-themes~ have
+similar functionality):
+
+#+begin_src emacs-lisp
+(defun my-modus-themes-denote-faces (&rest _)
+ (modus-themes-with-colors
+ (custom-set-faces
+ `(denote-faces-year ((,c :foreground ,cyan)))
+ `(denote-faces-month ((,c :foreground ,magenta-warmer)))
+ `(denote-faces-day ((,c :foreground ,cyan)))
+ `(denote-faces-time-delimiter ((,c :foreground ,fg-main)))
+ `(denote-faces-hour ((,c :foreground ,magenta-warmer)))
+ `(denote-faces-minute ((,c :foreground ,cyan)))
+ `(denote-faces-second ((,c :foreground ,magenta-warmer))))))
+
+(add-hook 'modus-themes-post-load-hook #'my-modus-themes-denote-faces)
+#+end_src
+
+*** New convenience command for users of the optional =denote-journal-extras.el=
+:PROPERTIES:
+:CUSTOM_ID: h:9e7bff88-a6ad-45e7-b802-0493153e0e20
+:END:
+
+The command ~denote-journal-extras-link-or-create-entry~ links to the
+journal entry for today or creates it in the background, if missing,
+and then links to it from the current file. If there are multiple
+journal entries for the same day, it prompts to select one among them
+and then links to it. When called with an optional prefix argument
+(such as =C-u= with default key bindings), the command prompts for a
+date and then performs the aforementioned. With a double prefix
+argument (=C-u C-u=), it also produces a link whose description
+includes just the file's identifier.
+
+Thanks to Alan Schmitt for contributing this command, based on
+previous discussions. It was done in pull request 243:
+<https://github.com/protesilaos/denote/pull/243>.
+
+** For developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:03778c8c-60aa-449c-96df-7e41916668a6
+:END:
+
+These has new parameters or are new symbols altogether. Please read
+their respective doc string for the details.
+
++ Function ~denote-convert-file-name-keywords-to-crm~.
++ Function ~denote-valid-date-p~.
++ Function ~denote-parse-date~.
++ Function ~denote-retrieve-title-or-filename~.
++ Function ~denote-get-identifier~.
++ Function ~denote-signature-prompt~.
++ Function ~denote-file-prompt~.
++ Function ~denote-keywords-prompt~.
++ Function ~denote-title-prompt~.
++ Function ~denote-rewrite-front-matter~.
++ Function ~denote-rewrite-keywords~.
++ Function ~denote-update-dired-buffers~.
++ Function ~denote-format-string-for-org-front-matter~.
++ Function ~denote-format-string-for-md-front-matter~.
++ Variable ~denote-link-signature-format~.
++ Function ~denote-link-description-with-signature-and-title~.
++ Variable ~denote-link-description-function~.
+
+** Miscellaneous
+:PROPERTIES:
+:CUSTOM_ID: h:040f2678-674d-4e99-b428-659cd3a3b7c3
+:END:
+
+- The ~denote-sort-dired~ function no longer errors out when there is
+ no match for the given search terms. Thanks to Vedang Manerikar for
+ the initial patch! This was done in the now-defunct mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/patches/47625>. Further
+ changes by me.
+
+- The ~denote-keywords-sort~ function no longer tries to sort keywords
+ that are not a list. Thanks to Ashton Wiersdorf for the patch. The
+ change is small. As such, Ashton does not need to assign copyright
+ to the Free Software Foundation.
+
+- Documented in the manual that custom convenience commands can be
+ accessed by the ~denote-command-prompt~. Thanks to Glenna D. for
+ clarifying the language.
+
+- The ~denote-user-enforced-denote-directory~ is obsolete. Those who
+ used it in their custom code can simply ~let~ bind the value of the
+ variable ~denote-directory~. Thanks to Jean-Philippe Gagné Guay for
+ making the relevant changes (the Git history is not direct here and
+ I cannot quickly find the pull request---the commit is =a48a1da=).
+
+- The ~denote-link-return-links~ no longer keeps buffers around.
+ Thanks to Matteo Cavada for the patch. This was done in pull request
+ 252: <https://github.com/protesilaos/denote/pull/252>. The change is
+ small and so Matteo does not need to assign copyright to the Free
+ Software Foundation.
+
+- Thanks to user jarofromel (recorded in Git as "random" author) for
+ fixing a mismatched parenthesis in ~denote-parse-date~. This was
+ done in pull request 258: <https://github.com/protesilaos/denote/pull/258>.
+
+- The ~denote-rename-buffer-mode~ now works as expected with
+ non-editable files, like PDFs. Thanks to Alan Schmitt for bringing
+ this matter to my attention and then refining the implementation
+ details in pull request 268: <https://github.com/protesilaos/denote/pull/268>.
+
+- All the Denote linking functions can be used from any file outside
+ the ~denote-directory~ (links are still resolved to files inside the
+ ~denote-directory~). Thanks to Jean-Philippe Gagné Guay for the
+ contribution in pull request 236: <https://github.com/protesilaos/denote/pull/236>.
+
+- We removed all glue code that integrated Denote with the built-in
+ ~ffap~, ~xref~, and ~project~ libraries. We may reconsider how best
+ to organise such features in the future. Thanks to Noboru Ota
+ (nobiot), who originally contributed those extensions, for
+ suggesting their removal from our code base. We did this by
+ evaluating all use-cases. The discussion with Noboru happened in
+ issue 264: <https://github.com/protesilaos/denote/issues/264>. Also
+ thanks to Jean-Philippe Gagné Guay and Alan Schnmitt for checking
+ the impact of this on how we generate backlinks. The latest
+ iteration of this was done in pull request 294, by Jean-Philippe:
+ <https://github.com/protesilaos/denote/pull/294>.
+
+- While renaming files, signatures no longer lose consecutive spaces.
+ Thanks to Wesley Harvey for the contribution in pull request 207:
+ <https://github.com/protesilaos/denote/pull/207>. The change is
+ within the ~15 line limit and so Wesley does not need to assign
+ copyright to the Free Software Foundation.
+
+- All of the above and lots more are documented at length in the
+ manual. This is a big task in its own right (as are release notes,
+ by the way), though it ensures we keep a high standard for the
+ entire package and can communicate all our knowledge to the user.
+
+** No more SourceHut
+:PROPERTIES:
+:CUSTOM_ID: h:9a0d6afc-95e0-490e-a573-5a50fe7bdf28
+:END:
+
+Development continues on GitHub with GitLab as a mirror. I explained
+my reasons here: <https://protesilaos.com/codelog/2024-01-27-sourcehut-no-more/>.
+
+This is a change that affects all my Emacs packages.
+
+** Forward guidance for Denote version 3.0.0
+:PROPERTIES:
+:CUSTOM_ID: h:61fb340e-5c7c-4a4b-927c-63faf4759a09
+:END:
+
+We will not any new features until mid-April or a bit later if
+necessary. This gives users enough time to report any potential issues
+with version =2.3.0=. If there are any bugs, they will be fixed right
+away and new minor releases will be introduced (though without release
+notes).
+
+Once we are done with this release cycle, we want to prepare for the
+next major version of Denote. The plan is to make the placement of
+file name components entirely customisable, among many other power
+user features. Though the defaults will remain intact.
+
+For the immediate future, please prioritise bug reports/fixes. Then
+see you around for another round of hacking. The Denote code base is a
+pleasure to work with due to how composable everything is. I happy to
+make it even better for developers and users alike.
+
+** Git commits
+:PROPERTIES:
+:CUSTOM_ID: h:a6fd8e16-ded9-49cf-afbb-6e1373c3c43d
+:END:
+
+Just an overview of what we did. Thanks again to everyone involved.
+
+#+begin_src sh
+~/Git/Projects/denote $ git shortlog 2.2.0..2.3.0 --summary --numbered
+ 246 Protesilaos Stavrou
+ 46 Jean-Philippe Gagné Guay
+ 6 Vedang Manerikar
+ 3 Joseph Turner
+ 2 Alan Schmitt
+ 2 Max
+ 2 Peter Prevos
+ 1 Ashton Wiersdorf
+ 1 Glenna D.
+ 1 Matteo Cavada
+ 1 mattyonweb
+ 1 random
+ 1 wlharvey4
+#+end_src
+
+** All contributions are valuable
+:PROPERTIES:
+:CUSTOM_ID: h:967372fa-933b-40d2-b1a8-546d1a50d35d
+:END:
+
+I encourage you to provide feedback on any of the functionality of the
+Denote package. You do not need to be a developer or indeed an expert
+in Emacs. When you have an idea in mind on how you use Denote, or you
+think something could be done differently, please speak your mind. I
+do listen to feedback and am interested in further improving this
+package. Everybody is welcome!
+
+* Version 2.2.0 on 2023-12-10
+:PROPERTIES:
+:CUSTOM_ID: h:8efed390-cfa0-420d-b300-0cb76bf2c9f9
+:END:
+
+The present version covers four broad themes:
+
+1. Denote rename commands are more user-friendly and featureful.
+2. An optional sorting facility makes it possible to produce a
+ filtered and sorted Dired buffer with Denote files.
+3. The optional Denote Org dynamic blocks have received a lot of attention.
+4. Bug fixes and internal refinements.
+
+[ Remember that you do not need to be a programmer to contribute to
+ Denote. Report a bug, make a suggestion, or just describe how you
+ want to use this package. Every idea counts and we may implement it
+ if we can. ]
+
+** The rename commands can remove a Denote file name component
+:PROPERTIES:
+:CUSTOM_ID: h:54d803d8-4863-4160-bb2f-3302fb8bff23
+:END:
+
+The commands we provide to rename files using the Denote file-naming
+scheme---~denote-rename-file~, ~denote-dired-rename-files~, and
+~denote-dired-rename-marked-files-with-keywords~---can now remove
+Denote file name components. This is done by providing an empty string
+at the relevant prompt.
+
+For example, to remove the =TITLE= component from a file called
+=20231209T110322==sig--title__keywords.ext= we provide an empty string
+at the title prompt. The end result will look something like this:
+=20231209T110322==sig__keywords.ext=.
+
+All prompts now include a hint that leaving them empty will ignore the
+given field if it does not exist or remove it if it does exist.
+
+Note that you must *check how to input an empty string* with your
+minibuffer user interface of choice. For instance, with the ~vertico~
+package you can do that with the =M-RET= key binding or by selecting
+the prompt line directly (notice the counter showing something like
+=*/5= instead of =1/5=). Please make sure to consult the documentation
+of the package you are using as this behaviour is not controlled by
+Denote. Vertico, and others like it, selects the first candidate if
+you type =RET= without any input, which is not the same as an empty
+string---it is the first candidate.
+
+Also read the Denote manual on the matter of [[https://protesilaos.com/emacs/denote#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]. In short,
+we use this facility to name all our files, regardless of file type,
+in a consistent way that makes them easier to find (I do this with my
+videos, for example, and I do it across my filesystem for all personal
+files).
+
+** The file-to-be-renamed is easier to read in the minibuffer
+:PROPERTIES:
+:CUSTOM_ID: h:69d85d3b-0200-4cc1-baff-9d59aa0ff57b
+:END:
+
+The commands ~denote-rename-file~ and ~denote-dired-rename-files~
+show the name of the file they are operating on in the minibuffer
+prompt. This is now produced relative to the current directory,
+meaning that instead of =/some/rather/long/path/to/file-name.txt=
+Denote only displays =file-name.txt=.
+
+Our rename commands never move files to another directory, anyway, so
+we do not need to remind the user of the entire file system path.
+
+To make things easier for users/themes, file names highlighted in
+Denote prompts are fontified with either of following faces,
+depending on the specifics of the case:
+
+- ~denote-faces-prompt-old-name~
+- ~denote-faces-prompt-new-name~
+- ~denote-faces-prompt-current-name~
+
+These faces inherit the attributes of basic faces, so they should look
+decent without further tweaks across all themes.
+
+** Prompts for title, keywords, and signature accept an empty string
+:PROPERTIES:
+:CUSTOM_ID: h:5897bcc1-4637-4268-8518-8404d939b4b9
+:END:
+
+The prompts defined by Denote that apply to file name components all
+accept an empty string. This has the effect of skipping the given
+component. For example, we can create a file without a title and
+keywords, with the following sequence of actions (I assume you are
+using ~vertico~ for the minibuffer user interface):
+
+- Type =M-x denote=.
+- Type =M-RET= at the title prompt to input an empty string.
+- Now type =M-RET= at the keywords prompt for another empty string.
+
+The resulting file name is something like =20231209T110950.org=.
+
+** Dired with sorting and filtering
+:PROPERTIES:
+:CUSTOM_ID: h:05aa437b-2fc8-4e01-ac38-ab77baad83af
+:END:
+
+The new optional =denote-sort.el= library provides facilities to sort
+Denote files by any of their file name components. Users can benefit
+from this facility to produce a filtered and sorted listing of Denote
+files with the command ~denote-sort-dired~.
+
+~denote-sort-dired~ produces a fully fledged Dired buffer. It asks for a
+regular expression that matches file names in the ~denote-directory~.
+It then prompts for a sort key and finally checks with the user
+whether to reverse the order or not.
+
+[ Do not be discouraged by the term "regular expression". Ordinary
+ words work fine. Plus, with Denote's file-naming scheme we have
+ semantics such as =_keyword=, =-title=, ~=signature~, as explained
+ in the manual. This is the whole point of using a thoughtful naming
+ scheme. ]
+
+The resulting Dired listing is flat, meaning that files inside of
+subdirectories are bundled together with those present at the root of
+the ~denote-directory~. In this case, files inside of a subdirectory
+include the directory component as a prefix. So we have something like
+this:
+
+#+begin_example
+test-subdir/20230320T105950--a-new-note__testing.txt
+20231202T095629--rename-works-as-intended__one_test_two.org
+#+end_example
+
+I think this is a killer feature, as the fully fledged Dired buffer
+allows us to perform all supported operations on our Denote
+sorted+filtered files (e.g. change file permissions, move files to
+another directory, or open them in an external application).
+
+I recorded a video to show how this works:
+<https://protesilaos.com/codelog/2023-12-04-emacs-denote-sort-mechanism/>.
+
+[ Remember that we can rename any file using the Denote file-naming
+ scheme, meaning that our files can include stuff like PDFs and
+ videos. Combine this with the concept of "silos", which is covered
+ in the Denote manual, to organise your long-term storage and
+ retrieve it efficiently. ]
+
+** Combine contents of files with an Org dynamic block
+:PROPERTIES:
+:CUSTOM_ID: h:d41009c1-9833-4b28-8240-9666bfd26559
+:END:
+
+The new =denote-files= Org dynamic block produces a continuous stream
+of file contents. It joins together the contents of files inside the
+~denote-directory~ whose name matches the given regular expression.
+Optional parameters control whether to include links to those files,
+omit their front matter, sort by a given file name component, or tweak
+the separator between each file's contents.
+
+I produced a video to demonstrate the functionality:
+<https://protesilaos.com/codelog/2023-11-25-emacs-denote-org-dynamic-blocks/>.
+
+Use the command ~denote-org-dblock-insert-files~ to insert such a
+block directly at point. Read the Denote manual for the
+technicalities: [[https://protesilaos.com/emacs/denote#h:f15fa143-5036-416f-9bff-1bcabbb03456][Org dynamic block to insert file contents]].
+
+[ Videos I do will eventually be out-of-date. The manual is the source
+ of truth. ]
+
+Bear in mind that this feature is not "transclusion". We are simply
+printing a copy of the contents of the files in the current buffer.
+Changes made to this copy are not reflected in the original files.
+
+The =denote-files= Org dynamic block is an excellent way to quickly
+collect your thoughts on a given topic. Although dynamic blocks are a
+feature of Org, the contents of the files do not need to be in Org
+syntax (I write most of my notes in plain text (=.txt=)).
+
+Thanks to Claudiu Tănăselia for proposing this idea and discussing it
+with me. This was done via a private channel and the information is
+shared with permission.
+
+** Sort parameters are used in all Denote Org dynamic blocks
+:PROPERTIES:
+:CUSTOM_ID: h:7b51fe38-302e-488d-9816-7015a8071ddb
+:END:
+
+All Denote Org dynamic blocks make use of =denote-sort.el= (described
+further above). It powers the =:sort-by-component= and =:reverse-sort=
+parameters.
+
+Thanks to Glenna D. for suggesting this feature and discussing it with
+me. This was done via a private channel and the information is shared
+with permission. It is what inspired me to start work on
+=denote-sort.el=, which I then extended to cover Dired, as noted
+above.
+
+** The =:missing-only= parameter is removed from Org dynamic blocks
+:PROPERTIES:
+:CUSTOM_ID: h:2bd26aef-70ad-4d83-a4ab-c75a893a733a
+:END:
+
+I am removing it because the underlying functionality of
+~denote-add-missing-links~ was not always reliable.
+
+** Files with signature are linked appropriately in Org dynamic blocks
+:PROPERTIES:
+:CUSTOM_ID: h:144436eb-e674-4052-ac0a-d582b6aa2f53
+:END:
+
+In general, we provide the command ~denote-link-with-signature~ to let
+the user pick a file that has a signature and link to it. The
+description of such a link contains the signature text as well as the
+file title. The ~denote-link-with-signature~ is distinct from the
+standard ~denote-link~, as it allows the user to express intent about
+the inclusion of the signature.
+
+In Org dynamic blocks for links/backlinks, we make this happen
+automatically since there can be no manual intervention to express
+intent on a link-by-link basis.
+
+** Fontification in Dired can now extend to subdirectories
+:PROPERTIES:
+:CUSTOM_ID: h:46e08576-4c17-4b1e-a268-e0223250e7c1
+:END:
+
+The user option ~denote-dired-directories~ activates the
+~denote-dired-mode~ in the specified list of directories when the user
+sets this in their init file:
+
+#+begin_src emacs-lisp
+(add-hook 'dired-mode-hook #'denote-dired-mode-in-directories)
+#+end_src
+
+The new user option ~denote-dired-directories-include-subdirectories~
+extends the reach of this feature to all subdirectories thereof.
+
+Thanks to Henrik Hörmann for discussing this with me and contributing
+a patch. This was originally done in pull request 191 on the GitHub
+mirror: <https://github.com/protesilaos/denote/pull/191>. Subsequent
+refinements by me.
+
+** Signatures are sluggified as intended
+:PROPERTIES:
+:CUSTOM_ID: h:73e1efaa-2c22-48ee-be46-072b55177c99
+:END:
+
+The file name signature component is now sluggified properly. This
+means that multiple words are separated by the equals sign, in
+accordance with the Denote file-naming scheme where a word separator
+is the same as the given field separator (this is the low-tech feature
+that makes Denote files so easy to retrieve without fancy extras).
+
+Vedang Manerikar fixed two relevant bugs in the "rename" commands,
+while I rewrote internal functions and tests in the interest of consistency. Vedang's patches: <https://lists.sr.ht/~protesilaos/denote/patches/46790>.
+
+[ The "signature" is a free form component of the file name. Users can
+ add anything they want there, such as to use it as a "category" that
+ is different from "tags/keywords", or to introduce sequences in
+ their notes, or to just have an extra marker for files they need to
+ spot quickly. ]
+
+** For developers
+:PROPERTIES:
+:CUSTOM_ID: h:79f2fd7e-d5a7-4c78-bce7-f8d21e86e32c
+:END:
+
+There is a section in the manual titled "For developers or advanced
+users". There we document functions or variables that are
+public-facing, meaning that we test and document their behaviour and
+encourage others to use them for code they write on top of Denote.
+Refer to this section if you are looking to extend Denote. Though you
+can also just check the source code, which is designed to be readable
+and hackable.
+
+- The ~denote-directory-files~ function gains new functionality that
+ subsumes that of the now-deprecated functions
+ ~denote-directory-files-matching-regexp~, ~denote-all-files~,
+ ~denote-directory-text-only-files~. Thanks to Jean-Philippe Gagné
+ Guay for the contribution, which was done in pull request 195 on the
+ GitHub mirror: <https://github.com/protesilaos/denote/pull/195>.
+
+- The font-lock keywords we define are consolidated into a single
+ variable: ~denote-faces-file-name-keywords~ instead of being split
+ into two variables. This means that we cover all our fontification
+ needs in the backlinks buffer as well as the ~denote-dired-mode~
+ with this one point of entry. It also works for ~denote-sort-dired~,
+ which can include files with their subdirectory component in the
+ same flat listing.
+
+- Use the function ~denote-retrieve-filename-keywords~ to extract
+ keywords from the file name alone, without going into the file
+ contents.
+
+- The ~denote-retrieve-filename-title~ function now returns an empty
+ string if no title is present. Its behaviour is thus consistent with
+ ~denote-retrieve-filename-keywords~ and ~denote-retrieve-filename-signature~.
+
+- The ~denote-retrieve-filename-title~ will now use the
+ ~file-name-base~ function as a fallback subject to a non-nil
+ optional argument. This case come into effect when the file does not
+ have a title component. The new optional argument allows the caller
+ to handle such cases as they see fit.
+
+- The ~denote-signature-prompt~ and ~denote-title-prompt~ functions
+ accept an optional =DEFAULT-SIGNATURE= or =DEFAULT-TITLE= argument.
+ Internally, this is used as the =INITIAL-INPUT= of ~completing-read~
+ instead of the =DEF= argument. This matters because we want the
+ prompt to return an empty string if there is no input, whereas the
+ presence of =DEF= means that =DEF= is returned when the prompt is
+ empty.
+
+- All our functions that interactively match file names with a regular
+ expression now use the ~denote-files-matching-regexp-prompt~
+ function. When called from Lisp, it takes a =REGEXP= argument as
+ well as an optional =PROMPT-TEXT=.
+
+For the purposes of this release cycle, I am not documenting the
+points of entry provided by =denote-sort.el=. It is a new feature that
+I may eventually incorporate in =denote.el=. If you are interested in
+the functionality (e.g. to have more elaborate sorting algorithms),
+please take a look at the source code and then let us discuss the
+implementation details.
+
+** Miscellaneous
+:PROPERTIES:
+:CUSTOM_ID: h:ce5c7865-9ec1-49ba-9388-5a251ab56735
+:END:
+
+- Rewrote the manual on the topic of Org dynamic blocks. Same idea for
+ practically the entirety of =denote-org-dblock.el=.
+
+- Marked the interactive specification of a few commands with the
+ major mode they belong to. This means that =M-X= (note the capital
+ X), which calls ~execute-extended-command-for-buffer~ by default,
+ will only show those commands in the relevant context.
+
+- Made internal refinements and simplified the implementation of a few
+ functions. This is important work to keep the code base clean and
+ easy to read/maintain. Thanks to Jean-Philippe Gagné Guay for the
+ contribution. It was done in pull request 193 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/193>.
+
+- Improved the doc string of the ~denote-format-file-name~ function.
+ Also introduced a unit test for it to be sure it does what we expect
+ (I eventually want to have tests for everything we do, but this is a
+ long-term project).
+
+** Git commits
+:PROPERTIES:
+:CUSTOM_ID: h:6830d3f3-130c-4346-b3ca-a3d4b0e9f974
+:END:
+
+Just an overview of what we did. Thanks again to everyone involved.
+
+#+begin_src sh
+~/Git/Projects/denote $ git shortlog 2.1.0..2.2.0 --summary --numbered
+ 125 Protesilaos Stavrou
+ 17 Jean-Philippe Gagné Guay
+ 2 Vedang Manerikar
+ 1 Henrik Hörmann
+#+end_src
+
+** Policy for the next development cycle
+:PROPERTIES:
+:CUSTOM_ID: h:cb0cae4f-c9a1-40b3-98ae-781a57270d4e
+:END:
+
+I will give a ~1 week pause on Denote development before making any
+feature changes. This is to ensure that we catch possible bugs and
+push fixes right away. If there are other changes in place, it is not
+possible to make point updates of this sort, as we must first wait for
+the new features to be tested in real-world scenaria.
+
+* Version 2.1.0 on 2023-11-12
+:PROPERTIES:
+:CUSTOM_ID: h:167beb8f-14be-40de-a1f2-d13910924c00
+:END:
+
+The general theme of this release is improvements to the quality of
+life with Denote. While these release notes and the overall
+documentation are comprehensive, make no mistake: Denote can be used
+with =M-x denote=, =M-x denote-link=, =M-x denote-backlinks=, =M-x
+denote-rename-file=. These have been rock solid from the beginning.
+Everything else is for more specialised workflows.
+
+I hope to produce a companion video to this changelog in the coming
+days. Though I am still reeling from the injury to my left hand (I
+wrote all this to not delay the package any longer). Please check back
+in my website's coding blog section to find the follow-up video:
+<https://protesilaos.com/codelog>.
+
+[ Remember to consult the manual whenever you have a question about
+ Denote. It is comprehensive and, in my opinion, a paradigm of how
+ free software should be done for the benefit of users. I document
+ everything in detail and am eager to continue this way. If something
+ is unclear, contact me in person, use the mailing list, or open an
+ issue on the GitHub/GitLab mirror. I do not check other fora or
+ media and will thus not help you there. If you are writing custom
+ code, remember to read the doc strings. I write them for you too. ]
+
+** Deprecated the ~denote-allow-multi-word-keywords~
+:PROPERTIES:
+:CUSTOM_ID: h:a086d1d2-adb3-4151-a7af-813d79b4b3dc
+:END:
+
+This user option enabled the use of keywords that consisted of
+multiple words. Those would be separated by hyphens. Such keywords do
+not work as Org =#+filetags= and also mess up with the neat search
+semantics of Denote's file-naming scheme where a hyphen prefix
+anchors the query to the =TITLE= component of the name.
+
+Users who absolutely need multi-word keywords are encouraged to use
+the new ~denote-file-name-letter-casing~ option. More below.
+
+** Control the letter casing of file name components
+:PROPERTIES:
+:CUSTOM_ID: h:29319b8a-698b-4a1c-bab4-7b106a623de8
+:END:
+
+By default, Denote downcases all components of the file name. The user
+option ~denote-file-name-letter-casing~ provides granular control over
+this behaviour.
+
+The value it accepts is an alist where each element is a cons cell of
+the form =(COMPONENT . METHOD)=. The manual, or the variable's doc
+string, cover the details. The gist is that we can now instruct Denote
+to accept input verbatim, such as because we want to apply a
+=camelCase= convention or variants thereof.
+
+Here is an example, where we downcase the title, but preserve the
+letter casing of the signature and keyword components with this:
+
+#+begin_src emacs-lisp
+(setq denote-file-name-letter-casing
+ '((title . downcase)
+ (signature . verbatim)
+ (keywords . verbatim)
+ (t . downcase)))
+#+end_src
+
+Users of the now-deprecated ~denote-allow-multi-word-keywords~ are
+encouraged to implement a letter casing convention with the help of
+this new user option.
+
+Relevant sections in the manual:
+
+- The file-naming scheme:
+ <https://protesilaos.com/emacs/denote#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d>.
+- Contol the letter casing of file names:
+ <https://protesilaos.com/emacs/denote#h:6ae1ab8c-5e36-4216-8e93-f37f4447582c>
+
+** The ~denote-dired-mode~ should now work while toggling ~wdired~
+:PROPERTIES:
+:CUSTOM_ID: h:18a3b515-9306-4911-ba2d-73e36efbdd32
+:END:
+
+The writable version of Dired would break the colouration applied by
+~denote-dired-mode~. I have arranged for this to not happen anymore,
+although it means that I had to add an advice to relevant wdired
+symbols because no proper hook is on offer.
+
+** The "do or create" commands are more intuitive to use
+:PROPERTIES:
+:CUSTOM_ID: h:5bcdc4b8-ecba-44d7-accc-0b26657aa29b
+:END:
+
+Denote provides several commands with a "do or create" logic. For
+example, the ~denote-open-or-create~ prompts for a file to visit: if
+something matches the user's input, it is visited in a buffer,
+otherwise a new note is created with the given input. Same for
+~denote-link-or-create~, mutatis mutandis.
+
+Before, the "... or create" step did not make it obvious how the
+previous search terms could be reused. Whereas now those are set as
+the default minibuffer value at the title prompt, meaning that typing
+=RET= at the empty prompt will use that value, while =M-n=
+(~next-history-element~ with default settings) will put the text into
+the prompt for further editing.
+
+I will answer this because I get asked about it: we still refrain from
+creating the new note outright because the search terms are not
+necessarily suitable for a new title. Remember that Denote's file name
+is optimised for searching: =-word= is specific to the title, =_word=
+to the keywords, and ==word= to the signature. Combine this with the
+~orderless~ package and you frequently type something like =_jou -he=
+to match a file with the =journal= keyword and the word =hesitation=
+in its title.
+
+*IMPORTANT NOTE:* some minibuffer completion User Interfaces preselect
+the first completion candidate, which is not always the same as the
+default value. Check with your UI of choice how to pass a default
+value and/or provide an empty input. For example, with the ~vertico~
+package one can move up from the first candidate to select the prompt
+itself (the counter switches from =1/N= to =*/N=).
+
+Relevant sections in the manual:
+
+- Open an existing note or create it if missing:
+ <https://protesilaos.com/emacs/denote#h:ad91ca39-cf10-4e16-b224-fdf78f093883>.
+- Link to a note or create it if missing:
+ <https://protesilaos.com/emacs/denote#h:9e41e7df-2aac-4835-94c5-659b6111e6de>.
+
+*** New "... or create with command" features for more flexibility
+:PROPERTIES:
+:CUSTOM_ID: h:6f475151-9d64-4dfb-8c59-694c93d56ce8
+:END:
+
+As part of the wider "do or create" feature set, Denote provides the
+option to run a specific note-creating command instead of just using
+the standard ~denote~ one. For example, it is possible to call the
+~denote-subdirectory~ command to pick a subdirectory of the
+~denote-directory~ for the new note. Commands providing this facility
+are ~denote-open-or-create-with-command~ and ~denote-link-after-creating-with-command~.
+
+Thanks to Vedang Manerikar for fixing a broken ~if~ clause during
+development: <https://lists.sr.ht/~protesilaos/denote/patches/46087>.
+
+** The title and signature prompts use minibuffer completion
+:PROPERTIES:
+:CUSTOM_ID: h:429847c8-ebf4-4b23-a597-5276309ef61a
+:END:
+
+All Denote minibuffer prompts come with their own history. This means
+that =M-p= (~previous-history-element~) and =M-n=
+(~next-history-element~) always return relevant input.
+
+The title and signature prompts now reuse their input history to
+provide completion. This means that the user can quickly access
+previous inputs, either to pass them directly or edit them further
+before inputting them.
+
+[ Use the built-in ~savehist-mode~ to persist histories across sessions. ]
+
+Remember to check with your minibuffer UI on how to input empty
+values at the prompt, should you ever need to do so.
+
+For posterity, I first implemented this in commit =0d855bb=. However,
+it did not work with the default minibuffer because the =SPC= key
+performs completion (popping up the Completions buffer). So users
+could not easily input an arbitrary string for the title/signature. I
+thus reverted that commit in =9f692cb=.
+
+[ The bug was reported by Suhail Singh on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C652d82c0.c80a0220.e6282.dc47%40mx.google.com%3E#%3C65392fa6.050a0220.da61c.0ac8@mx.google.com%3E>. ]
+
+Stefan Monnier suggested the use of the ~minibuffer-with-setup-hook~,
+which lets us disable =SPC= completion for the purposes of these
+functions. This is most welcome as the functionality is nice to have.
+Stefan's feedback was provided on the emacs-devel mailing list:
+<https://lists.gnu.org/archive/html/emacs-devel/2023-10/msg00631.html>.
+
+** Create a note with the region's contents
+:PROPERTIES:
+:CUSTOM_ID: h:ae798d1f-6fa2-4d99-91c9-0d5eb18b1bb0
+:END:
+
+The command ~denote-region~ takes the contents of the active region
+and then prompts for a title and keywords. Once a new note is
+created, it inserts the contents of the region therein. This is
+useful to quickly elaborate on some snippet of text or capture it for
+future reference.
+
+It also provides the ~denote-region-after-new-note-functions~ abnormal
+hook. Read the manual for more:
+<https://protesilaos.com/emacs/denote#h:2f8090f1-50af-4965-9771-d5a91a0a87bd>.
+
+** Comprehensive refinements to the ~denote-rename-buffer-mode~
+:PROPERTIES:
+:CUSTOM_ID: h:91b3ba9f-8b10-4f1c-a08b-70f5e7140923
+:END:
+
+This is an opt-in feature that automatically renames the buffer of
+newly visited Denote files according to the user's preferences. Not to
+be confused with renaming files: buffers are internal to Emacs. Enable
+it at startup by adding this to your configuration file:
+
+#+begin_src emacs-lisp
+(denote-rename-buffer-mode 1)
+#+end_src
+
+Relevant entries in the manual:
+
+- Automatically rename Denote buffers:
+ <https://protesilaos.com/emacs/denote#h:3ca4db16-8f26-4d7d-b748-bac48ae32d69>.
+- The ~denote-rename-buffer-format~ option:
+ <https://protesilaos.com/emacs/denote#h:35507c18-35b1-41b9-9d80-52f54fcef3cb>.
+
+*** The ~denote-rename-buffer-format~ option
+:PROPERTIES:
+:CUSTOM_ID: h:beeafe57-f110-4c11-87e7-10f682ca2386
+:END:
+
+The user option ~denote-rename-buffer-format~ controls how the
+function ~denote-rename-buffer~ chooses the name of the
+buffer-to-be-renamed. This function is the one used by the
+~denote-rename-buffer-mode~.
+
+Users may want, for example, to include some text that makes Denote
+buffers stand out, such as a =[D]= prefix. Examples:
+
+#+begin_src emacs-lisp
+;; Use the title (default)
+(setq denote-rename-buffer-format "%t")
+
+;; Use the title and keywords with some emoji in between.
+(setq denote-rename-buffer-format "%t 🤨 %k")
+
+;; Use the title with a literal "[D]" before it
+(setq denote-rename-buffer-format "[D] %t")
+#+end_src
+
+Users who need yet more flexibility are best served by writing their
+own function and assigning it to the ~denote-rename-buffer-function~
+(in such a case, please contact me as I am curious to know what the
+underlying need is).
+
+The manual or doc string of ~denote-rename-buffer-format~ cover the
+technicalities of the available format specifiers.
+
+Thanks to Jean-Philippe Gagné Guay for intermediately refining parts
+of the code. This was done in pull request 177 on the GitHub mirror:
+<https://github.com/protesilaos/denote/pull/177>.
+
+Thanks to Vedang Manerikar for ensuring that the string of the buffer
+is trimmed so that it never starts with an empty space (those buffers
+count as "internal" to Emacs and are not shown to the user):
+<https://lists.sr.ht/~protesilaos/denote/patches/46243>.
+
+*** The ~denote-rename-buffer-mode~ also works with unsaved buffers
+:PROPERTIES:
+:CUSTOM_ID: h:e65bb546-af22-45fb-a918-d0e621b0e415
+:END:
+
+Internal refinements to a Denote Lisp macro make this minor mode also
+work with new and unsaved Denote buffers. Whereas before only the
+buffers of existing files would be renamed.
+
+** Denote's renaming facilities are better than ever
+:PROPERTIES:
+:CUSTOM_ID: h:703b9021-f917-4b3f-9406-14992b2a4fe8
+:END:
+
+Denote's value proposition is its efficient file-naming scheme that
+makes it easier to retrieve files even with rudimentary search tools.
+We provide several commands to rename existing files according to this
+scheme. The underlying file type does not matter (e.g. I use Denote to
+name my video files).
+
+Relevant sections in the manual:
+
+- Renaming files:
+ <https://protesilaos.com/emacs/denote#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca>.
+- Front matter:
+ <https://protesilaos.com/emacs/denote#h:13218826-56a5-482a-9b91-5b6de4f14261>.
+
+*** Rename like an expert with ~denote-rename-no-confirm~
+:PROPERTIES:
+:CUSTOM_ID: h:8798dd8c-819d-4fda-9865-77d9734da28c
+:END:
+
+By default, the ~denote-rename-file~ command asks for a final
+confirmation before carrying out its function. The new user option
+~denote-rename-no-confirm~ can be bound to a non-nil value to skip
+that step.
+
+This only applies to ~denote-rename-file~. Other commands that rename
+files in bulk never prompt for such confirmation (it would make them
+cumbersome to use, plus it is assumed that the user who performs a
+batch operation understands the implications).
+
+*** The ~denote-rename-file~ command prompts for a signature
+:PROPERTIES:
+:CUSTOM_ID: h:e4e7e3d8-40e3-4f58-a19f-df34ccbfdbbd
+:END:
+
+This command used to only ask for a title and keywords. Now it allows
+to use a signature as well. An empty input means that the signature is
+ignored. AGAIN, please check with your minibuffer completion UI on how
+to input an empty value, otherwise you will not get what you expect.
+
+*** Rename mutliple files sequentially with ~denote-dired-rename-files~
+:PROPERTIES:
+:CUSTOM_ID: h:dcb623aa-fc4d-4d84-80e4-a540b6dbb144
+:END:
+
+This provides the same interface as ~denote-rename-file~, only it
+works over a list of marked Dired files.
+
+Internally, the prompts for title, keywords, and signature are
+improved to display the underlying file that is affected by the
+current operation. As the user renames files, the prompts reflect
+which one is current.
+
+*** The name of ~denote-dired-rename-marked-files~ has changed
+:PROPERTIES:
+:CUSTOM_ID: h:f9a16fc1-840d-400f-a5ae-a7791fac441f
+:END:
+
+It is now called ~denote-dired-rename-marked-files-with-keywords~ to
+better communicate what it does. In short, this is a quick way to add
+the given keywords to a list of files, converting them to the Denote
+file-naming scheme in case they are not already using it. For the full
+interactive power, use the aforementioned ~denote-dired-rename-files~.
+
+*** The ~denote-rename-file-using-front-matter~ can be used without saving its buffer
+:PROPERTIES:
+:CUSTOM_ID: h:bfc194c2-5980-482a-aa1c-feb4ced992d1
+:END:
+
+This is now possible because of changes to underlying functions (a
+Denote Lisp macro---not to bother you with technicalities).
+
+Same principle for ~denote-rename-file-using-front-matter~.
+
+*** The name of ~denote-change-file-type~ has changed
+:PROPERTIES:
+:CUSTOM_ID: h:3bf4b6c4-8399-4d5d-8df1-6495f5bfc579
+:END:
+
+It is now called ~denote-change-file-type-and-front-matter~ to avoid
+confusion as to whether Denote converts files from one format to
+another (there are specialised tools for that).
+
+*** Renaming a file returns the new file path for programmatic use
+:PROPERTIES:
+:CUSTOM_ID: h:1d7bffd1-e422-420d-b453-9a36dd8508f7
+:END:
+
+Thanks to mentalisttraceur for requesting this feature in issue 183 on
+the GitHub mirror: <https://github.com/protesilaos/denote/issues/183>.
+
+** Link to a file with a signature
+:PROPERTIES:
+:CUSTOM_ID: h:b154ef64-c3b4-4e15-b533-c59d5b2ebf6b
+:END:
+
+The ~denote-link-with-signature~ command prompts for a file that has a
+=SIGNATURE= component and links to it. The link's description includes
+the text of the signature as well as the title.
+
+Thanks to Mark Olson for mentioning this idea. It was done in issue
+167 on the GitHub mirror: <https://github.com/protesilaos/denote/issues/167>.
+
+I implemented it live, while also refactoring relevant parts of the
+code to be more abstract/reusable:
+<https://protesilaos.com/codelog/2023-09-25-emacs-live-mostly-denote/>.
+
+Thanks to Alan Schmitt for spotting and fixing a regression caused by
+the above:
+<https://lists.sr.ht/~protesilaos/denote/%3Cm2cyy5rt68.fsf%40mac-03220211.irisa.fr%3E>.
+
+** Renaming GPG or Age encrypted file works as expected
+:PROPERTIES:
+:CUSTOM_ID: h:9ceaf432-797c-46e5-aaf8-d7180ad66689
+:END:
+
+Emacs can seamlessly visit a =.gpg= or =.age= file. Denote has nothing
+to do with encryption, though it takes care to recognise the
+underlying file type and to perform its work accordingly. However,
+prior versions of Denote contained a bug in how file extensions were
+handled: it would keep the encryption extension but remove the file
+type extension before it (so ".org.gpg" would wrongly become ".gpg").
+
+Thanks to Jens Östlund for reporting a bug with ~denote-keywords-add~
+on an encrypted file, which prompted me to investigate this further
+and fix the issue holistically. This was done in issue 172 on the
+GitHub mirror: <https://github.com/protesilaos/denote/issues/172>.
+
+Interested parties are advised to check the two new public functions,
+~denote-get-file-extension~ and ~denote-get-file-extension-sans-encryption~,
+for the implementation details. In short, we had a problem with all
+operations that needed to retrieve the file extension when that
+included an encryption component.
+
+** The optional ~denote-journal-extras~
+:PROPERTIES:
+:CUSTOM_ID: h:54723661-31f8-4cab-9be5-4cab19e44dc7
+:END:
+
+The manual of Denote has long provided code samples to achieve
+particularised results. Among those were snippets to streamline the
+use of Denote for journaling.
+
+To make things even easier for users, we now have the
+=denote-journal-extras.el=. It consolidates the rich corpus of
+documented snippets into an easy-to-use and formally maintained
+package. Thanks to Vedang Manerikar for providing the impetus for this
+process. This was done on the mailing list:
+<https://lists.sr.ht/~protesilaos/denote/patches/43255#%3C20230803170935.60833-2-ved.manerikar@gmail.com%3E>.
+
+The new file is optional. It can be loaded thus:
+
+#+begin_src emacs-lisp
+(require 'denote-journal-extras)
+#+end_src
+
+The main idea is to quickly create journal entries. Check the manual
+for the details, including the commands to use and the variables to
+configure: <https://protesilaos.com/emacs/denote#h:4a6d92dd-19eb-4fcc-a7b5-05ce04da3a92>.
+
+Thanks to Kostas Andreadis for working on a comment I had included in
+a working state of the code about the inclusion of templates. Kostas
+made it possible to use the Denote template prompt (per the
+~denote-templates~ user option) as part of the creation of a new
+journal entry. This was done in pull request 173 on the GitHub mirror:
+<https://github.com/protesilaos/denote/pull/173>. The change is less
+than 15 lines and thus Kostas does not need to assign copyright to the
+Free Software Foundation.
+
+Also thanks to TJ Stankus for reporting a case where
+~denote-journal-extras-title-format~ did not accept a ~nil~ value (as
+it should). This was done in issue 176 on the GitHub mirror:
+<https://github.com/protesilaos/denote/issues/176>.
+
+** The optional ~denote-silo-extras~
+:PROPERTIES:
+:CUSTOM_ID: h:618495d2-0c5b-48b4-af88-56f3d969697c
+:END:
+
+This is the same idea as with the =denote-journal-extras.el=: we had
+the code in the manual and are now formally distributing it. Thanks
+again to Vedang Manerikar for initiating this process. It was done on
+the mailing list:
+<https://lists.sr.ht/~protesilaos/denote/patches/43255>.
+
+Use this optional feature with:
+
+#+begin_src emacs-lisp
+(require 'denote-silo-extras)
+#+end_src
+
+Consult the manual for the details:
+<https://protesilaos.com/emacs/denote#h:e43baf95-f201-4fec-8620-c0eb5eaa1c85>.
+
+** The infrastructure for unique identifiers is more robust
+:PROPERTIES:
+:CUSTOM_ID: h:1d538d7f-52e6-4653-b057-c62606752934
+:END:
+
+For Denote version =2.0.0= I introduced a general scheme intended to
+avoid scenaria where duplicate identifiers could be created (thus
+breaking a premise of Denote). Jean-Philippe Gagné Guay iterated over
+the code to make it more robust and to fix some of the cases I had not
+accounted for. This was done in pull request 159 on the GitHub mirror:
+<https://github.com/protesilaos/denote/pull/159>. Same idea in pull
+request 187: <https://github.com/protesilaos/denote/pull/187>.
+
+** For developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:9031dc82-ab75-438c-a2c8-a1250ae48671
+:END:
+
+Denote has a clean code base with small and composable functions. This
+encourages hackability. Each definition in the source is documented,
+while the manual provides an overview of every public symbol.
+
+- Added :: ~denote-get-file-extension~,
+ ~denote-get-file-extension-sans-encryption~,
+ ~denote-keywords-combine~,
+ ~denote-retrieve-keywords-value-as-string~,
+ ~denote-title-prompt-current-default~, ~denote-command-prompt~.
+
+- Refactored :: ~denote-all-files~, ~denote-signature-prompt~,
+ ~denote-file-prompt~, ~denote-title-prompt~,
+ ~denote-rewrite-front-matter~.
+
+Please read their documentation strings for the details. Or check the
+manual: <https://protesilaos.com/emacs/denote#h:c916d8c5-540a-409f-b780-6ccbd90e088e>.
+
+** Check out the ~denote-explore~ package by Peter Prevos
+:PROPERTIES:
+:CUSTOM_ID: h:759d0276-17e8-4461-9ee4-b4d07840dd7a
+:END:
+
+Peter posted this on the mailing list and I asked if it was okay to
+mention it in the release notes of Denote. If you have a relevant
+announcement to make, consider sending it to our mailing list.
+
+#+begin_quote
+Hi folks,
+
+I have just updated the denote-explore package:
+https://github.com/pprevos/denote-explore
+
+It does three things:
+
+1. Summary statistics: Count and visualise keywords and note types
+2. Random walks: Generate new ideas using serendipity
+3. Network visualisation: Visualise your Denote network of links
+
+It contains a rudimentary network visualisation function, relying
+on the R language. I will need some D3.js expertise to improve the
+visualisation.
+
+There should be a way to generate the basic network structure just
+using Elisp and feeding a JSON to D3.js.
+
+Regards
+
+P:)
+#+end_quote
+
+** Miscellaneous
+:PROPERTIES:
+:CUSTOM_ID: h:01dc6bb0-53ac-43e1-b12e-484c99a6c2a7
+:END:
+
+- During this release cycle, I made lots of changes that in one way or
+ another related to the ~denote-file-prompt~. It was relying on a
+ =project.el= mechanism that did not allow us to do everything we
+ needed. I have thus arranged for it to use the standard
+ ~completing-read~ mechanism. There are subtle differences in
+ behaviour, though the core idea is the same. This change fixes a few
+ not-so-obvious bugs. Interested parties are advised to refer to the
+ message in commit =50d1bbdf1e8ffe0f449f2f5da02f9b70322fff7d=.
+
+- All commands that use the ~denote~ function internally (i.e.
+ anything that creates a new note) call the
+ ~denote-after-new-note-hook~ as part of their work. This hook is
+ mostly intended for advanced users who want to do something after a
+ new note is produced.
+
+- The ~menu-bar-mode~ submenu of Denote is now positioned where it
+ should be after the "Tools". Thanks to Noboru Ota for the patch:
+ <https://lists.sr.ht/~protesilaos/denote/patches/44738>.
+
+- The ~menu-bar-mode~ entry of Denote includes the new commands. This
+ is a nice way to discover more of what Denote can do.
+
+- The commands ~denote-backlinks-prev~ and ~denote-backlinks-next~ are
+ only meant to be used inside the Denote backlinks buffer. As such,
+ they now produce an error when called elsewhere (I wish I could hide
+ them from =M-x= altogether).
+
+- The ~denote-extract-keywords-from-front-matter~ always returns a
+ list, thus avoiding an erroneous case. Thanks to Vedang Manerikar
+ for fixing the bug: <https://lists.sr.ht/~protesilaos/denote/patches/46420>.
+
+- The =T= in the Denote identifier component now has its own face:
+ ~denote-faces-time-delimiter~. This is used by the backlinks buffer
+ and the ~denote-dired-mode~. The idea is to introduce a subtle
+ distinction between the date and time constituents of the
+ identifier. Those who want the =T= to be the same colour as the rest
+ of the identifier, can make the ~denote-faces-time-delimiter~
+ inherit the ~denote-faces-date~. For example:
+
+ #+begin_src emacs-lisp
+ (set-face-attribute 'denote-faces-time-delimiter nil :inherit 'denote-faces-date)
+ #+end_src
+
+ Thanks to Jean-Charles Bagneris for sending this patch:
+ <https://lists.sr.ht/~protesilaos/denote/patches/43072>.
+
+- Fixed a ~nil~ file expansion in the function
+ ~denote--extract-title-from-file-history~. Thanks to ezchi for
+ bringing this matter to my attention. It was done in issue 166 on
+ the GitHub mirror:
+ <https://github.com/protesilaos/denote/issues/166>.
+
+- A link can be created from inside an ~org-capture~ buffer. This
+ means that we can call ~denote-link~ (and related) while capturing a
+ new note with ~org-capture~. Thanks to Peter Smith for reporting the
+ bug in issue 186 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/issues/186>.
+
+- We stopped using ~vc-rename-file~ to rename files. The reason is
+ that it requires the buffer to be saved, but we do not want that
+ after modifying the front matter because we want to give the user a
+ chance to confirm what happened. Thanks to Frédéric Willem for
+ reporting the problem in issue 185 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/issues/185>.
+
+- Thanks to Ivan Sokolov for removing a double negative logic in a
+ snippet. This was done in pull request 162 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/162>.
+
+** Git commits
+:PROPERTIES:
+:CUSTOM_ID: h:d8f30943-70dd-45fe-8cf1-4c3918152aeb
+:END:
+
+Just an overview of what we did. Every contribution matters.
+
+#+begin_src
+~/Git/Projects/denote $ git shortlog 2.0.0..2.1.0 --summary --numbered
+ 153 Protesilaos Stavrou
+ 15 Jean-Philippe Gagné Guay
+ 5 Vedang Manerikar
+ 1 Alan Schmitt
+ 1 Ivan Sokolov
+ 1 Jean-Charles Bagneris
+ 1 Kostas Andreadis
+ 1 Noboru Ota
+ 1 Peter Prevos
+#+end_src
+
+* Version 2.0.0 on 2023-07-21
+:PROPERTIES:
+:CUSTOM_ID: h:3f17bf03-4c47-4410-abf8-1db4a0ac7775
+:END:
+
+This is the second major version of Denote, close to one year after
+its initial release. The video demo I did back then remains relevant,
+even though lots of details have changed.
+
+** Notes have a new optional SIGNATURE field
+:PROPERTIES:
+:CUSTOM_ID: h:a3a9e14d-4132-47c0-a23c-cb008a141668
+:END:
+
+It is now possible to create notes that include a =SIGNATURE= field in
+their file name. Either use the convenience command ~denote-signature~
+or configure the user option ~denote-prompts~ to affect what the ~denote~
+command should prompt for.
+
+Signatures are arbitrary strings of characters that enable the user to
+further qualify their documents. One possible workflow is to write
+relational notes, such that =1a1= is the first extension of another
+note with a =1a= signature.
+
+The design of the =SIGNATURE= field is consistent with the Denote
+file-naming scheme. The field separator is the double equals sign
+(~==~), while words that comprise the signature are joined together by
+a single equals sign. As such, the user can prefix a search with an
+equals sign to match words in the =SIGNATURE=, just as they would use
+dashes for the =TITLE= and underscores for the =KEYWORDS=.
+
+[ Read the manual for the technicalities of the Denote file-naming
+ scheme. This is not limited to "notes": any file can be named
+ accordingly (I do it with my videos, for example). ]
+
+Signatures are not included in a file's front matter. This is a
+strategic decision to preserve backward compatibility, while not
+introducing a feature that has not enjoyed widespread usage. I want
+to make signatures behave the same as the rest of the file name
+fields, though I am interested to learn how users employ them in their
+workflow.
+
+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.
+
+Also thanks to Alfredo Borrás and Jeremy Friesen for discussing with
+me the field delimiter of signatures on the mailing list:
+<https://lists.sr.ht/~protesilaos/denote/%3C2A597B4E-5F18-4D97-9457-B3C859DAA020%40zoho.eu%3E>.
+Thanks to Kai von Fintel for doing the same on the GitHub mirror in
+issue 147: <https://github.com/protesilaos/denote/issues/147>.
+
+Read the original announcement:
+<https://protesilaos.com/codelog/2023-03-20-emacs-denote-signature-feature/>.
+
+As part of the development, I fixed a case where
+~denote-rename-file-using-front-matter~ would fail if it could not
+find a signature
+
+The idea is that we want the command to behave the way it always did
+when the file has no signature and to preserve the signature when it
+is present.
+
+Thanks to relict for reporting the issue on the mailing list:
+<https://lists.sr.ht/~protesilaos/denote/%3C87zg86lru9.fsf%40kotlak.com%3E>.
+
+** The rename commands avoid creating duplicate identifiers
+:PROPERTIES:
+:CUSTOM_ID: h:d24645a3-ad02-450c-b3d7-af7802aa0b26
+:END:
+
+Denote provides commands to rename an existing file to one that
+follows the Denote file-naming scheme (videos, PDFs, other text
+documents, ...). Check, for example, the ~denote-rename-file~ and
+~denote-dired-rename-marked-files~. The idea is to make everything
+easier to search.
+
+In prior versions, these commands could produce duplicate identifiers
+if the modification date of the underlying files was the same. Such a
+scenario occurs when the files are modified programmatically, as with
+the =touch= command or the various =git= operations.
+
+Denote will now take care to increment the identifier until it becomes
+unique within the current scope.
+
+Thanks to Felipe Balbi for reporting this bug in issue 105 on the
+GitHub mirror: <https://github.com/protesilaos/denote/issues/105>.
+
+Thanks to Vedang Manerikar and Jean-Charles Bagneris for commenting on
+this feature on the mailing list:
+<https://lists.sr.ht/~protesilaos/denote/%3C87v8emeus0.fsf%40protesilaos.com%3E>.
+
+Thanks to Ashton Wiersdorf for noticing a mistake I made that caused a
+regression in ~denote-rename-file~:
+<https://lists.sr.ht/~protesilaos/denote/%3Cm2lefbbzl1.fsf%40wiersdorfmail.net%3E>.
+
+*** Optional arguments affect ~denote-dired-rename-marked-files~
+:PROPERTIES:
+:CUSTOM_ID: h:6ea998be-83dd-4c67-945c-11011372818f
+:END:
+
+The ~denote-dired-rename-marked-files~ now accepts two optional
+arguments. When called interactively, these are interpreted as a
+single or double universal prefix argument (=C-u= by default, though
+do =M-x where-is= and search for ~universal-argument~).
+
+The first argument, named =SKIP-FRONT-MATTER-PROMPT=, skips the "yes
+or no" prompt requested at the outset of the operation, passing to it
+an affirmative response. Thanks to Jay Rajput for asking the question
+that inspired me to implement this. It was done in issue 155 on the
+GitHub mirror: <https://github.com/protesilaos/denote/issues/155>.
+
+The second argument, named =NO-UNIQUE-ID-CHECK=, will not perform any
+checks for potential duplicate identifiers. The default is to check
+for duplicates and increment them such that they become unique. The
+reason this optional argument exists is for those who want to speed up
+the process, perhaps because they know ahead of time all identifiers
+will be unique or do not care about them.
+
+Thanks to Bruno Boal for refining how the prefix argument is
+processed. The patch was sent via a private channel. The change is
+small and thus does not require copyright assignment to the Free
+Software Foundation.
+
+** Menu entries help users discover Denote
+:PROPERTIES:
+:CUSTOM_ID: h:651e5561-f9ce-41f6-bad3-d54ce2dcff04
+:END:
+
+Users of ~menu-bar-mode~ and/or ~context-menu-mode~ will now find a
+submenu with points of entry to Denote. Refer to the publication I
+made on my website, as it includes a picture:
+<https://protesilaos.com/codelog/2023-03-31-emacs-denote-menu/>. I
+will save the thousand words for the following sections. 🙃
+
+There is a known issue where the ~menu-bar-mode~ entry is positioned
+before the =File= submenu. Apparently, there exists an inelegant way
+to place the menu elsewhere, but I am not willing to maintain hacks
+for missing functionality. If someone knows a clear way to put the
+submenu elsewhere, please contact me: I want it to be after =Tools=.
+
+Thanks to Kai von Fintel and Noboru Ota (nobiot) for discussing the
+placement of the submenu:
+<https://lists.sr.ht/~protesilaos/denote/%3C2B60992C-0FC9-42CC-B669-69A544450FEF%40mit.edu%3E>.
+
+** "Link" commands have simpler names
+:PROPERTIES:
+:CUSTOM_ID: h:acf95a79-3c45-423d-a88f-d6eed7fa5387
+:END:
+
+Originally, Denote was organised as a collection of several files,
+each of which had its own prefix like =denote-dired.el=, and
+=denote-link.el=. This arrangement was deemed surplus to requirements
+and all core code was consolidated in =denote.el=. An artefact of
+that design was the presence of symbols that retained their admittedly
+awkward names, like the command ~denote-link-backlinks~ or
+~denote-link-add-missing-links~.
+
+All such commands are deprecated. They are replaced with more
+discoverable names. The deprecation is done in such a way that the
+old names are aliases for the new ones, but the user is warned not to
+rely on them.
+
+The new names in detail:
+
+| Old name 🤨 | New name 🤩 |
+|-------------------------------------+---------------------------------------------------------------|
+| ~denote-link-add-links~ | ~denote-add-links~ |
+| ~denote-link-add-missing-links~ | ~denote-add-missing-links~ |
+| ~denote-link-backlinks~ | ~denote-backlinks~ |
+| ~denote-link-find-file~ | ~denote-find-link~ |
+| ~denote-link-insert-link~ | ~denote-insert-link~ (alias for ~denote-link~) |
+| ~denote-link-show-backlinks-buffer~ | ~denote-show-backlinks-buffer~ (alias for ~denote-backlinks~) |
+
+** Denote buffers can have shorter names
+:PROPERTIES:
+:CUSTOM_ID: h:98f6b10a-ea29-49d1-8d3f-e2f0409f4c8f
+:END:
+
+The Denote file-naming scheme is designed to be a low-tech way of
+embedding information in files, making them easier to find. A
+downside is that the names are longer than =blah.txt= and so the
+default Emacs behaviour is to derive a buffer name from the file name.
+
+The new optional =denote-rename-buffer.el= provides a minor mode to
+automatically rename the buffer of an existing file, such that it
+reflects the file's =TITLE= field. Users must enable
+~denote-rename-buffer-mode~.
+
+The renaming procedure is controlled by the user option
+~denote-rename-buffer-function~. By default, it provides the means to
+rename using (i) the title, (ii) the identifier, or (iii) a custom
+function that returns a string. Experienced users can refer to
+~denote-rename-buffer-with-title~ to draw inspiration on the design of
+such a function.
+
+Thanks to Morgan Davidson for asking a question that inspired me to
+implement this feature. The discussion took place in issue 151 on the
+GitHub mirror <https://github.com/protesilaos/denote/issues/151>.
+
+** Silos work as directory trees
+:PROPERTIES:
+:CUSTOM_ID: h:113820c4-7a6f-4126-9a44-92bfa59744e2
+:END:
+
+Denote provides a feature to isolate files in to their own silos, each
+of which functions as its own ~denote-directory~ variable. The
+technicalities are explained in the manual. Silos have proven to be a
+valuable aspect of file management and I have thus expanded their
+scope to work as fully fledged directory trees. This means that we no
+longer assume a silo to be a flat directory listing, but instead
+recognise any subdirectories inside of it.
+
+Thanks to relict007, Hilde Rhyne, Mirko Hernández, Noboru Ota
+(nobiot), Alan Schmitt, hapst3r, and Hilde Rhyne for their
+participation in the relevant discussions:
+
+- <https://lists.sr.ht/~protesilaos/denote/%3C87fsb72nge.fsf%40protesilaos.com%3E>
+- <https://lists.sr.ht/~protesilaos/denote/%3C80CBB671-D812-4EA8-8C80-85F9F4144051%40disroot.org%3E>
+- <https://lists.sr.ht/~protesilaos/denote/%3C87pma6t59i.fsf%40kotlak.com%3E>
+- <https://github.com/protesilaos/denote/issues/129> (GitHub mirror)
+- <https://lists.sr.ht/~protesilaos/denote/%3CB124A5AF-9968-4F7E-9F4B-2BC763E0BFCF@disroot.org%3E#%3Cm0sff0nnhb.fsf@disroot.org%3E>.
+
+** Keywords do not accept multiple words by default
+:PROPERTIES:
+:CUSTOM_ID: h:08f23806-9570-4031-86e4-810b3e93be81
+:END:
+
+The idea is to have short keywords and then use more than one, if
+necessary. We do not want to encourage the habit of long keywords
+that become overly specific, while we want to avoid the use of
+dashes as delimited in the file name's =KEYWORDS= field.
+
+Technically, this changes the default value of the user option
+~denote-allow-multi-word-keywords~. Users who preferred the old
+behaviour can simply toggle it on.
+
+** Pass arguments to Org capture
+:PROPERTIES:
+:CUSTOM_ID: h:58ff6dd3-693a-4437-9217-8e876d92c975
+:END:
+
+Denote is not an extension of Org mode, though it can integrate with
+~org-capture~. I now make it possible to design a capture template
+that uses specific prompts. Consult the section in the manual titled
+"Create note with specific prompts using Org capture".
+
+Thanks to Aditya Yadav for asking about this in issue 132 on the
+GitHub mirror: <https://github.com/protesilaos/denote/issues/132>.
+
+** Change an existing note's file type
+:PROPERTIES:
+:CUSTOM_ID: h:e1e874e3-d8ad-4685-aa62-59ad07078db2
+:END:
+
+The command ~denote-change-file-type~ changes the file type of an
+existing note. The available options are those among
+~denote-file-type~. Thanks to Jean-Philippe Gagné Guay for the
+contribution, which was done in pull request 137 on the GitHub mirror:
+<https://github.com/protesilaos/denote/pull/137>.
+
+** Denote dynamic blocks can now parse ~rx~ notation
+:PROPERTIES:
+:CUSTOM_ID: h:fe595ee7-8ba6-4ca3-aa66-35aa4e5ca0f5
+:END:
+
+Denote can leverage the Org feature of "dynamic blocks" to produce
+lists of links/backlinks. This is especially useful for metanotes
+(read the Denote manual---I document everything for a reason).
+
+Before, regular expressions were implemented only as strings while now
+they can also be written using the ~rx~ notation. Thanks to Mirko
+Hernandez for proposing this feature and discussing it with me in
+issue 122 on the GitHub mirror:
+<https://github.com/protesilaos/denote/issues/122>.
+
+Thanks to Elias Storms, the author of =denote-org-dblock.el=, for
+iterating on this functionality. This was done in pull request 130 on
+the GitHub mirror: <https://github.com/protesilaos/denote/pull/130>.
+
+** Made links to non-note files works as intended
+:PROPERTIES:
+:CUSTOM_ID: h:431a8952-0d71-4ba6-b6ae-85e5f7d520b9
+:END:
+
+The function ~denote-get-path-by-id~ is refactored to accept any file
+with an identifier. This always was its intended purpose. The user
+was always able to create =denote:= Org link types to, for example,
+=jpg= files but ~denote-get-path-by-id~ was refusing to resolve the
+otherwise valid path. Thanks to user relict007 for reporting the
+problem and discussing it with me in issue 135 on the GitHub mirror:
+<https://github.com/protesilaos/denote/issues/135>.
+
+The change was not trivial. It was followed up by a patch from Noboru
+Ota (nobiot) which elaborated on the conditionality. Quoting from
+commit =9ce9a24=:
+
+#+begin_quote
+fix(denote-get-path-by-id): #135
+
+Reference: https://github.com/protesilaos/denote/issues/135
+
+This patch change function 'denote-get-path-by-id' to allow for the following:
+
+- A single ID points to multiple files with different extensions
+- Denote needs to find a single file out of the multiple files
+- This is not necessarily a user error (export an Org file to an HTML)
+- Denote should let user decide their "primary" file extension
+
+The case the patch is intended to fix goes something like this:
+
+- You have 20230216__mynotes--tag.org.
+- You export it to 20230216__mynotes--tag.html.
+- Both files are in denote-directory
+- This means you have two files with the same ID with different
+ extensions denote-link-find-file, denote-link-find-backlink, and xref
+ integration might find the html file INSTEAD OF the .org file
+
+This is because html is earlier in the alphabetical order than
+org. Because the function uses seq-find, it will find the .html file
+first and returns it.
+#+end_quote
+
+** The ~denote-rename-file-using-front-matter~ works with empty keywords
+:PROPERTIES:
+:CUSTOM_ID: h:b00f228d-7f84-4d84-8d5f-ac90ea6b1065
+:END:
+
+Keywords are an optional field in the Denote file-naming scheme.
+However, an earlier version of the command mentioned in this heading
+was considering them mandatory and would refuse to proceed if the
+keywords were nil. Thanks to Eduardo Grajeda for fixing this:
+<https://lists.sr.ht/~protesilaos/denote/patches/39896>.
+
+The change is within the ~15 line limit and does not require copyright
+assignment to the Free Software Foundation.
+
+** The ~denote-title-prompt~ has its own history
+:PROPERTIES:
+:CUSTOM_ID: h:91f370f4-9fd1-461b-8ba4-fd9ba2d9c7a8
+:END:
+
+Denote implements minibuffer histories for all its relevant functions.
+This makes it easier for users to retrieve their previous inputs and
+to not get irrelevant ones.
+
+Before, the ~denote-title-prompt~ was not using its own history but
+was instead relying on another one that was intended only for file
+paths, thus mixing unrelated inputs.
+
+Thanks to Jonathan Sahar for bringing this matter to my attention.
+This was done in issue 144 on the GitHub mirror:
+<https://github.com/protesilaos/denote/issues/144>.
+
+** For developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:dcc52671-2127-47e0-9167-003f40ca3a54
+:END:
+
+*** Made it possible to add predicates for recursive file listing
+:PROPERTIES:
+:CUSTOM_ID: h:62546ec1-6ec8-41c7-9a18-10a531b534ce
+:END:
+
+The helper function ~denote--directory-all-files-recursively~ accepts
+predicates to help speed up its work.
+
+Thanks to Wade Mealing for reporting the issue about the performance
+of the built-in function ~directory-files-recursively~ in large,
+nested directories. And thanks to Graham Marlow for the patch, which
+was prepared as part of an extended discussion with me:
+
+- <https://lists.sr.ht/~protesilaos/denote/patches/40370>
+- <https://lists.sr.ht/~protesilaos/denote/%3C20230414000311.1981-1-graham%40mgmarlow.com%3E#%3C76ed9fe2-d597-f7b9-5e59-717aeb77c3c3@mgmarlow.com%3E>
+- <https://lists.sr.ht/~protesilaos/denote/patches/40384>
+- <https://lists.sr.ht/~protesilaos/denote/%3C87edonhvy0.fsf%40protesilaos.com%3E>
+- <https://lists.sr.ht/~protesilaos/denote/%3C76ed9fe2-d597-f7b9-5e59-717aeb77c3c3%40mgmarlow.com%3E>
+- <https://lists.sr.ht/~protesilaos/denote/%3C87zg75q4er.fsf%40protesilaos.com%3E>
+- <https://lists.sr.ht/~protesilaos/denote/%3CCAO4UgPQtxhhqW0tB7eZnVh4nF9vLvnVGx+5oB_78_dg32URSLA%40mail.gmail.com%3E>
+
+*** New public symbols
+:PROPERTIES:
+:CUSTOM_ID: h:ed723274-a78e-4cfd-9655-c3bfe0fb1e68
+:END:
+
+The following are now public symbols that we commit to support and
+document henceforth:
+
++ Function ~denote-file-type-extensions~ :: Return all file type
+ extensions in ~denote-file-types~.
+
++ Variable ~denote-encryption-file-extensions~ :: List of strings
+ specifying file extensions for encryption.
+
++ Function ~denote-file-type-extensions-with-encryption~ :: Derive
+ ~denote-file-type-extensions~ plus ~denote-encryption-file-extensions~.
+
++ Function ~denote-link-return-links~ :: Return list of links in
+ current or optional =FILE=. Also see ~denote-link-return-backlinks~.
+
++ Function ~denote-link-return-backlinks~ :: Return list of links in
+ current or optional =FILE=. Also see ~denote-link-return-links~.
+
++ Function ~denote-rewrite-front-matter~ :: Rewrite front matter of
+ note after ~denote-rename-file~ (or related) The =FILE=, =TITLE=,
+ =KEYWORDS=, and =FILE-TYPE= arguments are given by the renaming
+ command and are used to construct new front matter values if
+ appropriate.
+
++ Function ~denote-rewrite-keywords~ :: Rewrite =KEYWORDS= in =FILE=
+ outright according to =FILE-TYPE=. Do the same as
+ ~denote-rewrite-front-matter~ for keywords, but do not ask for
+ confirmation. This is for use in ~denote-keywords-add~,
+ ~denote-keywords-remove~, ~denote-dired-rename-marked-files~, or
+ related.
+
+I am publicising the ~denote-link-return-links~ and its counterpart in
+response to the mailing list thread started by relict007:
+<https://lists.sr.ht/~protesilaos/denote/%3C87a5ygk6yi.fsf@kotlak.com%3E>.
+relict007 is the developer of the ~denote-cache~ package (in
+progress): <https://git.sr.ht/~relict007/denote-cache>.
+
+Similarly, the ~denote-rewrite-keywords~ is made public upon the
+request of Alan Schmitt:
+<https://lists.sr.ht/~protesilaos/denote/%3Cm2ttzgn2wu.fsf%40m4x.org%3E>.
+
+** Miscellaneous
+:PROPERTIES:
+:CUSTOM_ID: h:918087e6-8cd5-4d4f-a11a-b465dcbd9fe3
+:END:
+
+- Revised ~denote-link-return-{links,backlinks}~ to not produce a
+ ~user-error~. The errors are reserved for the interactive
+ functions. The others are for developers. Thanks to Elias Storms for
+ bringing this matter to my attention:
+ <https://github.com/protesilaos/denote/commit/694c1517be73949edbc3993c105c764da8e2571f#commitcomment-112677876>.
+
+- Refrained from trying to find forward links in non-text-files. If a
+ file extension is not in ~denote-file-types~, we have no way of
+ parsing or finding outgoing links in it. This change checks for the
+ file extension early on in 'when-let*' block and avoids opening the
+ file which is a relatively costly operation (and would fail finding
+ links anyway). Thanks to relict007 for the patch. This was done on
+ the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C87r0riffdx.fsf%40kotlak.com%3E>
+ The change is small and thus does not require copyright assignment
+ to the Free Software Foundation.
+
+- Explained how to troubleshoot Denote. Refer to the section in the
+ manual titled "Troubleshoot Denote in a pristine environment."
+ While this is about Denote, the skills apply to all Emacs packages.
+
+- Ensured backlinks get correct ~denote-directory~ path. The
+ backlinks buffer will now get the correct path when it is generated
+ inside a silo. This is related to issue 129 reported by hapst3r on
+ the GitHub mirror: <https://github.com/protesilaos/denote/issues/129>.
+ The change is necessary because =.dir-locals.el= do not work for
+ buffers, so we must get the value from the file that calls
+ ~denote-link-backlinks~.
+
+- Added missing underscore from examples in exporting section. Thanks
+ to Peter Prevos for bringing this matter to my attention:
+ <https://lists.sr.ht/~protesilaos/denote/%3C87fs8b85tq.fsf%40prevos.net%3E#%3C87lehiuxfo.fsf@protesilaos.com%3E>.
+
+- Made the command ~denote-open-or-create~ work with an empty
+ ~denote-directory~. The ~denote-file-prompt~ would throw an error
+ before. The correct behaviour is to proceed to the "Create" phase
+ if the ~denote-directory~ is empty. Thanks to user drcxd for
+ reporting the bug in issue 131 on the GitHub mirror and for testing
+ my sample code: <https://github.com/protesilaos/denote/issues/131>.
+
+- Documented how to use tree-based file prompt on demand. This is my
+ solution to a request made by Mirko Hernandez on the possible use of
+ the old Denote file prompt. It is better not to introduce a user
+ option for this case, nor to keep multiple variants of the
+ ~denote-file-prompt~ in denote.el, as we want to keep things simple.
+ Mirko's feedback was provided in issue 121 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/issues/121>.
+
+- Added the variable ~denote-user-enforced-denote-directory~. This is
+ intended for users who write custom code to extend Denote. The
+ value of this variable should be ~let~ bound around calls to the
+ function ~denote-directory~, thus overriding its return value. This
+ was discussed on the mailing list and then introduced by Vedang
+ Manerikar in commit =977c757=, with further changes by me in
+ =20ddc97=: <https://lists.sr.ht/~protesilaos/denote/patches/41776>.
+ Vedang has assigned copyright to the Free Software Foundation.
+
+- Fixed ~my-denote-org-extract-subtree~ section of the documentation.
+ This is part of some sample code that is not part of =denote.el=,
+ but we provide as a convenience/inspiration for interested parties.
+
+ The provided function did not work correctly.
+
+ 1. Tags are extracted before deleting the region from the source file.
+ 2. The function ~org-end-of-subtree~ is called to calculate the
+ point we should delete up to. The previously used function
+ ~org-entry-end-position~ ends at the first sub-heading under the
+ tree, which is not what we want. Instead, we want to cut the
+ whole subtree.
+ 3. The date information available in the subtree is retained. We
+ look for three common places for this information: the =CREATED=
+ or =DATE= properties in the =PROPERTIES= drawer, and the =CLOSED=
+ cookie at the element level itself.
+
+ Thanks to Vedang Manerikar for the contribution:
+ <https://lists.sr.ht/~protesilaos/denote/%3CCABzEscbPx24LCUCc7JsMmQtVGwhou5fUH_5h+%3Dt%3Dqi4396NqNQ%40mail.gmail.com%3E>
+
+- Removed the dependency on the built-in ~xdg~ library and updated the
+ default value of the user option ~denote-directory~. The reason is
+ that XDG is a Linux standard that does not work on other operating
+ systems, according to private feedback I received.
+
+- Fixed a regression for =M-p= (~previous-history-element~) in "do or
+ create" commands. Read the doc string of the commands
+ ~denote-open-or-create~ or ~denote-link-or-create~ for how this is
+ supposed to work. In short:
+
+ - Invoke the "do or create" command.
+ - Type something that does not match a file.
+ - In the following title prompt, hit =M-p= to bring back the last input.
+
+ I realised there was a regression when I read issue 152 on the
+ GitHub mirror, which was created by user "ustcpxy":
+ <https://github.com/protesilaos/denote/issues/152>. The issue is
+ about skipping the file title prompt.
+
+- Simplified the internal ~denote--buffer-file-names~. Thanks to Adam
+ Růžička for noting that my change was not compatible with older
+ Emacs versions, and for preparing the change. This was discussed in
+ pull request 158 on the GitHub mirror, with my suggestion to not use
+ ~seq-filter~ as it affected the return value:
+ <https://github.com/protesilaos/denote/pull/158>. The change is
+ below the 15 line limit, meaning that Adam does have to assign
+ copyright to the Free Software Foundation.
+
+- Documented custom code in the manual on how to interactively select
+ a silo. I am providing this in response to a request from GitHub
+ user rbenit68. The discussion took place in issue 127 on the GitHub
+ mirror, with the participation of Mirko Hernandez:
+ <https://github.com/protesilaos/denote/issues/127>. The custom code
+ I provide is the expanded version of an idea put forth by Mirko, to
+ whom I am thankful.
+
+- Fixed an outdated reference in the ~denote-file-types~ doc string.
+ Thanks to user doolio for spotting the error and reporting it in
+ issue 139 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/issues/139>.
+
+- Cited in the manual's section "Publications about Denote" an article
+ by Mohamed Suliman titled /Managing a bibliography of BiBTeX entries
+ with Denote/ (2022-12-20):
+ <https://www.scss.tcd.ie/~sulimanm/posts/denote-bibliography.html>.
+ If you have published something related to Denote, please let me
+ know and I will add to the list.
+
+- Cited the essay by Summer Emacs titled /An explanation of how I use
+ Emacs/ (2023-05-04):
+ <https://github.com/summeremacs/howiuseemacs/blob/main/full-explanation-of-how-i-use-emacs.org>
+
+- Cited the video series by Stefan Thesing titled /Denote as a
+ Zettelkasten/: <https://www.thesing-online.de/blog/denote-as-a-zettelkasten/>.
+
+- Added link to Karl Voit's work in the manual's section "Alternative
+ implementations and further reading." Thanks to Norwid Behrnd for
+ the contribution in pull request 123 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/123>.
+
+- Fixed the broken link to jao's blog. Thanks to Tomasz Hołubowicz
+ for the contribution, which was done in pull request 145 on the
+ GitHub mirror: <https://github.com/protesilaos/denote/pull/145>.
+
+- Authored lots of other ancillary changes/features to the code base
+ or the manual (yes, this change log is how I "cut the long story
+ short").
+
+* Version 1.2.0 on 2022-12-12
+:PROPERTIES:
+:CUSTOM_ID: h:92478a05-4a69-413c-8d95-1dacbcf6af2c
+:END:
+
+** Denote now requires Emacs version 28.1 or higher
+:PROPERTIES:
+:CUSTOM_ID: h:bc0e173a-3b9f-427c-9fb0-d435a5ef127e
+:END:
+
+With the help of Noboru Ota (nobiot), we realised that Denote was
+broken on Emacs 27 for quite a while. The fact that we received no
+feedback about it suggests that this change is the best course of
+action going forward. Discussion:
+<https://lists.sr.ht/~protesilaos/denote/%3C86r0yvzm12.fsf%40nobiot.com%3E#%3C86sfja78ik.fsf@nobiot.com%3E>
+
+Emacs 27 lacks certain Xref facilities that we need for the
+backlinking facility. It was holding us back for no good reason,
+while also adding to the maintenance burden.
+
+If you are using Denote on Emacs 27 and things are working for you,
+there is no need to update the package. Do it when you also upgrade
+Emacs to a newer version.
+
+** Display context in backlinks' buffer
+:PROPERTIES:
+:CUSTOM_ID: h:dafbdbae-36f1-487a-94c8-2762568a766e
+:END:
+
+By default, the generic backlinks' buffer, which can be displayed with
+the command ~denote-link-backlinks~ (alias ~denote-link-show-backlinks-buffer~),
+only shows the file names of the linked notes.
+
+We have made it possible to produce a more informative view by showing
+the context of the link and also listing all links per file. This is
+done by setting the user option ~denote-backlinks-show-context~ to a
+non-nil value.
+
+To illustrate the difference, this is the default backlinks' buffer:
+
+#+begin_example
+Backlinks to "On being honest" (20220614T130812)
+------------------------------------------------
+
+20220614T145606--let-this-glance-become-a-stare__journal.txt
+20220616T182958--feeling-butterflies-in-your-stomach__journal.txt
+#+end_example
+
+And this is the one with ~denote-backlinks-show-context~ enabled:
+
+#+begin_example
+Backlinks to "On being honest" (20220614T130812)
+------------------------------------------------
+
+20220614T145606--let-this-glance-become-a-stare__journal.txt
+37: growing into it: [[denote:20220614T130812][On being honest]].
+64: As I said in [[denote:20220614T130812][On being honest]] I have never
+20220616T182958--feeling-butterflies-in-your-stomach__journal.txt
+62: indifference. In [[denote:20220614T130812][On being honest]] I alluded
+#+end_example
+
+Granted, here we show plain text though in Emacs the results have the
+appropriate colours of the active theme and are easier to read.
+
+Thanks to Noboru Ota (nobiot) for implementing this feature. We
+discussed it at length on the mailing list:
+<https://lists.sr.ht/~protesilaos/denote/%3C86r0yvzm12.fsf%40nobiot.com%3E>.
+
+Noboru has assigned copyright to the Free Software Foundation.
+
+** Dynamic Org blocks for lists of Denote links
+:PROPERTIES:
+:CUSTOM_ID: h:f7904a57-22c0-446f-b7e3-7a736332002c
+:END:
+
+Denote now includes the ~denote-org-dblock~ library. Activate it
+thus:
+
+#+begin_src emacs-lisp
+;; Register Denote's Org dynamic blocks
+(require 'denote-org-dblock)
+#+end_src
+
+A dynamic block gets its contents by evaluating a given function,
+depending on the type of block. The type of block and its parameters
+are stated in the opening =#+BEGIN= line of the block. Typing =C-c
+C-c= with point on that line runs the function, with the given
+arguments, and populates the block's contents accordingly.
+
+What Denote has is ways to write blocks that produce a list of links
+matching a given regular expression while conforming with some other
+parameters. The manual explains how to use this powerful feature
+(which is necessarily specific to the Org file type):
+<https://protesilaos.com/emacs/denote#h:8b542c50-dcc9-4bca-8037-a36599b22779>.
+
+Thanks to Elias Storms for authoring ~denote-org-dblock~ and for
+discussing this issue at length with me on the mailing list:
+<https://lists.sr.ht/~protesilaos/denote/%3Cm2sfisexx7.fsf%40MBA21.fritz.box%3E>.
+
+Elias has assigned copyright to the Free Software Foundation.
+
+** Integration with the built-in project.el and xref.el libraries
+:PROPERTIES:
+:CUSTOM_ID: h:e8a7d08c-cdf0-4207-92c1-391415b8371f
+:END:
+
+Denote was already using Xref internally but has now gained more
+capabilities which help it find files more effectively. With the help
+of Emacs' standard project library, all file-related prompts (e.g. to
+add a link) search all items in the ~denote-directory~ regardless of
+whether the user is in a subdirectory or not.
+
+All Denote commands benefit from this refactoring. One such request
+to "Make ~denote-open-or-create~ work better across subfolders" was
+made in issue 114 on the GitHub mirror:
+<https://github.com/protesilaos/denote/issues/114>.
+
+Thanks to Noboru Ota (nobiot) for introducing this feature together
+with a new system of "modules" for incorporating additional built-in
+functionality:
+
+- <https://lists.sr.ht/~protesilaos/denote/%3C86a64ooxyi.fsf%40nobiot.com%3E>
+- <https://lists.sr.ht/~protesilaos/denote/%3C86k03f4iq6.fsf%40nobiot.com%3E>
+
+I will not document the new user option ~denote-modules~ right now as
+my ongoing job search prevented me from exploring the full potential
+of this feature. I promise to do it for the next version of Denote
+and update the manual accordingly. Nevertheless, the doc string of
+~denote-modules~ already provides all one needs to get started.
+
+** Re-use last input in "do or create" commands
+:PROPERTIES:
+:CUSTOM_ID: h:5a003d44-7ad0-4c92-b908-ec7cf016b2dd
+:END:
+
+The commands ~denote-open-or-create~, ~denote-link-or-create~ first
+prompt for an existing note. If they find it, they act on it,
+otherwise they prompt for the creation of a new note to operate on.
+
+At the first prompt, it is common to use regular expressions and
+out-of-order pattern matching (such as with the ~orderless~ package),
+so the input can be something like =_test ^2022 some title=, which we
+obviously don't want to automatically reuse as the new note's actual
+title.
+
+To this end, and to accommodate all workflows, we leverage Emacs'
+minibuffer history to make the last input accessible with =M-p= at the
+minibuffer prompt (=M-x previous-history-element=). The text is
+available for further editing before it is submitted as the new note's
+title. Simple, effective, and flexible!
+
+Thanks to Guo Yong for starting the discussion that led me to this
+improvement:
+<https://lists.sr.ht/~protesilaos/denote/%3CNF6pFBq--3-9%40tutanota.com%3E>.
+
+** Add support for any file type
+:PROPERTIES:
+:CUSTOM_ID: h:e73a4e76-6c00-4691-8893-8f885c26f306
+:END:
+
+Denote provides the user option ~denote-file-type~ which specifies the
+file type to use for new notes. Options include Org mode (the
+default), Markdown+YAML, Markdown+TOML, and plain text. Furthermore,
+there exists the convenience command ~denote-type~ (alias
+~denote-create-note-using-type~) which prompts for a file type to use
+when creating a new note (I normally write in plain text, but
+sometimes switch to Org or Markdown).
+
+The variable ~denote-file-types~ (which is NOT a user option)
+specifies all the parameters of what a "file type" means, such as how
+to format its front matter, what style of date+time to use, which file
+type extension to write, how to rename the file, what style of link to
+apply, and so on. Advanced users can now edit this variable to either
+register new file types or redefine the behaviour of existing ones.
+Read this comprehensive guide on how to do it:
+<https://protesilaos.com/codelog/2022-10-30-demo-denote-custom-file-type/>.
+
+I repeat: this is for advanced users or, anyhow, for those who are
+prepared to maintain some custom code in their setup. The guide is
+accessible though and I am always willing to help anyone in need of
+assistance.
+
+A relevant request for such a feature can be found in issue 86 on the
+GitHub mirror: <https://github.com/protesilaos/denote/issues/86>.
+
+The ~denote-file-types~ were introduced by Jean-Philippe Gagné Guay in
+pull request 89 at the GitHub mirror and were part of Denote version
+0.6.0: <https://github.com/protesilaos/denote/pull/89>. I have made
+lots of changes since then to make all parts of Denote work with it
+and to parameterise its various facets.
+
+** Exclude certain directories from all operations
+:PROPERTIES:
+:CUSTOM_ID: h:04f42aab-d8fe-4c4a-b865-3bb0655e2631
+:END:
+
+The user option ~denote-excluded-directories-regexp~ instructs all
+Denote functions that read or check file/directory names to omit
+directories that match the given regular expression. The regexp needs
+to match only the name of the directory, not its full path.
+
+Affected operations include file prompts and functions that return the
+available files in the ~denote-directory~. File prompts are used by
+several commands, such as ~denote-link~ and ~denote-subdirectory~.
+Functions that check for files include ~denote-directory-files~ and
+~denote-directory-subdirectories~.
+
+Thanks to Graham Marlow for the contribution which was done in pull
+request 112 on the GitHub mirror:
+<https://github.com/protesilaos/denote/pull/112>.
+
+The original contribution, with the subsequent tweaks I made to it, is
+within the eligible line count and thus does not require copyright
+assignment to the Free Software Foundation.
+
+** Exclude certain keywords from being inferred
+:PROPERTIES:
+:CUSTOM_ID: h:226ba85e-1f5e-45f5-956a-f5e8a95c397e
+:END:
+
+The user option ~denote-excluded-keywords-regexp~ omits keywords that
+match a regular expression from the list of inferred keywords.
+
+Keywords are inferred from file names and provided at relevant prompts
+as completion candidates when the user option ~denote-infer-keywords~
+is non-nil.
+
+Thanks to Stefan Thesing for proposing this idea in issue 115 on the
+GitHub mirror: <https://github.com/protesilaos/denote/issues/115>.
+
+[ Other people participate in that thread and there may be something
+ more coming out of it. ]
+
+** Use the ~citar-denote~ package for bibliography notes
+:PROPERTIES:
+:CUSTOM_ID: h:ff16633f-5fb8-4935-9e2f-044ec998d3f7
+:END:
+
+Peter Prevos has produced the ~citar-denote~ package which makes it
+possible to write notes on BibTeX entries with the help of the ~citar~
+package. These notes have the citation's unique key associated with
+them in the file's front matter. They also get a configurable keyword
+in their file name, making it easy to find them in Dired and/or
+retrieve them with the various Denote methods.
+
+With ~citar-denote~, the user leverages standard minibuffer completion
+mechanisms (e.g. with the help of the ~vertico~ and ~embark~ packages)
+to manage bibliographic notes and access those notes with ease. The
+package's documentation covers the details: <https://github.com/pprevos/citar-denote/>.
+
+Thanks to Peter Prevos for developing this package and for mentioning
+it on the Denote mailing list:
+<https://lists.sr.ht/~protesilaos/denote/%3C877cz0e96r.fsf%40prevos.net%3E>.
+
+** New functions and variables for developers
+:PROPERTIES:
+:CUSTOM_ID: h:5cc2076d-d4d2-45be-b28e-9ec67eca82b4
+:END:
+
+Developers or users who maintain custom code now have access to:
+
++ Function ~denote-keywords-sort~
++ Function ~denote-keywords-prompt~
+
+Plus all the following which are related to the aforementioned ~denote-file-types~:
+
++ Variable ~denote-org-link-format~
++ Variable ~denote-md-link-format~
++ Variable ~denote-id-only-link-format~
++ Variable ~denote-org-link-in-context-regexp~
++ Variable ~denote-md-link-in-context-regexp~
++ Variable ~denote-id-only-link-in-context-regexp~
++ Function ~denote-date-org-timestamp~
++ Function ~denote-date-rfc3339~
++ Function ~denote-date-iso-8601~
+
+Again, users can implement support for ANY FILE TYPE and use it to
+write notes in, either as their default choice or on-demand. If
+anything, this highlights the flexibility of Denote.
+
+** Miscellaneous
+:PROPERTIES:
+:CUSTOM_ID: h:acbb0cf7-ad17-495e-85d2-821cbbfc3158
+:END:
+
++ Added the ~denote-keywords-sort~ function. The intent is to
+ abstract the task of sorting the keywords. Before, it was handled
+ by the ~denote-keywords-prompt~, which meant that keywords were not
+ sorted when the ~denote~ function was called from Lisp. Thanks to
+ Florian for bringing this matter to my attention, providing relevant
+ feedback, and fixing an omission of mine in ~denote-rename-file~:
+ <https://lists.sr.ht/~protesilaos/denote/%3C166689879712.8.6808878344988686135.71824507%40aboulafia.org%3E>.
+
++ Expanded the manual's entry on directory "silos" to include more
+ code examples. Thanks to Viktor Haag for asking a question on the
+ mailing list that inspired me to produce this entry:
+ <https://lists.sr.ht/~protesilaos/denote/%3CCANnkwC6NLd0VneUEqFrjh7TCUBLBgEtLCcPwM37JDvJXJCShVQ%40mail.gmail.com%3E>.
+
++ Included a section in the manual with a non-exhaustive list of
+ references to publications about Denote. As of this writing, it
+ includes entries from David Wilson (SystemCrafters), Jack Baty,
+ Jeremy Friesen, and Peter Prevos. If you have an article about
+ Denote, please contact me about it directly or on the Denote mailing
+ list and I will add it to the manual.
+
++ Tweaked how Org's HTML export produces links in order to avoid
+ broken subdirectory paths. Thanks to Thibaut Benjamin for the
+ contribution, which was done in pull request 116 on the GitHub
+ mirror: <https://github.com/protesilaos/denote/pull/116>.
+
+ The change concerns a single line and thus Thibaut requires no
+ copyright assignment to the Free Software Foundation.
+
++ Expanded the manual where necessary.
+
+* Version 1.1.0 on 2022-10-20
+:PROPERTIES:
+:CUSTOM_ID: h:8e0f536a-ab3b-4cab-82f7-529bc0e40dbd
+:END:
+
+** New commands or refinements to common use-cases
+:PROPERTIES:
+:CUSTOM_ID: h:5665e7ec-4f3a-4de3-8cb0-63d25a0db8c1
+:END:
+
++ The ~denote-link-add-missing-links~ is a companion to what we
+ already provide to produce a list of links to Denote files matching
+ a regular expression (the ~denote-link-add-links~). This new
+ command adds links that are not already present in the current file.
+ So if you have a metanote that references, say, your journal entries
+ but have not updated it in a month, you can revisit the metanote,
+ invoke ~denote-link-add-missing-links~, and then type the search
+ terms (e.g. =_journal=) to include what remains.
+
+ Thanks to Elias Storms for the initial contribution, which was done
+ in pull request 108 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/108>.
+
+ Elias has assigned copyright to the Free Software Foundation. It is
+ required for changes that exceed 15 lines in total.
+
++ The ~denote-link-find-backlink~ provides a minibuffer interface that
+ shows all backlinks to the current note. It complements the
+ existing ~denote-link-backlinks~ command (which also has the alias
+ ~denote-link-show-backlinks-buffer~). Each command has its own
+ niche: the minibuffer lets the user leverage powerful pattern
+ matching styles, such as those provided by the =orderless= package,
+ while the bespoke buffer provides an easy overview of what links to
+ the current note.
+
+ Thanks to Elias Storms for the original patch:
+ <https://lists.sr.ht/~protesilaos/denote/%3Cm2fsg6o2t6.fsf%40MBA21.fritz.box%3E#%3Cm2pmfam7yi.fsf@MBA21.fritz.box%3E>.
+
++ The ~denote-keywords-add~ and ~denote-keywords-remove~ are two
+ commands that interactively operate on the current note's front
+ matter to add or remove keywords. They use the familiar keywords'
+ prompt which means, among others, that they can read more than one
+ keyword at a time. To specify multiple keywords, separate each
+ input with a comma (or whatever the value of ~crm-separator~ is,
+ which should be a comma unless something out-of-the-ordinary is in
+ force).
+
+ Thanks to Elias Storms for the original patch, which was done as
+ part of a discussion on the mailing list and then iterated on:
+ <https://lists.sr.ht/~protesilaos/denote/%3Cm24jwvpbt2.fsf%40MBA21.fritz.box%3E#%3Cm28rlik0tc.fsf@MBA21.fritz.box%3E>.
+
++ The ~denote-link~ command will now recognise an active region and
+ use its text as the description of the inserted link. The default
+ behaviour is to use the file's title from its front matter or file
+ name. Thanks to Charanjit Singh for the original contribution,
+ which was done as part of pull request 109 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/109>. A subsequent
+ tweak was implemented in pull request 110, following a discussion
+ with me: <https://github.com/protesilaos/denote/pull/110>.
+
+ Charanjit's contribution is below the ~15 line threshold and thus
+ does not require copyright assignment to the Free Software
+ Foundation.
+
++ The renaming operations are now aware of the underlying version
+ control system and will use the appropriate command when a VCS is
+ available. In practice, renaming a file under, say, Git will
+ register it as a "rename" instead of two separate actions of
+ deletion and addition.
+
+ Thanks to Florian for the patch. It was discussed on the mailing
+ list and then underwent some changes:
+ <https://lists.sr.ht/~protesilaos/denote/%3C166547153518.8.941129310186454444.68125516@aboulafia.org%3E>.
+
++ The ~denote-rename-file-using-front-matter~ no longer fails to carry
+ out its intended task when the front matter has no keywords. If no
+ keywords are available, this is interpreted as a request to remove
+ the KEYWORDS component of the file name. This was always
+ technically possible and could be achieved with various permutations
+ of the user option ~denote-prompts~ (as explained in its doc string
+ or the manual). Denote only needs an identifier in the file name to
+ establish unique links (although I strongly encourage you to stick
+ to the standard file-naming scheme as it is informative, reliable,
+ and can work even if you access your data without Emacs).
+
+** For more advanced use-cases
+:PROPERTIES:
+:CUSTOM_ID: h:505c84dd-2959-4bd4-8af4-78d75592a6d5
+:END:
+
++ The variable ~denote-file-types~ has been tweaked to respond
+ directly to changes in its value done with ~setq~. Thanks to Noboru
+ Ota for the patch: <https://lists.sr.ht/~protesilaos/denote/%3C86k05gsqsg.fsf%40nobiot.com%3E>.
+
+ Noboru has assigned copyright to the Free Software Foundation.
+
++ The =:front-matter= property of the ~denote-file-types~ now accepts
+ a nil value. Denote could always work without front matter, but
+ this was not implemented flexibly in the ~denote-file-types~.
+ Thanks to Noboru Ota (nobiot) for pointing this out on the mailing
+ list: <https://lists.sr.ht/~protesilaos/denote/%3C86k05gsqsg.fsf%40nobiot.com%3E>.
+
++ The ~denote-file-prompt~ function now reads an optional
+ =INITIAL-TEXT= argument. This is a string that prepopulates the
+ minibuffer. It is useful for custom commands the user may have
+ where, for example, there is a need to automatically filter to
+ entries matching =_journal=. Thanks to Alan Schmitt for suggesting
+ the idea: <https://lists.sr.ht/~protesilaos/denote/%3C87pmf676n1.fsf@m4x.org%3E>.
+
++ The ~denote-rename-file-using-front-matter~ accepts an optional
+ =AUTO-CONFIRM= argument. It can either be passed interactively or
+ via Lisp. The doc string (or the manual) explains the details.
+ Thanks to Elias Storms for the initial patch:
+ <https://lists.sr.ht/~protesilaos/denote/%3Cm2a667aeku.fsf%40gmail.com%3E>.
+
++ The ~denote-prompt-for-date-return-id~ function uses the familiar
+ ~denote-date-prompt~ and returns the appropriate identifier. It is
+ used internally by some of our function, but we also provide it for
+ anyone who wants to write their own custom code.
+
++ The ~denote-retrieve-or-create-file-identifier~ function reads and
+ option =DATE= argument to its mandatory =FILE= argument. If =FILE=
+ does not have an identifier and optional =DATE= is non-nil, the
+ function invokes the ~denote-prompt-for-date-return-id~, as
+ mentioned above.
+
++ The ~denote-rename-file~ command accepts an optional =DATE=
+ argument. It functionally does what is described right above, with
+ the exception that this is for an interactive function (a
+ "command"). Read the detailed doc string or the manual for
+ everything that pertains to this powerful command.
+
+ Thanks to Florian for suggesting the idea on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C166521684647.7.5483179875879361874.67576870%40aboulafia.org%3E>.
+
++ The ~denote-directory-text-only-files~ function filters the
+ ~denote-directory-files~ to only return a list of text files. This
+ leaves out, say, mp3 files. The function is used internally, though
+ it may also prove useful in custom user code.
+
+** Miscellaneous refinements
+:PROPERTIES:
+:CUSTOM_ID: h:0531047f-ef15-412e-b265-886c55526d57
+:END:
+
++ Implemented a ~revert-buffer-function~ for the backlinks' buffer,
+ which is produced by the command ~denote-link-backlinks~. This
+ revert function is what the =g= key invokes with the default key
+ bindings (the command is ~revert-buffer~). It produces the buffer
+ anew, updating the list of backlinks accordingly.
+
++ Documented how to speed up the creation of the backlinks' buffer.
+ As this depends on the built-in =xref= library, the change is done
+ by specifying the value of the user option ~xref-search-program~ in
+ Emacs 28 or higher. For example:
+
+ #+begin_src emacs-lisp
+ (setq xref-search-program 'ripgrep)
+ #+end_src
+
+ For something more elaborate:
+
+ #+begin_src emacs-lisp
+ ;; Prefer ripgrep, then ugrep, and fall back to regular grep.
+ (setq xref-search-program
+ (cond
+ ((or (executable-find "ripgrep")
+ (executable-find "rg"))
+ 'ripgrep)
+ ((executable-find "ugrep")
+ 'ugrep)
+ (t
+ 'grep)))
+ #+end_src
+
++ Removed some minor duplication of effort in how the buttonisation of
+ links is done (what makes them clickable).
+
++ Made refinements to the definition of functions such as
+ ~denote-link-add-links~. There should be no noticeable change for
+ users, though this shows we care about code quality.
+
++ With Eshel Yaron, we tried to remove the empty indices for functions
+ and variables from the HTML version of the manual. These indices
+ are useful in the Info version, which can be accessed directly from
+ Emacs when the =denote= package is installed (for example, evaluate
+ =(info "(denote) Top")=), but they do not work with HTML. Alas,
+ what we tried to do did not work. Maybe Org has a way to control
+ what is exported where. We shall see. At any rate, thanks to Eshel
+ for the effort: <https://lists.sr.ht/~protesilaos/denote/patches/36028>.
+
++ All code that integrates the =denote:= custom hyperlink type with
+ Org's link facility is now assigned =autoload= cookies. These are
+ done to ensure that =denote= is loaded and is available in cases
+ where Org needs to access a =denote:= link at some early stage
+ (e.g. at startup before using Denote). Thanks to Sven Seebeck for
+ reporting the problem: <https://lists.sr.ht/~protesilaos/denote/%3C87r0zovwix.fsf%40svenseebeck.me%3E>.
+ Although Sven could not reproduce a bug reliably, I believe this
+ prevents such an eventuality.
+
++ Expanded or otherwise updated the manual to account for all of the
+ above, where appropriate.
+
+* Version 1.0.0 on 2022-09-30
+:PROPERTIES:
+:CUSTOM_ID: h:053975d7-3fe2-49e5-96a0-336483e5861c
+:END:
+
+This is the first major release of Denote. A part of the changes
+documented herein is for advanced users or developers who wish to
+extend Denote with their custom code. Though we first cover what
+applies to everyone.
+
+** Changes for all users
+:PROPERTIES:
+:CUSTOM_ID: h:25692d4f-08da-4938-a81e-54070d91f51a
+:END:
+
++ The custom Org hyperlink type of =denote:= can be visited from
+ outside the ~denote-directory~. We now provide the necessary glue
+ code that Org needs to store these =denote:= links. Storing them
+ can be done with an ~org-capture~ template or via the command
+ ~org-store-link~. Use this to, for example, capture a TODO that
+ references one of your notes.
+
+ =denote:= links work for as long as the referenced file is somewhere
+ in the ~denote-directory~ or one of its subdirectories.
+
+ Thanks to Marc Fargas for the contribution. Marc did not need to
+ assign copyright to the Free Software Foundation, as the patch was
+ within the ~15 line limit that is permissible.
+
+ The contribution was discussed on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/patches/35137>. A prior
+ exchange took place in issue 104 over at the GitHub mirror:
+ <https://github.com/protesilaos/denote/issues/104>.
+
+ Some further tweaks were made to the relevant function. Thanks to
+ Elias Storms for reporting on the mailing list a bug which revealed
+ a regression I introduced to the Org link storing mechanism:
+ <https://lists.sr.ht/~protesilaos/denote/%3C15D55F4B-64D1-4083-AD5E-B5BACA8F1909%40ap.be%3E>.
+
++ Following from above, the command ~denote-link-find-file~ finds
+ files reliably, regardless of where the link is stored. All it
+ needs is for the target file to be inside the ~denote-directory~.
+
+ I discovered this while exchanging views with Marc Fargas regarding
+ the aforementioned patch: <https://lists.sr.ht/~protesilaos/denote/patches/35137>.
+
++ The command ~denote-link-buttonize-buffer~, which "buttonizes"
+ =denote:= links in plain text and Markdown files, now performs its
+ task regardless of where the current file is stored. Those links
+ work for as long as the file they reference is somewhere inside the
+ ~denote-directory~.
+
++ The commands ~denote-link-after-creating~, ~denote-link-or-create~
+ provide a convenience for users who need to create link to notes
+ that may not exist yet. The idea is that one is expounding on a
+ given topic and wants to create a link to a relevant issue. They
+ are not sure if they have written anything about it yet, so they
+ invoke the relevant command. Consult their doc strings or read the
+ manual: <https://protesilaos.com/emacs/denote#h:9e41e7df-2aac-4835-94c5-659b6111e6de>.
+
+ Thanks to user sienic for suggesting the idea and for testing the
+ prototypes. And thanks to Juanjo Presa for participating in the
+ discussion to share the view that this functionality should be part of
+ denote.el. This happened in issue 96 over at the GitHub mirror:
+ <https://github.com/protesilaos/denote/issues/96>.
+
++ The command ~denote-open-or-create~ offers the convenience of
+ visiting a file, if it exists, else prompting for its creation.
+ Thanks to Alan Schmitt for the contribution. The patch was sent on
+ the mailing list: <https://lists.sr.ht/~protesilaos/denote/%3C87fsgvddny.fsf%40protesilaos.com%3E>.
+ It is within the limit of what is allowed without assigning
+ copyright to the Free Software Foundation, though Alan has done the
+ relevant paperwork.
+
++ The manual expands on two sections: (1) Variants of
+ ~denote-open-or-create~, (2) Variants of ~denote-link-or-create~.
+ They show how one can use the above "do or create" commands with
+ different permutations of the Denote prompts for new note creation.
+
++ The manual includes a section titled "Create a note with the
+ region's contents". Quote:
+
+ #+begin_quote
+ Sometimes it makes sense to gather notes in a single file and later
+ review it to make multiple notes out of it. With the following
+ code, the user marks a region and then invokes the command
+ ~my-denote-create-new-note-from-region~: it prompts for a title and
+ keywords and then uses the region's contents to fill in the newly
+ created note.
+ #+end_quote
+
+ This is not part of denote.el, though we provide it in the manual
+ for users that may need it. Thanks to sundar bp for suggesting the
+ idea. This was done via a private channel and the information is
+ shared with permission.
+
++ The manual has another entry titled "Split an Org subtree into its
+ own note", which is similar to the above idea of using the region's
+ contents but has some extra niceties provided by Org. Quote:
+
+ #+begin_quote
+ With Org files in particular, it is common to have nested headings which
+ could be split off into their own standalone notes. In Org parlance an
+ entry with all its subheadings is a "subtree". With the following code,
+ the user places the point inside the heading they want to split off and
+ invokes the command ~my-denote-split-org-subtree~. It will create a
+ note using the heading's text and tags for the new file. The contents
+ of the subtree become the contents of the new note and are removed from
+ the old one.
+ #+end_quote
+
+ Thanks to Sven Seebeck for suggesting the idea and for testing my
+ prototypes. This information is shared with permission, as it was
+ provided via a private channel.
+
++ The manual describes how a user can leverage the built-in
+ ~dired-virtual-mode~ to perform arbitrary sorting of their list of
+ notes. It also includes code for Eshell to quickly "export" a
+ command's output into a dedicated buffer (which can then be used to
+ derive a "virtual" Dired). Thanks to Yi Liu for asking the question
+ that inspired this entry:
+ <https://lists.sr.ht/~protesilaos/denote/%3C1C75FF01-EC76-49DF-9AEB-ED718A2795FF@gmail.com%3E>.
+
++ The ~denote-faces-broken-link~ has been removed. It was used for
+ Org links. The idea was to apply a different style if the link was
+ broken. However, the way fontification works means that there may
+ be a performance penalty as Org tries to check again and again if
+ the link is broken or note. As =denote:= links are robust (unless
+ the user tries to break them), this penalty is unacceptable. Thanks
+ to Peter Prevos for reporting the issue and discussing it with me on
+ the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C87k05umyyo.fsf%40prevos.net%3E>.
+
++ The "denote" group in Custom UI buffers now provides a link to the
+ Info manual that is shipped with the package. To read the manual,
+ evaluate =(info "(denote) Top")=. Else visit the official web page:
+ <https://protesilaos.com/emacs/denote>.
+
++ Fixed a case where an internal check for a note would throw an error
+ if the buffer was not visiting a file. Thanks to Hilde Rhyne was
+ the patch: it is below the ~15 line threshold and thus does not
+ require copyright assignment to the Free Software Foundation. The
+ issue was discussed on the mailing list and was pushed to users as
+ version =0.6.1=:
+ <https://lists.sr.ht/~protesilaos/denote/%3Cm035d7nq22.fsf%40disroot.org%3E>.
+
++ When linking to a file that has no front matter, Denote tries to use
+ the TITLE component of the file name (per our file-naming scheme) as
+ the link's descriptive text. We now make this look a bit better, by
+ capitalising only the first letter while dehyphenating the text,
+ converting =this-is-a-test= to =This is a test=. Before, we would
+ capitalise all words. Thanks to Clemens Radermacher for the patch.
+ It was sent via a private channel. Clemens has assigned copyright
+ to the Free Software Foundation.
+
+** Changes for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:165cd056-5e27-4536-b8ac-57f88c927a43
+:END:
+
+Lots of functions and variables which once were for "private" use (the
+presence of double hyphens in the symbol) are now made public.
+Concretely this means that they no longer have double hyphens in their
+name and we pledge to support them henceforth. "Support" means that
+we (i) consider them stable, (ii) document them properly, (iii) will
+record any changes made to them such as in a change log, a blog post
+on my website, and via ~make-obsolete~.
+
+The manual provides a complete reference of what is on offer. The
+section is titled "For developers or advanced users":
+<https://protesilaos.com/emacs/denote#h:c916d8c5-540a-409f-b780-6ccbd90e088e>.
+
+Normally, we do not support private forms and can delete/modify them
+without notice. However, I decided to write obsoletion aliases for
+all forms I made public or otherwise revised, in an effort not to
+break any existing custom code. The following table covers all
+obsolete symbols and their new counterparts. PLEASE UPDATE YOUR CODE
+as those aliases will be removed in the near future.
+
+| Index | Old symbol | New symbol |
+|-------+------------------------------------------------+---------------------------------------------------|
+| 1 | denote--id-format | denote-id-format |
+| 2 | denote--id-regexp | denote-id-regexp |
+| 3 | denote--title-regexp | denote-title-regexp |
+| 4 | denote--keywords-regexp | denote-keywords-regexp |
+| 5 | denote--punctuation-regexp | denote-excluded-punctuation-regexp |
+| 6 | denote-punctuation-excluded-extra-regexp | denote-excluded-punctuation-extra-regexp |
+| 7 | denote--sluggify | denote-sluggify |
+| 8 | denote--sluggify-and-join | denote-sluggify-and-join |
+| 9 | denote--sluggify-keywords | denote-sluggify-keywords |
+| 10 | denote--desluggify | denote-desluggify |
+| 11 | denote--only-note-p | denote-file-is-note-p |
+| 12 | denote--file-has-identifier-p | denote-file-has-identifier-p |
+| 13 | denote--file-supported-extension-p | denote-file-has-supported-extension-p |
+| 14 | denote--writable-and-supported-p | denote-file-is-writable-and-supported-p |
+| 15 | denote--file-name-relative-to-denote-directory | denote-get-file-name-relative-to-denote-directory |
+| 16 | denote-link--id-from-string | denote-extract-id-from-string |
+| 17 | denote--directory-files | denote-directory-files |
+| 18 | denote--subdirs | denote-directory-subdirectories |
+| 19 | denote--get-note-path-by-id | denote-get-path-by-id |
+| 20 | denote--directory-files-matching-regexp | denote-directory-files-matching-regexp |
+| 21 | denote--retrieve-read-file-prompt | denote-file-prompt |
+| 22 | denote--extract-keywords-from-path | denote-extract-keywords-from-path |
+| 23 | denote--keywords-prompt | denote-keywords-prompt |
+| 24 | denote--retrieve-filename-identifier | denote-retrieve-filename-identifier |
+| 25 | denote--file-name-id | denote-retrieve-or-create-file-identifier |
+| 26 | denote--retrieve-filename-title | denote-retrieve-filename-title |
+| 27 | denote--retrieve-title-value | denote-retrieve-title-value |
+| 28 | denote--retrieve-title-line | denote-retrieve-title-line |
+| 29 | denote--retrieve-keywords-value | denote-retrieve-keywords-value |
+| 30 | denote--retrieve-keywords-line | denote-retrieve-keywords-line |
+| 31 | denote--format-file | denote-format-file-name |
+| 32 | denote--barf-duplicate-id | denote-barf-duplicate-id |
+| 33 | denote--title-prompt | denote-title-prompt |
+| 34 | denote--file-type-prompt | denote-file-type-prompt |
+| 35 | denote--date-prompt | denote-date-prompt |
+| 36 | denote--subdirs-prompt | denote-subdirectory-prompt |
+| 37 | denote--template-prompt | denote-template-prompt |
+| 38 | denote--filetype-heuristics | denote-filetype-heuristics |
+| 39 | denote--rename-file | denote-rename-file-and-buffer |
+| 40 | denote--rename-file-prompt | denote-rename-file-prompt |
+
+If you are writing code that extends Denote and feel that something is
+either missing or has remained private, please contact us on the
+mailing list, the GitHub/GitLab mirror, or send me an email directly.
+I always respond in a timely fashion.
+
+** Open to everyone
+:PROPERTIES:
+:CUSTOM_ID: h:27a391cf-8d5e-4d19-942f-46fc52dea80c
+:END:
+
+The most common feedback I get about Denote is that its documentation
+is good. As you can tell from these change logs, the plan is to
+continue on this path.
+
+Please note that the communication channels for Denote (mailing list,
+mirrors, my personal email) are open to users of all levels. Do not
+hesitate to contact us/me.
+
+Thanks again to everyone for their contributions, direct or indirect,
+either in the form of code or the discussion of ideas. Quoting from
+the "Acknowledgements" section of the manual (all my packages have
+such a section):
+
+#+begin_quote
+Denote is meant to be a collective effort. Every bit of help matters.
+
++ Author/maintainer :: Protesilaos Stavrou.
+
++ Contributions to code or the manual :: Abin Simon, Alan Schmitt,
+ Benjamin Kästner, Clemens Radermacher, Colin McLear, Damien Cassou,
+ Eshel Yaron, Hilde Rhyne, Jack Baty, Jean-Philippe Gagné Guay, Jürgen
+ Hötzel, Kaushal Modi, Kyle Meyer, Marc Fargas, Peter Prevos, Philip
+ Kaludercic, Quiliro Ordóñez, Stefan Monnier.
+
++ Ideas and/or user feedback :: Abin Simon, Alan Schmitt, Alfredo
+ Borrás, Benjamin Kästner, Colin McLear, Damien Cassou, Elias Storms,
+ Frank Ehmsen, Hanspeter Gisler, Jack Baty, Juanjo Presa, Kaushal
+ Modi, M. Hadi Timachi, Paul van Gelder, Peter Prevos, Shreyas
+ Ragavan, Summer Emacs, Sven Seebeck, Taoufik, Yi Liu, Ypot, atanasj,
+ hpgisler, pRot0ta1p, sienic, sundar bp.
+
+Special thanks to Peter Povinec who helped refine the file-naming
+scheme, which is the cornerstone of this project.
+
+Special thanks to Jean-Philippe Gagné Guay for the numerous
+contributions to the code base.
+#+end_quote
+
+* Version 0.6.0 on 2022-08-31
+:PROPERTIES:
+:CUSTOM_ID: h:50aba79a-d702-42b4-a2a5-7fa29033f904
+:END:
+
+Denote is in a stable state. I consider it feature-complete, without
+prejudice to possible refinements to its existing feature set. The next
+version shall be =1.0.0=.
+
+** User-facing changes
+:PROPERTIES:
+:CUSTOM_ID: h:566a770b-399e-47a6-9aa4-326fd6ade9a7
+:END:
+
++ The Denote linking facility can now link to any file that has the
+ Denote file-naming scheme. Before, we limited this feature to what we
+ consider "note" files, else the supported plain text formats (per
+ ~denote-file-type~). Thanks to Peter Prevos for the discussion on the
+ mailing list: <https://lists.sr.ht/~protesilaos/denote/%3C87fsi1m5ze.fsf%40prevos.net%3E>.
+
++ Date prompts may optionally use the familiar Org date-selection
+ mechanism that leverages the calendar. This feature is subject to the
+ user option ~denote-date-prompt-use-org-read-date~. A date prompt is
+ used by the ~denote-date~ command or, optionally, by the ~denote~
+ command when the user option ~denote-prompts~ is configured
+ accordingly. The manual elaborates on the specificities. Thanks to
+ Jean-Philippe Gagné Guay for the contribution in pull request 97 at
+ the GitHub mirror: <https://github.com/protesilaos/denote/pull/97>.
+
++ Leading empty spaces at the ~denote~ =TITLE= prompt no longer produce
+ hyphens: they are simply ignored to keep file names consistent.
+ Thanks to Peter Prevos for the contribution in pull request 99 at the
+ GitHub mirror: <https://github.com/protesilaos/denote/pull/99>.
+
+ [ Peter has started the process for copyright assignment to the Free
+ Software Foundation, though the total contributions are still within
+ the permitted boundaries. ]
+
++ When linking to files that have no front matter, the link's anchor
+ text (the human-readable part) is derived from the file name =TITLE=
+ component. We apply a de-hyphenation and capitalisation of its
+ constituent words. This is not always perfect, but it is better than
+ something like =this-is-the-title=. Thanks to Peter Prevos for the
+ original idea in pull request 93 at the GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/93>.
+
++ The active region is now used as the default value of the ~denote~
+ command =TITLE= prompt. The idea behind this Do-What-I-Mean-flavoured
+ patch is to be able to take a note about a subject that appears in a
+ buffer by simply marking it before invoking the ~denote~ command.
+
+ Thanks to Eshel Yaron for the patch: <https://lists.sr.ht/~protesilaos/denote/patches/34870>.
+ It is below the ~15 line threshold that thus requires no copyright
+ assignment to the Free Software Foundation.
+
++ The ~denote-rename-file-using-front-matter~ command now offers to save
+ the buffer if appropriate. In the past, it would simply produce an
+ error asking the user to save the buffer. Thanks to Peter Prevos for
+ the contribution in pull request 103 at he GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/103>.
+
++ Fixed the text of the confirmation prompt in the command
+ ~denote-migrate-old-markdown-yaml-tags~. Thanks to Abin Simon for the
+ patch: <https://lists.sr.ht/~protesilaos/denote/patches/34632>.
+
+ This patchset also fixes (i) how a tag is identified for the purposes
+ of migrating old to new front matter, (ii) the regular expression for
+ Org front matter keywords
+
+ [ The total changes are below the ~15 line threshold and thus do not
+ require copyright assignment to the Free Software Foundation. ]
+
++ Fixed a bug that prevented the creation of new notes. Thanks to
+ Juergen Hoetzel for the contribution in pull request 84 at the GitHub
+ mirror: <https://github.com/protesilaos/denote/pull/84>. This was
+ done immediately after the release of version =0.5.0= on 2022-08-10
+ and was provided to users as version =0.5.1=
+
+ [ The change is below the ~15 line threshold. ]
+
+** Internal refinements
+:PROPERTIES:
+:CUSTOM_ID: h:9374b533-faaa-4ab4-b668-f74b5eae7ab5
+:END:
+
+These make the code simpler and more predictable. As the individual
+changes are not user-facing, I invite interested parties to consult the
+Git log. Special thanks to Jean-Philippe Gagné Guay for the multiple
+contributions (and relevant discussions) over at the GitHub mirror:
+
+- <https://github.com/protesilaos/denote/pull/88>
+- <https://github.com/protesilaos/denote/pull/89>
+- <https://github.com/protesilaos/denote/pull/91>
+- <https://github.com/protesilaos/denote/pull/94>
+- <https://github.com/protesilaos/denote/pull/101>
+- <https://github.com/protesilaos/denote/pull/102>
+
+[ Jean-Philippe has assigned copyright to the Free Software Foundation.
+ It is required for non-trivial changes. ]
+
+** For advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:c6fc05a2-ff31-4a0c-91a1-f64d2cfd6a16
+:END:
+
+The variable ~denote-file-types~ is an alist of plists which
+substantiates the supported file types (per the user option
+~denote-file-type~). Properties pertain to the formatting of front
+matter and the retrieval of relevant values. The doc string of
+~denote-file-types~ explains the details, while the default value uses
+the ancillary functions we define. Thanks to Jean-Philippe Gagné Guay
+for the relevant contributions in pull request 89 at the GitHub mirror:
+<https://github.com/protesilaos/denote/pull/89>.
+
+
+* Version 0.5.0 on 2022-08-10
+:PROPERTIES:
+:CUSTOM_ID: h:80b9daaa-c3c8-4457-b109-966bb6a99832
+:END:
+
+The general theme of this release is to refine what we already offer.
+As I explained in some discussions, Denote is feature-complete. We can
+always improve the code or add some ancillary function/command/variable,
+though all the main ideas have already been implemented. Additional
+functionality can be provided by other packages: I remain at the
+disposal of anyone willing to write such a package.
+
+The present release covers more than 150 commits since version 0.4.0 on
+2022-07-25.
+
+All release notes: <https://protesilaos.com/emacs/denote-changelog>.
+
+** Templates for new notes
+:PROPERTIES:
+:CUSTOM_ID: h:0878125f-8392-48e6-aeff-1469eb1e18fc
+:END:
+
+We now provide the ~denote-templates~ user option. A "template" is
+arbitrary text that Denote will add to a newly created note right below
+the front matter.
+
+Templates are expressed as a =(KEY . STRING)= association.
+
+- The =KEY= is the name which identifies the template. It is an
+ arbitrary symbol, such as =report=, =memo=, =statement=.
+
+- The =STRING= is ordinary text that Denote will insert as-is. It can
+ contain newline characters to add spacing. The manual of Denote
+ contains examples on how to use the ~concat~ function, beside writing
+ a generic string:
+ <https://protesilaos.com/emacs/denote#h:f635a490-d29e-4608-9372-7bd13b34d56c>.
+
+The user can choose a template either by invoking the new command
+~denote-template~ or by changing the user option ~denote-prompts~ to
+always prompt for a template when calling the ~denote~ command.
+
+Thanks to Jean-Philippe Gagné Guay for refinements to this facility.
+Done in pull request 77 on the GitHub mirror:
+<https://github.com/protesilaos/denote/pull/77>.
+
+[ Jean-Philippe has assigned copyright to the Free Software Foundation. ]
+
+** Revised format for Org =#+filetags= entry
+:PROPERTIES:
+:CUSTOM_ID: h:17688b79-cb1b-4a59-831e-fbf2a81245d3
+:END:
+
+Denote used to format tags in Org files by separating them with two
+spaces:
+
+#+begin_example
+#+filetags: tag1 tag2
+#+end_example
+
+While this worked for some obvious use-cases, it is not supported by
+Org. The Org documentation stipulates that tags be separated by the
+colon sign. The above would then be written thus:
+
+#+begin_example
+#+filetags: :tag1:tag2:
+#+end_example
+
+Denote now conforms with Org's specifications. To help users update
+their existing notes, we provide the ~denote-migrate-old-org-filetags~
+command. It will perform the conversion in all Org files that had the
+old notation. As with all Denote operations that rewrite file contents,
+it DOES NOT SAVE BUFFERS. The user is expected to review the changes,
+such as by using ~diff-buffer-with-file~. Multiple buffers can be saved
+with ~save-some-buffers~ (check its doc string).
+
+This command is provided for the convenience of the user. It shall be
+deprecated and eventually removed from future versions of Denote.
+
+If you need help with any of this, please do not hesitate to contact me
+either in private or in one of Denote's official channels (mailing list,
+GitHub/GitLab mirror).
+
+Thanks to Alan Schmitt for bringing this matter to my attention:
+<https://lists.sr.ht/~protesilaos/denote/%3C871qu0jw5l.fsf%40protesilaos.com%3E>.
+Also thanks to Jean-Philippe Gagné Guay for commenting on it as it
+helped me decide to include the command in =denote.el=:
+<https://github.com/protesilaos/denote/pull/83#issuecomment-1210167133>.
+
+** Revised format for Markdown+YAML =tags:= entry
+:PROPERTIES:
+:CUSTOM_ID: h:205a09cf-0159-425e-a6b3-41700fa3ad31
+:END:
+
+This is the same idea as above. Before, we were making the mistake of
+using incorrect YAML notation:
+
+#+begin_src yaml
+tags: tag1 tag2
+#+end_src
+
+Now we do:
+
+#+begin_src yaml
+tags: ["tag1", "tag2"]
+#+end_src
+
+This is how the TOML variant always worked.
+
+For the user's convenience, we provide a command to migrate from the old
+to the new syntax: ~denote-migrate-old-markdown-yaml-tags~.
+
+** Changes to file renaming and front matter rewriting
+:PROPERTIES:
+:CUSTOM_ID: h:15ecb4e8-d1ce-4e42-b74d-a3a046d93220
+:END:
+
+Denote adds "front matter" to newly created notes which includes data
+such as the title and keywords/tags of the document. Strictly speaking,
+the front matter is not required by Denote. It is provided for the
+user's convenience, such as for readability or if they want to use the
+note with other programs (e.g. Org export, a blog with Hugo/Jekyll,
+...).
+
+Denote provides commands which help the user rename their notes, by
+changing the file name's =TITLE= and/or =KEYWORDS= components (per
+Denote's file-naming scheme). These commands also operate on the front
+matter to keep the data between file name and file contents in sync
+(again, for the user's convenience).
+
+For this release we have consolidated and refined our offerings in order
+to improve their ergonomics. All changes are the result of fruitful
+discussions on the mailing list and the issue tracker of the GitHub
+mirror:
+
+- <https://lists.sr.ht/~protesilaos/denote/%3C87k081l6vw.fsf%40silverstone.mail-host-address-is-not-set%3E>
+- <https://lists.sr.ht/~protesilaos/denote/%3C878rogw5kk.fsf%40protesilaos.com%3E>
+- <https://lists.sr.ht/~protesilaos/denote/%3C87fsiljv1s.fsf%40hu.mail-host-address-is-not-set%3E>
+- <https://lists.sr.ht/~protesilaos/denote/%3C87r122afe3.fsf%40hu.mail-host-address-is-not-set%3E>
+- <https://github.com/protesilaos/denote/issues/74>
+
+Thanks to (A-Z) Hanspeter Gisler, Jean-Philippe Gagné Guay, and Peter
+Prevos for their participation.
+
+Also thanks to Jean-Philippe Gagné Guay for relevant code contributions
+(please consult the Git log for the minutiae):
+
+- <https://github.com/protesilaos/denote/pull/66>
+- <https://github.com/protesilaos/denote/pull/67>
+- <https://github.com/protesilaos/denote/pull/69>
+- <https://github.com/protesilaos/denote/pull/75>
+- <https://github.com/protesilaos/denote/pull/76>
+
+*** Renaming a single file
+:PROPERTIES:
+:CUSTOM_ID: h:1d695e54-1481-42dd-916b-c0542c48aa6f
+:END:
+
+The commands ~denote-dired-rename-file-and-add-front-matter~ and
+~denote-dired-rename-file~ are deprecated and superseded by the new
+~denote-rename-file~. Please update any key bindings in your setup.
+
+The difference between the old commands and the new ~denote-rename-file~
+is that the latter will now insert front matter to supported file types
+(per ~denote-file-type~) if they have none. This basically means that,
+e.g., renaming a generic Org/Markdown/Plain text file with
+~denote-rename-file~ will update its file name to comply with Denote's
+file-naming scheme and also add the appropriate front matter (it
+"converts" it to a Denote note). If front matter exists, this command
+will rewrite it to reflect the changes to the file name's =TITLE= and/or
+=KEYWORDS=.
+
+Consult the manual for the details:
+<https://protesilaos.com/emacs/denote#h:7cc9e000-806a-48da-945c-711bbc7426b0>.
+
+Or, if the new version of the GNU ELPA package is installed, evaluate:
+
+#+begin_src emacs-lisp
+(info "(denote) Rename a single file")
+#+end_src
+
+The user option ~denote-dired-rename-expert~ is obsolete. Denote always
+asks for confirmation when renaming a single file. This is because the
+user can rely on batch-renaming commands which ask for confirmation only
+once per batch.
+
+*** Renaming multiple files at once
+:PROPERTIES:
+:CUSTOM_ID: h:82455fb4-576b-4753-af66-ac48fd158327
+:END:
+
+The command ~denote-dired-rename-marked-files-and-add-front-matter~ is
+deprecated and its functionality is absorbed by the existing
+~denote-dired-rename-marked-files~ command. The deprecated command was
+used to insert front matter to supported file types (per
+~denote-file-type~) that had none. We now handle this internally, thus
+streamlining the experience for the user.
+
+Refer to the manual for the details:
+<https://protesilaos.com/emacs/denote#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde>
+
+Assuming the latest Info manual is installed, evaluate:
+
+#+begin_src emacs-lisp
+(info "(denote) Rename multiple files at once")
+#+end_src
+
+*** Renaming a single file based on its front matter
+:PROPERTIES:
+:CUSTOM_ID: h:d913e369-9325-46c4-985b-cf5b3e35372b
+:END:
+
+Introduced the ~denote-rename-file-using-front-matter~ command. This is
+new functionality we provide which uses the front matter as input to
+perform a rename of the file. The aforementioned offerings prompt for
+input via the minibuffer and propagate the changes firstly to the file
+name and subsequently to the front matter. Whereas with the command
+~denote-rename-file-using-front-matter~, the user can edit the front
+matter manually and then invoke the command to pass the changes to the
+file name, subject to a confirmation. Relevant entries are the title
+and tags/filetags (depending on the file type). The date and the
+identifier are not pertinent. Identifiers in file names are NEVER
+rewritten by Denote.
+
+Consult the manual:
+<https://protesilaos.com/emacs/denote#h:3ab08ff4-81fa-4d24-99cb-79f97c13a373>.
+
+With the latest package, evaluate:
+
+#+begin_src emacs-lisp
+(info "(denote) Rename a single file based on its front matter")
+#+end_src
+
+*** Renaming multiple files based on their front matter
+:PROPERTIES:
+:CUSTOM_ID: h:4efc6c14-fd71-4bd8-8bb1-e8e720b98eff
+:END:
+
+The command ~denote-dired-rename-marked-files-using-front-matter~
+completes the set of features we provide for syncing between file name
+and front matter. It applies to all marked files in a Dired buffer.
+
+Read the manual to understand how the command works and what it does
+exactly: <https://protesilaos.com/emacs/denote#h:ea5673cd-e6ca-4c42-a066-07dc6c9d57f8>.
+
+Or evaluate:
+
+#+begin_src emacs-lisp
+(info "(denote) Rename multiple files based on their front matter")
+#+end_src
+
+*** Add missing front matter on demand
+:PROPERTIES:
+:CUSTOM_ID: h:32a103be-71a2-48e4-a18e-7727c04545ed
+:END:
+
+Sometimes the user may have incomplete front matter, perhaps due to a
+mistake that was saved on disk. The command ~denote-add-front-matter~
+appends a new front matter block to the current note.
+
+Read:
+<https://protesilaos.com/emacs/denote#h:54b48277-e0e5-4188-ad54-ef3db3b7e772>
+
+Or evaluate:
+
+#+begin_src emacs-lisp
+(info "(denote) Regenerate front matter")
+#+end_src
+
+** Faces for Denote links
+:PROPERTIES:
+:CUSTOM_ID: h:507fb46c-a2e9-48a7-8cd2-53c5fc73394d
+:END:
+
+We provide the ~denote-faces-link~ and the ~denote-faces-broken-link~.
+The latter is only relevant for Org, as Emacs' standard button mechanism
+does not have a way to apply a face dynamically.
+
+This is a change for themes/tinkerers who need to differentiate
+=denote:= links from other links. Otherwise, the presentation is the
+same as before.
+
+Thanks to Peter Prevos for asking about it on the mailing list:
+<https://lists.sr.ht/~protesilaos/denote/%3C03618bb20d3eaba78c32cd0cb63bfc71%40prevos.net%3E>.
+
+** Use of XDG path in ~denote-directory~
+:PROPERTIES:
+:CUSTOM_ID: h:efa3049e-f1fa-48ff-af7d-d16edc677704
+:END:
+
+The default value of the ~denote-directory~ user option used to be
+=~/Documents/notes= (subject to some conversion via Elisp). Denote now
+conforms with the freedesktop.org specifications by using the =XDG=
+directory for =DOCUMENTS= instead of =~/Documents=:
+<https://www.freedesktop.org/wiki/Software/xdg-user-dirs/>.
+
+Users who already bind the ~denote-directory~ are not affected by this
+change. Same for those who do not tinker with =XDG= environment
+variables and/or do not use some exotic setup.
+
+Thanks to Philip Kaludercic for the patch:
+<https://lists.sr.ht/~protesilaos/denote/patches/34561#%3C20220809115824.43089-1-philipk@posteo.net%3E>
+
+** Bespoke major-mode for the backlinks' buffer
+:PROPERTIES:
+:CUSTOM_ID: h:feb9a0ed-ba15-486e-ae11-5b222b00bc31
+:END:
+
+The backlinks' buffer now uses the ~denote-backlink-mode~ instead of the
+generic ~special-mode~. The former derives from the latter. It binds
+keys to move between links with =n= (next) and =p= (previous). These
+are stored in the ~denote-backlink-mode-map~ (use =M-x describe-mode=
+(=C-h m=) in an unfamiliar buffer to learn more about it).
+
+Thanks to Philip Kaludercic for the patch:
+<https://lists.sr.ht/~protesilaos/denote/patches/34561#%3C20220809115824.43089-2-philipk@posteo.net%3E>
+
+** Changes to the manual
+:PROPERTIES:
+:CUSTOM_ID: h:80217a39-86b8-4310-b7c4-dcc14e0b98fd
+:END:
+
++ Documented all of the aforementioned. Improved how information is
+ presented and, generally, iterated on an already comprehensive
+ document.
+
++ Introduced a node which explains how to tweak the front matter:
+ <https://protesilaos.com/emacs/denote#h:7f918854-5ed4-4139-821f-8ee9ba06ad15>.
+ Or evaluate:
+
+ #+begin_src emacs-lisp
+ (info "(denote) Change the front matter format")
+ #+end_src
+
++ Updated the reference to =consult-notes=. This is a package that uses
+ the =consult= interface to provide access and search facilities for
+ notes. It can integrate with Denote. Thanks to Colin McLear for the
+ change in pull request 70 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/70>.
+
+ [ The change is below the ~15 line threshold and thus does not require
+ copyright assignment to the Free Software Foundation. ]
+
+** Internal restructuring
+:PROPERTIES:
+:CUSTOM_ID: h:5d09d0af-3c25-4419-8448-90b8e1adab0d
+:END:
+
++ All Denote code is consolidated in =denote.el=. We no longer maintain
+ separate files like =denote-link.el=, =denote-dired.el=, etc. Users
+ who had ~require~ calls to such libraries must remove them and only
+ keep:
+
+ #+begin_src emacs-lisp
+ (require 'denote)
+ #+end_src
+
++ User options that have an entry in the manual will now provide a link
+ to it via their Help buffer and/or the Custom UI. This is done by
+ adding the =:link= attribute to their declaration.
+
+ Furthermore, user options and faces now specify the version of Denote
+ that last affected their value (e.g. ~denote-directory~, which was
+ mentioned above for the XDG spec, now informs the user that it changed
+ for version =0.5.0=).
+
+ [ I learnt these by developing the =modus-themes=. ]
+
++ The variables ~denote-last-title~, ~denote-last-keywords~,
+ ~denote-last-buffer~, and ~denote-last-front-matter~ are all obsolete.
+ These were used prior to version =0.1.0= to help with development but
+ are now deemed surplus to requirements.
+
++ Lots of changes were made to private functions, variables, doc
+ strings, and comments, in the interest of simplifying the code and/or
+ ensuring consistency in how operations are carried out. Though
+ everything is the same for the end-user.
+
+Thanks to Jean-Philippe Gagné Guay for the numerous contributions on the
+GitHub mirror. They are important for Denote, though the user does not
+need to know what is happening internally (consult the Git log for the
+details):
+
+- <https://github.com/protesilaos/denote/pull/65>
+- <https://github.com/protesilaos/denote/pull/72>
+- <https://github.com/protesilaos/denote/pull/73>
+- <https://github.com/protesilaos/denote/pull/78>
+- <https://github.com/protesilaos/denote/pull/80>
+- <https://github.com/protesilaos/denote/pull/81>
+- <https://github.com/protesilaos/denote/pull/82>
+- <https://github.com/protesilaos/denote/pull/83>
+
+** Discussions
+:PROPERTIES:
+:CUSTOM_ID: h:79089c06-9e0c-49cc-9d53-a1a2fd72fb65
+:END:
+
+*** Encrypting Denote notes
+:PROPERTIES:
+:CUSTOM_ID: h:87e4556a-4864-4955-a98c-62b2e6a509c3
+:END:
+
+Paul van Gelder asked about this on the mailing list. I provided
+guidelines on what can be done, though did not record anything in the
+manual: I prefer to elicit more feedback from users. The gist is that
+Emacs already has all the requisite functionality, though encryption per
+se is outside the scope of Denote:
+<https://lists.sr.ht/~protesilaos/denote/%3C1123434736.64290.1658954014673%40kpc.webmail.kpnmail.nl%3E>.
+
+Denote's relevant internal mechanisms will recognise files ending in
+=.gpg= (e.g. for fontification in Dired).
+
+*** Visualise usage of Denote keywords
+:PROPERTIES:
+:CUSTOM_ID: h:d94ee5e3-0a54-404c-b44b-34edc3703fbc
+:END:
+
+Peter Prevos shared a proof-of-concept way to visualise keywords in the
+~denote-directory~ and show usage statistics:
+<https://lists.sr.ht/~protesilaos/denote/%3Ce9e5d6ae85984b51067b47f4d8e134fa%40prevos.net%3E>.
+
+We do not include this information in the manual, as we wait for the
+fully fledged code. Though do give it a try if you are interested and,
+perhaps, share your thoughts for Peter's consideration.
+
+*** Conflict between ~denote-dired-mode~ and ~diredfl-mode~
+:PROPERTIES:
+:CUSTOM_ID: h:0cbf504c-676c-436e-8ae8-e7115368e691
+:END:
+
+Hilde Rhyne shared a workaround they have to disable ~diredfl-mode~ in
+the buffers where ~denote-dired-mode~ is enabled. The conflict between
+the two is a known issue that is acknowledged in the manual:
+<https://lists.sr.ht/~protesilaos/denote/%3Cm0tu6q6bg0.fsf%40disroot.org%3E>.
+
+I think we need a proper solution in the code we provide, so this
+workaround is not mentioned in the manual.
+
+*** Why doesn't Denote provide a search facility?
+:PROPERTIES:
+:CUSTOM_ID: h:068108f4-a4fa-4ff8-be49-f1f10a862451
+:END:
+
+There was a discussion started by Fourchaux, with the participation of
+basaran and Andre0991 on the GitHub mirror:
+<https://github.com/protesilaos/denote/issues/71>.
+
+The gist of my answer is that Denote does not need to provide such a
+facility because notes are ordinary files: whatever the user already has
+for them should apply to Denote. If the user has nothing to search
+through files, they anyhow need something that works outside the
+confines of Denote: a =denote-SEARCH= command is not an adequate
+solution.
+
+Emacs has numerous built-in commands, such as ~grep~ (~lgrep~ and
+~rgrep~), ~project-find-regexp~, ~find-grep-dired~, ~ibuffer-do-occur~,
+... Furthermore, there are lots of high quality packages that have
+their own wrappers or extensions for searching file contents, such as
+the =ivy= and =helm= completion frameworks, as well as =consult= (the
+commands ~consult-grep~ and ~consult-ripgrep~), =consult-notes=, =rg=,
+=deadgrep=, =deft=, and probably plenty more that do not come to mind
+right now.
+
+I strongly encourage the user to find a universal search solution to the
+problem of searching file contents.
+
+* Version 0.4.0 on 2022-07-25
+:PROPERTIES:
+:CUSTOM_ID: h:1c8098ee-089c-4511-bc6a-4140aab01321
+:END:
+
++ Defined the ~denote-link-dired-marked-notes~ command. It lets the
+ user produce a typographic list of links to the note files that are
+ marked in Dired. The list is written at point. If there are multiple
+ buffers which visit Denote notes, the command first prompts with
+ minibuffer completion for one among them.
+
+ In terms of workflow, ~denote-link-dired-marked-notes~ complements the
+ ~denote-link-add-links~ command for those cases where it is easier to
+ select files than write an elegant regular expression.
+
++ Implemented the ~denote-dired-rename-marked-files~ command. This
+ provides a much-requested facility to perform the familiar renaming
+ operation on a set of files. In particular:
+
+ - the file's existing file name is retained and becomes the =TITLE=
+ field, per Denote's file-naming scheme;
+
+ - the =TITLE= is sluggified and downcased, per our conventions;
+
+ - an identifier is prepended to the =TITLE=;
+
+ - the file's extension is retained;
+
+ - a prompt is asked once for the =KEYWORDS= field and the input is
+ applied to all file names;
+
+ - if the file is recognised as a Denote note, the command rewrites its
+ front matter to include the new keywords. A confirmation to carry
+ out this step is performed once at the outset. Note that the
+ affected buffers are not saved. The user can thus check them to
+ confirm that the new front matter does not cause any problems
+ (e.g. with the command ~diff-buffer-with-file~). Multiple buffers
+ can be saved with ~save-some-buffers~ (read its doc string).
+
+ Parts of ~denote-dired-rename-marked-files~ were added or refined over
+ a series of commits. Consult the Git log for the minutia. Thanks to
+ Jean-Philippe Gagné Guay for the relevant additions in pull requests
+ 51 and 52 on the GitHub mirror:
+
+ - <https://github.com/protesilaos/denote/pull/51>
+ - <https://github.com/protesilaos/denote/pull/52>
+
+ Jean-Philippe has assigned copyright to the Free Software Foundation.
+
++ Improved how the ~denote-dired-rename-file~ command rewrites front
+ matter. Before, it would perform a replacement of the whole block,
+ which had the adverse effect of overwriting custom front matter
+ entries. Now, it only targets the lines which hold the title and
+ keywords, leaving everything else intact. Thanks to Peter Prevos for
+ reporting the problem and testing the solution to it in issue 60 on
+ the GitHub mirror: <https://github.com/protesilaos/denote/issues/60>.
+
++ Introduced the ~denote-dired-rename-file-and-add-front-matter~ command
+ that always prepends front matter to a file whose extension is among
+ the supported ones (per the user option ~denote-file-type~). This
+ differs from the standard ~denote-dired-rename-file~ command which
+ only rewrites the front matter's title and keywords if they exist.
+
+ In practice, ~denote-dired-rename-file-and-add-front-matter~ empowers
+ the user to convert a generic text file to a Denote note.
+
+ This command was originally added by Jean-Philippe Gagné Guay in pull
+ request 49 on the GitHub mirror and refined in subsequent commits:
+ <https://github.com/protesilaos/denote/pull/49>. Also read issue 48
+ where this idea was originally discussed:
+ <https://github.com/protesilaos/denote/issues/48>.
+
++ Added the ~denote-dired-rename-marked-files-and-add-front-matters~
+ command, which is like the ~denote-dired-rename-marked-files~ but adds
+ front matter instead of rewriting existing one, just how the command
+ ~denote-dired-rename-file-and-add-front-matter~ does it (both are
+ mentioned above). Thanks to Jean-Philippe Gagné Guay for the
+ refinements to it in pull request 53 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/53>.
+
++ Wrote an interactive spec for ~denote-link-buttonize-buffer~. It can
+ now be invoked with =M-x= or a key binding, should the need arise.
+ This function is normally called via a hook and takes effect in plain
+ text as well as Markdown files.
+
++ Extended the fontification rules so that file names with non-ASCII
+ characters are styled properly. This issue was brought up on the
+ mailing list by Frank Ehmsen and was discussed with the participation
+ of Peter Prevos:
+ <https://lists.sr.ht/~protesilaos/denote/%3C2273b3b1-344c-6c6e-3ab6-a227b6bc3721%40eh-is.de%3E>.
+
+ The same topic was raised at the same time on the GitHub mirror by
+ user hpgisler in issue 61:
+ <https://github.com/protesilaos/denote/issues/61>.
+
+ After some discussion, we agreed on the right approach, which was
+ formalised by Peter Prevos as pull request 64 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/pull/64>. The change is below
+ the ~15 line threshold and thus does not require copyright assignment
+ to the Free Software Foundation.
+
++ Made the registration of the =denote:= custom Org hyperlink type
+ conditional on the availability of the ~org~ feature. In other words,
+ those who do not use Org will not be loading this part of the code.
+ Thanks to Abin Simon for reporting the problem and for showing how
+ Elfeed handles this case. This was done in issue 47 on the GitHub
+ mirror: <https://github.com/protesilaos/denote/issues/47>.
+
++ Ensured that duplicate keywords are not produced by the relevant
+ prompt. Thanks to user Taoufik for the contribution in pull request
+ 50 on the GitHub mirror: <https://github.com/protesilaos/denote/pull/50>.
+ The change is below the ~15 line threshold and thus does not require
+ copyright assignment to the Free Software Foundation.
+
++ Fixed a typo in the reference to the ~crm-separator~ in the manual.
+ David Wilson (System Crafters channel) spotted the error in a recent
+ live stream whose main topic was about Denote (thanks, by the way!):
+ <https://www.youtube.com/watch?v=QcRY_rsX0yY>.
+
++ Addressed an inconsistency in the command ~denote-link-find-file~
+ where it would not recognise links without a title in their format
+ (those can be inserted by passing a prefix argument (=C-u= by default)
+ to the commands that insert links, such as ~denote-link~).
+
++ Attached conditionality to the ~denote~ command's =SUBDIRECTORY=
+ argument, so that it does not create new file paths. This is only
+ relevant for those who call ~denote~ from Lisp. Interactive use is
+ the same as before.
+
++ Clarified that the user option ~denote-org-capture-specifiers~ can
+ accept arbitrary text in addition to the formatting specifiers that
+ Org's capture mechanism introduces.
+
++ Explained in the manual why ~denote-org-capture-specifiers~ is needed
+ instead of writing the capture template directly the way one normally
+ does. The gist is that because our file names are derived dynamically
+ based on user input, we need to account for the sequence in which the
+ value of arguments is reified by ~org-capture~.
+
++ Refactored how notes are prepared internally. Thanks to Jean-Philippe
+ Gagné Guay for the contribution in pull request 55 on the GitHub
+ mirror: <https://github.com/protesilaos/denote/pull/55>.
+
++ Declared the ~denote-punctuation-excluded-extra-regexp~ variable which
+ is, for the time being, targeted at experienced users. Its purpose is
+ to extend what we consider "illegal" punctuation for the file name.
+ Thanks to pRot0ta1p for the feedback in issue 57 over at the GitHub
+ mirror: <https://github.com/protesilaos/denote/issues/57>. Example
+ based on the input of pRot0ta1p:
+
+ #+begin_src emacs-lisp
+ (setq denote-punctuation-excluded-extra-regexp
+ "[『』〖〗{}「」【】〔〕[]()《》〈〉«»!#¥%…&"'*,。;:、?—]*")
+ #+end_src
+
+ The ideal is to make ~denote--punctuation-regexp~ work for all
+ scripts, but that may be unrealistic.
+
++ Clarified what the manual means by "attachments" to notes. Those are
+ for Org, if the user resorts to the relevant Org mechanisms. Denote
+ does not do any of that.
+
++ Revised the parsing of a date input as used in the ~denote-date~
+ command or related. The idea is to turn =2020-01-15= into something
+ like =2020-01-15 16:19= by using the current time, so that the hour
+ and minute component is not left to =00:00= when the user does not
+ specify it explicitly.
+
+ This reduces the burden on the user who would otherwise need to input
+ the time value in order to avoid the error of duplicate identifiers in
+ the scenario where the same date is used more than once.
+
+ The change also addresses a difference between Emacs 28 and Emacs 29
+ where the former does not read dates without a time component.
+
+ Thanks to Peter Prevos for the feedback in issue 58 over at the GitHub
+ mirror: <https://github.com/protesilaos/denote/issues/58>.
+
++ Fixed compilation warnings in Emacs 29 about the format of doc strings
+ that need to output a literal single quote. Thanks to Kyle Meyer for
+ the patch, which was sent on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/patches/34117>.
+
++ Fixed typo in the user option ~denote-prompts~ about the
+ ~crm-separator~. Thanks to Kyle Meyer for the patch, which was sent
+ on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/patches/34116>.
+
++ Made the built-in =subr-x= library a runtime dependency, due to
+ complications with the ~when-let*~ form. The problem was made
+ manifest in a renaming operation, though it was not about renaming per
+ se. Thanks to hpgisler for reporting the problem in issue 62 and for
+ testing the proposed solution:
+ <https://github.com/protesilaos/denote/issues/62>.
+
++ Streamlined the use of the =seq= library instead of =cl-lib=, as we
+ were already using the former more heavily and there was no need for
+ the latter. Thanks to Philip Kaludercic for pointing this out on the
+ emacs-devel mailing list:
+ <https://lists.gnu.org/archive/html/emacs-devel/2022-07/msg00838.html>.
+
++ Added a generic =README.md= file to placate the Git forges. Neither
+ SourceHut nor GitHub/GitLab are fully compliant with the Org markup we
+ use in =README.org= (we use Org because it is easy to generate the
+ Info manual and HTML pages out of it). SourceHut will not render the
+ file at all, while the others render it but do not parse it properly.
+
++ Made several other internal tweaks and refinements in the interest of
+ robustness and/or clarity.
+
++ Rewrote all relevant documentation.
+
+** Non-changes
+:PROPERTIES:
+:CUSTOM_ID: h:0ac79968-a575-4380-addc-d58cc2b5f627
+:END:
+
+The following are not part of any changes that were made during this
+release cycle, though they provide potentially interesting insight into
+the workings of the project.
+
++ Identifiers with milliseconds :: Denote's identifier format extends up
+ to seconds. This is the product of years of experimentation and is,
+ in my opinion, the best compromise between usability/readability and
+ precision. If a user produces two notes within a fraction of a
+ second, then yes they will have duplicate identifiers. In principle,
+ there is no reason not to address this potential problem, provided we
+ do not compromise on Denote's file-naming scheme (making the
+ identifier less readable is a compromise). We shall see what the best
+ course of action is. Thanks to Felipe Balbi and Jean-Philippe Gagné
+ Guay for the discussion thus far in issue 54 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/issues/54>.
+
++ Denote and evil-mode :: Users of evil-mode do not have to worry about
+ Denote, as we do not define any key bindings. The manual includes
+ sample configuration, which proposes some key bindings, but that is
+ the user's prerogative. Thanks to Saša Janiška and Alan Schmitt for
+ their participation on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C87czdxf1dz.fsf%40atmarama.ddns.net%3E>.
+
++ Denote and Citar :: Peter Prevos started developing a package that
+ connects Denote with Citar: <https://github.com/pprevos/denote-citar>.
+ The idea is to use notes as part of one's bibliography. Discussions
+ which include sample code on how to leverage ~denote~ from Lisp:
+
+ - <https://lists.sr.ht/~protesilaos/denote/%3C6add8bc63cab0a557fa4b9919e025afc%40prevos.net%3E>
+ - <https://lists.sr.ht/~protesilaos/denote/%3C87r12d2w96.fsf%40protesilaos.com%3E>
+ - <https://lists.sr.ht/~protesilaos/denote/%3C87a69060q6.fsf%40protesilaos.com%3E>
+
++ Denote and graph of connections :: Saša Janiška asked whether Denote
+ will provide some way to visualise links between notes. The answer is
+ negative. Denote's scope is clearly delineated and its feature set is
+ largely complete (notwithstanding refinements to what we already
+ provide). Peter Prevos is experimenting with some code that uses the
+ R language. Any such facility will have to be implemented as a
+ separate package. I remain at the disposal of anyone who needs help
+ with Denote's internals. Thanks to the aforementioned fellows for
+ their participation on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C878roleze1.fsf%40atmarama.ddns.net%3E>.
+
++ Denote's scalability :: There was a discussion whether Denote will
+ work well with very large sets of files. The short answer is that it
+ will work the same way Emacs and/or standard Unix tools do: good
+ enough! If there are improvements to be made, which do not jeopardise
+ the principles of the project, we shall implement them without
+ hesitation. Thanks to Saša Janiška and Peter Prevos for their
+ participation on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C87sfmtf7im.fsf%40atmarama.ddns.net%3E>.
+
++ Denote's minimum requirement of Emacs 27.2 :: We cannot depend on
+ Emacs 27.1 due to this message from the byte compiler:
+
+ : You should depend on (emacs "27.2") or the (org "9.3") package if you need `org-link-open-as-file'.
+
+ Depending on Org is not an option because Denote optionally works
+ without Org, so Emacs 27.2 is what we have to opt for. If your
+ operating system does not provide this version in package format,
+ please petition its maintainers/providers to do so. Thanks to
+ Alexander for asking about it on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C9ec818e6a7979efbb2f8b1f5a497665b%40purelymail.com%3E>.
+
+Finally, a mildly interesting piece of trivia: we have exceeded 600
+commits since the first day of the project's Git history on 2022-06-04
+(the actual history is much longer). That averages to more than 10 per
+day! I think things will slow down eventually.
+
+* Version 0.3.0 on 2022-07-11
+:PROPERTIES:
+:CUSTOM_ID: h:6864cfd4-d0be-4c89-b313-39ba6e892a03
+:END:
+
++ Fixed how references are analysed to produce the backlinks' buffer.
+ This should resolve the issue that some users faced where the
+ backlinks would not be produced.
+
+ The previous implementation would not yield the appropriate results if
+ (i) the value of the user option ~denote-directory~ was a "project"
+ per the built-in project.el and (ii) the link to the given entry was
+ from a subdirectory. In short, the references were sometimes returned
+ as relative file paths, whereas they should always be absolute.
+ Thanks to Jean-Philippe Gagné Guay for the feedback in issue 42 over
+ at the GitHub mirror: <https://github.com/protesilaos/denote/pull/42>.
+
+ [ Jean-Philippe has assigned copyright to the Free Software
+ Foundation. It is a prerequisite for contributing to core Emacs
+ and/or any package distributed via the official GNU ELPA. ]
+
++ Addressed a regression in the function ~denote-directory~ (this is the
+ function that normalises the variable of the same name) which
+ prevented it from returning an expanded file path. This too
+ contributed to problems with the backlinking facility. Thanks to
+ Jean-Philippe Gagné Guay for the contribution in pull request 44 over
+ at the GitHub mirror: <https://github.com/protesilaos/denote/pull/44>.
+
+ Also thanks to user pRot0ta1p for the relevant feedback in issue 43
+ (also on the mirror): <https://github.com/protesilaos/denote/issues/43>.
+ More thanks to Alfredo Borrás, Benjamin Kästner, and Sven Seebeck for
+ their comments in a related thread on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3CCA73E705-1194-4324-9962-70708C4C72E5%40zoho.eu%3E>.
+ These discussions showed we had a problem, which we managed to
+ identify.
+
++ Introduced the user option ~denote-prompts~ (read its doc string or
+ the relevant entry in the manual). It governs how the standard
+ ~denote~ command for creating new notes will behave in interactive
+ usage. By default, ~denote~ prompts for a title and keywords. With
+ ~denote-prompts~, the command can also ask for a file type (per
+ ~denote-file-type~), subdirectory of the ~denote-directory~, and a
+ specific date+time. Prompts occur in the order they are specified.
+ Furthermore, the ~denote-prompts~ can be set to values which do not
+ include the title and keywords. This means that the resulting file
+ names can be any of those permutations:
+
+ : DATE.EXT
+ : DATE--TITLE.EXT
+ : DATE__KEYWORDS.EXT
+
+ Recall that Denote's standard file-naming scheme is defined as follows
+ (read the manual for the details):
+
+ : DATE--TITLE__KEYWORDS.EXT
+
+ For our purposes, Denote will work perfectly fine for linking and
+ backlinking, even if file names do not include the =TITLE= and
+ =KEYWORDS= fields. However, the user is advised to consider the
+ implications on usability: notes without a descriptive title and/or
+ useful keywords may be hard to filter and practically impossible to
+ manage at scale. File names without such information should at least
+ be added to subdirectories which themselves have a descriptive name.
+
+ At any rate, Denote does not have strong opinions about one's
+ workflow. The standard file name is the culmination of years of
+ experience.
+
+ Consider the ~denote-prompts~ the affirmative answer to the question
+ "Can keywords be optional?" as posed by Jack Baty on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C8D392BC3-980A-4E5B-9480-D6A00BE8279F%40baty.net%3E>.
+
+ Thanks to Jean-Philippe Gagné Guay for the original contribution in
+ commit 9b981a2. It was originally part of a pull request, but due to
+ some internal changes I had to merge it as a patch and technically the
+ web UI did not count the PR as "merged" (though it was in terms of
+ substance).
+
++ Refactored the ~denote~ command to (i) accommodate the new user option
+ ~denote-prompts~ via its interactive specification and (ii) be more
+ flexible when called from Lisp. The latter scenario is for advanced
+ users or, generally, those who can maintain some custom code in their
+ configuration. A case in point is one of the examples we show in the
+ manual for a programmatic way to create notes that automatically get
+ the =journal= tag:
+
+ #+begin_src emacs-lisp
+ (defun my-denote-journal ()
+ "Create an entry tagged 'journal', while prompting for a title."
+ (interactive)
+ (denote
+ (denote--title-prompt)
+ '("journal")))
+ #+end_src
+
+ Notice that the ='("journal")= is a list of strings even for a single
+ keyword. Whereas before a single one was a plain string. This is a
+ breaking change.
+
+ Please consult the doc string of the ~denote~ command for the
+ technicalities.
+
++ Refashioned the interactive convenience functions of ~denote-type~,
+ ~denote-date~, ~denote-subdirectory~ to leverage the ~denote-prompts~
+ user option while calling ~denote~ interactively. In practical terms,
+ they no longer accept any arguments when called from Lisp. Users who
+ need a programmatic approach are advised to either call ~denote~
+ directly, or check how these commands ~let~ bind the ~denote-prompts~
+ to carry out their operations. The doc string of each command
+ explains how it works. Or evaluate this to check the manual:
+
+ #+begin_src emacs-lisp
+ (info "(denote) Convenience commands for note creation")
+ #+end_src
+
+ Else visit:
+ <https://protesilaos.com/emacs/denote#h:887bdced-9686-4e80-906f-789e407f2e8f>
+
++ Documented how the user option ~denote-directory~ can accept a local
+ value. This is pertinent to scenaria where the user needs to maintain
+ separate directories of notes. By "separate" we mean sets of notes
+ that do not communicate with each other, cannot create links between
+ them, etc. The manual delves into the technicalities. If you have
+ the Info entry installed, evaluate:
+
+ #+begin_src emacs-lisp
+ (info "(denote) Maintain separate directories for notes")
+ #+end_src
+
+ Else visit:
+ <https://protesilaos.com/emacs/denote#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5>.
+
+ Thanks to user "Summer Emacs" for starting the discussion on the
+ mailing list, and Benjamin Kästner for their participation:
+ <https://lists.sr.ht/~protesilaos/denote/%3Cm25yk5e856.fsf@gmail.com%3E>.
+
++ Added an entry to the manual's Frequently Asked Questions about a
+ failed search for backlinks. It includes sample code that users of
+ Windows can apply, if necessary. (The error is not Denote's fault.)
+ Thanks to Benjamin Kästner for the patch, which is below the ~15 line
+ threshold and thus does not require copyright assignment to the Free
+ Software Foundation:
+ <https://lists.sr.ht/~protesilaos/denote/%3Cce117b14-55cf-622e-6cd8-0af698091ae3%40gmail.com%3E>.
+
++ Removed duplicate entries from the list of file paths that the =xref=
+ library returns for the purposes of backlinking. Thanks to
+ Jean-Philippe Gagné Guay for the contribution in pull request 44 on
+ the GitHub mirror: <https://github.com/protesilaos/denote/issues/44>.
+
++ Applied an appropriate face to the backlinks' button to mitigate an
+ error. Thanks to Jean-Philippe Gagné Guay for the contribution in
+ pull request 45 on the GitHub mirror and for later testing a
+ subsequent tweak: <https://github.com/protesilaos/denote/issues/45>.
+
++ Simplfied all the faces we define to make them work with all themes.
+ The previous colours were consistent with the =modus-themes=:
+ <https://protesilaos.com/emacs/modus-themes>.
+
++ Refined how strings are sluggified under all circumstances. Before, a
+ nil value for the user option ~denote-allow-multi-word-keywords~ would
+ have the adverse effect of joining all the strings in the title field
+ of the file name. The intent always was to do that only for
+ multi-word keywords, not the title. This change was part of a hotfix,
+ formalised as version =0.2.1= a day after the release of =0.2.0=.
+
++ Made the fontification rules more robust, while avoiding any false
+ positives. This was done over a series of commits as it had
+ implications for the file name permutations that were mentioned
+ earlier. Thanks to Jean-Philippe Gagné Guay for the patches and/or
+ discussion about the merits of each change and concomitant
+ considerations:
+
+ - https://github.com/protesilaos/denote/pull/36
+ - https://github.com/protesilaos/denote/pull/38
+ - https://github.com/protesilaos/denote/pull/40
+ - https://github.com/protesilaos/denote/pull/42
+
++ Rewrote all relevant entries in the manual to reflect all the
+ user-facing aspects of the aforementioned.
+
++ Discussed a use-case of rewriting old journal entries as Denote-style
+ files. As of this writing, we do not support migration of files in
+ bulk. It might happen at some point, though it is no mean task.
+ Thanks to Summer Emacs and Alan Schmitt for their participation:
+ <https://lists.sr.ht/~protesilaos/denote/%3Cm27d4mbktj.fsf%40gmail.com%3E>.
+
+ An aside here as this topic was brought up: my packages are open to
+ users of all skill levels and is why I maintain a mailing list as well
+ as mirrors of the official git repository on SourceHut. Do not
+ hesitate to ask a question. If, for whatever reason, those
+ communication channels are not appropriate, you are welcome to contact
+ me in private: <https://protesilaos.com/contact>.
+
+Thanks again to Jean-Philippe Gagné Guay for the numerous contributions.
+Please read the commit log for the minutia, as this change log entry
+omitted some of the finer yet important details.
+
+* Version 0.2.0 on 2022-07-04
+:PROPERTIES:
+:CUSTOM_ID: h:2002fee6-3f0c-48be-9727-6d4e20f34856
+:END:
+
++ Version =0.1.0= (from 2022-06-27) was never built as a package. The
+ reason is that the GNU ELPA machinery reads the =Version:= header of
+ the main file, not the git tag. As the original commit in =denote.el=
+ included =Version: 0.1.0=, GNU ELPA rightly tries to build the package
+ using that reference. But because at that time I had not yet updated
+ the Copyright header to name the Free Software Foundation, the package
+ could not be prepared. As such, please consider this release to be
+ the "first formal stable version". My apologies for the delay,
+ contrary to what was promised in the last change log entry.
+
+ - Prospective users are advised to read the manual:
+ <https://protesilaos.com/emacs/denote>. For a video demonstration:
+ <https://protesilaos.com/codelog/2022-06-18-denote-demo/>.
+
+ - Thanks to Benjamin Kästner for reporting the issue with the GNU ELPA
+ package on the mailing list:
+ <https://lists.sr.ht/~protesilaos/denote/%3C9d600ff0-4fed-2ad7-5dbc-5a194639a045@gmail.com%3E>.
+
++ Originally, Denote was designed to only work with notes in a flat
+ directory. With code contributions from Jean-Philippe Gagné Guay,
+ support for subdirectories of the user option ~denote-directory~ is
+ now available. This covers the case of creating links between notes,
+ following them, and viewing the backlinks' buffer of the current
+ entry.
+
+ - Thanks to Jean-Philippe for the contributions which took place on
+ the GitHub mirror:
+
+ + <https://github.com/protesilaos/denote/pull/24>
+ + <https://github.com/protesilaos/denote/pull/25>
+ + <https://github.com/protesilaos/denote/pull/26>
+
+ - Jean-Philippe Gagné Guay has assigned copyright to the Free Software
+ Foundation. This is a prerequisite to contribute code to any
+ package on the official GNU ELPA archive (and to emacs.git for that
+ matter).
+
++ The new ~denote-subdirectory~ command lets the user select a directory
+ to place the new note in. Available candidates are the value of the
+ ~denote-directory~ as well as all of its subdirectories, minus =.git=.
+ In future versions, we will consider how to provide a blocklist or a
+ regexp filter for the user to specify which subdirectories should be
+ omitted from minibuffer completion. Please consider providing your
+ feedback on the technicalities.
+
+ - Thanks to Jean-Philippe Gagné Guay and Shreyas Ragavan for the
+ feedback in issue 31 on the GitHub mirror:
+ <https://github.com/protesilaos/denote/issues/31>.
+
+ - Thanks to Jean-Philippe Gagné Guay for fixing a potential problem in
+ how directories are represented when commands enter the directory
+ instead of selecting it (again, at the GitHub mirror):
+ <https://github.com/protesilaos/denote/pull/35>.
+
++ From 2022-06-24 to 2022-07-03, Denote provided support for links
+ between Org notes that leveraged the =id:= hyperlink type.
+ Discussions on the mailing list and the GitHub mirror revealed the
+ longer-term problems in our implementation. In the Annex below, I
+ provide my detailed opinion on the matter. The gist is that Denote
+ does not---and will not---create =id:= links between its notes, but
+ shall use the =denote:= hyperlink type instead (which works like the
+ standard =file:= type). As the Annex explains, Denote is not org-roam
+ lite and we try not to engender such false expectations.
+
+ - Despite the fact that the relevant patches are no longer applicable,
+ I wish to thank Kaushal Modi and Jean-Philippe Gagné Guay for their
+ contributions over at the GitHub mirror:
+
+ + <https://github.com/protesilaos/denote/pull/20>
+ + <https://github.com/protesilaos/denote/pull/28>
+
++ The user option ~denote-date-format~ controls how the date and time is
+ recorded in the file's contents (what we call "front matter"). When
+ nil (the default value), we use a file-type-specific format (also
+ check the user option ~denote-file-type~):
+
+ - For Org, an inactive timestamp is used, such as =[2022-06-30 Wed 15:31]=.
+
+ - For Markdown, the RFC3339 standard is applied: =2022-06-30T15:48:00+03:00=.
+
+ - For plain text, the format is that of ISO 8601: =2022-06-30=.
+
+ If the value is a string, ignore the above and use it instead. The
+ string must include format specifiers for the date. These are described
+ in the doc string of ~format-time-string~.
+
+ The ~denote-date-format~ supersedes the now obsolete
+ ~denote-front-matter-date-format~.
+
+ Thanks to Peter Prevos and Kaushal Modi for their feedback in issue 27
+ on the GitHub mirror: <https://github.com/protesilaos/denote/issues/27>.
+
++ All the faces we define are now declared in the =denote-faces.el=
+ file. The fontification rules are shared by ~denote-dired-mode~ and
+ the backlinks' buffer (invoked by ~denote-link-backlinks~ and
+ controlled by the user option ~denote-link-fontify-backlinks~). The
+ current list of faces:
+
+ - ~denote-faces-date~
+ - ~denote-faces-delimiter~
+ - ~denote-faces-extension~
+ - ~denote-faces-keywords~
+ - ~denote-faces-subdirectory~
+ - ~denote-faces-time~
+ - ~denote-faces-title~
+
++ Named the mailing list address as the =Maintainer:= of Denote.
+ Together with the other package headers, they help the user find our
+ primary sources and/or communication channels. This change conforms
+ with work being done upstream in package.el by Philip Kaludercic. I
+ was informed about it here:
+ <https://lists.sr.ht/~protesilaos/general-issues/%3C875ykl84yi.fsf%40posteo.net%3E>.
+
++ Fixed how keywords are inferred and combined. The previous code did not
+ work properly when the user option =denote-infer-keywords= was nil.
+ It would return a list of symbols, with the parentheses, whereas the
+ file name needs a string where each keyword is delimited by an
+ underscore.
+
++ Simplified how information in the front matter is retrieved. It fixes
+ cases where, for example, a special character at the end of the title
+ was ignored. Thanks to Jean-Philippe Gagné Guay for the patch over at
+ the GitHub mirror: <https://github.com/protesilaos/denote/pull/21>.
+
++ Rewrote parts of the manual in the interest of clarity.
+
+** Annex about discontinuing support for org-id
+:PROPERTIES:
+:CUSTOM_ID: h:647d6155-1ac3-4ecb-bd4c-06d09fecd3ba
+:END:
+
+My thanks for their participation in the discussions go to Jean-Philippe
+Gagné Guay, Kaushal Modi, and Shreyas Ragavan.
+
+#+begin_example
+commit f35ef05cb451f265213c3aafc1e62c425b1ff043
+Author: Protesilaos Stavrou <info@protesilaos.com>
+Date: Sun Jul 3 17:34:38 2022 +0300
+
+ REMOVE support for 'id:' hyperlink types
+
+ The original idea was to support the 'org-id' library on the premise
+ that it makes Denote a good Emacs citizen. However, discussions on the
+ mailing list[0] and the GitHub mirror[1] have made it clear to me that
+ 'org-id' is not consistent with Denote's emphasis on simplicity.
+
+ To support the way 'org-id' works, we will eventually have to develop
+ some caching mechanism, just how the org-roam package does it. This is
+ because the variable 'org-id-extra-files' needs to be kept up-to-date
+ whenever an operation on a file is performed. At scale, this sort of
+ monitoring requires specialised software. Such a mechanism is outside
+ the scope of Denote---if you need a db, use org-roam which is already
+ great.
+
+ [0] <https://lists.sr.ht/~protesilaos/denote/%3C8735fk4y1w.fsf%40hallac.net%3E#%3C877d4un73c.fsf@protesilaos.com%3E>
+
+ [1] <https://github.com/protesilaos/denote/issues/29>
+
+ Quote of what I wrote on the GitHub mirror issue 29:
+
+ [ggjp] This is what I was implying. That we are, in fact,
+ providing an option that is not viable long-term, but keeping
+ the option for expert users who will be able to handle this.
+ And we should warn about this clearly in the doc of that option.
+
+ [protesilaos] What you write here @ggjp and what @shrysr explained
+ tells me that those expert users will need to be real experts. To
+ put it concretely, I am an experienced Emacs user with no
+ programming background, who has written several Emacs
+ packages (including the modus-themes which are built into Emacs),
+ but I have zero knowledge of using a db or of handling things with
+ python and the like. So if I opt in to 'denote-link-use-org-id' I
+ will eventually run into problems that my non-existent skills will
+ prevent me from solving. At that point, I will just use org-roam
+ which already handles this use-case in a competent way (and has a
+ massive community to rely on in case I need further support).
+
+ If each package needs to write its own optimisations and maintain
+ its own cache, to me this shows that 'org-id' is not good enough for
+ the time being: more work needs to be done in org.git to provide a
+ universal solution.
+
+ I wanted to support 'org-id' by default on the premise that Denote
+ must be a good Emacs citizen which interoperates with the rest of
+ the wider ecosystem. But if 'org-id' leaves something to be
+ desired, then that goal is not worth pursuing: we add complexity to
+ our code, offer an option that we cannot genuinely/adequately
+ support, and make usage of it contingent on reading the docs and
+ having a high level of expertise.
+
+ I think being a good Emacs citizen is a laudable principle. In this
+ case, the right thing to do is to recommend the use of org-roam
+ instead of trying to accommodate 'org-id'. As such, I have now
+ changed my mind and think we should remove what we previously added.
+
+ For some context here: the reason I never used org-roam is
+ because (i) it is Org-specific whereas I write notes in different
+ file types and (ii) I did not want to ever rely on a db or
+ equivalent dependency.
+
+ <https://github.com/protesilaos/denote/issues/29#issuecomment-1173036924>
+
+ README.org | 226 ++++++++---------------------------------------------
+ denote-link.el | 99 ++++++-----------------
+ denote-retrieve.el | 2 +-
+ denote.el | 14 +---
+ 4 files changed, 63 insertions(+), 278 deletions(-)
+#+end_example
+
+Followed up by my explanation:
+
+#+begin_src text
+> can we not have denote style links to be default for (de)notes - and
+> explicitly supported, while if they need to, users can still link
+> denote org files via org-id to any other notes/files (and vice versa)
+> -- in which case performance + testing for org-id driven linking is
+> not within Denote's purview at all?
+
+The formal support for `id:` links was added shortly before the release
+of version `0.1.0`. In the days prior, we supported what you describe
+via the manual. The user could change the `denote-org-front-matter`
+variable to include a `PROPERTIES` drawer. This possibility still
+exists, though yesterday I removed the relevant entry from the manual.
+This way only the real do-it-yourself experts will go down that path.
+
+My concern here is with managing expectations. If our Org notes are
+superficially the same as org-roam's, an unsuspecting user may think
+that Denote is an org-roam lite. We will thus get issues/requests, such
+as those already mentioned in this GitHub repo, about migrating from
+org-roam to Denote. While there are similarities, Denote is not a
+minimalist org-roam and I would not like to encourage the idea of
+treating the two as interchangeable.
+
+Doing things half-way-through is a way to create false expectations. A
+package on GNU ELPA must be usable by users of all skill levels. If the
+functionality we provide is incomplete and needs to be covered by
+user-level tweaks, we are excluding a portion of the user base while
+still assuming the maintenance burden. If someone trusts Denote to,
+say, write a 1000 notes, we do not want to surprise them after the fact.
+Imagine if the reported issues that triggered this change happened 6
+months into one's daily usage of Denote: it wouldn't be nice.
+
+Setting the right expectations is a matter of responsibility: we let the
+user make a more informed choice and show respect for their time. It
+also makes it easier for me to keep Denote's scope in check by not
+supporting every little extra that Org implements. The premier Org
+extension is org-roam: we do not need another one (or, if we do, I am
+not the one to implement it).
+
+,* * *
+
+Some comments on the `denote:` hyperlink type for Org as they may be
+relevant in this context:
+
+,* It is meant to work like the standard `file:` type. This means that
+ it links to a file, while it can also have additional search
+ parameters, as explained in the Org manual. Evaluate:
+
+ (info "(org) Search Options")
+
+,* It does not read the front matter, but only the file name. You can
+ create a note as usual, delete all its contents, save it, and try to
+ link to it from another note. It works.
+
+,* Exporting now works like the `file:` type for HTML, LaTeX, Texinfo,
+ and Markdown. Technically, it also supports the ASCII backend but the
+ format of the output could be tweaked further.
+
+There may be refinements to be made, which is okay as that is part of a
+maintainer's duties.
+#+end_src
+
+* Version 0.1.0 on 2022-06-27
+:PROPERTIES:
+:CUSTOM_ID: h:33939747-ad60-4913-a170-4b2f48f139cc
+:END:
+
+The present entry is intended for early adopters of Denote who may have
+not caught up with the latest developments. Prospective users are
+advised to read the manual: <https://protesilaos.com/emacs/denote>. For
+a video demonstration: <https://protesilaos.com/codelog/2022-06-18-denote-demo/>.
+
++ The =denote= package on GNU ELPA will be available a few hours after
+ this release. GNU ELPA provides the latest stable release. To use a
+ development snapshot, read:
+ <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>.
+
++ Remember that any significant contribution (above ~15 lines) requires
+ copyright assignment to the Free Software Foundation. A form with
+ instructions is included in the manual's "Contributing" section:
+ <https://protesilaos.com/emacs/denote#h:1ebe4865-c001-4747-a6f2-0fe45aad71cd>.
+
++ The front matter of notes in Org has changed to be compliant with the
+ standard =org-id= infrastructure. A =PROPERTIES= drawer is added to
+ the top of the file, which includes an =ID= property with the value of
+ the Denote identifier. Sample:
+
+ #+begin_src org
+ :PROPERTIES:
+ :ID: 20220610T202537
+ :END:
+ ,#+title: Sample Org front matter
+ ,#+date: 2022-06-10
+ ,#+filetags: denote testing
+ #+end_src
+
++ The front matter of Markdown (YAML or TOML) and plain text files
+ remains constant. For completeness, this is how they look:
+
+ #+begin_src md
+ ---
+ title: "Sample with Markdown and YAML"
+ date: 2022-06-10
+ tags: denote testing
+ identifier: "20220610T202021"
+ ---
+ #+end_src
+
+ #+begin_src md
+ +++
+ title = "Sample with Markdown and TOML"
+ date = 2022-06-10
+ tags = ["denote", "testing"]
+ identifier = "20220610T201510"
+ +++
+ #+end_src
+
+ #+begin_example
+ title: Sample plain text
+ date: 2022-06-10
+ tags: denote testing
+ identifier: 20220610T202232
+ ---------------------------
+ #+end_example
+
++ The integration with =org-id= extends to how linking works. By
+ default, Denote uses its own custom hyperlink type which starts with
+ the =denote:= prefix. In Org, it works like the =file:= type. When
+ the user option ~denote-link-use-org-id~ is non-nil, links from Org
+ notes to other Org notes will use the standard =id:= type instead. As
+ this is an Org-specific feature, Denote takes care to use the
+ major-mode-agnostic =denote:= type when the link targets a non-Org
+ note.
+
++ In Org files the links created by Denote are buttonized automatically.
+ For Markdown and plain text, we use our own methods. When a link is
+ inserted it is buttonized outright. To buttonize links in existing
+ notes while visiting them in a buffer, add/evaluate this (it excludes
+ Org on its own):
+
+ #+begin_src emacs-lisp
+ (add-hook 'find-file-hook #'denote-link-buttonize-buffer)
+ #+end_src
+
++ The generation of the backlinks' buffer now uses the built-in =xref=
+ library instead of relying on a hardcoded call to the =find=
+ executable. This means that the ~denote-link-backlinks~ command will,
+ in principle, work properly with all Emacs builds.
+
++ Users of Emacs 28 or higher can configure ~xref-search-program~ to
+ change from the default =grep= to =ripgrep=, =ugrep=, or a
+ user-defined alternative.
+
++ This is the first stable release of Denote. It covers close to 400
+ commits starting from 2022-06-04. Denote is the successor to a toy
+ package of mine, USLS, whose first public version was made available
+ in early November 2020: <https://gitlab.com/protesilaos/usls>.
+
++ Thanks to everyone involved in the development of Denote. Code
+ contributions, bug reports, discussion of ideas, are all valuable.
+ From A-Z the names mentioned in the manual's "Acknowledgements"
+ section: Colin McLear, Damien Cassou, Frank Ehmsen, Jack Baty, Kaushal
+ Modi, Peter Povinec, Sven Seebeck, Ypot.
+
++ Sources of Denote:
+
+ + Package name (GNU ELPA): =denote=
+ + Official manual: <https://protesilaos.com/emacs/denote>
+ + Change log: <https://protesilaos.com/emacs/denote-changelog>
+ + Git repo on SourceHut: <https://git.sr.ht/~protesilaos/denote>
+ - Mirrors:
+ + GitHub: <https://github.com/protesilaos/denote>
+ + GitLab: <https://gitlab.com/protesilaos/denote>
+ + Mailing list: <https://lists.sr.ht/~protesilaos/denote>
blob - /dev/null
blob + 60c3b58af49708709570ca120a7154ed079f17d7 (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/README-elpa
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+ DENOTE: SIMPLE NOTES WITH AN EFFICIENT
+ FILE-NAMING SCHEME
+
+ Protesilaos Stavrou
+ info@protesilaos.com
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+
+This manual, written by Protesilaos Stavrou, describes the customization
+options for the Emacs package called `denote' (or `denote.el'), and
+provides every other piece of information pertinent to it.
+
+The documentation furnished herein corresponds to stable version 4.0.0,
+released on 2025-04-15. Any reference to a newer feature which does not
+yet form part of the latest tagged commit, is explicitly marked as such.
+
+Current development target is 4.1.0-dev.
+
+⁃ Package name (GNU ELPA): `denote'
+⁃ Official manual: <https://protesilaos.com/emacs/denote>
+⁃ Change log: <https://protesilaos.com/emacs/denote-changelog>
+⁃ Git repositories:
+ ⁃ GitHub: <https://github.com/protesilaos/denote>
+ ⁃ GitLab: <https://gitlab.com/protesilaos/denote>
+⁃ Video demo: <https://protesilaos.com/codelog/2022-06-18-denote-demo/>
+⁃ Backronyms: Denote Everything Neatly; Omit The Excesses. Don’t Ever
+ Note Only The Epiphenomenal.
+
+If you are viewing the README.org version of this file, please note that
+the GNU ELPA machinery automatically generates an Info manual out of it.
+
+Table of Contents
+─────────────────
+
+1. COPYING
+2. Installation
+.. 1. GNU ELPA package
+.. 2. Manual installation
+3. Sample configuration
+.. 1. Get started with this sample configuration
+.. 2. More comprehensive sample configuration
+4. Overview
+5. Points of entry
+.. 1. Standard note creation
+..... 1. The `denote-prompts' option
+..... 2. The `denote-history-completion-in-prompts' option
+..... 3. The `denote-templates' option
+..... 4. Convenience commands for note creation
+..... 5. The `denote-save-buffers' option
+..... 6. The `denote-kill-buffers' option
+..... 7. The `denote-date-prompt-use-org-read-date' option
+.. 2. Create note using Org capture
+.. 3. Create note with specific prompts using Org capture
+.. 4. Create note with specific values using Org capture
+.. 5. Create a note with the region’s contents
+..... 1. A custom `denote-region' that references the source
+.. 6. Open an existing note or create it if missing
+.. 7. Maintain separate directory silos for notes
+..... 1. Make Org export work with silos
+.. 8. Exclude certain files from file prompts
+.. 9. Exclude certain directories from all operations
+.. 10. Exclude certain keywords from being inferred
+.. 11. Create a controlled vocabulary for keywords
+.. 12. Use Denote commands from the menu bar or context menu
+6. Renaming files
+.. 1. Rename a single file
+..... 1. The `denote-rename-confirmations' option
+.. 2. Rename a single file based on its front matter
+.. 3. Rename multiple files interactively
+.. 4. Rename multiple files at once by asking only for keywords
+.. 5. Rename multiple files based on their front matter
+.. 6. Rename a file by changing only its file type
+.. 7. Rename a file by adding or removing a title interactively
+.. 8. Rename a file by adding or removing keywords interactively
+.. 9. Rename a file by adding or removing a signature interactively
+.. 10. Find duplicate identifiers and put them in a Dired buffer
+.. 11. Faces used by rename commands
+7. The file-naming scheme
+.. 1. Change the order of file name components
+.. 2. Sluggification of file name components
+.. 3. User-defined sluggification of file name components
+..... 1. Custom sluggification to remove non-ASCII characters
+.. 4. Features of the file-naming scheme for searching or filtering
+8. Front matter
+.. 1. Change the front matter format
+.. 2. Regenerate front matter
+9. Linking notes
+.. 1. Add a single direct link using a file name prompt
+.. 2. Add a direct link to a file whose contents include the given query
+.. 3. Add a query link
+.. 4. Insert links to all files matching a query in their file name
+.. 5. Insert links to all files matching a query in their contents
+.. 6. The `denote-open-link-function' user option
+.. 7. The `denote-org-store-link-to-heading' user option
+.. 8. Adding direct links to files matching contents
+.. 9. Insert links from marked files in Dired
+.. 10. Link to an existing note or create a new one
+.. 11. The backlinks’ buffer
+.. 12. Writing metanotes
+.. 13. Visiting linked files via the minibuffer
+.. 14. Fontify links in non-Org buffers
+.. 15. The `denote-link-description-format' to format link descriptions
+10. Choose which commands to prompt for
+11. Fontification in Dired
+12. Automatically rename Denote buffers
+.. 1. The `denote-rename-buffer-format' option
+13. Use Org dynamic blocks
+14. Display filtered and sorted files with `denote-sort-dired' or `denote-dired'
+.. 1. Configure what extra prompts `denote-sort-dired' issues
+.. 2. Define a sorting function per component
+..... 1. Sort signatures that include Luhmann-style sequences
+15. Use `denote-grep' to search inside files
+16. Interact with the links buffer
+17. Minibuffer histories
+18. Packages that build on Denote
+.. 1. Use the `consult-denote' package for enhanced minibuffer interactions
+.. 2. Sequence notes
+.. 3. Use the `denote-markdown' package to better integrate Markdown with Denote
+.. 4. Use the `denote-journal' package which was formerly `denote-journal-extras.el'
+.. 5. Use the `denote-silo' package which formerly was `denote-silo-extras.el'
+.. 6. Use the `denote-search' package as a search interface
+.. 7. Use the `denote-explore' package to explore your notes
+.. 8. Use the `citar-denote' package for bibliography notes
+.. 9. Use the `consult-notes' package
+.. 10. Use the `denote-menu' package
+.. 11. Use the `denote-zettel-interface' package
+19. Extending Denote
+.. 1. Access the data of the latest note
+.. 2. Create a new note in any directory
+.. 3. Find empty notes and put them in a Dired buffer
+.. 4. Automatically rename the note after saving it
+.. 5. Narrow the list of files in Dired
+.. 6. Use `dired-virtual-mode' for arbitrary file listings
+.. 7. Use Embark to collect minibuffer candidates
+.. 8. Search file contents
+.. 9. Bookmark the directory with the notes
+.. 10. Treat your notes as a project
+.. 11. Use the tree-based file prompt for select commands
+.. 12. Rename files with Denote in the Image Dired thumbnails buffer
+.. 13. Rename files with Denote using `dired-preview'
+.. 14. Avoid duplicate identifiers when exporting Denote notes
+..... 1. Export Denote notes with Org Mode
+..... 2. Export Denote notes with Markdown
+.. 15. Set up your workflow for daily or weekly meeting notes
+20. For developers or advanced users
+.. 1. Common building blocks for developers or advanced users
+.. 2. File path interface for developers or advanced users
+.. 3. Data retrieval interface for developers or advanced users
+.. 4. Prompt interface for developers or advanced users
+.. 5. Front matter interface for developers or advanced users
+.. 6. Link interface for developers or advanced users
+.. 7. Xref interface for developers or advanced users
+.. 8. Renaming files interface for developers or advanced users
+21. Troubleshoot Denote in a pristine environment
+22. Contributing
+.. 1. Wishlist of what we can do to extend Denote
+23. Publications about Denote
+24. Alternatives to Denote
+.. 1. Alternative implementations and further reading
+25. Frequently Asked Questions
+.. 1. Why develop Denote when PACKAGE already exists?
+.. 2. Why not rely exclusively on Org?
+.. 3. Why care about Unix tools when you use Emacs?
+.. 4. Why many small files instead of few large ones?
+.. 5. Does Denote perform well at scale?
+.. 6. I add TODOs to my notes; will many files slow down the Org agenda?
+.. 7. I want to sort by last modified in Dired, why won’t Denote let me?
+.. 8. How do you handle the last modified case?
+.. 9. Why are some Org links opening outside Emacs?
+.. 10. Speed up backlinks’ or query links’ buffer creation?
+.. 11. Why do I get “Search failed with status 1” when I search for backlinks?
+.. 12. Why do I get a double `#+title' in Doom Emacs?
+26. Acknowledgements
+27. GNU Free Documentation License
+28. Indices
+.. 1. Function index
+.. 2. Variable index
+.. 3. Concept index
+
+
+1 COPYING
+═════════
+
+ Copyright (C) 2022-2025 Free Software Foundation, Inc.
+
+ Permission is granted to copy, distribute and/or modify
+ this document under the terms of the GNU Free
+ Documentation License, Version 1.3 or any later version
+ published by the Free Software Foundation; with no
+ Invariant Sections, with the Front-Cover Texts being “A
+ GNU Manual,” and with the Back-Cover Texts as in (a)
+ below. A copy of the license is included in the section
+ entitled “GNU Free Documentation License.”
+
+ (a) The FSF’s Back-Cover Text is: “You have the freedom to
+ copy and modify this GNU manual.”
+
+
+2 Installation
+══════════════
+
+
+
+
+2.1 GNU ELPA package
+────────────────────
+
+ The package is available as `denote'. Simply do:
+
+ ┌────
+ │ M-x package-refresh-contents
+ │ M-x package-install
+ └────
+
+
+ And search for it.
+
+ GNU ELPA provides the latest stable release. Those who prefer to
+ follow the development process in order to report bugs or suggest
+ changes, can use the version of the package from the GNU-devel ELPA
+ archive. Read:
+ <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>.
+
+
+2.2 Manual installation
+───────────────────────
+
+ Assuming your Emacs files are found in `~/.emacs.d/', execute the
+ following commands in a shell prompt:
+
+ ┌────
+ │ cd ~/.emacs.d
+ │
+ │ # Create a directory for manually-installed packages
+ │ mkdir manual-packages
+ │
+ │ # Go to the new directory
+ │ cd manual-packages
+ │
+ │ # Clone this repo, naming it "denote"
+ │ git clone https://github.com/protesilaos/denote denote
+ └────
+
+ Finally, in your `init.el' (or equivalent) evaluate this:
+
+ ┌────
+ │ ;; Make Elisp files in that directory available to the user.
+ │ (add-to-list 'load-path "~/.emacs.d/manual-packages/denote")
+ └────
+
+ Everything is in place to set up the package.
+
+
+3 Sample configuration
+══════════════════════
+
+ Denote is immediately useful for beginners and power users alike. This
+ manual covers everything in detail, though do not let the numerous
+ possibilities distract you from the fact that a basic configuration is
+ enough to be highly productive ([Get started with this sample
+ configuration]).
+
+
+[Get started with this sample configuration] See section 3.1
+
+3.1 Get started with this sample configuration
+──────────────────────────────────────────────
+
+ If you are new to Denote, this a good place to start. Then work your
+ way through the manual and expand your configuration accordingly. Only
+ include commands/variables that are useful to you. We provide another
+ code sample if you need some ideas ([More comprehensive sample
+ configuration]).
+
+ ┌────
+ │ ;; Remember that the website version of this manual shows the latest
+ │ ;; developments, which may not be available in the package you are
+ │ ;; using. Instead of copying from the web site, refer to the version
+ │ ;; of the documentation that comes with your package. Evaluate:
+ │ ;;
+ │ ;; (info "(denote) Sample configuration")
+ │ (use-package denote
+ │ :ensure t
+ │ :hook (dired-mode . denote-dired-mode)
+ │ :bind
+ │ (("C-c n n" . denote)
+ │ ("C-c n r" . denote-rename-file)
+ │ ("C-c n l" . denote-link)
+ │ ("C-c n b" . denote-backlinks)
+ │ ("C-c n d" . denote-dired)
+ │ ("C-c n g" . denote-grep))
+ │ :config
+ │ (setq denote-directory (expand-file-name "~/Documents/notes/"))
+ │
+ │ ;; Automatically rename Denote buffers when opening them so that
+ │ ;; instead of their long file name they have, for example, a literal
+ │ ;; "[D]" followed by the file's title. Read the doc string of
+ │ ;; `denote-rename-buffer-format' for how to modify this.
+ │ (denote-rename-buffer-mode 1))
+ └────
+
+
+[More comprehensive sample configuration] See section 3.2
+
+
+3.2 More comprehensive sample configuration
+───────────────────────────────────────────
+
+ Here we include more of what you can configure with Denote ([Get
+ started with this sample configuration]).
+
+ ┌────
+ │ ;; Remember that the website version of this manual shows the latest
+ │ ;; developments, which may not be available in the package you are
+ │ ;; using. Instead of copying from the web site, refer to the version
+ │ ;; of the documentation that comes with your package. Evaluate:
+ │ ;;
+ │ ;; (info "(denote) Sample configuration")
+ │ (use-package denote
+ │ :ensure t
+ │ :hook
+ │ ( ;; If you use Markdown or plain text files, then you want to make
+ │ ;; the Denote links clickable (Org renders links as buttons right
+ │ ;; away)
+ │ (text-mode . denote-fontify-links-mode-maybe)
+ │ ;; Apply colours to Denote names in Dired. This applies to all
+ │ ;; directories. Check `denote-dired-directories' for the specific
+ │ ;; directories you may prefer instead. Then, instead of
+ │ ;; `denote-dired-mode', use `denote-dired-mode-in-directories'.
+ │ (dired-mode . denote-dired-mode))
+ │ :bind
+ │ ;; Denote DOES NOT define any key bindings. This is for the user to
+ │ ;; decide. For example:
+ │ ( :map global-map
+ │ ("C-c n n" . denote)
+ │ ("C-c n d" . denote-dired)
+ │ ("C-c n g" . denote-grep)
+ │ ;; If you intend to use Denote with a variety of file types, it is
+ │ ;; easier to bind the link-related commands to the `global-map', as
+ │ ;; shown here. Otherwise follow the same pattern for `org-mode-map',
+ │ ;; `markdown-mode-map', and/or `text-mode-map'.
+ │ ("C-c n l" . denote-link)
+ │ ("C-c n L" . denote-add-links)
+ │ ("C-c n b" . denote-backlinks)
+ │ ("C-c n q c" . denote-query-contents-link) ; create link that triggers a grep
+ │ ("C-c n q f" . denote-query-filenames-link) ; create link that triggers a dired
+ │ ;; Note that `denote-rename-file' can work from any context, not just
+ │ ;; Dired bufffers. That is why we bind it here to the `global-map'.
+ │ ("C-c n r" . denote-rename-file)
+ │ ("C-c n R" . denote-rename-file-using-front-matter)
+ │
+ │ ;; Key bindings specifically for Dired.
+ │ :map dired-mode-map
+ │ ("C-c C-d C-i" . denote-dired-link-marked-notes)
+ │ ("C-c C-d C-r" . denote-dired-rename-files)
+ │ ("C-c C-d C-k" . denote-dired-rename-marked-files-with-keywords)
+ │ ("C-c C-d C-R" . denote-dired-rename-marked-files-using-front-matter))
+ │
+ │ :config
+ │ ;; Remember to check the doc string of each of those variables.
+ │ (setq denote-directory (expand-file-name "~/Documents/notes/"))
+ │ (setq denote-save-buffers nil)
+ │ (setq denote-known-keywords '("emacs" "philosophy" "politics" "economics"))
+ │ (setq denote-infer-keywords t)
+ │ (setq denote-sort-keywords t)
+ │ (setq denote-prompts '(title keywords))
+ │ (setq denote-excluded-directories-regexp nil)
+ │ (setq denote-excluded-keywords-regexp nil)
+ │ (setq denote-rename-confirmations '(rewrite-front-matter modify-file-name))
+ │
+ │ ;; Pick dates, where relevant, with Org's advanced interface:
+ │ (setq denote-date-prompt-use-org-read-date t)
+ │
+ │ ;; Automatically rename Denote buffers using the `denote-rename-buffer-format'.
+ │ (denote-rename-buffer-mode 1))
+ └────
+
+
+[Get started with this sample configuration] See section 3.1
+
+
+4 Overview
+══════════
+
+ Denote aims to be a simple-to-use, focused-in-scope, and effective
+ note-taking and file-naming tool for Emacs.
+
+ Denote is based on the idea that files should follow a predictable and
+ descriptive file-naming scheme. The file name must offer a clear
+ indication of what the contents are about, without reference to any
+ other metadata. Denote basically streamlines the creation of such
+ files or file names while providing facilities to link between them
+ (where those files are editable).
+
+ Denote’s file-naming scheme is not limited to “notes”. It can be used
+ for all types of file, including those that are not editable in Emacs,
+ such as videos. Naming files in a consistent way makes their
+ filtering and retrieval considerably easier. Denote provides relevant
+ facilities to rename files, regardless of file type.
+
+ Denote is based on the following core design principles:
+
+ Predictability
+ File names must follow a consistent and descriptive naming
+ convention ([The file-naming scheme]). The file name alone
+ should offer a clear indication of what the contents are,
+ without reference to any other metadatum. This convention is
+ not specific to note-taking, as it is pertinent to any form of
+ file that is part of the user’s long-term storage ([Renaming
+ files]).
+
+ Composability
+ Be a good Emacs citizen, by integrating with other packages or
+ built-in functionality instead of re-inventing functions such as
+ for filtering or greping. The author of Denote (Protesilaos,
+ aka “Prot”) writes ordinary notes in plain text (`.txt'),
+ switching on demand to an Org file only when its expanded set of
+ functionality is required for the task at hand ([Points of
+ entry]).
+
+ Portability
+ Notes are plain text and should remain portable. The way Denote
+ writes file names, the front matter it includes in the note’s
+ header, and the links it establishes must all be adequately
+ usable with standard Unix tools. No need for a database or some
+ specialised software. As Denote develops and this manual is
+ fully fleshed out, there will be concrete examples on how to do
+ the Denote-equivalent on the command-line.
+
+ Flexibility
+ Do not assume the user’s preference for a note-taking
+ methodology. Denote is conceptually similar to the Zettelkasten
+ Method, which you can learn more about in this detailed
+ introduction: <https://zettelkasten.de/introduction/>. Notes
+ are atomic (one file per note) and have a unique identifier.
+ However, Denote does not enforce a particular methodology for
+ knowledge management, such as a restricted vocabulary or
+ mutually exclusive sets of keywords. Denote also does not check
+ if the user writes thematically atomic notes. It is up to the
+ user to apply the requisite rigor and/or creativity in pursuit
+ of their preferred workflow ([Writing metanotes]).
+
+ Hackability
+ Denote’s code base consists of small and reusable functions.
+ They all have documentation strings. The idea is to make it
+ easier for users of varying levels of expertise to understand
+ what is going on and make surgical interventions where necessary
+ (e.g. to tweak some formatting). In this manual, we provide
+ concrete examples on such user-level configurations ([Keep a
+ journal or diary]).
+
+ Now the important part… “Denote” is the familiar word, though it also
+ is a play on the “note” concept. Plus, we can come up with acronyms,
+ recursive or otherwise, of increasingly dubious utility like:
+
+ ⁃ Don’t Ever Note Only The Epiphenomenal
+ ⁃ Denote Everything Neatly; Omit The Excesses
+
+ But we’ll let you get back to work. Don’t Eschew or Neglect your
+ Obligations, Tasks, and Engagements.
+
+
+[The file-naming scheme] See section 7
+
+[Renaming files] See section 6
+
+[Points of entry] See section 5
+
+[Writing metanotes] See section 9.12
+
+[Keep a journal or diary] See section 18.4
+
+
+5 Points of entry
+═════════════════
+
+ There are seven main ways to write a note with Denote: invoke the
+ `denote', `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. Other more specialised commands exist as well,
+ which one shall learn about as they read through this manual. We do
+ not want to overwhelm the user with options at this stage.
+
+ All these commands construct the file name in accordance with the user
+ option `denote-file-name-components-order' ([Change the order of file
+ name components]).
+
+
+[Change the order of file name components] See section 7.1
+
+5.1 Standard note creation
+──────────────────────────
+
+ The `denote' command will prompt for a title. If a region is active,
+ the text of the region becomes the default at the minibuffer prompt
+ (meaning that typing `RET' without any input will use the default
+ value). Once the title is supplied, the `denote' command will then
+ ask for keywords. The resulting note will have a file name as already
+ explained: [The file naming scheme]
+
+ The `denote' command runs the hook `denote-after-new-note-hook' after
+ creating the new note ([Access the data of the latest note]). When
+ called from Lisp, it returns the path it generates. Before returning
+ the path, it decides what to do with the buffer of the note, in
+ accordance with the user option `denote-kill-buffers' ([The
+ `denote-kill-buffers' option]).
+
+ The file type of the new note is determined by the user option
+ `denote-file-type' ([Front matter]).
+
+ The keywords’ prompt supports minibuffer completion. Available
+ candidates are those defined in the user option
+ `denote-known-keywords'. More candidates can be inferred from the
+ names of existing notes, by setting `denote-infer-keywords' to non-nil
+ (which is the case by default) ([Create a controlled vocabulary for
+ keywords]).
+
+ Multiple keywords can be inserted by separating them with a comma (or
+ whatever the value of the `crm-separator' is—which should be a comma).
+ When the user option `denote-sort-keywords' is non-nil (the default),
+ keywords are sorted alphabetically (technically, the sorting is done
+ with `string-lessp').
+
+ The interactive behaviour of the `denote' command is influenced by the
+ user option `denote-prompts' ([The denote-prompts option]).
+
+ The `denote' command can also be called from Lisp. Read its doc
+ string for the technicalities.
+
+ In the interest of discoverability, `denote' is also available under
+ the alias `denote-create-note'.
+
+
+[The file naming scheme] See section 7
+
+[Access the data of the latest note] See section 19.1
+
+[The `denote-kill-buffers' option] See section 5.1.6
+
+[Front matter] See section 8
+
+[Create a controlled vocabulary for keywords] See section 5.11
+
+[The denote-prompts option] See section 5.1.1
+
+5.1.1 The `denote-prompts' option
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ The user option `denote-prompts' determines how the `denote' command
+ will behave interactively ([Standard note creation]).
+
+ Commands that prompt for user input to construct a Denote file name
+ include, but are not limited to: `denote', `denote-signature',
+ `denote-type', `denote-date', `denote-subdirectory',
+ `denote-rename-file', `denote-dired-rename-files'.
+
+ • [Convenience commands for note creation].
+ • [Renaming files].
+
+ The value of this user option is a list of symbols, which includes any
+ of the following:
+
+ • `title': Prompt for the title of the new note ([The
+ `denote-history-completion-in-prompts' option]).
+
+ • `keywords': Prompts with completion for the keywords of the new
+ note. Available candidates are those specified in the user option
+ `denote-known-keywords'. If the user option `denote-infer-keywords'
+ is non-nil, keywords in existing note file names are included in the
+ list of candidates. The `keywords' prompt uses
+ `completing-read-multiple', meaning that it can accept multiple
+ keywords separated by a comma (or whatever the value of
+ `crm-separator' is).
+
+ • `file-type': Prompts with completion for the file type of the new
+ note. Available candidates are those specified in the user option
+ `denote-file-type'. Without this prompt, `denote' uses the value of
+ `denote-file-type'.
+
+ • `subdirectory': Prompts with completion for a subdirectory in which
+ to create the note. Available candidates are the value of the user
+ option `denote-directory' and all of its subdirectories. Any
+ subdirectory must already exist: Denote will not create it.
+
+ • `date': Prompts for the date of the new note. It will expect an
+ input like 2022-06-16 or a date plus time: 2022-06-16 14:30.
+ Without the `date' prompt, the `denote' command uses the
+ `current-time'.
+
+ [The denote-date-prompt-use-org-read-date option].
+
+ • `template': Prompts for a KEY among the `denote-templates'. The
+ value of that KEY is used to populate the new note with content,
+ which is added after the front matter ([The denote-templates
+ option]).
+
+ • `signature': - Prompts for an arbitrary string that can be used for
+ any kind of workflow, such as a special tag to label the `part1' and
+ `part2' of a large file that is split in half, or to add special
+ contexts like `home' and `work', or even priorities like `a', `b',
+ `c'. One other use-case is to implement a sequencing scheme that
+ makes notes have hierarchical relationships. This is handled by our
+ optional extension `denote-sequence.el', which is part of the
+ `denote' package ([Write sequence notes or “folgezettel”]).
+
+ The prompts occur in the given order.
+
+ If the value of this user option is nil, no prompts are used. The
+ resulting file name will consist of an identifier (i.e. the date and
+ time) and a supported file type extension (per `denote-file-type').
+
+ Recall that Denote’s standard file-naming scheme is defined as follows
+ ([The file-naming scheme]):
+
+ ┌────
+ │ 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:
+
+ ┌────
+ │ DATE.EXT
+ │ DATE--TITLE.EXT
+ │ DATE__KEYWORDS.EXT
+ └────
+
+
+ When in doubt, always include the `title' and `keywords' prompts.
+
+ Finally, this user option only affects the interactive use of the
+ `denote' or other relevant commands (advanced users can call it from
+ Lisp). In Lisp usage, the behaviour is always what the caller
+ specifies, based on the supplied arguments.
+
+
+[Standard note creation] See section 5.1
+
+[Convenience commands for note creation] See section 5.1.4
+
+[Renaming files] See section 6
+
+[The `denote-history-completion-in-prompts' option] See section 5.1.2
+
+[The denote-date-prompt-use-org-read-date option] See section 5.1.7
+
+[The denote-templates option] See section 5.1.3
+
+[Write sequence notes or “folgezettel”] See section 18.2
+
+[The file-naming scheme] See section 7
+
+
+5.1.2 The `denote-history-completion-in-prompts' option
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ The user option `denote-history-completion-in-prompts' toggles history
+ completion in all `denote-prompts-with-history-as-completion'.
+
+ When this user option is set to a non-nil value, Denote will use
+ minibuffer history entries as completion candidates in all of the
+ `denote-prompts-with-history-as-completion'. Those will show previous
+ inputs from their respective history as possible values to select,
+ either to (i) re-insert them verbatim or (ii) with the intent to edit
+ them further (depending on the minibuffer user interface, one can
+ select a candidate with `TAB' without exiting the minibuffer, as
+ opposed to what `RET' normally does by selecting and exiting).
+
+ When this user option is set to a nil value, all of the
+ `denote-prompts-with-history-as-completion' will not use minibuffer
+ completion: they will just prompt for a string of characters. Their
+ history is still available through all the standard ways of retrieving
+ minibuffer history, such as with the command
+ `previous-history-element'.
+
+ History completion still allows arbitrary values to be provided as
+ input: they do not have to match the available minibuffer completion
+ candidates.
+
+ Note that some prompts, like `denote-keywords-prompt', always use
+ minibuffer completion, due to the specifics of their data.
+
+ [ Consider enabling the built-in `savehist-mode' to persist minibuffer
+ histories between sessions.]
+
+
+5.1.3 The `denote-templates' option
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ The user option `denote-templates' is an alist of content templates
+ for new notes. A template is arbitrary text that Denote will add to a
+ newly created note right below the front matter.
+
+ Templates are expressed as a `(KEY . VALUE)' association.
+
+ • The `KEY' is the name which identifies the template. It is an
+ arbitrary symbol, such as `report', `memo', `statement'.
+
+ • The `VALUE' is either a string or the symbol of a function.
+
+ • If it is a string, it is ordinary text that Denote will insert
+ as-is. It can contain newline characters to add spacing. The
+ manual of Denote contains examples on how to use the `concat'
+ function, beside writing a generic string.
+
+ • If it is a function, it is called without arguments and is
+ expected to return a string. Denote will call the function and
+ insert the result in the buffer.
+
+ The user can choose a template either by invoking the command
+ `denote-template' or by changing the user option `denote-prompts' to
+ always prompt for a template when calling the `denote' command.
+
+ [The denote-prompts option].
+
+ [Convenience commands for note creation].
+
+ Templates can be written directly as one large string. For example
+ (the `\n' character is read as a newline):
+
+ ┌────
+ │ (setq denote-templates
+ │ '((report . "* Some heading\n\n* Another heading")
+ │ (memo . "* Some heading
+ │
+ │ * Another heading
+ │
+ │ ")))
+ └────
+
+ Long strings may be easier to type but interpret indentation
+ literally. Also, they do not scale well. A better way is to use some
+ Elisp code to construct the string. This would typically be the
+ `concat' function, which joins multiple strings into one. The
+ following is the same as the previous example:
+
+ ┌────
+ │ (setq denote-templates
+ │ `((report . "* Some heading\n\n* Another heading")
+ │ (memo . ,(concat "* Some heading"
+ │ "\n\n"
+ │ "* Another heading"
+ │ "\n\n"))))
+ └────
+
+ Notice that to evaluate a function inside of an alist we use the
+ backtick to quote the alist (NOT the straight quote) and then prepend
+ a comma to the expression that should be evaluated. The `concat' form
+ here is not sensitive to indentation, so it is easier to adjust for
+ legibility.
+
+ For when the `VALUE' is a function, we have this:
+
+ ┌────
+ │ (setq denote-templates
+ │ `((report . "* Some heading\n\n* Another heading")
+ │ (blog . my-denote-template-function-for-blog) ; a function to return a string
+ │ (memo . ,(concat "* Some heading"
+ │ "\n\n"
+ │ "* Another heading"
+ │ "\n\n"))))
+ └────
+
+ In this example, `my-denote-template-function-for-blog' is a function
+ that returns a string. Denote will take care to insert it in the
+ buffer.
+
+ DEV NOTE: We do not provide more examples at this point, though feel
+ welcome to ask for help if the information provided herein is not
+ sufficient. We shall expand the manual accordingly.
+
+
+[The denote-prompts option] See section 5.1.1
+
+[Convenience commands for note creation] See section 5.1.4
+
+
+5.1.4 Convenience commands for note creation
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ Sometimes the user needs to create a note that has different
+ requirements from those of `denote' ([Standard note creation]). While
+ this can be achieved globally by changing the `denote-prompts' user
+ option, there are cases where an ad-hoc method is the appropriate one
+ ([The denote-prompts option]).
+
+ To this end, Denote provides the following interactive convenience
+ commands for note creation. They all work by appending a new prompt to
+ the existing `denote-prompts'.
+
+ Create note by specifying file type
+ The `denote-type' command creates a note while prompting for a
+ file type.
+
+ This is the equivalent of calling `denote' when `denote-prompts'
+ has the `file-type' prompt appended to its existing prompts. In
+ practical terms, this lets you produce, say, a note in Markdown
+ even though you normally write in Org ([Standard note
+ creation]).
+
+ The `denote-create-note-using-type' is an alias of
+ `denote-type'.
+
+ Create note using a date
+ Normally, Denote reads the current date and time to construct
+ the unique identifier of a newly created note ([Standard note
+ creation]). Sometimes, however, the user needs to set an
+ explicit date+time value.
+
+ This is where the `denote-date' command comes in. It creates a
+ note while prompting for a date. The date can be in
+ YEAR-MONTH-DAY notation like `2022-06-30' or that plus the time:
+ `2022-06-16 14:30'.
+
+ [The denote-date-prompt-use-org-read-date option].
+
+ This is the equivalent of calling `denote' when `denote-prompts'
+ has the `date' prompt appended to its existing prompts.
+
+ The `denote-create-note-using-date' is an alias of
+ `denote-date'.
+
+ Create note in a specific directory
+ The `denote-subdirectory' command creates a note while prompting
+ for a subdirectory. Available candidates include the value of
+ the variable `denote-directory' and any subdirectory thereof
+ (Denote does not create subdirectories).
+
+ This is the equivalent of calling `denote' when `denote-prompts'
+ has the `subdirectory' prompt appended to its existing prompts.
+
+ The `denote-create-note-in-subdirectory' is a more descriptive
+ alias of `denote-subdirectory'.
+
+ Create note and add a template
+ The `denote-template' command creates a new note and inserts the
+ specified template below the front matter ([The denote-templates
+ option]). Available candidates for templates are specified in
+ the user option `denote-templates'.
+
+ This is the equivalent of calling `denote' when `denote-prompts'
+ has the `template' prompt appended to its existing prompts.
+
+ 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, …).
+
+ This is the equivalent of calling `denote' when `denote-prompts'
+ has the `signature' prompt appended to its existing prompts.
+
+ The `denote-create-note-using-signature' is an alias of the
+ command `denote-signature' intended to make the functionality
+ more discoverable.
+
+
+[Standard note creation] See section 5.1
+
+[The denote-prompts option] See section 5.1.1
+
+[The denote-date-prompt-use-org-read-date option] See section 5.1.7
+
+[The denote-templates option] See section 5.1.3
+
+◊ 5.1.4.1 Write your own convenience commands
+
+ The convenience commands we provide only cover some basic use-cases
+ ([Convenience commands for note creation]). The user may require
+ combinations that are not covered, such as to prompt for a template
+ and for a subdirectory, instead of only one of the two. To this end,
+ we show how to follow the code we use in Denote to write your own
+ variants of those commands.
+
+ First let’s take a look at the definition of one of those commands.
+ They all look the same, but we use `denote-subdirectory' for this
+ example:
+
+ ┌────
+ │ (defun denote-subdirectory ()
+ │ "Create note while prompting for a subdirectory.
+ │
+ │ Available candidates include the value of the variable
+ │ `denote-directory' and any subdirectory thereof.
+ │
+ │ This is the equivalent of calling `denote' when `denote-prompts'
+ │ has the `subdirectory' prompt appended to its existing prompts."
+ │ (declare (interactive-only t))
+ │ (interactive)
+ │ (let ((denote-prompts (denote-add-prompts '(subdirectory))))
+ │ (call-interactively #'denote)))
+ └────
+
+ The hyphenated word after `defun' is the name of the function. It has
+ to be unique. Then we have the documentation string (or “doc string”)
+ which is for the user’s convenience.
+
+ This function is `interactive', meaning that it can be called via
+ `M-x' or be assigned to a key binding. Then we have the local binding
+ of the `denote-prompts' to the desired combination (“local” means
+ specific to this function without affecting other contexts). Lastly,
+ it calls the standard `denote' command interactively, so it uses all
+ the prompts in their specified order.
+
+ The function call `(denote-add-prompts '(subdirectory))' will append
+ the subdirectory prompt to the existing value of the `denote-prompts'.
+ If, for example, the default value is `'(title keywords)' (to prompt
+ for a title and then for keywords), it will become `'(subdirectory
+ title keywords)' inside the context of this `let'. Remember that this
+ is “local”, so the global value of `denote-prompts' remains
+ unaffected.
+
+ Now let’s say we want to have a command that (i) asks for a template
+ (ii) for a subdirectory ([The denote-templates option]), and (iii)
+ then goes through the remaining `denote-prompts'. All we need to do is
+ tweak the `let' bound value of `denote-prompts' and give our command a
+ unique name:
+
+ ┌────
+ │ ;; Like `denote-subdirectory' but also ask for a template
+ │ (defun my-denote-subdirectory-with-template ()
+ │ "Create note while also prompting for a template and subdirectory.
+ │
+ │ This is the equivalent of calling `denote' when `denote-prompts' has the
+ │ `subdirectory' and `template' prompts appended to its existing prompts."
+ │ (declare (interactive-only t))
+ │ (interactive)
+ │ (let ((denote-prompts (denote-add-prompts '(subdirectory template))))
+ │ (call-interactively #'denote)))
+ └────
+
+ The tweaks to `denote-prompts' determine how the command will behave
+ ([The denote-prompts option]). Use this paradigm to write your own
+ variants which you can then assign to keys, invoke with `M-x', or add
+ to the list of commands available at the `denote-command-prompt'
+ ([Choose which commands to prompt for]).
+
+ In the above scenario, we are using the `denote-add-prompts' function,
+ which appends whatever prompts we want to the existing value of
+ `denote-prompts'. If the user prefers to completely override the
+ `denote-prompts', they can set the value outright:
+
+ ┌────
+ │ (defun my-denote-subdirectory-with-template-title-and-keywords ()
+ │ "Create a note while prompting for subdirectory, template, title, and keywords.
+ │
+ │ This is the equivalent of calling `denote' when `denote-prompts' has the
+ │ value '(template subdirectory title keywords)."
+ │ (declare (interactive-only t))
+ │ (interactive)
+ │ (let ((denote-prompts '(subdirectory template title keywords)))
+ │ (call-interactively #'denote)))
+ └────
+
+
+ [Convenience commands for note creation] See section 5.1.4
+
+ [The denote-templates option] See section 5.1.3
+
+ [The denote-prompts option] See section 5.1.1
+
+ [Choose which commands to prompt for] See section 10
+
+
+5.1.5 The `denote-save-buffers' option
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ The user option `denote-save-buffer-after-creation' controls whether
+ commands that create new notes save their buffer outright.
+
+ The default behaviour of commands such as `denote' (or related) is to
+ not save the buffer they create ([Points of entry]). This gives the
+ user the chance to review the text before writing it to a file. The
+ user may choose to delete the unsaved buffer, thus not creating a new
+ note ([The `denote-save-buffer-after-creation' option]).
+
+ This option also applies to notes affected by the renaming commands
+ (`denote-rename-file' and related).
+
+ If this user option is set to a non-nil value, such buffers are saved
+ automatically. The assumption is that the user who opts in to this
+ feature is familiar with the `denote-rename-file' operation (or
+ related) and knows it is reliable ([Renaming files]).
+
+ [The `denote-kill-buffers' option].
+
+
+[Points of entry] See section 5
+
+[The `denote-save-buffer-after-creation' option] See section 5.1.5
+
+[Renaming files] See section 6
+
+[The `denote-kill-buffers' option] See section 5.1.6
+
+
+5.1.6 The `denote-kill-buffers' option
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ The user option `denote-kill-buffers' controls whether to kill a
+ buffer that was generated by a Denote command. This can happen when
+ creating a new file or renaming an existing one.
+
+ • [Points of entry].
+ • [Renaming files].
+
+ The default behaviour of creation or renaming commands such as
+ `denote' or `denote-rename-file' is to not kill the buffer they create
+ or modify at the end of their operation. The idea is to give the user
+ the chance to confirm that everything is in order.
+
+ If this user option is nil (the default), buffers affected by a
+ creation or renaming command are not automatically killed.
+
+ If set to the symbol `on-creation', new notes are automatically
+ killed.
+
+ If set to the symbol `on-rename', renamed notes are automatically
+ killed.
+
+ If set to t, new and renamed notes are killed.
+
+ If a buffer is killed, it is also saved, as if `denote-save-buffers'
+ were t ([The `denote-save-buffers' option]).
+
+ In all cases, if the buffer already existed before the Denote
+ operation it is NOT automatically killed.
+
+
+[Points of entry] See section 5
+
+[Renaming files] See section 6
+
+[The `denote-save-buffers' option] See section 5.1.5
+
+
+5.1.7 The `denote-date-prompt-use-org-read-date' option
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ By default, Denote uses its own simple prompt for date or date+time
+ input ([The denote-prompts option]). This is done when the
+ `denote-prompts' option includes a `date' symbol and/or when the user
+ invokes the `denote-date' command.
+
+ Users who want to benefit from the more advanced date selection method
+ that is common in interactions with Org mode, can set the user option
+ `denote-date-prompt-use-org-read-date' to a non-nil value.
+
+
+[The denote-prompts option] See section 5.1.1
+
+
+5.2 Create note using Org capture
+─────────────────────────────────
+
+ For integration with `org-capture', the user must first add the
+ relevant template. Such as:
+
+ ┌────
+ │ (with-eval-after-load 'org-capture
+ │ (add-to-list 'org-capture-templates
+ │ '("n" "New note (with Denote)" plain
+ │ (file denote-last-path)
+ │ #'denote-org-capture
+ │ :no-save t
+ │ :immediate-finish nil
+ │ :kill-buffer t
+ │ :jump-to-captured t)))
+ └────
+
+ Once the template is added, it is accessed from the specified key. If,
+ for instance, `org-capture' is bound to `C-c c', then the note
+ creation is initiated with `C-c c n', per the above snippet. After
+ that, the process is the same as with invoking `denote' directly,
+ namely: a prompt for a title followed by a prompt for keywords,
+ assuming the default settings ([Standard note creation]). Concretely,
+ this method always respects the value of the user option
+ `denote-prompts' ([The `denote-prompts' option]).
+
+ It is also possible to define templates that have specific prompts or
+ certain values set, for which there is no prompt:
+
+ • [Create note with specific values using Org capture]
+ • [Create note with specific prompts using Org capture]
+
+ Users may prefer to leverage `org-capture' in order to extend file
+ creation with the specifiers described in the `org-capture-templates'
+ documentation (such as to capture the active region and/or create a
+ hyperlink pointing to the given context).
+
+ IMPORTANT. Due to the particular file-naming scheme of Denote, which
+ is derived dynamically, such specifiers or other arbitrary text cannot
+ be written directly in the template. Instead, they have to be
+ assigned to the user option `denote-org-capture-specifiers', which is
+ interpreted by the function `denote-org-capture'. Example with our
+ default value:
+
+ ┌────
+ │ (setq denote-org-capture-specifiers "%l\n%i\n%?")
+ └────
+
+ Note that `denote-org-capture' ignores the `denote-file-type': it
+ always sets the Org file extension for the created note to ensure that
+ the capture process works as intended, especially for the desired
+ output of the `denote-org-capture-specifiers'.
+
+ [ You may not need `org-capture' to do what you want ([Write your own
+ convenience commands]). ]
+
+
+[Standard note creation] See section 5.1
+
+[The `denote-prompts' option] See section 5.1.1
+
+[Create note with specific values using Org capture] See section 5.4
+
+[Create note with specific prompts using Org capture] See section 5.3
+
+[Write your own convenience commands] See section 5.1.4.1
+
+
+5.3 Create note with specific prompts using Org capture
+───────────────────────────────────────────────────────
+
+ This section assumes knowledge of how Denote+org-capture work, as
+ explained in the previous section ([Create note using Org capture]).
+
+ The previous section shows how to define an Org capture template that
+ always prompts for whatever is set in the user option `denote-prompts'
+ (title and keywords, by default). There are, however, cases where the
+ user wants more control over what kind of input Denote will prompt
+ for. To this end, we provide the function
+ `denote-org-capture-with-prompts'. Below we explain it and then show
+ some examples of how to use it.
+
+ The `denote-org-capture-with-prompts' is like `denote-org-capture' but
+ with optional prompt parameters.
+
+ When called without arguments, it does not prompt for anything. It
+ just returns the front matter with title and keyword fields empty and
+ the date and identifier fields specified. It also makes the file name
+ consist of only the identifier plus the Org file name extension ([The
+ file-naming scheme]).
+
+ Otherwise, it produces a minibuffer prompt for every non-nil value
+ that corresponds to the `TITLE', `KEYWORDS', `SUBDIRECTORY', `DATE',
+ and `TEMPLATE' arguments. The prompts are those used by the standard
+ `denote' command and all of its utility commands ([Points of entry]).
+
+ When returning the contents that fill in the Org capture template, the
+ sequence is as follows: front matter, `TEMPLATE', and then the value
+ of the user option `denote-org-capture-specifiers'.
+
+ Important note: in the case of `SUBDIRECTORY' actual subdirectories
+ must exist—Denote does not create them. Same principle for `TEMPLATE'
+ as templates must exist and are specified in the user option
+ `denote-templates'.
+
+ This is how one can incorporate `denote-org-capture-with-prompts' in
+ their Org capture templates. Instead of passing a generic `t' which
+ makes it hard to remember what the argument means, we use semantic
+ keywords like `:title' for our convenience (internally this does not
+ matter as the value still counts as non-nil, so `:foo' for `TITLE' is
+ treated the same as `:title' or `t').
+
+ ┌────
+ │ ;; This prompts for TITLE, KEYWORDS, and SUBDIRECTORY
+ │ (add-to-list 'org-capture-templates
+ │ '("N" "New note with prompts (with denote.el)" plain
+ │ (file denote-last-path)
+ │ (function
+ │ (lambda ()
+ │ (denote-org-capture-with-prompts :title :keywords :subdirectory)))
+ │ :no-save t
+ │ :immediate-finish nil
+ │ :kill-buffer t
+ │ :jump-to-captured t))
+ │
+ │ ;; This prompts only for SUBDIRECTORY
+ │ (add-to-list 'org-capture-templates
+ │ '("N" "New note with prompts (with denote.el)" plain
+ │ (file denote-last-path)
+ │ (function
+ │ (lambda ()
+ │ (denote-org-capture-with-prompts nil nil :subdirectory)))
+ │ :no-save t
+ │ :immediate-finish nil
+ │ :kill-buffer t
+ │ :jump-to-captured t))
+ │
+ │ ;; This prompts for TITLE and SUBDIRECTORY
+ │ (add-to-list 'org-capture-templates
+ │ '("N" "New note with prompts (with denote.el)" plain
+ │ (file denote-last-path)
+ │ (function
+ │ (lambda ()
+ │ (denote-org-capture-with-prompts :title nil :subdirectory)))
+ │ :no-save t
+ │ :immediate-finish nil
+ │ :kill-buffer t
+ │ :jump-to-captured t))
+ └────
+
+ [ You may not need `org-capture' to do what you want ([Write your own
+ convenience commands]). ]
+
+
+[Create note using Org capture] See section 5.2
+
+[The file-naming scheme] See section 7
+
+[Points of entry] See section 5
+
+[Write your own convenience commands] See section 5.1.4.1
+
+
+5.4 Create note with specific values using Org capture
+──────────────────────────────────────────────────────
+
+ The ordinary procedure to create a note with `org-capture' respects
+ the value of the user option `denote-prompts' ([Create note using Org
+ capture]): the user is prompted for all the values they have
+ configured (title and keywords, by default). Sometimes, there is no
+ need to have a certain prompt because the value of it will be
+ constant. For example, the user wants to have a template that (i)
+ respects the `denote-prompts' but (ii) puts the new note in an
+ existing subdirectory of the `denote-directory'. The following code
+ block does exactly that.
+
+ [ It also is possible to have a template that deviates from
+ `denote-prompts' and prompts for specific values ([Create note with
+ specific prompts using Org capture]). ]
+
+ ┌────
+ │ (with-eval-after-load 'org-capture
+ │ (add-to-list 'org-capture-templates
+ │ '("r" "New reference (with Denote)" plain
+ │ (file denote-last-path)
+ │ (function
+ │ (lambda ()
+ │ (let ((denote-use-directory (expand-file-name "reference" (denote-directory))))
+ │ (denote-org-capture))))
+ │ :no-save t
+ │ :immediate-finish nil
+ │ :kill-buffer t
+ │ :jump-to-captured t)))
+ └────
+
+ The values one may predefine in this way are via these variables ([For
+ developers or advanced users]):
+
+ ⁃ `denote-use-date'
+
+ ⁃ `denote-use-directory'
+
+ ⁃ `denote-use-file-type'
+
+ ⁃ `denote-use-keywords'
+
+ ⁃ `denote-use-signature'
+
+ ⁃ `denote-use-template'
+
+ ⁃ `denote-use-title'
+
+ When there exists a binding for the aforementioned variables, the
+ corresponding prompt is always skipped. It is thus paramount to never
+ set those variables outside the scope of a `let' (or equivalent).
+
+ With those granted, here is another example scenario where the user
+ wants to have a constant value for the subdirectory but also be
+ prompted for a date.
+
+ ┌────
+ │ (with-eval-after-load 'org-capture
+ │ (add-to-list 'org-capture-templates
+ │ '("j" "New journal (with Denote)" plain
+ │ (file denote-last-path)
+ │ (function
+ │ (lambda ()
+ │ ;; The "journal" subdirectory of the `denote-directory'---this must exist!
+ │ (let* ((denote-use-directory (expand-file-name "journal" (denote-directory)))
+ │ ;; Use the existing `denote-prompts' as well as the one for a date.
+ │ (denote-prompts (denote-add-prompts '(date))))
+ │ (denote-org-capture))))
+ │ :no-save t
+ │ :immediate-finish nil
+ │ :kill-buffer t
+ │ :jump-to-captured t)))
+ └────
+
+ The above highlights the hackability of the Denote code base, namely,
+ how we can affect the behaviour of the underlying `denote' command by
+ `let' binding variables that affect every aspect of its behaviour
+ ([Write your own convenience commands]).
+
+
+[Create note using Org capture] See section 5.2
+
+[Create note with specific prompts using Org capture] See section 5.3
+
+[For developers or advanced users] See section 20
+
+[Write your own convenience commands] See section 5.1.4.1
+
+
+5.5 Create a note with the region’s contents
+────────────────────────────────────────────
+
+ The command `denote-region' takes the contents of the active region
+ and then calls the `denote' command. Once a new note is created, it
+ inserts the contents of the region therein. This is useful to quickly
+ elaborate on some snippet of text or capture it for future reference.
+
+ When the `denote-region' command is called with an active region, it
+ finalises its work by calling
+ `denote-region-after-new-note-functions'. This is an abnormal hook,
+ meaning that the functions added to it are called with arguments. The
+ arguments are two, representing the beginning and end positions of the
+ newly inserted text.
+
+ A common use-case for Org mode users is to call the command
+ `org-insert-structure-template' after a region is inserted. Emacs
+ will thus prompt for a structure template, such as the one
+ corresponding to a source block. In this case the function added to
+ `denote-region-after-new-note-functions' does not actually need
+ aforementioned arguments: it can simply declare those as ignored by
+ prefixing the argument names with an underscore (an underscore is
+ enough, but it is better to include a name for clarity). For example,
+ the following will prompt for a structure template as soon as
+ `denote-region' is done:
+
+ ┌────
+ │ (defun my-denote-region-org-structure-template (_beg _end)
+ │ (when (derived-mode-p 'org-mode)
+ │ (activate-mark)
+ │ (call-interactively 'org-insert-structure-template)))
+ │
+ │ (add-hook 'denote-region-after-new-note-functions #'my-denote-region-org-structure-template)
+ └────
+
+ Remember that `denote-region-after-new-note-functions' are not called
+ if `denote-region' is used without an active region.
+
+
+5.5.1 A custom `denote-region' that references the source
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ The `denote-region' command simply creates a new note and includes the
+ highlighted region’s contents as the initial text of the note ([Create
+ a note with the region’s contents]). However, users may want a more
+ streamlined workflow where the command is always used to capture
+ quotes from other sources. In this example, we consider “other
+ sources” to come from Emacs EWW buffers (with `M-x eww') or regular
+ files outside the `denote-directory'.
+
+ [ This is a proof-of-concept that does not cover all cases. If anyone
+ wants to use a variation of this, just let me know. ]
+
+ ┌────
+ │ ;; Variant of `my-denote-region' to reference the source
+ │
+ │ (defun my-denote-region-get-source-reference ()
+ │ "Get a reference to the source for use with `my-denote-region'.
+ │ The reference is a URL or an Org-formatted link to a file."
+ │ ;; We use a `cond' here because we can extend it to cover move
+ │ ;; cases.
+ │ (cond
+ │ ((derived-mode-p 'eww-mode)
+ │ (plist-get eww-data :url))
+ │ ;; Here we are just assuming an Org format. We can make this more
+ │ ;; involved, if needed.
+ │ (buffer-file-name
+ │ (format "[[file:%s][%s]]" buffer-file-name (buffer-name)))))
+ │
+ │ (defun my-denote-region ()
+ │ "Like `denote-region', but add the context afterwards.
+ │ For how the context is retrieved, see `my-denote-region-get-source-reference'."
+ │ (interactive)
+ │ (let ((context (my-denote-region-get-source-reference)))
+ │ (call-interactively 'denote-region)
+ │ (when context
+ │ (goto-char (point-max))
+ │ (insert "\n")
+ │ (insert context))))
+ │
+ │ ;; Add quotes around snippets of text captured with `denote-region' or `my-denote-region'.
+ │
+ │ (defun my-denote-region-org-structure-template (beg end)
+ │ "Automatically quote (with Org syntax) the contents of `denote-region'."
+ │ (when (derived-mode-p 'org-mode)
+ │ (goto-char end)
+ │ (insert "#+end_quote\n")
+ │ (goto-char beg)
+ │ (insert "#+begin_quote\n")))
+ │
+ │ (add-hook 'denote-region-after-new-note-functions #'my-denote-region-org-structure-template)
+ └────
+
+ With the above in place, calling the `my-denote-region' command does
+ the following:
+
+ • It creates a new note as usual, prompting for the relevant data.
+ • Inserts the contents of the region below the front matter of the new
+ note.
+ • Adds Org-style quotation block markers around the inserted region.
+ • Adds a link to the URL or file from where `my-denote-region' was
+ called.
+
+
+[Create a note with the region’s contents] See section 5.5
+
+
+5.6 Open an existing note or create it if missing
+─────────────────────────────────────────────────
+
+ Sometimes it is necessary to briefly interrupt the ongoing writing
+ session to open an existing note or, if that is missing, to create it.
+ This happens when a new tangential thought occurs and the user wants
+ to confirm that an entry for it is in place. To this end, Denote
+ provides the command `denote-open-or-create' as well as its more
+ flexible counterpart `denote-open-or-create-with-command'.
+
+ The `denote-open-or-create' prompts to visit a file in the
+ `denote-directory'. At this point, the user must type in search terms
+ that match a file name. If the input does not return any matches and
+ the user confirms their choice to proceed (usually by typing RET
+ twice, depending on the minibuffer settings), `denote-open-or-create'
+ will call the `denote' command interactively to create a new note. It
+ will then use whatever prompts `denote' normally has, per the user
+ option `denote-prompts' ([Standard note creation]). If the title
+ prompt is involved (the default behaviour), the
+ `denote-open-or-create' sets up this prompt to have the previous input
+ as the default title of the note to-be-created. This means that the
+ user can type RET at the empty prompt to re-use what they typed in
+ previously. Commands to use previous inputs from the history are also
+ available (`M-p' or `M-n' in the minibuffer, which call
+ `previous-history-element' and `next-history-element' by default).
+ Accessing the history is helpful to, for example, make further edits
+ to the available text.
+
+ The `denote-open-or-create-with-command' is like the above, except
+ when it is about to create the new note it first prompts for the
+ specific file-creating command to use ([Points of entry]). For
+ example, the user may want to specify a signature for this new file,
+ so they can select the `denote-signature' command.
+
+ Denote provides similar functionality for linking to an existing note
+ or creating a new one ([Link to a note or create it if missing]).
+
+
+[Standard note creation] See section 5.1
+
+[Points of entry] See section 5
+
+[Link to a note or create it if missing] See section 9.10
+
+
+5.7 Maintain separate directory silos for notes
+───────────────────────────────────────────────
+
+ The user option `denote-directory' accepts a value that represents the
+ path to a directory, such as `~/Documents/notes'. Normally, the user
+ will have one place where they store all their notes, in which case
+ this arrangement shall suffice.
+
+ There is, however, the possibility to maintain separate directories of
+ notes. By “separate”, we mean that they do not communicate with each
+ other: no linking between them, no common keywords, nothing. Think of
+ the scenario where one set of notes is for private use and another is
+ for an employer. We call these separate directories “silos”.
+
+ To create silos, the user must specify a local variable at the root of
+ the desired directory. This is done by creating a `.dir-locals.el'
+ file, with the following contents:
+
+ ┌────
+ │ ;;; Directory Local Variables. For more information evaluate:
+ │ ;;;
+ │ ;;; (info "(emacs) Directory Variables")
+ │
+ │ ((nil . ((denote-directory . "/path/to/silo/"))))
+ └────
+
+ When inside the directory that contains this `.dir-locals.el' file,
+ all Denote commands/functions for note creation, linking, the
+ inference of available keywords, et cetera will use the silo as their
+ point of reference ([The `denote-silo' package which formerly was
+ `denote-silo-extras.el']). They will not read the global value of
+ `denote-directory'. The global value of `denote-directory' is read
+ everywhere else except the silos.
+
+ In concrete terms, this is a representation of the directory
+ structures (notice the `.dir-locals.el' file is needed only for the
+ silos):
+
+ ┌────
+ │ ;; This is the global value of 'denote-directory' (no need for a .dir-locals.el)
+ │ ~/Documents/notes
+ │ |-- 20210303T120534--this-is-a-test__journal_philosophy.txt
+ │ |-- 20220303T120534--another-sample__journal_testing.md
+ │ `-- 20220620T181255--the-third-test__keyword.org
+ │
+ │ ;; A silo with notes for the employer
+ │ ~/different/path/to/notes-for-employer
+ │ |-- .dir-locals.el
+ │ |-- 20210303T120534--this-is-a-test__conference.txt
+ │ |-- 20220303T120534--another-sample__meeting.md
+ │ `-- 20220620T181255--the-third-test__keyword.org
+ │
+ │ ;; Another silo with notes for my volunteering
+ │ ~/different/path/to/notes-for-volunteering
+ │ |-- .dir-locals.el
+ │ |-- 20210303T120534--this-is-a-test__activism.txt
+ │ |-- 20220303T120534--another-sample__teambuilding.md
+ │ `-- 20220620T181255--the-third-test__keyword.org
+ └────
+
+ It is possible to configure other user options of Denote to have a
+ silo-specific value. For example, this one changes the
+ `denote-known-keywords' only for this particular silo:
+
+ ┌────
+ │ ;;; Directory Local Variables. For more information evaluate:
+ │ ;;;
+ │ ;;; (info "(emacs) Directory Variables")
+ │
+ │ ((nil . ((denote-directory . "/path/to/silo/")
+ │ (denote-known-keywords . ("food" "drink")))))
+ └────
+
+ This one is like the above, but also disables `denote-infer-keywords':
+
+ ┌────
+ │ ;;; Directory Local Variables. For more information evaluate:
+ │ ;;;
+ │ ;;; (info "(emacs) Directory Variables")
+ │
+ │ ((nil . ((denote-directory . "/path/to/silo/")
+ │ (denote-known-keywords . ("food" "drink"))
+ │ (denote-infer-keywords . nil))))
+ └────
+
+ To expand the list of local variables to, say, cover specific major
+ modes, we can do something like this:
+
+ ┌────
+ │ ;;; Directory Local Variables. For more information evaluate:
+ │ ;;;
+ │ ;;; (info "(emacs) Directory Variables")
+ │
+ │ ((nil . ((denote-directory . "/path/to/silo/")
+ │ (denote-known-keywords . ("food" "drink"))
+ │ (denote-infer-keywords . nil)))
+ │ (org-mode . ((org-hide-emphasis-markers . t)
+ │ (org-hide-macro-markers . t)
+ │ (org-hide-leading-stars . t))))
+ └────
+
+ As not all user options have a “safe” local value, Emacs will ask the
+ user to confirm their choice and to store it in the Custom code
+ snippet that is normally appended to init file (or added to the file
+ specified by the user option `custom-file').
+
+ Finally, it is possible to have a `.dir-locals.el' for subdirectories
+ of any `denote-directory'. Perhaps to specify a different set of
+ known keywords, while not making the subdirectory a silo in its own
+ right. We shall not expand on such an example, as we trust the user
+ to experiment with the best setup for their workflow.
+
+ Feel welcome to ask for help if the information provided herein is not
+ sufficient. The manual shall be expanded accordingly.
+
+
+[The `denote-silo' package which formerly was `denote-silo-extras.el']
+See section 18.5
+
+5.7.1 Make Org export work with silos
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ The Org export infrastructure is designed to ignore directory-local
+ variables. This means that Denote silos, which depend on setting the
+ local value of the variable `denote-directory', do not work as
+ intended ([Maintain separate directory silos for notes]). More
+ specifically, the Denote links do not resolve to the right file,
+ because their path is changed during the export process.
+
+ I brought this to the attention of the Org maintainer. The guidance
+ from their side is to use the `#+bind' keyword to specify a local
+ value for the `denote-directory':
+ <https://lists.gnu.org/archive/html/emacs-orgmode/2024-06/msg00206.html>.
+ The prerequisite is to set `org-export-allow-bind-keywords' to a
+ non-nil value:
+
+ ┌────
+ │ (setq org-export-allow-bind-keywords t)
+ └────
+
+ I do not think this is an elegant solution, but here are two possible
+ ways to go about it, anyway:
+
+ 1. Manually add the `#+bind' keyword to each file you want to export.
+ It has to be like this:
+
+ ┌────
+ │ #+bind: denote-directory "/path/to/silo/"
+ └────
+
+ 2. Alternatively, you can make the Org front matter that Denote uses
+ for new files automatically include the `#+bind' keyword with its
+ desired value. Here is a complete `.dir-locals.el' which (i)
+ defines the silo and (ii) modifies the `denote-org-front-matter'
+ accordingly:
+
+ ┌────
+ │ ;;; Directory Local Variables. For more information evaluate:
+ │ ;;;
+ │ ;;; (info "(emacs) Directory Variables")
+ │
+ │ ((nil . ((denote-directory . "/path/to/silo/")
+ │ (denote-org-front-matter .
+ │ "#+title: %s
+ │ #+date: %s
+ │ #+filetags: %s
+ │ #+identifier: %s
+ │ #+bind: denote-directory \"/path/to/silo/\"
+ │ \n"))))
+ └────
+
+ [ Note that if you are reading the Org source of this manual, you
+ need to use the command `org-edit-special' on the above code
+ blocks before copying the code. This is because Org automatically
+ prepends a comma to disambiguate those entries from actual
+ keywords of the current file. ]
+
+
+[Maintain separate directory silos for notes] See section 5.7
+
+
+5.8 Exclude certain files from file prompts
+───────────────────────────────────────────
+
+ The user option `denote-excluded-files-regexp' is a regular expression
+ that matches files names which should be excluded from all Denote file
+ prompts. Such prompts are present when linking to a file with one of
+ the many commands, like `denote-link' ([Linking notes]), or when
+ trying to open a file that may or may not exist ([Open an existing
+ note or create it if missing]).
+
+ Functions that check for files include `denote-directory-files' and
+ `denote-file-prompt'.
+
+ The match is performed with `string-match-p'.
+
+ [For developers or advanced users].
+
+
+[Linking notes] See section 9
+
+[Open an existing note or create it if missing] See section 5.6
+
+[For developers or advanced users] See section 20
+
+
+5.9 Exclude certain directories from all operations
+───────────────────────────────────────────────────
+
+ The user option `denote-excluded-directories-regexp' instructs all
+ Denote functions that read or check file/directory names to omit
+ directories that match the given regular expression. The regexp needs
+ to match only the name of the directory, not its full path.
+
+ Affected operations include file prompts and functions that return the
+ available files in the value of the user option `denote-directory'
+ ([Maintain separate directory silos for notes]).
+
+ File prompts are used by several commands, such as `denote-link' and
+ `denote-subdirectory'.
+
+ Functions that check for files include `denote-directory-files' and
+ `denote-directory-subdirectories'.
+
+ The match is performed with `string-match-p'.
+
+ [For developers or advanced users].
+
+
+[Maintain separate directory silos for notes] See section 5.7
+
+[For developers or advanced users] See section 20
+
+
+5.10 Exclude certain keywords from being inferred
+─────────────────────────────────────────────────
+
+ The user option `denote-excluded-keywords-regexp' omits keywords that
+ match a regular expression from the list of inferred keywords.
+
+ Keywords are inferred from file names and provided at relevant prompts
+ as completion candidates when the user option `denote-infer-keywords'
+ is non-nil.
+
+ The match is performed with `string-match-p'.
+
+
+5.11 Create a controlled vocabulary for keywords
+────────────────────────────────────────────────
+
+ Denote has two ways to know about keywords: the predefined list of
+ strings specified in the user option `denote-known-keywords' as well
+ as all the keywords it finds in the files of the `denote-directory'
+ when the user option `denote-infer-keywords' is set to a non-nil value
+ ([Exclude certain keywords from being inferred]).
+
+ While this is a viable setup, users may prefer to implement a
+ “controlled vocabulary”. This is a predefined set of keywords whose
+ purpose is to avoid the creation of overly specific or inconsistent
+ keywords.
+
+ To establish such a controlled vocabulary, users need only have
+ something like this in their configuration:
+
+ ┌────
+ │ ;; Do not read keywords from files. The only source is the `denote-known-keywords'.
+ │ (setq denote-infer-keywords nil)
+ │
+ │ ;; Define the list of keywords. Each keyword is a string.
+ │ (setq denote-known-keywords (list "politics" "economics" "emacs" "philosophy"))
+ └────
+
+
+[Exclude certain keywords from being inferred] See section 5.10
+
+
+5.12 Use Denote commands from the menu bar or context menu
+──────────────────────────────────────────────────────────
+
+ Denote registers a submenu for the `menu-bar-mode'. Users will find
+ the entry called “Denote”. From there they can use their pointer to
+ select a command. For a sample of how this looks, read the
+ development log:
+ <https://protesilaos.com/codelog/2023-03-31-emacs-denote-menu/>.
+
+ The command `denote-menu-bar-mode' toggles the presentation of the
+ menu. It is enabled by default.
+
+ Emacs also provides support for operations through a context menu.
+ This is typically the set of actions that are made available via a
+ right mouse click. Users who enable `context-menu-mode' can register
+ the Denote entry for it by adding the following to their configuration
+ file:
+
+ ┌────
+ │ (add-hook 'context-menu-functions #'denote-context-menu)
+ └────
+
+
+6 Renaming files
+════════════════
+
+ Denote provides commands to rename files and update their front matter
+ where relevant. For Denote to work, only the file name needs to be in
+ order, by following our naming conventions ([The file-naming scheme]).
+ The linking mechanism, in particular, needs just the identifier in the
+ file name ([Linking notes]).
+
+ We write front matter in notes for the user’s convenience and for
+ other tools to make use of that information (e.g. Org’s export
+ mechanism). The renaming mechanism takes care to keep this data in
+ sync with the file name, when the user performs a change.
+
+ Renaming is useful for managing existing files created with Denote,
+ but also for converting older text files to Denote notes. Denote’s
+ file-naming scheme is not specific to notes or text files: it is
+ relevant for all sorts of items, such as multimedia and PDFs that form
+ part of the user’s longer-term storage. While Denote does not manage
+ such files (e.g. doesn’t create links to them), it already has all the
+ mechanisms to facilitate the task of renaming them.
+
+ All renaming commands run the `denote-after-rename-file-hook' after a
+ succesful operation ([Access the data of the latest note]). They also
+ construct the file name in accordance with the user option
+ `denote-file-name-components-order' ([Change the order of file name
+ components]).
+
+ Apart from renaming files, Denote can also rename only the buffer.
+ The idea is that the underlying file name is correct but it can be
+ easier to use shorter buffer names when displaying them on the mode
+ line or switching between then with commands like `switch-to-buffer'.
+
+ [Automatically rename Denote buffers].
+
+ [Find duplicate identifiers and put them in a Dired buffer].
+
+
+[The file-naming scheme] See section 7
+
+[Linking notes] See section 9
+
+[Access the data of the latest note] See section 19.1
+
+[Change the order of file name components] See section 7.1
+
+[Automatically rename Denote buffers] See section 12
+
+[Find duplicate identifiers and put them in a Dired buffer] See section
+6.10
+
+6.1 Rename a single file
+────────────────────────
+
+ The `denote-rename-file' command renames a file and updates existing
+ front matter if appropriate. It is possible to do the same with
+ multiple files ([Rename multiple files interactively]).
+
+ It always renames the file where it is located in the file system: it
+ never moves it to another directory.
+
+ If in Dired, it considers `FILE' to be the one at point, else it
+ prompts with minibuffer completion for one. When called from Lisp,
+ `FILE' is a file system path represented as a string.
+
+ If `FILE' has a Denote-compliant identifier, it retains it while
+ updating components of the file name referenced by the user option
+ `denote-prompts' ([The `denote-prompts' option]). By default, these
+ are the `TITLE' and `KEYWORDS'. The `SIGNATURE' is another one. When
+ called from Lisp, `TITLE' and `SIGNATURE' are strings, while
+ `KEYWORDS' is a list of strings.
+
+ If there is no identifier, `denote-rename-file' creates an identifier
+ based on the following conditions:
+
+ 1. If the `denote-prompts' includes an entry for date prompts, then it
+ prompts for `DATE' and takes its input to produce a new
+ identifier. For use in Lisp, `DATE' must conform with
+ `denote-valid-date-p'.
+
+ 2. If `DATE' is nil (e.g. when `denote-prompts' does not include a
+ date entry), it uses the file attributes to determine the last
+ modified date of `FILE' and formats it as an identifier.
+
+ 3. As a fallback, it derives an identifier from the current date and
+ time.
+
+ 4. At any rate, if the resulting identifier is not unique among the
+ files in the variable `denote-directory', it increments it such
+ that it becomes unique.
+
+ In interactive use, and assuming `denote-prompts' includes a title
+ entry, the `denote-rename-file' makes the `TITLE' prompt have
+ prefilled text in the minibuffer that consists of the current title of
+ `FILE'. The current title is either retrieved from the front matter
+ (such as the `#+title' in Org) or from the file name.
+
+ The command does the same for the `SIGNATURE' prompt, subject to
+ `denote-prompts', by prefilling the minibuffer with the current
+ signature of `FILE', if any.
+
+ Same principle for the `KEYWORDS' prompt: it converts the keywords in
+ the file name into a comma-separated string and prefills the
+ minibuffer with it (the `KEYWORDS' prompt accepts more than one
+ keywords, each separated by a comma, else the `crm-separator').
+
+ For all prompts, the `denote-rename-file' interprets an empty input as
+ an instruction to remove that file name component. For example, if a
+ `TITLE' prompt is available and `FILE' is
+ `20240211T093531--some-title__keyword1.org' then it renames `FILE' to
+ `20240211T093531__keyword1.org'.
+
+ In interactive use, if there is no entry for a file name component in
+ `denote-prompts', keep it as-is ([The `denote-prompts' option]).
+
+ When called from Lisp, the special symbol `keep-current’ can be used
+ for the TITLE, KEYWORDS, SIGNATURE and DATE parameters to keep them
+ as-is.
+
+ [ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the empty
+ minibuffer contents as they are, though popular packages like
+ `vertico' use the first available completion candidate instead. For
+ `vertico', the user must either move one up to select the prompt and
+ then type `RET' there with empty contents, or use the command
+ `vertico-exit-input' with empty contents. That Vertico command is
+ bound to `M-RET' as of this writing on 2024-02-13 08:08 +0200. ]
+
+ When renaming `FILE', the command reads its file type extension (like
+ `.org') and preserves it through the renaming process. Files that have
+ no extension are left without one.
+
+ As a final step, ask for confirmation, showing the difference between
+ old and new file names. Do not ask for confirmation if the user
+ option `denote-rename-confirmations' does not contain the symbol
+ `modify-file-name' ([The `denote-rename-confirmations' option]).
+
+ If `FILE' has front matter for `TITLE' and `KEYWORDS', ask to rewrite
+ their values in order to reflect the new input, unless
+ `denote-rename-confirmations' lacks `rewrite-front-matter'. When the
+ `denote-save-buffers' is nil (the default), do not save the underlying
+ buffer, thus giving the user the option to double-check the result,
+ such as by invoking the command `diff-buffer-with-file'. The rewrite
+ of the `TITLE' and `KEYWORDS' in the front matter should not affect
+ the rest of the front matter.
+
+ If the file does not have front matter but is among the supported file
+ types (per `denote-file-type'), add front matter to the top of it and
+ leave the buffer unsaved for further inspection ([Front matter]). Save
+ the buffer if `denote-save-buffers' is non-nil ([The
+ `denote-save-buffers' option]).
+
+ Construct the file name in accordance with the user option
+ `denote-file-name-components-order' ([Change the order of file name
+ components]).
+
+ Run the `denote-after-rename-file-hook' after renaming `FILE' ([Access
+ the data of the latest note]).
+
+ This command is intended to (i) rename Denote files, (ii) convert
+ existing supported file types to Denote notes, and (ii) rename
+ non-note files (e.g. `PDF') that can benefit from Denote’s file-naming
+ scheme.
+
+ For a version of this command that works with multiple files
+ one-by-one, use `denote-dired-rename-files' ([Rename multiple files
+ interactively]).
+
+
+[Rename multiple files interactively] See section 6.3
+
+[The `denote-prompts' option] See section 5.1.1
+
+[The `denote-rename-confirmations' option] See section 6.1.1
+
+[Front matter] See section 8
+
+[The `denote-save-buffers' option] See section 5.1.5
+
+[Change the order of file name components] See section 7.1
+
+[Access the data of the latest note] See section 19.1
+
+6.1.1 The `denote-rename-confirmations' option
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ The user option `denote-rename-confirmations' controls what kind of
+ confirmation renaming commands ask for ([Renaming files]). Its value
+ is a list of symbols.
+
+ The value is either nil, in which case no confirmation is ever
+ requested, or a list of symbols among the following:
+
+ • `modify-file-name' means that renaming commands will ask for
+ confirmation before modifying the file name.
+
+ • `rewrite-front-matter' means that renaming commands will ask for
+ confirmation before rewritting the front matter.
+
+ • `add-front-matter' means that renaming commands will ask for
+ confirmation before adding new front matter to the file.
+
+ The default behaviour of the `denote-rename-file' command (and others
+ like it) is to ask for an affirmative answer as a final step before
+ changing the file name and, where relevant, inserting or updating the
+ corresponding front matter.
+
+ Specialized commands that build on top of `denote-rename-file' (or
+ related) may internally bind this user option to a non-nil value in
+ order to perform their operation (e.g. `denote-dired-rename-files'
+ goes through each marked Dired file, prompting for the information to
+ use, but carries out the renaming without asking for confirmation
+ ([Rename multiple files interactively])).
+
+
+[Renaming files] See section 6
+
+[Rename multiple files interactively] See section 6.3
+
+
+6.2 Rename a single file based on its front matter
+──────────────────────────────────────────────────
+
+ In the previous section, we covered the more general mechanism of the
+ command `denote-rename-file' ([Rename a single file]). There is also
+ a way to have the same outcome by making Denote read the data in the
+ current file’s front matter and use it to construct/update the file
+ name. The command for this is
+ `denote-rename-file-using-front-matter'. It is only relevant for
+ files that (i) are among the supported file types, per
+ `denote-file-type', and (ii) have the requisite front matter in place.
+
+ Suppose you have an `.org' file with this front matter ([Front
+ matter]):
+
+ ┌────
+ │ #+title: My sample note file
+ │ #+date: [2022-08-05 Fri 13:10]
+ │ #+filetags: :testing:
+ │ #+identifier: 20220805T131044
+ └────
+
+ Its file name reflects this information:
+
+ ┌────
+ │ 20220805T131044--my-sample-note-file__testing.org
+ └────
+
+
+ You want to change its title and keywords manually, so you modify it
+ thus:
+
+ ┌────
+ │ #+title: My modified sample note file
+ │ #+date: [2022-08-05 Fri 13:10]
+ │ #+filetags: :testing:denote:emacs:
+ │ #+identifier: 20220805T131044
+ └────
+
+ At this stage, the file name still shows the old title and keywords.
+ You now invoke `denote-rename-file-using-front-matter' and it updates
+ the file name to:
+
+ ┌────
+ │ 20220805T131044--my-modified-sample-note-file__testing_denote_emacs.org
+ └────
+
+
+ By default, the renaming is subject to a “yes or no” prompt that shows
+ the old and new names, just so the user is certain about the change.
+ Though this can be modified ([The `denote-rename-confirmations'
+ option]).
+
+ The identifier of the file, if any, is never modified even if it is
+ edited in the front matter: Denote considers the file name to be the
+ source of truth in this case, to avoid potential breakage with typos
+ and the like.
+
+ This command constructs the file name in accordance with the user
+ option `denote-file-name-components-order' ([Change the order of file
+ name components]).
+
+
+[Rename a single file] See section 6.1
+
+[Front matter] See section 8
+
+[The `denote-rename-confirmations' option] See section 6.1.1
+
+[Change the order of file name components] See section 7.1
+
+
+6.3 Rename multiple files interactively
+───────────────────────────────────────
+
+ The command `denote-dired-rename-files' (alias
+ `denote-dired-rename-marked-files') renames the files that are marked
+ in a Dired buffer. Its behaviour is similar to the
+ `denote-rename-file' in that it prompts for a title, keywords, and
+ signature ([Rename a single file]). It does so over each marked file,
+ renaming one after the other.
+
+ Unlike `denote-rename-file', the command `denote-dired-rename-files'
+ does not ask to confirm the changes made to the files: it performs
+ them outright (same as setting `denote-rename-confirmations' to a nil
+ value). This is done to make it easier to rename multiple files
+ without having to confirm each step. For an even more direct approach,
+ check the command `denote-dired-rename-marked-files-with-keywords'.
+
+ • [Rename by writing only keywords]
+ • [Rename multiple files based on their front matter]
+
+
+[Rename a single file] See section 6.1
+
+[Rename by writing only keywords] See section 6.4
+
+[Rename multiple files based on their front matter] See section 6.5
+
+
+6.4 Rename multiple files at once by asking only for keywords
+─────────────────────────────────────────────────────────────
+
+ The `denote-dired-rename-marked-files-with-keywords' command renames
+ marked files in Dired to conform with our file-naming scheme. It does
+ so by writing keywords to them. Specifically, it does the following:
+
+ • retains the file’s existing name and makes it the `TITLE' field, per
+ Denote’s file-naming scheme;
+
+ • sluggifies the `TITLE' and adjusts its letter casing, according to
+ our conventions;
+
+ • prepends an identifier to the `TITLE', if one is missing;
+
+ • preserves the file’s extension, if any;
+
+ • prompts once for `KEYWORDS' and applies the user’s input to the
+ corresponding field in the file name, rewriting any keywords that
+ may exist while removing keywords that do exist if `KEYWORDS' is
+ empty;
+
+ • adds or rewrites existing front matter to the underlying file, if it
+ is recognized as a Denote note (per the `denote-file-type' user
+ option), such that it includes the new keywords.
+
+ [ Note that the affected buffers are not saved, unless the user option
+ `denote-rename-no-confirm' is non-nil. Users can thus check them to
+ confirm that the new front matter does not cause any problems (e.g.
+ with the `diff-buffer-with-file' command). Multiple buffers can be
+ saved in one go with the command `save-some-buffers' (read its doc
+ string). ]
+
+ Construct the file name in accordance with the user option
+ `denote-file-name-components-order' ([Change the order of file name
+ components]).
+
+ Run the `denote-after-rename-file-hook' after the renaming is done.
+
+ For more specialized versions of this command that only add or remove
+ keywords, use `denote-dired-rename-marked-files-add-keywords' and
+ `denote-dired-rename-marked-files-remove-keywords', respectively.
+
+
+[Change the order of file name components] See section 7.1
+
+
+6.5 Rename multiple files based on their front matter
+─────────────────────────────────────────────────────
+
+ As already noted, Denote can rename a file based on the data in its
+ front matter ([Rename a single file based on its front matter]). The
+ command `denote-dired-rename-marked-files-using-front-matter' extends
+ this principle to a batch operation which applies to all marked files
+ in Dired.
+
+ Marked files must count as notes for the purposes of Denote, which
+ means that they at least have an identifier in their file name and use
+ a supported file type, per `denote-file-type'. Files that do not meet
+ this criterion are ignored, because Denote cannot know if they have
+ front matter and what that may be. For such files, it is still
+ possible to rename them interactively ([Rename multiple files
+ interactively]).
+
+
+[Rename a single file based on its front matter] See section 6.2
+
+[Rename multiple files interactively] See section 6.3
+
+
+6.6 Rename a file by changing only its file type
+────────────────────────────────────────────────
+
+ The command `denote-change-file-type-and-front-matter' provides the
+ convenience of converting a note taken in one file type, say, `.txt'
+ into another like `.org'. It presents a choice among the
+ `denote-file-type' options.
+
+ The conversion does NOT modify the existing front matter. Instead, it
+ prepends new front matter to the top of the file. We do this as a
+ safety precaution since the user can, in principle, add arbitrary
+ extras to their front matter that we would not want to touch.
+
+ If in Dired, `denote-change-file-type-and-front-matter' operates on
+ the file at point, else the current file, else it prompts with
+ minibuffer completion for one.
+
+ The title of the file is retrieved from a line starting with a title
+ field in the file’s front matter, depending on the previous file type
+ (e.g. `#+title' for Org). The same process applies for keywords.
+
+ As a final step, the command asks for confirmation, showing the
+ difference between old and new file names.
+
+ This command constructs the file name in accordance with the user
+ option `denote-file-name-components-order' ([Change the order of file
+ name components]).
+
+
+[Change the order of file name components] See section 7.1
+
+
+6.7 Rename a file by adding or removing a title interactively
+─────────────────────────────────────────────────────────────
+
+ The command `denote-rename-file-title' streamlines the process of
+ interactively adding or removing a title to/from a file, while
+ changing its file name accordingly. It asks for a title using the
+ familiar minibuffer prompt ([Standard note creation]). It then renames
+ the file. The command respect the values of
+ `denote-rename-confirmations' and `denote-save-buffers':
+
+ • [The `denote-rename-confirmations' option].
+ • [The `denote-save-buffers' option].
+
+ Technically, `denote-rename-file-title' is a wrapper for
+ `denote-rename-file', doing all the things that does ([Rename a single
+ file]).
+
+ Concretely, this command can add or remove a title in one go. It does
+ it by prepopulating the minibuffer prompt with the existing
+ title. Users can then modify it. An empty input means to remove the
+ title altogether ([The file-naming scheme]).
+
+ [ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the empty
+ minibuffer contents as they are, though popular packages like
+ `vertico' use the first available completion candidate instead. For
+ `vertico', the user must either move one up to select the prompt and
+ then type `RET' there with empty contents, or use the command
+ `vertico-exit-input' with empty contents. That Vertico command is
+ bound to `M-RET' as of this writing on 2024-06-30 10:37 +0300. ]
+
+
+[Standard note creation] See section 5.1
+
+[The `denote-rename-confirmations' option] See section 6.1.1
+
+[The `denote-save-buffers' option] See section 5.1.5
+
+[Rename a single file] See section 6.1
+
+[The file-naming scheme] See section 7
+
+
+6.8 Rename a file by adding or removing keywords interactively
+──────────────────────────────────────────────────────────────
+
+ The command `denote-rename-file-keywords' streamlines the process of
+ interactively adding or removing keywords to a file, while changing
+ its file name and front matter accordingly. It asks for keywords using
+ the familiar minibuffer prompt ([Standard note creation]). It then
+ renames the file ([Rename a single file based on its front matter]).
+ The command respect the values of `denote-rename-confirmations' and
+ `denote-save-buffers':
+
+ • [The `denote-rename-confirmations' option].
+ • [The `denote-save-buffers' option].
+
+ Technically, `denote-rename-file-keywords' is a wrapper for
+ `denote-rename-file', doing all the things that does ([Rename a single
+ file]).
+
+ Concretely, this command can add or remove keywords in one go. It does
+ it by prepopulating the minibuffer prompt with the existing keywords.
+ Users can then use the `crm-separator' (normally a comma), to write
+ new keywords or edit what is in the prompt to rewrite them
+ accordingly. An empty input means to remove all keywords ([The
+ file-naming scheme]).
+
+ [ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the empty
+ minibuffer contents as they are, though popular packages like
+ `vertico' use the first available completion candidate instead. For
+ `vertico', the user must either move one up to select the prompt and
+ then type `RET' there with empty contents, or use the command
+ `vertico-exit-input' with empty contents. That Vertico command is
+ bound to `M-RET' as of this writing on 2024-06-30 10:37 +0300. ]
+
+
+[Standard note creation] See section 5.1
+
+[Rename a single file based on its front matter] See section 6.2
+
+[The `denote-rename-confirmations' option] See section 6.1.1
+
+[The `denote-save-buffers' option] See section 5.1.5
+
+[Rename a single file] See section 6.1
+
+[The file-naming scheme] See section 7
+
+
+6.9 Rename a file by adding or removing a signature interactively
+─────────────────────────────────────────────────────────────────
+
+ The command `denote-rename-file-signature' streamlines the process of
+ interactively adding or removing a signature to/from a file, while
+ changing its file name accordingly. It asks for a signature using the
+ familiar minibuffer prompt ([Standard note creation]). It then renames
+ the file. The command respect the values of
+ `denote-rename-confirmations' and `denote-save-buffers':
+
+ • [The `denote-rename-confirmations' option].
+ • [The `denote-save-buffers' option].
+
+ Technically, `denote-rename-file-signature' is a wrapper for
+ `denote-rename-file', doing all the things that does ([Rename a single
+ file]).
+
+ Concretely, this command can add or remove a signature in one go. It
+ does it by prepopulating the minibuffer prompt with the existing
+ signature. Users can then modify it. An empty input means to remove
+ the signature altogether ([The file-naming scheme]).
+
+ [ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the empty
+ minibuffer contents as they are, though popular packages like
+ `vertico' use the first available completion candidate instead. For
+ `vertico', the user must either move one up to select the prompt and
+ then type `RET' there with empty contents, or use the command
+ `vertico-exit-input' with empty contents. That Vertico command is
+ bound to `M-RET' as of this writing on 2024-06-30 10:37 +0300. ]
+
+
+[Standard note creation] See section 5.1
+
+[The `denote-rename-confirmations' option] See section 6.1.1
+
+[The `denote-save-buffers' option] See section 5.1.5
+
+[Rename a single file] See section 6.1
+
+[The file-naming scheme] See section 7
+
+
+6.10 Find duplicate identifiers and put them in a Dired buffer
+──────────────────────────────────────────────────────────────
+
+ Denote takes care to create unique identifiers, though its mechanism
+ relies on reading the existing identifiers in the `denote-directory'
+ or the current directory. When we are renaming files across different
+ directories, there is a small chance that some files have the same
+ attributes and are thus assigned identical identifiers. If those files
+ ever make it into a consolidated `denote-directory', we will have
+ duplicates, which break the linking mechanism.
+
+ As this is an edge case, we do not include any code to address it in
+ the Denote code base. Though here is a way to find duplicate
+ identifiers inside the current directory:
+
+ ┌────
+ │ (defun my-denote--get-files-in-dir (directory)
+ │ "Return file names in DIRECTORY."
+ │ (directory-files directory :full-paths directory-files-no-dot-files-regexp))
+ │
+ │ (defun my-denote--same-identifier-p (file1 file2)
+ │ "Return non-nil if FILE1 and FILE2 have the same identifier."
+ │ (let ((id1 (denote-retrieve-filename-identifier file1))
+ │ (id2 (denote-retrieve-filename-identifier file2)))
+ │ (equal id1 id2)))
+ │
+ │ (defun my-denote-find-duplicate-identifiers (directory)
+ │ "Find all files in DIRECTORY that need a new identifier."
+ │ (let* ((ids (my-denote--get-files-in-dir directory))
+ │ (unique-ids (seq-uniq ids #'my-denote--same-identifier-p)))
+ │ (seq-difference ids unique-ids #'equal)))
+ │
+ │ (defun my-denote-dired-show-duplicate-identifiers (directory)
+ │ "Put duplicate identifiers from DIRECTORY in a dedicated Dired buffer."
+ │ (interactive
+ │ (list
+ │ (read-directory-name "Select DIRECTORY to check for duplicate identifiers: " default-directory)))
+ │ (if-let* ((duplicates (my-denote-find-duplicate-identifiers directory)))
+ │ (dired (cons (format "Denote duplicate identifiers" directory) duplicates))
+ │ (message "No duplicates identifiers in `%s'" directory)))
+ └────
+
+ Evaluate this code and then call the command
+ `my-denote-dired-show-duplicate-identifiers'. If there are
+ duplicates, it will put them in a dedicated Dired buffer. From there,
+ you can view the file contents as usual, and manually edit the
+ identifiers as you see fit (e.g. edit them one by one, or change to
+ the writable Dired and record a keyboard macro that makes use of a
+ counter to increment by 1—contact me if you need any help).
+
+
+6.11 Faces used by rename commands
+──────────────────────────────────
+
+ These are the faces used by the various Denote rename commands to
+ style or highlight the old/new/current file shown in the relevant
+ minibuffer prompts:
+
+ • `denote-faces-prompt-current-name'
+ • `denote-faces-prompt-new-name'
+ • `denote-faces-prompt-old-name'
+
+
+7 The file-naming scheme
+════════════════════════
+
+ 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 by default ([Points
+ of entry]):
+
+ ┌────
+ │ DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION
+ └────
+
+
+ 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 and, as such, is also known as the file’s ID or identifier.
+
+ File names can include an arbitrary string of alphanumeric characters
+ in the `SIGNATURE' field. Signatures have no clearly defined purpose
+ and are up to the user to define. They can serve as special labels,
+ such as `part1' and `part2' of a large file, or as priority indicators
+ like `a', `b', `c', or even context/scope specifiers like `home' and
+ `work'. Another use-case is to write sequences of thoughts, such that
+ notes form a hierarchy, something we support with the optional and
+ comprehensive extension `denote-sequence.el' ([Write sequence notes or
+ “folgezettel”]). Signatures are an optional extension to Denote’s
+ file-naming scheme. In the simplest form, 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' option]).
+
+ The `TITLE' field is the title of the note, as provided by the user.
+ It automatically gets downcased by default and is also hyphenated
+ ([Sluggification of file name components]). An entry about “Economics
+ in the Euro Area” produces an `economics-in-the-euro-area' string for
+ the `TITLE' of the file name.
+
+ The `KEYWORDS' field consists of one or more entries demarcated by an
+ underscore (the separator is inserted automatically). Each keyword is
+ a string provided by the user at the relevant prompt which broadly
+ describes the contents of the entry.
+
+ Each of the keywords is a single word, with multiple keywords
+ providing the multi-dimensionality needed for advanced searches
+ through Denote files. Users who need to compose a keyword out of
+ multiple words such as camelCase/CamelCase and are encouraged to use
+ the `denote-file-name-slug-functions' user option accordingly
+ ([Sluggification of file name components]).
+
+ The `EXTENSION' is the file type. By default, it is `.org'
+ (`org-mode') though the user option `denote-file-type' provides
+ support for Markdown with YAML or TOML variants (`.md' which runs
+ `markdown-mode') and plain text (`.txt' via `text-mode'). Consult its
+ doc string for the minutiae. While files end in the `.org' extension
+ by default, the Denote code base does not actually depend on org.el
+ and/or its accoutrements.
+
+ Examples:
+
+ ┌────
+ │ 20220610T043241--initial-thoughts-on-the-zettelkasten-method__notetaking.org
+ │ 20220610T062201--define-custom-org-hyperlink-type__denote_emacs_package.md
+ │ 20220610T162327--on-hierarchy-and-taxis__notetaking_philosophy.txt
+ └────
+
+
+ The different field separators, namely `--' and `__' introduce an
+ efficient way to anchor searches (such as with Emacs commands like
+ `isearch' or from the command-line with `find' and related). A query
+ for `_word' always matches a keyword, while a regexp in the form of,
+ say, `"\\([0-9T]+?\\)--\\(.*?\\)_"' captures the date in group `\1'
+ and the title in `\2' (test any regular expression in the current
+ buffer by invoking `M-x re-builder').
+
+ [Features of the file-naming scheme for searching or filtering].
+
+ 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, which is carefully
+ considered and works well ([Change the order of file name
+ components]).
+
+ 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. The Denote file-naming scheme is, in essence, an effective,
+ low-tech invention.
+
+
+[Points of entry] See section 5
+
+[Write sequence notes or “folgezettel”] See section 18.2
+
+[The `denote-prompts' option] See section 5.1.1
+
+[Sluggification of file name components] See section 7.2
+
+[Features of the file-naming scheme for searching or filtering] See
+section 7.4
+
+[Change the order of file name components] See section 7.1
+
+7.1 Change the order of file name components
+────────────────────────────────────────────
+
+ Our standard file-naming scheme prescribes a specific order for the
+ file name components ([The file-naming scheme]). Though we provide the
+ user option `denote-file-name-components-order' to let the user
+ reorder them as they see fit.
+
+ The value of this user option is a list of the following symbols:
+
+ • `identifier': This is the combination of the date and time. When it
+ is the first on the list, it looks like `20240519T073456' and does
+ not have a component separator of its own due its unambiguous
+ format. When it is placed anywhere else in the file name, it is
+ prefixed with `@@', so it looks like `@@20240519T073456'.
+
+ • `signature': This is an arbitrary string that can be used to qualify
+ the file in some way, according to the user’s methodology (e.g. to
+ add a sequence to notes). The string is always prefixed with the
+ `==' to remain unambiguous.
+
+ • `title': This is an arbitrary string which describes the file. It is
+ always prefixed with `--' to be unambiguous.
+
+ • `keywords': This is a series of one or more words that succinctly
+ group the file. Multiple keywords are separated by an underscore
+ prefixed to each of them. The file name component is always prefixed
+ with `__'.
+
+ All four symbols must appear exactly once. Duplicates are ignored. Any
+ missing symbol is added automatically.
+
+ Some examples:
+
+ ┌────
+ │ (setq denote-file-name-components-order '(identifier signature title keywords))
+ │ ;; => 20240519T07345==hello--this-is-the-title__denote_testing.org
+ │
+ │ (setq denote-file-name-components-order '(signature identifier title keywords))
+ │ ;; => ==hello@@20240519T07345--this-is-the-title__denote_testing.org
+ │
+ │ (setq denote-file-name-components-order '(title signature identifier keywords))
+ │ ;; => --this-is-the-title==hello@@20240519T07345__denote_testing.org
+ │
+ │ (setq denote-file-name-components-order '(keywords title signature identifier))
+ │ ;; => __denote_testing--this-is-the-title==hello@@20240519T07345.org
+ └────
+
+ Also see how to configure the Denote prompts, which affect which
+ components are actually used in the order specified herein ([The
+ `denote-prompts' option]).
+
+ Before deciding on this, please consider the longer-term implications
+ of file names with varying patterns. Consistency makes things
+ predictable and thus easier to find. So pick one order and never touch
+ it again. When in doubt, leave the default file-naming scheme as-is.
+
+
+[The file-naming scheme] See section 7
+
+[The `denote-prompts' option] See section 5.1.1
+
+
+7.2 Sluggification of file name components
+──────────────────────────────────────────
+
+ Files names can contain any character that the file system
+ permits. Denote imposes a few additional restrictions:
+
+ ⁃ The tokens “`=", =__' and `--' are interpreted by Denote and should
+ appear only once.
+
+ ⁃ The dot character is not allowed in a note’s file name, except to
+ indicate the file type extension. Denote recognises two extensions
+ for encrypted files, like `.txt.gpg'.
+
+ By default, Denote enforces other rules to file names through the user
+ option `denote-file-name-slug-functions'. These rules are applied to
+ file names by default:
+
+ ⁃ What we count as “illegal characters” are removed.
+
+ ⁃ Input for a file title is hyphenated. The original value is
+ preserved in the note’s contents ([Front matter]).
+
+ ⁃ Spaces or other delimiters are removed from keywords, meaning that
+ `hello-world' becomes `helloworld'. This is because hyphens in
+ keywords do not work everywhere, such as in Org. Plus, hyphens are
+ word separators in the title and we want to keep distinct separators
+ for each component to make search easier and semantic ([Features of
+ the file-naming scheme for searching or filtering]).
+
+ ⁃ Signatures are like the above, but use the equals sign instead of
+ hyphens as a word separator.
+
+ ⁃ All file name components are downcased. Further down we document how
+ to deviate from these rules, such as to accept input of the form
+ `helloWorld' or `HelloWorld' verbatim.
+
+ Denote imposes these restrictions to enforce uniformity, which is
+ helpful long-term as it keeps all files with the same predictable
+ pattern. Too many permutations make searches more difficult to express
+ accurately and be confident that the matches cover all files.
+ Nevertheless, one of the principles of Denote is its flexibility or
+ hackability and so users can deviate from the aforementioned
+ ([User-defined sluggification of file name components]).
+
+
+[Front matter] See section 8
+
+[Features of the file-naming scheme for searching or filtering] See
+section 7.4
+
+[User-defined sluggification of file name components] See section 7.3
+
+
+7.3 User-defined sluggification of file name components
+───────────────────────────────────────────────────────
+
+ The user option `denote-file-name-slug-functions' controls the
+ sluggification of file name components ([Sluggification of file name
+ components]). The default method is outlined above and in the
+ previous section ([The file-naming scheme]).
+
+ The value of this user option is an alist where each element is a cons
+ cell of the form `(COMPONENT . METHOD)'. For example, here is the
+ default value:
+
+ ┌────
+ │ '((title . denote-sluggify-title)
+ │ (signature . denote-sluggify-signature)
+ │ (keyword . denote-sluggify-keyword))
+ └────
+
+ • The `COMPONENT' is an unquoted symbol among `title', `signature',
+ `keyword', which refers to the corresponding component of the file
+ name.
+
+ • The `METHOD' is a function to format the given component. This
+ function must take a string as its parameter and return the string
+ formatted for the file name. Note that even in the case of the
+ `keyword' component, the function receives one string representing a
+ single keyword and returns it formatted for the file name. Joining
+ the keywords together is handled internally by Denote.
+
+ One commonly requested deviation from the sluggification rules is to
+ not sluggify individual keywords, such that the user’s input is taken
+ as-is. This can be done as follows:
+
+ ┌────
+ │ (setq denote-file-name-slug-functions
+ │ '((title . denote-sluggify-title)
+ │ (keyword . identity)
+ │ (signature . denote-sluggify-signature)))
+ └────
+
+ The `identity' function simply returns the string it receives, thus
+ not altering it in any way.
+
+ Another approach is to keep the sluggification but not downcase the
+ string. We can do this by modifying the original functions used by
+ Denote. For example, we have this:
+
+ ┌────
+ │ ;; The original function for reference
+ │ (defun denote-sluggify-title (str)
+ │ "Make STR an appropriate slug for title."
+ │ (downcase
+ │ (denote-slug-hyphenate
+ │ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/=]*" "" str))))
+ │
+ │ ;; Our variant of the above, which does the same thing except from
+ │ ;; downcasing the string.
+ │ (defun my-denote-sluggify-title (str)
+ │ "Make STR an appropriate slug for title."
+ │ (denote-slug-hyphenate
+ │ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/=]*" "" str)))
+ │
+ │ ;; Now we use our function to sluggify titles without affecting their
+ │ ;; letter casing.
+ │ (setq denote-file-name-slug-functions
+ │ '((title . my-denote-sluggify-title) ; our function here
+ │ (signature . denote-sluggify-signature)
+ │ (keyword . denote-sluggify-keyword)))
+ └────
+
+ Follow this principle for all the sluggification functions ([Custom
+ sluggification to remove non-ASCII characters]).
+
+ To access the source code, use either of the following built-in
+ methods:
+
+ 1. Call the command `find-library' and search for `denote'. Then
+ navigate to the symbol you are searching for.
+
+ 2. Invoke the command `describe-symbol', search for the symbol you are
+ interested in, and from the resulting Help buffer either click on
+ the first link or do `M-x help-view-source' (bound to `s' in Help
+ buffers, by default).
+
+ Remember that deviating from the default file-naming scheme of Denote
+ will make things harder to use in the future, as files can/will have
+ permutations that create uncertainty. The sluggification scheme and
+ concomitant restrictions we impose by default are there for a very
+ good reason: they are the distillation of years of experience. Here we
+ give you what you wish, but bear in mind it may not be what you need.
+ You have been warned.
+
+
+[Sluggification of file name components] See section 7.2
+
+[The file-naming scheme] See section 7
+
+[Custom sluggification to remove non-ASCII characters] See section 7.3.1
+
+7.3.1 Custom sluggification to remove non-ASCII characters
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ A common use-case for Denote is to rename files such as videos
+ downloaded from the Internet. Sometimes, those files have Unicode
+ characters that (i) not all fonts support and (ii) create all sorts of
+ problems with pattern matching, such as when searching through file
+ names.
+
+ By default, Denote does not remove Unicode characters because users
+ may actually want them (e.g. Latin characters with accents). Those who
+ do, however, wish to keep everything limited to the ASCII range can
+ use the following in their Emacs configuration ([User-defined
+ sluggification of file name components]).
+
+ ┌────
+ │ ;; These are the same as the default Denote sluggification functions,
+ │ ;; except they remove all non-ASCII characters.
+ │ (defun my-denote-sluggify-title (str)
+ │ (downcase
+ │ (denote-slug-hyphenate
+ │ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/=]*" ""
+ │ (denote-slug-keep-only-ascii str)))))
+ │
+ │ (defun my-denote-sluggify-signature (str)
+ │ (downcase
+ │ (denote-slug-put-equals
+ │ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/-]*" ""
+ │ (denote-slug-keep-only-ascii str)))))
+ │
+ │ (defun my-denote-sluggify-keyword (str)
+ │ (downcase
+ │ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/_ =-]*" ""
+ │ (denote-slug-keep-only-ascii str))))
+ │
+ │ (defcustom denote-file-name-slug-functions
+ │ '((title . my-denote-sluggify-title)
+ │ (signature . my-denote-sluggify-signature)
+ │ (keyword . my-denote-sluggify-keyword)))
+ └────
+
+
+[User-defined sluggification of file name components] See section 7.3
+
+
+7.4 Features of the file-naming scheme for searching or filtering
+─────────────────────────────────────────────────────────────────
+
+ By default, file names have three fields and two sets of field
+ delimiters between them:
+
+ ┌────
+ │ DATE--TITLE__KEYWORDS.EXTENSION
+ └────
+
+
+ When a signature is present, this becomes:
+
+ ┌────
+ │ DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION
+ └────
+
+
+ Field delimiters practically serve as anchors for easier searching.
+ Consider this example:
+
+ ┌────
+ │ 20220621T062327==1a2--introduction-to-denote__denote_emacs.txt
+ └────
+
+
+ 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 can 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.
+
+
+8 Front matter
+══════════════
+
+ Notes have their own “front matter”. This is a block of data at the
+ top of the file, with no empty lines between the entries, which is
+ automatically generated at the creation of a new note. The front
+ matter includes the title and keywords (aka “tags” or “filetags”,
+ depending on the file type) which the user specified at the relevant
+ prompt, as well as the date and unique identifier, which are derived
+ automatically.
+
+ This is how it looks for Org mode (when `denote-file-type' is nil or
+ the `org' symbol):
+
+ ┌────
+ │ #+title: This is a sample note
+ │ #+date: [2022-06-30 Thu 16:09]
+ │ #+filetags: :denote:testing:
+ │ #+identifier: 20220630T160934
+ └────
+
+ For Markdown with YAML (`denote-file-type' has the `markdown-yaml'
+ value), the front matter looks like this:
+
+ ┌────
+ │ ---
+ │ title: "This is a sample note"
+ │ date: 2022-06-30T16:09:58+03:00
+ │ tags: ["denote", "testing"]
+ │ identifier: "20220630T160958"
+ │ ---
+ └────
+
+ For Markdown with TOML (`denote-file-type' has the `markdown-toml'
+ value), it is:
+
+ ┌────
+ │ +++
+ │ title = "This is a sample note"
+ │ date = 2022-06-30T16:10:13+03:00
+ │ tags = ["denote", "testing"]
+ │ identifier = "20220630T161013"
+ │ +++
+ └────
+
+ And for plain text (`denote-file-type' has the `text' value), we have
+ the following:
+
+ ┌────
+ │ title: This is a sample note
+ │ date: 2022-06-30
+ │ tags: denote testing
+ │ identifier: 20220630T161028
+ │ ---------------------------
+ └────
+
+ The format of the date in the front matter is controlled by the user
+ option `denote-date-format'. When nil, Denote uses a
+ file-type-specific format:
+
+ • For Org, an inactive timestamp is used, such as `[2022-06-30 Wed
+ 15:31]'.
+
+ • For Markdown, the RFC3339 standard is applied:
+ `2022-06-30T15:48:00+03:00'.
+
+ • For plain text, the format is that of ISO 8601: `2022-06-30'.
+
+ If the value is a string, ignore the above and use it instead. The
+ string must include format specifiers for the date. These are
+ described in the doc string of `format-time-string'..
+
+
+8.1 Change the front matter format
+──────────────────────────────────
+
+ Per Denote’s design principles, the code is hackable. All front
+ matter is stored in variables that are intended for public use. We do
+ not declare those as “user options” because (i) they expect the user
+ to have some degree of knowledge in Emacs Lisp and (ii) implement
+ custom code.
+
+ [ NOTE for tinkerers: code intended for internal use includes double
+ hyphens in its symbol. “Internal use” means that it can be changed
+ without warning and with no further reference in the change log. Do
+ not use any of it without understanding the consequences. ]
+
+ The variables which hold the front matter format are:
+
+ • `denote-org-front-matter'
+
+ • `denote-text-front-matter'
+
+ • `denote-toml-front-matter'
+
+ • `denote-yaml-front-matter'
+
+ These variables have a string value with specifiers that are used by
+ the `format' function. The formatting operation passes four arguments
+ which include the values of the given entries. If you are an advanced
+ user who wants to edit this variable to affect how front matter is
+ produced, consider using something like `%2$s' to control where the
+ Nth argument is placed.
+
+ When editing the value, make sure to:
+
+ 1. Not use empty lines inside the front matter block.
+
+ 2. Insert at least one empty line after the front matter block and do
+ not use any empty line before it.
+
+ These help with consistency and might prove useful if we ever need to
+ operate on the front matter as a whole.
+
+ With those granted, below are some examples. The approach is the same
+ for all variables.
+
+ ┌────
+ │ ;; Like the default, but upcase the entries
+ │ (setq denote-org-front-matter
+ │ "#+TITLE: %s
+ │ #+DATE: %s
+ │ #+FILETAGS: %s
+ │ #+IDENTIFIER: %s
+ │ \n")
+ │
+ │ ;; Change the order (notice the %N$s notation)
+ │ (setq denote-org-front-matter
+ │ "#+title: %1$s
+ │ #+filetags: %3$s
+ │ #+date: %2$s
+ │ #+identifier: %4$s
+ │ \n")
+ │
+ │ ;; Remove the date
+ │ (setq denote-org-front-matter
+ │ "#+title: %1$s
+ │ #+filetags: %3$s
+ │ #+identifier: %4$s
+ │ \n")
+ │
+ │ ;; Remove the date and the identifier
+ │ (setq denote-org-front-matter
+ │ "#+title: %1$s
+ │ #+filetags: %3$s
+ │ \n")
+ └────
+
+ Note that `setq' has a global effect: it affects the creation of all
+ new notes. Depending on the workflow, it may be preferrable to have a
+ custom command which `let' binds the different format. We shall not
+ provide examples at this point as this is a more advanced feature and
+ we are not yet sure what the user’s needs are. Please provide
+ feedback and we shall act accordingly.
+
+
+8.2 Regenerate front matter
+───────────────────────────
+
+ As part of version 4.0.0, the command `denote-add-front-matter' is
+ superseded by `denote-rename-file' and related ([Renaming
+ files]). Those commands will add missing front matter or rewrite the
+ modified lines of existing front matter.
+
+
+[Renaming files] See section 6
+
+
+9 Linking notes
+═══════════════
+
+ Denote offers several commands for linking between notes. Those use
+ the `denote:' hyperlink type. There are two types of links supported
+ by Denote:
+
+ Direct links
+ A direct link points to a file inside the
+ `denote-directory'. The link is constructed by using the
+ `denote:' prefix and the target file’s identifier ([The
+ file-naming scheme]). This looks like
+ `denote:20250328T075526'. The syntax of a link depends on the
+ file type. For example, in Org and plain text links look like
+ `[[denote:20250328T075526][The title of the target file]]',
+ while in Markdown they are written as `[The title of the target
+ file](denote:20250328T075526)'.
+
+ Query links
+ The `denote:' hyperlink type also supports special qualifiers
+ that change how the target of the link is interpreted. The
+ qualifier is a special token than tells Denote how to treat the
+ target of the link. It is written thus
+ `denote:TOKEN:QUERY'. There are two kinds of tokens:
+ `query-contents' and `query-filenames'. Those determine how the
+ query terms are used. As their names suggest, these two tokens
+ trigger a search in (i) the file contents of all readable files
+ or (ii) in the file names only. They are, in other words,
+ counterparts of the Unix `grep' and `find' programs,
+ respectively.
+
+ The following sections cover all the details ([Why are some Org links
+ opening outside Emacs?]).
+
+
+[The file-naming scheme] See section 7
+
+[Why are some Org links opening outside Emacs?] See section 25.9
+
+9.1 Add a single direct link using a file name prompt
+─────────────────────────────────────────────────────
+
+ The `denote-link' command (alias `denote-insert-link') inserts a link
+ at point to a file selected at the minibuffer prompt. Links are
+ formatted depending on the file type of the current note. In Org and
+ plain text buffers, links are formatted thus:
+ `[[denote:IDENTIFIER][DESCRIPTION]]'. While in Markdown they are
+ expressed as `[DESCRIPTION](denote:IDENTIFIER)'.
+
+ When `denote-link' is called with a prefix argument (`C-u' by
+ default), it formats links like `[[denote:IDENTIFIER]]', regardless of
+ file type ([Fontify links in non-Org buffers]). The user might prefer
+ its simplicity.
+
+ By default, the description of the link is determined thus:
+
+ • If the region is active, its text becomes the description of the
+ link. In other words, the region text becomes the link.
+ • If the region is active but has no text, the description is empty
+ and so the link is formatted the same way as if using the `C-u'
+ prefix argument.
+ • If there is no region active, the description consists of the target
+ file’s signature and title, using the former only if it is present.
+ The title is retrieved either from the front matter or the file
+ name.
+ • If the target file has no signature, the title is used.
+
+ To insert multiple such links at once, use the command
+ `denote-add-links' ([Insert links matching a regexp in their file
+ name]).
+
+ If you want to directly link to a single file whose contents match a
+ given query, then use the command `denote-link-to-file-with-contents'
+ ([Adding a direct link to a file whose contents include the given
+ query]).
+
+ Links are styled with the `denote-faces-link' face, which looks
+ exactly like an ordinary link by default.
+
+ [ We optionally support direct links to a file followed by an extra
+ target to an Org headings ([The `denote-org-store-link-to-heading'
+ user option]). Other file types do not have the features of Org, so
+ we cannot generalise this. ]
+
+
+[Fontify links in non-Org buffers] See section 9.14
+
+[Insert links matching a regexp in their file name] See section 9.4
+
+[Adding a direct link to a file whose contents include the given query]
+See section 9.2
+
+[The `denote-org-store-link-to-heading' user option] See section 9.7
+
+
+9.2 Add a direct link to a file whose contents include the given query
+──────────────────────────────────────────────────────────────────────
+
+ The `denote-link' command that we covered before prompts to select a
+ file among those in the `denote-directory' ([Adding a single direct
+ link using a file name prompt]). The match is done against the file’s
+ name. Users may, however, be interested to create a link to a file
+ whose contents include some text, regardless of how the file name is
+ called. To this end, the command `denote-link-to-file-with-contents',
+ (i) prompts for a query which is a plain string or regular expression,
+ (ii) if there are matching files, asks to select one among them, and
+ (iii) inserts the direct link at point.
+
+ When called with an optional prefix argument (`C-u' by default), the
+ command `denote-link-to-file-with-contents' creates a link that does
+ not include a description for the target file: it just has the file’s
+ identifier (same as with `denote-link').
+
+ The command `denote-link-to-file-with-contents' is the counterpart of
+ `denote-link-to-all-files-with-contents' ([Insert links to all files
+ matching a query in their contents]).
+
+
+[Adding a single direct link using a file name prompt] See section 9.1
+
+[Insert links to all files matching a query in their contents] See
+section 9.5
+
+
+9.3 Add a query link
+────────────────────
+
+ As noted in the introduction to this section of the manual, the
+ `denote:' hyperlink type supports query links ([Linking
+ notes]). Unlike direct links, they do not point to any given
+ file. Instead, they trigger a search, whose results are displayed in a
+ separate buffer.
+
+ Query links are expressed as `denote:TOKEN:QUERY', where `TOKEN' is
+ either `query-contents' or `query-filenames', while `QUERY' is a
+ string or Emacs regular expression to search for.
+
+ The exact syntax of a query link depends on the file type. In Org and
+ plain text buffers, links are of the form
+ `[[denote:TOKEN:QUERY][QUERY]]'. In Markdown, they are formatted as
+ `[QUERY](denote:TOKEN:QUERY)'. In all cases, the description of the
+ link is the query text itself.
+
+ The command `denote-query-contents-link' inserts a link at point that
+ triggers a search in the file contents of all readable documents in
+ the `denote-directory' ([Interact with the links buffer]). This is the
+ equivalent of the Unix `grep' command and uses the built-in Emacs Xref
+ interface ([Speed up backlinks’ or query links’ buffer creation?]).
+ Matches are displayed in a separate buffer, highlighting the exact
+ text while showing its context.
+
+ The command `denote-query-filenames-link' creates a link at point that
+ initiates a search across file names in the `denote-directory'. This
+ is the equivalent of the Unix `find' command. Results are placed in a
+ Dired buffer ([Display filtered and sorted files with
+ `denote-sort-dired']).
+
+ The user option `denote-query-links-display-buffer-action' controls
+ the placement of query link buffers. By default, they are designed to
+ appear below the current window.
+
+ Query links are styled with the `denote-faces-query-link' face, which
+ looks a bit different that `denote-faces-link' (though this depends on
+ the active theme).
+
+
+[Linking notes] See section 9
+
+[Interact with the links buffer] See section 16
+
+[Speed up backlinks’ or query links’ buffer creation?] See section 25.10
+
+[Display filtered and sorted files with `denote-sort-dired'] See section
+14
+
+
+9.4 Insert links to all files matching a query in their file name
+─────────────────────────────────────────────────────────────────
+
+ The command `denote-add-links' adds links at point to all file names
+ in the `denote-directory' that match a regular expression or plain
+ string. This is similar to the `denote-link' command, which
+ establishes a direct link to a specified file ([Adding a single direct
+ link]). Links to files whose names match the given search terms are
+ inserted as a typographic list, such as:
+
+ ┌────
+ │ - link1
+ │ - link2
+ │ - link3
+ └────
+
+ Each link is formatted according to the file type of the current note,
+ as explained further above about the `denote-link' command. The
+ current note is excluded from the matching entries (adding a link to
+ itself is pointless).
+
+ When called with a prefix argument (`C-u') `denote-add-links' will
+ format all links as `[[denote:IDENTIFIER]]', hence a typographic list:
+
+ ┌────
+ │ - [[denote:IDENTIFIER-1]]
+ │ - [[denote:IDENTIFIER-2]]
+ │ - [[denote:IDENTIFIER-3]]
+ └────
+
+ Same examples of a regular expression that can be used with this
+ command:
+
+ • `journal' match all files which include `journal' anywhere in their
+ name.
+
+ • `_journal' match all files which include `journal' as a keyword.
+
+ • `^2022.*_journal' match all file names starting with `2022' and
+ including the keyword `journal'.
+
+ • `\.txt' match all files including `.txt'. In practical terms, this
+ only applies to the file extension, as Denote automatically removes
+ dots (and other characters) from the base file name.
+
+ If files are created with `denote-sort-keywords' as non-nil (the
+ default), then it is easy to write a regexp that includes multiple
+ keywords in alphabetic order:
+
+ • `_denote.*_package' match all files that include both the `denote'
+ and `package' keywords, in this order.
+
+ • `\(.*denote.*package.*\)\|\(.*package.*denote.*\)' is the same as
+ above, but out-of-order.
+
+ Remember that regexp constructs only need to be escaped once (like
+ `\|') when done interactively but twice when called from Lisp. What
+ we show above is for interactive usage.
+
+ Links are created only for files which qualify as a “note” for our
+ purposes ([Linking notes]).
+
+
+[Adding a single direct link] See section 9.1
+
+[Linking notes] See section 9
+
+
+9.5 Insert links to all files matching a query in their contents
+────────────────────────────────────────────────────────────────
+
+ The aforementioned `denote-add-links' command takes a query that
+ matches it against file names ([Insert links to all files matching a
+ query in their file name]). It then creates a typographic list
+ (bullet list) with direct links to all the matching files. Users who
+ wish to achieve the same result but have the query be matched against
+ file contents (not file names), can use the command
+ `denote-link-to-all-files-with-contents'.
+
+ The command `denote-link-to-all-files-with-contents' is the
+ counterpart of `denote-link-to-file-with-contents' ([Add a direct link
+ to a file whose contents include the given query]).
+
+
+[Insert links to all files matching a query in their file name] See
+section 9.4
+
+[Add a direct link to a file whose contents include the given query] See
+section 9.2
+
+
+9.6 The `denote-open-link-function' user option
+───────────────────────────────────────────────
+
+ The user option `denote-open-link-function' specifies the function
+ used by Denote to open the file of a link. The default value opens the
+ file in the other window. Another common value is the function
+ `find-file', which will open the file in the current window. Users may
+ also specify a function of their choosing.
+
+ Note that this is relevant in buffers other than Org mode because Org
+ has its own mechanism for how to open links (read the documentation of
+ the command `org-open-at-point').
+
+
+9.7 The `denote-org-store-link-to-heading' user option
+──────────────────────────────────────────────────────
+
+ The user option `denote-org-store-link-to-heading' determines whether
+ `org-store-link' links to the current Org heading.
+
+ [ Remember that what `org-store-link' does is merely collect a link.
+ To actually insert it, use the command `org-insert-link'. Note that
+ `org-capture' uses `org-store-link' internally when it needs to
+ store a link. ]
+
+ When the value is nil, the Denote handler for `org-store-link'
+ produces links only to the current file (by using the file’s
+ identifier). For example:
+
+ ┌────
+ │ [[denote:20240118T060608][Some test]]
+ └────
+
+
+ If the value is `context', the link consists of the file’s identifier
+ and the text of the current heading, like this:
+
+ ┌────
+ │ [[denote:20240118T060608::*Heading text][Some test::Heading text]].
+ └────
+
+
+ However, if there already exists a `CUSTOM_ID' property for the
+ current heading, this is always given priority and is used instead of
+ the context.
+
+ If the value is `id' or, for backward-compatibility, any other non-nil
+ value, then Denote will use the standard Org mechanism of the
+ `CUSTOM_ID' property to create a unique link to the heading. If the
+ heading does not have a `CUSTOM_ID', it creates it and includes it in
+ its `PROPERTIES' drawer. If a `CUSTOM_ID' exists, it takes it as-is.
+ The result is like this:
+
+ ┌────
+ │ [[denote:20240118T060608::#h:eed0fb8e-4cc7-478f][Some test::Heading text]]
+ └────
+
+
+ The value of the `CUSTOM_ID' is determined by the Org user option
+ `org-id-method'. The sample shown above uses the default UUID
+ infrastructure (though I deleted a few characters to not get
+ complaints from the byte compiler about long lines in the doc
+ string…).
+
+ Note that this option does not affect how Org behaves with regard to
+ `org-id-link-to-org-use-id'. If that user option is set to create `ID'
+ properties, then those will be created by Org even if the Denote link
+ handler will take care to not use/store the `ID' value. Concretely,
+ users who never want `ID' properties under their headings should keep
+ `org-id-link-to-org-use-id' in its nil value.
+
+ Context links are easier to break than those with a `CUSTOM_ID' in
+ cases where either the heading text changes or there is another
+ heading that matches that text. The potential advantage of context
+ links is that they do not require a `PROPERTIES' drawer.
+
+ When visiting a link to a heading, Org opens the Denote file and then
+ navigates to that heading.
+
+ [ This feature only works in Org mode files, as other file types do
+ not have a linking mechanism that handles unique identifiers for
+ headings or other patterns to jump to. If `org-store-link' is
+ invoked in one such file, it captures only the Denote identifier of
+ the file, even if this user option is set to a non-nil value. ]
+
+
+9.8 Adding direct links to files matching contents
+──────────────────────────────────────────────────
+
+
+9.9 Insert links from marked files in Dired
+───────────────────────────────────────────
+
+ The command `denote-link-dired-marked-notes' is similar to
+ `denote-add-links' in that it inserts in the buffer a typographic list
+ of links to Denote notes ([Insert links matching a regexp]). Though
+ instead of reading a regular expression, it lets the user mark files
+ in Dired and link to them. This should be easier for users of all
+ skill levels, instead of having to write a potentially complex regular
+ expression.
+
+ If there are multiple buffers that visit a Denote note, this command
+ will ask to select one among them, using minibuffer completion. If
+ there is only one buffer, it will operate in it outright. If there
+ are no buffers, it will produce an error.
+
+ With optional `ID-ONLY' as a prefix argument (`C-u' by default), the
+ command inserts links with just the identifier, which is the same
+ principle as with `denote-link' and others ([Adding a single link]).
+
+ The command `denote-link-dired-marked-notes' is meant to be used from
+ a Dired buffer.
+
+ As always, links are created only for files which qualify as a “note”
+ for our purposes ([Linking notes]).
+
+ The `denote-dired-link-marked-notes' is an alias for
+ `denote-link-dired-marked-notes'.
+
+
+[Insert links matching a regexp] See section 9.4
+
+[Adding a single link] See section 9.1
+
+[Linking notes] See section 9
+
+
+9.10 Link to an existing note or create a new one
+─────────────────────────────────────────────────
+
+ In one’s note-taking workflow, there may come a point where they are
+ expounding on a certain topic but have an idea about another subject
+ they would like to link to ([Linking notes]). The user can always
+ rely on the other linking facilities we have covered herein to target
+ files that already exist. Though they may not know whether they
+ already have notes covering the subject or whether they would need to
+ write new ones. To this end, Denote provides two convenience
+ commands:
+
+ `denote-link-after-creating'
+ Create new note in the background and link to it directly.
+
+ Use `denote' interactively to produce the new note. Its doc
+ string or this manual explains which prompts will be used and
+ under what conditions ([Standard note creation]).
+
+ With optional `ID-ONLY' as a prefix argument (this is the `C-u'
+ key, by default) 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' ([Adding a single
+ link]).
+
+ IMPORTANT NOTE: Normally, `denote' does not save the buffer it
+ produces for the new note ([The
+ `denote-save-buffer-after-creation' option]). This is a safety
+ precaution to not write to disk unless the user wants it
+ (e.g. the user may choose to kill the buffer, thus cancelling
+ the creation of the note). However, for this command the
+ creation of the note happens in the background and the user may
+ miss the step of saving their buffer. We thus have to save the
+ buffer in order to (i) establish valid links, and (ii) retrieve
+ whatever front matter from the target file.
+
+ `denote-link-after-creating-with-command'
+ This command is like `denote-link-after-creating' except it
+ prompts for a note-creating command ([Points of entry]). Use
+ this to, for example, call `denote-signature' so that the newly
+ created note has a signature as part of its file name. Optional
+ `ID-ONLY' has the same meaning as in the command
+ `denote-link-after-creating'.
+
+ `denote-link-or-create'
+ Use `denote-link' on `TARGET' file, creating it if necessary.
+
+ If `TARGET' file does not exist, call
+ `denote-link-after-creating' which runs the `denote' command
+ interactively to create the file. The established link will
+ then be targeting that new file.
+
+ If `TARGET' file does not exist, add the user input that was
+ used to search for it to the history of the
+ `denote-file-prompt'. The user can then retrieve and possibly
+ further edit their last input, using it as the newly created
+ note’s actual title. At the `denote-file-prompt' type `M-p'
+ with the default key bindings, which calls
+ `previous-history-element'.
+
+ With optional `ID-ONLY' as a prefix argument create a link with
+ just the file’s identifier. This has the same meaning as in
+ `denote-link'.
+
+ This command has the alias
+ `denote-link-to-existing-or-new-note', which helps with
+ discoverability.
+
+ In all of the above, an optional prefix argument (`C-u' by default)
+ creates a link that consists of just the identifier. This has the
+ same meaning as in the regular `denote-link' command.
+
+ Denote provides similar functionality for opening an existing note or
+ creating a new one ([Open an existing note or create it if missing]).
+
+
+[Linking notes] See section 9
+
+[Standard note creation] See section 5.1
+
+[Adding a single link] See section 9.1
+
+[The `denote-save-buffer-after-creation' option] See section 5.1.5
+
+[Points of entry] See section 5
+
+[Open an existing note or create it if missing] See section 5.6
+
+
+9.11 The backlinks’ buffer
+──────────────────────────
+
+ [ Older versions of Denote had two types of formatting for the
+ backlinks’ buffer. As part of version `4.0.0', we only support the
+ standard Xref view which shows matches in their context. The user
+ option `denote-backlinks-show-context' is thus removed. ]
+
+ The command `denote-backlinks' (alias `denote-show-backlinks-buffer')
+ produces a bespoke buffer which displays backlinks to the current note
+ ([Interact with the links buffer]). A “backlink” is a link back to the
+ present entry. Backlinks can be generated for any file type that has a
+ Denote file-naming scheme, such as PDFs, images, and videos, as well
+ as the regular plain text files.
+
+ The backlinks’ buffer is, in essence, the equivalent of a Unix `grep'
+ command across the `denote-directory' ([Speed up backlinks’ buffer
+ creation?]). It groups matches by file name, while it displays the
+ line on which a link to the current file occurs together with its
+ context. It looks like this (plus the appropriate fontification):
+
+ ┌────
+ │ Backlinks to "On being honest" (20220614T130812)
+ │ ------------------------------------------------
+ │
+ │ 20220614T145606--let-this-glance-become-a-stare__journal.txt
+ │ 37: growing into it: [[denote:20220614T130812][On being honest]].
+ │ 64: As I said in [[denote:20220614T130812][On being honest]] I have never
+ │ 20220616T182958--feeling-butterflies-in-your-stomach__journal.txt
+ │ 62: indifference. In [[denote:20220614T130812][On being honest]] I alluded
+ └────
+
+ Note that the width of the lines in the context depends on the
+ underlying file. In the above example, the lines are split at the
+ `fill-column'. Long lines will show up just fine. Also note that the
+ built-in user option `xref-truncation-width' can truncate long lines
+ to a given maximum number of characters.
+
+ As with query links, the backlinking facility uses Emacs’ built-in
+ Xref infrastructure ([Adding a query link]). On some operating
+ systems, the user may need to add certain executables to the relevant
+ environment variable ([Why do I get “Search failed with status 1” when
+ I search for backlinks?]).
+
+ The placement of the backlinks’ buffer is subject to the user option
+ `denote-backlinks-display-buffer-action'. Due to the nature of the
+ underlying `display-buffer' mechanism, this inevitably is a relatively
+ advanced feature. By default, the backlinks’ buffer is displayed below
+ the current window.
+
+ Backlinks to the current file can also be visited by using the
+ minibuffer completion interface with the `denote-find-backlink'
+ command ([Visiting linked files via the minibuffer]).
+
+
+[Interact with the links buffer] See section 16
+
+[Speed up backlinks’ buffer creation?] See section 25.10
+
+[Adding a query link] See section 9.3
+
+[Why do I get “Search failed with status 1” when I search for
+backlinks?] See section 25.11
+
+[Visiting linked files via the minibuffer] See section 9.13
+
+
+9.12 Writing metanotes
+──────────────────────
+
+ A “metanote” is an entry that describes other entries who have
+ something in common. Writing metanotes can be part of a workflow
+ where the user periodically reviews their work in search of patterns
+ and deeper insights. For example, you might want to read your journal
+ entries from the past year to reflect on your experiences, evolution
+ as a person, and the like.
+
+ The commands `denote-add-links', `denote-link-dired-marked-notes' are
+ suited for this task.
+
+ [Insert links matching a regexp].
+
+ [Insert links from marked files in Dired].
+
+ You will create your metanote the way you use Denote ordinarily
+ (metanotes may have the `metanote' keyword, among others), write an
+ introduction or however you want to go about it, invoke the command
+ which inserts multiple links at once (see the above-cited nodes), and
+ continue writing.
+
+ Metanotes can serve as entry points to groupings of individual notes.
+ They are not the same as a filtered list of files, i.e. what you would
+ do in Dired or the minibuffer where you narrow the list of notes to a
+ given query. Metanotes contain the filtered list plus your thoughts
+ about it. The act of purposefully grouping notes together and
+ contemplating on their shared patterns is what adds value.
+
+ Your future self will appreciate metanotes for the function they serve
+ in encapsulating knowledge, while current you will be equipped with
+ the knowledge derived from the deliberate self-reflection.
+
+
+[Insert links matching a regexp] See section 9.4
+
+[Insert links from marked files in Dired] See section 9.9
+
+
+9.13 Visiting linked files via the minibuffer
+─────────────────────────────────────────────
+
+ Denote has a major-mode-agnostic mechanism to collect all linked file
+ references in the current buffer and return them as an appropriately
+ formatted list. This list can then be used in interactive commands.
+ The `denote-find-link' is such a command. It uses minibuffer
+ completion to visit a file that is linked to from the current note.
+ The candidates have the correct metadata, which is ideal for
+ integration with other standards-compliant tools ([Extending Denote]).
+ For instance, a package such as `marginalia' will display accurate
+ annotations, while the `embark' package will be able to work its magic
+ such as in exporting the list into a filtered Dired buffer (i.e. a
+ familiar Dired listing with only the files of the current minibuffer
+ session).
+
+ To visit backlinks to the current note via the minibuffer, use
+ `denote-find-backlink'. This is an alternative to placing backlinks
+ in a dedicated buffer ([The backlinks’ buffer]).
+
+
+[Extending Denote] See section 19
+
+[The backlinks’ buffer] See section 9.11
+
+
+9.14 Fontify links in non-Org buffers
+─────────────────────────────────────
+
+ Denote links are automatically fontified in Org buffers ([Adding a
+ single link]). This means that Org recognises the link and applies
+ the relevant properties to it to make it clickable/actionable. Other
+ major modes, such as `markdown-mode' (for `.md' files) or `text-mode'
+ (for `.txt' files) do not have this feature built into them. Users can
+ still get the same behaviour as with Org by activating the
+ `denote-fontify-links-mode'.
+
+ The `denote-fontify-links-mode' is a buffer-local minor mode. Users
+ can enable it automatically in plain text files that correspond to
+ denote notes with something like this:
+
+ ┌────
+ │ (add-hook 'text-mode-hook #'denote-fontify-links-mode-maybe)
+ └────
+
+ The `text-mode-hook' applies to all modes derived from `text-mode',
+ including `markdown-mode'. Though a more explicit setup does no harm:
+
+ ┌────
+ │ (add-hook 'markdown-mode-hook #'denote-fontify-links-mode-maybe)
+ └────
+
+ Because Org already recognises `denote:' links, the function
+ `denote-fontify-links-mode-maybe' will not enable the mode
+ `denote-fontify-links-mode' in Org buffers.
+
+ In files whose major mode is `markdown-mode', the default key binding
+ `C-c C-o' (which calls the command `markdown-follow-thing-at-point')
+ correctly resolves `denote:' links. Interested users can refer to the
+ function `denote-link-markdown-follow' for the implementation details.
+
+
+[Adding a single link] See section 9.1
+
+
+9.15 The `denote-link-description-format' to format link descriptions
+─────────────────────────────────────────────────────────────────────
+
+ The user option `denote-link-description-format' controls how the
+ command `denote-link' and related functions create a link description
+ by default.
+
+ The value can be either a function or a string. If it is a function,
+ it is called with one argument, the file, and should return a string
+ representing the link description.
+
+ The default is a function that returns the active region or the title
+ of the note (with the signature if present).
+
+ If the value is a string, it treats specially the following
+ specifiers:
+
+ • The `%t' is the Denote `TITLE' in the front matter or the file name.
+ • The `%T' is the Denote `TITLE' in the file name.
+ • The `%i' is the Denote `IDENTIFIER' of the file.
+ • The `%I' is the identifier converted to `DAYNAME, DAYNUM MONTHNUM
+ YEAR'.
+ • The `%d' is the same as `%i' (`DATE' mnemonic).
+ • The `%D' is a “do what I mean” which behaves the same as `%t' and if
+ that returns nothing, it falls back to `%I', then `%i'.
+ • The `%d' is the same as `%i' (`DATE' mnemonic).
+ • The `%s' is the Denote `SIGNATURE' of the file.
+ • The `%k' is the Denote `KEYWORDS' of the file.
+ • The `%%' is a literal percent sign.
+
+ In addition, the following flags are available for each of the
+ specifiers:
+
+ 0
+ Pad to the width, if given, with zeros instead of spaces.
+ -
+ Pad to the width, if given, on the right instead of the left.
+ <
+ Truncate to the width and precision, if given, on the left.
+ >
+ Truncate to the width and precision, if given, on the right.
+ ^
+ Convert to upper case.
+ _
+ Convert to lower case.
+
+ When combined all together, the above are written thus:
+
+ ┌────
+ │ %<flags><width><precision>SPECIFIER-CHARACTER
+ └────
+
+
+ Any other text in the string it taken as-is. Users may want, for
+ example, to include some text that makes Denote links stand out, such
+ as a `[D]' prefix.
+
+ If the region is active, its text is used as the link’s description.
+
+
+10 Choose which commands to prompt for
+══════════════════════════════════════
+
+ The user option `denote-commands-for-new-notes' specifies a list of
+ commands that are available at the `denote-command-prompt'. This
+ prompt is used by Denote commands that ask the user how to create a
+ new note, as described elsewhere in this manual:
+
+ • [Open an existing note or create it if missing]
+ • [Link to a note or create it if missing]
+
+ The default value includes all the basic file-creating commands
+ ([Points of entry]). Users may customise this value if (i) they only
+ want to see fewer options and/or (ii) wish to include their own custom
+ command in the list ([Write your own convenience commands]).
+
+
+[Open an existing note or create it if missing] See section 5.6
+
+[Link to a note or create it if missing] See section 9.10
+
+[Points of entry] See section 5
+
+[Write your own convenience commands] See section 5.1.4.1
+
+
+11 Fontification in Dired
+═════════════════════════
+
+ One of the upsides of Denote’s file-naming scheme is the predictable
+ pattern it establishes, which appears as a near-tabular presentation
+ in a listing of notes (i.e. in Dired). The `denote-dired-mode' can
+ help enhance this impression, by fontifying the components of the file
+ name to make the date (identifier) and keywords stand out.
+
+ There are two ways to set the mode. Either use it for all
+ directories, which probably is not needed:
+
+ ┌────
+ │ (add-hook 'dired-mode-hook #'denote-dired-mode)
+ └────
+
+ Or configure the user option `denote-dired-directories' and then set
+ up the function `denote-dired-mode-in-directories':
+
+ ┌────
+ │ ;; We use different ways to specify a path for demo purposes.
+ │ (setq denote-dired-directories
+ │ (list denote-directory
+ │ (thread-last denote-directory (expand-file-name "attachments"))
+ │ (expand-file-name "~/Documents/vlog")))
+ │
+ │ (add-hook 'dired-mode-hook #'denote-dired-mode-in-directories)
+ └────
+
+ The user option `denote-dired-directories-include-subdirectories'
+ specifies whether the `denote-dired-directories' also cover their
+ subdirectories. By default they do not. Set this option to `t' to
+ include subdirectories as well.
+
+ The faces we define for this purpose are:
+
+ ⁃ `denote-faces-date'
+ ⁃ `denote-faces-delimiter'
+ ⁃ `denote-faces-extension'
+ ⁃ `denote-faces-keywords'
+ • `denote-faces-signature'
+ ⁃ `denote-faces-subdirectory'
+ ⁃ `denote-faces-time'
+ ⁃ `denote-faces-title'
+
+ For more control, we also provide these:
+
+ #+vindex denote-faces-year +vindex denote-faces-month +vindex
+ #denote-faces-day +vindex denote-faces-hour +vindex
+ #denote-faces-minute +vindex denote-faces-second
+ ⁃ `denote-faces-year'
+ ⁃ `denote-faces-month'
+ ⁃ `denote-faces-day'
+ ⁃ `denote-faces-hour'
+ ⁃ `denote-faces-minute'
+ ⁃ `denote-faces-second'
+
+ For the time being, the `diredfl' package is not compatible with this
+ facility.
+
+ The `denote-dired-mode' does not only fontify note files that were
+ created by Denote: it covers every file name that follows our naming
+ conventions ([The file-naming scheme]). This is particularly useful
+ for scenaria where, say, one wants to organise their collection of
+ PDFs and multimedia in a systematic way (and, perhaps, use them as
+ attachments for the notes Denote produces if you are writing Org notes
+ and are using its standand attachments’ facility).
+
+
+[The file-naming scheme] See section 7
+
+
+12 Automatically rename Denote buffers
+══════════════════════════════════════
+
+ The minor mode `denote-rename-buffer-mode' provides the means to
+ automatically rename the buffer of a Denote file upon visiting the
+ file. This applies both to existing Denote files as well as new ones
+ ([Points of entry]). Enable the mode thus:
+
+ ┌────
+ │ (denote-rename-buffer-mode 1)
+ └────
+
+ Buffers are named by applying the function specified in the user
+ option `denote-rename-buffer-function'. The default function is
+ `denote-rename-buffer': it renames the buffer based on the template
+ set in the user option `denote-rename-buffer-format'. By default, the
+ formatting template targets only the `TITLE' component of the file
+ name ([The file-naming scheme]). Other fields are explained elsewhere
+ in this manual ([The denote-rename-buffer-format]).
+
+ Note that renaming a buffer is not the same as renaming a file
+ ([Renaming files]). The former is just for convenience inside of
+ Emacs. Whereas the latter is for writing changes to disk, making them
+ available to all programs.
+
+
+[Points of entry] See section 5
+
+[The file-naming scheme] See section 7
+
+[The denote-rename-buffer-format] See section 12.1
+
+[Renaming files] See section 6
+
+12.1 The `denote-rename-buffer-format' option
+─────────────────────────────────────────────
+
+ The user option `denote-rename-buffer-format' controls how the
+ function `denote-rename-buffer' chooses the name of the
+ buffer-to-be-renamed.
+
+ The value of this user option is a string. The following specifiers
+ are placeholders for Denote file name components ([The file-naming
+ scheme]):
+
+ • The `%t' is the Denote `TITLE' in the front matter or the file name.
+ • The `%T' is the Denote `TITLE' in the file name.
+ • The `%i' is the Denote `IDENTIFIER' of the file.
+ • The `%I' is the identifier converted to `DAYNAME, DAYNUM MONTHNUM
+ YEAR'.
+ • The `%d' is the same as `%i' (`DATE' mnemonic).
+ • The `%D' is a “do what I mean” which behaves the same as `%t' and if
+ that returns nothing, it falls back to `%I', then `%i'.
+ • The `%s' is the Denote `SIGNATURE' of the file.
+ • The `%k' is the Denote `KEYWORDS' of the file.
+ • The `%b' is an indicator of whether or not the file has backlinks
+ pointing to it. The indicator string is defined in the user option
+ `denote-rename-buffer-backlinks-indicator', alias
+ `denote-buffer-has-backlinks-string'.
+ • The `%%' is a literal percent sign.
+
+ In addition, the following flags are available for each of the
+ specifiers:
+
+ `0'
+ Pad to the width, if given, with zeros instead of spaces.
+ `-'
+ Pad to the width, if given, on the right instead of the left.
+ `<'
+ Truncate to the width and precision, if given, on the left.
+ `>'
+ Truncate to the width and precision, if given, on the right.
+ `^'
+ Convert to upper case.
+ `_'
+ Convert to lower case.
+
+ When combined all together, the above are written thus:
+
+ ┌────
+ │ %<flags><width><precision>SPECIFIER-CHARACTER
+ └────
+
+
+ Any other string it taken as-is. Users may want, for example, to
+ include some text that makes Denote buffers stand out, such as a `[D]'
+ prefix. Examples:
+
+ ┌────
+ │ ;; The following is the default value. Use a literal [D] prefix,
+ │ ;; followed by the title and then the backlinks indicator. If there
+ │ ;; is no title, use the identifier in its human-readable date
+ │ ;; representation, and if that is not possible, use the identifier
+ │ ;; as-is.
+ │ (setq denote-rename-buffer-format "[D] %D%b")
+ │
+ │ ;; Customize what the backlink indicator looks like. This two-faced
+ │ ;; arrow is the default.
+ │ (setq denote-rename-buffer-backlinks-indicator "<-->")
+ │
+ │ ;; Use just the title and keywords with some emoji in between, because
+ │ ;; why not?
+ │ (setq denote-rename-buffer-format "%t 🤨 %k")
+ │
+ │ ;; Use the title with a literal "[D]" before it.
+ │ (setq denote-rename-buffer-format "[D] %t")
+ │
+ │ ;; As above, but also add the `denote-rename-buffer-backlinks-indicator' at the end.
+ │ (setq denote-rename-buffer-format "[D] %t%b")
+ └────
+
+ Users who need yet more flexibility are best served by writing their
+ own function and assigning it to the `denote-rename-buffer-function'.
+
+
+[The file-naming scheme] See section 7
+
+
+13 Use Org dynamic blocks
+═════════════════════════
+
+ This section is about the external package `denote-org' (by
+ Protesilaos). The code of `denote-org' used to be available as part of
+ the main `denote' package, but we decided to keep each optional
+ extension as a separate package to make things easier to maintain and
+ to understand.
+
+ Denote can optionally integrate with Org mode’s “dynamic blocks”
+ facility. This means that it can use special blocks that are evaluated
+ with `C-c C-x C-u' (`org-dblock-update') to generate their contents.
+
+ Dynamic blocks are particularly useful for metanote entries that
+ reflect on the status of earlier notes ([Writing metanotes]). The
+ `denote-org' package defines many of these Org dynamic blocks.
+
+ ⁃ Package name (GNU ELPA): `denote-org'
+ ⁃ Official manual: <https://protesilaos.com/emacs/denote-org>
+ ⁃ Git repository: <https://github.com/protesilaos/denote-org>
+ ⁃ Backronym: Denote… Ordinarily Restricts Gyrations.
+
+
+[Writing metanotes] See section 9.12
+
+
+14 Display filtered and sorted files with `denote-sort-dired' or `denote-dired'
+═══════════════════════════════════════════════════════════════════════════════
+
+ The `denote.el' file contains functions which empower user or
+ developers to sort files by the given file name component ([The
+ file-naming scheme]).
+
+ The command `denote-sort-dired' (alias `denote-dired') produces a
+ Dired file listing with a flat, filtered, and sorted set of files from
+ the `denote-directory' ([Define a sorting function per component]). It
+ does so by a series of prompts, which can be configured with the user
+ option `denote-sort-dired-extra-prompts' ([Configure what extra
+ prompts `denote-sort-dired' issues]).
+
+ Think of `denote-sort-dired' as the counterpart to the Unix `find'
+ command. While `denote-grep' corresponds to the Unix `grep' ([Use
+ `denote-grep' to search inside files]).
+
+ The out-of-the-box behaviour of `denote-sort-dired' is as follows:
+
+ 1. It first asks for a regular expression with which to match Denote
+ file names. Remember that due to Denote’s efficient file-naming
+ scheme, you usually do not need to write some complex regular
+ expression. For example, something like `_journal' will match only
+ files with a `journal' keyword.
+ 2. Once the regular expression is provided, the command asks for a
+ Denote file name component to sort files by. This is a symbol among
+ `title', `keywords', `signature', and `identifier' ([Define a
+ sorting function per component]).
+ 3. Finally, it asks a “yes or no” on whether to reverse the sort
+ order.
+
+ The resulting listing is a regular Dired buffer, unlike that of
+ `dired-virtual-mode' ([Use `dired-virtual-mode' for arbitrary file
+ listings]).
+
+ The sorting mechanism can be used by other packages to achieve their
+ ends. As an example, the dynamic Org blocks that the `denote-org'
+ package (by Protesilaos) defines also use this feature internally by
+ means of the non-interactive function `denote-sort-files'.
+
+
+[The file-naming scheme] See section 7
+
+[Define a sorting function per component] See section 14.2
+
+[Configure what extra prompts `denote-sort-dired' issues] See section
+14.1
+
+[Use `denote-grep' to search inside files] See section 15
+
+[Use `dired-virtual-mode' for arbitrary file listings] See section 19.6
+
+14.1 Configure what extra prompts `denote-sort-dired' issues
+────────────────────────────────────────────────────────────
+
+ By default, the `denote-sort-dired' command prompts for (i) a query to
+ match file names, (ii) a file name component to sort by, and (iii)
+ whether to reverse the sorting ([Display filtered and sorted files
+ with denote-sort-dired]). Users can configure the latter two by
+ modifying the user option `denote-sort-dired-extra-prompts'.
+
+ The `denote-sort-dired-extra-prompts' accepts either a nil value or a
+ list of symbols among `sort-by-component', `reverse-sort', and
+ `exclude-regexp'. The order those symbols appear in the list is
+ significant, with the leftmost coming first.
+
+ These symbols correspond to the following:
+
+ • A choice to select the file name component to sort by.
+ • A yes or no prompt on whether to reverse the sorting.
+ • A string (or regular expression) of files to be excluded from the
+ results.
+
+ In case of a nil value, those extra prompts will not happen, meaning
+ that `denote-sort-dired' will fall back to using whatever is defined
+ in the variables `denote-sort-dired-default-sort-component' and
+ `denote-sort-dired-default-reverse-sort'.
+
+ Here are some examples:
+
+ ┌────
+ │ ;; The default extra prompts...
+ │ (setq denote-sort-dired-extra-prompts '(sort-by-component reverse-sort))
+ │
+ │ ;; When using `denote-sort-dired', ask whether to reverse the sort and
+ │ ;; then which file name component to sort by. These are always done
+ │ ;; after the prompt to search for files matching a regexp.
+ │ (setq denote-sort-dired-extra-prompts '(reverse-sort sort-by-component))
+ │
+ │ ;; Do not prompt for a reverse sort. Just use the value of
+ │ ;; `denote-sort-dired-default-reverse-sort' (which is nil out-of-the-box).
+ │ (setq denote-sort-dired-extra-prompts '(sort-by-component))
+ │
+ │ ;; Do not issue any extra prompts. Always sort by the `title' file
+ │ ;; name component and never do a reverse sort.
+ │ (setq denote-sort-dired-extra-prompts nil)
+ │ (setq denote-sort-dired-default-sort-component 'title)
+ │ (setq denote-sort-dired-default-reverse-sort nil)
+ └────
+
+
+[Display filtered and sorted files with denote-sort-dired] See section
+14
+
+
+14.2 Define a sorting function per component
+────────────────────────────────────────────
+
+ When sorting by `title', `keywords', or `signature' with the
+ `denote-sort-dired' command, Denote will internally apply a sorting
+ function that is specific to each component ([Configure what extra
+ prompts `denote-sort-dired' issues]). These are subject to user
+ configuration:
+
+ • `denote-sort-identifier-comparison-function'
+
+ • `denote-sort-title-comparison-function'
+
+ • `denote-sort-keywords-comparison-function'
+
+ • `denote-sort-signature-comparison-function'
+
+ By default, all these user options use the same sorting function,
+ namely `string-collate-lessp'. Users who have specific needs for any
+ of those file name components can write their own sorting algorithms
+ ([Sort signatures that include Luhmann-style sequences]).
+
+
+[Configure what extra prompts `denote-sort-dired' issues] See section
+14.1
+
+[Sort signatures that include Luhmann-style sequences] See section
+14.2.1
+
+14.2.1 Sort signatures that include Luhmann-style sequences
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ [ The `denote-sequence' package (by Protesilaos) covers this use-case
+ and many others ([Write sequence notes or folgezettel]). It is the
+ superior option for anyone interested in this functionality. We keep
+ the code below for reference, as there may be users of it who need
+ to revisit it. Though long-term, it is better to use
+ `denote-sequence'. ]
+
+ Niklas Luhmann would edit notes to form sequences of thoughts with
+ branching paths, such as `1.1', `1.1a', `1.2', `1.2a', `1.2b', etc.
+ With the Denote file-naming scheme, we make the word separator in each
+ file name component use the same character as the entire field, so
+ words in a title have a dash between them and signatures have the
+ equals sign ([The file-naming scheme]). Thus, our Luhmann-style
+ signature will be slightly different in their looks: `1=1', `1=1a',
+ `1=2', `1=2a', `1=2b'.
+
+ When using the `denote-sort-dired' command with default settings, our
+ signatures will not sort in an intuitive way. This is because they
+ combine numbers and letters, which require a different approach than
+ what the default sorting function is using ([Define a sorting function
+ per component]). In the following code block, we show a sorting
+ algorithm that should do the right thing while dealing with
+ Luhmann-style signatures.
+
+ ┌────
+ │ (defun my-denote--split-luhman-sig (signature)
+ │ "Split numbers and letters in Luhmann-style SIGNATURE string."
+ │ (replace-regexp-in-string
+ │ "\\([a-zA-Z]+?\\)\\([0-9]\\)" "\\1=\\2"
+ │ (replace-regexp-in-string
+ │ "\\([0-9]+?\\)\\([a-zA-Z]\\)" "\\1=\\2"
+ │ signature)))
+ │
+ │ (defun my-denote--pad-sig (signature)
+ │ "Create a new signature with padded spaces for all components"
+ │ (combine-and-quote-strings
+ │ (mapcar
+ │ (lambda (x)
+ │ (string-pad x 5 32 t))
+ │ (split-string (my-denote--split-luhman-sig signature) "=" t))
+ │ "="))
+ │
+ │ (defun my-denote-sort-for-signatures (sig1 sig2)
+ │ "Return non-nil if SIG1 is smaller that SIG2.
+ │ Perform the comparison with `string<'."
+ │ (string< (my-denote--pad-sig sig1) (my-denote--pad-sig sig2)))
+ │
+ │ ;; Change the sorting function only when we sort by signature.
+ │ (setq denote-sort-signature-comparison-function #'my-denote-sort-for-signatures)
+ └────
+
+
+[Write sequence notes or folgezettel] See section 18.2
+
+[The file-naming scheme] See section 7
+
+[Define a sorting function per component] See section 14.2
+
+
+15 Use `denote-grep' to search inside files
+═══════════════════════════════════════════
+
+ The command `denote-grep' searches for the given query across all
+ readable files in the `denote-directory'. It puts the collected
+ results in an Xref buffer (just like with our backlinks and query
+ links functionality). In this buffer, users can do `M-x describe-mode'
+ (`C-h m' with default key bindings) to learn about all the actions
+ they can perform and the keys they are bound to ([Interact with the
+ links buffer]).
+
+ Think of `denote-grep' as the counterpart to the Unix `grep' command.
+ While `denote-sort-dired' corresponds to the Unix `find' ([Display
+ filtered and sorted files with `denote-sort-dired']).
+
+ The command `denote-grep-marked-dired-files' is like `denote-grep' but
+ operates on the files that are marked in a Dired buffer.
+
+ The command `denote-grep-files-referenced-in-region' is like
+ `denote-grep' for any files referenced within the boundaries of the
+ marked region. Files are referenced by their identifier. This includes
+ links with just the identifier (as described in `denote-link' and
+ related ([Add a single direct link using a file name prompt])), links
+ written by an Org dynamic block (see the `denote-org' package ([Use
+ Org dynamic blocks])), or even file listings such as those of `dired'
+ and the command-line `ls' program.
+
+ The user option `denote-grep-display-buffer-action' controls where the
+ buffer with the search results is displayed at. By default, they
+ appear in the same window where the command `denote-grep' is called
+ from.
+
+
+[Interact with the links buffer] See section 16
+
+[Display filtered and sorted files with `denote-sort-dired'] See section
+14
+
+[Add a single direct link using a file name prompt] See section 9.1
+
+[Use Org dynamic blocks] See section 13
+
+
+16 Interact with the links buffer
+═════════════════════════════════
+
+ Denote commands, such as `denote-grep', `denote-backlinks', and
+ `denote-query-contents-link', produce an Xref buffer with search
+ results ([Speed up backlinks’ or query links’ buffer creation?]).
+ Matching lines are grouped by the file name they belong to.
+
+ • [Use `denote-grep' to search inside files].
+ • [The backlinks’ buffer].
+ • [Add a query link].
+
+ This buffer uses the major mode `denote-query-mode'. It binds commands
+ to keys in the `denote-query-mode-map'. Those allow users to filter
+ the output of the last search. Here, “last search” refers to the list
+ of files that were returned by whichever command produced the buffer
+ (e.g. the last `denote-grep').
+
+ `denote-query-focus-last-search'
+ Perform a search in the contents of files that were matched by
+ the last search.
+
+ `denote-query-exclude-files'
+ Exclude files from the last search whose name matches the given
+ input.
+
+ `denote-query-only-include-files'
+ Only keep files from the last search whose name matches the
+ given input.
+
+ `denote-query-exclude-files-with-keywords'
+ Exclude files from the last search whose name includes the given
+ keywords.
+
+ `denote-query-only-include-files-with-keywords'
+ Only keep files from the last search whose name includes the
+ given keywords.
+
+ `denote-query-clear-all-filters'
+ Clear all the applied filters.
+
+ Remember that these are easy to use even without knowledge of regular
+ expressions, thanks to the efficiency of the Denote file-naming scheme
+ ([Features of the file-naming scheme for searching or filtering]). For
+ instance, to exclude notes with the keyword `philosophy' from current
+ search buffer, use `denote-query-exclude-files' and then type
+ `_philosophy' as your input.
+
+ In addition to those filtering options, the `denote-query-mode' also
+ allows provides an outline mechanism to hide or show the matches as
+ these are grouped per file. There also are some of the default actions
+ provided by the Xref infrastructure. Users can do `M-x describe-mode'
+ (`C-h m' with default key bindings) to learn about all the actions
+ they can perform.
+
+
+[Speed up backlinks’ or query links’ buffer creation?] See section 25.10
+
+[Use `denote-grep' to search inside files] See section 15
+
+[The backlinks’ buffer] See section 9.11
+
+[Add a query link] See section 9.3
+
+[Features of the file-naming scheme for searching or filtering] See
+section 7.4
+
+
+17 Minibuffer histories
+═══════════════════════
+
+ Denote has a dedicated minibuffer history for each one of its prompts.
+ This practically means that using `M-p' (`previous-history-element')
+ and `M-n' (`next-history-element') will only cycle through the
+ relevant record of inputs, such as your latest titles in the `TITLE'
+ prompt, and keywords in the `KEYWORDS' prompt.
+
+ The built-in `savehist' library saves minibuffer histories. Sample
+ configuration:
+
+ ┌────
+ │ (require 'savehist)
+ │ (setq savehist-file (locate-user-emacs-file "savehist"))
+ │ (setq history-length 500)
+ │ (setq history-delete-duplicates t)
+ │ (setq savehist-save-minibuffer-history t)
+ │ (add-hook 'after-init-hook #'savehist-mode)
+ └────
+
+
+18 Packages that build on Denote
+════════════════════════════════
+
+ This is a list of packages that extend Denote. If you are a package
+ author, please let us know about your work and we will include it here
+ (either use the Git repositories or email Protesilaos directly).
+
+
+18.1 Use the `consult-denote' package for enhanced minibuffer interactions
+──────────────────────────────────────────────────────────────────────────
+
+ The `consult-denote' package by me (Protesilaos) integrates Denote
+ with Daniel Mendler’s `consult' package:
+ <https://github.com/protesilaos/consult-denote>.
+
+ The idea is to preserve the familiar patterns of interaction with the
+ various Denote commands but add to them an extra layer of
+ functionality, such as the preview mechanism that Consult provides
+ (e.g. preview the file you are about to link to).
+
+ Additionally, `consult-denote' defines new “sources” for the
+ `consult-buffer' command. This command provides a single point of
+ entry for buffers, recently opened files, and bookmarks. With
+ `consult-denote', it has a dedicated place for Denote-specific
+ buffers, silos, and more (all of which are configurable).
+
+ Unlike the `consult-notes' package by Colin McLear, `consult-denote'
+ uses the same presentation of data in the minibuffer to stay in sync
+ with Denote and make its feature set entirely optional ([Use the
+ `consult-notes' package]). It also only works with Denote.
+
+
+[Use the `consult-notes' package] See section 18.9
+
+
+18.2 Use the `denote-sequence' package to write sequence notes or "folgezettel"
+───────────────────────────────────────────────────────────────────────────────
+
+ This section is about the external package `denote-sequence' (by
+ Protesilaos). The original idea was to include the code as part of the
+ `denote' package, but we decided to keep each optional extension as a
+ separate package to make things easier to maintain and to understand.
+
+ Denote defines an optional file name component called the `SIGNATURE'
+ ([The file-naming scheme]). This is a free-form field that users can
+ fill in with whatever text they want, such as to have a video split up
+ into `part1' and `part2', or to set some kind of priority like `a' and
+ `b', or even to have a special tag that stands out from the rest of
+ the keywords.
+
+ A more specialised use-case of the `SIGNATURE' is to define a
+ hierarchical relationship between notes, such that the thoughts they
+ expound on form sequences. For example, an article about the Labrador
+ Retriever dog breed is a continuation of a thought process that
+ extends something about dog breeds in general which, in turn, is a
+ topic that belongs to the wider theme of dogs.
+
+ The `denote-sequence' package has a manual that explains these
+ concepts and relevant commands in further detail:
+
+ ⁃ Package name (GNU ELPA): `denote-sequence'
+ ⁃ Official manual: <https://protesilaos.com/emacs/denote-sequence>
+ ⁃ Git repository: <https://github.com/protesilaos/denote-sequence>
+ ⁃ Backronym: Denote… Sequences Efficiently Queue Unsorted Entries
+ Notwithstanding Curation Efforts.
+
+
+[The file-naming scheme] See section 7
+
+
+18.3 Use the `denote-markdown' package to better integrate Markdown with Denote
+───────────────────────────────────────────────────────────────────────────────
+
+ The `denote-markdown' package (by Protesilaos) provides some
+ convenience functions to better integrate Markdown with Deonte. This
+ is mostly about converting links from one type to another so that they
+ can work in different applications (because Markdown does not have a
+ standardised way to define custom link types).
+
+ The code of `denote-markdown' used to be bundled up with the `denote'
+ package before version `4.0.0' of the latter and was available in the
+ file `denote-md-extras.el'. Users of the old code will need to adapt
+ their setup to use the `denote-markdown' package. This can be done by
+ replacing all instances of `denote-md-extras' with `denote-markdown'
+ across their configuration.
+
+ ⁃ Package name (GNU ELPA): `denote-markdown'
+ ⁃ Official manual: <https://protesilaos.com/emacs/denote-markdown>
+ ⁃ Git repository: <https://github.com/protesilaos/denote-markdown>
+ ⁃ Backronyms: Denote… Markdown’s Ambitious Reimplimentations Knowingly
+ Dilute Obvious Widespread Norms; Denote… Markup Agnosticism Requires
+ Knowhow to Do Only What’s Necessary.
+
+
+18.4 Use the `denote-journal' package which was formerly `denote-journal-extras.el'
+───────────────────────────────────────────────────────────────────────────────────
+
+ The `denote-journal' package (by Protesilaos) makes it easier to use
+ Denote for journaling. While it is possible to use the generic
+ `denote' command (and related) to maintain a journal, this package
+ defines extra functionality to streamline the journaling workflow.
+
+ The code of `denote-journal' used to be bundled up with the `denote'
+ package before version `4.0.0' of the latter and was available in the
+ file `denote-journal-extras.el'. Users of the old code will need to
+ adapt their setup to use the `denote-journal' package. This can be
+ done by replacing all instances of `denote-journal-extras' with
+ `denote-journal' across their configuration.
+
+ ⁃ Package name (GNU ELPA): `denote-journal'
+ ⁃ Official manual: <https://protesilaos.com/emacs/denote-journal>
+ ⁃ Git repository: <https://github.com/protesilaos/denote-journal>
+ ⁃ Backronym: Denote… Journaling Obviously Utilises Reasonableness
+ Notwithstanding Affectionate Longing.
+
+
+18.5 Use the `denote-silo' package which formerly was `denote-silo-extras.el'
+─────────────────────────────────────────────────────────────────────────────
+
+ The `denote-silo' package (by Protesilaos) provides convenience
+ functions for working with silos ([Maintain separate directory silos
+ for notes]).
+
+ The code of `denote-silo' used to be bundled up with the `denote'
+ package before version `4.0.0' of the latter and was available in the
+ file `denote-silo-extras.el'. Users of the old code will need to adapt
+ their setup to use the `denote-silo' package. This can be done by
+ replacing all instances of `denote-silo-extras' with `denote-silo'
+ across their configuration.
+
+ ⁃ Package name (GNU ELPA): `denote-silo'
+ ⁃ Official manual: <https://protesilaos.com/emacs/denote-silo>
+ ⁃ Git repository: <https://github.com/protesilaos/denote-silo>
+ ⁃ Backronym: Denote… Silos Insulate Localised Objects.
+
+
+[Maintain separate directory silos for notes] See section 5.7
+
+
+18.6 Use the `denote-search' package as a search interface
+──────────────────────────────────────────────────────────
+
+ [ As part of version `4.0.0', Denote comes with the `denote-grep'
+ command and related functionality ([Use `denote-grep' to search
+ inside files]). The core of this feature set was written by Lucas
+ Quintana. ]
+
+ The `denote-search' package by Lucas Quintana provides a search
+ utility for Denote: <https://github.com/lmq-10/denote-search>.
+
+ It allows you to search for a regular expression in the content of
+ your notes. Its main advantages over other similar tools are the
+ possibility of filtering the results by file name and doing further
+ searches in the files matched previously. This allows for advanced
+ usage (think about finding a note with two or three specific words in
+ different lines and with a specific keyword). More features are
+ described in its comprehensive manual. `denote-search' builds upon
+ standard Emacs libraries, namely Xref, and so it doesn’t have external
+ dependencies other than Denote itself.
+
+
+[Use `denote-grep' to search inside files] See section 15
+
+
+18.7 Use the `denote-explore' package to explore your notes
+───────────────────────────────────────────────────────────
+
+ Peter Prevos has developed the `denote-explore' package which provides
+ four groups of Emacs commands to explore your Denote files:
+
+ Summary statistics
+ Count notes, attachments and keywords.
+ Random walks
+ Generate new ideas using serendipity.
+ Janitor
+ Manage your denote collection.
+ Visualisations
+ Visualise your Denote network.
+
+ The package’s documentation covers the details:
+ <https://lucidmanager.org/productivity/denote-explore/>.
+
+
+18.8 Use the `citar-denote' package for bibliography notes
+──────────────────────────────────────────────────────────
+
+ Peter Prevos has produced the `citar-denote' package which makes it
+ possible to write notes on BibTeX entries with the help of the `citar'
+ package. These notes have the citation’s unique key associated with
+ them in the file’s front matter. They also get a configurable keyword
+ in their file name, making it easy to find them in Dired and/or
+ retrieve them with the various Denote methods.
+
+ With `citar-denote', the user leverages standard minibuffer completion
+ mechanisms (e.g. with the help of the `vertico' and `embark' packages)
+ to manage bibliographic notes and access those notes with ease. The
+ package’s documentation covers the details:
+ <https://lucidmanager.org/productivity/bibliographic-notes-in-emacs-with-citar-denote/>.
+
+
+18.9 Use the `consult-notes' package
+────────────────────────────────────
+
+ [ Also check the `consult-denote' package by me (Protesilaos): [Use
+ the `consult-denote' package for enhanced minibuffer
+ interactions]. ]
+
+ If you are using Daniel Mendler’s `consult' (which is a brilliant
+ package), you will most probably like its `consult-notes' extension,
+ developed by Colin McLear. It uses the familiar mechanisms of Consult
+ to preview the currently selected entry and to filter searches via a
+ prefix key. For example:
+
+ ┌────
+ │ (setq consult-notes-file-dir-sources
+ │ `(("Denote Notes" ?d ,(denote-directory))
+ │ ("Books" ?b "~/Documents/books/")))
+ └────
+
+ With the above, `M-x consult-notes' will list the files in those two
+ directories. If you type `d' and space, it narrows the list to just
+ the notes, while `b' does the same for books.
+
+ The other approach is to enable the `consult-notes-denote-mode'. It
+ takes care to add the `denote-directory' to the sources that
+ `consult-notes' reads from. Denote notes are then filtered by the `d'
+ prefix followed by a space.
+
+ The minor mode has the extra feature of reformatting the title of
+ notes shown in the minibuffer. It isolates the `TITLE' component of
+ each note and shows it without hyphens, while presenting keywords in
+ their own column. The user option `consult-notes-denote-display-id'
+ can be set to `nil' to hide the identifier. Depending on how one
+ searches through their notes, this refashioned presentation may be the
+ best option ([Features of the file-naming scheme for searching or
+ filtering]).
+
+
+[Use the `consult-denote' package for enhanced minibuffer interactions]
+See section 18.1
+
+[Features of the file-naming scheme for searching or filtering] See
+section 7.4
+
+
+18.10 Use the `denote-menu' package
+───────────────────────────────────
+
+ Denote’s file-naming scheme is designed to be efficient and to provide
+ valueable meta information about the file. The cost, however, is that
+ it is terse and harder to read, depending on how the user chooses to
+ filter and process their notes.
+
+ To this end, [the `denote-menu' package by Mohamed Suliman] provides
+ the convenience of a nice tabular interface for all notes.
+ `denote-menu' removes the delimiters that are found in Denote file
+ names and presents the information in a human-readable format.
+ Furthermore, the package provides commands to interact with the list
+ of notes, such as to filter them and to transition from the tabular
+ list to Dired. Its documentation expands on the technicalities.
+
+
+[the `denote-menu' package by Mohamed Suliman]
+<https://github.com/namilus/denote-menu>
+
+
+18.11 Use the `denote-zettel-interface' package
+───────────────────────────────────────────────
+
+ The [`denote-zettel-interface' package by Kristoffer Balintona] is
+ designed for those who want to use Denote while adhering to a strict
+ Zettelkasten methodology of sequence notes (Folgezettel). This method
+ leverages the optional `SIGNATURE' file name component of Denote ([The
+ file-naming scheme]). The package provides a point of entry to one’s
+ note by visualising them in a tabulated (grid) interface. Files are
+ sorted by their Folgezettel index. Users can then use a number of
+ commands to filter their files, navigate around, and the like.
+
+ Note that the package is in early development as of this writing
+ (2024-12-03 10:18 +0200).
+
+
+[`denote-zettel-interface' package by Kristoffer Balintona]
+<https://github.com/krisbalintona/denote-zettel-interface>
+
+[The file-naming scheme] See section 7
+
+
+19 Extending Denote
+═══════════════════
+
+ Denote is a tool with a narrow scope: create notes and link between
+ them, based on the aforementioned file-naming scheme. For other common
+ operations the user is advised to rely on standard Emacs facilities or
+ specialised third-party packages ([Packages that build on
+ Denote]). This section covers the details.
+
+
+[Packages that build on Denote] See section 18
+
+19.1 Access the data of the latest note
+───────────────────────────────────────
+
+ The variable `denote-current-data' is updated each time a new note is
+ created as well as after a rename operation.
+
+ This is an alist where each `car' is one among `title', `keywords',
+ `signature', `directory', `date', `id', `file-type', `template'. The
+ value each of them contains is the unprocessed input (e.g. the title
+ before it is sluggified).
+
+ Users who need to access this data as part of their custom code can
+ rely on the hooks `denote-after-new-note-hook' and
+ `denote-after-rename-file-hook'.
+
+
+19.2 Create a new note in any directory
+───────────────────────────────────────
+
+ The commands that create new files are designed to write to the
+ `denote-directory'. The idea is that the linking mechanism can find
+ any file by its identifier if it is in the `denote-directory'
+ (searching the entire file system would be cumbersome).
+
+ However, these are cases where the user needs to create a new note in
+ an arbitrary directory. The following command can do this. Put the
+ code in your configuration file and evaluate it. Then call the command
+ by its name with `M-x'.
+
+ ┌────
+ │ (defun my-denote-create-note-in-any-directory ()
+ │ "Create new Denote note in any directory.
+ │ Prompt for the directory using minibuffer completion."
+ │ (declare (interactive-only t))
+ │ (interactive)
+ │ (let ((denote-directory (read-directory-name "New note in: " nil nil :must-match)))
+ │ (call-interactively 'denote)))
+ └────
+
+
+19.3 Find empty notes and put them in a Dired buffer
+────────────────────────────────────────────────────
+
+ [ This feature is based on the command `denote-sort-dired' ([Sort
+ files by component]). ]
+
+ Users may have a workflow where they use the commands
+ `denote-link-or-create' or `denote-link-after-creating' (and related)
+ to produce new notes that they plan to elaborate on later ([Link to an
+ existing note or create a new one]).
+
+ To help users find those empty notes, we document the following
+ commands:
+
+ • `my-denote-sort-dired-empty-files'
+ • `my-denote-sort-dired-without-empty-files'
+ • `my-denote-sort-dired-all-empty-files'
+ • `my-denote-sort-dired-without-all-empty-files'
+
+ ┌────
+ │ (require 'denote-sort)
+ │
+ │ (defun my-denote--note-has-no-contents-p (file)
+ │ "Return non-nil if FILE is an empty note.
+ │ This means that FILE conforms with `denote-file-is-note-p' and either
+ │ has no contents or has only the front matter."
+ │ (and (denote-file-is-note-p file)
+ │ (or (denote--file-with-temp-buffer file
+ │ (re-search-forward "^$" nil t)
+ │ (if (re-search-forward "[^\s\t\n\r]+" nil t)
+ │ nil
+ │ t))
+ │ ;; This must come later because here we consider a file
+ │ ;; "empty" even if it only has front matter.
+ │ (denote--file-empty-p file))))
+ │
+ │ (defun my-denote-sort-dired-empty-files (files-matching-regexp sort-by-component reverse)
+ │ "Like `denote-sort-dired' but only cover empty files.
+ │ Empty files are those that satisfy `my-denote--note-has-no-contents-p'."
+ │ (interactive
+ │ (append (list (denote-files-matching-regexp-prompt)) (denote-sort-dired--prompts)))
+ │ (let ((component (or sort-by-component
+ │ denote-sort-dired-default-sort-component
+ │ 'identifier))
+ │ (reverse-sort (or reverse
+ │ denote-sort-dired-default-reverse-sort
+ │ nil)))
+ │ (if-let* ((default-directory (denote-directory))
+ │ (files (denote-sort-get-directory-files files-matching-regexp component reverse-sort))
+ │ (empty-files (seq-filter #'my-denote--note-has-no-contents-p files))
+ │ ;; NOTE 2023-12-04: Passing the FILES-MATCHING-REGEXP as
+ │ ;; buffer-name produces an error if the regexp contains a
+ │ ;; wildcard for a directory. I can reproduce this in emacs
+ │ ;; -Q and am not sure if it is a bug. Anyway, I will report
+ │ ;; it upstream, but even if it is fixed we cannot use it
+ │ ;; for now (whatever fix will be available for Emacs 30+).
+ │ (denote-sort-dired-buffer-name (format "Denote sort `%s' by `%s'" files-matching-regexp component))
+ │ (buffer-name (format "Denote sort by `%s' at %s" component (format-time-string "%T"))))
+ │ (let ((dired-buffer (dired (cons buffer-name (mapcar #'file-relative-name empty-files)))))
+ │ (setq denote-sort--dired-buffer dired-buffer)
+ │ (with-current-buffer dired-buffer
+ │ (setq-local revert-buffer-function
+ │ (lambda (&rest _)
+ │ (kill-buffer dired-buffer)
+ │ (denote-sort-dired files-matching-regexp component reverse-sort))))
+ │ ;; Because of the above NOTE, I am printing a message. Not
+ │ ;; what I want, but it is better than nothing...
+ │ (message denote-sort-dired-buffer-name))
+ │ (message "No matching files for: %s" files-matching-regexp))))
+ │
+ │ (defun my-denote-sort-dired-without-empty-files (files-matching-regexp sort-by-component reverse)
+ │ "Like `denote-sort-dired' but only cover empty files.
+ │ Empty files are those that satisfy `my-denote--note-has-no-contents-p'."
+ │ (interactive
+ │ (append (list (denote-files-matching-regexp-prompt)) (denote-sort-dired--prompts)))
+ │ (let ((component (or sort-by-component
+ │ denote-sort-dired-default-sort-component
+ │ 'identifier))
+ │ (reverse-sort (or reverse
+ │ denote-sort-dired-default-reverse-sort
+ │ nil)))
+ │ (if-let* ((default-directory (denote-directory))
+ │ (files (denote-sort-get-directory-files files-matching-regexp component reverse-sort))
+ │ (empty-files (seq-remove #'my-denote--note-has-no-contents-p files))
+ │ ;; NOTE 2023-12-04: Passing the FILES-MATCHING-REGEXP as
+ │ ;; buffer-name produces an error if the regexp contains a
+ │ ;; wildcard for a directory. I can reproduce this in emacs
+ │ ;; -Q and am not sure if it is a bug. Anyway, I will report
+ │ ;; it upstream, but even if it is fixed we cannot use it
+ │ ;; for now (whatever fix will be available for Emacs 30+).
+ │ (denote-sort-dired-buffer-name (format "Denote sort `%s' by `%s'" files-matching-regexp component))
+ │ (buffer-name (format "Denote sort by `%s' at %s" component (format-time-string "%T"))))
+ │ (let ((dired-buffer (dired (cons buffer-name (mapcar #'file-relative-name empty-files)))))
+ │ (setq denote-sort--dired-buffer dired-buffer)
+ │ (with-current-buffer dired-buffer
+ │ (setq-local revert-buffer-function
+ │ (lambda (&rest _)
+ │ (kill-buffer dired-buffer)
+ │ (denote-sort-dired files-matching-regexp component reverse-sort))))
+ │ ;; Because of the above NOTE, I am printing a message. Not
+ │ ;; what I want, but it is better than nothing...
+ │ (message denote-sort-dired-buffer-name))
+ │ (message "No matching files for: %s" files-matching-regexp))))
+ │
+ │ (defun my-denote-sort-dired-all-empty-files ()
+ │ "List all empty files in a Dired buffer.
+ │ This is the same as calling `my-denote-sort-dired' with a
+ │ FILES-MATCHING-REGEXP of \".*\"."
+ │ (declare (interactive-only t))
+ │ (interactive)
+ │ (let* ((other-prompts (denote-sort-dired--prompts))
+ │ (sort-key (nth 1 other-prompts))
+ │ (reverse (nth 2 other-prompts)))
+ │ (funcall-interactively #'my-denote-sort-dired-empty-files ".*" sort-key reverse)))
+ │
+ │ (defun my-denote-sort-dired-without-all-empty-files ()
+ │ "List all empty files in a Dired buffer.
+ │ This is the same as calling `my-denote-sort-dired' with a
+ │ FILES-MATCHING-REGEXP of \".*\"."
+ │ (declare (interactive-only t))
+ │ (interactive)
+ │ (let* ((other-prompts (denote-sort-dired--prompts))
+ │ (sort-key (nth 1 other-prompts))
+ │ (reverse (nth 2 other-prompts)))
+ │ (funcall-interactively #'my-denote-sort-dired-without-empty-files ".*" sort-key reverse)))
+ └────
+
+ [ In the above snippet, I am purposefully duplicating code to make it
+ easier for users to pick the ones they need. ]
+
+
+[Sort files by component] See section 14
+
+[Link to an existing note or create a new one] See section 9.10
+
+
+19.4 Automatically rename the note after saving it
+──────────────────────────────────────────────────
+
+ While experimenting with Denote, users may need to try different
+ workflows to figure out what works for them. Those might involve
+ changing keywords and specifying titles in a particular way. The
+ following sample can be used:
+
+ ┌────
+ │ (defun my-denote-always-rename-on-save-based-on-front-matter ()
+ │ "Rename the current Denote file, if needed, upon saving the file.
+ │ Rename the file based on its front matter, checking for changes in the
+ │ title or keywords fields.
+ │
+ │ Add this function to the `after-save-hook'."
+ │ (let ((denote-rename-confirmations nil)
+ │ (denote-save-buffers t)) ; to save again post-rename
+ │ (when (and buffer-file-name (denote-file-is-note-p buffer-file-name))
+ │ (ignore-errors (denote-rename-file-using-front-matter buffer-file-name))
+ │ (message "Buffer saved; Denote file renamed"))))
+ │
+ │ (add-hook 'after-save-hook #'my-denote-always-rename-on-save-based-on-front-matter)
+ └────
+
+
+19.5 Narrow the list of files in Dired
+──────────────────────────────────────
+
+ Emacs’ standard file manager (or directory editor) can read a regular
+ expression to mark the matching files. This is the command
+ `dired-mark-files-regexp', which is bound to `% m' by default. For
+ example, `% m _denote' will match all files that have the `denote'
+ keyword ([Features of the file-naming scheme for searching or
+ filtering]).
+
+ Once the files are matched, the user has two options: (i) narrow the
+ list to the matching items or (ii) exclude the matching items from the
+ list.
+
+ For the former, we want to toggle the marks by typing `t' (calls the
+ command `dired-toggle-marks' by default) and then hit the letter `k'
+ (for `dired-do-kill-lines'). The remaining files are those that match
+ the regexp that was provided earlier.
+
+ For the latter approach of filtering out the matching items, simply
+ involves the use of the `k' command (`dired-do-kill-lines') to omit
+ the marked files from the list.
+
+ These sequences can be combined to incrementally narrow the list.
+ Note that `dired-do-kill-lines' does not delete files: it simply hides
+ them from the current view.
+
+ Revert to the original listing with `g' (`revert-buffer').
+
+ For a convenient wrapper, consider this example:
+
+ ┌────
+ │ (defvar prot-dired--limit-hist '()
+ │ "Minibuffer history for `prot-dired-limit-regexp'.")
+ │
+ │ ;;;###autoload
+ │ (defun prot-dired-limit-regexp (regexp omit)
+ │ "Limit Dired to keep files matching REGEXP.
+ │
+ │ With optional OMIT argument as a prefix (\\[universal-argument]),
+ │ exclude files matching REGEXP.
+ │
+ │ Restore the buffer with \\<dired-mode-map>`\\[revert-buffer]'."
+ │ (interactive
+ │ (list
+ │ (read-regexp
+ │ (concat "Files "
+ │ (when current-prefix-arg
+ │ (propertize "NOT " 'face 'warning))
+ │ "matching PATTERN: ")
+ │ nil 'prot-dired--limit-hist)
+ │ current-prefix-arg))
+ │ (dired-mark-files-regexp regexp)
+ │ (unless omit (dired-toggle-marks))
+ │ (dired-do-kill-lines))
+ └────
+
+
+[Features of the file-naming scheme for searching or filtering] See
+section 7.4
+
+
+19.6 Use `dired-virtual-mode' for arbitrary file listings
+─────────────────────────────────────────────────────────
+
+ Emacs’ Dired is a powerful file manager that builds its functionality
+ on top of the Unix `ls' command. As noted elsewhere in this manual,
+ the user can update the `ls' flags that Dired uses to display its
+ contents ([I want to sort by last modified, why won’t Denote let
+ me?]).
+
+ What Dired cannot do is parse the output of a result that is produced
+ by piped commands, such as `ls -l | sort -t _ -k2'. This specific
+ example targets the second underscore-separated field of the file
+ name, per our conventions ([The file-naming scheme]). Conceretely, it
+ matches the “alpha” as the sorting key in something like this:
+
+ ┌────
+ │ 20220929T200432--testing-file-one__alpha.txt
+ └────
+
+ Consider then, how Dired will sort those files by their identifier:
+
+ ┌────
+ │ 20220929T200432--testing-file-one__alpha.txt
+ │ 20220929T200532--testing-file-two__beta.txt
+ │ 20220929T200632--testing-file-three__alpha.txt
+ │ 20220929T200732--testing-file-four__beta.txt
+ └────
+
+ Whereas on the command line, we can get the following:
+
+ ┌────
+ │ $ ls | sort -t _ -k 2
+ │ 20220929T200432--testing-file-one__alpha.txt
+ │ 20220929T200632--testing-file-three__alpha.txt
+ │ 20220929T200532--testing-file-two__beta.txt
+ │ 20220929T200732--testing-file-four__beta.txt
+ └────
+
+ This is where `dired-virtual-mode' shows its utility. If we tweak our
+ command-line invocation to include `ls -l', this mode can behave like
+ Dired on the listed files. (We omit the output of the `-l' flag from
+ this tutorial, as it is too verbose.)
+
+ What we now need is to capture the output of `ls -l | sort -t _ -k 2'
+ in an Emacs buffer and then enable `dired-virtual-mode'. To do that,
+ we can rely on either `M-x shell' or `M-x eshell' and then manually
+ copy the relevant contents.
+
+ For the user’s convenience, I share what I have for Eshell to quickly
+ capture the last command’s output in a dedicated buffer:
+
+ ┌────
+ │ (defcustom prot-eshell-output-buffer "*Exported Eshell output*"
+ │ "Name of buffer with the last output of Eshell command.
+ │ Used by `prot-eshell-export'."
+ │ :type 'string
+ │ :group 'prot-eshell)
+ │
+ │ (defcustom prot-eshell-output-delimiter "* * *"
+ │ "Delimiter for successive `prot-eshell-export' outputs.
+ │ This is formatted internally to have newline characters before
+ │ and after it."
+ │ :type 'string
+ │ :group 'prot-eshell)
+ │
+ │ (defun prot-eshell--command-prompt-output ()
+ │ "Capture last command prompt and its output."
+ │ (let ((beg (save-excursion
+ │ (goto-char (eshell-beginning-of-input))
+ │ (goto-char (point-at-bol)))))
+ │ (when (derived-mode-p 'eshell-mode)
+ │ (buffer-substring-no-properties beg (eshell-end-of-output)))))
+ │
+ │ ;;;###autoload
+ │ (defun prot-eshell-export ()
+ │ "Produce a buffer with output of the last Eshell command.
+ │ If `prot-eshell-output-buffer' does not exist, create it. Else
+ │ append to it, while separating multiple outputs with
+ │ `prot-eshell-output-delimiter'."
+ │ (interactive)
+ │ (let ((eshell-output (prot-eshell--command-prompt-output)))
+ │ (with-current-buffer (get-buffer-create prot-eshell-output-buffer)
+ │ (let ((inhibit-read-only t))
+ │ (goto-char (point-max))
+ │ (unless (eq (point-min) (point-max))
+ │ (insert (format "\n%s\n\n" prot-eshell-output-delimiter)))
+ │ (goto-char (point-at-bol))
+ │ (insert eshell-output)
+ │ (switch-to-buffer-other-window (current-buffer))))))
+ └────
+
+ Bind `prot-eshell-export' to a key in the `eshell-mode-map' and give
+ it a try (I use `C-c C-e'). In the produced buffer, activate the
+ `dired-virtual-mode'.
+
+
+[I want to sort by last modified, why won’t Denote let me?] See section
+25.7
+
+[The file-naming scheme] See section 7
+
+
+19.7 Use Embark to collect minibuffer candidates
+────────────────────────────────────────────────
+
+ `embark' is a remarkable package that lets you perform relevant,
+ context-dependent actions using a prefix key (simplifying in the
+ interest of brevity).
+
+ For our purposes, Embark can be used to produce a Dired listing
+ directly from the minibuffer. Suppose the current note has links to
+ three other notes. You might use the `denote-find-link' command to
+ pick one via the minibuffer. But why not turn those three links into
+ their own Dired listing? While in the minibuffer, invoke `embark-act'
+ which you may have already bound to `C-.' and then follow it up with
+ `E' (for the `embark-export' command).
+
+ This pattern can be repeated with any list of candidates, meaning that
+ you can narrow the list by providing some input before eventually
+ exporting the results with Embark.
+
+ Overall, this is very powerful and you might prefer it over doing the
+ same thing directly in Dired, since you also benefit from all the
+ power of the minibuffer ([Narrow the list of files in Dired]).
+
+
+[Narrow the list of files in Dired] See section 19.5
+
+
+19.8 Search file contents
+─────────────────────────
+
+ [ Users of `consult' can use the `consult-denote' package instead
+ ([Use the `consult-denote' package for enhanced minibuffer
+ interactions]). ]
+
+ Emacs provides built-in commands which are wrappers of standard Unix
+ tools: `M-x grep' lets the user input the flags of a `grep' call and
+ pass a regular expression to the `-e' flag.
+
+ The author of Denote uses this thin wrapper instead:
+
+ ┌────
+ │ (defvar prot-search--grep-hist '()
+ │ "Input history of grep searches.")
+ │
+ │ ;;;###autoload
+ │ (defun prot-search-grep (regexp &optional recursive)
+ │ "Run grep for REGEXP.
+ │
+ │ Search in the current directory using `lgrep'. With optional
+ │ prefix argument (\\[universal-argument]) for RECURSIVE, run a
+ │ search starting from the current directory with `rgrep'."
+ │ (interactive
+ │ (list
+ │ (read-from-minibuffer (concat (if current-prefix-arg
+ │ (propertize "Recursive" 'face 'warning)
+ │ "Local")
+ │ " grep for PATTERN: ")
+ │ nil nil nil 'prot-search--grep-hist)
+ │ current-prefix-arg))
+ │ (unless grep-command
+ │ (grep-compute-defaults))
+ │ (if recursive
+ │ (rgrep regexp "*" default-directory)
+ │ (lgrep regexp "*" default-directory)))
+ └────
+
+ Rather than maintain custom code, consider using the excellent
+ `consult' package: it provides commands such as `consult-grep' and
+ `consult-find' which provide live results and are generally easier to
+ use than the built-in commands.
+
+
+[Use the `consult-denote' package for enhanced minibuffer interactions]
+See section 18.1
+
+
+19.9 Bookmark the directory with the notes
+──────────────────────────────────────────
+
+ Part of the reason Denote does not reinvent existing functionality is
+ to encourage you to learn more about Emacs. You do not need a bespoke
+ “jump to my notes” directory because such commands do not scale well.
+ Will you have a “jump to my downloads” then another for multimedia and
+ so on? No.
+
+ Emacs has a built-in framework for recording persistent markers to
+ locations. Visit the `denote-directory' (or any dir/file for that
+ matter) and invoke the `bookmark-set' command (bound to `C-x r m' by
+ default). It lets you create a bookmark.
+
+ The list of bookmarks can be reviewed with the `bookmark-bmenu-list'
+ command (bound to `C-x r l' by default). A minibuffer interface is
+ available with `bookmark-jump' (`C-x r b').
+
+ If you use the `consult' package, its default `consult-buffer' command
+ has the means to group together buffers, recent files, and bookmarks.
+ Each of those types can be narrowed to with a prefix key. The package
+ `consult-dir' is an extension to `consult' which provides useful
+ extras for working with directories, including bookmarks.
+
+
+19.10 Treat your notes as a project
+───────────────────────────────────
+
+ Emacs has a built-in library for treating a directory tree as a
+ “project”. This means that the contents of this tree are seen as part
+ of the same set, so commands like `project-switch-to-buffer' (`C-x p
+ b' by default) will only consider buffers in the current project
+ (e.g. three notes that are currently being visited).
+
+ Normally, a “project” is a directory tree whose root is under version
+ control. For our purposes, all you need is to navigate to the
+ `denote-directory' (for the shell or via Dired) and use the
+ command-line to run this (requires the `git' executable):
+
+ ┌────
+ │ git init
+ └────
+
+
+ From Dired, you can type `M-!' which invokes
+ `dired-smart-shell-command' and then run the git call there.
+
+ The project can then be registered by invoking any project-related
+ command inside of it, such as `project-find-file' (`C-x p f').
+
+ It is a good idea to keep your notes under version control, as that
+ gives you a history of changes for each file. We shall not delve into
+ the technicalities here, though suffice to note that Emacs’ built-in
+ version control framework or the exceptionally well-crafted `magit'
+ package will get the job done (VC can work with other backends besides
+ Git).
+
+
+19.11 Use the tree-based file prompt for select commands
+────────────────────────────────────────────────────────
+
+ Older versions of Denote had a file prompt that resembled that of the
+ standard `find-file' command (bound to `C-x C-f' by default). This
+ means that it used a tree-based method of navigating the filesystem by
+ selecting the specific directory and then the given file.
+
+ Currently, Denote flattens the file prompt so that every file in the
+ `denote-directory' and its subdirectories can be matched from anywhere
+ using the power of Emacs’ minibuffer completion (such as with the help
+ of the `orderless' package in addition to built-in options).
+
+ Users who need the old behaviour on a per-command basis can define
+ their own wrapper functions as shown in the following code block.
+
+ ┌────
+ │ ;; This is the old `denote-file-prompt' that we renamed to
+ │ ;; `denote-file-prompt-original' for clarity.
+ │ (defun denote-file-prompt-original (&optional initial-text)
+ │ "Prompt for file with identifier in variable `denote-directory'.
+ │ With optional INITIAL-TEXT, use it to prepopulate the minibuffer."
+ │ (read-file-name "Select note: " (denote-directory) nil nil initial-text
+ │ (lambda (f)
+ │ (or (denote-file-has-identifier-p f)
+ │ (file-directory-p f)))))
+ │
+ │ ;; Our wrapper command that changes the current `denote-file-prompt'
+ │ ;; to the functionality of `denote-file-prompt-original' only when
+ │ ;; this command is used.
+ │ (defun my-denote-link ()
+ │ "Call `denote-link' but use Denote's original file prompt.
+ │ See `denote-file-prompt-original'."
+ │ (interactive)
+ │ (cl-letf (((symbol-function 'denote-file-prompt) #'denote-file-prompt-original))
+ │ (call-interactively #'denote-link)))
+ └────
+
+
+19.12 Rename files with Denote in the Image Dired thumbnails buffer
+───────────────────────────────────────────────────────────────────
+
+ [Rename files with Denote using `dired-preview']
+
+ Just as with the `denote-dired-rename-marked-files-with-keywords', we
+ can use Denote in the Image Dired buffer ([Rename multiple files at
+ once]). Here is the custom code:
+
+ ┌────
+ │ (autoload 'image-dired--with-marked "image-dired")
+ │ (autoload 'image-dired-original-file-name "image-dired-util")
+ │
+ │ (defun my-denote-image-dired-rename-marked-files (keywords)
+ │ "Like `denote-dired-rename-marked-files-with-keywords' but for Image Dired.
+ │ Prompt for KEYWORDS and rename all marked files in the Image
+ │ Dired buffer to have a Denote-style file name with the given
+ │ KEYWORDS.
+ │
+ │ IMPORTANT NOTE: if there are marked files in the corresponding
+ │ Dired buffers, those will be targeted as well. This is not the
+ │ fault of Denote: it is how Dired and Image Dired work in tandem.
+ │ To only rename the marked thumbnails, start by unmarking
+ │ everything in Dired. Then mark the items in Image Dired and
+ │ invoke this command."
+ │ (interactive (list (denote-keywords-prompt)) image-dired-thumbnail-mode)
+ │ (image-dired--with-marked
+ │ (when-let* ((file (image-dired-original-file-name))
+ │ (dir (file-name-directory file))
+ │ (id (or (denote-retrieve-filename-identifier file) ""))
+ │ (file-type (denote-filetype-heuristics file))
+ │ (title (denote--retrieve-title-or-filename file file-type))
+ │ (signature (or (denote-retrieve-filename-signature file) "")
+ │ (extension (file-name-extension file t))
+ │ (new-name (denote-format-file-name dir id keywords title extension signature))
+ │ (default-directory dir))
+ │ (denote-rename-file-and-buffer file new-name))))
+ └────
+
+ While the `my-denote-image-dired-rename-marked-files' renames files in
+ the helpful Denote-compliant way, users may still need to not prepend
+ a unique identifier and not sluggify (hyphenate and downcase) the
+ image’s existing file name. To this end, the following custom command
+ can be used instead:
+
+ ┌────
+ │ (defun my-image-dired-rename-marked-files (keywords)
+ │ "Like `denote-dired-rename-marked-files-with-keywords' but for Image Dired.
+ │ Prompt for keywords and rename all marked files in the Image
+ │ Dired buffer to have Denote-style keywords, but none of the other
+ │ conventions of Denote's file-naming scheme."
+ │ (interactive (list (denote-keywords-prompt)) image-dired-thumbnail-mode)
+ │ (image-dired--with-marked
+ │ (when-let* ((file (image-dired-original-file-name))
+ │ (dir (file-name-directory file))
+ │ (file-type (denote-filetype-heuristics file))
+ │ (title (denote--retrieve-title-or-filename file file-type))
+ │ (extension (file-name-extension file t))
+ │ (kws (denote--keywords-combine keywords))
+ │ (new-name (concat dir title "__" kws extension))
+ │ (default-directory dir))
+ │ (denote-rename-file-and-buffer file new-name))))
+ └────
+
+
+[Rename files with Denote using `dired-preview'] See section 19.13
+
+[Rename multiple files at once] See section 6.3
+
+
+19.13 Rename files with Denote using `dired-preview'
+────────────────────────────────────────────────────
+
+ The `dired-preview' package (by me/Protesilaos) automatically displays
+ a preview of the file at point in Dired. This can be helpful in
+ tandem with Denote when we want to rename multiple files by taking a
+ quick look at their contents.
+
+ The command `denote-dired-rename-marked-files-with-keywords' will
+ generate Denote-style file names based on the keywords it prompts
+ for. Identifiers are derived from each file’s modification date
+ ([Rename multiple files at once]). There is no need for any custom
+ code in this scenario.
+
+ As noted in the section about Image Dired, the user may sometimes not
+ need a fully fledged Denote-style file name but only append
+ Denote-like keywords to each file name (e.g. `Original
+ Name__denote_test.jpg' instead of
+ `20230710T195843--original-name__denote_test.jpg').
+
+ [Rename files with Denote in the Image Dired thumbnails buffer]
+
+ In such a workflow, it is unlikely to be dealing with ordinary text
+ files where front matter can be helpful. A custom command does not
+ need to behave like what Denote provides out-of-the-box, but can
+ instead append keywords to file names without conducting any further
+ actions. We thus have:
+
+ ┌────
+ │ (defun my-denote-dired-rename-marked-files-keywords-only ()
+ │ "Like `denote-dired-rename-marked-files-with-keywords' but only for keywords in file names.
+ │
+ │ Prompt for keywords and rename all marked files in the Dired
+ │ buffer to only have Denote-style keywords, but none of the other
+ │ conventions of Denote's file-naming scheme."
+ │ (interactive nil dired-mode)
+ │ (if-let* ((marks (dired-get-marked-files)))
+ │ (let ((keywords (denote-keywords-prompt)))
+ │ (dolist (file marks)
+ │ (let* ((dir (file-name-directory file))
+ │ (file-type (denote-filetype-heuristics file))
+ │ (title (denote--retrieve-title-or-filename file file-type))
+ │ (extension (file-name-extension file t))
+ │ (kws (denote--keywords-combine keywords))
+ │ (new-name (concat dir title "__" kws extension)))
+ │ (denote-rename-file-and-buffer file new-name)))
+ │ (revert-buffer))
+ │ (user-error "No marked files; aborting")))
+ └────
+
+
+[Rename multiple files at once] See section 6.3
+
+[Rename files with Denote in the Image Dired thumbnails buffer] See
+section 19.12
+
+
+19.14 Avoid duplicate identifiers when exporting Denote notes
+─────────────────────────────────────────────────────────────
+
+ When exporting Denote notes to, for example, an HTML or PDF file,
+ there is a high probability that the same file name is used with a new
+ extension. This is problematic because it creates files with
+ duplicate identifiers. The `20230515T085612--example__keyword.org'
+ produces a `20230515T085612--example__keyword.pdf'. Any link to the
+ `20230515T085612' will thus break: it does not honor Denote’s
+ expectation of finding unique identifiers. This is not the fault of
+ Denote: exporting is done by the user without Denote’s involvement.
+
+ Org Mode and Markdown use different approaches to exporting files. No
+ recommended method is available for plain text files as there is no
+ standardised export functionality for this format (the user can always
+ create a new note using the file type they want on a case-by-case
+ basis: [Convenience commands for note creation]).
+
+
+[Convenience commands for note creation] See section 5.1.4
+
+19.14.1 Export Denote notes with Org Mode
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ Org Mode has a built-in configurable export engine. You can prevent
+ duplicate identifiers when exporting manually for each exported file
+ or by advising the Org export function.
+
+ The `denote-org' package (by Protesilaos) also provides commands to
+ convert `denote:' links to their `file:' equivalent, in case this is a
+ required pre-processing step for export purposes.
+
+
+◊ 19.14.1.1 Manually configure Org export
+
+ Insert `#+export_file_name: FILENAME' in the front matter before
+ exporting to force a filename called whatever the value of `FILENAME'
+ is. The `FILENAME' does not specify the file type extension, such as
+ `.pdf'. This is up to the export engine. For example, a Denote note
+ with a complete file name of `20230515T085612--example__keyword.org'
+ and a front matter entry of `#+export_file_name: hello' will be
+ exported as `hello.pdf'.
+
+ The advantage of this manual method is that it gives the user full
+ control over the resulting file name. The disadvantage is that it
+ depends on the user’s behaviour. Forgetting to add a new name can
+ lead to duplicate identifiers, as already noted in the introduction to
+ this section ([Export Denote notes]).
+
+
+ [Export Denote notes] See section 19.14
+
+
+◊ 19.14.1.2 Automatically store Org exports in another folder
+
+ It is possible to automatically place all exports in another folder by
+ making Org’s function `org-export-output-file-name' create the target
+ directory if needed and move the exported file there. Remember that
+ advising Elisp code must be handled with care, as it might break the
+ original function in subtle ways.
+
+ ┌────
+ │ (defvar my-org-export-output-directory-prefix "./export_"
+ │ "Prefix of directory used for org-mode export.
+ │
+ │ The single dot means that the directory is created on the same
+ │ level as the one where the Org file that performs the exporting
+ │ is. Use two dots to place the directory on a level above the
+ │ current one.
+ │
+ │ If this directory is part of `denote-directory', make sure it is
+ │ not read by Denote. See `denote-excluded-directories-regexp'.
+ │ This way there will be no known duplicate Denote identifiers
+ │ produced by the Org export mechanism.")
+ │
+ │ (defun my-org-export-create-directory (fn extension &rest args)
+ │ "Move Org export file to its appropriate directory.
+ │
+ │ Append the file type EXTENSION of the exported file to
+ │ `my-org-export-output-directory-prefix' and, if absent, create a
+ │ directory named accordingly.
+ │
+ │ Install this as advice around `org-export-output-file-name'. The
+ │ EXTENSION is supplied by that function. ARGS are its remaining
+ │ arguments."
+ │ (let ((export-dir (format "%s%s" my-org-export-output-directory-prefix extension)))
+ │ (unless (file-directory-p export-dir)
+ │ (make-directory export-dir)))
+ │ (apply fn extension args))
+ │
+ │ (advice-add #'org-export-output-file-name :around #'my-org-export-create-directory)
+ └────
+
+ The target export directory should not be a subdirectory of
+ `denote-directory', as that will result in duplicate identifiers.
+ Exclude it with the `denote-excluded-directories-regexp' user option
+ ([Exclude certain directories from all operations]).
+
+ [ NOTE: I (Protesilaos) am not a LaTeX user and cannot test the
+ following. ]
+
+ Using a different directory will require some additional configuration
+ when exporting using LaTeX. The export folder cannot be inside the
+ path of the `denote-directory' to prevent Denote from recognising it
+ as an attachment:
+ <https://emacs.stackexchange.com/questions/45751/org-export-to-different-directory>.
+
+
+ [Exclude certain directories from all operations] See section 5.9
+
+
+◊ 19.14.1.3 Org Mode Publishing
+
+ Org Mode also has a publishing tool for exporting a collection of
+ files. Some user might apply this approach to convert their note
+ collection to a public or private website.
+
+ The `org-publish-project-alist' variable drives the publishing
+ process, including the publishing directory.
+
+ The publishing directory should not be a subdirectory of
+ `denote-directory', as that will result in duplicate identifiers.
+ Exclude it with the `denote-excluded-directories-regexp' user option
+ ([Exclude certain directories from all operations]).
+
+
+ [Exclude certain directories from all operations] See section 5.9
+
+
+19.14.2 Export Denote notes with Markdown
+╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+
+ Exporting from Markdown requires an external processor (e.g.,
+ Markdown.pl, Pandoc, or MultiMarkdown). The `markdown-command'
+ variable defines the command line used in export, for example:
+
+ ┌────
+ │ (setq markdown-command "multimarkdown")
+ └────
+
+ The export process thus occurs outside of Emacs. Users need to read
+ the documentation of their preferred processor to prevent the creation
+ of duplicate Denote identifiers.
+
+
+19.15 Set up your workflow for daily or weekly meeting notes
+────────────────────────────────────────────────────────────
+
+ Perhaps as part of work, we meet with certain people on a regular
+ basis. During the meeting we may discuss a variety of topics. How best
+ to approach with the help of Denote?
+
+ One option is to write a new file for each meeting, giving it the
+ appropriate keywords each time ([Points of entry]). This is what
+ Denote does by default and does not need any further tweaks. If we
+ need to review those notes, we can use the command `denote-sort-dired'
+ ([Sort files by component]), or one of the Org dynamic blocks we
+ provide ([Use Org dynamic blocks]), among other options.
+
+ Another approach is to write one file per person with the regular
+ `denote' command (or related), give it the name of the person as a
+ title and, optionally, use some relevant keywords. Inside each file,
+ write a top-level heading with the date of the meeting, and then
+ produce the meeting notes below as paragraphs and subheadings. This
+ can all be done without any changes to Denote, though we can
+ streamline it by incorporating the following code in our setup.
+ Configure `my-denote-colleagues' and then use the command
+ `my-denote-colleagues-new-meeting' to see how it works.
+
+ ┌────
+ │ (defvar my-denote-colleagues '("Prot" "Protesilaos")
+ │ "List of names I collaborate with.
+ │ There is at least one file in the variable `denote-directory' that has
+ │ the name of this person.")
+ │
+ │ (defvar my-denote-colleagues-prompt-history nil
+ │ "Minibuffer history for `my-denote-colleagues-new-meeting'.")
+ │
+ │ (defun my-denote-colleagues-prompt ()
+ │ "Prompt with completion for a name among `my-denote-colleagues'.
+ │ Use the last input as the default value."
+ │ (let ((default-value (car my-denote-colleagues-prompt-history)))
+ │ (completing-read
+ │ (format-prompt "New meeting with COLLEAGUE" default-value)
+ │ my-denote-colleagues
+ │ nil :require-match nil
+ │ 'my-denote-colleagues-prompt-history
+ │ default-value)))
+ │
+ │ (defun my-denote-colleagues-get-file (name)
+ │ "Find file in variable `denote-directory' for NAME colleague.
+ │ If there are more than one files, prompt with completion for one among
+ │ them.
+ │
+ │ NAME is one among `my-denote-colleagues'."
+ │ (if-let* ((files (denote-directory-files name))
+ │ (length-of-files (length files)))
+ │ (cond
+ │ ((= length-of-files 1)
+ │ (car files))
+ │ ((> length-of-files 1)
+ │ (completing-read "Select a file: " files nil :require-match)))
+ │ (user-error "No files for colleague with name `%s'" name)))
+ │
+ │ (defun my-denote-colleagues-new-meeting ()
+ │ "Prompt for the name of a colleague and insert a timestamped heading therein.
+ │ The name of a colleague corresponds to at least one file in the variable
+ │ `denote-directory'. In case there are multiple files, prompt to choose
+ │ one among them and operate therein."
+ │ (declare (interactive-only t))
+ │ (interactive)
+ │ (let* ((name (my-denote-colleagues-prompt))
+ │ (file (my-denote-colleagues-get-file name))
+ │ (time (format-time-string "%F %a %R"))) ; remove %R if you do not want the time
+ │ (with-current-buffer (find-file file)
+ │ (goto-char (point-max))
+ │ ;; Here I am assuming we are in `org-mode', hence the leading
+ │ ;; asterisk for the heading. Adapt accordingly.
+ │ (insert (format "* [%s]\n\n" time)))))
+ └────
+
+
+[Points of entry] See section 5
+
+[Sort files by component] See section 14
+
+[Use Org dynamic blocks] See section 13
+
+
+20 For developers or advanced users
+═══════════════════════════════════
+
+ Denote is in a stable state and can be relied upon as the basis for
+ custom extensions ([Packages that build on Denote]). Further below is
+ a list with the functions or variables we provide for public usage.
+ Those are in addition to all user options and commands that are
+ already documented in the various sections of this manual.
+
+ In this context “public” is any form with single hyphens in its
+ symbol, such as `denote-directory-files'. We expressly support those,
+ meaning that we consider them reliable and commit to documenting any
+ changes in their particularities (such as through `make-obsolete', a
+ record in the change log, a blog post on the maintainer’s website, and
+ the like).
+
+ By contradistinction, a “private” form is declared with two hyphens in
+ its symbol such as `denote--file-extension'. Do not use those as we
+ might change them without further notice.
+
+ The following sections cover the specifics.
+
+
+[Packages that build on Denote] See section 18
+
+20.1 Common building blocks for developers or advanced users
+────────────────────────────────────────────────────────────
+
+ Variable `denote-id-format'
+ Format of ID prefix of a note’s filename. The note’s ID is
+ derived from the date and time of its creation ([The file-naming
+ scheme]).
+
+ Variable `denote-id-regexp'
+ Regular expression to match `denote-id-format'.
+
+ Variable `denote-signature-regexp'
+ Regular expression to match the `SIGNATURE' field in a file
+ name.
+
+ Variable `denote-title-regexp'
+ Regular expression to match the `TITLE' field in a file name
+ ([The file-naming scheme]).
+
+ Variable `denote-keywords-regexp'
+ Regular expression to match the `KEYWORDS' field in a file name
+ ([The file-naming scheme]).
+
+ Function `denote-identifier-p'
+ Return non-nil if `IDENTIFIER' string is a Denote identifier.
+
+ Function `denote-file-is-note-p'
+ Return non-nil if `FILE' is an actual Denote note. For our
+ purposes, a note must satisfy `file-regular-p' and
+ `denote-filename-is-note-p'.
+
+ Function `denote-file-has-identifier-p'
+ Return non-nil if `FILE' has a Denote identifier.
+
+ Function `denote-file-has-denoted-filename-p'
+ Return non-nil if `FILE' respects the file-naming scheme of
+ Denote. This tests the rules of Denote’s file-naming
+ scheme. Sluggification is ignored. It is done by removing all
+ file name components and validating what remains.
+
+ Function `denote-file-has-signature-p'
+ Return non-nil if `FILE' has a signature.
+
+ Function `denote-file-has-supported-extension-p'
+ Return non-nil if `FILE' has supported extension. Also account
+ for the possibility of an added `.gpg' suffix. Supported
+ extensions are those implied by `denote-file-type'.
+
+ Function `denote-file-is-writable-and-supported-p'
+ Return non-nil if `FILE' is writable and has supported
+ extension.
+
+ Function `denote-file-type-extensions'
+ Return all file type extensions in `denote-file-types'.
+
+ Variable `denote-encryption-file-extensions'
+ List of strings specifying file extensions for encryption.
+
+ Function `denote-file-type-extensions-with-encryption'
+ Derive `denote-file-type-extensions' plus
+ `denote-encryption-file-extensions'.
+
+ Function `denote-get-file-extension'
+ Return extension of `FILE' with dot included. Account for
+ `denote-encryption-file-extensions'. In other words, return
+ something like `.org.gpg' if it is part of the file, else return
+ `.org'.
+
+ Function `denote-get-file-extension-sans-encryption'
+ Return extension of `FILE' with dot included and without the
+ encryption part. Build on top of `denote-get-file-extension'
+ though always return something like `.org' even if the actual
+ file extension is `.org.gpg'.
+
+ Functions `denote-infer-keywords-from-files'
+ Return list of keywords in `denote-directory-files'. With
+ optional `FILES-MATCHING-REGEXP', only extract keywords from the
+ matching files. Otherwise, do it for all files. Keep any
+ duplicates. Users who do not want duplicates should refer to the
+ functions `denote-keywords'.
+
+ Function `denote-keywords'
+ Return appropriate list of keyword candidates. If
+ `denote-infer-keywords' is non-nil, infer keywords from existing
+ notes and combine them into a list with
+ `denote-known-keywords'. Else use only the latter set of
+ keywords ([Standard note creation]). In the case of keyword
+ inferrence, use optional `FILES-MATCHING-REGEXP', to extract
+ keywords only from the matching files. Otherwise, do it for all
+ files. Filter inferred keywords with the user option
+ `denote-excluded-keywords-regexp'.
+
+ Function `denote-keywords-sort'
+ Sort `KEYWORDS' if `denote-sort-keywords' is non-nil.
+ `KEYWORDS' is a list of strings, per `denote-keywords-prompt'.
+
+ Function `denote-keywords-combine'
+ Combine `KEYWORDS' list of strings into a single
+ string. Keywords are separated by the underscore character, per
+ the Denote file-naming scheme.
+
+ Function `denote-valid-date-p'
+ Return `DATE' as a valid date. A valid `DATE' is a value that
+ can be parsed by either `decode-time' or `date-to-time' .Those
+ functions signal an error if `DATE' is a value they do not
+ recognise. If `DATE' is nil, return nil.
+
+ Function `denote-directory'
+ Return path of the variable `denote-directory' as a proper
+ directory, also because it accepts a directory-local value for
+ what we internally refer to as “silos” ([Maintain separate
+ directories for notes]). Custom Lisp code can `let' bind the
+ value of the variable `denote-directory' to override what this
+ function returns.
+
+ Function `denote-directory-files'
+ Return list of absolute file paths in variable
+ `denote-directory'. Files that match
+ `denote-excluded-files-regexp' are excluded from the list. Files
+ only need to have an identifier. The return value may thus
+ include file types that are not implied by
+ `denote-file-type'. With optional `FILES-MATCHING-REGEXP',
+ restrict files to those matching the given regular
+ expression. With optional `OMIT-CURRENT' as a non-nil value, do
+ not include the current Denote file in the returned list. With
+ optional `TEXT-ONLY' as a non-nil value, limit the results to
+ text files that satisfy `denote-file-is-note-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.
+
+ Function `denote-directory-subdirectories'
+ Return list of subdirectories in variable
+ `denote-directory'. Omit dotfiles (such as .git)
+ unconditionally. Also exclude whatever matches
+ `denote-excluded-directories-regexp'. Note that the
+ `denote-directory' accepts a directory-local value for what we
+ call “silos” ([Maintain separate directories for notes]).
+
+
+[The file-naming scheme] See section 7
+
+[Standard note creation] See section 5.1
+
+[Maintain separate directories for notes] See section 5.7
+
+
+20.2 File path interface for developers or advanced users
+─────────────────────────────────────────────────────────
+
+ Function `denote-file-name-relative-to-denote-directory'
+ Return name of `FILE' relative to the variable
+ `denote-directory'. `FILE' must be an absolute path.
+
+ Function `denote-slug-keep-only-ascii'
+ Remove all non-ASCII characters from `STR' and replace them with
+ spaces. This is useful as a helper function to construct
+ `denote-file-name-slug-functions' ([Custom sluggification to
+ remove non-ASCII characters]).
+
+ Function `denote-sluggify'
+ Make `STR' an appropriate slug for file name `COMPONENT'
+ ([Sluggification of file name components]). 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.
+
+ Function `denote-sluggify-keyword'
+ Sluggify `STR' while joining separate words.
+
+ Function `denote-sluggify-signature'
+ Make `STR' an appropriate slug for signatures ([Sluggification
+ of file name components]).
+
+ Function `denote-sluggify-keywords'
+ Sluggify `KEYWORDS', which is a list of strings ([Sluggification
+ of file name components]).
+
+ Function `denote-use-date'
+ The date to be used in a note creation command. See the
+ documentation of `denote' for acceptable values. This variable
+ is ignored if nil. Only ever `let' bind this, otherwise the
+ title will always be the same and the title prompt will be
+ skipped.
+
+ Function `denote-use-directory'
+ The directory to be used in a note creation command. See the
+ documentation of `denote' for acceptable values. This variable
+ is ignored if nil. Only ever `let' bind this, otherwise the
+ title will always be the same and the title prompt will be
+ skipped.
+
+ Function `denote-use-file-type'
+ The file type to be used in a note creation command. See the
+ documentation of `denote' for acceptable values. This variable
+ is ignored if nil. Only ever `let' bind this, otherwise the
+ title will always be the same and the title prompt will be
+ skipped.
+
+ Function `denote-use-keywords'
+ The keywords to be used in a note creation command. See the
+ documentation of `denote' for acceptable values. This variable
+ is ignored if `default'. Only ever `let' bind this, otherwise
+ the title will always be the same and the title prompt will be
+ skipped.
+
+ Function `denote-use-signature'
+ The signature to be used in a note creation command. See the
+ documentation of `denote' for acceptable values. This variable
+ is ignored if nil. Only ever `let' bind this, otherwise the
+ title will always be the same and the title prompt will be
+ skipped.
+
+ Function `denote-use-template'
+ The template to be used in a note creation command. See the
+ documentation of `denote' for acceptable values. This variable
+ is ignored if nil. Only ever `let' bind this, otherwise the
+ title will always be the same and the title prompt will be
+ skipped.
+
+ Function `denote-use-title'
+ The title to be used in a note creation command. See the
+ documentation of `denote' for acceptable values. This variable
+ is ignored if nil. Only ever `let' bind this, otherwise the
+ title will always be the same and the title prompt will be
+ skipped.
+
+ Function `denote-format-file-name'
+ Format file name. `DIR-PATH', `ID', `KEYWORDS', `TITLE',
+ `EXTENSION' and `SIGNATURE' are expected to be supplied by
+ `denote' or equivalent command.
+
+ `DIR-PATH' is a string pointing to a directory. It ends with a
+ forward slash (the function `denote-directory' makes sure this
+ is the case when returning the value of the variable
+ `denote-directory'). `DIR-PATH' cannot be nil or an empty
+ string.
+
+ `ID' is a string holding the identifier of the note. It can be
+ an empty string, in which case its respective file name
+ component is not added to the base file name.
+
+ `DIR-PATH' and `ID' form the base file name.
+
+ `KEYWORDS' is a list of strings that is reduced to a single
+ string by `denote-keywords-combine'. `KEYWORDS' can be an empty
+ list or a nil value, in which case the relevant file name
+ component is not added to the base file name.
+
+ `TITLE' and `SIGNATURE' are strings. They can be an empty
+ string, in which case their respective file name component is
+ not added to the base file name.
+
+ `EXTENSION' is a string that contains a dot followed by the file
+ type extension. It can be an empty string or a nil value, in
+ which case it is not added to the base file name.
+
+
+[Custom sluggification to remove non-ASCII characters] See section 7.3.1
+
+[Sluggification of file name components] See section 7.2
+
+
+20.3 Data retrieval interface for developers or advanced users
+──────────────────────────────────────────────────────────────
+
+ Function `denote-get-path-by-id'
+ Return absolute path of `ID' string in `denote-directory-files'.
+
+ Function `denote-get-identifier-at-point'
+ Return the identifier at point or `POINT'.
+
+ Function `denote-extract-keywords-from-path'
+ Extract keywords from `PATH' and return them as a list of
+ strings. `PATH' must be a Denote-style file name where keywords
+ are prefixed with an underscore. If `PATH' has no such
+ keywords, which is possible, return nil ([The file-naming
+ scheme]).
+
+ Function `denote-extract-id-from-string'
+ Return existing Denote identifier in `STRING', else nil.
+
+ Function `denote-retrieve-filename-identifier'
+ Extract identifier from `FILE' name, if present, else return
+ nil. To create a new one from a date, refer to the
+ `denote-get-identifier' function.
+
+ Function `denote-retrieve-filename-title'
+ Extract Denote title component from `FILE' name, if present,
+ else return nil.
+
+ Function `denote-retrieve-filename-keywords'
+ Extract keywords from `FILE' name, if present, else return
+ nil. Return matched keywords as a single string.
+
+ Function `denote-retrieve-filename-signature'
+ Extract signature from `FILE' name, if present, else return nil.
+
+ Function `denote-retrieve-title-or-filename'
+ Return appropriate title for `FILE' given its `TYPE'. This is a
+ wrapper for `denote-retrieve-front-matter-title-value' and
+ `denote-retrieve-filename-title'.
+
+ Function `denote-get-identifier'
+ Convert `DATE' into a Denote identifier using
+ `denote-id-format'. If `DATE' is nil, return an empty string as
+ the identifier.
+
+ Function `denote-retrieve-front-matter-title-value'
+ Return title value from `FILE' front matter per `FILE-TYPE'.
+
+ Function `denote-retrieve-front-matter-title-line'
+ Return title line from `FILE' front matter per `FILE-TYPE'.
+
+ Function `denote-retrieve-front-matter-keywords-value'
+ Return keywords value from `FILE' front matter per
+ `FILE-TYPE'. The return value is a list of strings.
+
+ Function `denote-retrieve-front-matter-keywords-line'
+ Return keywords line from `FILE' front matter per `FILE-TYPE'.
+
+
+[The file-naming scheme] See section 7
+
+
+20.4 Prompt interface for developers or advanced users
+──────────────────────────────────────────────────────
+
+ Function `denote-add-prompts'
+ Add list of `ADDITIONAL-PROMPTS' to `denote-prompts'. This is
+ best done inside of a `let' to create a wrapper function around
+ `denote', `denote-rename-file', and generally any command that
+ consults the value of `denote-prompts'.
+
+ Function `denote-signature-prompt'
+ Prompt for signature string. With optional `INITIAL-SIGNATURE'
+ use it as the initial minibuffer text. With optional
+ `PROMPT-TEXT' use it in the minibuffer instead of the default
+ prompt. Previous inputs at this prompt are available for
+ minibuffer completion if the user option
+ `denote-history-completion-in-prompts' is set to a non-nil value
+ ([The `denote-history-completion-in-prompts' option]).
+
+ Function `denote-file-prompt'
+ Prompt for file in variable `denote-directory'. Files that match
+ `denote-excluded-files-regexp' are excluded from the list. With
+ optional `FILES-MATCHING-REGEXP', filter the candidates per the
+ given regular expression. With optional `PROMPT-TEXT', use it
+ instead of the default call to select a file. With optional
+ `NO-REQUIRE-MATCH' accept the given input as-is. Return the
+ absolute path to the matching file.
+
+ Function `denote-keywords-prompt'
+ Prompt for one or more keywords. Read entries as separate when
+ they are demarcated by the `crm-separator', which typically is a
+ comma. With optional `PROMPT-TEXT', use it to prompt the user
+ for keywords. Else use a generic prompt. With optional
+ `INITIAL-KEYWORDS' use them as the initial minibuffer text.
+
+ Function `denote-title-prompt'
+ Prompt for title string. With optional `INITIAL-TITLE' use it as
+ the initial minibuffer text. With optional `PROMPT-TEXT' use it
+ in the minibuffer instead of the default prompt. Previous inputs
+ at this prompt are available for minibuffer completion if the
+ user option `denote-history-completion-in-prompts' is set to a
+ non-nil value ([The `denote-history-completion-in-prompts'
+ option]).
+
+ Variable `denote-title-prompt-current-default'
+ Currently bound default title for `denote-title-prompt'. Set
+ the value of this variable within the lexical scope of a command
+ that needs to supply a default title before calling
+ `denote-title-prompt' and use `unwind-protect' to set its value
+ back to nil.
+
+ Function `denote-file-type-prompt'
+ Prompt for `denote-file-type'. Note that a non-nil value other
+ than `text', `markdown-yaml', and `markdown-toml' falls back to
+ an Org file type. We use `org' here for clarity.
+
+ Function `denote-date-prompt'
+ Prompt for date, expecting `YYYY-MM-DD' or that plus `HH:MM' (or
+ even `HH:MM:SS'). Use Org’s more advanced date selection utility
+ if the user option `denote-date-prompt-use-org-read-date' is
+ non-nil. It requires Org ([The
+ denote-date-prompt-use-org-read-date option]). With optional
+ `INITIAL-DATE' use it as the initial minibuffer text. With
+ optional `PROMPT-TEXT' use it in the minibuffer instead of the
+ default prompt. `INITIAL-DATE' is a string that can be processed
+ by `denote-valid-date-p', a value that can be parsed by
+ `decode-time' or nil.
+
+ Function `denote-command-prompt'
+ Prompt for command among `denote-commands-for-new-notes'
+ ([Points of entry]).
+
+ Variable `denote-prompts-with-history-as-completion'
+ Prompts that conditionally perform completion against their
+ history. These are minibuffer prompts that ordinarily accept a
+ free form string input, as opposed to matching against a
+ predefined set. These prompts can optionally perform completion
+ against their own minibuffer history when the user option
+ `denote-history-completion-in-prompts' is set to a non-nil value
+ ([The `denote-history-completion-in-prompts' option]).
+
+ Function `denote-files-matching-regexp-prompt'
+ Prompt for `REGEXP' to filter Denote files by. With optional
+ `PROMPT-TEXT' use it instead of a generic prompt.
+
+ Function `denote-prompt-for-date-return-id'
+ Use `denote-date-prompt' and return it as `denote-id-format'.
+
+ Function `denote-template-prompt'
+ Prompt for template key in `denote-templates' and return its
+ value.
+
+ Function `denote-subdirectory-prompt'
+ Prompt for subdirectory of the variable `denote-directory'. The
+ table uses the `file' completion category (so it works with
+ packages such as `marginalia' and `embark').
+
+
+[The `denote-history-completion-in-prompts' option] See section 5.1.2
+
+[The denote-date-prompt-use-org-read-date option] See section 5.1.7
+
+[Points of entry] See section 5
+
+
+20.5 Front matter interface for developers or advanced users
+────────────────────────────────────────────────────────────
+
+ Function `denote-filetype-heuristics'
+ Return likely file type of `FILE'. If in the process of
+ `org-capture', consider the file type to be that of
+ Org. Otherwise, use the file extension to detect the file type
+ of `FILE'.
+
+ If more than one file type correspond to this file extension,
+ use the first file type for which the :title-key-regexp in
+ `denote-file-types' matches in the file.
+
+ Return nil if the file type is not recognized.
+
+ Variable `denote-org-front-matter'
+ Specifies the Org front matter. It is passed to `format' with
+ arguments `TITLE', `DATE', `KEYWORDS', `ID' ([Change the front
+ matter format])
+
+ Variable `denote-yaml-front-matter'
+ Specifies the YAML (Markdown) front matter. It is passed to
+ `format' with arguments `TITLE', `DATE', `KEYWORDS', `ID'
+ ([Change the front matter format])
+
+ Variable `denote-toml-front-matter'
+ Specifies the TOML (Markdown) front matter. It is passed to
+ `format' with arguments `TITLE', `DATE', `KEYWORDS', `ID'
+ ([Change the front matter format])
+
+ Variable `denote-text-front-matter'
+ Specifies the plain text front matter. It is passed to `format'
+ with arguments `TITLE', `DATE', `KEYWORDS', `ID' ([Change the
+ front matter format])
+
+ Function `denote-date-org-timestamp'
+ Format `DATE' using the Org inactive timestamp notation.
+
+ Function `denote-date-rfc3339'
+ Format `DATE' using the RFC3339 specification.
+
+ Function `denote-date-iso-8601'
+ Format `DATE' according to ISO 8601 standard.
+
+ Function `denote-trim-whitespace'
+ Trim whitespace around string `S'. This can be used in
+ `denote-file-types' to format front mattter.
+
+ Function `denote-trim-whitespace-then-quotes'
+ Trim whitespace then quotes around string `S'. This can be used
+ in `denote-file-types' to format front mattter.
+
+ Function `denote-format-string-for-org-front-matter'
+ Return string `S' as-is for Org or plain text front matter. If
+ `S' is not a string, return an empty string.
+
+ Function `denote-format-string-for-md-front-matter'
+ Surround string `S' with quotes. If `S' is not a string, return
+ a literal emptry string. This can be used in `denote-file-types'
+ to format front mattter.
+
+ Function `denote-format-keywords-for-md-front-matter'
+ Format front matter `KEYWORDS' for markdown file type.
+ `KEYWORDS' is a list of strings. Consult the
+ `denote-file-types' for how this is used.
+
+ Function `denote-format-keywords-for-text-front-matter'
+ Format front matter `KEYWORDS' for text file type. `KEYWORDS'
+ is a list of strings. Consult the `denote-file-types' for how
+ this is used.
+
+ Function `denote-format-keywords-for-org-front-matter'
+ Format front matter `KEYWORDS' for org file type. `KEYWORDS' is
+ a list of strings. Consult the `denote-file-types' for how this
+ is used.
+
+ Function `denote-extract-keywords-from-front-matter'
+ Format front matter `KEYWORDS' for org file type. `KEYWORDS' is
+ a list of strings. Consult the `denote-file-types' for how this
+ is used.
+
+ Variable `denote-file-types'
+ Alist of `denote-file-type' and their format properties.
+
+ Each element is of the form `(SYMBOL PROPERTY-LIST)'. `SYMBOL'
+ is one of those specified in `denote-file-type' or an arbitrary
+ symbol that defines a new file type.
+
+ `PROPERTY-LIST' is a plist that consists of the following
+ elements:
+
+ 1. `:extension' is a string with the file extension including
+ the period.
+
+ 2. `:date-function' is a function that can format a date. See
+ the functions `denote--date-iso-8601',
+ `denote--date-rfc3339', and `denote--date-org-timestamp'.
+
+ 3. `:front-matter' is either a string passed to `format' or a
+ variable holding such a string. The `format' function
+ accepts four arguments, which come from `denote' in this
+ order: `TITLE', `DATE', `KEYWORDS', `IDENTIFIER'. Read the
+ doc string of `format' on how to reorder arguments.
+
+ 4. `:title-key-regexp' is a regular expression that is used to
+ retrieve the title line in a file. The first line matching
+ this regexp is considered the title line.
+
+ 5. `:title-value-function' is the function used to format the
+ raw title string for inclusion in the front matter (e.g. to
+ surround it with quotes). Use the `identity' function if no
+ further processing is required.
+
+ 6. `:title-value-reverse-function' is the function used to
+ retrieve the raw title string from the front matter. It
+ performs the reverse of `:title-value-function'.
+
+ 7. `:keywords-key-regexp' is a regular expression used to
+ retrieve the keywords’ line in the file. The first line
+ matching this regexp is considered the keywords’ line.
+
+ 8. `:keywords-value-function' is the function used to format the
+ keywords’ list of strings as a single string, with
+ appropriate delimiters, for inclusion in the front matter.
+
+ 9. `:keywords-value-reverse-function' is the function used to
+ retrieve the keywords’ value from the front matter. It
+ performs the reverse of the `:keywords-value-function'.
+
+ 10. `:link' is a string, or variable holding a string, that
+ specifies the format of a link. See the variables
+ `denote-org-link-format', `denote-md-link-format'.
+
+ 11. `:link-in-context-regexp' is a regular expression that is
+ used to match the aforementioned link format. See the
+ variables `denote-org-link-in-context-regexp',
+ `denote-md-link-in-context-regexp'.
+
+ If `denote-file-type' is nil, use the first element of this list
+ for new note creation. The default is `org'.
+
+
+[Change the front matter format] See section 8.1
+
+
+20.6 Link interface for developers or advanced users
+────────────────────────────────────────────────────
+
+ Variable `denote-org-link-format'
+ Format of Org link to note. The value is passed to `format'
+ with `IDENTIFIER' and `TITLE' arguments, in this order. Also
+ see `denote-org-link-in-context-regexp'.
+
+ Variable `denote-md-link-format'
+ Format of Markdown link to note. The `%N$s' notation used in
+ the default value is for `format' as the supplied arguments are
+ `IDENTIFIER' and `TITLE', in this order. Also see
+ `denote-md-link-in-context-regexp'.
+
+ Variable `denote-id-only-link-format'
+ Format of identifier-only link to note. The value is passed to
+ `format' with `IDENTIFIER' as its sole argument. Also see
+ `denote-id-only-link-in-context-regexp'.
+
+ Variable `denote-org-link-in-context-regexp'
+ Regexp to match an Org link in its context. The format of such
+ links is `denote-org-link-format'.
+
+ Variable `denote-md-link-in-context-regexp'
+ Regexp to match an Markdown link in its context. The format of
+ such links is `denote-md-link-format'.
+
+ Variable `denote-id-only-link-in-context-regexp'
+ Regexp to match an identifier-only link in its context. The
+ format of such links is `denote-id-only-link-format'.
+
+ Function `denote-select-linked-file-prompt'
+ Prompt for linked file among `FILES'.
+
+ Function `denote-link-return-links'
+ Return list of links in current or optional `FILE'. Also see
+ `denote-link-return-backlinks'.
+
+ Function `denote-link-return-backlinks'
+ Return list of backlinks in current or optional `FILE'. Also
+ see `denote-link-return-links'.
+
+ Function `denote-link-description-with-signature-and-title'
+ Return link description for `FILE'. Produce a description as
+ follows:
+
+ • If the region is active, use it as the description.
+
+ • If `FILE' as a signature, then use the
+ `denote-link-signature-format'. By default, this looks like
+ “signature title”.
+
+ • If `FILE' does not have a signature, then use its title as the
+ description.
+
+ Variable `denote-link-description-function'
+ Function to use to create the description of links. The function
+ specified should take a `FILE' argument and should return the
+ description as a string. By default, the title of the file is
+ returned as the description.
+
+
+20.7 Xref interface for developers or advanced users
+────────────────────────────────────────────────────
+
+ Function `denote-retrieve-groups-xref-query'
+ Access location of xrefs for `QUERY' and group them per
+ file. Limit the search to text files.
+
+ Function `denote-retrieve-files-xref-query'
+ Return sorted, deduplicated file names with matches for `QUERY'
+ in their contents. Limit the search to text files.
+
+ Function `denote-retrieve-xref-alist'
+ Return xref alist of files with location of matches for
+ `QUERY'. With optional `FILES-MATCHING-REGEXP', limit the list
+ of files accordingly (per `denote-directory-files'). At all
+ times limit the search to text files.
+
+
+20.8 Renaming files interface for developers or advanced users
+──────────────────────────────────────────────────────────────
+
+ Function `denote-rename-file-prompt'
+ Prompt to rename file named `OLD-NAME' to `NEW-NAME'. If
+ `denote-rename-confirmations' does not contain
+ `modify-file-name', return t without prompting.
+
+ Function `denote-rename-file-and-buffer'
+ Rename file named `OLD-NAME' to `NEW-NAME', updating buffer
+ name.
+
+ Function `denote-prepend-front-matter'
+ Prepend front matter to `FILE'. The `TITLE', `KEYWORDS', `DATE',
+ `ID', `SIGNATURE', and `FILE-TYPE' are passed from the renaming
+ command and are used to construct a new front matter block if
+ appropriate.
+
+ Function `denote-rewrite-front-matter'
+ Rewrite front matter of note after `denote-rename-file' (or
+ related). The `FILE', `TITLE', `KEYWORDS', `SIGNATURE', `DATE',
+ `IDENTIFIER', and `FILE-TYPE' arguments are given by the
+ renaming command and are used to construct new front matter
+ values if appropriate. If `denote-rename-confirmations' contains
+ `rewrite-front-matter', prompt to confirm the rewriting of the
+ front matter. Otherwise produce a `y-or-n-p' prompt to that
+ effect.
+
+ Function `denote-add-front-matter-prompt'
+ Prompt to add a front-matter to `FILE'. Return non-nil if a new
+ front matter should be added. If `denote-rename-confirmations'
+ does not contain `add-front-matter', return t without prompting.
+
+ Function `denote-rewrite-keywords'
+ Rewrite `KEYWORDS' in `FILE' outright according to
+ `FILE-TYPE'. Do the same as `denote-rewrite-front-matter' for
+ keywords, but do not ask for confirmation. With optional
+ `SAVE-BUFFER', save the buffer corresponding to `FILE'. This
+ function is for use in the commands `denote-keywords-add',
+ `denote-keywords-remove', `denote-dired-rename-files', or
+ related.
+
+ Function `denote-update-dired-buffers'
+ Update Dired buffers of variable `denote-directory'. Also revert
+ the current Dired buffer even if it is not inside the
+ `denote-directory'. Note that the `denote-directory' accepts a
+ directory-local value for what we internally refer to as “silos”
+ ([Maintain separate directories for notes]).
+
+
+[Maintain separate directories for notes] See section 5.7
+
+
+21 Troubleshoot Denote in a pristine environment
+════════════════════════════════════════════════
+
+ Sometimes we get reports on bugs that may not be actually caused by
+ some error in the Denote code base. To help gain insight into what
+ the problem is, we need to be able to reproduce the issue in a minimum
+ viable system. Below is one way to achieve this.
+
+ 1. Find where your `denote.el' file is stored on your filesystem.
+
+ 2. Assuming you have already installed the package, one way to do this
+ is to invoke `M-x find-library' and search for `denote'. It will
+ take you to the source file. There do `M-x eval-expression', which
+ will bring up a minibuffer prompt. At the prompt evaluate:
+
+ ┌────
+ │ (kill-new (expand-file-name (buffer-file-name)))
+ └────
+
+ 1. The above will save the full file system path to your kill ring.
+
+ 2. In a terminal emulator or an `M-x shell' buffer execute:
+
+ ┌────
+ │ emacs -Q
+ └────
+
+ 1. This will open a new instance of Emacs in a pristine environment.
+ Only the default settings are loaded.
+
+ 2. In the `*scratch*' buffer of `emacs -Q', add your configurations
+ like the following and try to reproduce the issue:
+
+ ┌────
+ │ (require 'denote "/full/path/to/what/you/got/denote.el")
+ │
+ │ ;; Your configurations here
+ └────
+
+ Then try to see if your problem still occurs. If it does, then the
+ fault is with Denote. Otherwise there is something external to it
+ that we need to account for. Whatever the case, this exercise helps
+ us get a better sense of the specifics.
+
+
+22 Contributing
+═══════════════
+
+ Denote is a GNU ELPA package. As such, any significant change to the
+ code requires copyright assignment to the Free Software Foundation
+ (more below).
+
+ You do not need to be a programmer to contribute to this package.
+ Sharing an idea or describing a workflow is equally helpful, as it
+ teaches us something we may not know and might be able to cover either
+ by extending Denote or expanding this manual. If you prefer to write a
+ blog post, make sure you share it with us: we can add a section herein
+ referencing all such articles. Everyone gets acknowledged
+ ([Acknowledgements]). There is no such thing as an “insignificant
+ contribution”—they all matter.
+
+ ⁃ Package name (GNU ELPA): `denote'
+ ⁃ Official manual: <https://protesilaos.com/emacs/denote>
+ ⁃ Change log: <https://protesilaos.com/emacs/denote-changelog>
+ ⁃ Git repositories:
+ ⁃ GitHub: <https://github.com/protesilaos/denote>
+ ⁃ GitLab: <https://gitlab.com/protesilaos/denote>
+
+ If our public media are not suitable, you are welcome to contact me
+ (Protesilaos) in private: <https://protesilaos.com/contact>.
+
+ Copyright assignment is a prerequisite to sharing code. It is a simple
+ process. Check the request form below (please adapt it accordingly).
+ You must write an email to the address mentioned in the form and then
+ wait for the FSF to send you a legal agreement. Sign the document and
+ file it back to them. This could all happen via email and take about a
+ week. You are encouraged to go through this process. You only need to
+ do it once. It will allow you to make contributions to Emacs in
+ general.
+
+ ┌────
+ │ Please email the following information to assign@gnu.org, and we
+ │ will send you the assignment form for your past and future changes.
+ │
+ │ Please use your full legal name (in ASCII characters) as the subject
+ │ line of the message.
+ │
+ │ REQUEST: SEND FORM FOR PAST AND FUTURE CHANGES
+ │
+ │ [What is the name of the program or package you're contributing to?]
+ │
+ │ GNU Emacs
+ │
+ │ [Did you copy any files or text written by someone else in these changes?
+ │ Even if that material is free software, we need to know about it.]
+ │
+ │ Copied a few snippets from the same files I edited. Their author,
+ │ Protesilaos Stavrou, has already assigned copyright to the Free Software
+ │ Foundation.
+ │
+ │ [Do you have an employer who might have a basis to claim to own
+ │ your changes? Do you attend a school which might make such a claim?]
+ │
+ │
+ │ [For the copyright registration, what country are you a citizen of?]
+ │
+ │
+ │ [What year were you born?]
+ │
+ │
+ │ [Please write your email address here.]
+ │
+ │
+ │ [Please write your postal address here.]
+ │
+ │
+ │
+ │
+ │
+ │ [Which files have you changed so far, and which new files have you written
+ │ so far?]
+ │
+ └────
+
+
+[Acknowledgements] See section 26
+
+22.1 Wishlist of what we can do to extend Denote
+────────────────────────────────────────────────
+
+ These are various ideas to extend Denote. Whether they should be in
+ the core package or a separate extension is something we can discuss.
+ I, Protesilaos, am happy to help anyone who wants to do any of this.
+
+ denote-embark.el
+ Provide integration with the `embark' package. This can be for
+ doing something with the identifier/link at point. For example,
+ it could provide an action to produce backlinks for the
+ identifier/file we are linking to, not just the current one.
+
+ denote-transient.el
+ The `transient' package is built into Emacs 29 (Denote supports
+ Emacs 28 though). We can use it to define an alternative to what
+ we have for the menu bar. Perhaps this interface can used to
+ toggle various options, such as to call `denote' with a
+ different set of prompts.
+
+ A `denote-directories' user option
+ This can be either an extension of the `denote-directory'
+ (accept a list of file paths value) or a new variable. The idea
+ is to let the user define separate Denote directories which do
+ know about the presence of each other (unlike silos). This way,
+ a user can have an entry in `~/Documents/notes/' link to
+ something `~/Git/projects/' and everything work as if the
+ `denote-directory' is set to the `~/' (with the status quo as of
+ 2024-02-18 08:27 +0200).
+
+ Encode the day in the identifier
+ The idea is to use some coded reference for Monday, Tuesday,
+ etc. instead of having the generic `T' in the identifier. For
+ example, Monday is `A' so the identifier for it is something
+ like `20240219A101522' instead of what we now have as
+ `20240219T101522'. The old method should still be supported.
+ Apart from changing a few regular expressions, this does not
+ seem too complex to me. We would need a user option to opt in to
+ such a feature. Then tweak the relevant parts. The tricky issue
+ is to define a mapping of day names to letters/symbols that
+ works for everyone. Do all countries have a seven-day week, for
+ example? We need something universally applicable here.
+
+ Anything else? You are welcome to discuss these and/or add to the
+ list.
+
+
+23 Publications about Denote
+════════════════════════════
+
+ The Emacs community is putting Denote to great use. This section
+ includes publications that show how people configure their note-taking
+ setup. If you have a blog post, video, or configuration file about
+ Denote, feel welcome to tell us about it ([Contributing]).
+
+ ⁃ David Wilson (SystemCrafters): /Generating a Blog Site from Denote
+ Entries/, 2022-09-09, <https://www.youtube.com/watch?v=5R7ad5xz5wo>
+
+ ⁃ David Wilson (SystemCrafters): /Trying Out Prot’s Denote, an Org
+ Roam Alternative?/, 2022-07-15,
+ <https://www.youtube.com/watch?v=QcRY_rsX0yY>
+
+ ⁃ Jack Baty: /Keeping my Org Agenda updated based on Denote keywords/,
+ 2022-11-30, <https://baty.net/2022/keeping-my-org-agenda-updated>
+
+ ⁃ Jeremy Friesen: /Denote Emacs Configuration/, 2022-10-02,
+ <https://takeonrules.com/2022/10/09/denote-emacs-configuration/>
+
+ ⁃ Jeremy Friesen: /Exploring the Denote Emacs Package/, 2022-10-01,
+ <https://takeonrules.com/2022/10/01/exploring-the-denote-emacs-package/>
+
+ ⁃ Jeremy Friesen: /Migration Plan for Org-Roam Notes to Denote/,
+ 2022-10-02,
+ <https://takeonrules.com/2022/10/02/migration-plan-for-org-roam-notes-to-denote/>
+
+ ⁃ Jeremy Friesen: /Project Dispatch Menu with Org Mode Metadata,
+ Denote, and Transient/, 2022-11-19,
+ <https://takeonrules.com/2022/11/19/project-dispatch-menu-with-org-mode-metadata-denote-and-transient/>
+
+ ⁃ Mohamed Suliman: /Managing a bibliography of BiBTeX entries with
+ Denote/, 2022-12-20,
+ <https://www.scss.tcd.ie/~sulimanm/posts/denote-bibliography.html>
+
+ ⁃ Peter Prevos: /Simulating Text Files with R to Test the Emacs Denote
+ Package/, 2022-07-28,
+ <https://lucidmanager.org/productivity/testing-denote-package/>
+
+ ⁃ Peter Prevos: /Emacs Writing Studio/, 2023-10-19. A configuration
+ for authors, using Denote for taking notes, literature reviews and
+ manage collections of images:
+ • <https://lucidmanager.org/productivity/taking-notes-with-emacs-denote/>
+ • <https://lucidmanager.org/productivity/denote-explore/>
+ • <https://lucidmanager.org/productivity/bibliographic-notes-in-emacs-with-citar-denote/>
+ • <https://lucidmanager.org/productivity/using-emacs-image-dired/>
+
+ ⁃ Stefan Thesing: /Denote as a Zettelkasten/, 2023-03-02,
+ <https://www.thesing-online.de/blog/denote-as-a-zettelkasten>
+
+ ⁃ Summer Emacs: /An explanation of how I use Emacs/, 2023-05-04,
+ <https://github.com/summeremacs/howiuseemacs/blob/main/full-explanation-of-how-i-use-emacs.org>
+
+
+[Contributing] See section 22
+
+
+24 Alternatives to Denote
+═════════════════════════
+
+ What follows is a list of Emacs packages for note-taking. I
+ (Protesilaos) have not used any of them, as I was manually applying my
+ file-naming scheme beforehand and by the time those packages were
+ available I was already hacking on the predecessor of Denote as a
+ means of learning Emacs Lisp (a package which I called “Unassuming
+ Sidenotes of Little Significance”, aka “USLS” which is pronounced as
+ “U-S-L-S” or “useless”). As such, I cannot comment at length on the
+ differences between Denote and each of those packages, beside what I
+ gather from their documentation.
+
+ [org-roam]
+ The de facto standard in the Emacs milieu—and rightly so! It
+ has a massive community, is featureful, and should be an
+ excellent companion to anyone who is invested in the Org
+ ecosystem and/or knows what “Roam” is (I don’t). It has been
+ explained to me that Org Roam uses a database to store a cache
+ about your notes. It otherwise uses standard Org files. The
+ cache helps refer to the same node through aliases which can
+ provide lots of options. Personally, I follow a
+ single-topic-per-note approach, so anything beyond that is
+ overkill. If the database is only for a cache, then maybe that
+ has no downside, though I am careful with any kind of
+ specialised program as it creates a dependency. If you ask me
+ about database software in particular, I have no idea how to use
+ one, let alone debug it or retrieve data from it if something
+ goes awry (I could learn, but that is beside the point).
+
+ [zk (or zk.el)]
+ Reading its documentation makes me think that this is Denote’s
+ sibling—the two projects have a lot of things in common,
+ including the preference to rely on plain files and standard
+ tools. The core difference is that Denote has a strict
+ file-naming scheme. Other differences in available features
+ are, in principle, matters of style or circumstance: both
+ packages can have them. As its initials imply, ZK enables a
+ zettelkasten-like workflow. It does not enforce it though,
+ letting the user adapt the method to their needs and
+ requirements.
+
+ [zettelkasten]
+ This is another one of Denote’s relatives, at least insofar as
+ the goal of simplicity is concerned. The major difference is
+ that according to its documentation “the name of the file that
+ is created is just a unique ID”. This is not consistent with
+ our file-naming scheme which is all about making sense of your
+ files by their name alone and being able to visually parse a
+ listing of them without any kind of specialised tool (e.g. `ls
+ -l' or `ls -C' on the command-line from inside the
+ `denote-directory' give you a human-readable set of files names,
+ while `find * -maxdepth 0 -type f' is another approach).
+
+ [zetteldeft]
+ This is a zettelkasten note-taking system built on top of the
+ `deft' package. Deft provides a search interface to a
+ directory, in this case the one holding the user’s `zetteldeft'
+ notes. Denote has no such dependency and is not opinionated
+ about how the user prefers to search/access their notes: use
+ Dired, Grep, the `consult' package, or whatever else you already
+ have set up for all things Emacs, not just your notes.
+
+ Searching through `M-x list-packages' for “zettel” brings up more
+ matches. `zetteldesk' is an extension to Org Roam and, as such, I
+ cannot possibly know what Org Roam truly misses and what the
+ added-value of this package is. `neuron-mode' builds on top of an
+ external program called `neuron', which I have never used.
+
+ Searching for “note” gives us a few more results. `notes-mode' has
+ precious little documentation and I cannot tell what it actually does
+ (as I said in my presentation for LibrePlanet 2022, inadequate docs
+ are a bug). `side-notes' differs from what we try to do with Denote,
+ as it basically gives you the means to record your thoughts about some
+ other project you are working on and keep them on the side: so it and
+ Denote should not be mutually exclusive.
+
+ If I missed something, please let me know.
+
+
+[org-roam] <https://github.com/org-roam/org-roam>
+
+[zk (or zk.el)] <https://github.com/localauthor/zk>
+
+[zettelkasten] <https://github.com/ymherklotz/emacs-zettelkasten>
+
+[zetteldeft] <https://github.com/EFLS/zetteldeft>
+
+24.1 Alternative implementations and further reading
+────────────────────────────────────────────────────
+
+ This section covers blog posts and implementations from the Emacs
+ community about the topic of note-taking and file organization. They
+ may refer to some of the packages covered in the previous section or
+ provide their custom code ([Alternatives to Denote]). The list is
+ unsorted.
+
+ ⁃ José Antonio Ortega Ruiz (aka “jao”) explains a note-taking method
+ that is simple like Denote but differs in other ways. An
+ interesting approach overall:
+ <https://jao.io/blog/simple-note-taking.html>.
+
+ ⁃ Jethro Kuan (the main `org-roam' developer) explains their
+ note-taking techniques:
+ <https://jethrokuan.github.io/org-roam-guide/>. Good ideas all
+ round, regardless of the package/code you choose to use.
+
+ ⁃ Karl Voit’s tools [date2name], [filetags], [appendfilename], and
+ [move2archive] provide a Python-based implementation to organize
+ individual files which do not require Emacs. His approach ([blog
+ post] and his [presentation at GLT18]) has been complemented by
+ [memacs] to process e.g., the date of creation of photographs, or
+ the log of a phone call in a format compatible to org.
+
+ [ Development note: help expand this list. ]
+
+
+[Alternatives to Denote] See section 24
+
+[date2name] <https://github.com/novoid/date2name>
+
+[filetags] <https://github.com/novoid/filetags/>
+
+[appendfilename] <https://github.com/novoid/appendfilename/>
+
+[move2archive] <https://github.com/novoid/move2archive>
+
+[blog post] <https://karl-voit.at/managing-digital-photographs/>
+
+[presentation at GLT18] <https://www.youtube.com/watch?v=rckSVmYCH90>
+
+[memacs] <https://github.com/novoid/memacs>
+
+
+25 Frequently Asked Questions
+═════════════════════════════
+
+ I (Protesilaos) answer some questions I have received or might get.
+ It is assumed that you have read the rest of this manual: I will not
+ go into the specifics of how Denote works.
+
+
+25.1 Why develop Denote when PACKAGE already exists?
+────────────────────────────────────────────────────
+
+ I wrote Denote because I was using a variant of Denote’s file-naming
+ scheme before I was even an Emacs user (I switched to Emacs from
+ Tmux+Vim+CLI in the summer of 2019). I was originally inspired by
+ Jekyll, the static site generator, which I started using for my
+ website in 2016 (was on WordPress before). Jekyll’s files follow the
+ `YYYY-MM-DD-TITLE.md' pattern. I liked its efficiency relative to the
+ unstructured mess I had before. Eventually, I started using that
+ scheme outside the confines of my website’s source code. Over time I
+ refined it and here we are.
+
+ Note-taking is something I take very seriously, as I am a prolific
+ writer (just check my website, which only reveals the tip of the
+ iceberg). As such, I need a program that does exactly what I want and
+ which I know how to extend. I originally tried to use Org capture
+ templates to create new files with a Denote-style file-naming scheme
+ but never managed to achieve it. Maybe because `org-capture' has some
+ hard-coded assumptions or I simply am not competent enough to hack on
+ core Org facilities. Whatever the case, an alternative was in order.
+
+ The existence of PACKAGE is never a good reason for me not to conduct
+ my own experiments for recreational, educational, or practical
+ purposes. When the question arises of “why not contribute to PACKAGE
+ instead?” the answer is that without me experimenting in the first
+ place, I would lack the skills for such a task. Furthermore,
+ contributing to another package does not guarantee I get what I want
+ in terms of workflow.
+
+ Whether you should use Denote or not is another matter altogether:
+ choose whatever you want.
+
+
+25.2 Why not rely exclusively on Org?
+─────────────────────────────────────
+
+ I think Org is one of Emacs’ killer apps. I also believe it is not
+ the right tool for every job. When I write notes, I want to focus on
+ writing. Nothing more. I thus have no need for stuff like org-babel,
+ scheduling to-do items, clocking time, and so on. The more “mental
+ dependencies” you add to your workflow, the heavier the burden you
+ carry and the less focused you are on the task at hand: there is
+ always that temptation to tweak the markup, tinker with some syntactic
+ construct, obsess about what ought to be irrelevant to writing as
+ such.
+
+ In technical terms, I also am not fond of Org’s code base (I
+ understand why it is the way it is—just commenting on the fact). Ever
+ tried to read it? You will routinely find functions that are
+ tens-to-hundreds of lines long and have all sorts of special casing.
+ As I am not a programmer and only learnt to write Elisp through trial
+ and error, I have no confidence in my ability to make Org do what I
+ want at that level, hence `denote' instead of `org-denote' or
+ something.
+
+ Perhaps the master programmer is one who can deal with complexity and
+ keep adding to it. I am of the opposite view, as language—code
+ included—is at its communicative best when it is clear and accessible.
+
+ Make no mistake: I use Org for the agenda and also to write technical
+ documentation that needs to be exported to various formats, including
+ this very manual.
+
+
+25.3 Why care about Unix tools when you use Emacs?
+──────────────────────────────────────────────────
+
+ My notes form part of my longer-term storage. I do not want to have
+ to rely on a special program to be able to read them or filter them.
+ Unix is universal, at least as far as I am concerned.
+
+ Denote streamlines some tasks and makes things easier in general,
+ which is consistent with how Emacs provides a layer of interactivity
+ on top of Unix. Still, Denote’s utilities can, in principle, be
+ implemented as POSIX shell scripts (minus the Emacs-specific parts
+ like fontification in Dired or the buttonization of links).
+
+ Portability matters. For example, in the future I might own a
+ smartphone, so I prefer not to require Emacs, Org, or some other
+ executable to access my files on the go.
+
+ Furthermore, I might want to share those files with someone. If I
+ make Emacs a requirement, I am limiting my circle to a handful of
+ relatively advanced users.
+
+ Please don’t misinterpret this: I am using Emacs full-time for my
+ computing and maintain a growing list of packages for it. This is
+ just me thinking long-term.
+
+
+25.4 Why many small files instead of few large ones?
+────────────────────────────────────────────────────
+
+ I have read that Org favours the latter method. If true, I strongly
+ disagree with it because of the implicit dependency it introduces and
+ the way it favours machine-friendliness over human-readability in
+ terms of accessing information. Notes are long-term storage. I might
+ want to access them on (i) some device with limited features, (ii)
+ print on paper, (iii) share with another person who is not a tech
+ wizard.
+
+ There are good arguments for few large files, but all either
+ prioritize machine-friendliness or presuppose the use of sophisticated
+ tools like Emacs+Org.
+
+ Good luck using `less' on a generic TTY to read a file with a zillion
+ words, headings, sub-headings, sub-sub-headings, property drawers, and
+ other constructs! You will not get the otherwise wonderful folding of
+ headings the way you do in Emacs—do not take such features for
+ granted.
+
+ My point is that notes should be atomic to help the user—and
+ potentially the user’s family, friends, acquaintances—make sense of
+ them in a wide range of scenaria. The more program-agnostic your file
+ is, the better for you and/or everyone else you might share your
+ writings with.
+
+ Human-readability means that we optimize for what matters to us. If
+ (a) you are the only one who will ever read your notes, (b) always
+ have access to good software like Emacs+Org, (c) do not care about
+ printing on paper, then Denote’s model is not for you. Maybe you need
+ to tweak some `org-capture' template to append a new entry to one mega
+ file (I do that for my Org agenda, by the way, as I explained before
+ about using the right tool for the job).
+
+
+25.5 Does Denote perform well at scale?
+───────────────────────────────────────
+
+ Denote does not do anything fancy and has no special requirements: it
+ uses standard tools to accomplish ordinary tasks. If Emacs can cope
+ with lots of files, then that is all you need to know: Denote will
+ work.
+
+ To put this to the test, Peter Prevos is running simulations with R
+ that generate large volumes of notes. You can read the technicalities
+ here: <https://lucidmanager.org/productivity/testing-denote-package/>.
+ Excerpt:
+
+ Using this code I generated ten thousands notes and used
+ this to test the Denote package to see it if works at a
+ large scale. This tests shows that Prot’s approach is
+ perfectly capable of working with thousands of notes.
+
+ Of course, we are always prepared to make refinements to the code,
+ where necessary, without compromising on the project’s principles.
+
+
+25.6 I add TODOs to my notes; will many files slow down the Org agenda?
+───────────────────────────────────────────────────────────────────────
+
+ Yes, many files will slow down the agenda due to how that works. Org
+ collects all files specified in the `org-agenda-files', searches
+ through their contents for timestamped entries, and then loops through
+ all days to determine where each entry belongs. The more days and
+ more files, the longer it takes to build the agenda. Doing this with
+ potentially hundreds of files will have a noticeable impact on
+ performance.
+
+ This is not a deficiency of Denote. It happens with generic Org
+ files. The way the agenda is built is heavily favoring the use of a
+ single file that holds all your timestamped entries (or at least a few
+ such files). Tens or hundreds of files are inefficient for this job.
+ Plus doing so has the side-effect of making Emacs open all those
+ files, which you probably do not need.
+
+ If you want my opinion though, be more forceful with the separation of
+ concerns. Decouple your knowledge base from your ephemeral to-do
+ list: Denote (and others) can be used for the former, while you let
+ standard Org work splendidly for the latter—that is what I do, anyway.
+
+ Org has a powerful linking facility, whether you use `org-store-link'
+ or do it via an `org-capture' template. If you want a certain note to
+ be associated with a task, just store the task in a single `tasks.org'
+ (or however you name it) and link to the relevant context.
+
+ Do not mix your knowledge base with your to-do items. If you need
+ help figuring out the specifics of this workflow, you are welcome to
+ ask for help in our relevant channels ([Contributing]).
+
+
+[Contributing] See section 22
+
+
+25.7 I want to sort by last modified in Dired, why won’t Denote let me?
+───────────────────────────────────────────────────────────────────────
+
+ Denote does not control how Dired sorts files. I encourage you to read
+ the manpage of the `ls' executable. It will help you in general, while
+ it applies to Emacs as well via Dired. The gist is that you can update
+ the `ls' flags that Dired uses on-the-fly: type `C-u M-x
+ dired-sort-toggle-or-edit' (`C-u s' by default) and append
+ `--sort=time' at the prompt. To reverse the order, add the `-r' flag.
+ The user option `dired-listing-switches' sets your default preference.
+
+ For an on-demand sorted and filtered Dired listing of Denote files,
+ use the command `denote-sort-dired' ([Sort files by component]).
+
+
+[Sort files by component] See section 14
+
+
+25.8 How do you handle the last modified case?
+──────────────────────────────────────────────
+
+ Denote does not insert any meta data or heading pertaining to edits in
+ the file. I am of the view that these either do not scale well or are
+ not descriptive enough. Suppose you use a “lastmod” heading with a
+ timestamp: which lines where edited and what did the change amount to?
+
+ This is where an external program can be helpful. Use a Version
+ Control System, such as Git, to keep track of all your notes. Every
+ time you add a new file, record the addition. Same for post-creation
+ edits. Your VCS will let you review the history of those changes.
+ For instance, Emacs’ built-in version control framework has a command
+ that produces a log of changes for the current file: `M-x
+ vc-print-log', bound to `C-x v l' by default. From there one can
+ access the corresponding diff output (use `M-x describe-mode' (`C-h
+ m') in an unfamiliar buffer to learn more about it). With Git in
+ particular, Emacs users have the option of the all-round excellent
+ `magit' package.
+
+ In short: let Denote (or equivalent) create notes and link between
+ them, the file manager organise and provide access to files, search
+ programs deal with searching and narrowing, and version control
+ software handle the tracking of changes.
+
+
+25.9 Why are some Org links opening outside Emacs?
+──────────────────────────────────────────────────
+
+ Org has its own mechanism to determine how best to open a link. This
+ affects the `file:' link type, but also the `denote:' one (which is
+ designed to be as close to `file:' as possible).
+
+ When following a link, Org usually displays the data in an Emacs
+ buffer, though it might launch an external application instead. The
+ idea is to use a specialised program when that is relevant, such as to
+ display a video. Though there can be scenaria the user does not like,
+ such as when Org decides to load `.md' or `.html' files with an
+ external app. To compound the problem, users can name any file type
+ using the Denote file-naming scheme, including images, PDFs, videos,
+ and more ([Renaming files]).
+
+ To instruct Org to stay in Emacs for such cases, the user needs to
+ modify the variable `org-file-apps', which is not specific to Denote.
+ As one use-case, `org-file-apps' associates a regular expression to
+ match file names with a method on how to display them (do `M-x
+ describe-variable' and then search for `org-file-apps' to read its
+ documentation). Thus, the user can use something like the following in
+ their Org or Denote configuration:
+
+ ┌────
+ │ ;; Tell Org to use Emacs when opening files that end in .md
+ │ (add-to-list 'org-file-apps '("\\.md\\'" . emacs))
+ │
+ │ ;; Do the same for .html
+ │ (add-to-list 'org-file-apps '("\\.html\\'" . emacs))
+ └────
+
+ Each of these adds a new entry to the existing value of that user
+ option. Replace `md' or `html' with the desired file type extension.
+
+
+[Renaming files] See section 6
+
+
+25.10 Speed up backlinks’ or query links’ buffer creation?
+──────────────────────────────────────────────────────────
+
+ Denote leverages the built-in `xref' library to search for the
+ identifier of the current file and return any links to it. For users
+ of Emacs version 28 or higher, there exists a user option to specify
+ the program that performs this search: `xref-search-program'. The
+ default is `grep', which can be slow, though one may opt for `ugrep',
+ `ripgrep', or even specify something else (read the doc string of that
+ user option for the details).
+
+ Try either for these for better results:
+
+ ┌────
+ │ (setq xref-search-program 'ripgrep)
+ │
+ │ ;; OR
+ │
+ │ (setq xref-search-program 'ugrep)
+ └────
+
+ To use whatever executable is available on your system, use something
+ like this:
+
+ ┌────
+ │ ;; Prefer ripgrep, then ugrep, and fall back to regular grep.
+ │ (setq xref-search-program
+ │ (cond
+ │ ((or (executable-find "ripgrep")
+ │ (executable-find "rg"))
+ │ 'ripgrep)
+ │ ((executable-find "ugrep")
+ │ 'ugrep)
+ │ (t
+ │ 'grep)))
+ └────
+
+
+25.11 Why do I get “Search failed with status 1” when I search for backlinks?
+─────────────────────────────────────────────────────────────────────────────
+
+ Denote uses [Emacs’ Xref] to find backlinks. Xref requires `xargs'
+ and one of `grep' or `ripgrep', depending on your configuration.
+
+ This is usually not an issue on *nix systems, but the necessary
+ executables are not available on Windows Emacs distributions. Please
+ ensure that you have both `xargs' and either `grep' or `ripgrep'
+ available within your `PATH' environment variable.
+
+ If you have `git' on Windows installed, then you may use the following
+ code (adjust the git’s installation path if necessary):
+ ┌────
+ │ (setenv "PATH" (concat (getenv "PATH") ";" "C:\\Program Files\\Git\\usr\\bin"))
+ └────
+
+
+[Emacs’ Xref] <info:emacs#Xref>
+
+
+25.12 Why do I get a double `#+title' in Doom Emacs?
+────────────────────────────────────────────────────
+
+ Doom Emacs provides a set of bespoke templates for Org. One of those
+ prefills any new Org file with a `#+title' field. So when Denote
+ creates a new Org file and inserts front matter to it, it inevitably
+ adds an extra title to the existing one.
+
+ This is not a Denote problem. We can only expect a new file to be
+ empty by default. Check how to disable the relevant module in your
+ Doom Emacs configuration file.
+
+
+26 Acknowledgements
+═══════════════════
+
+ Denote is meant to be a collective effort. Every bit of help matters.
+
+ Author/maintainer
+ Protesilaos Stavrou.
+
+ Contributions to code or the manual
+ Abdul-Lateef Haji-Ali, Abin Simon, Adam Růžička, Alan Schmitt,
+ Alexandre Rousseau, Ashton Wiersdorf, Aziz, Benjamin Kästner,
+ Bruno Boal, Charanjit Singh, Claudio Migliorelli, Clemens
+ Radermacher, Colin McLear, Damien Cassou, Eduardo Grajeda, Elias
+ Storms, Eshel Yaron, Florian, Glenna D., Graham Marlow, Hilde
+ Rhyne, Ivan Sokolov, Jack Baty, Jakub Szczerbowski, Jean-Charles
+ Bagneris, Jean-Philippe Gagné Guay, Jianwei Hou, Joseph Turner,
+ Jürgen Hötzel, Kaushal Modi, Kai von Fintel, Kierin Bell, Kostas
+ Andreadis, Kristoffer Balintona, Kyle Meyer, Laurent Gatto,
+ Lucas Quintana, Maikol Solis, Marc Fargas, Matthew Lemon, Noboru
+ Ota (nobiot), Norwid Behrnd, Octavian, Peter Prevos, Philip
+ Kaludercic, Quiliro Ordóñez, Stephen R. Kifer, Stefan Monnier,
+ Stefan Thesing, Thibaut Benjamin, Tomasz Hołubowicz, TomoeMami ,
+ Vedang Manerikar, Wesley Harvey, Zhenxu Xu, arsaber101,
+ bryanrinders, eum3l, ezchi, jarofromel, leinfink (Henrik),
+ l-o-l-h (Lincoln), mattyonweb, maxbrieiev, mentalisttraceur,
+ pmenair, relict007, skissue.
+
+ Ideas and/or user feedback
+ Abin Simon, Aditya Yadav, Alan Schmitt, Aleksandr Vityazev, Alex
+ Griffin, Alex Hirschfeld, Alexis Purslane, Alfredo Borrás, Alp
+ Eren Kose, André Bering, Ashton Wiersdorf, Benjamin Kästner,
+ Claudio Migliorelli, Claudiu Tănăselia, Colin McLear,
+ Cosmin-Octavian C, Damien Cassou, Elias Storms, Federico
+ Stilman, Florian, Frédéric Willem Frank Ehmsen, Glenna D., Guo
+ Yong, Hanspeter Gisler Harold Ollivier, IceAsteroid, Jack Baty,
+ Jay Rajput, Jean-Charles Bagneris, Jeff Valk, Jens Östlund,
+ Jeremy Friesen, Jonathan Sahar, Johan Bolmsjö, Jonas
+ Großekathöfer, Jousimies, Juanjo Presa, Julian Hoch, Kai von
+ Fintel, Kaushal Modi, Kolmas, Lukas C. Bossert, M. Hadi Timachi,
+ Maikol Solis, Mark Olson, Mirko Hernandez, Niall Dooley, Nick
+ Bell, Oliver Epper, Paul van Gelder, Peter Prevos, Peter Smith,
+ Riccardo Giannitrapani, Samuel W. Flint, Sergio Rey, Suhail
+ Singh, Shreyas Ragavan, Stefan Thesing, Summer Emacs, Sven
+ Seebeck, Taoufik, TJ Stankus, Vick (VicZz), Viktor Haag, Vineet
+ C. Kulkarni, Wade Mealing, Wilf, Yi Liu, Ypot, atanasj, azegas,
+ babusri, bdillahu, coherentstate, doolio, duli, drcxd, elge70,
+ elliottw, fingerknight, hpgisler, hyperfocus1337, jtpavlock,
+ juh, leafarbelm, mentalisttraceur, pRot0ta1p, rbenit68,
+ relict007, sarcom-sar, sienic, skissue, sundar bp,
+ yetanotherfossman, zadca123
+
+ Special thanks to Peter Povinec who helped refine the file-naming
+ scheme, which is the cornerstone of this project.
+
+ Special thanks to Jean-Philippe Gagné Guay for the numerous
+ contributions to the code base.
+
+
+27 GNU Free Documentation License
+═════════════════════════════════
+
+
+28 Indices
+══════════
+
+28.1 Function index
+───────────────────
+
+
+28.2 Variable index
+───────────────────
+
+
+28.3 Concept index
+──────────────────
blob - /dev/null
blob + 08540f63c17e06cdba24c8ce6c447e6950d89c6e (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/README.md
+# denote: Simple notes with an efficient file-naming scheme
+
+Denote is a simple note-taking tool for Emacs. It is based on the idea
+that notes should follow a predictable and descriptive file-naming
+scheme. The file name must offer a clear indication of what the note is
+about, without reference to any other metadata. Denote basically
+streamlines the creation of such files while providing facilities to
+link between them.
+
+Denote's file-naming scheme is not limited to "notes". It can be used
+for all types of file, including those that are not editable in Emacs,
+such as videos. Naming files in a consistent way makes their
+filtering and retrieval considerably easier. Denote provides relevant
+facilities to rename files, regardless of file type.
+
++ Package name (GNU ELPA): `denote`
++ Official manual: <https://protesilaos.com/emacs/denote>
++ Change log: <https://protesilaos.com/emacs/denote-changelog>
++ Git repositories:
+ + GitHub: <https://github.com/protesilaos/denote>
+ + GitLab: <https://gitlab.com/protesilaos/denote>
++ Video demo: <https://protesilaos.com/codelog/2022-06-18-denote-demo/>
++ Backronyms: Denote Everything Neatly; Omit The Excesses. Don't Ever
+ Note Only The Epiphenomenal.
blob - /dev/null
blob + 505300ca60d8d62af9aedbb0d466ed97860601d3 (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/README.org
+#+title: denote: Simple notes with an efficient file-naming scheme
+#+author: Protesilaos Stavrou
+#+email: info@protesilaos.com
+#+language: en
+#+options: ':t toc:nil author:t email:t num:t
+#+startup: content
+#+macro: stable-version 4.0.0
+#+macro: release-date 2025-04-15
+#+macro: development-version 4.1.0-dev
+#+export_file_name: denote.texi
+#+texinfo_filename: denote.info
+#+texinfo_dir_category: Emacs misc features
+#+texinfo_dir_title: Denote: (denote)
+#+texinfo_dir_desc: Simple notes with an efficient file-naming scheme
+#+texinfo_header: @set MAINTAINERSITE @uref{https://protesilaos.com,maintainer webpage}
+#+texinfo_header: @set MAINTAINER Protesilaos Stavrou
+#+texinfo_header: @set MAINTAINEREMAIL @email{info@protesilaos.com}
+#+texinfo_header: @set MAINTAINERCONTACT @uref{mailto:info@protesilaos.com,contact the maintainer}
+
+#+texinfo: @insertcopying
+
+This manual, written by Protesilaos Stavrou, describes the customization
+options for the Emacs package called ~denote~ (or =denote.el=), and
+provides every other piece of information pertinent to it.
+
+The documentation furnished herein corresponds to stable version
+{{{stable-version}}}, released on {{{release-date}}}. Any reference to
+a newer feature which does not yet form part of the latest tagged
+commit, is explicitly marked as such.
+
+Current development target is {{{development-version}}}.
+
++ Package name (GNU ELPA): ~denote~
++ Official manual: <https://protesilaos.com/emacs/denote>
++ Change log: <https://protesilaos.com/emacs/denote-changelog>
++ Git repositories:
+ + GitHub: <https://github.com/protesilaos/denote>
+ + GitLab: <https://gitlab.com/protesilaos/denote>
++ Video demo: <https://protesilaos.com/codelog/2022-06-18-denote-demo/>
++ Backronyms: Denote Everything Neatly; Omit The Excesses. Don't Ever
+ Note Only The Epiphenomenal.
+
+If you are viewing the README.org version of this file, please note that
+the GNU ELPA machinery automatically generates an Info manual out of it.
+
+#+toc: headlines 8 insert TOC here, with eight headline levels
+
+* COPYING
+:PROPERTIES:
+:COPYING: t
+:CUSTOM_ID: h:40b18bb2-4dc1-4202-bd0b-6fab535b2a0f
+:END:
+
+Copyright (C) 2022-2025 Free Software Foundation, Inc.
+
+#+begin_quote
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.3 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, with the Front-Cover Texts being “A GNU Manual,” and
+with the Back-Cover Texts as in (a) below. A copy of the license is
+included in the section entitled “GNU Free Documentation License.”
+
+(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and
+modify this GNU manual.”
+#+end_quote
+
+* Installation
+:PROPERTIES:
+:CUSTOM_ID: h:f3bdac2c-4704-4a51-948c-a789a2589790
+:END:
+#+cindex: Installation instructions
+
+** GNU ELPA package
+:PROPERTIES:
+:CUSTOM_ID: h:42953f87-82bd-43ec-ab99-22b1e22955e7
+:END:
+
+The package is available as =denote=. Simply do:
+
+: M-x package-refresh-contents
+: M-x package-install
+
+And search for it.
+
+GNU ELPA provides the latest stable release. Those who prefer to follow
+the development process in order to report bugs or suggest changes, can
+use the version of the package from the GNU-devel ELPA archive. Read:
+https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/.
+
+** Manual installation
+:PROPERTIES:
+:CUSTOM_ID: h:d397712c-c8c0-4cfa-ad1a-ef28cf78d1f0
+:END:
+
+Assuming your Emacs files are found in =~/.emacs.d/=, execute the
+following commands in a shell prompt:
+
+#+begin_src sh
+cd ~/.emacs.d
+
+# Create a directory for manually-installed packages
+mkdir manual-packages
+
+# Go to the new directory
+cd manual-packages
+
+# Clone this repo, naming it "denote"
+git clone https://github.com/protesilaos/denote denote
+#+end_src
+
+Finally, in your =init.el= (or equivalent) evaluate this:
+
+#+begin_src emacs-lisp
+;; Make Elisp files in that directory available to the user.
+(add-to-list 'load-path "~/.emacs.d/manual-packages/denote")
+#+end_src
+
+Everything is in place to set up the package.
+
+* Sample configuration
+:PROPERTIES:
+:CUSTOM_ID: h:5d16932d-4f7b-493d-8e6a-e5c396b15fd6
+:END:
+#+cindex: Package configuration
+
+Denote is immediately useful for beginners and power users alike. This
+manual covers everything in detail, though do not let the numerous
+possibilities distract you from the fact that a basic configuration is
+enough to be highly productive ([[#h:998ae528-9276-47ec-b642-3d7355a38f27][Get started with this sample configuration]]).
+
+** Get started with this sample configuration
+:PROPERTIES:
+:CUSTOM_ID: h:998ae528-9276-47ec-b642-3d7355a38f27
+:END:
+
+If you are new to Denote, this a good place to start. Then work your
+way through the manual and expand your configuration accordingly. Only
+include commands/variables that are useful to you. We provide another
+code sample if you need some ideas ([[#h:58c4746b-b0d8-4896-9d88-a99b1d487231][More comprehensive sample configuration]]).
+
+#+begin_src emacs-lisp
+;; Remember that the website version of this manual shows the latest
+;; developments, which may not be available in the package you are
+;; using. Instead of copying from the web site, refer to the version
+;; of the documentation that comes with your package. Evaluate:
+;;
+;; (info "(denote) Sample configuration")
+(use-package denote
+ :ensure t
+ :hook (dired-mode . denote-dired-mode)
+ :bind
+ (("C-c n n" . denote)
+ ("C-c n r" . denote-rename-file)
+ ("C-c n l" . denote-link)
+ ("C-c n b" . denote-backlinks)
+ ("C-c n d" . denote-dired)
+ ("C-c n g" . denote-grep))
+ :config
+ (setq denote-directory (expand-file-name "~/Documents/notes/"))
+
+ ;; Automatically rename Denote buffers when opening them so that
+ ;; instead of their long file name they have, for example, a literal
+ ;; "[D]" followed by the file's title. Read the doc string of
+ ;; `denote-rename-buffer-format' for how to modify this.
+ (denote-rename-buffer-mode 1))
+#+end_src
+
+** More comprehensive sample configuration
+:PROPERTIES:
+:CUSTOM_ID: h:58c4746b-b0d8-4896-9d88-a99b1d487231
+:END:
+
+Here we include more of what you can configure with Denote ([[#h:998ae528-9276-47ec-b642-3d7355a38f27][Get started with this sample configuration]]).
+
+#+begin_src emacs-lisp
+;; Remember that the website version of this manual shows the latest
+;; developments, which may not be available in the package you are
+;; using. Instead of copying from the web site, refer to the version
+;; of the documentation that comes with your package. Evaluate:
+;;
+;; (info "(denote) Sample configuration")
+(use-package denote
+ :ensure t
+ :hook
+ ( ;; If you use Markdown or plain text files, then you want to make
+ ;; the Denote links clickable (Org renders links as buttons right
+ ;; away)
+ (text-mode . denote-fontify-links-mode-maybe)
+ ;; Apply colours to Denote names in Dired. This applies to all
+ ;; directories. Check `denote-dired-directories' for the specific
+ ;; directories you may prefer instead. Then, instead of
+ ;; `denote-dired-mode', use `denote-dired-mode-in-directories'.
+ (dired-mode . denote-dired-mode))
+ :bind
+ ;; Denote DOES NOT define any key bindings. This is for the user to
+ ;; decide. For example:
+ ( :map global-map
+ ("C-c n n" . denote)
+ ("C-c n d" . denote-dired)
+ ("C-c n g" . denote-grep)
+ ;; If you intend to use Denote with a variety of file types, it is
+ ;; easier to bind the link-related commands to the `global-map', as
+ ;; shown here. Otherwise follow the same pattern for `org-mode-map',
+ ;; `markdown-mode-map', and/or `text-mode-map'.
+ ("C-c n l" . denote-link)
+ ("C-c n L" . denote-add-links)
+ ("C-c n b" . denote-backlinks)
+ ("C-c n q c" . denote-query-contents-link) ; create link that triggers a grep
+ ("C-c n q f" . denote-query-filenames-link) ; create link that triggers a dired
+ ;; Note that `denote-rename-file' can work from any context, not just
+ ;; Dired bufffers. That is why we bind it here to the `global-map'.
+ ("C-c n r" . denote-rename-file)
+ ("C-c n R" . denote-rename-file-using-front-matter)
+
+ ;; Key bindings specifically for Dired.
+ :map dired-mode-map
+ ("C-c C-d C-i" . denote-dired-link-marked-notes)
+ ("C-c C-d C-r" . denote-dired-rename-files)
+ ("C-c C-d C-k" . denote-dired-rename-marked-files-with-keywords)
+ ("C-c C-d C-R" . denote-dired-rename-marked-files-using-front-matter))
+
+ :config
+ ;; Remember to check the doc string of each of those variables.
+ (setq denote-directory (expand-file-name "~/Documents/notes/"))
+ (setq denote-save-buffers nil)
+ (setq denote-known-keywords '("emacs" "philosophy" "politics" "economics"))
+ (setq denote-infer-keywords t)
+ (setq denote-sort-keywords t)
+ (setq denote-prompts '(title keywords))
+ (setq denote-excluded-directories-regexp nil)
+ (setq denote-excluded-keywords-regexp nil)
+ (setq denote-rename-confirmations '(rewrite-front-matter modify-file-name))
+
+ ;; Pick dates, where relevant, with Org's advanced interface:
+ (setq denote-date-prompt-use-org-read-date t)
+
+ ;; Automatically rename Denote buffers using the `denote-rename-buffer-format'.
+ (denote-rename-buffer-mode 1))
+#+end_src
+
+* Overview
+:PROPERTIES:
+:CUSTOM_ID: h:a09b70a2-ae0b-4855-ac14-1dddfc8e3241
+:END:
+
+Denote aims to be a simple-to-use, focused-in-scope, and effective
+note-taking and file-naming tool for Emacs.
+
+Denote is based on the idea that files should follow a predictable and
+descriptive file-naming scheme. The file name must offer a clear
+indication of what the contents are about, without reference to any
+other metadata. Denote basically streamlines the creation of such
+files or file names while providing facilities to link between them
+(where those files are editable).
+
+Denote's file-naming scheme is not limited to "notes". It can be used
+for all types of file, including those that are not editable in Emacs,
+such as videos. Naming files in a consistent way makes their
+filtering and retrieval considerably easier. Denote provides relevant
+facilities to rename files, regardless of file type.
+
+Denote is based on the following core design principles:
+
++ Predictability :: File names must follow a consistent and descriptive
+ naming convention ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). The file name alone
+ should offer a clear indication of what the contents are, without
+ reference to any other metadatum. This convention is not specific to
+ note-taking, as it is pertinent to any form of file that is part of
+ the user's long-term storage ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]).
+
++ Composability :: Be a good Emacs citizen, by integrating with other
+ packages or built-in functionality instead of re-inventing functions
+ such as for filtering or greping. The author of Denote (Protesilaos,
+ aka "Prot") writes ordinary notes in plain text (=.txt=), switching on
+ demand to an Org file only when its expanded set of functionality is
+ required for the task at hand ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]).
+
++ Portability :: Notes are plain text and should remain portable. The
+ way Denote writes file names, the front matter it includes in the
+ note's header, and the links it establishes must all be adequately
+ usable with standard Unix tools. No need for a database or some
+ specialised software. As Denote develops and this manual is fully
+ fleshed out, there will be concrete examples on how to do the
+ Denote-equivalent on the command-line.
+
++ Flexibility :: Do not assume the user's preference for a note-taking
+ methodology. Denote is conceptually similar to the Zettelkasten
+ Method, which you can learn more about in this detailed introduction:
+ <https://zettelkasten.de/introduction/>. Notes are atomic (one file
+ per note) and have a unique identifier. However, Denote does not
+ enforce a particular methodology for knowledge management, such as a
+ restricted vocabulary or mutually exclusive sets of keywords. Denote
+ also does not check if the user writes thematically atomic notes. It
+ is up to the user to apply the requisite rigor and/or creativity in
+ pursuit of their preferred workflow ([[#h:6060a7e6-f179-4d42-a9de-a9968aaebecc][Writing metanotes]]).
+
++ Hackability :: Denote's code base consists of small and reusable
+ functions. They all have documentation strings. The idea is to make
+ it easier for users of varying levels of expertise to understand what
+ is going on and make surgical interventions where necessary (e.g. to
+ tweak some formatting). In this manual, we provide concrete examples
+ on such user-level configurations ([[#h:4a6d92dd-19eb-4fcc-a7b5-05ce04da3a92][Keep a journal or diary]]).
+
+Now the important part... "Denote" is the familiar word, though it also
+is a play on the "note" concept. Plus, we can come up with acronyms,
+recursive or otherwise, of increasingly dubious utility like:
+
++ Don't Ever Note Only The Epiphenomenal
++ Denote Everything Neatly; Omit The Excesses
+
+But we'll let you get back to work. Don't Eschew or Neglect your
+Obligations, Tasks, and Engagements.
+
+* Points of entry
+:PROPERTIES:
+:CUSTOM_ID: h:17896c8c-d97a-4faa-abf6-31df99746ca6
+:END:
+
+#+findex: denote
+#+findex: denote-type
+#+findex: denote-org-capture
+#+findex: denote-date
+#+findex: denote-subdirectory
+#+findex: denote-template
+#+findex: denote-signature
+There are seven main ways to write a note with Denote: invoke the
+~denote~, ~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. Other more specialised commands exist as well,
+which one shall learn about as they read through this manual. We do
+not want to overwhelm the user with options at this stage.
+
+All these commands construct the file name in accordance with the user option
+~denote-file-name-components-order~ ([[#h:dc8c40e0-233a-4991-9ad3-2cf5f05ef1cd][Change the order of file name components]]).
+
+** Standard note creation
+:PROPERTIES:
+:CUSTOM_ID: h:6a92a8b5-d766-42cc-8e5b-8dc255466a23
+:END:
+
+The ~denote~ command will prompt for a title. If a region is active,
+the text of the region becomes the default at the minibuffer prompt
+(meaning that typing =RET= without any input will use the default
+value). Once the title is supplied, the ~denote~ command will then ask
+for keywords. The resulting note will have a file name as already
+explained: [[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file naming scheme]]
+
+#+vindex: denote-after-new-note-hook
+The ~denote~ command runs the hook ~denote-after-new-note-hook~ after
+creating the new note ([[#h:a947908e-1847-4471-ba07-377ee2f4b36c][Access the data of the latest note]]). When
+called from Lisp, it returns the path it generates. Before returning
+the path, it decides what to do with the buffer of the note, in
+accordance with the user option ~denote-kill-buffers~ ([[#h:c8fd826f-3ac9-4820-9709-4375603f8865][The ~denote-kill-buffers~ option]]).
+
+The file type of the new note is determined by the user option
+~denote-file-type~ ([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]).
+
+#+vindex: denote-known-keywords
+#+vindex: denote-infer-keywords
+The keywords' prompt supports minibuffer completion. Available
+candidates are those defined in the user option ~denote-known-keywords~.
+More candidates can be inferred from the names of existing notes, by
+setting ~denote-infer-keywords~ to non-nil (which is the case by
+default) ([[#h:c0fb477f-4f99-4d76-9cce-132bcfcb351d][Create a controlled vocabulary for keywords]]).
+
+#+vindex: denote-sort-keywords
+Multiple keywords can be inserted by separating them with a comma (or
+whatever the value of the ~crm-separator~ is---which should be a comma).
+When the user option ~denote-sort-keywords~ is non-nil (the default),
+keywords are sorted alphabetically (technically, the sorting is done
+with ~string-lessp~).
+
+The interactive behaviour of the ~denote~ command is influenced by the
+user option ~denote-prompts~ ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]]).
+
+The ~denote~ command can also be called from Lisp. Read its doc string
+for the technicalities.
+
+#+findex: denote-create-note
+In the interest of discoverability, ~denote~ is also available under the
+alias ~denote-create-note~.
+
+*** The ~denote-prompts~ option
+:PROPERTIES:
+:CUSTOM_ID: h:f9204f1f-fcee-49b1-8081-16a08a338099
+:END:
+
+#+vindex: denote-prompts
+The user option ~denote-prompts~ determines how the ~denote~ command
+will behave interactively ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]).
+
+Commands that prompt for user input to construct a Denote file name
+include, but are not limited to: ~denote~, ~denote-signature~,
+~denote-type~, ~denote-date~, ~denote-subdirectory~,
+~denote-rename-file~, ~denote-dired-rename-files~.
+
+- [[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]].
+- [[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]].
+
+The value of this user option is a list of symbols, which includes any
+of the following:
+
+- =title=: Prompt for the title of the new note ([[#h:403422a7-7578-494b-8f33-813874c12da3][The ~denote-history-completion-in-prompts~ option]]).
+
+- =keywords=: Prompts with completion for the keywords of the new note.
+ Available candidates are those specified in the user option
+ ~denote-known-keywords~. If the user option ~denote-infer-keywords~
+ is non-nil, keywords in existing note file names are included in the
+ list of candidates. The =keywords= prompt uses
+ ~completing-read-multiple~, meaning that it can accept multiple
+ keywords separated by a comma (or whatever the value of ~crm-separator~
+ is).
+
+- =file-type=: Prompts with completion for the file type of the new
+ note. Available candidates are those specified in the user option
+ ~denote-file-type~. Without this prompt, ~denote~ uses the value of
+ ~denote-file-type~.
+
+- =subdirectory=: Prompts with completion for a subdirectory in which to
+ create the note. Available candidates are the value of the user
+ option ~denote-directory~ and all of its subdirectories. Any
+ subdirectory must already exist: Denote will not create it.
+
+- =date=: Prompts for the date of the new note. It will expect an input
+ like 2022-06-16 or a date plus time: 2022-06-16 14:30. Without the
+ =date= prompt, the ~denote~ command uses the ~current-time~.
+
+ [[#h:e7ef08d6-af1b-4ab3-bb00-494a653e6d63][The denote-date-prompt-use-org-read-date option]].
+
+- =template=: Prompts for a KEY among the ~denote-templates~. The value
+ 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 for
+ any kind of workflow, such as a special tag to label the =part1= and
+ =part2= of a large file that is split in half, or to add special
+ contexts like =home= and =work=, or even priorities like =a=, =b=,
+ =c=. One other use-case is to implement a sequencing scheme that
+ makes notes have hierarchical relationships. This is handled by our
+ optional extension =denote-sequence.el=, which is part of the
+ ~denote~ package ([[#h:d5ca722d-e7fa-46fa-9a57-6363b1d4186f][Write sequence notes or "folgezettel"]]).
+
+The prompts occur in the given order.
+
+If the value of this user option is nil, no prompts are used. The
+resulting file name will consist of an identifier (i.e. the date and
+time) and a supported file type extension (per ~denote-file-type~).
+
+Recall that Denote's standard file-naming scheme is defined as follows
+([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]):
+
+: 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:
+
+: DATE.EXT
+: DATE--TITLE.EXT
+: DATE__KEYWORDS.EXT
+
+When in doubt, always include the =title= and =keywords= prompts.
+
+Finally, this user option only affects the interactive use of the
+~denote~ or other relevant commands (advanced users can call it from
+Lisp). In Lisp usage, the behaviour is always what the caller
+specifies, based on the supplied arguments.
+
+*** The ~denote-history-completion-in-prompts~ option
+:PROPERTIES:
+:CUSTOM_ID: h:403422a7-7578-494b-8f33-813874c12da3
+:END:
+
+#+vindex: denote-history-completion-in-prompts
+The user option ~denote-history-completion-in-prompts~ toggles history
+completion in all ~denote-prompts-with-history-as-completion~.
+
+When this user option is set to a non-nil value, Denote will use
+minibuffer history entries as completion candidates in all of the
+~denote-prompts-with-history-as-completion~. Those will show previous
+inputs from their respective history as possible values to select,
+either to (i) re-insert them verbatim or (ii) with the intent to edit
+them further (depending on the minibuffer user interface, one can
+select a candidate with =TAB= without exiting the minibuffer, as
+opposed to what =RET= normally does by selecting and exiting).
+
+When this user option is set to a nil value, all of the
+~denote-prompts-with-history-as-completion~ will not use minibuffer
+completion: they will just prompt for a string of characters. Their
+history is still available through all the standard ways of retrieving
+minibuffer history, such as with the command ~previous-history-element~.
+
+History completion still allows arbitrary values to be provided as
+input: they do not have to match the available minibuffer completion
+candidates.
+
+Note that some prompts, like ~denote-keywords-prompt~, always use
+minibuffer completion, due to the specifics of their data.
+
+[ Consider enabling the built-in ~savehist-mode~ to persist minibuffer
+ histories between sessions.]
+
+*** The ~denote-templates~ option
+:PROPERTIES:
+:CUSTOM_ID: h:f635a490-d29e-4608-9372-7bd13b34d56c
+:END:
+
+#+vindex: denote-templates
+The user option ~denote-templates~ is an alist of content templates for
+new notes. A template is arbitrary text that Denote will add to a newly
+created note right below the front matter.
+
+Templates are expressed as a =(KEY . VALUE)= association.
+
+- The =KEY= is the name which identifies the template. It is an
+ arbitrary symbol, such as =report=, =memo=, =statement=.
+
+- The =VALUE= is either a string or the symbol of a function.
+
+ - If it is a string, it is ordinary text that Denote will insert
+ as-is. It can contain newline characters to add spacing. The
+ manual of Denote contains examples on how to use the ~concat~
+ function, beside writing a generic string.
+
+ - If it is a function, it is called without arguments and is expected
+ to return a string. Denote will call the function and insert the
+ result in the buffer.
+
+The user can choose a template either by invoking the command
+~denote-template~ or by changing the user option ~denote-prompts~ to
+always prompt for a template when calling the ~denote~ command.
+
+[[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]].
+
+[[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]].
+
+Templates can be written directly as one large string. For example (the
+=\n= character is read as a newline):
+
+#+begin_src emacs-lisp
+(setq denote-templates
+ '((report . "* Some heading\n\n* Another heading")
+ (memo . "* Some heading
+
+,* Another heading
+
+")))
+#+end_src
+
+Long strings may be easier to type but interpret indentation literally.
+Also, they do not scale well. A better way is to use some Elisp code to
+construct the string. This would typically be the ~concat~ function,
+which joins multiple strings into one. The following is the same as the
+previous example:
+
+#+begin_src emacs-lisp
+(setq denote-templates
+ `((report . "* Some heading\n\n* Another heading")
+ (memo . ,(concat "* Some heading"
+ "\n\n"
+ "* Another heading"
+ "\n\n"))))
+#+end_src
+
+Notice that to evaluate a function inside of an alist we use the
+backtick to quote the alist (NOT the straight quote) and then prepend a
+comma to the expression that should be evaluated. The ~concat~ form
+here is not sensitive to indentation, so it is easier to adjust for
+legibility.
+
+For when the =VALUE= is a function, we have this:
+
+#+begin_src emacs-lisp
+(setq denote-templates
+ `((report . "* Some heading\n\n* Another heading")
+ (blog . my-denote-template-function-for-blog) ; a function to return a string
+ (memo . ,(concat "* Some heading"
+ "\n\n"
+ "* Another heading"
+ "\n\n"))))
+#+end_src
+
+In this example, ~my-denote-template-function-for-blog~ is a function
+that returns a string. Denote will take care to insert it in the buffer.
+
+DEV NOTE: We do not provide more examples at this point, though feel
+welcome to ask for help if the information provided herein is not
+sufficient. We shall expand the manual accordingly.
+
+*** Convenience commands for note creation
+:PROPERTIES:
+:CUSTOM_ID: h:887bdced-9686-4e80-906f-789e407f2e8f
+:END:
+
+Sometimes the user needs to create a note that has different
+requirements from those of ~denote~ ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). While
+this can be achieved globally by changing the ~denote-prompts~ user
+option, there are cases where an ad-hoc method is the appropriate one
+([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]]).
+
+To this end, Denote provides the following interactive convenience
+commands for note creation. They all work by appending a new prompt to
+the existing ~denote-prompts~.
+
++ Create note by specifying file type :: The ~denote-type~ command
+ creates a note while prompting for a file type.
+
+ This is the equivalent of calling ~denote~ when ~denote-prompts~ has
+ the =file-type= prompt appended to its existing prompts. In practical
+ terms, this lets you produce, say, a note in Markdown even though
+ you normally write in Org ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]).
+
+ #+findex: denote-create-note-using-type
+ The ~denote-create-note-using-type~ is an alias of ~denote-type~.
+
++ Create note using a date :: Normally, Denote reads the current date
+ and time to construct the unique identifier of a newly created note
+ ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). Sometimes, however, the user needs to set
+ an explicit date+time value.
+
+ This is where the ~denote-date~ command comes in. It creates a note
+ while prompting for a date. The date can be in YEAR-MONTH-DAY
+ notation like =2022-06-30= or that plus the time: =2022-06-16 14:30=.
+
+ [[#h:e7ef08d6-af1b-4ab3-bb00-494a653e6d63][The denote-date-prompt-use-org-read-date option]].
+
+ This is the equivalent of calling ~denote~ when ~denote-prompts~ has
+ the =date= prompt appended to its existing prompts.
+
+ #+findex: denote-create-note-using-date
+ The ~denote-create-note-using-date~ is an alias of ~denote-date~.
+
++ Create note in a specific directory :: The ~denote-subdirectory~
+ command creates a note while prompting for a subdirectory. Available
+ candidates include the value of the variable ~denote-directory~ and
+ any subdirectory thereof (Denote does not create subdirectories).
+
+ This is the equivalent of calling ~denote~ when ~denote-prompts~ has
+ the =subdirectory= prompt appended to its existing prompts.
+
+ #+findex: denote-create-note-in-subdirectory
+ The ~denote-create-note-in-subdirectory~ is a more descriptive alias
+ of ~denote-subdirectory~.
+
++ Create note and add a template :: The ~denote-template~ command
+ creates a new note and inserts the specified template below the front
+ matter ([[#h:f635a490-d29e-4608-9372-7bd13b34d56c][The denote-templates option]]). Available candidates for
+ templates are specified in the user option ~denote-templates~.
+
+ This is the equivalent of calling ~denote~ when ~denote-prompts~ has
+ the =template= prompt appended to its existing prompts.
+
+ #+findex: denote-create-note-with-template
+ 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, ...).
+
+ This is the equivalent of calling ~denote~ when ~denote-prompts~ has
+ the =signature= prompt appended to its existing prompts.
+
+ The ~denote-create-note-using-signature~ is an alias of the command
+ ~denote-signature~ intended to make the functionality more
+ discoverable.
+
+**** Write your own convenience commands
+:PROPERTIES:
+:CUSTOM_ID: h:11946562-7eb0-4925-a3b5-92d75f1f5895
+:END:
+
+The convenience commands we provide only cover some basic use-cases
+([[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]]). The user may require
+combinations that are not covered, such as to prompt for a template
+and for a subdirectory, instead of only one of the two. To this end,
+we show how to follow the code we use in Denote to write your own
+variants of those commands.
+
+First let's take a look at the definition of one of those commands.
+They all look the same, but we use ~denote-subdirectory~ for this
+example:
+
+#+begin_src emacs-lisp
+(defun denote-subdirectory ()
+ "Create note while prompting for a subdirectory.
+
+Available candidates include the value of the variable
+`denote-directory' and any subdirectory thereof.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `subdirectory' prompt appended to its existing prompts."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts (denote-add-prompts '(subdirectory))))
+ (call-interactively #'denote)))
+#+end_src
+
+The hyphenated word after ~defun~ is the name of the function. It has
+to be unique. Then we have the documentation string (or "doc string")
+which is for the user's convenience.
+
+This function is ~interactive~, meaning that it can be called via
+=M-x= or be assigned to a key binding. Then we have the local binding
+of the ~denote-prompts~ to the desired combination ("local" means
+specific to this function without affecting other contexts). Lastly,
+it calls the standard ~denote~ command interactively, so it uses all
+the prompts in their specified order.
+
+The function call ~(denote-add-prompts '(subdirectory))~ will append
+the subdirectory prompt to the existing value of the ~denote-prompts~.
+If, for example, the default value is ='(title keywords)= (to prompt
+for a title and then for keywords), it will become ='(subdirectory
+title keywords)= inside the context of this ~let~. Remember that this
+is "local", so the global value of ~denote-prompts~ remains unaffected.
+
+Now let's say we want to have a command that (i) asks for a template
+(ii) for a subdirectory ([[#h:f635a490-d29e-4608-9372-7bd13b34d56c][The denote-templates option]]), and (iii) then
+goes through the remaining ~denote-prompts~. All we need to do is
+tweak the ~let~ bound value of ~denote-prompts~ and give our command a
+unique name:
+
+#+begin_src emacs-lisp
+;; Like `denote-subdirectory' but also ask for a template
+(defun my-denote-subdirectory-with-template ()
+ "Create note while also prompting for a template and subdirectory.
+
+This is the equivalent of calling `denote' when `denote-prompts' has the
+`subdirectory' and `template' prompts appended to its existing prompts."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts (denote-add-prompts '(subdirectory template))))
+ (call-interactively #'denote)))
+#+end_src
+
+The tweaks to ~denote-prompts~ determine how the command will behave
+([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]]). Use this paradigm to write your own
+variants which you can then assign to keys, invoke with =M-x=, or add
+to the list of commands available at the ~denote-command-prompt~
+([[#h:98c732ac-da0e-4ebd-a0e3-5c47f9075e51][Choose which commands to prompt for]]).
+
+In the above scenario, we are using the ~denote-add-prompts~ function,
+which appends whatever prompts we want to the existing value of
+~denote-prompts~. If the user prefers to completely override the
+~denote-prompts~, they can set the value outright:
+
+#+begin_src emacs-lisp
+(defun my-denote-subdirectory-with-template-title-and-keywords ()
+ "Create a note while prompting for subdirectory, template, title, and keywords.
+
+This is the equivalent of calling `denote' when `denote-prompts' has the
+value '(template subdirectory title keywords)."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts '(subdirectory template title keywords)))
+ (call-interactively #'denote)))
+#+end_src
+
+*** The ~denote-save-buffers~ option
+:PROPERTIES:
+:CUSTOM_ID: h:bf80f4cd-6f56-4f7c-a991-8573161e4511
+:END:
+
+#+vindex: denote-save-buffers
+The user option ~denote-save-buffer-after-creation~ controls whether
+commands that create new notes save their buffer outright.
+
+The default behaviour of commands such as ~denote~ (or related) is to
+not save the buffer they create ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). This gives the user
+the chance to review the text before writing it to a file. The user
+may choose to delete the unsaved buffer, thus not creating a new note
+([[#h:bf80f4cd-6f56-4f7c-a991-8573161e4511][The ~denote-save-buffer-after-creation~ option]]).
+
+This option also applies to notes affected by the renaming commands
+(~denote-rename-file~ and related).
+
+If this user option is set to a non-nil value, such buffers are saved
+automatically. The assumption is that the user who opts in to this
+feature is familiar with the ~denote-rename-file~ operation (or
+related) and knows it is reliable ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]).
+
+[[#h:c8fd826f-3ac9-4820-9709-4375603f8865][The ~denote-kill-buffers~ option]].
+
+*** The ~denote-kill-buffers~ option
+:PROPERTIES:
+:CUSTOM_ID: h:c8fd826f-3ac9-4820-9709-4375603f8865
+:END:
+
+#+vindex: denote-kill-buffers
+The user option ~denote-kill-buffers~ controls whether to kill a
+buffer that was generated by a Denote command. This can happen when
+creating a new file or renaming an existing one.
+
+- [[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]].
+- [[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]].
+
+The default behaviour of creation or renaming commands such as
+~denote~ or ~denote-rename-file~ is to not kill the buffer they
+create or modify at the end of their operation. The idea is to give
+the user the chance to confirm that everything is in order.
+
+If this user option is nil (the default), buffers affected by a
+creation or renaming command are not automatically killed.
+
+If set to the symbol =on-creation=, new notes are automatically killed.
+
+If set to the symbol =on-rename=, renamed notes are automatically
+killed.
+
+If set to t, new and renamed notes are killed.
+
+If a buffer is killed, it is also saved, as if ~denote-save-buffers~
+were t ([[#h:bf80f4cd-6f56-4f7c-a991-8573161e4511][The ~denote-save-buffers~ option]]).
+
+In all cases, if the buffer already existed before the Denote operation
+it is NOT automatically killed.
+
+*** The ~denote-date-prompt-use-org-read-date~ option
+:PROPERTIES:
+:CUSTOM_ID: h:e7ef08d6-af1b-4ab3-bb00-494a653e6d63
+:END:
+
+By default, Denote uses its own simple prompt for date or date+time
+input ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The denote-prompts option]]). This is done when the
+~denote-prompts~ option includes a =date= symbol and/or when the user
+invokes the ~denote-date~ command.
+
+#+vindex: denote-date-prompt-use-org-read-date
+Users who want to benefit from the more advanced date selection method
+that is common in interactions with Org mode, can set the user option
+~denote-date-prompt-use-org-read-date~ to a non-nil value.
+
+
+** Create note using Org capture
+:PROPERTIES:
+:CUSTOM_ID: h:656c70cd-cf9a-4471-a0b5-4f0aaf60f881
+:END:
+
+For integration with ~org-capture~, the user must first add the relevant
+template. Such as:
+
+#+begin_src emacs-lisp
+(with-eval-after-load 'org-capture
+ (add-to-list 'org-capture-templates
+ '("n" "New note (with Denote)" plain
+ (file denote-last-path)
+ #'denote-org-capture
+ :no-save t
+ :immediate-finish nil
+ :kill-buffer t
+ :jump-to-captured t)))
+#+end_src
+
+Once the template is added, it is accessed from the specified key. If,
+for instance, ~org-capture~ is bound to =C-c c=, then the note
+creation is initiated with =C-c c n=, per the above snippet. After
+that, the process is the same as with invoking ~denote~ directly,
+namely: a prompt for a title followed by a prompt for keywords,
+assuming the default settings ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). Concretely,
+this method always respects the value of the user option
+~denote-prompts~ ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The ~denote-prompts~ option]]).
+
+It is also possible to define templates that have specific prompts or
+certain values set, for which there is no prompt:
+
+- [[#h:115b6797-f265-40e9-a603-32eeda13a7ac][Create note with specific values using Org capture]]
+- [[#h:95b78582-9086-47e8-967f-62373e2369a0][Create note with specific prompts using Org capture]]
+
+#+vindex: denote-org-capture-specifiers
+Users may prefer to leverage ~org-capture~ in order to extend file
+creation with the specifiers described in the ~org-capture-templates~
+documentation (such as to capture the active region and/or create a
+hyperlink pointing to the given context).
+
+IMPORTANT. Due to the particular file-naming scheme of Denote, which is
+derived dynamically, such specifiers or other arbitrary text cannot be
+written directly in the template. Instead, they have to be assigned to
+the user option ~denote-org-capture-specifiers~, which is interpreted by
+the function ~denote-org-capture~. Example with our default value:
+
+#+begin_src emacs-lisp
+(setq denote-org-capture-specifiers "%l\n%i\n%?")
+#+end_src
+
+Note that ~denote-org-capture~ ignores the ~denote-file-type~: it always
+sets the Org file extension for the created note to ensure that the
+capture process works as intended, especially for the desired output of
+the ~denote-org-capture-specifiers~.
+
+[ You may not need ~org-capture~ to do what you want ([[#h:11946562-7eb0-4925-a3b5-92d75f1f5895][Write your own convenience commands]]). ]
+
+** Create note with specific prompts using Org capture
+:PROPERTIES:
+:CUSTOM_ID: h:95b78582-9086-47e8-967f-62373e2369a0
+:END:
+
+This section assumes knowledge of how Denote+org-capture work, as
+explained in the previous section ([[#h:656c70cd-cf9a-4471-a0b5-4f0aaf60f881][Create note using Org capture]]).
+
+#+findex: denote-org-capture-with-prompts
+The previous section shows how to define an Org capture template that
+always prompts for whatever is set in the user option ~denote-prompts~
+(title and keywords, by default). There are, however, cases where the
+user wants more control over what kind of input Denote will prompt
+for. To this end, we provide the function ~denote-org-capture-with-prompts~.
+Below we explain it and then show some examples of how to use it.
+
+The ~denote-org-capture-with-prompts~ is like ~denote-org-capture~ but
+with optional prompt parameters.
+
+When called without arguments, it does not prompt for anything. It
+just returns the front matter with title and keyword fields empty and
+the date and identifier fields specified. It also makes the file name
+consist of only the identifier plus the Org file name extension ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+
+Otherwise, it produces a minibuffer prompt for every non-nil value
+that corresponds to the =TITLE=, =KEYWORDS=, =SUBDIRECTORY=, =DATE=,
+and =TEMPLATE= arguments. The prompts are those used by the standard
+~denote~ command and all of its utility commands ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]).
+
+When returning the contents that fill in the Org capture template, the
+sequence is as follows: front matter, =TEMPLATE=, and then the value
+of the user option ~denote-org-capture-specifiers~.
+
+Important note: in the case of =SUBDIRECTORY= actual subdirectories
+must exist---Denote does not create them. Same principle for
+=TEMPLATE= as templates must exist and are specified in the user
+option ~denote-templates~.
+
+This is how one can incorporate ~denote-org-capture-with-prompts~ in
+their Org capture templates. Instead of passing a generic ~t~ which
+makes it hard to remember what the argument means, we use semantic
+keywords like =:title= for our convenience (internally this does not
+matter as the value still counts as non-nil, so =:foo= for =TITLE= is
+treated the same as =:title= or ~t~).
+
+#+begin_src emacs-lisp
+;; This prompts for TITLE, KEYWORDS, and SUBDIRECTORY
+(add-to-list 'org-capture-templates
+ '("N" "New note with prompts (with denote.el)" plain
+ (file denote-last-path)
+ (function
+ (lambda ()
+ (denote-org-capture-with-prompts :title :keywords :subdirectory)))
+ :no-save t
+ :immediate-finish nil
+ :kill-buffer t
+ :jump-to-captured t))
+
+;; This prompts only for SUBDIRECTORY
+(add-to-list 'org-capture-templates
+ '("N" "New note with prompts (with denote.el)" plain
+ (file denote-last-path)
+ (function
+ (lambda ()
+ (denote-org-capture-with-prompts nil nil :subdirectory)))
+ :no-save t
+ :immediate-finish nil
+ :kill-buffer t
+ :jump-to-captured t))
+
+;; This prompts for TITLE and SUBDIRECTORY
+(add-to-list 'org-capture-templates
+ '("N" "New note with prompts (with denote.el)" plain
+ (file denote-last-path)
+ (function
+ (lambda ()
+ (denote-org-capture-with-prompts :title nil :subdirectory)))
+ :no-save t
+ :immediate-finish nil
+ :kill-buffer t
+ :jump-to-captured t))
+#+end_src
+
+[ You may not need ~org-capture~ to do what you want ([[#h:11946562-7eb0-4925-a3b5-92d75f1f5895][Write your own convenience commands]]). ]
+
+** Create note with specific values using Org capture
+:PROPERTIES:
+:CUSTOM_ID: h:115b6797-f265-40e9-a603-32eeda13a7ac
+:END:
+
+The ordinary procedure to create a note with ~org-capture~ respects
+the value of the user option ~denote-prompts~ ([[#h:656c70cd-cf9a-4471-a0b5-4f0aaf60f881][Create note using Org capture]]):
+the user is prompted for all the values they have configured (title
+and keywords, by default). Sometimes, there is no need to have a
+certain prompt because the value of it will be constant. For example,
+the user wants to have a template that (i) respects the
+~denote-prompts~ but (ii) puts the new note in an existing subdirectory
+of the ~denote-directory~. The following code block does exactly that.
+
+[ It also is possible to have a template that deviates from
+ ~denote-prompts~ and prompts for specific values ([[#h:95b78582-9086-47e8-967f-62373e2369a0][Create note with specific prompts using Org capture]]). ]
+
+#+begin_src emacs-lisp
+(with-eval-after-load 'org-capture
+ (add-to-list 'org-capture-templates
+ '("r" "New reference (with Denote)" plain
+ (file denote-last-path)
+ (function
+ (lambda ()
+ (let ((denote-use-directory (expand-file-name "reference" (denote-directory))))
+ (denote-org-capture))))
+ :no-save t
+ :immediate-finish nil
+ :kill-buffer t
+ :jump-to-captured t)))
+#+end_src
+
+The values one may predefine in this way are via these variables ([[#h:c916d8c5-540a-409f-b780-6ccbd90e088e][For developers or advanced users]]):
+
+#+findex: denote-use-date
++ ~denote-use-date~
+
+#+findex: denote-use-directory
++ ~denote-use-directory~
+
+#+findex: denote-use-file-type
++ ~denote-use-file-type~
+
+#+findex: denote-use-keywords
++ ~denote-use-keywords~
+
+#+findex: denote-use-signature
++ ~denote-use-signature~
+
+#+findex: denote-use-template
++ ~denote-use-template~
+
+#+findex: denote-use-title
++ ~denote-use-title~
+
+When there exists a binding for the aforementioned variables, the
+corresponding prompt is always skipped. It is thus paramount to never
+set those variables outside the scope of a ~let~ (or equivalent).
+
+With those granted, here is another example scenario where the user
+wants to have a constant value for the subdirectory but also be
+prompted for a date.
+
+#+begin_src emacs-lisp
+(with-eval-after-load 'org-capture
+ (add-to-list 'org-capture-templates
+ '("j" "New journal (with Denote)" plain
+ (file denote-last-path)
+ (function
+ (lambda ()
+ ;; The "journal" subdirectory of the `denote-directory'---this must exist!
+ (let* ((denote-use-directory (expand-file-name "journal" (denote-directory)))
+ ;; Use the existing `denote-prompts' as well as the one for a date.
+ (denote-prompts (denote-add-prompts '(date))))
+ (denote-org-capture))))
+ :no-save t
+ :immediate-finish nil
+ :kill-buffer t
+ :jump-to-captured t)))
+#+end_src
+
+The above highlights the hackability of the Denote code base, namely,
+how we can affect the behaviour of the underlying ~denote~ command by
+~let~ binding variables that affect every aspect of its behaviour
+([[#h:11946562-7eb0-4925-a3b5-92d75f1f5895][Write your own convenience commands]]).
+
+
+** Create a note with the region's contents
+:PROPERTIES:
+:CUSTOM_ID: h:2f8090f1-50af-4965-9771-d5a91a0a87bd
+:END:
+
+#+findex: denote-region
+The command ~denote-region~ takes the contents of the active region
+and then calls the ~denote~ command. Once a new note is created, it
+inserts the contents of the region therein. This is useful to
+quickly elaborate on some snippet of text or capture it for future
+reference.
+
+#+vindex: denote-region-after-new-note-functions
+When the ~denote-region~ command is called with an active region, it
+finalises its work by calling ~denote-region-after-new-note-functions~.
+This is an abnormal hook, meaning that the functions added to it are
+called with arguments. The arguments are two, representing the
+beginning and end positions of the newly inserted text.
+
+A common use-case for Org mode users is to call the command
+~org-insert-structure-template~ after a region is inserted. Emacs
+will thus prompt for a structure template, such as the one
+corresponding to a source block. In this case the function added to
+~denote-region-after-new-note-functions~ does not actually need
+aforementioned arguments: it can simply declare those as ignored by
+prefixing the argument names with an underscore (an underscore is
+enough, but it is better to include a name for clarity). For example,
+the following will prompt for a structure template as soon as
+~denote-region~ is done:
+
+#+begin_src emacs-lisp
+(defun my-denote-region-org-structure-template (_beg _end)
+ (when (derived-mode-p 'org-mode)
+ (activate-mark)
+ (call-interactively 'org-insert-structure-template)))
+
+(add-hook 'denote-region-after-new-note-functions #'my-denote-region-org-structure-template)
+#+end_src
+
+Remember that ~denote-region-after-new-note-functions~ are not called
+if ~denote-region~ is used without an active region.
+
+*** A custom ~denote-region~ that references the source
+:PROPERTIES:
+:CUSTOM_ID: h:eb72086e-05be-4ae3-af51-7616999fc7c9
+:END:
+
+The ~denote-region~ command simply creates a new note and includes the
+highlighted region's contents as the initial text of the note ([[#h:2f8090f1-50af-4965-9771-d5a91a0a87bd][Create a note with the region's contents]]).
+However, users may want a more streamlined workflow where the command
+is always used to capture quotes from other sources. In this example,
+we consider "other sources" to come from Emacs EWW buffers (with ~M-x
+eww~) or regular files outside the ~denote-directory~.
+
+[ This is a proof-of-concept that does not cover all cases. If anyone
+ wants to use a variation of this, just let me know. ]
+
+#+begin_src emacs-lisp
+;; Variant of `my-denote-region' to reference the source
+
+(defun my-denote-region-get-source-reference ()
+ "Get a reference to the source for use with `my-denote-region'.
+The reference is a URL or an Org-formatted link to a file."
+ ;; We use a `cond' here because we can extend it to cover move
+ ;; cases.
+ (cond
+ ((derived-mode-p 'eww-mode)
+ (plist-get eww-data :url))
+ ;; Here we are just assuming an Org format. We can make this more
+ ;; involved, if needed.
+ (buffer-file-name
+ (format "[[file:%s][%s]]" buffer-file-name (buffer-name)))))
+
+(defun my-denote-region ()
+ "Like `denote-region', but add the context afterwards.
+For how the context is retrieved, see `my-denote-region-get-source-reference'."
+ (interactive)
+ (let ((context (my-denote-region-get-source-reference)))
+ (call-interactively 'denote-region)
+ (when context
+ (goto-char (point-max))
+ (insert "\n")
+ (insert context))))
+
+;; Add quotes around snippets of text captured with `denote-region' or `my-denote-region'.
+
+(defun my-denote-region-org-structure-template (beg end)
+ "Automatically quote (with Org syntax) the contents of `denote-region'."
+ (when (derived-mode-p 'org-mode)
+ (goto-char end)
+ (insert "#+end_quote\n")
+ (goto-char beg)
+ (insert "#+begin_quote\n")))
+
+(add-hook 'denote-region-after-new-note-functions #'my-denote-region-org-structure-template)
+#+end_src
+
+With the above in place, calling the ~my-denote-region~ command does
+the following:
+
+- It creates a new note as usual, prompting for the relevant data.
+- Inserts the contents of the region below the front matter of the new
+ note.
+- Adds Org-style quotation block markers around the inserted region.
+- Adds a link to the URL or file from where ~my-denote-region~ was called.
+
+** Open an existing note or create it if missing
+:PROPERTIES:
+:CUSTOM_ID: h:ad91ca39-cf10-4e16-b224-fdf78f093883
+:END:
+
+#+findex: denote-open-or-create
+#+findex: denote-open-or-create-with-command
+Sometimes it is necessary to briefly interrupt the ongoing writing
+session to open an existing note or, if that is missing, to create it.
+This happens when a new tangential thought occurs and the user wants
+to confirm that an entry for it is in place. To this end, Denote
+provides the command ~denote-open-or-create~ as well as its more
+flexible counterpart ~denote-open-or-create-with-command~.
+
+The ~denote-open-or-create~ prompts to visit a file in the
+~denote-directory~. At this point, the user must type in search terms
+that match a file name. If the input does not return any matches and
+the user confirms their choice to proceed (usually by typing RET
+twice, depending on the minibuffer settings), ~denote-open-or-create~
+will call the ~denote~ command interactively to create a new note. It
+will then use whatever prompts ~denote~ normally has, per the user
+option ~denote-prompts~ ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). If the title prompt
+is involved (the default behaviour), the ~denote-open-or-create~ sets
+up this prompt to have the previous input as the default title of the
+note to-be-created. This means that the user can type RET at the
+empty prompt to re-use what they typed in previously. Commands to use
+previous inputs from the history are also available (=M-p= or =M-n= in
+the minibuffer, which call ~previous-history-element~ and
+~next-history-element~ by default). Accessing the history is helpful
+to, for example, make further edits to the available text.
+
+The ~denote-open-or-create-with-command~ is like the above, except
+when it is about to create the new note it first prompts for the
+specific file-creating command to use ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). For example,
+the user may want to specify a signature for this new file, so they
+can select the ~denote-signature~ command.
+
+Denote provides similar functionality for linking to an existing note
+or creating a new one ([[#h:b6056e6b-93df-4e6b-a778-eebd105bac46][Link to a note or create it if missing]]).
+
+** Maintain separate directory silos for notes
+:PROPERTIES:
+:CUSTOM_ID: h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5
+:END:
+#+cindex: Note silos
+
+The user option ~denote-directory~ accepts a value that represents the
+path to a directory, such as =~/Documents/notes=. Normally, the user
+will have one place where they store all their notes, in which case
+this arrangement shall suffice.
+
+There is, however, the possibility to maintain separate directories of
+notes. By "separate", we mean that they do not communicate with each
+other: no linking between them, no common keywords, nothing. Think of
+the scenario where one set of notes is for private use and another is
+for an employer. We call these separate directories "silos".
+
+To create silos, the user must specify a local variable at the root of
+the desired directory. This is done by creating a =.dir-locals.el=
+file, with the following contents:
+
+#+begin_src emacs-lisp
+;;; Directory Local Variables. For more information evaluate:
+;;;
+;;; (info "(emacs) Directory Variables")
+
+((nil . ((denote-directory . "/path/to/silo/"))))
+#+end_src
+
+When inside the directory that contains this =.dir-locals.el= file,
+all Denote commands/functions for note creation, linking, the
+inference of available keywords, et cetera will use the silo as their
+point of reference ([[#h:e43baf95-f201-4fec-8620-c0eb5eaa1c85][The ~denote-silo~ package which formerly was =denote-silo-extras.el=]]).
+They will not read the global value of ~denote-directory~. The global
+value of ~denote-directory~ is read everywhere else except the silos.
+
+In concrete terms, this is a representation of the directory structures
+(notice the =.dir-locals.el= file is needed only for the silos):
+
+#+begin_example
+;; This is the global value of 'denote-directory' (no need for a .dir-locals.el)
+~/Documents/notes
+|-- 20210303T120534--this-is-a-test__journal_philosophy.txt
+|-- 20220303T120534--another-sample__journal_testing.md
+`-- 20220620T181255--the-third-test__keyword.org
+
+;; A silo with notes for the employer
+~/different/path/to/notes-for-employer
+|-- .dir-locals.el
+|-- 20210303T120534--this-is-a-test__conference.txt
+|-- 20220303T120534--another-sample__meeting.md
+`-- 20220620T181255--the-third-test__keyword.org
+
+;; Another silo with notes for my volunteering
+~/different/path/to/notes-for-volunteering
+|-- .dir-locals.el
+|-- 20210303T120534--this-is-a-test__activism.txt
+|-- 20220303T120534--another-sample__teambuilding.md
+`-- 20220620T181255--the-third-test__keyword.org
+#+end_example
+
+It is possible to configure other user options of Denote to have a
+silo-specific value. For example, this one changes the
+~denote-known-keywords~ only for this particular silo:
+
+#+begin_src emacs-lisp
+;;; Directory Local Variables. For more information evaluate:
+;;;
+;;; (info "(emacs) Directory Variables")
+
+((nil . ((denote-directory . "/path/to/silo/")
+ (denote-known-keywords . ("food" "drink")))))
+#+end_src
+
+This one is like the above, but also disables ~denote-infer-keywords~:
+
+#+begin_src emacs-lisp
+;;; Directory Local Variables. For more information evaluate:
+;;;
+;;; (info "(emacs) Directory Variables")
+
+((nil . ((denote-directory . "/path/to/silo/")
+ (denote-known-keywords . ("food" "drink"))
+ (denote-infer-keywords . nil))))
+#+end_src
+
+To expand the list of local variables to, say, cover specific major
+modes, we can do something like this:
+
+#+begin_src emacs-lisp
+;;; Directory Local Variables. For more information evaluate:
+;;;
+;;; (info "(emacs) Directory Variables")
+
+((nil . ((denote-directory . "/path/to/silo/")
+ (denote-known-keywords . ("food" "drink"))
+ (denote-infer-keywords . nil)))
+ (org-mode . ((org-hide-emphasis-markers . t)
+ (org-hide-macro-markers . t)
+ (org-hide-leading-stars . t))))
+#+end_src
+
+As not all user options have a "safe" local value, Emacs will ask the
+user to confirm their choice and to store it in the Custom code
+snippet that is normally appended to init file (or added to the file
+specified by the user option ~custom-file~).
+
+Finally, it is possible to have a =.dir-locals.el= for subdirectories
+of any ~denote-directory~. Perhaps to specify a different set of
+known keywords, while not making the subdirectory a silo in its own
+right. We shall not expand on such an example, as we trust the user
+to experiment with the best setup for their workflow.
+
+Feel welcome to ask for help if the information provided herein is not
+sufficient. The manual shall be expanded accordingly.
+
+*** Make Org export work with silos
+:PROPERTIES:
+:CUSTOM_ID: h:fed09992-7c43-4237-b48f-f654bc29d1d8
+:END:
+
+The Org export infrastructure is designed to ignore directory-local
+variables. This means that Denote silos, which depend on setting the
+local value of the variable ~denote-directory~, do not work as
+intended ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directory silos for notes]]). More
+specifically, the Denote links do not resolve to the right file,
+because their path is changed during the export process.
+
+I brought this to the attention of the Org maintainer. The guidance
+from their side is to use the =#+bind= keyword to specify a local
+value for the ~denote-directory~: <https://lists.gnu.org/archive/html/emacs-orgmode/2024-06/msg00206.html>.
+The prerequisite is to set ~org-export-allow-bind-keywords~ to a
+non-nil value:
+
+#+begin_src emacs-lisp
+(setq org-export-allow-bind-keywords t)
+#+end_src
+
+I do not think this is an elegant solution, but here are two possible
+ways to go about it, anyway:
+
+1. Manually add the =#+bind= keyword to each file you want to export.
+ It has to be like this:
+
+ #+begin_src emacs-lisp
+ ,#+bind: denote-directory "/path/to/silo/"
+ #+end_src
+
+2. Alternatively, you can make the Org front matter that Denote uses
+ for new files automatically include the =#+bind= keyword with its
+ desired value. Here is a complete =.dir-locals.el= which (i)
+ defines the silo and (ii) modifies the ~denote-org-front-matter~
+ accordingly:
+
+ #+begin_src emacs-lisp
+ ;;; Directory Local Variables. For more information evaluate:
+ ;;;
+ ;;; (info "(emacs) Directory Variables")
+
+ ((nil . ((denote-directory . "/path/to/silo/")
+ (denote-org-front-matter .
+ "#+title: %s
+,#+date: %s
+,#+filetags: %s
+,#+identifier: %s
+,#+bind: denote-directory \"/path/to/silo/\"
+\n"))))
+ #+end_src
+
+ [ Note that if you are reading the Org source of this manual, you
+ need to use the command ~org-edit-special~ on the above code
+ blocks before copying the code. This is because Org automatically
+ prepends a comma to disambiguate those entries from actual
+ keywords of the current file. ]
+
+** Exclude certain files from file prompts
+:PROPERTIES:
+:CUSTOM_ID: h:53db09de-2cec-4670-b163-5cb791f997b4
+:END:
+
+#+vindex: denote-excluded-files-regexp
+The user option ~denote-excluded-files-regexp~ is a regular expression
+that matches files names which should be excluded from all Denote file
+prompts. Such prompts are present when linking to a file with one of
+the many commands, like ~denote-link~ ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]), or when trying
+to open a file that may or may not exist ([[#h:ad91ca39-cf10-4e16-b224-fdf78f093883][Open an existing note or create it if missing]]).
+
+Functions that check for files include ~denote-directory-files~ and
+~denote-file-prompt~.
+
+The match is performed with ~string-match-p~.
+
+[[#h:c916d8c5-540a-409f-b780-6ccbd90e088e][For developers or advanced users]].
+
+** Exclude certain directories from all operations
+:PROPERTIES:
+:CUSTOM_ID: h:8458f716-f9c2-4888-824b-2bf01cc5850a
+:END:
+
+#+vindex: denote-excluded-directories-regexp
+The user option ~denote-excluded-directories-regexp~ instructs all
+Denote functions that read or check file/directory names to omit
+directories that match the given regular expression. The regexp needs
+to match only the name of the directory, not its full path.
+
+Affected operations include file prompts and functions that return the
+available files in the value of the user option ~denote-directory~
+([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directory silos for notes]]).
+
+File prompts are used by several commands, such as ~denote-link~ and
+~denote-subdirectory~.
+
+Functions that check for files include ~denote-directory-files~ and
+~denote-directory-subdirectories~.
+
+The match is performed with ~string-match-p~.
+
+[[#h:c916d8c5-540a-409f-b780-6ccbd90e088e][For developers or advanced users]].
+
+** Exclude certain keywords from being inferred
+:PROPERTIES:
+:CUSTOM_ID: h:69e518ee-ed43-40ab-a5f4-c780a23e5358
+:END:
+
+#+vindex: denote-excluded-keywords-regexp
+The user option ~denote-excluded-keywords-regexp~ omits keywords that
+match a regular expression from the list of inferred keywords.
+
+Keywords are inferred from file names and provided at relevant prompts
+as completion candidates when the user option ~denote-infer-keywords~
+is non-nil.
+
+The match is performed with ~string-match-p~.
+
+** Create a controlled vocabulary for keywords
+:PROPERTIES:
+:CUSTOM_ID: h:c0fb477f-4f99-4d76-9cce-132bcfcb351d
+:END:
+
+Denote has two ways to know about keywords: the predefined list of
+strings specified in the user option ~denote-known-keywords~ as well
+as all the keywords it finds in the files of the ~denote-directory~
+when the user option ~denote-infer-keywords~ is set to a non-nil value
+([[#h:69e518ee-ed43-40ab-a5f4-c780a23e5358][Exclude certain keywords from being inferred]]).
+
+While this is a viable setup, users may prefer to implement a
+"controlled vocabulary". This is a predefined set of keywords whose
+purpose is to avoid the creation of overly specific or inconsistent
+keywords.
+
+To establish such a controlled vocabulary, users need only have
+something like this in their configuration:
+
+#+begin_src emacs-lisp
+;; Do not read keywords from files. The only source is the `denote-known-keywords'.
+(setq denote-infer-keywords nil)
+
+;; Define the list of keywords. Each keyword is a string.
+(setq denote-known-keywords (list "politics" "economics" "emacs" "philosophy"))
+#+end_src
+
+** Use Denote commands from the menu bar or context menu
+:PROPERTIES:
+:CUSTOM_ID: h:c4290e15-e97e-4a9b-b8db-6b9738e37e78
+:END:
+
+Denote registers a submenu for the ~menu-bar-mode~. Users will find
+the entry called "Denote". From there they can use their pointer to
+select a command. For a sample of how this looks, read the
+development log: <https://protesilaos.com/codelog/2023-03-31-emacs-denote-menu/>.
+
+#+findex: denote-menu-bar-mode
+The command ~denote-menu-bar-mode~ toggles the presentation of the
+menu. It is enabled by default.
+
+Emacs also provides support for operations through a context menu.
+This is typically the set of actions that are made available via a
+right mouse click. Users who enable ~context-menu-mode~ can register
+the Denote entry for it by adding the following to their configuration
+file:
+
+#+begin_src emacs-lisp
+(add-hook 'context-menu-functions #'denote-context-menu)
+#+end_src
+
+* Renaming files
+:PROPERTIES:
+:CUSTOM_ID: h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca
+:END:
+
+Denote provides commands to rename files and update their front matter
+where relevant. For Denote to work, only the file name needs to be in
+order, by following our naming conventions ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+The linking mechanism, in particular, needs just the identifier in the
+file name ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]).
+
+We write front matter in notes for the user's convenience and for other
+tools to make use of that information (e.g. Org's export mechanism).
+The renaming mechanism takes care to keep this data in sync with the
+file name, when the user performs a change.
+
+Renaming is useful for managing existing files created with Denote,
+but also for converting older text files to Denote notes. Denote's
+file-naming scheme is not specific to notes or text files: it is
+relevant for all sorts of items, such as multimedia and PDFs that form
+part of the user's longer-term storage. While Denote does not manage
+such files (e.g. doesn't create links to them), it already has all the
+mechanisms to facilitate the task of renaming them.
+
+#+vindex: denote-after-rename-file-hook
+All renaming commands run the ~denote-after-rename-file-hook~ after a
+succesful operation ([[#h:a947908e-1847-4471-ba07-377ee2f4b36c][Access the data of the latest note]]). They also
+construct the file name in accordance with the user option
+~denote-file-name-components-order~ ([[#h:dc8c40e0-233a-4991-9ad3-2cf5f05ef1cd][Change the order of file name components]]).
+
+Apart from renaming files, Denote can also rename only the buffer.
+The idea is that the underlying file name is correct but it can be
+easier to use shorter buffer names when displaying them on the mode
+line or switching between then with commands like ~switch-to-buffer~.
+
+[[#h:3ca4db16-8f26-4d7d-b748-bac48ae32d69][Automatically rename Denote buffers]].
+
+[[#h:9051f15d-ea7e-4b17-adc2-bc6a749c721b][Find duplicate identifiers and put them in a Dired buffer]].
+
+** Rename a single file
+:PROPERTIES:
+:CUSTOM_ID: h:7cc9e000-806a-48da-945c-711bbc7426b0
+:END:
+
+#+findex: denote-rename-file
+The ~denote-rename-file~ command renames a file and updates existing
+front matter if appropriate. It is possible to do the same with
+multiple files ([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files interactively]]).
+
+It always renames the file where it is located in the file system:
+it never moves it to another directory.
+
+If in Dired, it considers =FILE= to be the one at point, else it
+prompts with minibuffer completion for one. When called from Lisp,
+=FILE= is a file system path represented as a string.
+
+If =FILE= has a Denote-compliant identifier, it retains it while
+updating components of the file name referenced by the user option
+~denote-prompts~ ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The ~denote-prompts~ option]]). By default, these are
+the =TITLE= and =KEYWORDS=. The =SIGNATURE= is another one. When
+called from Lisp, =TITLE= and =SIGNATURE= are strings, while
+=KEYWORDS= is a list of strings.
+
+If there is no identifier, ~denote-rename-file~ creates an identifier
+based on the following conditions:
+
+1. If the ~denote-prompts~ includes an entry for date prompts, then it
+ prompts for =DATE= and takes its input to produce a new identifier. For
+ use in Lisp, =DATE= must conform with ~denote-valid-date-p~.
+
+2. If =DATE= is nil (e.g. when ~denote-prompts~ does not include a
+ date entry), it uses the file attributes to determine the last
+ modified date of =FILE= and formats it as an identifier.
+
+3. As a fallback, it derives an identifier from the current date and
+ time.
+
+4. At any rate, if the resulting identifier is not unique among the
+ files in the variable ~denote-directory~, it increments it such
+ that it becomes unique.
+
+In interactive use, and assuming ~denote-prompts~ includes a title
+entry, the ~denote-rename-file~ makes the =TITLE= prompt have
+prefilled text in the minibuffer that consists of the current title of
+=FILE=. The current title is either retrieved from the front matter
+(such as the =#+title= in Org) or from the file name.
+
+The command does the same for the =SIGNATURE= prompt, subject to
+~denote-prompts~, by prefilling the minibuffer with the current
+signature of =FILE=, if any.
+
+Same principle for the =KEYWORDS= prompt: it converts the keywords in
+the file name into a comma-separated string and prefills the minibuffer
+with it (the =KEYWORDS= prompt accepts more than one keywords, each
+separated by a comma, else the ~crm-separator~).
+
+For all prompts, the ~denote-rename-file~ interprets an empty input as
+an instruction to remove that file name component. For example, if a
+=TITLE= prompt is available and =FILE= is =20240211T093531--some-title__keyword1.org=
+then it renames =FILE= to =20240211T093531__keyword1.org=.
+
+In interactive use, if there is no entry for a file name component in
+~denote-prompts~, keep it as-is ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The ~denote-prompts~ option]]).
+
+When called from Lisp, the special symbol `keep-current' can be
+used for the TITLE, KEYWORDS, SIGNATURE and DATE parameters to
+keep them as-is.
+
+[ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the empty
+ minibuffer contents as they are, though popular packages like
+ ~vertico~ use the first available completion candidate instead. For
+ ~vertico~, the user must either move one up to select the prompt and
+ then type =RET= there with empty contents, or use the command
+ ~vertico-exit-input~ with empty contents. That Vertico command is
+ bound to =M-RET= as of this writing on 2024-02-13 08:08 +0200. ]
+
+When renaming =FILE=, the command reads its file type extension (like
+=.org=) and preserves it through the renaming process. Files that have
+no extension are left without one.
+
+As a final step, ask for confirmation, showing the difference
+between old and new file names. Do not ask for confirmation if
+the user option ~denote-rename-confirmations~ does not contain
+the symbol ~modify-file-name~ ([[#h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7][The ~denote-rename-confirmations~ option]]).
+
+If =FILE= has front matter for =TITLE= and =KEYWORDS=, ask to rewrite
+their values in order to reflect the new input, unless
+~denote-rename-confirmations~ lacks ~rewrite-front-matter~. When the
+~denote-save-buffers~ is nil (the default), do not save the underlying
+buffer, thus giving the user the option to double-check the result,
+such as by invoking the command ~diff-buffer-with-file~. The rewrite
+of the =TITLE= and =KEYWORDS= in the front matter should not affect
+the rest of the front matter.
+
+If the file does not have front matter but is among the supported file
+types (per ~denote-file-type~), add front matter to the top of it and
+leave the buffer unsaved for further inspection ([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]). Save
+the buffer if ~denote-save-buffers~ is non-nil ([[#h:bf80f4cd-6f56-4f7c-a991-8573161e4511][The ~denote-save-buffers~ option]]).
+
+Construct the file name in accordance with the user option
+~denote-file-name-components-order~ ([[#h:dc8c40e0-233a-4991-9ad3-2cf5f05ef1cd][Change the order of file name components]]).
+
+Run the ~denote-after-rename-file-hook~ after renaming =FILE= ([[#h:a947908e-1847-4471-ba07-377ee2f4b36c][Access the data of the latest note]]).
+
+This command is intended to (i) rename Denote files, (ii) convert
+existing supported file types to Denote notes, and (ii) rename
+non-note files (e.g. =PDF=) that can benefit from Denote's file-naming
+scheme.
+
+For a version of this command that works with multiple files
+one-by-one, use ~denote-dired-rename-files~ ([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files interactively]]).
+
+*** The ~denote-rename-confirmations~ option
+:PROPERTIES:
+:CUSTOM_ID: h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7
+:END:
+
+#+vindex: denote-rename-confirmations
+The user option ~denote-rename-confirmations~ controls what kind of
+confirmation renaming commands ask for ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]). Its value is
+a list of symbols.
+
+The value is either nil, in which case no confirmation is ever
+requested, or a list of symbols among the following:
+
+- ~modify-file-name~ means that renaming commands will ask for
+ confirmation before modifying the file name.
+
+- ~rewrite-front-matter~ means that renaming commands will ask for
+ confirmation before rewritting the front matter.
+
+- ~add-front-matter~ means that renaming commands will ask for
+ confirmation before adding new front matter to the file.
+
+The default behaviour of the ~denote-rename-file~ command (and others
+like it) is to ask for an affirmative answer as a final step before
+changing the file name and, where relevant, inserting or updating the
+corresponding front matter.
+
+Specialized commands that build on top of ~denote-rename-file~ (or
+related) may internally bind this user option to a non-nil value in
+order to perform their operation (e.g. ~denote-dired-rename-files~
+goes through each marked Dired file, prompting for the information to
+use, but carries out the renaming without asking for confirmation
+([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files interactively]])).
+
+** Rename a single file based on its front matter
+:PROPERTIES:
+:CUSTOM_ID: h:3ab08ff4-81fa-4d24-99cb-79f97c13a373
+:END:
+
+#+findex: denote-rename-file-using-front-matter
+In the previous section, we covered the more general mechanism of the
+command ~denote-rename-file~ ([[#h:7cc9e000-806a-48da-945c-711bbc7426b0][Rename a single file]]). There is also a
+way to have the same outcome by making Denote read the data in the
+current file's front matter and use it to construct/update the file
+name. The command for this is ~denote-rename-file-using-front-matter~.
+It is only relevant for files that (i) are among the supported file
+types, per ~denote-file-type~, and (ii) have the requisite front matter
+in place.
+
+Suppose you have an =.org= file with this front matter ([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]):
+
+#+begin_example
+#+title: My sample note file
+#+date: [2022-08-05 Fri 13:10]
+#+filetags: :testing:
+#+identifier: 20220805T131044
+#+end_example
+
+Its file name reflects this information:
+
+: 20220805T131044--my-sample-note-file__testing.org
+
+You want to change its title and keywords manually, so you modify it thus:
+
+#+begin_example
+#+title: My modified sample note file
+#+date: [2022-08-05 Fri 13:10]
+#+filetags: :testing:denote:emacs:
+#+identifier: 20220805T131044
+#+end_example
+
+At this stage, the file name still shows the old title and keywords.
+You now invoke ~denote-rename-file-using-front-matter~ and it updates
+the file name to:
+
+: 20220805T131044--my-modified-sample-note-file__testing_denote_emacs.org
+
+By default, the renaming is subject to a "yes or no" prompt that shows
+the old and new names, just so the user is certain about the change.
+Though this can be modified ([[#h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7][The ~denote-rename-confirmations~ option]]).
+
+The identifier of the file, if any, is never modified even if it is
+edited in the front matter: Denote considers the file name to be the
+source of truth in this case, to avoid potential breakage with typos and
+the like.
+
+This command constructs the file name in accordance with the user option
+~denote-file-name-components-order~ ([[#h:dc8c40e0-233a-4991-9ad3-2cf5f05ef1cd][Change the order of file name components]]).
+
+** Rename multiple files interactively
+:PROPERTIES:
+:CUSTOM_ID: h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde
+:END:
+
+#+findex: denote-dired-rename-files
+#+findex: denote-dired-rename-marked-files
+The command ~denote-dired-rename-files~ (alias ~denote-dired-rename-marked-files~)
+renames the files that are marked in a Dired buffer. Its behaviour is
+similar to the ~denote-rename-file~ in that it prompts for a title,
+keywords, and signature ([[#h:7cc9e000-806a-48da-945c-711bbc7426b0][Rename a single file]]). It does so over each
+marked file, renaming one after the other.
+
+Unlike ~denote-rename-file~, the command ~denote-dired-rename-files~
+does not ask to confirm the changes made to the files: it performs
+them outright (same as setting ~denote-rename-confirmations~ to a nil
+value). This is done to make it easier to rename multiple files
+without having to confirm each step. For an even more direct approach,
+check the command ~denote-dired-rename-marked-files-with-keywords~.
+
+- [[#h:f365ff7e-2140-4e14-a92f-666ae97382a4][Rename by writing only keywords]]
+- [[#h:ea5673cd-e6ca-4c42-a066-07dc6c9d57f8][Rename multiple files based on their front matter]]
+
+** Rename multiple files at once by asking only for keywords
+:PROPERTIES:
+:CUSTOM_ID: h:f365ff7e-2140-4e14-a92f-666ae97382a4
+:END:
+
+#+findex: denote-dired-rename-marked-files-with-keywords
+The ~denote-dired-rename-marked-files-with-keywords~ command renames
+marked files in Dired to conform with our file-naming scheme. It does
+so by writing keywords to them. Specifically, it does the following:
+
+- retains the file's existing name and makes it the =TITLE= field, per
+ Denote's file-naming scheme;
+
+- sluggifies the =TITLE= and adjusts its letter casing, according to
+ our conventions;
+
+- prepends an identifier to the =TITLE=, if one is missing;
+
+- preserves the file's extension, if any;
+
+- prompts once for =KEYWORDS= and applies the user's input to the
+ corresponding field in the file name, rewriting any keywords that
+ may exist while removing keywords that do exist if =KEYWORDS= is
+ empty;
+
+- adds or rewrites existing front matter to the underlying file, if it
+ is recognized as a Denote note (per the ~denote-file-type~ user
+ option), such that it includes the new keywords.
+
+[ Note that the affected buffers are not saved, unless the user option
+ ~denote-rename-no-confirm~ is non-nil. Users can thus check them to
+ confirm that the new front matter does not cause any problems (e.g.
+ with the ~diff-buffer-with-file~ command). Multiple buffers can be
+ saved in one go with the command ~save-some-buffers~ (read its doc
+ string). ]
+
+Construct the file name in accordance with the user option
+~denote-file-name-components-order~ ([[#h:dc8c40e0-233a-4991-9ad3-2cf5f05ef1cd][Change the order of file name components]]).
+
+Run the ~denote-after-rename-file-hook~ after the renaming is done.
+
+#+findex: denote-dired-rename-marked-files-add-keywords
+#+findex: denote-dired-rename-marked-files-remove-keywords
+For more specialized versions of this command that only add or remove
+keywords, use ~denote-dired-rename-marked-files-add-keywords~ and
+~denote-dired-rename-marked-files-remove-keywords~, respectively.
+
+** Rename multiple files based on their front matter
+:PROPERTIES:
+:CUSTOM_ID: h:ea5673cd-e6ca-4c42-a066-07dc6c9d57f8
+:END:
+
+#+findex: denote-dired-rename-marked-files-using-front-matter
+As already noted, Denote can rename a file based on the data in its
+front matter ([[#h:3ab08ff4-81fa-4d24-99cb-79f97c13a373][Rename a single file based on its front matter]]). The
+command ~denote-dired-rename-marked-files-using-front-matter~ extends
+this principle to a batch operation which applies to all marked files in
+Dired.
+
+Marked files must count as notes for the purposes of Denote, which
+means that they at least have an identifier in their file name and use
+a supported file type, per ~denote-file-type~. Files that do not meet
+this criterion are ignored, because Denote cannot know if they have
+front matter and what that may be. For such files, it is still
+possible to rename them interactively ([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files interactively]]).
+
+** Rename a file by changing only its file type
+:PROPERTIES:
+:CUSTOM_ID: h:85b65995-89fd-4978-bba3-7bb6c8d6f945
+:END:
+
+#+findex: denote-change-file-type-and-front-matter
+The command ~denote-change-file-type-and-front-matter~ provides the
+convenience of converting a note taken in one file type, say, =.txt=
+into another like =.org=. It presents a choice among the
+~denote-file-type~ options.
+
+The conversion does NOT modify the existing front matter. Instead, it
+prepends new front matter to the top of the file. We do this as a
+safety precaution since the user can, in principle, add arbitrary
+extras to their front matter that we would not want to touch.
+
+If in Dired, ~denote-change-file-type-and-front-matter~ operates on the
+file at point, else the current file, else it prompts with minibuffer
+completion for one.
+
+The title of the file is retrieved from a line starting with a title
+field in the file's front matter, depending on the previous file type
+(e.g. =#+title= for Org). The same process applies for keywords.
+
+As a final step, the command asks for confirmation, showing the
+difference between old and new file names.
+
+This command constructs the file name in accordance with the user option
+~denote-file-name-components-order~ ([[#h:dc8c40e0-233a-4991-9ad3-2cf5f05ef1cd][Change the order of file name components]]).
+
+** Rename a file by adding or removing a title interactively
+:PROPERTIES:
+:CUSTOM_ID: h:a26e28c7-8222-4377-92e9-3b0a709010a5
+:END:
+
+#+findex: denote-rename-file-title
+The command ~denote-rename-file-title~ streamlines the process of
+interactively adding or removing a title to/from a file, while
+changing its file name accordingly. It asks for a title using the
+familiar minibuffer prompt ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). It then renames
+the file. The command respect the values of
+~denote-rename-confirmations~ and ~denote-save-buffers~:
+
+- [[#h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7][The ~denote-rename-confirmations~ option]].
+- [[#h:bf80f4cd-6f56-4f7c-a991-8573161e4511][The ~denote-save-buffers~ option]].
+
+Technically, ~denote-rename-file-title~ is a wrapper for
+~denote-rename-file~, doing all the things that does ([[#h:7cc9e000-806a-48da-945c-711bbc7426b0][Rename a single file]]).
+
+Concretely, this command can add or remove a title in one go. It
+does it by prepopulating the minibuffer prompt with the existing
+title. Users can then modify it. An empty input means to remove
+the title altogether ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+
+[ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the empty
+ minibuffer contents as they are, though popular packages like
+ ~vertico~ use the first available completion candidate instead. For
+ ~vertico~, the user must either move one up to select the prompt and
+ then type =RET= there with empty contents, or use the command
+ ~vertico-exit-input~ with empty contents. That Vertico command is
+ bound to =M-RET= as of this writing on 2024-06-30 10:37 +0300. ]
+
+** Rename a file by adding or removing keywords interactively
+:PROPERTIES:
+:CUSTOM_ID: h:ad4dde4a-8e88-470a-97ae-e7b9d4b41fb4
+:END:
+
+#+findex: denote-rename-file-keywords
+The command ~denote-rename-file-keywords~ streamlines the process of
+interactively adding or removing keywords to a file, while changing
+its file name and front matter accordingly. It asks for keywords using
+the familiar minibuffer prompt ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). It then
+renames the file ([[#h:3ab08ff4-81fa-4d24-99cb-79f97c13a373][Rename a single file based on its front matter]]).
+The command respect the values of ~denote-rename-confirmations~ and
+~denote-save-buffers~:
+
+- [[#h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7][The ~denote-rename-confirmations~ option]].
+- [[#h:bf80f4cd-6f56-4f7c-a991-8573161e4511][The ~denote-save-buffers~ option]].
+
+Technically, ~denote-rename-file-keywords~ is a wrapper for
+~denote-rename-file~, doing all the things that does ([[#h:7cc9e000-806a-48da-945c-711bbc7426b0][Rename a single file]]).
+
+Concretely, this command can add or remove keywords in one go. It does
+it by prepopulating the minibuffer prompt with the existing keywords.
+Users can then use the ~crm-separator~ (normally a comma), to write
+new keywords or edit what is in the prompt to rewrite them
+accordingly. An empty input means to remove all keywords ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+
+[ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the empty
+ minibuffer contents as they are, though popular packages like
+ ~vertico~ use the first available completion candidate instead. For
+ ~vertico~, the user must either move one up to select the prompt and
+ then type =RET= there with empty contents, or use the command
+ ~vertico-exit-input~ with empty contents. That Vertico command is
+ bound to =M-RET= as of this writing on 2024-06-30 10:37 +0300. ]
+
+** Rename a file by adding or removing a signature interactively
+:PROPERTIES:
+:CUSTOM_ID: h:b08a350f-b269-47ed-8c2a-b8ecf1b63c7f
+:END:
+
+#+findex: denote-rename-file-signature
+The command ~denote-rename-file-signature~ streamlines the process of
+interactively adding or removing a signature to/from a file, while
+changing its file name accordingly. It asks for a signature using the
+familiar minibuffer prompt ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). It then renames
+the file. The command respect the values of
+~denote-rename-confirmations~ and ~denote-save-buffers~:
+
+- [[#h:a2ae9090-c49e-4b32-bcf5-eb8944241fd7][The ~denote-rename-confirmations~ option]].
+- [[#h:bf80f4cd-6f56-4f7c-a991-8573161e4511][The ~denote-save-buffers~ option]].
+
+Technically, ~denote-rename-file-signature~ is a wrapper for
+~denote-rename-file~, doing all the things that does ([[#h:7cc9e000-806a-48da-945c-711bbc7426b0][Rename a single file]]).
+
+Concretely, this command can add or remove a signature in one go. It
+does it by prepopulating the minibuffer prompt with the existing
+signature. Users can then modify it. An empty input means to remove
+the signature altogether ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+
+[ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the empty
+ minibuffer contents as they are, though popular packages like
+ ~vertico~ use the first available completion candidate instead. For
+ ~vertico~, the user must either move one up to select the prompt and
+ then type =RET= there with empty contents, or use the command
+ ~vertico-exit-input~ with empty contents. That Vertico command is
+ bound to =M-RET= as of this writing on 2024-06-30 10:37 +0300. ]
+
+** Find duplicate identifiers and put them in a Dired buffer
+:PROPERTIES:
+:CUSTOM_ID: h:9051f15d-ea7e-4b17-adc2-bc6a749c721b
+:END:
+
+Denote takes care to create unique identifiers, though its mechanism
+relies on reading the existing identifiers in the ~denote-directory~
+or the current directory. When we are renaming files across different
+directories, there is a small chance that some files have the same
+attributes and are thus assigned identical identifiers. If those files
+ever make it into a consolidated ~denote-directory~, we will have
+duplicates, which break the linking mechanism.
+
+As this is an edge case, we do not include any code to address it in
+the Denote code base. Though here is a way to find duplicate
+identifiers inside the current directory:
+
+#+begin_src emacs-lisp
+(defun my-denote--get-files-in-dir (directory)
+ "Return file names in DIRECTORY."
+ (directory-files directory :full-paths directory-files-no-dot-files-regexp))
+
+(defun my-denote--same-identifier-p (file1 file2)
+ "Return non-nil if FILE1 and FILE2 have the same identifier."
+ (let ((id1 (denote-retrieve-filename-identifier file1))
+ (id2 (denote-retrieve-filename-identifier file2)))
+ (equal id1 id2)))
+
+(defun my-denote-find-duplicate-identifiers (directory)
+ "Find all files in DIRECTORY that need a new identifier."
+ (let* ((ids (my-denote--get-files-in-dir directory))
+ (unique-ids (seq-uniq ids #'my-denote--same-identifier-p)))
+ (seq-difference ids unique-ids #'equal)))
+
+(defun my-denote-dired-show-duplicate-identifiers (directory)
+ "Put duplicate identifiers from DIRECTORY in a dedicated Dired buffer."
+ (interactive
+ (list
+ (read-directory-name "Select DIRECTORY to check for duplicate identifiers: " default-directory)))
+ (if-let* ((duplicates (my-denote-find-duplicate-identifiers directory)))
+ (dired (cons (format "Denote duplicate identifiers" directory) duplicates))
+ (message "No duplicates identifiers in `%s'" directory)))
+#+end_src
+
+Evaluate this code and then call the command ~my-denote-dired-show-duplicate-identifiers~.
+If there are duplicates, it will put them in a dedicated Dired buffer.
+From there, you can view the file contents as usual, and manually edit
+the identifiers as you see fit (e.g. edit them one by one, or change
+to the writable Dired and record a keyboard macro that makes use of a
+counter to increment by 1---contact me if you need any help).
+
+** Faces used by rename commands
+:PROPERTIES:
+:CUSTOM_ID: h:ab3f355a-f763-43ae-a4c9-179d2d9265a5
+:END:
+
+These are the faces used by the various Denote rename commands to
+style or highlight the old/new/current file shown in the relevant
+minibuffer prompts:
+
+- ~denote-faces-prompt-current-name~
+- ~denote-faces-prompt-new-name~
+- ~denote-faces-prompt-old-name~
+
+* The file-naming scheme
+:PROPERTIES:
+:CUSTOM_ID: h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d
+:END:
+
+#+vindex: denote-directory
+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 by default
+([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]):
+
+: DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION
+
+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 and, as such, is also known as the file's ID or identifier.
+
+File names can include an arbitrary string of alphanumeric characters
+in the =SIGNATURE= field. Signatures have no clearly defined purpose
+and are up to the user to define. They can serve as special labels,
+such as =part1= and =part2= of a large file, or as priority indicators
+like =a=, =b=, =c=, or even context/scope specifiers like =home= and
+=work=. Another use-case is to write sequences of thoughts, such that
+notes form a hierarchy, something we support with the optional and
+comprehensive extension =denote-sequence.el= ([[#h:d5ca722d-e7fa-46fa-9a57-6363b1d4186f][Write sequence notes or "folgezettel"]]).
+Signatures are an optional extension to Denote's file-naming scheme.
+In the simplest form, 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~ ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The ~denote-prompts~ option]]).
+
+The =TITLE= field is the title of the note, as provided by the user.
+It automatically gets downcased by default and is also hyphenated
+([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]). An entry about "Economics
+in the Euro Area" produces an =economics-in-the-euro-area= string for
+the =TITLE= of the file name.
+
+The =KEYWORDS= field consists of one or more entries demarcated by an
+underscore (the separator is inserted automatically). Each keyword is
+a string provided by the user at the relevant prompt which broadly
+describes the contents of the entry.
+
+Each of the keywords is a single word, with multiple keywords providing
+the multi-dimensionality needed for advanced searches through Denote
+files. Users who need to compose a keyword out of multiple words such
+as camelCase/CamelCase and are encouraged to use the
+~denote-file-name-slug-functions~ user option accordingly
+([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]).
+
+#+vindex: denote-file-type
+The =EXTENSION= is the file type. By default, it is =.org= (~org-mode~)
+though the user option ~denote-file-type~ provides support for Markdown
+with YAML or TOML variants (=.md= which runs ~markdown-mode~) and plain
+text (=.txt= via ~text-mode~). Consult its doc string for the minutiae.
+While files end in the =.org= extension by default, the Denote code base
+does not actually depend on org.el and/or its accoutrements.
+
+Examples:
+
+: 20220610T043241--initial-thoughts-on-the-zettelkasten-method__notetaking.org
+: 20220610T062201--define-custom-org-hyperlink-type__denote_emacs_package.md
+: 20220610T162327--on-hierarchy-and-taxis__notetaking_philosophy.txt
+
+The different field separators, namely =--= and =__= introduce an
+efficient way to anchor searches (such as with Emacs commands like
+~isearch~ or from the command-line with ~find~ and related). A query
+for =_word= always matches a keyword, while a regexp in the form of,
+say, ="\\([0-9T]+?\\)--\\(.*?\\)_"= captures the date in group =\1= and
+the title in =\2= (test any regular expression in the current buffer by
+invoking =M-x re-builder=).
+
+[[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]].
+
+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, which is carefully
+considered and works well ([[#h:dc8c40e0-233a-4991-9ad3-2cf5f05ef1cd][Change the order of file name components]]).
+
+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. The Denote file-naming scheme is, in essence, an effective,
+low-tech invention.
+
+** Change the order of file name components
+:PROPERTIES:
+:CUSTOM_ID: h:dc8c40e0-233a-4991-9ad3-2cf5f05ef1cd
+:END:
+
+#+vindex: denote-file-name-components-order
+Our standard file-naming scheme prescribes a specific order for the
+file name components ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). Though we provide the
+user option ~denote-file-name-components-order~ to let the user
+reorder them as they see fit.
+
+The value of this user option is a list of the following symbols:
+
+- ~identifier~: This is the combination of the date and time. When it
+ is the first on the list, it looks like =20240519T073456= and does
+ not have a component separator of its own due its unambiguous
+ format. When it is placed anywhere else in the file name, it is
+ prefixed with =@@=, so it looks like =@@20240519T073456=.
+
+- ~signature~: This is an arbitrary string that can be used to qualify
+ the file in some way, according to the user's methodology (e.g. to
+ add a sequence to notes). The string is always prefixed with the
+ ~==~ to remain unambiguous.
+
+- ~title~: This is an arbitrary string which describes the file. It is
+ always prefixed with =--= to be unambiguous.
+
+- ~keywords~: This is a series of one or more words that succinctly
+ group the file. Multiple keywords are separated by an underscore
+ prefixed to each of them. The file name component is always prefixed
+ with =__=.
+
+All four symbols must appear exactly once. Duplicates are ignored. Any
+missing symbol is added automatically.
+
+Some examples:
+
+#+begin_src emacs-lisp
+(setq denote-file-name-components-order '(identifier signature title keywords))
+;; => 20240519T07345==hello--this-is-the-title__denote_testing.org
+
+(setq denote-file-name-components-order '(signature identifier title keywords))
+;; => ==hello@@20240519T07345--this-is-the-title__denote_testing.org
+
+(setq denote-file-name-components-order '(title signature identifier keywords))
+;; => --this-is-the-title==hello@@20240519T07345__denote_testing.org
+
+(setq denote-file-name-components-order '(keywords title signature identifier))
+;; => __denote_testing--this-is-the-title==hello@@20240519T07345.org
+#+end_src
+
+Also see how to configure the Denote prompts, which affect which
+components are actually used in the order specified herein ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The ~denote-prompts~ option]]).
+
+Before deciding on this, please consider the longer-term implications
+of file names with varying patterns. Consistency makes things
+predictable and thus easier to find. So pick one order and never touch
+it again. When in doubt, leave the default file-naming scheme as-is.
+
+** Sluggification of file name components
+:PROPERTIES:
+:CUSTOM_ID: h:ae8b19a1-7f67-4258-96b3-370a72c43f4e
+:END:
+
+Files names can contain any character that the file system
+permits. Denote imposes a few additional restrictions:
+
++ The tokens "==", =__= and =--= are interpreted by Denote and should
+ appear only once.
+
++ The dot character is not allowed in a note's file name, except to
+ indicate the file type extension. Denote recognises two extensions
+ for encrypted files, like =.txt.gpg=.
+
+By default, Denote enforces other rules to file names through the user
+option ~denote-file-name-slug-functions~. These rules are applied to
+file names by default:
+
++ What we count as "illegal characters" are removed.
+
++ Input for a file title is hyphenated. The original value is
+ preserved in the note's contents ([[#h:13218826-56a5-482a-9b91-5b6de4f14261][Front matter]]).
+
++ Spaces or other delimiters are removed from keywords, meaning that
+ =hello-world= becomes =helloworld=. This is because hyphens in
+ keywords do not work everywhere, such as in Org. Plus, hyphens are
+ word separators in the title and we want to keep distinct separators
+ for each component to make search easier and semantic
+ ([[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]).
+
++ Signatures are like the above, but use the equals sign instead of
+ hyphens as a word separator.
+
++ All file name components are downcased. Further down we document how
+ to deviate from these rules, such as to accept input of the form
+ =helloWorld= or =HelloWorld= verbatim.
+
+Denote imposes these restrictions to enforce uniformity, which is
+helpful long-term as it keeps all files with the same predictable
+pattern. Too many permutations make searches more difficult to express
+accurately and be confident that the matches cover all files.
+Nevertheless, one of the principles of Denote is its flexibility or
+hackability and so users can deviate from the aforementioned
+([[#h:d375c6d2-92c7-425f-9d9d-219ff47ed2a3][User-defined sluggification of file name components]]).
+
+** User-defined sluggification of file name components
+:PROPERTIES:
+:CUSTOM_ID: h:d375c6d2-92c7-425f-9d9d-219ff47ed2a3
+:END:
+
+#+vindex: denote-file-name-slug-functions
+The user option ~denote-file-name-slug-functions~ controls the
+sluggification of file name components ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]).
+The default method is outlined above and in the previous section
+([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+
+The value of this user option is an alist where each element is a cons
+cell of the form =(COMPONENT . METHOD)=. For example, here is the
+default value:
+
+#+begin_example emacs-lisp
+'((title . denote-sluggify-title)
+ (signature . denote-sluggify-signature)
+ (keyword . denote-sluggify-keyword))
+#+end_example
+
+- The =COMPONENT= is an unquoted symbol among =title=, =signature=,
+ =keyword=, which refers to the corresponding component of the file
+ name.
+
+- The =METHOD= is a function to format the given component. This
+ function must take a string as its parameter and return the string
+ formatted for the file name. Note that even in the case of the
+ =keyword= component, the function receives one string representing a
+ single keyword and returns it formatted for the file name. Joining
+ the keywords together is handled internally by Denote.
+
+One commonly requested deviation from the sluggification rules is to
+not sluggify individual keywords, such that the user's input is taken
+as-is. This can be done as follows:
+
+#+begin_src emacs-lisp
+(setq denote-file-name-slug-functions
+ '((title . denote-sluggify-title)
+ (keyword . identity)
+ (signature . denote-sluggify-signature)))
+#+end_src
+
+The ~identity~ function simply returns the string it receives, thus
+not altering it in any way.
+
+Another approach is to keep the sluggification but not downcase the
+string. We can do this by modifying the original functions used by
+Denote. For example, we have this:
+
+#+begin_src emacs-lisp
+;; The original function for reference
+(defun denote-sluggify-title (str)
+ "Make STR an appropriate slug for title."
+ (downcase
+ (denote-slug-hyphenate
+ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/=]*" "" str))))
+
+;; Our variant of the above, which does the same thing except from
+;; downcasing the string.
+(defun my-denote-sluggify-title (str)
+ "Make STR an appropriate slug for title."
+ (denote-slug-hyphenate
+ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/=]*" "" str)))
+
+;; Now we use our function to sluggify titles without affecting their
+;; letter casing.
+(setq denote-file-name-slug-functions
+ '((title . my-denote-sluggify-title) ; our function here
+ (signature . denote-sluggify-signature)
+ (keyword . denote-sluggify-keyword)))
+#+end_src
+
+Follow this principle for all the sluggification functions ([[#h:d1e4eb5b-e7f2-4a3b-9243-e1c653817a4a][Custom sluggification to remove non-ASCII characters]]).
+
+To access the source code, use either of the following built-in
+methods:
+
+1. Call the command ~find-library~ and search for ~denote~. Then
+ navigate to the symbol you are searching for.
+
+2. Invoke the command ~describe-symbol~, search for the symbol you are
+ interested in, and from the resulting Help buffer either click on
+ the first link or do =M-x help-view-source= (bound to =s= in Help
+ buffers, by default).
+
+Remember that deviating from the default file-naming scheme of Denote
+will make things harder to use in the future, as files can/will have
+permutations that create uncertainty. The sluggification scheme and
+concomitant restrictions we impose by default are there for a very
+good reason: they are the distillation of years of experience. Here we
+give you what you wish, but bear in mind it may not be what you need.
+You have been warned.
+
+*** Custom sluggification to remove non-ASCII characters
+:PROPERTIES:
+:CUSTOM_ID: h:d1e4eb5b-e7f2-4a3b-9243-e1c653817a4a
+:END:
+
+A common use-case for Denote is to rename files such as videos
+downloaded from the Internet. Sometimes, those files have Unicode
+characters that (i) not all fonts support and (ii) create all sorts of
+problems with pattern matching, such as when searching through file
+names.
+
+By default, Denote does not remove Unicode characters because users
+may actually want them (e.g. Latin characters with accents). Those who
+do, however, wish to keep everything limited to the ASCII range can
+use the following in their Emacs configuration ([[#h:d375c6d2-92c7-425f-9d9d-219ff47ed2a3][User-defined sluggification of file name components]]).
+
+#+begin_src emacs-lisp
+;; These are the same as the default Denote sluggification functions,
+;; except they remove all non-ASCII characters.
+(defun my-denote-sluggify-title (str)
+ (downcase
+ (denote-slug-hyphenate
+ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/=]*" ""
+ (denote-slug-keep-only-ascii str)))))
+
+(defun my-denote-sluggify-signature (str)
+ (downcase
+ (denote-slug-put-equals
+ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/-]*" ""
+ (denote-slug-keep-only-ascii str)))))
+
+(defun my-denote-sluggify-keyword (str)
+ (downcase
+ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/_ =-]*" ""
+ (denote-slug-keep-only-ascii str))))
+
+(defcustom denote-file-name-slug-functions
+ '((title . my-denote-sluggify-title)
+ (signature . my-denote-sluggify-signature)
+ (keyword . my-denote-sluggify-keyword)))
+#+end_src
+
+** Features of the file-naming scheme for searching or filtering
+:PROPERTIES:
+:CUSTOM_ID: h:1a953736-86c2-420b-b566-fb22c97df197
+:END:
+
+By default, file names have three fields and two sets of field
+delimiters between them:
+
+: DATE--TITLE__KEYWORDS.EXTENSION
+
+When a signature is present, this becomes:
+
+: DATE==SIGNATURE--TITLE__KEYWORDS.EXTENSION
+
+Field delimiters practically serve as anchors for easier searching.
+Consider this example:
+
+: 20220621T062327==1a2--introduction-to-denote__denote_emacs.txt
+
+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 can 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:
+:CUSTOM_ID: h:13218826-56a5-482a-9b91-5b6de4f14261
+:END:
+
+Notes have their own "front matter". This is a block of data at the top
+of the file, with no empty lines between the entries, which is
+automatically generated at the creation of a new note. The front matter
+includes the title and keywords (aka "tags" or "filetags", depending on
+the file type) which the user specified at the relevant prompt, as well
+as the date and unique identifier, which are derived automatically.
+
+This is how it looks for Org mode (when ~denote-file-type~ is nil or the
+=org= symbol):
+
+#+begin_example
+#+title: This is a sample note
+#+date: [2022-06-30 Thu 16:09]
+#+filetags: :denote:testing:
+#+identifier: 20220630T160934
+#+end_example
+
+For Markdown with YAML (~denote-file-type~ has the =markdown-yaml=
+value), the front matter looks like this:
+
+#+begin_example
+---
+title: "This is a sample note"
+date: 2022-06-30T16:09:58+03:00
+tags: ["denote", "testing"]
+identifier: "20220630T160958"
+---
+#+end_example
+
+For Markdown with TOML (~denote-file-type~ has the =markdown-toml=
+value), it is:
+
+#+begin_example
++++
+title = "This is a sample note"
+date = 2022-06-30T16:10:13+03:00
+tags = ["denote", "testing"]
+identifier = "20220630T161013"
++++
+#+end_example
+
+And for plain text (~denote-file-type~ has the =text= value), we have
+the following:
+
+#+begin_example
+title: This is a sample note
+date: 2022-06-30
+tags: denote testing
+identifier: 20220630T161028
+---------------------------
+#+end_example
+
+#+vindex: denote-date-format
+The format of the date in the front matter is controlled by the user
+option ~denote-date-format~. When nil, Denote uses a file-type-specific
+format:
+
+- For Org, an inactive timestamp is used, such as
+ =[2022-06-30 Wed 15:31]=.
+
+- For Markdown, the RFC3339 standard is applied:
+ =2022-06-30T15:48:00+03:00=.
+
+- For plain text, the format is that of ISO 8601: =2022-06-30=.
+
+If the value is a string, ignore the above and use it instead. The
+string must include format specifiers for the date. These are described
+in the doc string of ~format-time-string~..
+
+** Change the front matter format
+:PROPERTIES:
+:CUSTOM_ID: h:7f918854-5ed4-4139-821f-8ee9ba06ad15
+:END:
+
+Per Denote's design principles, the code is hackable. All front matter
+is stored in variables that are intended for public use. We do not
+declare those as "user options" because (i) they expect the user to have
+some degree of knowledge in Emacs Lisp and (ii) implement custom code.
+
+[ NOTE for tinkerers: code intended for internal use includes double
+ hyphens in its symbol. "Internal use" means that it can be changed
+ without warning and with no further reference in the change log. Do
+ not use any of it without understanding the consequences. ]
+
+The variables which hold the front matter format are:
+
+#+vindex: denote-org-front-matter
+- ~denote-org-front-matter~
+
+#+vindex: denote-text-front-matter
+- ~denote-text-front-matter~
+
+#+vindex: denote-toml-front-matter
+- ~denote-toml-front-matter~
+
+#+vindex: denote-yaml-front-matter
+- ~denote-yaml-front-matter~
+
+These variables have a string value with specifiers that are used by the
+~format~ function. The formatting operation passes four arguments which
+include the values of the given entries. If you are an advanced user
+who wants to edit this variable to affect how front matter is produced,
+consider using something like =%2$s= to control where the Nth argument
+is placed.
+
+When editing the value, make sure to:
+
+1. Not use empty lines inside the front matter block.
+
+2. Insert at least one empty line after the front matter block and do
+ not use any empty line before it.
+
+These help with consistency and might prove useful if we ever need to
+operate on the front matter as a whole.
+
+With those granted, below are some examples. The approach is the same
+for all variables.
+
+#+begin_src emacs-lisp
+;; Like the default, but upcase the entries
+(setq denote-org-front-matter
+ "#+TITLE: %s
+#+DATE: %s
+#+FILETAGS: %s
+#+IDENTIFIER: %s
+\n")
+
+;; Change the order (notice the %N$s notation)
+(setq denote-org-front-matter
+ "#+title: %1$s
+#+filetags: %3$s
+#+date: %2$s
+#+identifier: %4$s
+\n")
+
+;; Remove the date
+(setq denote-org-front-matter
+ "#+title: %1$s
+#+filetags: %3$s
+#+identifier: %4$s
+\n")
+
+;; Remove the date and the identifier
+(setq denote-org-front-matter
+ "#+title: %1$s
+#+filetags: %3$s
+\n")
+#+end_src
+
+Note that ~setq~ has a global effect: it affects the creation of all new
+notes. Depending on the workflow, it may be preferrable to have a
+custom command which ~let~ binds the different format. We shall not
+provide examples at this point as this is a more advanced feature and we
+are not yet sure what the user's needs are. Please provide feedback and
+we shall act accordingly.
+
+** Regenerate front matter
+:PROPERTIES:
+:CUSTOM_ID: h:54b48277-e0e5-4188-ad54-ef3db3b7e772
+:END:
+
+As part of version 4.0.0, the command ~denote-add-front-matter~ is
+superseded by ~denote-rename-file~ and related ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]). Those
+commands will add missing front matter or rewrite the modified lines
+of existing front matter.
+
+* Linking notes
+:PROPERTIES:
+:CUSTOM_ID: h:fc913d54-26c8-4c41-be86-999839e8ad31
+:END:
+
+Denote offers several commands for linking between notes. Those use
+the =denote:= hyperlink type. There are two types of links supported
+by Denote:
+
+- Direct links :: A direct link points to a file inside the
+ ~denote-directory~. The link is constructed by using the =denote:=
+ prefix and the target file's identifier ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+ This looks like =denote:20250328T075526=. The syntax of a link
+ depends on the file type. For example, in Org and plain text links look like
+ =[[denote:20250328T075526][The title of the target file]]=, while in
+ Markdown they are written as =[The title of the target file](denote:20250328T075526)=.
+
+- Query links :: The =denote:= hyperlink type also supports special
+ qualifiers that change how the target of the link is interpreted.
+ The qualifier is a special token than tells Denote how to treat the
+ target of the link. It is written thus =denote:TOKEN:QUERY=. There
+ are two kinds of tokens: =query-contents= and =query-filenames=.
+ Those determine how the query terms are used. As their names
+ suggest, these two tokens trigger a search in (i) the file contents
+ of all readable files or (ii) in the file names only. They are, in
+ other words, counterparts of the Unix ~grep~ and ~find~ programs,
+ respectively.
+
+The following sections cover all the details ([[#h:4f354db1-aa78-47fd-ac60-c3d1e0f0b0a4][Why are some Org links opening outside Emacs?]]).
+
+** Add a single direct link using a file name prompt
+:PROPERTIES:
+:CUSTOM_ID: h:5e5e3370-12ab-454f-ba09-88ff44214324
+:END:
+
+#+findex: denote-link
+#+findex: denote-insert-link
+The ~denote-link~ command (alias ~denote-insert-link~) inserts a link
+at point to a file selected at the minibuffer prompt. Links are
+formatted depending on the file type of the current note. In Org and
+plain text buffers, links are formatted thus: =[[denote:IDENTIFIER][DESCRIPTION]]=.
+While in Markdown they are expressed as =[DESCRIPTION](denote:IDENTIFIER)=.
+
+When ~denote-link~ is called with a prefix argument (=C-u= by
+default), it formats links like =[[denote:IDENTIFIER]]=, regardless of
+file type ([[#h:156c5ea3-147b-4f9d-a404-86a00558c60a][Fontify links in non-Org buffers]]). The user might prefer
+its simplicity.
+
+By default, the description of the link is determined thus:
+
+- If the region is active, its text becomes the description of the
+ link. In other words, the region text becomes the link.
+- If the region is active but has no text, the description is empty
+ and so the link is formatted the same way as if using the =C-u=
+ prefix argument.
+- If there is no region active, the description consists of the target
+ file's signature and title, using the former only if it is present.
+ The title is retrieved either from the front matter or the file
+ name.
+- If the target file has no signature, the title is used.
+
+To insert multiple such links at once, use the command
+~denote-add-links~ ([[#h:9bec2c83-36ca-4951-aefc-7187c5463f90][Insert links matching a regexp in their file name]]).
+
+If you want to directly link to a single file whose contents match a
+given query, then use the command ~denote-link-to-file-with-contents~
+([[#h:25a983ca-049e-43d4-8f6e-06a2325e2c3c][Adding a direct link to a file whose contents include the given query]]).
+
+#+vindex: denote-faces-link
+Links are styled with the ~denote-faces-link~ face, which looks
+exactly like an ordinary link by default.
+
+[ We optionally support direct links to a file followed by an extra
+ target to an Org headings ([[#h:d99de1fb-b1b7-4a74-8667-575636a4d6a4][The ~denote-org-store-link-to-heading~ user option]]).
+ Other file types do not have the features of Org, so we cannot
+ generalise this. ]
+
+** Add a direct link to a file whose contents include the given query
+:PROPERTIES:
+:CUSTOM_ID: h:25a983ca-049e-43d4-8f6e-06a2325e2c3c
+:END:
+
+#+findex: denote-link-to-file-with-contents
+The ~denote-link~ command that we covered before prompts to select a
+file among those in the ~denote-directory~ ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Adding a single direct link using a file name prompt]]).
+The match is done against the file's name. Users may, however, be
+interested to create a link to a file whose contents include some
+text, regardless of how the file name is called. To this end, the
+command ~denote-link-to-file-with-contents~, (i) prompts for a query
+which is a plain string or regular expression, (ii) if there are
+matching files, asks to select one among them, and (iii) inserts the
+direct link at point.
+
+When called with an optional prefix argument (=C-u= by default), the
+command ~denote-link-to-file-with-contents~ creates a link that does
+not include a description for the target file: it just has the file's
+identifier (same as with ~denote-link~).
+
+The command ~denote-link-to-file-with-contents~ is the counterpart of
+~denote-link-to-all-files-with-contents~ ([[#h:299d3aeb-9946-489e-bd91-e06f8c4ae2a9][Insert links to all files matching a query in their contents]]).
+
+** Add a query link
+:PROPERTIES:
+:CUSTOM_ID: h:d9a84289-2f73-4ef9-b4f0-9a0aa3e9bf0d
+:END:
+
+As noted in the introduction to this section of the manual, the
+=denote:= hyperlink type supports query links ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]). Unlike
+direct links, they do not point to any given file. Instead, they
+trigger a search, whose results are displayed in a separate buffer.
+
+Query links are expressed as =denote:TOKEN:QUERY=, where =TOKEN= is
+either =query-contents= or =query-filenames=, while =QUERY= is a
+string or Emacs regular expression to search for.
+
+The exact syntax of a query link depends on the file type. In Org and
+plain text buffers, links are of the form =[[denote:TOKEN:QUERY][QUERY]]=.
+In Markdown, they are formatted as =[QUERY](denote:TOKEN:QUERY)=. In
+all cases, the description of the link is the query text itself.
+
+#+findex: denote-query-contents-link
+The command ~denote-query-contents-link~ inserts a link at point that
+triggers a search in the file contents of all readable documents in
+the ~denote-directory~ ([[#h:435592bc-e896-429f-a599-9f1bcd5ab9b8][Interact with the links buffer]]). This is the
+equivalent of the Unix ~grep~ command and uses the built-in Emacs Xref
+interface ([[#h:893eec49-d7be-4603-bcff-fcc247244011][Speed up backlinks' or query links' buffer creation?]]).
+Matches are displayed in a separate buffer, highlighting the exact
+text while showing its context.
+
+#+findex: denote-query-filenames-link
+The command ~denote-query-filenames-link~ creates a link at point that
+initiates a search across file names in the ~denote-directory~. This
+is the equivalent of the Unix ~find~ command. Results are placed in a
+Dired buffer ([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Display filtered and sorted files with ~denote-sort-dired~]]).
+
+#+vindex: denote-query-links-display-buffer-action
+The user option ~denote-query-links-display-buffer-action~ controls
+the placement of query link buffers. By default, they are designed to
+appear below the current window.
+
+#+vindex: denote-faces-query-link
+Query links are styled with the ~denote-faces-query-link~ face, which
+looks a bit different that ~denote-faces-link~ (though this depends on
+the active theme).
+
+** Insert links to all files matching a query in their file name
+:PROPERTIES:
+:CUSTOM_ID: h:9bec2c83-36ca-4951-aefc-7187c5463f90
+:END:
+
+#+findex: denote-add-links
+The command ~denote-add-links~ adds links at point to all file names
+in the ~denote-directory~ that match a regular expression or plain
+string. This is similar to the ~denote-link~ command, which
+establishes a direct link to a specified file ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Adding a single direct link]]).
+Links to files whose names match the given search terms are inserted
+as a typographic list, such as:
+
+#+begin_example
+- link1
+- link2
+- link3
+#+end_example
+
+Each link is formatted according to the file type of the current note,
+as explained further above about the ~denote-link~ command. The current
+note is excluded from the matching entries (adding a link to itself is
+pointless).
+
+When called with a prefix argument (=C-u=) ~denote-add-links~ will
+format all links as =[[denote:IDENTIFIER]]=, hence a typographic list:
+
+#+begin_example
+- [[denote:IDENTIFIER-1]]
+- [[denote:IDENTIFIER-2]]
+- [[denote:IDENTIFIER-3]]
+#+end_example
+
+Same examples of a regular expression that can be used with this
+command:
+
+- =journal= match all files which include =journal= anywhere in their
+ name.
+
+- =_journal= match all files which include =journal= as a keyword.
+
+- =^2022.*_journal= match all file names starting with =2022= and
+ including the keyword =journal=.
+
+- =\.txt= match all files including =.txt=. In practical terms, this
+ only applies to the file extension, as Denote automatically removes
+ dots (and other characters) from the base file name.
+
+If files are created with ~denote-sort-keywords~ as non-nil (the
+default), then it is easy to write a regexp that includes multiple
+keywords in alphabetic order:
+
+- =_denote.*_package= match all files that include both the =denote= and
+ =package= keywords, in this order.
+
+- =\(.*denote.*package.*\)\|\(.*package.*denote.*\)= is the same as
+ above, but out-of-order.
+
+Remember that regexp constructs only need to be escaped once (like =\|=)
+when done interactively but twice when called from Lisp. What we show
+above is for interactive usage.
+
+Links are created only for files which qualify as a "note" for our
+purposes ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]).
+
+** Insert links to all files matching a query in their contents
+:PROPERTIES:
+:CUSTOM_ID: h:299d3aeb-9946-489e-bd91-e06f8c4ae2a9
+:END:
+
+#+findex: denote-link-to-all-files-with-contents
+The aforementioned ~denote-add-links~ command takes a query that
+matches it against file names ([[#h:9bec2c83-36ca-4951-aefc-7187c5463f90][Insert links to all files matching a query in their file name]]).
+It then creates a typographic list (bullet list) with direct links to
+all the matching files. Users who wish to achieve the same result but
+have the query be matched against file contents (not file names), can
+use the command ~denote-link-to-all-files-with-contents~.
+
+The command ~denote-link-to-all-files-with-contents~ is the
+counterpart of ~denote-link-to-file-with-contents~ ([[#h:25a983ca-049e-43d4-8f6e-06a2325e2c3c][Add a direct link to a file whose contents include the given query]]).
+
+** The ~denote-open-link-function~ user option
+:PROPERTIES:
+:CUSTOM_ID: h:6aa22a2d-3338-433e-ab9f-ba272417aab9
+:END:
+
+#+vindex: denote-open-link-function
+The user option ~denote-open-link-function~ specifies the function
+used by Denote to open the file of a link. The default value opens the
+file in the other window. Another common value is the function
+~find-file~, which will open the file in the current window. Users may
+also specify a function of their choosing.
+
+Note that this is relevant in buffers other than Org mode because Org
+has its own mechanism for how to open links (read the documentation of
+the command ~org-open-at-point~).
+
+** The ~denote-org-store-link-to-heading~ user option
+:PROPERTIES:
+:CUSTOM_ID: h:d99de1fb-b1b7-4a74-8667-575636a4d6a4
+:END:
+
+#+vindex: denote-org-store-link-to-heading
+The user option ~denote-org-store-link-to-heading~ determines whether
+~org-store-link~ links to the current Org heading.
+
+[ Remember that what ~org-store-link~ does is merely collect a link. To
+ actually insert it, use the command ~org-insert-link~. Note that
+ ~org-capture~ uses ~org-store-link~ internally when it needs to store
+ a link. ]
+
+When the value is nil, the Denote handler for ~org-store-link~ produces
+links only to the current file (by using the file's identifier). For
+example:
+
+: [[denote:20240118T060608][Some test]]
+
+If the value is ~context~, the link consists of the file's identifier
+and the text of the current heading, like this:
+
+: [[denote:20240118T060608::*Heading text][Some test::Heading text]].
+
+However, if there already exists a =CUSTOM_ID= property for the
+current heading, this is always given priority and is used instead of
+the context.
+
+If the value is ~id~ or, for backward-compatibility, any other non-nil
+value, then Denote will use the standard Org mechanism of the
+=CUSTOM_ID= property to create a unique link to the heading. If the
+heading does not have a =CUSTOM_ID=, it creates it and includes it in
+its =PROPERTIES= drawer. If a =CUSTOM_ID= exists, it takes it as-is.
+The result is like this:
+
+ : [[denote:20240118T060608::#h:eed0fb8e-4cc7-478f][Some test::Heading text]]
+
+The value of the =CUSTOM_ID= is determined by the Org user option
+~org-id-method~. The sample shown above uses the default UUID
+infrastructure (though I deleted a few characters to not get
+complaints from the byte compiler about long lines in the doc
+string...).
+
+Note that this option does not affect how Org behaves with regard to
+~org-id-link-to-org-use-id~. If that user option is set to create =ID=
+properties, then those will be created by Org even if the Denote link
+handler will take care to not use/store the =ID= value. Concretely,
+users who never want =ID= properties under their headings should keep
+~org-id-link-to-org-use-id~ in its nil value.
+
+Context links are easier to break than those with a =CUSTOM_ID= in
+cases where either the heading text changes or there is another
+heading that matches that text. The potential advantage of context
+links is that they do not require a =PROPERTIES= drawer.
+
+When visiting a link to a heading, Org opens the Denote file and then
+navigates to that heading.
+
+[ This feature only works in Org mode files, as other file types do
+ not have a linking mechanism that handles unique identifiers for
+ headings or other patterns to jump to. If ~org-store-link~ is
+ invoked in one such file, it captures only the Denote identifier of
+ the file, even if this user option is set to a non-nil value. ]
+
+** Adding direct links to files matching contents
+:PROPERTIES:
+:CUSTOM_ID: h:28cb8d14-cf56-4d73-b126-8ff269dbaa64
+:END:
+
+** Insert links from marked files in Dired
+:PROPERTIES:
+:CUSTOM_ID: h:9cbb692e-5d8a-44a6-9193-899a07872a07
+:END:
+
+#+findex: denote-link-dired-marked-notes
+The command ~denote-link-dired-marked-notes~ is similar to
+~denote-add-links~ in that it inserts in the buffer a typographic list
+of links to Denote notes ([[#h:9bec2c83-36ca-4951-aefc-7187c5463f90][Insert links matching a regexp]]). Though
+instead of reading a regular expression, it lets the user mark files
+in Dired and link to them. This should be easier for users of all
+skill levels, instead of having to write a potentially complex regular
+expression.
+
+If there are multiple buffers that visit a Denote note, this command
+will ask to select one among them, using minibuffer completion. If
+there is only one buffer, it will operate in it outright. If there are
+no buffers, it will produce an error.
+
+With optional =ID-ONLY= as a prefix argument (=C-u= by default), the
+command inserts links with just the identifier, which is the same
+principle as with ~denote-link~ and others ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Adding a single link]]).
+
+The command ~denote-link-dired-marked-notes~ is meant to be used from a
+Dired buffer.
+
+As always, links are created only for files which qualify as a "note"
+for our purposes ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]).
+
+#+findex: denote-dired-link-marked-notes
+The ~denote-dired-link-marked-notes~ is an alias for ~denote-link-dired-marked-notes~.
+
+** Link to an existing note or create a new one
+:PROPERTIES:
+:CUSTOM_ID: h:b6056e6b-93df-4e6b-a778-eebd105bac46
+:END:
+
+In one's note-taking workflow, there may come a point where they are
+expounding on a certain topic but have an idea about another subject
+they would like to link to ([[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]). The user can always rely on
+the other linking facilities we have covered herein to target files that
+already exist. Though they may not know whether they already have notes
+covering the subject or whether they would need to write new ones. To
+this end, Denote provides two convenience commands:
+
+#+findex: denote-link-after-creating
++ ~denote-link-after-creating~ :: Create new note in the background and
+ link to it directly.
+
+ Use ~denote~ interactively to produce the new note. Its doc string or
+ this manual explains which prompts will be used and under what
+ conditions ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]).
+
+ With optional =ID-ONLY= as a prefix argument (this is the =C-u= key,
+ by default) 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~ ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Adding a single link]]).
+
+ IMPORTANT NOTE: Normally, ~denote~ does not save the buffer it
+ produces for the new note ([[#h:bf80f4cd-6f56-4f7c-a991-8573161e4511][The ~denote-save-buffer-after-creation~ option]]).
+ This is a safety precaution to not write to disk unless the user
+ wants it (e.g. the user may choose to kill the buffer, thus
+ cancelling the creation of the note). However, for this command the
+ creation of the note happens in the background and the user may miss
+ the step of saving their buffer. We thus have to save the buffer in
+ order to (i) establish valid links, and (ii) retrieve whatever front
+ matter from the target file.
+
+#+findex: denote-link-after-creating-with-command
++ ~denote-link-after-creating-with-command~ :: This command is like
+ ~denote-link-after-creating~ except it prompts for a note-creating
+ command ([[*Points of entry][Points of entry]]). Use this to, for example, call
+ ~denote-signature~ so that the newly created note has a signature as
+ part of its file name. Optional =ID-ONLY= has the same meaning as
+ in the command ~denote-link-after-creating~.
+
+#+findex: denote-link-or-create
++ ~denote-link-or-create~ :: Use ~denote-link~ on =TARGET= file,
+ creating it if necessary.
+
+ If =TARGET= file does not exist, call ~denote-link-after-creating~
+ which runs the ~denote~ command interactively to create the file. The
+ established link will then be targeting that new file.
+
+ If =TARGET= file does not exist, add the user input that was used to
+ search for it to the history of the ~denote-file-prompt~. The user
+ can then retrieve and possibly further edit their last input, using
+ it as the newly created note's actual title. At the ~denote-file-prompt~
+ type =M-p= with the default key bindings, which calls ~previous-history-element~.
+
+ With optional =ID-ONLY= as a prefix argument create a link with just
+ the file's identifier. This has the same meaning as in ~denote-link~.
+
+ This command has the alias ~denote-link-to-existing-or-new-note~,
+ which helps with discoverability.
+
+In all of the above, an optional prefix argument (=C-u= by default)
+creates a link that consists of just the identifier. This has the
+same meaning as in the regular ~denote-link~ command.
+
+Denote provides similar functionality for opening an existing note or
+creating a new one ([[#h:ad91ca39-cf10-4e16-b224-fdf78f093883][Open an existing note or create it if missing]]).
+
+** The backlinks' buffer
+:PROPERTIES:
+:CUSTOM_ID: h:c73f1f68-e214-49d5-b369-e694f6a5d708
+:END:
+
+[ Older versions of Denote had two types of formatting for the
+ backlinks' buffer. As part of version =4.0.0=, we only support the
+ standard Xref view which shows matches in their context. The user
+ option ~denote-backlinks-show-context~ is thus removed. ]
+
+#+findex: denote-backlinks
+#+findex: denote-show-backlinks-buffer
+The command ~denote-backlinks~ (alias ~denote-show-backlinks-buffer~)
+produces a bespoke buffer which displays backlinks to the current note
+([[#h:435592bc-e896-429f-a599-9f1bcd5ab9b8][Interact with the links buffer]]). A "backlink" is a link back to the
+present entry. Backlinks can be generated for any file type that has a
+Denote file-naming scheme, such as PDFs, images, and videos, as well
+as the regular plain text files.
+
+The backlinks' buffer is, in essence, the equivalent of a Unix ~grep~
+command across the ~denote-directory~ ([[#h:893eec49-d7be-4603-bcff-fcc247244011][Speed up backlinks' buffer creation?]]).
+It groups matches by file name, while it displays the line on which a
+link to the current file occurs together with its context. It looks
+like this (plus the appropriate fontification):
+
+#+begin_example
+Backlinks to "On being honest" (20220614T130812)
+------------------------------------------------
+
+20220614T145606--let-this-glance-become-a-stare__journal.txt
+37: growing into it: [[denote:20220614T130812][On being honest]].
+64: As I said in [[denote:20220614T130812][On being honest]] I have never
+20220616T182958--feeling-butterflies-in-your-stomach__journal.txt
+62: indifference. In [[denote:20220614T130812][On being honest]] I alluded
+#+end_example
+
+Note that the width of the lines in the context depends on the
+underlying file. In the above example, the lines are split at the
+~fill-column~. Long lines will show up just fine. Also note that the
+built-in user option ~xref-truncation-width~ can truncate long lines
+to a given maximum number of characters.
+
+As with query links, the backlinking facility uses Emacs' built-in
+Xref infrastructure ([[#h:d9a84289-2f73-4ef9-b4f0-9a0aa3e9bf0d][Adding a query link]]). On some operating systems,
+the user may need to add certain executables to the relevant
+environment variable ([[#h:42f6b07e-5956-469a-8294-17f9cf62eb2b][Why do I get "Search failed with status 1" when I search for backlinks?]]).
+
+#+vindex: denote-backlinks-display-buffer-action
+The placement of the backlinks' buffer is subject to the user option
+~denote-backlinks-display-buffer-action~. Due to the nature of the
+underlying ~display-buffer~ mechanism, this inevitably is a relatively
+advanced feature. By default, the backlinks' buffer is displayed below
+the current window.
+
+Backlinks to the current file can also be visited by using the
+minibuffer completion interface with the ~denote-find-backlink~
+command ([[#h:1bc2adad-dca3-4878-b9f0-b105d5dec6f4][Visiting linked files via the minibuffer]]).
+
+** Writing metanotes
+:PROPERTIES:
+:CUSTOM_ID: h:6060a7e6-f179-4d42-a9de-a9968aaebecc
+:END:
+
+A "metanote" is an entry that describes other entries who have something
+in common. Writing metanotes can be part of a workflow where the user
+periodically reviews their work in search of patterns and deeper
+insights. For example, you might want to read your journal entries from
+the past year to reflect on your experiences, evolution as a person, and
+the like.
+
+The commands ~denote-add-links~, ~denote-link-dired-marked-notes~ are
+suited for this task.
+
+[[#h:9bec2c83-36ca-4951-aefc-7187c5463f90][Insert links matching a regexp]].
+
+[[#h:9cbb692e-5d8a-44a6-9193-899a07872a07][Insert links from marked files in Dired]].
+
+You will create your metanote the way you use Denote ordinarily
+(metanotes may have the =metanote= keyword, among others), write an
+introduction or however you want to go about it, invoke the command
+which inserts multiple links at once (see the above-cited nodes), and
+continue writing.
+
+Metanotes can serve as entry points to groupings of individual notes.
+They are not the same as a filtered list of files, i.e. what you would
+do in Dired or the minibuffer where you narrow the list of notes to a
+given query. Metanotes contain the filtered list plus your thoughts
+about it. The act of purposefully grouping notes together and
+contemplating on their shared patterns is what adds value.
+
+Your future self will appreciate metanotes for the function they serve
+in encapsulating knowledge, while current you will be equipped with the
+knowledge derived from the deliberate self-reflection.
+
+** Visiting linked files via the minibuffer
+:PROPERTIES:
+:CUSTOM_ID: h:1bc2adad-dca3-4878-b9f0-b105d5dec6f4
+:END:
+
+#+findex: denote-find-link
+Denote has a major-mode-agnostic mechanism to collect all linked file
+references in the current buffer and return them as an appropriately
+formatted list. This list can then be used in interactive commands.
+The ~denote-find-link~ is such a command. It uses minibuffer
+completion to visit a file that is linked to from the current note.
+The candidates have the correct metadata, which is ideal for
+integration with other standards-compliant tools ([[#h:8ed2bb6f-b5be-4711-82e9-8bee5bb06ece][Extending Denote]]).
+For instance, a package such as =marginalia= will display accurate
+annotations, while the =embark= package will be able to work its magic
+such as in exporting the list into a filtered Dired buffer (i.e. a
+familiar Dired listing with only the files of the current minibuffer
+session).
+
+#+findex: denote-find-backlink
+To visit backlinks to the current note via the minibuffer, use
+~denote-find-backlink~. This is an alternative to placing backlinks
+in a dedicated buffer ([[#h:c73f1f68-e214-49d5-b369-e694f6a5d708][The backlinks' buffer]]).
+
+** Fontify links in non-Org buffers
+:PROPERTIES:
+:CUSTOM_ID: h:156c5ea3-147b-4f9d-a404-86a00558c60a
+:END:
+
+#+findex: denote-fontify-links-mode
+Denote links are automatically fontified in Org buffers ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Adding a single link]]).
+This means that Org recognises the link and applies the relevant
+properties to it to make it clickable/actionable. Other major modes,
+such as ~markdown-mode~ (for =.md= files) or ~text-mode~ (for =.txt=
+files) do not have this feature built into them. Users can still get
+the same behaviour as with Org by activating the ~denote-fontify-links-mode~.
+
+The ~denote-fontify-links-mode~ is a buffer-local minor mode. Users can enable
+it automatically in plain text files that correspond to denote notes with
+something like this:
+
+#+begin_src emacs-lisp
+(add-hook 'text-mode-hook #'denote-fontify-links-mode-maybe)
+#+end_src
+
+The ~text-mode-hook~ applies to all modes derived from ~text-mode~, including
+~markdown-mode~. Though a more explicit setup does no harm:
+
+#+begin_src emacs-lisp
+(add-hook 'markdown-mode-hook #'denote-fontify-links-mode-maybe)
+#+end_src
+
+Because Org already recognises =denote:= links, the function
+~denote-fontify-links-mode-maybe~ will not enable the mode
+~denote-fontify-links-mode~ in Org buffers.
+
+#+findex: denote-link-markdown-follow
+In files whose major mode is ~markdown-mode~, the default key binding
+=C-c C-o= (which calls the command ~markdown-follow-thing-at-point~)
+correctly resolves =denote:= links. Interested users can refer to the
+function ~denote-link-markdown-follow~ for the implementation details.
+
+** The ~denote-link-description-format~ to format link descriptions
+:PROPERTIES:
+:CUSTOM_ID: h:f634427c-b451-40e2-993e-e00ac627af68
+:END:
+
+The user option ~denote-link-description-format~ controls how the
+command ~denote-link~ and related functions create a link description
+by default.
+
+The value can be either a function or a string. If it is a function,
+it is called with one argument, the file, and should return a string
+representing the link description.
+
+The default is a function that returns the active region or the title of
+the note (with the signature if present).
+
+If the value is a string, it treats specially the following specifiers:
+
+- The =%t= is the Denote =TITLE= in the front matter or the file name.
+- The =%T= is the Denote =TITLE= in the file name.
+- The =%i= is the Denote =IDENTIFIER= of the file.
+- The =%I= is the identifier converted to =DAYNAME, DAYNUM MONTHNUM
+ YEAR=.
+- The =%d= is the same as =%i= (=DATE= mnemonic).
+- The =%D= is a "do what I mean" which behaves the same as =%t= and if
+ that returns nothing, it falls back to =%I=, then =%i=.
+- The =%d= is the same as =%i= (=DATE= mnemonic).
+- The =%s= is the Denote =SIGNATURE= of the file.
+- The =%k= is the Denote =KEYWORDS= of the file.
+- The =%%= is a literal percent sign.
+
+In addition, the following flags are available for each of the specifiers:
+
+- 0 :: Pad to the width, if given, with zeros instead of spaces.
+- - :: Pad to the width, if given, on the right instead of the left.
+- < :: Truncate to the width and precision, if given, on the left.
+- > :: Truncate to the width and precision, if given, on the right.
+- ^ :: Convert to upper case.
+- _ :: Convert to lower case.
+
+When combined all together, the above are written thus:
+
+: %<flags><width><precision>SPECIFIER-CHARACTER
+
+Any other text in the string it taken as-is. Users may want, for
+example, to include some text that makes Denote links stand out, such
+as a =[D]= prefix.
+
+If the region is active, its text is used as the link's description.
+
+* Choose which commands to prompt for
+:PROPERTIES:
+:CUSTOM_ID: h:98c732ac-da0e-4ebd-a0e3-5c47f9075e51
+:END:
+
+#+vindex: denote-commands-for-new-notes
+The user option ~denote-commands-for-new-notes~ specifies a list of
+commands that are available at the ~denote-command-prompt~. This
+prompt is used by Denote commands that ask the user how to create a
+new note, as described elsewhere in this manual:
+
+- [[#h:ad91ca39-cf10-4e16-b224-fdf78f093883][Open an existing note or create it if missing]]
+- [[#h:b6056e6b-93df-4e6b-a778-eebd105bac46][Link to a note or create it if missing]]
+
+The default value includes all the basic file-creating commands
+([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). Users may customise this value if (i) they only
+want to see fewer options and/or (ii) wish to include their own custom
+command in the list ([[#h:11946562-7eb0-4925-a3b5-92d75f1f5895][Write your own convenience commands]]).
+
+* Fontification in Dired
+:PROPERTIES:
+:CUSTOM_ID: h:337f9cf0-9f66-45af-b73f-f6370472fb51
+:END:
+
+#+findex: denote-dired-mode
+One of the upsides of Denote's file-naming scheme is the predictable
+pattern it establishes, which appears as a near-tabular presentation in
+a listing of notes (i.e. in Dired). The ~denote-dired-mode~ can help
+enhance this impression, by fontifying the components of the file name
+to make the date (identifier) and keywords stand out.
+
+There are two ways to set the mode. Either use it for all directories,
+which probably is not needed:
+
+#+begin_src emacs-lisp
+(add-hook 'dired-mode-hook #'denote-dired-mode)
+#+end_src
+
+#+vindex: denote-dired-directories
+#+findex: denote-dired-mode-in-directories
+Or configure the user option ~denote-dired-directories~ and then set up
+the function ~denote-dired-mode-in-directories~:
+
+#+begin_src emacs-lisp
+;; We use different ways to specify a path for demo purposes.
+(setq denote-dired-directories
+ (list denote-directory
+ (thread-last denote-directory (expand-file-name "attachments"))
+ (expand-file-name "~/Documents/vlog")))
+
+(add-hook 'dired-mode-hook #'denote-dired-mode-in-directories)
+#+end_src
+
+#+vindex: denote-dired-directories-include-subdirectories
+The user option ~denote-dired-directories-include-subdirectories~
+specifies whether the ~denote-dired-directories~ also cover their
+subdirectories. By default they do not. Set this option to ~t~ to
+include subdirectories as well.
+
+The faces we define for this purpose are:
+
+#+vindex: denote-faces-date
+#+vindex: denote-faces-delimiter
+#+vindex: denote-faces-extension
+#+vindex: denote-faces-keywords
+#+vindex: denote-faces-signature
+#+vindex: denote-faces-subdirectory
+#+vindex: denote-faces-time
+#+vindex: denote-faces-title
++ ~denote-faces-date~
++ ~denote-faces-delimiter~
++ ~denote-faces-extension~
++ ~denote-faces-keywords~
+- ~denote-faces-signature~
++ ~denote-faces-subdirectory~
++ ~denote-faces-time~
++ ~denote-faces-title~
+
+For more control, we also provide these:
+
+#+vindex denote-faces-year
+#+vindex denote-faces-month
+#+vindex denote-faces-day
+#+vindex denote-faces-hour
+#+vindex denote-faces-minute
+#+vindex denote-faces-second
++ ~denote-faces-year~
++ ~denote-faces-month~
++ ~denote-faces-day~
++ ~denote-faces-hour~
++ ~denote-faces-minute~
++ ~denote-faces-second~
+
+For the time being, the =diredfl= package is not compatible with this
+facility.
+
+The ~denote-dired-mode~ does not only fontify note files that were
+created by Denote: it covers every file name that follows our naming
+conventions ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). This is particularly useful for
+scenaria where, say, one wants to organise their collection of PDFs and
+multimedia in a systematic way (and, perhaps, use them as attachments
+for the notes Denote produces if you are writing Org notes and are using
+its standand attachments' facility).
+
+* Automatically rename Denote buffers
+:PROPERTIES:
+:CUSTOM_ID: h:3ca4db16-8f26-4d7d-b748-bac48ae32d69
+:END:
+
+#+findex: denote-rename-buffer-mode
+The minor mode ~denote-rename-buffer-mode~ provides the means to
+automatically rename the buffer of a Denote file upon visiting the
+file. This applies both to existing Denote files as well as new ones
+([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). Enable the mode thus:
+
+#+begin_src emacs-lisp
+(denote-rename-buffer-mode 1)
+#+end_src
+
+#+vindex: denote-rename-buffer-function
+#+findex: denote-rename-buffer
+#+vindex: denote-rename-buffer-format
+Buffers are named by applying the function specified in the user
+option ~denote-rename-buffer-function~. The default function is
+~denote-rename-buffer~: it renames the buffer based on the template
+set in the user option ~denote-rename-buffer-format~. By default, the
+formatting template targets only the =TITLE= component of the file
+name ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). Other fields are explained elsewhere in
+this manual ([[#h:35507c18-35b1-41b9-9d80-52f54fcef3cb][The denote-rename-buffer-format]]).
+
+Note that renaming a buffer is not the same as renaming a file
+([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]). The former is just for convenience inside of Emacs.
+Whereas the latter is for writing changes to disk, making them
+available to all programs.
+
+** The ~denote-rename-buffer-format~ option
+:PROPERTIES:
+:CUSTOM_ID: h:35507c18-35b1-41b9-9d80-52f54fcef3cb
+:END:
+
+The user option ~denote-rename-buffer-format~ controls how the
+function ~denote-rename-buffer~ chooses the name of the
+buffer-to-be-renamed.
+
+The value of this user option is a string. The following specifiers
+are placeholders for Denote file name components ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]):
+
+#+vindex: denote-rename-buffer-backlinks-indicator
+- The =%t= is the Denote =TITLE= in the front matter or the file name.
+- The =%T= is the Denote =TITLE= in the file name.
+- The =%i= is the Denote =IDENTIFIER= of the file.
+- The =%I= is the identifier converted to =DAYNAME, DAYNUM MONTHNUM
+ YEAR=.
+- The =%d= is the same as =%i= (=DATE= mnemonic).
+- The =%D= is a "do what I mean" which behaves the same as =%t= and if
+ that returns nothing, it falls back to =%I=, then =%i=.
+- The =%s= is the Denote =SIGNATURE= of the file.
+- The =%k= is the Denote =KEYWORDS= of the file.
+- The =%b= is an indicator of whether or not the file has backlinks
+ pointing to it. The indicator string is defined in the user option
+ ~denote-rename-buffer-backlinks-indicator~, alias
+ ~denote-buffer-has-backlinks-string~.
+- The =%%= is a literal percent sign.
+
+In addition, the following flags are available for each of the specifiers:
+
+- =0= :: Pad to the width, if given, with zeros instead of spaces.
+- =-= :: Pad to the width, if given, on the right instead of the left.
+- =<= :: Truncate to the width and precision, if given, on the left.
+- =>= :: Truncate to the width and precision, if given, on the right.
+- =^= :: Convert to upper case.
+- =_= :: Convert to lower case.
+
+When combined all together, the above are written thus:
+
+: %<flags><width><precision>SPECIFIER-CHARACTER
+
+Any other string it taken as-is. Users may want, for example, to
+include some text that makes Denote buffers stand out, such as
+a =[D]= prefix. Examples:
+
+#+begin_src emacs-lisp
+;; The following is the default value. Use a literal [D] prefix,
+;; followed by the title and then the backlinks indicator. If there
+;; is no title, use the identifier in its human-readable date
+;; representation, and if that is not possible, use the identifier
+;; as-is.
+(setq denote-rename-buffer-format "[D] %D%b")
+
+;; Customize what the backlink indicator looks like. This two-faced
+;; arrow is the default.
+(setq denote-rename-buffer-backlinks-indicator "<-->")
+
+;; Use just the title and keywords with some emoji in between, because
+;; why not?
+(setq denote-rename-buffer-format "%t 🤨 %k")
+
+;; Use the title with a literal "[D]" before it.
+(setq denote-rename-buffer-format "[D] %t")
+
+;; As above, but also add the `denote-rename-buffer-backlinks-indicator' at the end.
+(setq denote-rename-buffer-format "[D] %t%b")
+#+end_src
+
+Users who need yet more flexibility are best served by writing their
+own function and assigning it to the ~denote-rename-buffer-function~.
+
+* Use Org dynamic blocks
+:PROPERTIES:
+:CUSTOM_ID: h:8b542c50-dcc9-4bca-8037-a36599b22779
+:END:
+
+This section is about the external package ~denote-org~ (by
+Protesilaos). The code of ~denote-org~ used to be available as part of
+the main ~denote~ package, but we decided to keep each optional
+extension as a separate package to make things easier to maintain and
+to understand.
+
+Denote can optionally integrate with Org mode's "dynamic blocks"
+facility. This means that it can use special blocks that are evaluated
+with =C-c C-x C-u= (~org-dblock-update~) to generate their contents.
+
+Dynamic blocks are particularly useful for metanote entries that
+reflect on the status of earlier notes ([[#h:6060a7e6-f179-4d42-a9de-a9968aaebecc][Writing metanotes]]). The
+~denote-org~ package defines many of these Org dynamic blocks.
+
++ Package name (GNU ELPA): ~denote-org~
++ Official manual: <https://protesilaos.com/emacs/denote-org>
++ Git repository: <https://github.com/protesilaos/denote-org>
++ Backronym: Denote... Ordinarily Restricts Gyrations.
+
+* Display filtered and sorted files with ~denote-sort-dired~ or ~denote-dired~
+:PROPERTIES:
+:CUSTOM_ID: h:9fe01e63-f34f-4479-8713-f162a5ca865e
+:END:
+
+The =denote.el= file contains functions which empower user or
+developers to sort files by the given file name component ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+
+#+findex: denote-sort-dired
+#+findex: denote-dired
+The command ~denote-sort-dired~ (alias ~denote-dired~) produces a
+Dired file listing with a flat, filtered, and sorted set of files from
+the ~denote-directory~ ([[#h:c958e087-1d23-4a25-afdd-db7bf5606b4c][Define a sorting function per component]]). It
+does so by a series of prompts, which can be configured with the user
+option ~denote-sort-dired-extra-prompts~ ([[#h:a34228cb-484f-48fe-9cbc-8e41f313127b][Configure what extra prompts ~denote-sort-dired~ issues]]).
+
+Think of ~denote-sort-dired~ as the counterpart to the Unix ~find~
+command. While ~denote-grep~ corresponds to the Unix ~grep~ ([[#h:e71c9d14-7e88-4386-91d0-9ad249947077][Use ~denote-grep~ to search inside files]]).
+
+The out-of-the-box behaviour of ~denote-sort-dired~ is as follows:
+
+1. It first asks for a regular expression with which to match Denote
+ file names. Remember that due to Denote's efficient file-naming
+ scheme, you usually do not need to write some complex regular
+ expression. For example, something like =_journal= will match only
+ files with a =journal= keyword.
+2. Once the regular expression is provided, the command asks for a
+ Denote file name component to sort files by. This is a symbol among
+ =title=, =keywords=, =signature=, and =identifier= ([[#h:c958e087-1d23-4a25-afdd-db7bf5606b4c][Define a sorting function per component]]).
+3. Finally, it asks a "yes or no" on whether to reverse the sort order.
+
+The resulting listing is a regular Dired buffer, unlike that of
+~dired-virtual-mode~ ([[#h:d35d8d41-f51b-4139-af8f-9c8cc508e35b][Use ~dired-virtual-mode~ for arbitrary file listings]]).
+
+#+findex: denote-sort-files
+The sorting mechanism can be used by other packages to achieve their
+ends. As an example, the dynamic Org blocks that the ~denote-org~
+package (by Protesilaos) defines also use this feature internally by
+means of the non-interactive function ~denote-sort-files~.
+
+** Configure what extra prompts ~denote-sort-dired~ issues
+:PROPERTIES:
+:CUSTOM_ID: h:a34228cb-484f-48fe-9cbc-8e41f313127b
+:END:
+
+#+vindex: denote-sort-dired-extra-prompts
+By default, the ~denote-sort-dired~ command prompts for (i) a query to
+match file names, (ii) a file name component to sort by, and (iii)
+whether to reverse the sorting ([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Display filtered and sorted files with denote-sort-dired]]).
+Users can configure the latter two by modifying the user option
+~denote-sort-dired-extra-prompts~.
+
+The ~denote-sort-dired-extra-prompts~ accepts either a nil value or a
+list of symbols among ~sort-by-component~, ~reverse-sort~, and
+~exclude-regexp~. The order those symbols appear in the list is
+significant, with the leftmost coming first.
+
+These symbols correspond to the following:
+
+- A choice to select the file name component to sort by.
+- A yes or no prompt on whether to reverse the sorting.
+- A string (or regular expression) of files to be excluded from the
+ results.
+
+#+vindex: denote-sort-dired-default-sort-component
+#+vindex: denote-sort-dired-default-reverse-sort
+In case of a nil value, those extra prompts will not happen, meaning
+that ~denote-sort-dired~ will fall back to using whatever is defined
+in the variables ~denote-sort-dired-default-sort-component~ and
+~denote-sort-dired-default-reverse-sort~.
+
+Here are some examples:
+
+#+begin_src emacs-lisp
+;; The default extra prompts...
+(setq denote-sort-dired-extra-prompts '(sort-by-component reverse-sort))
+
+;; When using `denote-sort-dired', ask whether to reverse the sort and
+;; then which file name component to sort by. These are always done
+;; after the prompt to search for files matching a regexp.
+(setq denote-sort-dired-extra-prompts '(reverse-sort sort-by-component))
+
+;; Do not prompt for a reverse sort. Just use the value of
+;; `denote-sort-dired-default-reverse-sort' (which is nil out-of-the-box).
+(setq denote-sort-dired-extra-prompts '(sort-by-component))
+
+;; Do not issue any extra prompts. Always sort by the `title' file
+;; name component and never do a reverse sort.
+(setq denote-sort-dired-extra-prompts nil)
+(setq denote-sort-dired-default-sort-component 'title)
+(setq denote-sort-dired-default-reverse-sort nil)
+#+end_src
+
+** Define a sorting function per component
+:PROPERTIES:
+:CUSTOM_ID: h:c958e087-1d23-4a25-afdd-db7bf5606b4c
+:END:
+
+When sorting by =title=, =keywords=, or =signature= with the
+~denote-sort-dired~ command, Denote will internally apply a sorting
+function that is specific to each component ([[#h:a34228cb-484f-48fe-9cbc-8e41f313127b][Configure what extra prompts ~denote-sort-dired~ issues]]).
+These are subject to user configuration:
+
+#+vindex: denote-sort-identifier-comparison-function
+- ~denote-sort-identifier-comparison-function~
+
+#+vindex: denote-sort-title-comparison-function
+- ~denote-sort-title-comparison-function~
+
+#+vindex: denote-sort-keywords-comparison-function
+- ~denote-sort-keywords-comparison-function~
+
+#+vindex: denote-sort-signature-comparison-function
+- ~denote-sort-signature-comparison-function~
+
+By default, all these user options use the same sorting function,
+namely ~string-collate-lessp~. Users who have specific needs for any
+of those file name components can write their own sorting algorithms
+([[#h:95345870-4ccd-484f-9adf-de4747ad5760][Sort signatures that include Luhmann-style sequences]]).
+
+*** Sort signatures that include Luhmann-style sequences
+:PROPERTIES:
+:CUSTOM_ID: h:95345870-4ccd-484f-9adf-de4747ad5760
+:END:
+
+[ The ~denote-sequence~ package (by Protesilaos) covers this use-case
+ and many others ([[#h:d5ca722d-e7fa-46fa-9a57-6363b1d4186f][Write sequence notes or folgezettel]]). It is the
+ superior option for anyone interested in this functionality. We keep
+ the code below for reference, as there may be users of it who need
+ to revisit it. Though long-term, it is better to use ~denote-sequence~. ]
+
+Niklas Luhmann would edit notes to form sequences of thoughts with
+branching paths, such as =1.1=, =1.1a=, =1.2=, =1.2a=, =1.2b=, etc.
+With the Denote file-naming scheme, we make the word separator in each
+file name component use the same character as the entire field, so
+words in a title have a dash between them and signatures have the
+equals sign ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). Thus, our Luhmann-style
+signature will be slightly different in their looks: ~1=1~, ~1=1a~,
+~1=2~, ~1=2a~, ~1=2b~.
+
+When using the ~denote-sort-dired~ command with default settings, our
+signatures will not sort in an intuitive way. This is because they
+combine numbers and letters, which require a different approach than
+what the default sorting function is using ([[#h:c958e087-1d23-4a25-afdd-db7bf5606b4c][Define a sorting function per component]]).
+In the following code block, we show a sorting algorithm that should
+do the right thing while dealing with Luhmann-style signatures.
+
+#+begin_src emacs-lisp
+(defun my-denote--split-luhman-sig (signature)
+ "Split numbers and letters in Luhmann-style SIGNATURE string."
+ (replace-regexp-in-string
+ "\\([a-zA-Z]+?\\)\\([0-9]\\)" "\\1=\\2"
+ (replace-regexp-in-string
+ "\\([0-9]+?\\)\\([a-zA-Z]\\)" "\\1=\\2"
+ signature)))
+
+(defun my-denote--pad-sig (signature)
+ "Create a new signature with padded spaces for all components"
+ (combine-and-quote-strings
+ (mapcar
+ (lambda (x)
+ (string-pad x 5 32 t))
+ (split-string (my-denote--split-luhman-sig signature) "=" t))
+ "="))
+
+(defun my-denote-sort-for-signatures (sig1 sig2)
+ "Return non-nil if SIG1 is smaller that SIG2.
+Perform the comparison with `string<'."
+ (string< (my-denote--pad-sig sig1) (my-denote--pad-sig sig2)))
+
+;; Change the sorting function only when we sort by signature.
+(setq denote-sort-signature-comparison-function #'my-denote-sort-for-signatures)
+#+end_src
+
+* Use ~denote-grep~ to search inside files
+:PROPERTIES:
+:CUSTOM_ID: h:e71c9d14-7e88-4386-91d0-9ad249947077
+:END:
+
+#+findex: denote-grep
+The command ~denote-grep~ searches for the given query across all
+readable files in the ~denote-directory~. It puts the collected
+results in an Xref buffer (just like with our backlinks and query
+links functionality). In this buffer, users can do =M-x describe-mode=
+(=C-h m= with default key bindings) to learn about all the actions
+they can perform and the keys they are bound to ([[#h:435592bc-e896-429f-a599-9f1bcd5ab9b8][Interact with the links buffer]]).
+
+Think of ~denote-grep~ as the counterpart to the Unix ~grep~ command.
+While ~denote-sort-dired~ corresponds to the Unix ~find~ ([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Display filtered and sorted files with ~denote-sort-dired~]]).
+
+#+findex: denote-grep-marked-dired-files
+The command ~denote-grep-marked-dired-files~ is like ~denote-grep~ but
+operates on the files that are marked in a Dired buffer.
+
+#+findex: denote-grep-files-referenced-in-region
+The command ~denote-grep-files-referenced-in-region~ is like
+~denote-grep~ for any files referenced within the boundaries of the
+marked region. Files are referenced by their identifier. This includes
+links with just the identifier (as described in ~denote-link~ and
+related ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Add a single direct link using a file name prompt]])), links
+written by an Org dynamic block (see the ~denote-org~ package ([[#h:8b542c50-dcc9-4bca-8037-a36599b22779][Use Org dynamic blocks]])),
+or even file listings such as those of ~dired~ and the command-line
+~ls~ program.
+
+#+vindex: denote-grep-display-buffer-action
+The user option ~denote-grep-display-buffer-action~ controls where the
+buffer with the search results is displayed at. By default, they appear in
+the same window where the command ~denote-grep~ is called from.
+
+* Interact with the links buffer
+:PROPERTIES:
+:CUSTOM_ID: h:435592bc-e896-429f-a599-9f1bcd5ab9b8
+:END:
+
+Denote commands, such as ~denote-grep~, ~denote-backlinks~, and
+~denote-query-contents-link~, produce an Xref buffer with search
+results ([[#h:893eec49-d7be-4603-bcff-fcc247244011][Speed up backlinks' or query links' buffer creation?]]).
+Matching lines are grouped by the file name they belong to.
+
+- [[#h:e71c9d14-7e88-4386-91d0-9ad249947077][Use ~denote-grep~ to search inside files]].
+- [[#h:c73f1f68-e214-49d5-b369-e694f6a5d708][The backlinks' buffer]].
+- [[#h:d9a84289-2f73-4ef9-b4f0-9a0aa3e9bf0d][Add a query link]].
+
+#+findex: denote-query-mode
+#+vindex: denote-query-mode-map
+This buffer uses the major mode ~denote-query-mode~. It binds commands
+to keys in the ~denote-query-mode-map~. Those allow users to filter
+the output of the last search. Here, "last search" refers to the list
+of files that were returned by whichever command produced the buffer
+(e.g. the last ~denote-grep~).
+
+#+findex: denote-query-focus-last-search
+- ~denote-query-focus-last-search~ :: Perform a search in the contents
+ of files that were matched by the last search.
+
+#+findex: denote-query-exclude-files
+- ~denote-query-exclude-files~ :: Exclude files from the last search
+ whose name matches the given input.
+
+#+findex: denote-query-only-include-files
+- ~denote-query-only-include-files~ :: Only keep files from the last
+ search whose name matches the given input.
+
+#+findex: denote-query-exclude-files-with-keywords
+- ~denote-query-exclude-files-with-keywords~ :: Exclude files from the
+ last search whose name includes the given keywords.
+
+#+findex: denote-query-only-include-files-with-keywords
+- ~denote-query-only-include-files-with-keywords~ :: Only keep files
+ from the last search whose name includes the given keywords.
+
+#+findex: denote-query-clear-all-filters
+- ~denote-query-clear-all-filters~ :: Clear all the applied filters.
+
+Remember that these are easy to use even without knowledge of regular
+expressions, thanks to the efficiency of the Denote file-naming scheme
+([[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]). For
+instance, to exclude notes with the keyword =philosophy= from current
+search buffer, use ~denote-query-exclude-files~ and then type
+=_philosophy= as your input.
+
+In addition to those filtering options, the ~denote-query-mode~ also
+allows provides an outline mechanism to hide or show the matches as
+these are grouped per file. There also are some of the default actions
+provided by the Xref infrastructure. Users can do =M-x describe-mode=
+(=C-h m= with default key bindings) to learn about all the actions
+they can perform.
+
+* Minibuffer histories
+:PROPERTIES:
+:CUSTOM_ID: h:82dc1203-d689-44b2-9a6c-b37776209651
+:END:
+
+Denote has a dedicated minibuffer history for each one of its prompts.
+This practically means that using =M-p= (~previous-history-element~) and
+=M-n= (~next-history-element~) will only cycle through the relevant
+record of inputs, such as your latest titles in the =TITLE= prompt, and
+keywords in the =KEYWORDS= prompt.
+
+The built-in =savehist= library saves minibuffer histories. Sample
+configuration:
+
+#+begin_src emacs-lisp
+(require 'savehist)
+(setq savehist-file (locate-user-emacs-file "savehist"))
+(setq history-length 500)
+(setq history-delete-duplicates t)
+(setq savehist-save-minibuffer-history t)
+(add-hook 'after-init-hook #'savehist-mode)
+#+end_src
+
+* Packages that build on Denote
+:PROPERTIES:
+:CUSTOM_ID: h:08b14682-e73f-4b11-b2e2-be3b788c8572
+:END:
+
+This is a list of packages that extend Denote. If you are a package
+author, please let us know about your work and we will include it
+here (either use the Git repositories or email Protesilaos directly).
+
+** Use the ~consult-denote~ package for enhanced minibuffer interactions
+:PROPERTIES:
+:CUSTOM_ID: h:113a13e1-35da-46f1-9414-81e9be2facc1
+:END:
+
+The ~consult-denote~ package by me (Protesilaos) integrates Denote
+with Daniel Mendler's ~consult~ package: <https://github.com/protesilaos/consult-denote>.
+
+The idea is to preserve the familiar patterns of interaction with the
+various Denote commands but add to them an extra layer of
+functionality, such as the preview mechanism that Consult provides
+(e.g. preview the file you are about to link to).
+
+Additionally, ~consult-denote~ defines new "sources" for the
+~consult-buffer~ command. This command provides a single point of
+entry for buffers, recently opened files, and bookmarks. With
+~consult-denote~, it has a dedicated place for Denote-specific
+buffers, silos, and more (all of which are configurable).
+
+Unlike the ~consult-notes~ package by Colin McLear, ~consult-denote~
+uses the same presentation of data in the minibuffer to stay in sync
+with Denote and make its feature set entirely optional ([[#h:8907f4bc-992a-45bc-a60e-267ed1ce9c2d][Use the ~consult-notes~ package]]).
+It also only works with Denote.
+
+** Use the ~denote-sequence~ package to write sequence notes or "folgezettel"
+:PROPERTIES:
+:CUSTOM_ID: h:d5ca722d-e7fa-46fa-9a57-6363b1d4186f
+:ALT_TITLE: Sequence notes
+:END:
+
+This section is about the external package ~denote-sequence~ (by
+Protesilaos). The original idea was to include the code as part of the
+~denote~ package, but we decided to keep each optional extension as a
+separate package to make things easier to maintain and to understand.
+
+Denote defines an optional file name component called the =SIGNATURE=
+([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). This is a free-form field that users can
+fill in with whatever text they want, such as to have a video split up
+into =part1= and =part2=, or to set some kind of priority like =a= and
+=b=, or even to have a special tag that stands out from the rest of
+the keywords.
+
+A more specialised use-case of the =SIGNATURE= is to define a
+hierarchical relationship between notes, such that the thoughts they
+expound on form sequences. For example, an article about the Labrador
+Retriever dog breed is a continuation of a thought process that
+extends something about dog breeds in general which, in turn, is a
+topic that belongs to the wider theme of dogs.
+
+The ~denote-sequence~ package has a manual that explains these
+concepts and relevant commands in further detail:
+
++ Package name (GNU ELPA): ~denote-sequence~
++ Official manual: <https://protesilaos.com/emacs/denote-sequence>
++ Git repository: <https://github.com/protesilaos/denote-sequence>
++ Backronym: Denote... Sequences Efficiently Queue Unsorted Entries
+ Notwithstanding Curation Efforts.
+
+** Use the ~denote-markdown~ package to better integrate Markdown with Denote
+:PROPERTIES:
+:CUSTOM_ID: h:79b614e7-d7a3-4953-b776-216e6e1ede77
+:END:
+
+The ~denote-markdown~ package (by Protesilaos) provides some
+convenience functions to better integrate Markdown with Deonte. This
+is mostly about converting links from one type to another so that they
+can work in different applications (because Markdown does not have a
+standardised way to define custom link types).
+
+The code of ~denote-markdown~ used to be bundled up with the ~denote~
+package before version =4.0.0= of the latter and was available in the
+file =denote-md-extras.el=. Users of the old code will need to adapt
+their setup to use the ~denote-markdown~ package. This can be done by
+replacing all instances of =denote-md-extras= with =denote-markdown=
+across their configuration.
+
++ Package name (GNU ELPA): ~denote-markdown~
++ Official manual: <https://protesilaos.com/emacs/denote-markdown>
++ Git repository: <https://github.com/protesilaos/denote-markdown>
++ Backronyms: Denote... Markdown's Ambitious Reimplimentations
+ Knowingly Dilute Obvious Widespread Norms; Denote... Markup
+ Agnosticism Requires Knowhow to Do Only What's Necessary.
+
+** Use the ~denote-journal~ package which was formerly =denote-journal-extras.el=
+:PROPERTIES:
+:CUSTOM_ID: h:4a6d92dd-19eb-4fcc-a7b5-05ce04da3a92
+:END:
+
+The ~denote-journal~ package (by Protesilaos) makes it easier to use
+Denote for journaling. While it is possible to use the generic
+~denote~ command (and related) to maintain a journal, this package
+defines extra functionality to streamline the journaling workflow.
+
+The code of ~denote-journal~ used to be bundled up with the ~denote~
+package before version =4.0.0= of the latter and was available in the
+file =denote-journal-extras.el=. Users of the old code will need to
+adapt their setup to use the ~denote-journal~ package. This can be
+done by replacing all instances of =denote-journal-extras= with
+=denote-journal= across their configuration.
+
++ Package name (GNU ELPA): ~denote-journal~
++ Official manual: <https://protesilaos.com/emacs/denote-journal>
++ Git repository: <https://github.com/protesilaos/denote-journal>
++ Backronym: Denote... Journaling Obviously Utilises Reasonableness
+ Notwithstanding Affectionate Longing.
+
+** Use the ~denote-silo~ package which formerly was =denote-silo-extras.el=
+:PROPERTIES:
+:CUSTOM_ID: h:e43baf95-f201-4fec-8620-c0eb5eaa1c85
+:END:
+
+The ~denote-silo~ package (by Protesilaos) provides convenience
+functions for working with silos ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directory silos for notes]]).
+
+The code of ~denote-silo~ used to be bundled up with the ~denote~
+package before version =4.0.0= of the latter and was available in the
+file =denote-silo-extras.el=. Users of the old code will need to adapt
+their setup to use the ~denote-silo~ package. This can be done by
+replacing all instances of =denote-silo-extras= with =denote-silo=
+across their configuration.
+
++ Package name (GNU ELPA): ~denote-silo~
++ Official manual: <https://protesilaos.com/emacs/denote-silo>
++ Git repository: <https://github.com/protesilaos/denote-silo>
++ Backronym: Denote... Silos Insulate Localised Objects.
+
+** Use the ~denote-search~ package as a search interface
+:PROPERTIES:
+:CUSTOM_ID: h:c905b733-e959-4aa4-8f2c-0ed9eba459df
+:END:
+
+[ As part of version =4.0.0=, Denote comes with the ~denote-grep~
+ command and related functionality ([[#h:e71c9d14-7e88-4386-91d0-9ad249947077][Use ~denote-grep~ to search inside files]]).
+ The core of this feature set was written by Lucas Quintana. ]
+
+The ~denote-search~ package by Lucas Quintana provides a search
+utility for Denote: <https://github.com/lmq-10/denote-search>.
+
+It allows you to search for a regular expression in the content of
+your notes. Its main advantages over other similar tools are the
+possibility of filtering the results by file name and doing further
+searches in the files matched previously. This allows for advanced
+usage (think about finding a note with two or three specific words in
+different lines and with a specific keyword). More features are
+described in its comprehensive manual. ~denote-search~ builds upon
+standard Emacs libraries, namely Xref, and so it doesn't have external
+dependencies other than Denote itself.
+
+** Use the ~denote-explore~ package to explore your notes
+:PROPERTIES:
+:CUSTOM_ID: h:110ae3a4-5fc6-45da-b9bb-0e294bd12981
+:END:
+
+Peter Prevos has developed the ~denote-explore~ package which provides
+four groups of Emacs commands to explore your Denote files:
+
+- Summary statistics :: Count notes, attachments and keywords.
+- Random walks :: Generate new ideas using serendipity.
+- Janitor :: Manage your denote collection.
+- Visualisations :: Visualise your Denote network.
+
+The package's documentation covers the details:
+<https://lucidmanager.org/productivity/denote-explore/>.
+
+** Use the ~citar-denote~ package for bibliography notes
+:PROPERTIES:
+:CUSTOM_ID: h:226d66e4-b7de-4617-87e2-a7f2d6f007dd
+:END:
+
+Peter Prevos has produced the ~citar-denote~ package which makes it
+possible to write notes on BibTeX entries with the help of the ~citar~
+package. These notes have the citation's unique key associated with
+them in the file's front matter. They also get a configurable keyword
+in their file name, making it easy to find them in Dired and/or
+retrieve them with the various Denote methods.
+
+With ~citar-denote~, the user leverages standard minibuffer completion
+mechanisms (e.g. with the help of the ~vertico~ and ~embark~ packages)
+to manage bibliographic notes and access those notes with ease. The
+package's documentation covers the details: <https://lucidmanager.org/productivity/bibliographic-notes-in-emacs-with-citar-denote/>.
+
+** Use the ~consult-notes~ package
+:PROPERTIES:
+:CUSTOM_ID: h:8907f4bc-992a-45bc-a60e-267ed1ce9c2d
+:END:
+
+[ Also check the ~consult-denote~ package by me (Protesilaos):
+ [[#h:113a13e1-35da-46f1-9414-81e9be2facc1][Use the ~consult-denote~ package for enhanced minibuffer interactions]]. ]
+
+If you are using Daniel Mendler's ~consult~ (which is a brilliant
+package), you will most probably like its ~consult-notes~ extension,
+developed by Colin McLear. It uses the familiar mechanisms of Consult
+to preview the currently selected entry and to filter searches via a
+prefix key. For example:
+
+#+begin_src emacs-lisp
+(setq consult-notes-file-dir-sources
+ `(("Denote Notes" ?d ,(denote-directory))
+ ("Books" ?b "~/Documents/books/")))
+#+end_src
+
+With the above, =M-x consult-notes= will list the files in those two
+directories. If you type =d= and space, it narrows the list to just
+the notes, while =b= does the same for books.
+
+The other approach is to enable the ~consult-notes-denote-mode~. It
+takes care to add the ~denote-directory~ to the sources that
+~consult-notes~ reads from. Denote notes are then filtered by the =d=
+prefix followed by a space.
+
+The minor mode has the extra feature of reformatting the title of
+notes shown in the minibuffer. It isolates the =TITLE= component of
+each note and shows it without hyphens, while presenting keywords in
+their own column. The user option ~consult-notes-denote-display-id~
+can be set to ~nil~ to hide the identifier. Depending on how one
+searches through their notes, this refashioned presentation may be the
+best option ([[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]).
+
+** Use the ~denote-menu~ package
+:PROPERTIES:
+:CUSTOM_ID: h:472db709-27de-4a1f-a171-c3fe0a7a9be8
+:END:
+
+Denote's file-naming scheme is designed to be efficient and to provide
+valueable meta information about the file. The cost, however, is that
+it is terse and harder to read, depending on how the user chooses to
+filter and process their notes.
+
+To this end, [[https://github.com/namilus/denote-menu][the ~denote-menu~ package by Mohamed Suliman]] provides the
+convenience of a nice tabular interface for all notes. ~denote-menu~
+removes the delimiters that are found in Denote file names and
+presents the information in a human-readable format. Furthermore, the
+package provides commands to interact with the list of notes, such as
+to filter them and to transition from the tabular list to Dired. Its
+documentation expands on the technicalities.
+
+** Use the ~denote-zettel-interface~ package
+:PROPERTIES:
+:CUSTOM_ID: h:b57f73fc-ac4d-4df2-bd7a-8d47cb202647
+:END:
+
+The [[https://github.com/krisbalintona/denote-zettel-interface][~denote-zettel-interface~ package by Kristoffer Balintona]] is
+designed for those who want to use Denote while adhering to a strict
+Zettelkasten methodology of sequence notes (Folgezettel). This method
+leverages the optional =SIGNATURE= file name component of Denote ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+The package provides a point of entry to one's note by visualising
+them in a tabulated (grid) interface. Files are sorted by their
+Folgezettel index. Users can then use a number of commands to filter
+their files, navigate around, and the like.
+
+Note that the package is in early development as of this writing
+(2024-12-03 10:18 +0200).
+
+* Extending Denote
+:PROPERTIES:
+:CUSTOM_ID: h:8ed2bb6f-b5be-4711-82e9-8bee5bb06ece
+:END:
+
+Denote is a tool with a narrow scope: create notes and link between
+them, based on the aforementioned file-naming scheme. For other common
+operations the user is advised to rely on standard Emacs facilities or
+specialised third-party packages ([[#h:08b14682-e73f-4b11-b2e2-be3b788c8572][Packages that build on Denote]]). This
+section covers the details.
+
+** Access the data of the latest note
+:PROPERTIES:
+:CUSTOM_ID: h:a947908e-1847-4471-ba07-377ee2f4b36c
+:END:
+
+#+vindex: denote-current-data
+The variable ~denote-current-data~ is updated each time a new note is
+created as well as after a rename operation.
+
+This is an alist where each ~car~ is one among ~title~, ~keywords~,
+~signature~, ~directory~, ~date~, ~id~, ~file-type~, ~template~. The
+value each of them contains is the unprocessed input (e.g. the title
+before it is sluggified).
+
+Users who need to access this data as part of their custom code can
+rely on the hooks ~denote-after-new-note-hook~ and
+~denote-after-rename-file-hook~.
+
+** Create a new note in any directory
+:PROPERTIES:
+:CUSTOM_ID: h:c9d7c157-85a6-4bb7-bd21-d00bccf5e799
+:END:
+
+The commands that create new files are designed to write to the
+~denote-directory~. The idea is that the linking mechanism can find
+any file by its identifier if it is in the ~denote-directory~
+(searching the entire file system would be cumbersome).
+
+However, these are cases where the user needs to create a new note in
+an arbitrary directory. The following command can do this. Put the
+code in your configuration file and evaluate it. Then call the command
+by its name with =M-x=.
+
+#+begin_src emacs-lisp
+(defun my-denote-create-note-in-any-directory ()
+ "Create new Denote note in any directory.
+Prompt for the directory using minibuffer completion."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-directory (read-directory-name "New note in: " nil nil :must-match)))
+ (call-interactively 'denote)))
+#+end_src
+
+** Find empty notes and put them in a Dired buffer
+:PROPERTIES:
+:CUSTOM_ID: h:bbc7a769-19e8-4598-a2b7-06e1d673ae80
+:END:
+
+[ This feature is based on the command ~denote-sort-dired~ ([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Sort files by component]]). ]
+
+Users may have a workflow where they use the commands
+~denote-link-or-create~ or ~denote-link-after-creating~ (and related)
+to produce new notes that they plan to elaborate on later ([[#h:b6056e6b-93df-4e6b-a778-eebd105bac46][Link to an existing note or create a new one]]).
+
+To help users find those empty notes, we document the following
+commands:
+
+- ~my-denote-sort-dired-empty-files~
+- ~my-denote-sort-dired-without-empty-files~
+- ~my-denote-sort-dired-all-empty-files~
+- ~my-denote-sort-dired-without-all-empty-files~
+
+#+begin_src emacs-lisp
+(require 'denote-sort)
+
+(defun my-denote--note-has-no-contents-p (file)
+ "Return non-nil if FILE is an empty note.
+This means that FILE conforms with `denote-file-is-note-p' and either
+has no contents or has only the front matter."
+ (and (denote-file-is-note-p file)
+ (or (denote--file-with-temp-buffer file
+ (re-search-forward "^$" nil t)
+ (if (re-search-forward "[^\s\t\n\r]+" nil t)
+ nil
+ t))
+ ;; This must come later because here we consider a file
+ ;; "empty" even if it only has front matter.
+ (denote--file-empty-p file))))
+
+(defun my-denote-sort-dired-empty-files (files-matching-regexp sort-by-component reverse)
+ "Like `denote-sort-dired' but only cover empty files.
+Empty files are those that satisfy `my-denote--note-has-no-contents-p'."
+ (interactive
+ (append (list (denote-files-matching-regexp-prompt)) (denote-sort-dired--prompts)))
+ (let ((component (or sort-by-component
+ denote-sort-dired-default-sort-component
+ 'identifier))
+ (reverse-sort (or reverse
+ denote-sort-dired-default-reverse-sort
+ nil)))
+ (if-let* ((default-directory (denote-directory))
+ (files (denote-sort-get-directory-files files-matching-regexp component reverse-sort))
+ (empty-files (seq-filter #'my-denote--note-has-no-contents-p files))
+ ;; NOTE 2023-12-04: Passing the FILES-MATCHING-REGEXP as
+ ;; buffer-name produces an error if the regexp contains a
+ ;; wildcard for a directory. I can reproduce this in emacs
+ ;; -Q and am not sure if it is a bug. Anyway, I will report
+ ;; it upstream, but even if it is fixed we cannot use it
+ ;; for now (whatever fix will be available for Emacs 30+).
+ (denote-sort-dired-buffer-name (format "Denote sort `%s' by `%s'" files-matching-regexp component))
+ (buffer-name (format "Denote sort by `%s' at %s" component (format-time-string "%T"))))
+ (let ((dired-buffer (dired (cons buffer-name (mapcar #'file-relative-name empty-files)))))
+ (setq denote-sort--dired-buffer dired-buffer)
+ (with-current-buffer dired-buffer
+ (setq-local revert-buffer-function
+ (lambda (&rest _)
+ (kill-buffer dired-buffer)
+ (denote-sort-dired files-matching-regexp component reverse-sort))))
+ ;; Because of the above NOTE, I am printing a message. Not
+ ;; what I want, but it is better than nothing...
+ (message denote-sort-dired-buffer-name))
+ (message "No matching files for: %s" files-matching-regexp))))
+
+(defun my-denote-sort-dired-without-empty-files (files-matching-regexp sort-by-component reverse)
+ "Like `denote-sort-dired' but only cover empty files.
+Empty files are those that satisfy `my-denote--note-has-no-contents-p'."
+ (interactive
+ (append (list (denote-files-matching-regexp-prompt)) (denote-sort-dired--prompts)))
+ (let ((component (or sort-by-component
+ denote-sort-dired-default-sort-component
+ 'identifier))
+ (reverse-sort (or reverse
+ denote-sort-dired-default-reverse-sort
+ nil)))
+ (if-let* ((default-directory (denote-directory))
+ (files (denote-sort-get-directory-files files-matching-regexp component reverse-sort))
+ (empty-files (seq-remove #'my-denote--note-has-no-contents-p files))
+ ;; NOTE 2023-12-04: Passing the FILES-MATCHING-REGEXP as
+ ;; buffer-name produces an error if the regexp contains a
+ ;; wildcard for a directory. I can reproduce this in emacs
+ ;; -Q and am not sure if it is a bug. Anyway, I will report
+ ;; it upstream, but even if it is fixed we cannot use it
+ ;; for now (whatever fix will be available for Emacs 30+).
+ (denote-sort-dired-buffer-name (format "Denote sort `%s' by `%s'" files-matching-regexp component))
+ (buffer-name (format "Denote sort by `%s' at %s" component (format-time-string "%T"))))
+ (let ((dired-buffer (dired (cons buffer-name (mapcar #'file-relative-name empty-files)))))
+ (setq denote-sort--dired-buffer dired-buffer)
+ (with-current-buffer dired-buffer
+ (setq-local revert-buffer-function
+ (lambda (&rest _)
+ (kill-buffer dired-buffer)
+ (denote-sort-dired files-matching-regexp component reverse-sort))))
+ ;; Because of the above NOTE, I am printing a message. Not
+ ;; what I want, but it is better than nothing...
+ (message denote-sort-dired-buffer-name))
+ (message "No matching files for: %s" files-matching-regexp))))
+
+(defun my-denote-sort-dired-all-empty-files ()
+ "List all empty files in a Dired buffer.
+This is the same as calling `my-denote-sort-dired' with a
+FILES-MATCHING-REGEXP of \".*\"."
+ (declare (interactive-only t))
+ (interactive)
+ (let* ((other-prompts (denote-sort-dired--prompts))
+ (sort-key (nth 1 other-prompts))
+ (reverse (nth 2 other-prompts)))
+ (funcall-interactively #'my-denote-sort-dired-empty-files ".*" sort-key reverse)))
+
+(defun my-denote-sort-dired-without-all-empty-files ()
+ "List all empty files in a Dired buffer.
+This is the same as calling `my-denote-sort-dired' with a
+FILES-MATCHING-REGEXP of \".*\"."
+ (declare (interactive-only t))
+ (interactive)
+ (let* ((other-prompts (denote-sort-dired--prompts))
+ (sort-key (nth 1 other-prompts))
+ (reverse (nth 2 other-prompts)))
+ (funcall-interactively #'my-denote-sort-dired-without-empty-files ".*" sort-key reverse)))
+#+end_src
+
+[ In the above snippet, I am purposefully duplicating code to make it
+ easier for users to pick the ones they need. ]
+
+** Automatically rename the note after saving it
+:PROPERTIES:
+:CUSTOM_ID: h:c7d4dd3a-38bb-4f1c-a36e-989ec0bc79a6
+:END:
+
+While experimenting with Denote, users may need to try different
+workflows to figure out what works for them. Those might involve
+changing keywords and specifying titles in a particular way. The
+following sample can be used:
+
+#+begin_src emacs-lisp
+(defun my-denote-always-rename-on-save-based-on-front-matter ()
+ "Rename the current Denote file, if needed, upon saving the file.
+Rename the file based on its front matter, checking for changes in the
+title or keywords fields.
+
+Add this function to the `after-save-hook'."
+ (let ((denote-rename-confirmations nil)
+ (denote-save-buffers t)) ; to save again post-rename
+ (when (and buffer-file-name (denote-file-is-note-p buffer-file-name))
+ (ignore-errors (denote-rename-file-using-front-matter buffer-file-name))
+ (message "Buffer saved; Denote file renamed"))))
+
+(add-hook 'after-save-hook #'my-denote-always-rename-on-save-based-on-front-matter)
+#+end_src
+
+** Narrow the list of files in Dired
+:PROPERTIES:
+:CUSTOM_ID: h:ea173a01-69ef-4574-89a7-6e60ede02f13
+:END:
+
+Emacs' standard file manager (or directory editor) can read a regular
+expression to mark the matching files. This is the command
+~dired-mark-files-regexp~, which is bound to =% m= by default. For
+example, =% m _denote= will match all files that have the =denote=
+keyword ([[#h:1a953736-86c2-420b-b566-fb22c97df197][Features of the file-naming scheme for searching or filtering]]).
+
+Once the files are matched, the user has two options: (i) narrow the
+list to the matching items or (ii) exclude the matching items from the
+list.
+
+For the former, we want to toggle the marks by typing =t= (calls the
+command ~dired-toggle-marks~ by default) and then hit the letter =k=
+(for ~dired-do-kill-lines~). The remaining files are those that match
+the regexp that was provided earlier.
+
+For the latter approach of filtering out the matching items, simply
+involves the use of the =k= command (~dired-do-kill-lines~) to omit the
+marked files from the list.
+
+These sequences can be combined to incrementally narrow the list. Note
+that ~dired-do-kill-lines~ does not delete files: it simply hides them
+from the current view.
+
+Revert to the original listing with =g= (~revert-buffer~).
+
+For a convenient wrapper, consider this example:
+
+#+begin_src emacs-lisp
+(defvar prot-dired--limit-hist '()
+ "Minibuffer history for `prot-dired-limit-regexp'.")
+
+;;;###autoload
+(defun prot-dired-limit-regexp (regexp omit)
+ "Limit Dired to keep files matching REGEXP.
+
+With optional OMIT argument as a prefix (\\[universal-argument]),
+exclude files matching REGEXP.
+
+Restore the buffer with \\<dired-mode-map>`\\[revert-buffer]'."
+ (interactive
+ (list
+ (read-regexp
+ (concat "Files "
+ (when current-prefix-arg
+ (propertize "NOT " 'face 'warning))
+ "matching PATTERN: ")
+ nil 'prot-dired--limit-hist)
+ current-prefix-arg))
+ (dired-mark-files-regexp regexp)
+ (unless omit (dired-toggle-marks))
+ (dired-do-kill-lines))
+#+end_src
+
+** Use ~dired-virtual-mode~ for arbitrary file listings
+:PROPERTIES:
+:CUSTOM_ID: h:d35d8d41-f51b-4139-af8f-9c8cc508e35b
+:END:
+
+Emacs' Dired is a powerful file manager that builds its functionality
+on top of the Unix =ls= command. As noted elsewhere in this manual,
+the user can update the =ls= flags that Dired uses to display its
+contents ([[#h:a7fd5e0a-78f7-434e-aa2e-e150479c16e2][I want to sort by last modified, why won't Denote let me?]]).
+
+What Dired cannot do is parse the output of a result that is produced
+by piped commands, such as =ls -l | sort -t _ -k2=. This specific
+example targets the second underscore-separated field of the file
+name, per our conventions ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). Conceretely, it
+matches the "alpha" as the sorting key in something like this:
+
+#+begin_src emacs-lisp
+20220929T200432--testing-file-one__alpha.txt
+#+end_src
+
+Consider then, how Dired will sort those files by their identifier:
+
+#+begin_src emacs-lisp
+20220929T200432--testing-file-one__alpha.txt
+20220929T200532--testing-file-two__beta.txt
+20220929T200632--testing-file-three__alpha.txt
+20220929T200732--testing-file-four__beta.txt
+#+end_src
+
+Whereas on the command line, we can get the following:
+
+#+begin_example
+$ ls | sort -t _ -k 2
+20220929T200432--testing-file-one__alpha.txt
+20220929T200632--testing-file-three__alpha.txt
+20220929T200532--testing-file-two__beta.txt
+20220929T200732--testing-file-four__beta.txt
+#+end_example
+
+This is where ~dired-virtual-mode~ shows its utility. If we tweak our
+command-line invocation to include =ls -l=, this mode can behave like
+Dired on the listed files. (We omit the output of the =-l= flag from
+this tutorial, as it is too verbose.)
+
+What we now need is to capture the output of =ls -l | sort -t _ -k 2=
+in an Emacs buffer and then enable ~dired-virtual-mode~. To do that,
+we can rely on either =M-x shell= or =M-x eshell= and then manually
+copy the relevant contents.
+
+For the user's convenience, I share what I have for Eshell to quickly
+capture the last command's output in a dedicated buffer:
+
+#+begin_src emacs-lisp
+(defcustom prot-eshell-output-buffer "*Exported Eshell output*"
+ "Name of buffer with the last output of Eshell command.
+Used by `prot-eshell-export'."
+ :type 'string
+ :group 'prot-eshell)
+
+(defcustom prot-eshell-output-delimiter "* * *"
+ "Delimiter for successive `prot-eshell-export' outputs.
+This is formatted internally to have newline characters before
+and after it."
+ :type 'string
+ :group 'prot-eshell)
+
+(defun prot-eshell--command-prompt-output ()
+ "Capture last command prompt and its output."
+ (let ((beg (save-excursion
+ (goto-char (eshell-beginning-of-input))
+ (goto-char (point-at-bol)))))
+ (when (derived-mode-p 'eshell-mode)
+ (buffer-substring-no-properties beg (eshell-end-of-output)))))
+
+;;;###autoload
+(defun prot-eshell-export ()
+ "Produce a buffer with output of the last Eshell command.
+If `prot-eshell-output-buffer' does not exist, create it. Else
+append to it, while separating multiple outputs with
+`prot-eshell-output-delimiter'."
+ (interactive)
+ (let ((eshell-output (prot-eshell--command-prompt-output)))
+ (with-current-buffer (get-buffer-create prot-eshell-output-buffer)
+ (let ((inhibit-read-only t))
+ (goto-char (point-max))
+ (unless (eq (point-min) (point-max))
+ (insert (format "\n%s\n\n" prot-eshell-output-delimiter)))
+ (goto-char (point-at-bol))
+ (insert eshell-output)
+ (switch-to-buffer-other-window (current-buffer))))))
+#+end_src
+
+Bind ~prot-eshell-export~ to a key in the ~eshell-mode-map~ and give
+it a try (I use =C-c C-e=). In the produced buffer, activate the
+~dired-virtual-mode~.
+
+** Use Embark to collect minibuffer candidates
+:PROPERTIES:
+:CUSTOM_ID: h:edf9b651-86eb-4d5f-bade-3c9e270082f0
+:END:
+
+=embark= is a remarkable package that lets you perform relevant,
+context-dependent actions using a prefix key (simplifying in the
+interest of brevity).
+
+For our purposes, Embark can be used to produce a Dired listing
+directly from the minibuffer. Suppose the current note has links to
+three other notes. You might use the ~denote-find-link~ command to
+pick one via the minibuffer. But why not turn those three links into
+their own Dired listing? While in the minibuffer, invoke ~embark-act~
+which you may have already bound to =C-.= and then follow it up with
+=E= (for the ~embark-export~ command).
+
+This pattern can be repeated with any list of candidates, meaning that
+you can narrow the list by providing some input before eventually
+exporting the results with Embark.
+
+Overall, this is very powerful and you might prefer it over doing the
+same thing directly in Dired, since you also benefit from all the power
+of the minibuffer ([[#h:ea173a01-69ef-4574-89a7-6e60ede02f13][Narrow the list of files in Dired]]).
+
+** Search file contents
+:PROPERTIES:
+:CUSTOM_ID: h:76198fab-d6d2-4c67-9ccb-7a08cc883952
+:END:
+
+[ Users of ~consult~ can use the ~consult-denote~ package instead
+ ([[#h:113a13e1-35da-46f1-9414-81e9be2facc1][Use the ~consult-denote~ package for enhanced minibuffer interactions]]). ]
+
+Emacs provides built-in commands which are wrappers of standard Unix
+tools: =M-x grep= lets the user input the flags of a ~grep~ call and
+pass a regular expression to the =-e= flag.
+
+The author of Denote uses this thin wrapper instead:
+
+#+begin_src emacs-lisp
+(defvar prot-search--grep-hist '()
+ "Input history of grep searches.")
+
+;;;###autoload
+(defun prot-search-grep (regexp &optional recursive)
+ "Run grep for REGEXP.
+
+Search in the current directory using `lgrep'. With optional
+prefix argument (\\[universal-argument]) for RECURSIVE, run a
+search starting from the current directory with `rgrep'."
+ (interactive
+ (list
+ (read-from-minibuffer (concat (if current-prefix-arg
+ (propertize "Recursive" 'face 'warning)
+ "Local")
+ " grep for PATTERN: ")
+ nil nil nil 'prot-search--grep-hist)
+ current-prefix-arg))
+ (unless grep-command
+ (grep-compute-defaults))
+ (if recursive
+ (rgrep regexp "*" default-directory)
+ (lgrep regexp "*" default-directory)))
+#+end_src
+
+Rather than maintain custom code, consider using the excellent =consult=
+package: it provides commands such as ~consult-grep~ and ~consult-find~
+which provide live results and are generally easier to use than the
+built-in commands.
+
+** Bookmark the directory with the notes
+:PROPERTIES:
+:CUSTOM_ID: h:1bba4c1e-6812-4749-948f-57df4fd49b36
+:END:
+
+Part of the reason Denote does not reinvent existing functionality is to
+encourage you to learn more about Emacs. You do not need a bespoke
+"jump to my notes" directory because such commands do not scale well.
+Will you have a "jump to my downloads" then another for multimedia and
+so on? No.
+
+Emacs has a built-in framework for recording persistent markers to
+locations. Visit the ~denote-directory~ (or any dir/file for that
+matter) and invoke the ~bookmark-set~ command (bound to =C-x r m= by
+default). It lets you create a bookmark.
+
+The list of bookmarks can be reviewed with the ~bookmark-bmenu-list~
+command (bound to =C-x r l= by default). A minibuffer interface is
+available with ~bookmark-jump~ (=C-x r b=).
+
+If you use the =consult= package, its default ~consult-buffer~ command
+has the means to group together buffers, recent files, and bookmarks.
+Each of those types can be narrowed to with a prefix key. The package
+=consult-dir= is an extension to =consult= which provides useful extras
+for working with directories, including bookmarks.
+
+** Treat your notes as a project
+:PROPERTIES:
+:CUSTOM_ID: h:fad3eb08-ddc7-43e4-ba28-210d89668037
+:END:
+
+Emacs has a built-in library for treating a directory tree as a
+"project". This means that the contents of this tree are seen as part
+of the same set, so commands like ~project-switch-to-buffer~ (=C-x p b=
+by default) will only consider buffers in the current project
+(e.g. three notes that are currently being visited).
+
+Normally, a "project" is a directory tree whose root is under version
+control. For our purposes, all you need is to navigate to the
+~denote-directory~ (for the shell or via Dired) and use the command-line
+to run this (requires the =git= executable):
+
+: git init
+
+From Dired, you can type =M-!= which invokes ~dired-smart-shell-command~
+and then run the git call there.
+
+The project can then be registered by invoking any project-related
+command inside of it, such as ~project-find-file~ (=C-x p f=).
+
+It is a good idea to keep your notes under version control, as that
+gives you a history of changes for each file. We shall not delve into
+the technicalities here, though suffice to note that Emacs' built-in
+version control framework or the exceptionally well-crafted =magit=
+package will get the job done (VC can work with other backends besides
+Git).
+
+** Use the tree-based file prompt for select commands
+:PROPERTIES:
+:CUSTOM_ID: h:8f9e0971-8b30-4e7b-af79-8fed257dbcfa
+:END:
+
+Older versions of Denote had a file prompt that resembled that of the
+standard ~find-file~ command (bound to =C-x C-f= by default). This
+means that it used a tree-based method of navigating the filesystem by
+selecting the specific directory and then the given file.
+
+Currently, Denote flattens the file prompt so that every file in the
+~denote-directory~ and its subdirectories can be matched from anywhere
+using the power of Emacs' minibuffer completion (such as with the help
+of the ~orderless~ package in addition to built-in options).
+
+Users who need the old behaviour on a per-command basis can define
+their own wrapper functions as shown in the following code block.
+
+#+begin_src emacs-lisp
+;; This is the old `denote-file-prompt' that we renamed to
+;; `denote-file-prompt-original' for clarity.
+(defun denote-file-prompt-original (&optional initial-text)
+ "Prompt for file with identifier in variable `denote-directory'.
+With optional INITIAL-TEXT, use it to prepopulate the minibuffer."
+ (read-file-name "Select note: " (denote-directory) nil nil initial-text
+ (lambda (f)
+ (or (denote-file-has-identifier-p f)
+ (file-directory-p f)))))
+
+;; Our wrapper command that changes the current `denote-file-prompt'
+;; to the functionality of `denote-file-prompt-original' only when
+;; this command is used.
+(defun my-denote-link ()
+ "Call `denote-link' but use Denote's original file prompt.
+See `denote-file-prompt-original'."
+ (interactive)
+ (cl-letf (((symbol-function 'denote-file-prompt) #'denote-file-prompt-original))
+ (call-interactively #'denote-link)))
+#+end_src
+
+** Rename files with Denote in the Image Dired thumbnails buffer
+:PROPERTIES:
+:CUSTOM_ID: h:e666ced6-da75-4bdb-9be3-82c2f4455ee9
+:END:
+
+[[#h:2d5ee9bf-e8f2-426c-8bf7-bf78bc88d1ee][Rename files with Denote using ~dired-preview~]]
+
+Just as with the ~denote-dired-rename-marked-files-with-keywords~,
+we can use Denote in the Image Dired buffer ([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files at once]]).
+Here is the custom code:
+
+#+begin_src emacs-lisp
+(autoload 'image-dired--with-marked "image-dired")
+(autoload 'image-dired-original-file-name "image-dired-util")
+
+(defun my-denote-image-dired-rename-marked-files (keywords)
+ "Like `denote-dired-rename-marked-files-with-keywords' but for Image Dired.
+Prompt for KEYWORDS and rename all marked files in the Image
+Dired buffer to have a Denote-style file name with the given
+KEYWORDS.
+
+IMPORTANT NOTE: if there are marked files in the corresponding
+Dired buffers, those will be targeted as well. This is not the
+fault of Denote: it is how Dired and Image Dired work in tandem.
+To only rename the marked thumbnails, start by unmarking
+everything in Dired. Then mark the items in Image Dired and
+invoke this command."
+ (interactive (list (denote-keywords-prompt)) image-dired-thumbnail-mode)
+ (image-dired--with-marked
+ (when-let* ((file (image-dired-original-file-name))
+ (dir (file-name-directory file))
+ (id (or (denote-retrieve-filename-identifier file) ""))
+ (file-type (denote-filetype-heuristics file))
+ (title (denote--retrieve-title-or-filename file file-type))
+ (signature (or (denote-retrieve-filename-signature file) "")
+ (extension (file-name-extension file t))
+ (new-name (denote-format-file-name dir id keywords title extension signature))
+ (default-directory dir))
+ (denote-rename-file-and-buffer file new-name))))
+#+end_src
+
+While the ~my-denote-image-dired-rename-marked-files~ renames files in
+the helpful Denote-compliant way, users may still need to not prepend
+a unique identifier and not sluggify (hyphenate and downcase) the
+image's existing file name. To this end, the following custom command
+can be used instead:
+
+#+begin_src emacs-lisp
+(defun my-image-dired-rename-marked-files (keywords)
+ "Like `denote-dired-rename-marked-files-with-keywords' but for Image Dired.
+Prompt for keywords and rename all marked files in the Image
+Dired buffer to have Denote-style keywords, but none of the other
+conventions of Denote's file-naming scheme."
+ (interactive (list (denote-keywords-prompt)) image-dired-thumbnail-mode)
+ (image-dired--with-marked
+ (when-let* ((file (image-dired-original-file-name))
+ (dir (file-name-directory file))
+ (file-type (denote-filetype-heuristics file))
+ (title (denote--retrieve-title-or-filename file file-type))
+ (extension (file-name-extension file t))
+ (kws (denote--keywords-combine keywords))
+ (new-name (concat dir title "__" kws extension))
+ (default-directory dir))
+ (denote-rename-file-and-buffer file new-name))))
+#+end_src
+
+** Rename files with Denote using ~dired-preview~
+:PROPERTIES:
+:CUSTOM_ID: h:2d5ee9bf-e8f2-426c-8bf7-bf78bc88d1ee
+:END:
+
+The ~dired-preview~ package (by me/Protesilaos) automatically displays
+a preview of the file at point in Dired. This can be helpful in
+tandem with Denote when we want to rename multiple files by taking a
+quick look at their contents.
+
+The command ~denote-dired-rename-marked-files-with-keywords~
+will generate Denote-style file names based on the keywords it prompts
+for. Identifiers are derived from each file's modification date
+([[#h:1b6b2c78-42f0-45b8-9ef0-6de21a8b2cde][Rename multiple files at once]]). There is no need for any custom code
+in this scenario.
+
+As noted in the section about Image Dired, the user may sometimes not
+need a fully fledged Denote-style file name but only append Denote-like
+keywords to each file name (e.g. =Original Name__denote_test.jpg=
+instead of =20230710T195843--original-name__denote_test.jpg=).
+
+[[#h:e666ced6-da75-4bdb-9be3-82c2f4455ee9][Rename files with Denote in the Image Dired thumbnails buffer]]
+
+In such a workflow, it is unlikely to be dealing with ordinary text
+files where front matter can be helpful. A custom command does not
+need to behave like what Denote provides out-of-the-box, but can
+instead append keywords to file names without conducting any further
+actions. We thus have:
+
+#+begin_src emacs-lisp
+(defun my-denote-dired-rename-marked-files-keywords-only ()
+ "Like `denote-dired-rename-marked-files-with-keywords' but only for keywords in file names.
+
+Prompt for keywords and rename all marked files in the Dired
+buffer to only have Denote-style keywords, but none of the other
+conventions of Denote's file-naming scheme."
+ (interactive nil dired-mode)
+ (if-let* ((marks (dired-get-marked-files)))
+ (let ((keywords (denote-keywords-prompt)))
+ (dolist (file marks)
+ (let* ((dir (file-name-directory file))
+ (file-type (denote-filetype-heuristics file))
+ (title (denote--retrieve-title-or-filename file file-type))
+ (extension (file-name-extension file t))
+ (kws (denote--keywords-combine keywords))
+ (new-name (concat dir title "__" kws extension)))
+ (denote-rename-file-and-buffer file new-name)))
+ (revert-buffer))
+ (user-error "No marked files; aborting")))
+#+end_src
+
+** Avoid duplicate identifiers when exporting Denote notes
+:PROPERTIES:
+:CUSTOM_ID: h:4a8c4546-26b3-4195-8b2c-b08a519986a4
+:END:
+
+When exporting Denote notes to, for example, an HTML or PDF file,
+there is a high probability that the same file name is used with a new
+extension. This is problematic because it creates files with
+duplicate identifiers. The =20230515T085612--example__keyword.org=
+produces a =20230515T085612--example__keyword.pdf=. Any link to the
+=20230515T085612= will thus break: it does not honor Denote's
+expectation of finding unique identifiers. This is not the fault of
+Denote: exporting is done by the user without Denote's involvement.
+
+Org Mode and Markdown use different approaches to exporting files. No
+recommended method is available for plain text files as there is no
+standardised export functionality for this format (the user can always
+create a new note using the file type they want on a case-by-case
+basis: [[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]]).
+
+*** Export Denote notes with Org Mode
+:PROPERTIES:
+:CUSTOM_ID: h:67669d9d-17c3-45bd-8227-da57d8bc3b73
+:END:
+
+Org Mode has a built-in configurable export engine. You can prevent
+duplicate identifiers when exporting manually for each exported file
+or by advising the Org export function.
+
+The ~denote-org~ package (by Protesilaos) also provides commands to
+convert =denote:= links to their =file:= equivalent, in case this is a
+required pre-processing step for export purposes.
+
+**** Manually configure Org export
+:PROPERTIES:
+:CUSTOM_ID: h:bf791e28-73e5-4ed8-88bc-e4e9b3ebaedb
+:END:
+
+Insert =#+export_file_name: FILENAME= in the front matter before
+exporting to force a filename called whatever the value of =FILENAME=
+is. The =FILENAME= does not specify the file type extension, such as
+=.pdf=. This is up to the export engine. For example, a Denote note
+with a complete file name of =20230515T085612--example__keyword.org=
+and a front matter entry of =#+export_file_name: hello= will be
+exported as =hello.pdf=.
+
+The advantage of this manual method is that it gives the user full
+control over the resulting file name. The disadvantage is that it
+depends on the user's behaviour. Forgetting to add a new name can
+lead to duplicate identifiers, as already noted in the introduction to
+this section ([[#h:4a8c4546-26b3-4195-8b2c-b08a519986a4][Export Denote notes]]).
+
+**** Automatically store Org exports in another folder
+:PROPERTIES:
+:CUSTOM_ID: h:7a61a370-78e5-42a1-9650-98fee140723f
+:END:
+
+It is possible to automatically place all exports in another folder by
+making Org's function ~org-export-output-file-name~ create the target
+directory if needed and move the exported file there. Remember that
+advising Elisp code must be handled with care, as it might break the
+original function in subtle ways.
+
+#+begin_src emacs-lisp
+(defvar my-org-export-output-directory-prefix "./export_"
+ "Prefix of directory used for org-mode export.
+
+The single dot means that the directory is created on the same
+level as the one where the Org file that performs the exporting
+is. Use two dots to place the directory on a level above the
+current one.
+
+If this directory is part of `denote-directory', make sure it is
+not read by Denote. See `denote-excluded-directories-regexp'.
+This way there will be no known duplicate Denote identifiers
+produced by the Org export mechanism.")
+
+(defun my-org-export-create-directory (fn extension &rest args)
+ "Move Org export file to its appropriate directory.
+
+Append the file type EXTENSION of the exported file to
+`my-org-export-output-directory-prefix' and, if absent, create a
+directory named accordingly.
+
+Install this as advice around `org-export-output-file-name'. The
+EXTENSION is supplied by that function. ARGS are its remaining
+arguments."
+ (let ((export-dir (format "%s%s" my-org-export-output-directory-prefix extension)))
+ (unless (file-directory-p export-dir)
+ (make-directory export-dir)))
+ (apply fn extension args))
+
+(advice-add #'org-export-output-file-name :around #'my-org-export-create-directory)
+#+end_src
+
+The target export directory should not be a subdirectory of
+~denote-directory~, as that will result in duplicate identifiers.
+Exclude it with the ~denote-excluded-directories-regexp~ user option
+([[#h:8458f716-f9c2-4888-824b-2bf01cc5850a][Exclude certain directories from all operations]]).
+
+[ NOTE: I (Protesilaos) am not a LaTeX user and cannot test the
+ following. ]
+
+Using a different directory will require some additional configuration
+when exporting using LaTeX. The export folder cannot be inside the
+path of the ~denote-directory~ to prevent Denote from recognising it
+as an attachment:
+<https://emacs.stackexchange.com/questions/45751/org-export-to-different-directory>.
+
+**** Org Mode Publishing
+:PROPERTIES:
+:CUSTOM_ID: h:2f3451ed-2fc4-4f36-bcf2-112939963e20
+:END:
+
+Org Mode also has a publishing tool for exporting a collection of
+files. Some user might apply this approach to convert their note
+collection to a public or private website.
+
+The ~org-publish-project-alist~ variable drives the publishing
+process, including the publishing directory.
+
+The publishing directory should not be a subdirectory of
+~denote-directory~, as that will result in duplicate identifiers.
+Exclude it with the ~denote-excluded-directories-regexp~ user option
+([[#h:8458f716-f9c2-4888-824b-2bf01cc5850a][Exclude certain directories from all operations]]).
+
+*** Export Denote notes with Markdown
+:PROPERTIES:
+:CUSTOM_ID: h:44c6a34a-e9ad-4f43-a24f-12f2c5a8467e
+:END:
+
+Exporting from Markdown requires an external processor (e.g.,
+Markdown.pl, Pandoc, or MultiMarkdown). The ~markdown-command~
+variable defines the command line used in export, for example:
+
+#+begin_src emacs-lisp
+(setq markdown-command "multimarkdown")
+#+end_src
+
+The export process thus occurs outside of Emacs. Users need to read
+the documentation of their preferred processor to prevent the creation
+of duplicate Denote identifiers.
+
+** Set up your workflow for daily or weekly meeting notes
+:PROPERTIES:
+:CUSTOM_ID: h:00998f94-0194-43ef-b349-260106ef7177
+:END:
+
+Perhaps as part of work, we meet with certain people on a regular
+basis. During the meeting we may discuss a variety of topics. How best
+to approach with the help of Denote?
+
+One option is to write a new file for each meeting, giving it the
+appropriate keywords each time ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]). This is what Denote
+does by default and does not need any further tweaks. If we need to
+review those notes, we can use the command ~denote-sort-dired~
+([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Sort files by component]]), or one of the Org dynamic blocks we provide
+([[#h:8b542c50-dcc9-4bca-8037-a36599b22779][Use Org dynamic blocks]]), among other options.
+
+Another approach is to write one file per person with the regular
+~denote~ command (or related), give it the name of the person as a
+title and, optionally, use some relevant keywords. Inside each file,
+write a top-level heading with the date of the meeting, and then
+produce the meeting notes below as paragraphs and subheadings. This
+can all be done without any changes to Denote, though we can
+streamline it by incorporating the following code in our setup.
+Configure ~my-denote-colleagues~ and then use the command
+~my-denote-colleagues-new-meeting~ to see how it works.
+
+#+begin_src emacs-lisp
+(defvar my-denote-colleagues '("Prot" "Protesilaos")
+ "List of names I collaborate with.
+There is at least one file in the variable `denote-directory' that has
+the name of this person.")
+
+(defvar my-denote-colleagues-prompt-history nil
+ "Minibuffer history for `my-denote-colleagues-new-meeting'.")
+
+(defun my-denote-colleagues-prompt ()
+ "Prompt with completion for a name among `my-denote-colleagues'.
+Use the last input as the default value."
+ (let ((default-value (car my-denote-colleagues-prompt-history)))
+ (completing-read
+ (format-prompt "New meeting with COLLEAGUE" default-value)
+ my-denote-colleagues
+ nil :require-match nil
+ 'my-denote-colleagues-prompt-history
+ default-value)))
+
+(defun my-denote-colleagues-get-file (name)
+ "Find file in variable `denote-directory' for NAME colleague.
+If there are more than one files, prompt with completion for one among
+them.
+
+NAME is one among `my-denote-colleagues'."
+ (if-let* ((files (denote-directory-files name))
+ (length-of-files (length files)))
+ (cond
+ ((= length-of-files 1)
+ (car files))
+ ((> length-of-files 1)
+ (completing-read "Select a file: " files nil :require-match)))
+ (user-error "No files for colleague with name `%s'" name)))
+
+(defun my-denote-colleagues-new-meeting ()
+ "Prompt for the name of a colleague and insert a timestamped heading therein.
+The name of a colleague corresponds to at least one file in the variable
+`denote-directory'. In case there are multiple files, prompt to choose
+one among them and operate therein."
+ (declare (interactive-only t))
+ (interactive)
+ (let* ((name (my-denote-colleagues-prompt))
+ (file (my-denote-colleagues-get-file name))
+ (time (format-time-string "%F %a %R"))) ; remove %R if you do not want the time
+ (with-current-buffer (find-file file)
+ (goto-char (point-max))
+ ;; Here I am assuming we are in `org-mode', hence the leading
+ ;; asterisk for the heading. Adapt accordingly.
+ (insert (format "* [%s]\n\n" time)))))
+#+end_src
+
+* For developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:c916d8c5-540a-409f-b780-6ccbd90e088e
+:END:
+
+Denote is in a stable state and can be relied upon as the basis for
+custom extensions ([[#h:08b14682-e73f-4b11-b2e2-be3b788c8572][Packages that build on Denote]]). Further below is a
+list with the functions or variables we provide for public usage.
+Those are in addition to all user options and commands that are
+already documented in the various sections of this manual.
+
+In this context "public" is any form with single hyphens in its symbol,
+such as ~denote-directory-files~. We expressly support those, meaning
+that we consider them reliable and commit to documenting any changes in
+their particularities (such as through ~make-obsolete~, a record in the
+change log, a blog post on the maintainer's website, and the like).
+
+By contradistinction, a "private" form is declared with two hyphens in
+its symbol such as ~denote--file-extension~. Do not use those as we
+might change them without further notice.
+
+The following sections cover the specifics.
+
+** Common building blocks for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:c98aed45-341a-40a0-91ce-347a29c98ab4
+:END:
+
+#+vindex: denote-id-format
++ Variable ~denote-id-format~ :: Format of ID prefix of a note's
+ filename. The note's ID is derived from the date and time of its
+ creation ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+
+#+vindex: denote-id-regexp
++ 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]]).
+
+#+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]]).
+
+#+findex: denote-identifier-p
++ Function ~denote-identifier-p~ :: Return non-nil if =IDENTIFIER=
+ string is a Denote identifier.
+
+#+findex: denote-file-is-note-p
++ Function ~denote-file-is-note-p~ :: Return non-nil if =FILE= is an
+ actual Denote note. For our purposes, a note must satisfy
+ ~file-regular-p~ and ~denote-filename-is-note-p~.
+
+#+findex: denote-file-has-identifier-p
++ Function ~denote-file-has-identifier-p~ :: Return non-nil if =FILE=
+ has a Denote identifier.
+
+#+findex: denote-file-has-denoted-filename-p
++ Function ~denote-file-has-denoted-filename-p~ :: Return non-nil if
+ =FILE= respects the file-naming scheme of Denote. This tests the
+ rules of Denote's file-naming scheme. Sluggification is ignored. It
+ is done by removing all file name components and validating what
+ remains.
+
+#+findex: denote-file-has-signature-p
++ Function ~denote-file-has-signature-p~ :: Return non-nil if =FILE=
+ has a signature.
+
+#+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
+ of an added =.gpg= suffix. Supported extensions are those implied by
+ ~denote-file-type~.
+
+#+findex: denote-file-is-writable-and-supported-p
++ Function ~denote-file-is-writable-and-supported-p~ :: Return non-nil
+ if =FILE= is writable and has supported extension.
+
+#+findex: denote-file-type-extensions
++ Function ~denote-file-type-extensions~ :: Return all file type
+ extensions in ~denote-file-types~.
+
+#+vindex: denote-encryption-file-extensions
++ Variable ~denote-encryption-file-extensions~ :: List of strings
+ specifying file extensions for encryption.
+
+#+findex: denote-file-type-extensions-with-encryption
++ Function ~denote-file-type-extensions-with-encryption~ :: Derive
+ ~denote-file-type-extensions~ plus ~denote-encryption-file-extensions~.
+
+#+findex: denote-get-file-extension
++ Function ~denote-get-file-extension~ :: Return extension of =FILE=
+ with dot included. Account for ~denote-encryption-file-extensions~.
+ In other words, return something like =.org.gpg= if it is part of
+ the file, else return =.org=.
+
+#+findex: denote-get-file-extension-sans-encryption
++ Function ~denote-get-file-extension-sans-encryption~ :: Return
+ extension of =FILE= with dot included and without the encryption
+ part. Build on top of ~denote-get-file-extension~ though always
+ return something like =.org= even if the actual file extension is
+ =.org.gpg=.
+
+#+findex: denote-infer-keywords-from-files
++ Functions ~denote-infer-keywords-from-files~ :: Return list of
+ keywords in ~denote-directory-files~. With optional
+ =FILES-MATCHING-REGEXP=, only extract keywords from the matching
+ files. Otherwise, do it for all files. Keep any duplicates. Users
+ who do not want duplicates should refer to the functions
+ ~denote-keywords~.
+
+#+findex: denote-keywords
++ Function ~denote-keywords~ :: Return appropriate list of keyword
+ candidates. If ~denote-infer-keywords~ is non-nil, infer keywords
+ from existing notes and combine them into a list with
+ ~denote-known-keywords~. Else use only the latter set of keywords
+ ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]). In the case of keyword inferrence, use
+ optional =FILES-MATCHING-REGEXP=, to extract keywords only from the
+ matching files. Otherwise, do it for all files. Filter inferred
+ keywords with the user option ~denote-excluded-keywords-regexp~.
+
+#+findex: denote-keywords-sort
++ Function ~denote-keywords-sort~ :: Sort =KEYWORDS= if
+ ~denote-sort-keywords~ is non-nil. =KEYWORDS= is a list of strings,
+ per ~denote-keywords-prompt~.
+
+#+findex: denote-keywords-combine
++ Function ~denote-keywords-combine~ :: Combine =KEYWORDS= list of
+ strings into a single string. Keywords are separated by the
+ underscore character, per the Denote file-naming scheme.
+
+#+findex: denote-valid-date-p
++ Function ~denote-valid-date-p~ :: Return =DATE= as a valid date. A
+ valid =DATE= is a value that can be parsed by either ~decode-time~
+ or ~date-to-time~ .Those functions signal an error if =DATE= is a
+ value they do not recognise. If =DATE= is nil, return nil.
+
+#+findex: denote-directory
++ Function ~denote-directory~ :: Return path of the variable
+ ~denote-directory~ as a proper directory, also because it accepts a
+ directory-local value for what we internally refer to as "silos"
+ ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directories for notes]]). Custom Lisp code can
+ ~let~ bind the value of the variable ~denote-directory~
+ to override what this function returns.
+
+#+findex: denote-directory-files
++ Function ~denote-directory-files~ :: Return list of absolute file
+ paths in variable ~denote-directory~. Files that match
+ ~denote-excluded-files-regexp~ are excluded from the list. Files
+ only need to have an identifier. The return value may thus include
+ file types that are not implied by ~denote-file-type~. With optional
+ =FILES-MATCHING-REGEXP=, restrict files to those matching the given
+ regular expression. With optional =OMIT-CURRENT= as a non-nil value,
+ do not include the current Denote file in the returned list. With
+ optional =TEXT-ONLY= as a non-nil value, limit the results to text
+ files that satisfy ~denote-file-is-note-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.
+
+#+findex: denote-directory-subdirectories
++ Function ~denote-directory-subdirectories~ :: Return list of
+ subdirectories in variable ~denote-directory~. Omit dotfiles (such
+ as .git) unconditionally. Also exclude whatever matches
+ ~denote-excluded-directories-regexp~. Note that the
+ ~denote-directory~ accepts a directory-local value for what we call
+ "silos" ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directories for notes]]).
+
+** File path interface for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:08e696e4-c9e1-48b4-8349-e53451aed1df
+:END:
+
+#+findex: denote-file-name-relative-to-denote-directory
++ Function ~denote-file-name-relative-to-denote-directory~ :: Return
+ name of =FILE= relative to the variable ~denote-directory~. =FILE=
+ must be an absolute path.
+
+#+findex: denote-slug-keep-only-ascii
++ Function ~denote-slug-keep-only-ascii~ :: Remove all non-ASCII
+ characters from =STR= and replace them with spaces. This is useful
+ as a helper function to construct ~denote-file-name-slug-functions~
+ ([[#h:d1e4eb5b-e7f2-4a3b-9243-e1c653817a4a][Custom sluggification to remove non-ASCII characters]]).
+
+#+findex: denote-sluggify
++ Function ~denote-sluggify~ :: Make =STR= an appropriate slug for
+ file name =COMPONENT= ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]).
+ 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.
+
+#+findex: denote-sluggify-keyword
++ Function ~denote-sluggify-keyword~ :: Sluggify =STR= while joining
+ separate words.
+
+#+findex: denote-sluggify-signature
++ Function ~denote-sluggify-signature~ :: Make =STR= an appropriate
+ slug for signatures ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]).
+
+#+findex: denote-sluggify-keywords
++ Function ~denote-sluggify-keywords~ :: Sluggify =KEYWORDS=, which is
+ a list of strings ([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]).
+
+#+findex: denote-use-date
++ Function ~denote-use-date~ :: The date to be used in a note creation
+ command. See the documentation of ~denote~ for acceptable values.
+ This variable is ignored if nil. Only ever ~let~ bind this,
+ otherwise the title will always be the same and the title prompt
+ will be skipped.
+
+#+findex: denote-use-directory
++ Function ~denote-use-directory~ :: The directory to be used in a
+ note creation command. See the documentation of ~denote~ for
+ acceptable values. This variable is ignored if nil. Only ever ~let~
+ bind this, otherwise the title will always be the same and the title
+ prompt will be skipped.
+
+#+findex: denote-use-file-type
++ Function ~denote-use-file-type~ :: The file type to be used in a
+ note creation command. See the documentation of ~denote~ for
+ acceptable values. This variable is ignored if nil. Only ever ~let~
+ bind this, otherwise the title will always be the same and the title
+ prompt will be skipped.
+
+#+findex: denote-use-keywords
++ Function ~denote-use-keywords~ :: The keywords to be used in a note
+ creation command. See the documentation of ~denote~ for acceptable
+ values. This variable is ignored if ~default~. Only ever ~let~ bind this,
+ otherwise the title will always be the same and the title prompt
+ will be skipped.
+
+#+findex: denote-use-signature
++ Function ~denote-use-signature~ :: The signature to be used in a
+ note creation command. See the documentation of ~denote~ for
+ acceptable values. This variable is ignored if nil. Only ever ~let~
+ bind this, otherwise the title will always be the same and the title
+ prompt will be skipped.
+
+#+findex: denote-use-template
++ Function ~denote-use-template~ :: The template to be used in a note
+ creation command. See the documentation of ~denote~ for acceptable
+ values. This variable is ignored if nil. Only ever ~let~ bind this,
+ otherwise the title will always be the same and the title prompt
+ will be skipped.
+
+#+findex: denote-use-title
++ Function ~denote-use-title~ :: The title to be used in a note
+ creation command. See the documentation of ~denote~ for acceptable
+ values. This variable is ignored if nil. Only ever ~let~ bind this,
+ otherwise the title will always be the same and the title prompt
+ will be skipped.
+
+#+findex: denote-format-file-name
++ Function ~denote-format-file-name~ :: Format file name. =DIR-PATH=,
+ =ID=, =KEYWORDS=, =TITLE=, =EXTENSION= and =SIGNATURE= are expected to
+ be supplied by ~denote~ or equivalent command.
+
+ =DIR-PATH= is a string pointing to a directory. It ends with a
+ forward slash (the function ~denote-directory~ makes sure this is
+ the case when returning the value of the variable ~denote-directory~).
+ =DIR-PATH= cannot be nil or an empty string.
+
+ =ID= is a string holding the identifier of the note. It can be an
+ empty string, in which case its respective file name component is
+ not added to the base file name.
+
+ =DIR-PATH= and =ID= form the base file name.
+
+ =KEYWORDS= is a list of strings that is reduced to a single string
+ by ~denote-keywords-combine~. =KEYWORDS= can be an empty list or a
+ nil value, in which case the relevant file name component is not
+ added to the base file name.
+
+ =TITLE= and =SIGNATURE= are strings. They can be an empty string, in
+ which case their respective file name component is not added to the
+ base file name.
+
+ =EXTENSION= is a string that contains a dot followed by the file
+ type extension. It can be an empty string or a nil value, in which
+ case it is not added to the base file name.
+
+** Data retrieval interface for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:9b32bf7d-1569-4495-801c-8835f022d72e
+:END:
+
+#+findex: denote-get-path-by-id
++ Function ~denote-get-path-by-id~ :: Return absolute path of =ID=
+ string in ~denote-directory-files~.
+
+#+findex: denote-get-identifier-at-point
++ Function ~denote-get-identifier-at-point~ :: Return the identifier
+ at point or =POINT=.
+
+#+findex: denote-extract-keywords-from-path
++ Function ~denote-extract-keywords-from-path~ :: Extract keywords
+ from =PATH= and return them as a list of strings. =PATH= must be a
+ Denote-style file name where keywords are prefixed with an
+ underscore. If =PATH= has no such keywords, which is possible,
+ return nil ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
+
+#+findex: denote-extract-id-from-string
++ Function ~denote-extract-id-from-string~ :: Return existing Denote
+ identifier in =STRING=, else nil.
+
+#+findex: denote-retrieve-filename-identifier
++ Function ~denote-retrieve-filename-identifier~ :: Extract identifier
+ from =FILE= name, if present, else return nil. To create a new one
+ from a date, refer to the ~denote-get-identifier~ function.
+
+#+findex: denote-retrieve-filename-title
++ Function ~denote-retrieve-filename-title~ :: Extract Denote title
+ component from =FILE= name, if present, else return nil.
+
+#+findex: denote-retrieve-filename-keywords
++ Function ~denote-retrieve-filename-keywords~ :: Extract keywords
+ from =FILE= name, if present, else return nil. Return
+ matched keywords as a single string.
+
+#+findex: denote-retrieve-filename-signature
++ Function ~denote-retrieve-filename-signature~ :: Extract signature
+ from =FILE= name, if present, else return nil.
+
+#+findex: denote-retrieve-title-or-filename
++ Function ~denote-retrieve-title-or-filename~ :: Return appropriate
+ title for =FILE= given its =TYPE=. This is a wrapper for
+ ~denote-retrieve-front-matter-title-value~ and
+ =denote-retrieve-filename-title=.
+
+#+findex: denote-get-identifier
++ Function ~denote-get-identifier~ :: Convert =DATE= into a Denote
+ identifier using ~denote-id-format~. If =DATE= is nil, return an
+ empty string as the identifier.
+
+#+findex: denote-retrieve-front-matter-title-value
++ Function ~denote-retrieve-front-matter-title-value~ :: Return title value from
+ =FILE= front matter per =FILE-TYPE=.
+
+#+findex: denote-retrieve-front-matter-title-line
++ Function ~denote-retrieve-front-matter-title-line~ :: Return title line from
+ =FILE= front matter per =FILE-TYPE=.
+
+#+findex: denote-retrieve-front-matter-keywords-value
++ Function ~denote-retrieve-front-matter-keywords-value~ :: Return keywords value
+ from =FILE= front matter per =FILE-TYPE=. The return value is a list
+ of strings.
+
+#+findex: denote-retrieve-front-matter-keywords-line
++ Function ~denote-retrieve-front-matter-keywords-line~ :: Return keywords line
+ from =FILE= front matter per =FILE-TYPE=.
+
+** Prompt interface for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:e9d05541-bd00-43b9-8146-8c4445f78f07
+:END:
+
+#+findex: denote-add-prompts
++ Function ~denote-add-prompts~ :: Add list of =ADDITIONAL-PROMPTS= to
+ ~denote-prompts~. This is best done inside of a ~let~ to create a
+ wrapper function around ~denote~, ~denote-rename-file~, and
+ generally any command that consults the value of ~denote-prompts~.
+
+#+findex: denote-signature-prompt
++ Function ~denote-signature-prompt~ :: Prompt for signature string.
+ With optional =INITIAL-SIGNATURE= use it as the initial minibuffer
+ text. With optional =PROMPT-TEXT= use it in the minibuffer instead
+ of the default prompt. Previous inputs at this prompt are available
+ for minibuffer completion if the user option ~denote-history-completion-in-prompts~
+ is set to a non-nil value ([[#h:403422a7-7578-494b-8f33-813874c12da3][The ~denote-history-completion-in-prompts~ option]]).
+
+#+findex: denote-file-prompt
++ Function ~denote-file-prompt~ :: Prompt for file in variable
+ ~denote-directory~. Files that match ~denote-excluded-files-regexp~
+ are excluded from the list. With optional =FILES-MATCHING-REGEXP=,
+ filter the candidates per the given regular expression. With
+ optional =PROMPT-TEXT=, use it instead of the default call to select
+ a file. With optional =NO-REQUIRE-MATCH= accept the given input
+ as-is. Return the absolute path to the matching file.
+
+#+findex: denote-keywords-prompt
++ Function ~denote-keywords-prompt~ :: Prompt for one or more keywords.
+ Read entries as separate when they are demarcated by the
+ ~crm-separator~, which typically is a comma. With optional
+ =PROMPT-TEXT=, use it to prompt the user for keywords. Else use a
+ generic prompt. With optional =INITIAL-KEYWORDS= use them as the
+ initial minibuffer text.
+
+#+findex: denote-title-prompt
++ Function ~denote-title-prompt~ :: Prompt for title string. With
+ optional =INITIAL-TITLE= use it as the initial minibuffer text. With
+ optional =PROMPT-TEXT= use it in the minibuffer instead of the
+ default prompt. Previous inputs at this prompt are available
+ for minibuffer completion if the user option ~denote-history-completion-in-prompts~
+ is set to a non-nil value ([[#h:403422a7-7578-494b-8f33-813874c12da3][The ~denote-history-completion-in-prompts~ option]]).
+
+#+vindex: denote-title-prompt-current-default
++ Variable ~denote-title-prompt-current-default~ :: Currently bound
+ default title for ~denote-title-prompt~. Set the value of this
+ variable within the lexical scope of a command that needs to supply
+ a default title before calling ~denote-title-prompt~ and use
+ ~unwind-protect~ to set its value back to nil.
+
+#+findex: denote-file-type-prompt
++ Function ~denote-file-type-prompt~ :: Prompt for ~denote-file-type~.
+ Note that a non-nil value other than ~text~, ~markdown-yaml~, and
+ ~markdown-toml~ falls back to an Org file type. We use ~org~ here
+ for clarity.
+
+#+findex: denote-date-prompt
++ Function ~denote-date-prompt~ :: Prompt for date, expecting
+ =YYYY-MM-DD= or that plus =HH:MM= (or even =HH:MM:SS=). Use Org's
+ more advanced date selection utility if the user option
+ ~denote-date-prompt-use-org-read-date~ is non-nil. It requires Org
+ ([[#h:e7ef08d6-af1b-4ab3-bb00-494a653e6d63][The denote-date-prompt-use-org-read-date option]]). With optional
+ =INITIAL-DATE= use it as the initial minibuffer text. With optional
+ =PROMPT-TEXT= use it in the minibuffer instead of the default
+ prompt. =INITIAL-DATE= is a string that can be processed by
+ ~denote-valid-date-p~, a value that can be parsed by ~decode-time~
+ or nil.
+
+#+findex: denote-command-prompt
++ Function ~denote-command-prompt~ :: Prompt for command among
+ ~denote-commands-for-new-notes~ ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]).
+
+#+vindex: denote-prompts-with-history-as-completion
++ Variable ~denote-prompts-with-history-as-completion~ :: Prompts that
+ conditionally perform completion against their history. These are
+ minibuffer prompts that ordinarily accept a free form string input,
+ as opposed to matching against a predefined set. These prompts can
+ optionally perform completion against their own minibuffer history
+ when the user option ~denote-history-completion-in-prompts~ is set
+ to a non-nil value ([[#h:403422a7-7578-494b-8f33-813874c12da3][The ~denote-history-completion-in-prompts~ option]]).
+
+#+findex: denote-files-matching-regexp-prompt
++ Function ~denote-files-matching-regexp-prompt~ :: Prompt for
+ =REGEXP= to filter Denote files by. With optional =PROMPT-TEXT= use
+ it instead of a generic prompt.
+
+#+findex: denote-prompt-for-date-return-id
++ Function ~denote-prompt-for-date-return-id~ :: Use
+ ~denote-date-prompt~ and return it as ~denote-id-format~.
+
+#+findex: denote-template-prompt
++ Function ~denote-template-prompt~ :: Prompt for template key in
+ ~denote-templates~ and return its value.
+
+#+findex: denote-subdirectory-prompt
++ Function ~denote-subdirectory-prompt~ :: Prompt for subdirectory of
+ the variable ~denote-directory~. The table uses the ~file~
+ completion category (so it works with packages such as ~marginalia~
+ and ~embark~).
+
+** Front matter interface for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:80ddc044-5c64-4fb3-b4f4-2eaf6bceda3b
+:END:
+
+#+findex: denote-filetype-heuristics
++ Function ~denote-filetype-heuristics~ :: Return likely file type of
+ =FILE=. If in the process of ~org-capture~, consider the file type to
+ be that of Org. Otherwise, use the file extension to detect the file
+ type of =FILE=.
+
+ If more than one file type correspond to this file extension, use
+ the first file type for which the :title-key-regexp in
+ ~denote-file-types~ matches in the file.
+
+ Return nil if the file type is not recognized.
+
+#+vindex: denote-org-front-matter
++ Variable ~denote-org-front-matter~ :: Specifies the Org front
+ matter. It is passed to ~format~ with arguments =TITLE=, =DATE=,
+ =KEYWORDS=, =ID= ([[#h:7f918854-5ed4-4139-821f-8ee9ba06ad15][Change the front matter format]])
+
+#+vindex: denote-yaml-front-matter
++ Variable ~denote-yaml-front-matter~ :: Specifies the YAML (Markdown)
+ front matter. It is passed to ~format~ with arguments =TITLE=,
+ =DATE=, =KEYWORDS=, =ID= ([[#h:7f918854-5ed4-4139-821f-8ee9ba06ad15][Change the front matter format]])
+
+#+vindex: denote-toml-front-matter
++ Variable ~denote-toml-front-matter~ :: Specifies the TOML (Markdown)
+ front matter. It is passed to ~format~ with arguments =TITLE=,
+ =DATE=, =KEYWORDS=, =ID= ([[#h:7f918854-5ed4-4139-821f-8ee9ba06ad15][Change the front matter format]])
+
+#+vindex: denote-text-front-matter
++ Variable ~denote-text-front-matter~ :: Specifies the plain text
+ front matter. It is passed to ~format~ with arguments =TITLE=,
+ =DATE=, =KEYWORDS=, =ID= ([[#h:7f918854-5ed4-4139-821f-8ee9ba06ad15][Change the front matter format]])
+
+#+findex: denote-date-org-timestamp
++ Function ~denote-date-org-timestamp~ :: Format =DATE= using the Org
+ inactive timestamp notation.
+
+#+findex: denote-date-rfc3339
++ Function ~denote-date-rfc3339~ :: Format =DATE= using the RFC3339
+ specification.
+
+#+findex: denote-date-iso-8601
++ Function ~denote-date-iso-8601~ :: Format =DATE= according to ISO
+ 8601 standard.
+
+#+findex: denote-trim-whitespace
++ Function ~denote-trim-whitespace~ :: Trim whitespace around string
+ =S=. This can be used in ~denote-file-types~ to format front
+ mattter.
+
+#+findex: denote-trim-whitespace-then-quotes
++ Function ~denote-trim-whitespace-then-quotes~ :: Trim whitespace
+ then quotes around string =S=. This can be used in
+ ~denote-file-types~ to format front mattter.
+
+#+findex: denote-format-string-for-org-front-matter
++ Function ~denote-format-string-for-org-front-matter~ :: Return
+ string =S= as-is for Org or plain text front matter. If =S= is not a
+ string, return an empty string.
+
+#+findex: denote-format-string-for-md-front-matter
++ Function ~denote-format-string-for-md-front-matter~ :: Surround
+ string =S= with quotes. If =S= is not a string, return a literal
+ emptry string. This can be used in ~denote-file-types~ to format
+ front mattter.
+
+#+findex: denote-format-keywords-for-md-front-matter
++ Function ~denote-format-keywords-for-md-front-matter~ :: Format
+ front matter =KEYWORDS= for markdown file type. =KEYWORDS= is a
+ list of strings. Consult the ~denote-file-types~ for how this is
+ used.
+
+#+findex: denote-format-keywords-for-text-front-matter
++ Function ~denote-format-keywords-for-text-front-matter~ :: Format
+ front matter =KEYWORDS= for text file type. =KEYWORDS= is a list of
+ strings. Consult the ~denote-file-types~ for how this is used.
+
+#+findex: denote-format-keywords-for-org-front-matter
++ Function ~denote-format-keywords-for-org-front-matter~ :: Format
+ front matter =KEYWORDS= for org file type. =KEYWORDS= is a list of
+ strings. Consult the ~denote-file-types~ for how this is used.
+
+#+findex: denote-extract-keywords-from-front-matter
++ Function ~denote-extract-keywords-from-front-matter~ :: Format front
+ matter =KEYWORDS= for org file type. =KEYWORDS= is a list of
+ strings. Consult the ~denote-file-types~ for how this is used.
+
+#+vindex: denote-file-types
++ Variable ~denote-file-types~ :: Alist of ~denote-file-type~ and
+ their format properties.
+
+ Each element is of the form =(SYMBOL PROPERTY-LIST)=. =SYMBOL= is
+ one of those specified in ~denote-file-type~ or an arbitrary symbol
+ that defines a new file type.
+
+ =PROPERTY-LIST= is a plist that consists of the following elements:
+
+ 1. =:extension= is a string with the file extension including the
+ period.
+
+ 2. =:date-function= is a function that can format a date. See the
+ functions ~denote--date-iso-8601~, ~denote--date-rfc3339~, and
+ ~denote--date-org-timestamp~.
+
+ 3. =:front-matter= is either a string passed to ~format~ or a
+ variable holding such a string. The ~format~ function accepts
+ four arguments, which come from ~denote~ in this order: =TITLE=,
+ =DATE=, =KEYWORDS=, =IDENTIFIER=. Read the doc string of
+ ~format~ on how to reorder arguments.
+
+ 4. =:title-key-regexp= is a regular expression that is used to
+ retrieve the title line in a file. The first line matching this
+ regexp is considered the title line.
+
+ 5. =:title-value-function= is the function used to format the raw
+ title string for inclusion in the front matter (e.g. to surround
+ it with quotes). Use the ~identity~ function if no further
+ processing is required.
+
+ 6. =:title-value-reverse-function= is the function used to retrieve
+ the raw title string from the front matter. It performs the
+ reverse of =:title-value-function=.
+
+ 7. =:keywords-key-regexp= is a regular expression used to retrieve
+ the keywords' line in the file. The first line matching this
+ regexp is considered the keywords' line.
+
+ 8. =:keywords-value-function= is the function used to format the
+ keywords' list of strings as a single string, with appropriate
+ delimiters, for inclusion in the front matter.
+
+ 9. =:keywords-value-reverse-function= is the function used to retrieve
+ the keywords' value from the front matter. It performs the reverse
+ of the =:keywords-value-function=.
+
+ 10. =:link= is a string, or variable holding a string, that
+ specifies the format of a link. See the variables
+ ~denote-org-link-format~, ~denote-md-link-format~.
+
+ 11. =:link-in-context-regexp= is a regular expression that is used
+ to match the aforementioned link format. See the variables
+ ~denote-org-link-in-context-regexp~, ~denote-md-link-in-context-regexp~.
+
+ If ~denote-file-type~ is nil, use the first element of this list for
+ new note creation. The default is ~org~.
+
+** Link interface for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:97b8eb9e-07e1-4992-8b59-2a0e3b65574b
+:END:
+
+#+vindex: denote-org-link-format
++ Variable ~denote-org-link-format~ :: Format of Org link to note.
+ The value is passed to ~format~ with =IDENTIFIER= and =TITLE=
+ arguments, in this order. Also see ~denote-org-link-in-context-regexp~.
+
+#+vindex: denote-md-link-format
++ Variable ~denote-md-link-format~ :: Format of Markdown link to note.
+ The =%N$s= notation used in the default value is for ~format~ as the
+ supplied arguments are =IDENTIFIER= and =TITLE=, in this order.
+ Also see ~denote-md-link-in-context-regexp~.
+
+#+vindex: denote-id-only-link-format
++ Variable ~denote-id-only-link-format~ :: Format of identifier-only
+ link to note. The value is passed to ~format~ with =IDENTIFIER= as
+ its sole argument. Also see ~denote-id-only-link-in-context-regexp~.
+
+#+vindex: denote-org-link-in-context-regexp
++ Variable ~denote-org-link-in-context-regexp~ :: Regexp to match an
+ Org link in its context. The format of such links is ~denote-org-link-format~.
+
+#+vindex: denote-md-link-in-context-regexp
++ Variable ~denote-md-link-in-context-regexp~ :: Regexp to match an
+ Markdown link in its context. The format of such links is ~denote-md-link-format~.
+
+#+vindex: denote-id-only-link-in-context-regexp
++ Variable ~denote-id-only-link-in-context-regexp~ :: Regexp to match
+ an identifier-only link in its context. The format of such links is
+ ~denote-id-only-link-format~.
+
+#+findex: denote-select-linked-file-prompt
++ Function ~denote-select-linked-file-prompt~ :: Prompt for linked
+ file among =FILES=.
+
+#+findex: denote-link-return-links
++ Function ~denote-link-return-links~ :: Return list of links in
+ current or optional =FILE=. Also see ~denote-link-return-backlinks~.
+
+#+findex: denote-link-return-backlinks
++ Function ~denote-link-return-backlinks~ :: Return list of backlinks
+ in current or optional =FILE=. Also see ~denote-link-return-links~.
+
+#+findex: denote-link-description-with-signature-and-title
++ Function ~denote-link-description-with-signature-and-title~ :: Return
+ link description for =FILE=. Produce a description as follows:
+
+ - If the region is active, use it as the description.
+
+ - If =FILE= as a signature, then use the
+ ~denote-link-signature-format~. By default, this looks like
+ "signature title".
+
+ - If =FILE= does not have a signature, then use its title as the
+ description.
+
+#+vindex: denote-link-description-function
++ Variable ~denote-link-description-function~ :: Function to use to
+ create the description of links. The function specified should take
+ a =FILE= argument and should return the description as a string. By
+ default, the title of the file is returned as the description.
+
+** Xref interface for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:e824c215-1c31-4b26-b994-7df67341d075
+:END:
+
+- Function ~denote-retrieve-groups-xref-query~ :: Access location of
+ xrefs for =QUERY= and group them per file. Limit the search to text
+ files.
+
+- Function ~denote-retrieve-files-xref-query~ :: Return sorted,
+ deduplicated file names with matches for =QUERY= in their contents.
+ Limit the search to text files.
+
+- Function ~denote-retrieve-xref-alist~ :: Return xref alist of files
+ with location of matches for =QUERY=. With optional
+ =FILES-MATCHING-REGEXP=, limit the list of files accordingly (per
+ ~denote-directory-files~). At all times limit the search to text
+ files.
+
+** Renaming files interface for developers or advanced users
+:PROPERTIES:
+:CUSTOM_ID: h:66d31f42-6092-493a-97db-83b217db9d96
+:END:
+
+#+findex: denote-rename-file-prompt
+- Function ~denote-rename-file-prompt~ :: Prompt to rename file named
+ =OLD-NAME= to =NEW-NAME=. If ~denote-rename-confirmations~ does not
+ contain ~modify-file-name~, return t without prompting.
+
+#+findex: denote-rename-file-and-buffer
++ Function ~denote-rename-file-and-buffer~ :: Rename file named
+ =OLD-NAME= to =NEW-NAME=, updating buffer name.
+
+#+findex: denote-prepend-front-matter
++ Function ~denote-prepend-front-matter~ :: Prepend front matter to
+ =FILE=. The =TITLE=, =KEYWORDS=, =DATE=, =ID=, =SIGNATURE=, and
+ =FILE-TYPE= are passed from the renaming command and are used to
+ construct a new front matter block if appropriate.
+
+#+findex: denote-rewrite-front-matter
++ Function ~denote-rewrite-front-matter~ :: Rewrite front matter of
+ note after ~denote-rename-file~ (or related). The =FILE=, =TITLE=,
+ =KEYWORDS=, =SIGNATURE=, =DATE=, =IDENTIFIER=, and =FILE-TYPE=
+ arguments are given by the renaming command and are used to construct
+ new front matter values if appropriate. If ~denote-rename-confirmations~
+ contains ~rewrite-front-matter~, prompt to confirm the rewriting of
+ the front matter. Otherwise produce a ~y-or-n-p~ prompt to that
+ effect.
+
+#+findex: denote-add-front-matter-prompt
++ Function ~denote-add-front-matter-prompt~ :: Prompt to add a
+ front-matter to =FILE=. Return non-nil if a new front matter should
+ be added. If ~denote-rename-confirmations~ does not contain
+ ~add-front-matter~, return t without prompting.
+
+#+findex: denote-rewrite-keywords
++ Function ~denote-rewrite-keywords~ :: Rewrite =KEYWORDS= in =FILE=
+ outright according to =FILE-TYPE=. Do the same as
+ ~denote-rewrite-front-matter~ for keywords, but do not ask for
+ confirmation. With optional =SAVE-BUFFER=, save the buffer
+ corresponding to =FILE=. This function is for use in the commands
+ ~denote-keywords-add~, ~denote-keywords-remove~,
+ ~denote-dired-rename-files~, or related.
+
+#+findex: denote-update-dired-buffers
++ Function ~denote-update-dired-buffers~ :: Update Dired buffers of
+ variable ~denote-directory~. Also revert the current Dired buffer
+ even if it is not inside the ~denote-directory~. Note that the
+ ~denote-directory~ accepts a directory-local value for what we
+ internally refer to as "silos" ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directories for notes]]).
+
+* Troubleshoot Denote in a pristine environment
+:PROPERTIES:
+:CUSTOM_ID: h:9c4467d5-6480-4681-80fb-cd9717bf8b3b
+:END:
+
+Sometimes we get reports on bugs that may not be actually caused by
+some error in the Denote code base. To help gain insight into what
+the problem is, we need to be able to reproduce the issue in a minimum
+viable system. Below is one way to achieve this.
+
+1. Find where your =denote.el= file is stored on your filesystem.
+
+2. Assuming you have already installed the package, one way to do this
+ is to invoke =M-x find-library= and search for ~denote~. It will
+ take you to the source file. There do =M-x eval-expression=, which
+ will bring up a minibuffer prompt. At the prompt evaluate:
+
+#+begin_example emacs-lisp
+(kill-new (expand-file-name (buffer-file-name)))
+#+end_example
+
+3. The above will save the full file system path to your kill ring.
+
+4. In a terminal emulator or an =M-x shell= buffer execute:
+
+#+begin_example
+emacs -Q
+#+end_example
+
+5. This will open a new instance of Emacs in a pristine environment.
+ Only the default settings are loaded.
+
+6. In the =*scratch*= buffer of =emacs -Q=, add your configurations
+ like the following and try to reproduce the issue:
+
+#+begin_example emacs-lisp
+(require 'denote "/full/path/to/what/you/got/denote.el")
+
+;; Your configurations here
+#+end_example
+
+Then try to see if your problem still occurs. If it does, then the
+fault is with Denote. Otherwise there is something external to it
+that we need to account for. Whatever the case, this exercise helps
+us get a better sense of the specifics.
+
+* Contributing
+:PROPERTIES:
+:CUSTOM_ID: h:1ebe4865-c001-4747-a6f2-0fe45aad71cd
+:END:
+
+Denote is a GNU ELPA package. As such, any significant change to the
+code requires copyright assignment to the Free Software Foundation
+(more below).
+
+You do not need to be a programmer to contribute to this package.
+Sharing an idea or describing a workflow is equally helpful, as it
+teaches us something we may not know and might be able to cover either
+by extending Denote or expanding this manual. If you prefer to write a
+blog post, make sure you share it with us: we can add a section herein
+referencing all such articles. Everyone gets acknowledged
+([[#h:f8126820-3b59-49fa-bcc2-73bd60132bb9][Acknowledgements]]). There is no such thing as an "insignificant
+contribution"---they all matter.
+
++ Package name (GNU ELPA): ~denote~
++ Official manual: <https://protesilaos.com/emacs/denote>
++ Change log: <https://protesilaos.com/emacs/denote-changelog>
++ Git repositories:
+ + GitHub: <https://github.com/protesilaos/denote>
+ + GitLab: <https://gitlab.com/protesilaos/denote>
+
+If our public media are not suitable, you are welcome to contact me
+(Protesilaos) in private: <https://protesilaos.com/contact>.
+
+Copyright assignment is a prerequisite to sharing code. It is a simple
+process. Check the request form below (please adapt it accordingly).
+You must write an email to the address mentioned in the form and then
+wait for the FSF to send you a legal agreement. Sign the document and
+file it back to them. This could all happen via email and take about a
+week. You are encouraged to go through this process. You only need to
+do it once. It will allow you to make contributions to Emacs in
+general.
+
+#+begin_example text
+Please email the following information to assign@gnu.org, and we
+will send you the assignment form for your past and future changes.
+
+Please use your full legal name (in ASCII characters) as the subject
+line of the message.
+
+REQUEST: SEND FORM FOR PAST AND FUTURE CHANGES
+
+[What is the name of the program or package you're contributing to?]
+
+GNU Emacs
+
+[Did you copy any files or text written by someone else in these changes?
+Even if that material is free software, we need to know about it.]
+
+Copied a few snippets from the same files I edited. Their author,
+Protesilaos Stavrou, has already assigned copyright to the Free Software
+Foundation.
+
+[Do you have an employer who might have a basis to claim to own
+your changes? Do you attend a school which might make such a claim?]
+
+
+[For the copyright registration, what country are you a citizen of?]
+
+
+[What year were you born?]
+
+
+[Please write your email address here.]
+
+
+[Please write your postal address here.]
+
+
+
+
+
+[Which files have you changed so far, and which new files have you written
+so far?]
+
+#+end_example
+
+** Wishlist of what we can do to extend Denote
+:PROPERTIES:
+:CUSTOM_ID: h:044a6a0f-e382-4013-8279-8bf4e64e73c0
+:END:
+
+These are various ideas to extend Denote. Whether they should be in
+the core package or a separate extension is something we can discuss.
+I, Protesilaos, am happy to help anyone who wants to do any of this.
+
+- denote-embark.el :: Provide integration with the ~embark~ package.
+ This can be for doing something with the identifier/link at point.
+ For example, it could provide an action to produce backlinks for the
+ identifier/file we are linking to, not just the current one.
+
+- denote-transient.el :: The ~transient~ package is built into Emacs
+ 29 (Denote supports Emacs 28 though). We can use it to define an
+ alternative to what we have for the menu bar. Perhaps this interface
+ can used to toggle various options, such as to call ~denote~ with a
+ different set of prompts.
+
+- A ~denote-directories~ user option :: This can be either an
+ extension of the ~denote-directory~ (accept a list of file paths
+ value) or a new variable. The idea is to let the user define
+ separate Denote directories which do know about the presence of each
+ other (unlike silos). This way, a user can have an entry in
+ =~/Documents/notes/= link to something =~/Git/projects/= and
+ everything work as if the ~denote-directory~ is set to the =~/=
+ (with the status quo as of 2024-02-18 08:27 +0200).
+
+- Encode the day in the identifier :: The idea is to use some coded
+ reference for Monday, Tuesday, etc. instead of having the generic
+ =T= in the identifier. For example, Monday is =A= so the identifier
+ for it is something like =20240219A101522= instead of what we now
+ have as =20240219T101522=. The old method should still be supported.
+ Apart from changing a few regular expressions, this does not seem
+ too complex to me. We would need a user option to opt in to such a
+ feature. Then tweak the relevant parts. The tricky issue is to
+ define a mapping of day names to letters/symbols that works for
+ everyone. Do all countries have a seven-day week, for example? We
+ need something universally applicable here.
+
+Anything else? You are welcome to discuss these and/or add to the
+list.
+
+* Publications about Denote
+:PROPERTIES:
+:CUSTOM_ID: h:ca0c38f9-fa3e-4901-947e-1b589335781d
+:END:
+
+The Emacs community is putting Denote to great use. This section
+includes publications that show how people configure their note-taking
+setup. If you have a blog post, video, or configuration file about
+Denote, feel welcome to tell us about it ([[#h:1ebe4865-c001-4747-a6f2-0fe45aad71cd][Contributing]]).
+
++ David Wilson (SystemCrafters): /Generating a Blog Site from Denote
+ Entries/, 2022-09-09, <https://www.youtube.com/watch?v=5R7ad5xz5wo>
+
++ David Wilson (SystemCrafters): /Trying Out Prot's Denote, an Org
+ Roam Alternative?/, 2022-07-15, <https://www.youtube.com/watch?v=QcRY_rsX0yY>
+
++ Jack Baty: /Keeping my Org Agenda updated based on Denote keywords/,
+ 2022-11-30, <https://baty.net/2022/keeping-my-org-agenda-updated>
+
++ Jeremy Friesen: /Denote Emacs Configuration/, 2022-10-02,
+ <https://takeonrules.com/2022/10/09/denote-emacs-configuration/>
+
++ Jeremy Friesen: /Exploring the Denote Emacs Package/, 2022-10-01,
+ <https://takeonrules.com/2022/10/01/exploring-the-denote-emacs-package/>
+
++ Jeremy Friesen: /Migration Plan for Org-Roam Notes to Denote/,
+ 2022-10-02, <https://takeonrules.com/2022/10/02/migration-plan-for-org-roam-notes-to-denote/>
+
++ Jeremy Friesen: /Project Dispatch Menu with Org Mode Metadata,
+ Denote, and Transient/, 2022-11-19,
+ <https://takeonrules.com/2022/11/19/project-dispatch-menu-with-org-mode-metadata-denote-and-transient/>
+
++ Mohamed Suliman: /Managing a bibliography of BiBTeX entries with
+ Denote/, 2022-12-20, <https://www.scss.tcd.ie/~sulimanm/posts/denote-bibliography.html>
+
++ Peter Prevos: /Simulating Text Files with R to Test the Emacs Denote
+ Package/, 2022-07-28, <https://lucidmanager.org/productivity/testing-denote-package/>
+
++ Peter Prevos: /Emacs Writing Studio/, 2023-10-19. A configuration for authors, using Denote for taking notes, literature reviews and manage collections of images:
+ - <https://lucidmanager.org/productivity/taking-notes-with-emacs-denote/>
+ - <https://lucidmanager.org/productivity/denote-explore/>
+ - <https://lucidmanager.org/productivity/bibliographic-notes-in-emacs-with-citar-denote/>
+ - <https://lucidmanager.org/productivity/using-emacs-image-dired/>
+
++ Stefan Thesing: /Denote as a Zettelkasten/, 2023-03-02,
+ <https://www.thesing-online.de/blog/denote-as-a-zettelkasten>
+
++ Summer Emacs: /An explanation of how I use Emacs/, 2023-05-04,
+ <https://github.com/summeremacs/howiuseemacs/blob/main/full-explanation-of-how-i-use-emacs.org>
+
+* Alternatives to Denote
+:PROPERTIES:
+:CUSTOM_ID: h:dbb51a1b-90b8-48e8-953c-e2fb3e36981e
+:END:
+
+What follows is a list of Emacs packages for note-taking. I
+(Protesilaos) have not used any of them, as I was manually applying my
+file-naming scheme beforehand and by the time those packages were
+available I was already hacking on the predecessor of Denote as a means
+of learning Emacs Lisp (a package which I called "Unassuming Sidenotes
+of Little Significance", aka "USLS" which is pronounced as "U-S-L-S" or
+"useless"). As such, I cannot comment at length on the differences
+between Denote and each of those packages, beside what I gather from
+their documentation.
+
++ [[https://github.com/org-roam/org-roam][org-roam]] :: The de facto standard in the Emacs milieu---and rightly
+ so! It has a massive community, is featureful, and should be an
+ excellent companion to anyone who is invested in the Org ecosystem
+ and/or knows what "Roam" is (I don't). It has been explained to me
+ that Org Roam uses a database to store a cache about your notes. It
+ otherwise uses standard Org files. The cache helps refer to the same
+ node through aliases which can provide lots of options. Personally, I
+ follow a single-topic-per-note approach, so anything beyond that is
+ overkill. If the database is only for a cache, then maybe that has no
+ downside, though I am careful with any kind of specialised program as
+ it creates a dependency. If you ask me about database software in
+ particular, I have no idea how to use one, let alone debug it or
+ retrieve data from it if something goes awry (I could learn, but that
+ is beside the point).
+
++ [[https://github.com/localauthor/zk][zk (or zk.el)]] :: Reading its documentation makes me think that this is
+ Denote's sibling---the two projects have a lot of things in common,
+ including the preference to rely on plain files and standard tools.
+ The core difference is that Denote has a strict file-naming scheme.
+ Other differences in available features are, in principle, matters of
+ style or circumstance: both packages can have them. As its initials
+ imply, ZK enables a zettelkasten-like workflow. It does not enforce
+ it though, letting the user adapt the method to their needs and
+ requirements.
+
++ [[https://github.com/ymherklotz/emacs-zettelkasten][zettelkasten]] :: This is another one of Denote's relatives, at least
+ insofar as the goal of simplicity is concerned. The major difference
+ is that according to its documentation "the name of the file that is
+ created is just a unique ID". This is not consistent with our
+ file-naming scheme which is all about making sense of your files by
+ their name alone and being able to visually parse a listing of them
+ without any kind of specialised tool (e.g. =ls -l= or =ls -C= on the
+ command-line from inside the ~denote-directory~ give you a
+ human-readable set of files names, while =find * -maxdepth 0 -type f=
+ is another approach).
+
++ [[https://github.com/EFLS/zetteldeft][zetteldeft]] :: This is a zettelkasten note-taking system built on top
+ of the =deft= package. Deft provides a search interface to a
+ directory, in this case the one holding the user's =zetteldeft= notes.
+ Denote has no such dependency and is not opinionated about how the
+ user prefers to search/access their notes: use Dired, Grep, the
+ =consult= package, or whatever else you already have set up for all
+ things Emacs, not just your notes.
+
+Searching through =M-x list-packages= for "zettel" brings up more
+matches. =zetteldesk= is an extension to Org Roam and, as such, I
+cannot possibly know what Org Roam truly misses and what the added-value
+of this package is. =neuron-mode= builds on top of an external program
+called =neuron=, which I have never used.
+
+Searching for "note" gives us a few more results. =notes-mode= has
+precious little documentation and I cannot tell what it actually does
+(as I said in my presentation for LibrePlanet 2022, inadequate docs are
+a bug). =side-notes= differs from what we try to do with Denote, as it
+basically gives you the means to record your thoughts about some other
+project you are working on and keep them on the side: so it and Denote
+should not be mutually exclusive.
+
+If I missed something, please let me know.
+
+** Alternative implementations and further reading
+:PROPERTIES:
+:CUSTOM_ID: h:188c0986-f2fa-444f-b493-5429356e75cf
+:END:
+
+This section covers blog posts and implementations from the Emacs
+community about the topic of note-taking and file organization. They
+may refer to some of the packages covered in the previous section or
+provide their custom code ([[#h:dbb51a1b-90b8-48e8-953c-e2fb3e36981e][Alternatives to Denote]]). The list is
+unsorted.
+
++ José Antonio Ortega Ruiz (aka "jao") explains a note-taking method
+ that is simple like Denote but differs in other ways. An interesting
+ approach overall: https://jao.io/blog/simple-note-taking.html.
+
++ Jethro Kuan (the main =org-roam= developer) explains their note-taking
+ techniques: https://jethrokuan.github.io/org-roam-guide/. Good ideas
+ all round, regardless of the package/code you choose to use.
+
++ Karl Voit's tools [[https://github.com/novoid/date2name][date2name]], [[https://github.com/novoid/filetags/][filetags]], [[https://github.com/novoid/appendfilename/][appendfilename]], and
+ [[https://github.com/novoid/move2archive][move2archive]] provide a Python-based implementation to organize
+ individual files which do not require Emacs. His approach ([[https://karl-voit.at/managing-digital-photographs/][blog
+ post]] and his [[https://www.youtube.com/watch?v=rckSVmYCH90][presentation at GLT18]]) has been complemented by [[https://github.com/novoid/memacs][memacs]]
+ to process e.g., the date of creation of photographs, or the log of
+ a phone call in a format compatible to org.
+
+[ Development note: help expand this list. ]
+
+* Frequently Asked Questions
+:PROPERTIES:
+:CUSTOM_ID: h:da2944c6-cde6-4c65-8f2d-579305a159bb
+:END:
+
+I (Protesilaos) answer some questions I have received or might get. It
+is assumed that you have read the rest of this manual: I will not go
+into the specifics of how Denote works.
+
+** Why develop Denote when PACKAGE already exists?
+:PROPERTIES:
+:CUSTOM_ID: h:b875450a-ae22-4899-ac23-c10fa9c279bb
+:END:
+
+I wrote Denote because I was using a variant of Denote's file-naming
+scheme before I was even an Emacs user (I switched to Emacs from
+Tmux+Vim+CLI in the summer of 2019). I was originally inspired by
+Jekyll, the static site generator, which I started using for my website
+in 2016 (was on WordPress before). Jekyll's files follow the
+=YYYY-MM-DD-TITLE.md= pattern. I liked its efficiency relative to the
+unstructured mess I had before. Eventually, I started using that scheme
+outside the confines of my website's source code. Over time I refined
+it and here we are.
+
+Note-taking is something I take very seriously, as I am a prolific
+writer (just check my website, which only reveals the tip of the
+iceberg). As such, I need a program that does exactly what I want and
+which I know how to extend. I originally tried to use Org capture
+templates to create new files with a Denote-style file-naming scheme but
+never managed to achieve it. Maybe because ~org-capture~ has some
+hard-coded assumptions or I simply am not competent enough to hack on
+core Org facilities. Whatever the case, an alternative was in order.
+
+The existence of PACKAGE is never a good reason for me not to conduct my
+own experiments for recreational, educational, or practical purposes.
+When the question arises of "why not contribute to PACKAGE instead?" the
+answer is that without me experimenting in the first place, I would lack
+the skills for such a task. Furthermore, contributing to another
+package does not guarantee I get what I want in terms of workflow.
+
+Whether you should use Denote or not is another matter altogether:
+choose whatever you want.
+
+** Why not rely exclusively on Org?
+:PROPERTIES:
+:CUSTOM_ID: h:b9831849-5c71-484e-b444-bac19cc13151
+:END:
+
+I think Org is one of Emacs' killer apps. I also believe it is not the
+right tool for every job. When I write notes, I want to focus on
+writing. Nothing more. I thus have no need for stuff like org-babel,
+scheduling to-do items, clocking time, and so on. The more "mental
+dependencies" you add to your workflow, the heavier the burden you carry
+and the less focused you are on the task at hand: there is always that
+temptation to tweak the markup, tinker with some syntactic construct,
+obsess about what ought to be irrelevant to writing as such.
+
+In technical terms, I also am not fond of Org's code base (I understand
+why it is the way it is---just commenting on the fact). Ever tried to
+read it? You will routinely find functions that are tens-to-hundreds of
+lines long and have all sorts of special casing. As I am not a
+programmer and only learnt to write Elisp through trial and error, I
+have no confidence in my ability to make Org do what I want at that
+level, hence =denote= instead of =org-denote= or something.
+
+Perhaps the master programmer is one who can deal with complexity and
+keep adding to it. I am of the opposite view, as language---code
+included---is at its communicative best when it is clear and accessible.
+
+Make no mistake: I use Org for the agenda and also to write technical
+documentation that needs to be exported to various formats, including
+this very manual.
+
+** Why care about Unix tools when you use Emacs?
+:PROPERTIES:
+:CUSTOM_ID: h:da1e2469-8f04-450b-a379-a854efa80a36
+:END:
+
+My notes form part of my longer-term storage. I do not want to have to
+rely on a special program to be able to read them or filter them. Unix
+is universal, at least as far as I am concerned.
+
+Denote streamlines some tasks and makes things easier in general, which
+is consistent with how Emacs provides a layer of interactivity on top of
+Unix. Still, Denote's utilities can, in principle, be implemented as
+POSIX shell scripts (minus the Emacs-specific parts like fontification
+in Dired or the buttonization of links).
+
+Portability matters. For example, in the future I might own a
+smartphone, so I prefer not to require Emacs, Org, or some other
+executable to access my files on the go.
+
+Furthermore, I might want to share those files with someone. If I make
+Emacs a requirement, I am limiting my circle to a handful of relatively
+advanced users.
+
+Please don't misinterpret this: I am using Emacs full-time for my
+computing and maintain a growing list of packages for it. This is just
+me thinking long-term.
+
+** Why many small files instead of few large ones?
+:PROPERTIES:
+:CUSTOM_ID: h:7d2e7b8a-d484-4c1d-8688-17f70f242ad7
+:END:
+
+I have read that Org favours the latter method. If true, I strongly
+disagree with it because of the implicit dependency it introduces and
+the way it favours machine-friendliness over human-readability in terms
+of accessing information. Notes are long-term storage. I might want to
+access them on (i) some device with limited features, (ii) print on
+paper, (iii) share with another person who is not a tech wizard.
+
+There are good arguments for few large files, but all either prioritize
+machine-friendliness or presuppose the use of sophisticated tools like
+Emacs+Org.
+
+Good luck using =less= on a generic TTY to read a file with a zillion
+words, headings, sub-headings, sub-sub-headings, property drawers, and
+other constructs! You will not get the otherwise wonderful folding of
+headings the way you do in Emacs---do not take such features for
+granted.
+
+My point is that notes should be atomic to help the user---and
+potentially the user's family, friends, acquaintances---make sense of
+them in a wide range of scenaria. The more program-agnostic your file
+is, the better for you and/or everyone else you might share your
+writings with.
+
+Human-readability means that we optimize for what matters to us. If (a)
+you are the only one who will ever read your notes, (b) always have
+access to good software like Emacs+Org, (c) do not care about printing
+on paper, then Denote's model is not for you. Maybe you need to tweak
+some ~org-capture~ template to append a new entry to one mega file (I do
+that for my Org agenda, by the way, as I explained before about using
+the right tool for the job).
+
+** Does Denote perform well at scale?
+:PROPERTIES:
+:CUSTOM_ID: h:863f812a-aac7-42ea-83b3-fbbdb58e08d7
+:END:
+
+Denote does not do anything fancy and has no special requirements: it
+uses standard tools to accomplish ordinary tasks. If Emacs can cope
+with lots of files, then that is all you need to know: Denote will work.
+
+To put this to the test, Peter Prevos is running simulations with R that
+generate large volumes of notes. You can read the technicalities here:
+<https://lucidmanager.org/productivity/testing-denote-package/>.
+Excerpt:
+
+#+begin_quote
+Using this code I generated ten thousands notes and used this to test
+the Denote package to see it if works at a large scale. This tests shows
+that Prot's approach is perfectly capable of working with thousands of
+notes.
+#+end_quote
+
+Of course, we are always prepared to make refinements to the code, where
+necessary, without compromising on the project's principles.
+
+** I add TODOs to my notes; will many files slow down the Org agenda?
+:PROPERTIES:
+:CUSTOM_ID: h:63c2f8d4-79ed-4c55-b3ef-e048a05802c0
+:END:
+
+Yes, many files will slow down the agenda due to how that works. Org
+collects all files specified in the ~org-agenda-files~, searches through
+their contents for timestamped entries, and then loops through all days
+to determine where each entry belongs. The more days and more files,
+the longer it takes to build the agenda. Doing this with potentially
+hundreds of files will have a noticeable impact on performance.
+
+This is not a deficiency of Denote. It happens with generic Org files.
+The way the agenda is built is heavily favoring the use of a single file
+that holds all your timestamped entries (or at least a few such files).
+Tens or hundreds of files are inefficient for this job. Plus doing so
+has the side-effect of making Emacs open all those files, which you
+probably do not need.
+
+If you want my opinion though, be more forceful with the separation of
+concerns. Decouple your knowledge base from your ephemeral to-do list:
+Denote (and others) can be used for the former, while you let standard
+Org work splendidly for the latter---that is what I do, anyway.
+
+Org has a powerful linking facility, whether you use ~org-store-link~ or
+do it via an ~org-capture~ template. If you want a certain note to be
+associated with a task, just store the task in a single =tasks.org= (or
+however you name it) and link to the relevant context.
+
+Do not mix your knowledge base with your to-do items. If you need help
+figuring out the specifics of this workflow, you are welcome to ask for
+help in our relevant channels ([[#h:1ebe4865-c001-4747-a6f2-0fe45aad71cd][Contributing]]).
+
+** I want to sort by last modified in Dired, why won't Denote let me?
+:PROPERTIES:
+:CUSTOM_ID: h:a7fd5e0a-78f7-434e-aa2e-e150479c16e2
+:END:
+
+Denote does not control how Dired sorts files. I encourage you to read
+the manpage of the =ls= executable. It will help you in general, while
+it applies to Emacs as well via Dired. The gist is that you can update
+the =ls= flags that Dired uses on-the-fly: type =C-u M-x
+dired-sort-toggle-or-edit= (=C-u s= by default) and append
+=--sort=time= at the prompt. To reverse the order, add the =-r= flag.
+The user option ~dired-listing-switches~ sets your default preference.
+
+For an on-demand sorted and filtered Dired listing of Denote files,
+use the command ~denote-sort-dired~ ([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Sort files by component]]).
+
+** How do you handle the last modified case?
+:PROPERTIES:
+:CUSTOM_ID: h:764b5e87-cd22-4937-b5fc-af3892d6b3d8
+:END:
+
+Denote does not insert any meta data or heading pertaining to edits in
+the file. I am of the view that these either do not scale well or are
+not descriptive enough. Suppose you use a "lastmod" heading with a
+timestamp: which lines where edited and what did the change amount to?
+
+This is where an external program can be helpful. Use a Version Control
+System, such as Git, to keep track of all your notes. Every time you
+add a new file, record the addition. Same for post-creation edits.
+Your VCS will let you review the history of those changes. For
+instance, Emacs' built-in version control framework has a command that
+produces a log of changes for the current file: =M-x vc-print-log=,
+bound to =C-x v l= by default. From there one can access the
+corresponding diff output (use =M-x describe-mode= (=C-h m=) in an
+unfamiliar buffer to learn more about it). With Git in particular,
+Emacs users have the option of the all-round excellent =magit= package.
+
+In short: let Denote (or equivalent) create notes and link between them,
+the file manager organise and provide access to files, search programs
+deal with searching and narrowing, and version control software handle
+the tracking of changes.
+
+** Why are some Org links opening outside Emacs?
+:PROPERTIES:
+:CUSTOM_ID: h:4f354db1-aa78-47fd-ac60-c3d1e0f0b0a4
+:END:
+
+Org has its own mechanism to determine how best to open a link. This
+affects the =file:= link type, but also the =denote:= one (which is
+designed to be as close to =file:= as possible).
+
+When following a link, Org usually displays the data in an Emacs
+buffer, though it might launch an external application instead. The
+idea is to use a specialised program when that is relevant, such as to
+display a video. Though there can be scenaria the user does not like,
+such as when Org decides to load =.md= or =.html= files with an
+external app. To compound the problem, users can name any file type
+using the Denote file-naming scheme, including images, PDFs, videos,
+and more ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]).
+
+To instruct Org to stay in Emacs for such cases, the user needs to
+modify the variable ~org-file-apps~, which is not specific to Denote.
+As one use-case, ~org-file-apps~ associates a regular expression to
+match file names with a method on how to display them (do =M-x
+describe-variable= and then search for ~org-file-apps~ to read its
+documentation). Thus, the user can use something like the following
+in their Org or Denote configuration:
+
+#+begin_src emacs-lisp
+;; Tell Org to use Emacs when opening files that end in .md
+(add-to-list 'org-file-apps '("\\.md\\'" . emacs))
+
+;; Do the same for .html
+(add-to-list 'org-file-apps '("\\.html\\'" . emacs))
+#+end_src
+
+Each of these adds a new entry to the existing value of that user
+option. Replace =md= or =html= with the desired file type extension.
+
+** Speed up backlinks' or query links' buffer creation?
+:PROPERTIES:
+:CUSTOM_ID: h:893eec49-d7be-4603-bcff-fcc247244011
+:END:
+
+Denote leverages the built-in =xref= library to search for the
+identifier of the current file and return any links to it. For users
+of Emacs version 28 or higher, there exists a user option to specify
+the program that performs this search: ~xref-search-program~. The
+default is =grep=, which can be slow, though one may opt for =ugrep=,
+=ripgrep=, or even specify something else (read the doc string of that
+user option for the details).
+
+Try either for these for better results:
+
+#+begin_src emacs-lisp
+(setq xref-search-program 'ripgrep)
+
+;; OR
+
+(setq xref-search-program 'ugrep)
+#+end_src
+
+To use whatever executable is available on your system, use something
+like this:
+
+#+begin_src emacs-lisp
+;; Prefer ripgrep, then ugrep, and fall back to regular grep.
+(setq xref-search-program
+ (cond
+ ((or (executable-find "ripgrep")
+ (executable-find "rg"))
+ 'ripgrep)
+ ((executable-find "ugrep")
+ 'ugrep)
+ (t
+ 'grep)))
+#+end_src
+
+** Why do I get "Search failed with status 1" when I search for backlinks?
+:PROPERTIES:
+:CUSTOM_ID: h:42f6b07e-5956-469a-8294-17f9cf62eb2b
+:END:
+
+Denote uses [[info:emacs#Xref][Emacs' Xref]] to find backlinks. Xref requires ~xargs~ and
+one of ~grep~ or ~ripgrep~, depending on your configuration.
+
+This is usually not an issue on *nix systems, but the necessary
+executables are not available on Windows Emacs distributions. Please
+ensure that you have both ~xargs~ and either ~grep~ or ~ripgrep~
+available within your ~PATH~ environment variable.
+
+If you have ~git~ on Windows installed, then you may use the following
+code (adjust the git's installation path if necessary):
+#+begin_src emacs-lisp
+ (setenv "PATH" (concat (getenv "PATH") ";" "C:\\Program Files\\Git\\usr\\bin"))
+#+end_src
+
+** Why do I get a double =#+title= in Doom Emacs?
+:PROPERTIES:
+:CUSTOM_ID: h:0f737b7d-40e6-46a7-b1db-117c0ffcbfef
+:END:
+
+Doom Emacs provides a set of bespoke templates for Org. One of those
+prefills any new Org file with a =#+title= field. So when Denote
+creates a new Org file and inserts front matter to it, it inevitably
+adds an extra title to the existing one.
+
+This is not a Denote problem. We can only expect a new file to be
+empty by default. Check how to disable the relevant module in your
+Doom Emacs configuration file.
+
+* Acknowledgements
+:PROPERTIES:
+:CUSTOM_ID: h:f8126820-3b59-49fa-bcc2-73bd60132bb9
+:END:
+#+cindex: Contributors
+
+Denote is meant to be a collective effort. Every bit of help matters.
+
++ Author/maintainer :: Protesilaos Stavrou.
+
++ Contributions to code or the manual :: Abdul-Lateef Haji-Ali, Abin
+ Simon, Adam Růžička, Alan Schmitt, Alexandre Rousseau, Ashton
+ Wiersdorf, Aziz, Benjamin Kästner, Bruno Boal, Charanjit Singh,
+ Claudio Migliorelli, Clemens Radermacher, Colin McLear, Damien
+ Cassou, Eduardo Grajeda, Elias Storms, Eshel Yaron, Florian, Glenna
+ D., Graham Marlow, Hilde Rhyne, Ivan Sokolov, Jack Baty, Jakub
+ Szczerbowski, Jean-Charles Bagneris, Jean-Philippe Gagné Guay,
+ Jianwei Hou, Joseph Turner, Jürgen Hötzel, Kaushal Modi, Kai von
+ Fintel, Kierin Bell, Kostas Andreadis, Kristoffer Balintona, Kyle
+ Meyer, Laurent Gatto, Lucas Quintana, Maikol Solis, Marc Fargas,
+ Matthew Lemon, Noboru Ota (nobiot), Norwid Behrnd, Octavian, Peter
+ Prevos, Philip Kaludercic, Quiliro Ordóñez, Stephen R. Kifer, Stefan
+ Monnier, Stefan Thesing, Thibaut Benjamin, Tomasz Hołubowicz,
+ TomoeMami , Vedang Manerikar, Wesley Harvey, Zhenxu Xu, arsaber101,
+ bryanrinders, eum3l, ezchi, jarofromel, leinfink (Henrik), l-o-l-h
+ (Lincoln), mattyonweb, maxbrieiev, mentalisttraceur, pmenair,
+ relict007, skissue.
+
++ Ideas and/or user feedback :: Abin Simon, Aditya Yadav, Alan
+ Schmitt, Aleksandr Vityazev, Alex Griffin, Alex Hirschfeld, Alexis
+ Purslane, Alfredo Borrás, Alp Eren Kose, André Bering, Ashton
+ Wiersdorf, Benjamin Kästner, Claudio Migliorelli, Claudiu Tănăselia,
+ Colin McLear, Cosmin-Octavian C, Damien Cassou, Elias Storms,
+ Federico Stilman, Florian, Frédéric Willem Frank Ehmsen, Glenna D.,
+ Guo Yong, Hanspeter Gisler Harold Ollivier, IceAsteroid, Jack Baty,
+ Jay Rajput, Jean-Charles Bagneris, Jeff Valk, Jens Östlund, Jeremy
+ Friesen, Jonathan Sahar, Johan Bolmsjö, Jonas Großekathöfer,
+ Jousimies, Juanjo Presa, Julian Hoch, Kai von Fintel, Kaushal Modi,
+ Kolmas, Lukas C. Bossert, M. Hadi Timachi, Maikol Solis, Mark Olson,
+ Mirko Hernandez, Niall Dooley, Nick Bell, Oliver Epper, Paul van
+ Gelder, Peter Prevos, Peter Smith, Riccardo Giannitrapani, Samuel W.
+ Flint, Sergio Rey, Suhail Singh, Shreyas Ragavan, Stefan Thesing,
+ Summer Emacs, Sven Seebeck, Taoufik, TJ Stankus, Vick (VicZz),
+ Viktor Haag, Vineet C. Kulkarni, Wade Mealing, Wilf, Yi Liu, Ypot,
+ atanasj, azegas, babusri, bdillahu, coherentstate, doolio, duli,
+ drcxd, elge70, elliottw, fingerknight, hpgisler, hyperfocus1337,
+ jtpavlock, juh, leafarbelm, mentalisttraceur, pRot0ta1p, rbenit68,
+ relict007, sarcom-sar, sienic, skissue, sundar bp,
+ yetanotherfossman, zadca123
+
+Special thanks to Peter Povinec who helped refine the file-naming
+scheme, which is the cornerstone of this project.
+
+Special thanks to Jean-Philippe Gagné Guay for the numerous
+contributions to the code base.
+
+* GNU Free Documentation License
+:PROPERTIES:
+:APPENDIX: t
+:CUSTOM_ID: h:2d84e73e-c143-43b5-b388-a6765da974ea
+:END:
+
+#+texinfo: @include doclicense.texi
+
+#+begin_export html
+<pre>
+
+ GNU Free Documentation License
+ Version 1.3, 3 November 2008
+
+
+ Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
+ <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall
+subject (or to related matters) and contains nothing that could fall
+directly within that overall subject. (Thus, if the Document is in
+part a textbook of mathematics, a Secondary Section may not explain
+any mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+The "publisher" means any person or entity that distributes copies of
+the Document to the public.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no
+other conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to
+give them a chance to provide you with an updated version of the
+Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other
+documents released under this License, and replace the individual
+copies of this License in the various documents with a single copy
+that is included in the collection, provided that you follow the rules
+of this License for verbatim copying of each of the documents in all
+other respects.
+
+You may extract a single document from such a collection, and
+distribute it individually under this License, provided you insert a
+copy of this License into the extracted document, and follow this
+License in all other respects regarding verbatim copying of that
+document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense, or distribute it is void, and
+will automatically terminate your rights under this License.
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, receipt of a copy of some or all of the same material does
+not give you any rights to use it.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions of the
+GNU Free Documentation License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in
+detail to address new problems or concerns. See
+https://www.gnu.org/licenses/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation. If the Document
+specifies that a proxy can decide which future versions of this
+License can be used, that proxy's public statement of acceptance of a
+version permanently authorizes you to choose that version for the
+Document.
+
+11. RELICENSING
+
+"Massive Multiauthor Collaboration Site" (or "MMC Site") means any
+World Wide Web server that publishes copyrightable works and also
+provides prominent facilities for anybody to edit those works. A
+public wiki that anybody can edit is an example of such a server. A
+"Massive Multiauthor Collaboration" (or "MMC") contained in the site
+means any set of copyrightable works thus published on the MMC site.
+
+"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
+license published by Creative Commons Corporation, a not-for-profit
+corporation with a principal place of business in San Francisco,
+California, as well as future copyleft versions of that license
+published by that same organization.
+
+"Incorporate" means to publish or republish a Document, in whole or in
+part, as part of another Document.
+
+An MMC is "eligible for relicensing" if it is licensed under this
+License, and if all works that were first published under this License
+somewhere other than this MMC, and subsequently incorporated in whole or
+in part into the MMC, (1) had no cover texts or invariant sections, and
+(2) were thus incorporated prior to November 1, 2008.
+
+The operator of an MMC Site may republish an MMC contained in the site
+under CC-BY-SA on the same site at any time before August 1, 2009,
+provided the MMC is eligible for relicensing.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.3
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+</pre>
+#+end_export
+
+#+html: <!--
+
+* Indices
+:PROPERTIES:
+:CUSTOM_ID: h:dd530040-de9d-4f2b-8dfd-d8b8f14c058e
+:END:
+
+** Function index
+:PROPERTIES:
+:INDEX: fn
+:CUSTOM_ID: h:317b8c20-6dc1-4390-a20a-d01d75a48ccb
+:END:
+
+** Variable index
+:PROPERTIES:
+:INDEX: vr
+:CUSTOM_ID: h:2f69d4fe-0804-4f7f-aa57-4e03e7f20d98
+:END:
+
+** Concept index
+:PROPERTIES:
+:INDEX: cp
+:CUSTOM_ID: h:10365e44-2fc0-4b66-a613-682fea09ee68
+:END:
+
+#+html: -->
blob - /dev/null
blob + d19a789ff27c7479ccb21f61f543940c0edde9c4 (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/denote-autoloads.el
+;;; denote-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
+;; Generated by the `loaddefs-generate' function.
+
+;; This file is part of GNU Emacs.
+
+;;; Code:
+
+(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
+
+
+
+;;; Generated autoloads from denote.el
+
+ (put 'denote-directory 'safe-local-variable (lambda (val) (or (stringp val) (eq val 'local) (eq val 'default-directory))))
+ (put 'denote-known-keywords 'safe-local-variable #'listp)
+ (put 'denote-infer-keywords 'safe-local-variable (lambda (val) (or val (null val))))
+(autoload 'denote-sort-files "denote" "\
+Returned sorted list of Denote FILES.
+
+With COMPONENT as a symbol among `denote-sort-components',
+sort files based on the corresponding file name component.
+
+With COMPONENT as the symbol of a function, use it to perform the
+sorting. In this case, the function is called with two arguments, as
+described by `sort'.
+
+With COMPONENT as a nil value keep the original date-based
+sorting which relies on the identifier of each file name.
+
+With optional REVERSE as a non-nil value, reverse the sort order.
+
+(fn FILES COMPONENT &optional REVERSE)")
+(autoload 'denote-sort-dired "denote" "\
+Produce Dired buffer with sorted files from variable `denote-directory'.
+When called interactively, prompt for FILES-MATCHING-REGEXP and,
+depending on the value of the user option `denote-sort-dired-extra-prompts',
+also prompt for SORT-BY-COMPONENT, REVERSE, and EXCLUDE-REGEXP.
+
+1. FILES-MATCHING-REGEXP limits the list of Denote files to
+ those matching the provided regular expression.
+
+2. SORT-BY-COMPONENT sorts the files by their file name component (one
+ among `denote-sort-components'). If it is nil, sorting is performed
+ according to the user option `denote-sort-dired-default-sort-component',
+ falling back to the identifier.
+
+3. REVERSE is a boolean to reverse the order when it is a non-nil value.
+ If `denote-sort-dired-extra-prompts' is configured to skip this
+ prompt, then the sorting is done according to the user option
+ `denote-sort-dired-default-reverse-sort', falling back to
+ nil (i.e. no reverse sort).
+
+4. EXCLUDE-REGEXP excludes the files that match the given regular
+ expression. This is done after FILES-MATCHING-REGEXP and
+ OMIT-CURRENT have been applied.
+
+When called from Lisp, the arguments are a string, a symbol among
+`denote-sort-components', a non-nil value, and a string, respectively.
+
+(fn FILES-MATCHING-REGEXP SORT-BY-COMPONENT REVERSE EXCLUDE-REGEXP)" t)
+(autoload 'denote "denote" "\
+Create a new note with the appropriate metadata and file name.
+
+Run the `denote-after-new-note-hook' after creating the new note and
+return its path. Before returning the path, determine what needs to be
+done to the buffer, in accordance with the user option `denote-kill-buffers'.
+
+When called interactively, the metadata and file name are prompted
+according to the value of `denote-prompts'.
+
+When called from Lisp, all arguments are optional.
+
+- TITLE is a string or a function returning a string.
+
+- KEYWORDS is a list of strings. The list can be empty or the
+ value can be set to nil.
+
+- FILE-TYPE is a symbol among those described in the user option
+ `denote-file-type'.
+
+- DIRECTORY is a string representing the path to either the
+ value of the variable `denote-directory' or a subdirectory
+ thereof. The subdirectory must exist: Denote will not create
+ it. If DIRECTORY does not resolve to a valid path, the
+ variable `denote-directory' is used instead.
+
+- DATE is a string representing a date like 2022-06-30 or a date
+ and time like 2022-06-16 14:30. A nil value or an empty string
+ is interpreted as the `current-time'.
+
+- 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.
+
+- SIGNATURE is a string or a function returning a string.
+
+(fn &optional TITLE KEYWORDS FILE-TYPE DIRECTORY DATE TEMPLATE SIGNATURE)" t)
+(autoload 'denote-type "denote" "\
+Create note while prompting for a file type.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `file-type' prompt appended to its existing prompts." t)
+(function-put 'denote-type 'interactive-only 't)
+(autoload 'denote-date "denote" "\
+Create note while prompting for a date.
+
+The date can be in YEAR-MONTH-DAY notation like 2022-06-30 or
+that plus the time: 2022-06-16 14:30. When the user option
+`denote-date-prompt-use-org-read-date' is non-nil, the date
+prompt uses the more powerful Org+calendar system.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `date' prompt appended to its existing prompts." t)
+(function-put 'denote-date 'interactive-only 't)
+(autoload 'denote-subdirectory "denote" "\
+Create note while prompting for a subdirectory.
+
+Available candidates include the value of the variable
+`denote-directory' and any subdirectory thereof.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `subdirectory' prompt appended to its existing prompts." t)
+(function-put 'denote-subdirectory 'interactive-only 't)
+(autoload 'denote-template "denote" "\
+Create note while prompting for a template.
+
+Available candidates include the keys in the `denote-templates'
+alist. The value of the selected key is inserted in the newly
+created note after the front matter.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `template' prompt appended to its existing prompts." t)
+(function-put 'denote-template 'interactive-only 't)
+(autoload 'denote-signature "denote" "\
+Create note while prompting for a file signature.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `signature' prompt appended to its existing prompts." t)
+(function-put 'denote-signature 'interactive-only 't)
+(autoload 'denote-region "denote" "\
+Call `denote' and insert therein the text of the active region.
+
+Note that, currently, `denote-save-buffers' and
+`denote-kill-buffers' are NOT respected. The buffer is not
+saved or killed at the end of `denote-region'." t)
+(function-put 'denote-region 'interactive-only 't)
+(autoload 'denote-open-or-create "denote" "\
+Visit TARGET file in variable `denote-directory'.
+If file does not exist, invoke `denote' to create a file. In that case,
+use the last input at the file prompt as the default value of the title
+prompt.
+
+(fn TARGET)" t)
+(autoload 'denote-open-or-create-with-command "denote" "\
+Like `denote-open-or-create' but use one of the `denote-commands-for-new-notes'." t)
+(function-put 'denote-open-or-create-with-command 'interactive-only 't)
+(autoload 'denote-rename-file "denote" "\
+Rename file and update existing front matter if appropriate.
+
+Always rename the file where it is located in the file system:
+never move it to another directory.
+
+If in Dired, consider FILE to be the one at point, else the
+current file, else prompt with minibuffer completion for one.
+When called from Lisp, FILE is a file system path represented as
+a string.
+
+If FILE has a Denote-compliant identifier, retain it while
+updating components of the file name referenced by the user
+option `denote-prompts'. By default, these are the TITLE and
+KEYWORDS. The SIGNATURE is another one. When called from Lisp,
+TITLE and SIGNATURE are strings, while KEYWORDS is a list of
+strings.
+
+If there is no identifier, create an identifier based on the
+following conditions:
+
+1. If the `denote-prompts' includes an entry for date prompts,
+ then prompt for DATE and take its input to produce a new
+ identifier. For use in Lisp, DATE must conform with
+ `denote-valid-date-p'.
+
+2. If DATE is nil (e.g. when `denote-prompts' does not include a
+ date entry), use the file attributes to determine the last
+ modified date of FILE and format it as an identifier.
+
+3. As a fallback, derive an identifier from the current date and
+ time.
+
+4. At any rate, if the resulting identifier is not unique among
+ the files in the variable `denote-directory', increment it
+ such that it becomes unique.
+
+In interactive use, and assuming `denote-prompts' includes a
+title entry, make the TITLE prompt have prefilled text in the
+minibuffer that consists of the current title of FILE. The
+current title is either retrieved from the front matter (such as
+the #+title in Org) or from the file name.
+
+Do the same for the SIGNATURE prompt, subject to `denote-prompts',
+by prefilling the minibuffer with the current signature of FILE,
+if any.
+
+Same principle for the KEYWORDS prompt: convert the keywords in
+the file name into a comma-separated string and prefill the
+minibuffer with it (the KEYWORDS prompt accepts more than one
+keywords, each separated by a comma, else the `crm-separator').
+
+For all prompts, interpret an empty input as an instruction to
+remove that file name component. For example, if a TITLE prompt
+is available and FILE is 20240211T093531--some-title__keyword1.org
+then rename FILE to 20240211T093531__keyword1.org.
+
+In interactive use, if there is no entry for a file name
+component in `denote-prompts', keep it as-is.
+
+When called from Lisp, the special symbol `keep-current' can be
+used for the TITLE, KEYWORDS, SIGNATURE and DATE parameters to
+keep them as-is.
+
+[ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the
+ empty minibuffer contents as they are, though popular packages
+ like `vertico' use the first available completion candidate
+ instead. For `vertico', the user must either move one up to
+ select the prompt and then type RET there with empty contents,
+ or use the command `vertico-exit-input' with empty contents.
+ That Vertico command is bound to M-RET as of this writing on
+ 2024-02-13 08:08 +0200. ]
+
+As a final step, ask for confirmation, showing the difference
+between old and new file names. Do not ask for confirmation if
+the user option `denote-rename-confirmations' does not contain
+the symbol `modify-file-name'.
+
+If FILE has front matter for TITLE and KEYWORDS, ask to rewrite
+their values in order to reflect the new input, unless
+`denote-rename-confirmations' lacks `rewrite-front-matter'. When
+the `denote-save-buffers' is nil (the default), do not save the
+underlying buffer, thus giving the user the option to
+double-check the result, such as by invoking the command
+`diff-buffer-with-file'. The rewrite of the TITLE and KEYWORDS
+in the front matter should not affect the rest of the front
+matter.
+
+If the file does not have front matter but is among the supported file
+types (per the user option `denote-file-type'), add front matter to the
+top of it and leave the buffer unsaved for further inspection. Save the
+buffer if `denote-save-buffers' is non-nil.
+
+When `denote-kill-buffers' is t or `on-rename', kill the buffer
+if it was not already being visited before the rename operation.
+
+For the front matter of each file type, refer to the variables:
+
+- `denote-org-front-matter'
+- `denote-text-front-matter'
+- `denote-toml-front-matter'
+- `denote-yaml-front-matter'
+
+Construct the file name in accordance with the user option
+`denote-file-name-components-order'.
+
+Run the `denote-after-rename-file-hook' after renaming FILE.
+
+This command is intended to (i) rename Denote files, (ii) convert
+existing supported file types to Denote notes, and (ii) rename
+non-note files (e.g. PDF) that can benefit from Denote's
+file-naming scheme.
+
+For a version of this command that works with multiple files
+one-by-one, use `denote-dired-rename-files'.
+
+(fn FILE TITLE KEYWORDS SIGNATURE DATE)" t)
+(autoload 'denote-dired-rename-files "denote" "\
+Rename Dired marked files same way as `denote-rename-file'.
+Rename each file in sequence, making all the relevant prompts.
+Unlike `denote-rename-file', do not prompt for confirmation of
+the changes made to the file: perform them outright (same as
+setting `denote-rename-confirmations' to a nil value)." '(dired-mode))
+(function-put 'denote-dired-rename-files 'interactive-only 't)
+(autoload 'denote-dired-rename-marked-files-with-keywords "denote" "\
+Rename marked files in Dired to a Denote file name by writing keywords.
+
+Specifically, do the following:
+
+- retain the file's existing name and make it the TITLE field,
+ per Denote's file-naming scheme;
+
+- sluggify the TITLE, according to our conventions (check the
+ user option `denote-file-name-slug-functions');
+
+- prepend an identifier to the TITLE;
+
+- preserve the file's extension, if any;
+
+- prompt once for KEYWORDS and apply the user's input to the
+ corresponding field in the file name, rewriting any keywords
+ that may exist while removing keywords that do exist if
+ KEYWORDS is empty;
+
+- add or rewrite existing front matter to the underlying file, if it is
+ recognized as a Denote note (per the user option `denote-file-type'),
+ such that it includes the new keywords.
+
+Construct the file name in accordance with the user option
+`denote-file-name-components-order'.
+
+Run the `denote-after-rename-file-hook' after renaming is done.
+
+Also see the specialized commands to only add or remove keywords:
+
+- `denote-dired-rename-marked-files-add-keywords'.
+- `denote-dired-rename-marked-files-remove-keywords'." '(dired-mode))
+(function-put 'denote-dired-rename-marked-files-with-keywords 'interactive-only 't)
+(autoload 'denote-dired-rename-marked-files-add-keywords "denote" "\
+Like `denote-dired-rename-marked-files-with-keywords' to only add keywords." '(dired-mode))
+(function-put 'denote-dired-rename-marked-files-add-keywords 'interactive-only 't)
+(autoload 'denote-dired-rename-marked-files-remove-keywords "denote" "\
+Like `denote-dired-rename-marked-files-with-keywords' to only remove keywords." '(dired-mode))
+(function-put 'denote-dired-rename-marked-files-remove-keywords 'interactive-only 't)
+(autoload 'denote-rename-file-using-front-matter "denote" "\
+Rename FILE using its front matter as input.
+When called interactively, FILE is the variable `buffer-file-name' or
+the Dired file at point, which is subsequently inspected for the
+requisite front matter. It is thus implied that the FILE has a file
+type that is supported by Denote, per the user option `denote-file-type'.
+
+The values of `denote-rename-confirmations',
+`denote-save-buffers' and `denote-kill-buffers' are respected.
+
+Only the front matter lines that appear in the front matter template (as
+defined in `denote-file-types') will be handled.
+
+To change the identifier (date) of the note with this command, the
+identifier line (if present) of the front matter must be modified.
+Modifying the date line has no effect.
+
+While this command generally does not modify the front matter, there are
+exceptions. The value of the `date' line will follow that of the
+`identifier' line. If they are both in the front matter template and
+the `date' line is missing, it will be added again. Similarly, if they
+are both in the front matter template and the `date' line is present and
+the `identifier' line has been removed, the `date' line will be removed
+as well. Also, if the keywords are out of order and
+`denote-sort-keywords' is non-nil, they will be sorted. There will be a
+prompt for this if `denote-rename-confirmations' contains
+`rewrite-front-matter'.
+
+Construct the file name in accordance with the user option
+`denote-file-name-components-order'.
+
+(fn FILE)" t)
+(autoload 'denote-dired-rename-marked-files-using-front-matter "denote" "\
+Call `denote-rename-file-using-front-matter' over the Dired marked files.
+Refer to the documentation of that command for the technicalities.
+
+Marked files must count as notes for the purposes of Denote, which means
+that they at least have an identifier in their file name and use a
+supported file type, per the user option `denote-file-type'. Files that
+do not meet this criterion are ignored because Denote cannot know if
+they have front matter and what that may be." '(dired-mode))
+(autoload 'denote-change-file-type-and-front-matter "denote" "\
+Change file type of FILE and add an appropriate front matter.
+
+If in Dired, consider FILE to be the one at point, else the
+current file, else prompt with minibuffer completion for one.
+
+Add a front matter in the format of the NEW-FILE-TYPE at the
+beginning of the file.
+
+Retrieve the title of FILE from a line starting with a title
+field in its front matter, depending on the previous file
+type (e.g. #+title for Org). The same process applies for
+keywords.
+
+As a final step, ask for confirmation, showing the difference
+between old and new file names.
+
+Important note: No attempt is made to modify any other elements
+of the file. This needs to be done manually.
+
+Construct the file name in accordance with the user option
+`denote-file-name-components-order'.
+
+(fn FILE NEW-FILE-TYPE)" t)
+(autoload 'denote-dired-mode "denote" "\
+Fontify all Denote-style file names.
+
+Add this or `denote-dired-mode-in-directories' to
+`dired-mode-hook'.
+
+This is a minor mode. If called interactively, toggle the `Denote-Dired
+mode' mode. If the prefix argument is positive, enable the mode, and if
+it is zero or negative, disable the mode.
+
+If called from Lisp, toggle the mode if ARG is `toggle'. Enable the
+mode if ARG is nil, omitted, or is a positive number. Disable the mode
+if ARG is a negative number.
+
+To check whether the minor mode is enabled in the current buffer,
+evaluate the variable `denote-dired-mode'.
+
+The mode's hook is called both when the mode is enabled and when it is
+disabled.
+
+(fn &optional ARG)" t)
+(autoload 'denote-dired-mode-in-directories "denote" "\
+Enable `denote-dired-mode' in `denote-dired-directories'.
+Add this function to `dired-mode-hook'.
+
+If `denote-dired-directories-include-subdirectories' is non-nil,
+also enable it in all subdirectories.")
+(autoload 'denote-link "denote" "\
+Create link to FILE note in variable `denote-directory' with DESCRIPTION.
+
+When called interactively, prompt for FILE using completion. In this
+case, derive FILE-TYPE from the current buffer. FILE-TYPE is used to
+determine the format of the link.
+
+Return the DESCRIPTION of the link in the format specified by
+`denote-link-description-format'. The default is to return the text of
+the active region or the title of the note (plus the signature if
+present).
+
+With optional ID-ONLY as a non-nil argument, such as with a universal
+prefix (\\[universal-argument]), insert links with just the identifier
+and no further description. In this case, the link format is always
+[[denote:IDENTIFIER]].
+
+If the DESCRIPTION is empty, format the link the same as with ID-ONLY.
+
+When called from Lisp, FILE is a string representing a full file system
+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.
+
+(fn FILE FILE-TYPE DESCRIPTION &optional ID-ONLY)" t)
+(autoload 'denote-find-link "denote" "\
+Use minibuffer completion to visit linked file.
+Also see `denote-find-backlink'." t)
+(function-put 'denote-find-link 'interactive-only 't)
+(autoload 'denote-link-after-creating "denote" "\
+Create new note in the background and link to it directly.
+
+Use `denote' interactively to produce the new note. Its doc
+string explains which prompts will be used and under what
+conditions.
+
+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'.
+
+For a variant of this, see `denote-link-after-creating-with-command'.
+
+IMPORTANT NOTE: Normally, `denote' does not save the buffer it
+produces for the new note. This is a safety precaution to not
+write to disk unless the user wants it (e.g. the user may choose
+to kill the buffer, thus cancelling the creation of the note).
+However, for this command the creation of the note happens in the
+background and the user may miss the step of saving their buffer.
+We thus have to save the buffer in order to (i) establish valid
+links, and (ii) retrieve whatever front matter from the target
+file. Though see `denote-save-buffer-after-creation'.
+
+(fn &optional ID-ONLY)" t)
+(autoload 'denote-link-after-creating-with-command "denote" "\
+Like `denote-link-after-creating' but prompt for note-making COMMAND.
+Use this to, for example, call `denote-signature' so that the
+newly created note has a signature as part of its file name.
+
+Optional ID-ONLY has the same meaning as in the command
+`denote-link-after-creating'.
+
+(fn COMMAND &optional ID-ONLY)" t)
+(autoload 'denote-link-or-create "denote" "\
+Use `denote-link' on TARGET file, creating it if necessary.
+
+If TARGET file does not exist, call `denote-link-after-creating' which
+runs the `denote' command interactively to create the file. The
+established link will then be targeting that new file. In that case,
+use the last input at the file prompt as the default value of the title
+prompt.
+
+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'.
+
+(fn TARGET &optional ID-ONLY)" t)
+(autoload 'denote-grep "denote" "\
+Search QUERY in the content of Denote files.
+QUERY should be a regular expression accepted by `xref-search-program'.
+
+The files to search for are those returned by `denote-directory-files'
+with a non-nil TEXT-ONLY argument.
+
+Results are put in a buffer which allows folding and further
+filtering (see the manual for details).
+
+You can insert a link to a grep search in any note by using the command
+`denote-query-contents-link'.
+
+(fn QUERY)" t)
+(autoload 'denote-grep-marked-dired-files "denote" "\
+Do the equivalent of `denote-grep' for QUERY in marked Dired files.
+
+(fn QUERY)" t)
+(autoload 'denote-grep-files-referenced-in-region "denote" "\
+Perform `denote-grep' QUERY in files referenced between START and END.
+When called interactively, prompt for QUERY. Also get START and END as
+the buffer positions that delimit the marked region. When called from
+Lisp, QUERY is a string, while START and END are buffer positions, as
+integers.
+
+Find references to files by their identifier. This includes links with
+just the identifier (as described in `denote-link' and related), links
+written by an Org dynamic block (see the `denote-org' package), or even
+file listings such as those of `dired' and the command-line `ls' program.
+
+(fn QUERY START END)" t)
+(autoload 'denote-backlinks "denote" "\
+Produce a buffer with backlinks to the current note.
+
+Show the names of files linking to the current file. Include the
+context of each link if the user option `denote-backlinks-show-context'
+is non-nil.
+
+Place the buffer below the current window or wherever the user option
+`denote-backlinks-display-buffer-action' specifies." t)
+(autoload 'denote-find-backlink "denote" "\
+Use minibuffer completion to visit backlink to current file.
+Alo see `denote-find-link'." t)
+(function-put 'denote-find-backlink 'interactive-only 't)
+(autoload 'denote-query-contents-link "denote" "\
+Insert query link for file contents.
+Prompt for QUERY or use the text of the active region. When the user
+follows this link, place any matches in a separate buffer (using the
+built-in Xref mechanism). This is the equivalent of a Unix grep command
+across the variable `denote-directory'.
+
+(fn QUERY)" t)
+(autoload 'denote-query-filenames-link "denote" "\
+Insert query link for file names.
+Prompt for QUERY or use the text of the active region. When the user
+follows this link, place any matches in a separate buffer (using the
+built-in Dired mechanism). This is the equivalent of a Unix find
+command across the variable `denote-directory'.
+
+(fn QUERY)" t)
+(autoload 'denote-fontify-links-mode-maybe "denote" "\
+Enable `denote-fontify-links-mode' in a denote file unless in `org-mode'.")
+(autoload 'denote-fontify-links-mode "denote" "\
+A minor mode to fontify and fold Denote links.
+
+Enabled this mode only when the current buffer is a Denote note and the
+major mode is not `org-mode' (or derived therefrom). Consider using
+`denote-fontify-links-mode-maybe' for this purpose.
+
+This is a minor mode. If called interactively, toggle the
+`Denote-Fontify-Links mode' mode. If the prefix argument is positive,
+enable the mode, and if it is zero or negative, disable the mode.
+
+If called from Lisp, toggle the mode if ARG is `toggle'. Enable the
+mode if ARG is nil, omitted, or is a positive number. Disable the mode
+if ARG is a negative number.
+
+To check whether the minor mode is enabled in the current buffer,
+evaluate the variable `denote-fontify-links-mode'.
+
+The mode's hook is called both when the mode is enabled and when it is
+disabled.
+
+(fn &optional ARG)" t)
+(autoload 'denote-add-links "denote" "\
+Insert links to all files whose file names match REGEXP.
+Use this command to reference multiple files at once. Particularly
+useful for the creation of metanotes (read the manual for more on the
+matter).
+
+Optional ID-ONLY has the same meaning as in `denote-link': it
+inserts links with just the identifier.
+
+(fn REGEXP &optional ID-ONLY)" t)
+(autoload 'denote-link-to-file-with-contents "denote" "\
+Link to a file whose contents match QUERY.
+This is similar to `denote-link', except that the file prompt is limited
+to files matching QUERY. Optional ID-ONLY has the same meaning as in
+`denote-link'.
+
+(fn QUERY &optional ID-ONLY)" t)
+(autoload 'denote-link-to-all-files-with-contents "denote" "\
+Link to all files whose contents match QUERY.
+This is similar to `denote-add-links', except it searches inside file
+contents, not file names. Optional ID-ONLY has the same meaning as in
+`denote-link' and `denote-add-links'.
+
+(fn QUERY &optional ID-ONLY)" t)
+(autoload 'denote-link-dired-marked-notes "denote" "\
+Insert Dired marked FILES as links in BUFFER.
+
+FILES conform with the Denote file-naming scheme, such that they can be
+linked to using the `denote:' link type.
+
+The BUFFER is one which visits a Denote note file. If there are
+multiple BUFFER candidates in buffers, prompt with completion for
+one among them. If there is none, throw an error.
+
+With optional ID-ONLY as a prefix argument, insert links with
+just the identifier (same principle as with `denote-link').
+
+This command is meant to be used from a Dired buffer.
+
+(fn FILES BUFFER &optional ID-ONLY)" '(dired-mode))
+(defvar denote-menu-bar-mode t "\
+Non-nil if Denote-Menu-Bar mode is enabled.
+See the `denote-menu-bar-mode' command
+for a description of this minor mode.
+Setting this variable directly does not take effect;
+either customize it (see the info node `Easy Customization')
+or call the function `denote-menu-bar-mode'.")
+(custom-autoload 'denote-menu-bar-mode "denote" nil)
+(autoload 'denote-menu-bar-mode "denote" "\
+Show Denote menu bar.
+
+This is a global minor mode. If called interactively, toggle the
+`Denote-Menu-Bar mode' mode. If the prefix argument is positive, enable
+the mode, and if it is zero or negative, disable the mode.
+
+If called from Lisp, toggle the mode if ARG is `toggle'. Enable the
+mode if ARG is nil, omitted, or is a positive number. Disable the mode
+if ARG is a negative number.
+
+To check whether the minor mode is enabled in the current buffer,
+evaluate `(default-value \\='denote-menu-bar-mode)'.
+
+The mode's hook is called both when the mode is enabled and when it is
+disabled.
+
+(fn &optional ARG)" t)
+(autoload 'denote-link-ol-follow "denote" "\
+Find file of type `denote:' matching LINK.
+LINK is the identifier of the note, optionally followed by a file search
+option akin to that of standard Org `file:' link types. Read Info
+node `(org) Query Options'.
+
+If LINK is not an identifier, then it is not pointing to a file but to a
+query of file contents or file names (see the commands
+`denote-query-contents-link' and `denote-query-filenames-link').
+
+Uses the function `denote-directory' to establish the path to the file.
+
+(fn LINK)")
+(autoload 'denote-link-ol-complete "denote" "\
+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.")
+(autoload 'denote-link-ol-store "denote" "\
+Handler for `org-store-link' adding support for denote: links.
+Optional INTERACTIVE? is used by `org-store-link'.
+
+Also see the user option `denote-org-store-link-to-heading'.
+
+(fn &optional INTERACTIVE?)")
+(autoload 'denote-link-ol-export "denote" "\
+Export a `denote:' link from Org files.
+The LINK, DESCRIPTION, and FORMAT are handled by the export
+backend.
+
+(fn LINK DESCRIPTION FORMAT)")
+(eval-after-load 'org `(funcall ',(lambda nil (with-no-warnings (org-link-set-parameters "denote" :follow #'denote-link-ol-follow :face #'denote-get-link-face :help-echo #'denote-link-ol-help-echo :complete #'denote-link-ol-complete :store #'denote-link-ol-store :export #'denote-link-ol-export)))))
+(autoload 'denote-org-capture "denote" "\
+Create new note through `org-capture-templates'.
+Use this as a function that returns the path to the new file.
+The file is populated with Denote's front matter. It can then be
+expanded with the usual specifiers or strings that
+`org-capture-templates' supports.
+
+This function obeys `denote-prompts', but it ignores `file-type',
+if present: it always sets the Org file extension for the created
+note to ensure that the capture process works as intended,
+especially for the desired output of the
+`denote-org-capture-specifiers' (which can include arbitrary
+text).
+
+Consult the manual for template samples.")
+(autoload 'denote-org-capture-with-prompts "denote" "\
+Like `denote-org-capture' but with optional prompt parameters.
+
+When called without arguments, do not prompt for anything. Just
+return the front matter with title and keyword fields empty and
+the date and identifier fields specified. Also make the file
+name consist of only the identifier plus the Org file name
+extension.
+
+Otherwise produce a minibuffer prompt for every non-nil value
+that corresponds to the TITLE, KEYWORDS, SUBDIRECTORY, DATE, and
+TEMPLATE arguments. The prompts are those used by the standard
+`denote' command and all of its utility commands.
+
+When returning the contents that fill in the Org capture
+template, the sequence is as follows: front matter, TEMPLATE, and
+then the value of the user option `denote-org-capture-specifiers'.
+
+Important note: in the case of SUBDIRECTORY actual subdirectories
+must exist---Denote does not create them. Same principle for
+TEMPLATE as templates must exist and are specified in the user
+option `denote-templates'.
+
+(fn &optional TITLE KEYWORDS SUBDIRECTORY DATE TEMPLATE)")
+(defvar denote-rename-buffer-mode nil "\
+Non-nil if Denote-Rename-Buffer mode is enabled.
+See the `denote-rename-buffer-mode' command
+for a description of this minor mode.
+Setting this variable directly does not take effect;
+either customize it (see the info node `Easy Customization')
+or call the function `denote-rename-buffer-mode'.")
+(custom-autoload 'denote-rename-buffer-mode "denote" nil)
+(autoload 'denote-rename-buffer-mode "denote" "\
+Automatically rename Denote buffers to be easier to read.
+
+A buffer is renamed upon visiting the underlying file. This
+means that existing buffers are not renamed until they are
+visited again in a new buffer (files are visited with the command
+`find-file' or related).
+
+This is a global minor mode. If called interactively, toggle the
+`Denote-Rename-Buffer mode' mode. If the prefix argument is positive,
+enable the mode, and if it is zero or negative, disable the mode.
+
+If called from Lisp, toggle the mode if ARG is `toggle'. Enable the
+mode if ARG is nil, omitted, or is a positive number. Disable the mode
+if ARG is a negative number.
+
+To check whether the minor mode is enabled in the current buffer,
+evaluate `(default-value \\='denote-rename-buffer-mode)'.
+
+The mode's hook is called both when the mode is enabled and when it is
+disabled.
+
+(fn &optional ARG)" t)
+(register-definition-prefixes "denote" '("denote-"))
+
+;;; End of scraped data
+
+(provide 'denote-autoloads)
+
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; no-native-compile: t
+;; coding: utf-8-emacs-unix
+;; End:
+
+;;; denote-autoloads.el ends here
blob - /dev/null
blob + d9e18a70a8b0eedb75f28e1fe6317142707c3127 (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/denote-pkg.el
+;; Generated package description from mode: lisp-data; .el -*- denoteno-byte-compile: t -*-
+(define-package "denote" "4.0.0" "Simple notes with an efficient file-naming scheme" '((emacs "28.1")) :commit "c98c74ec74fb6f0c894c5bf730d092d716e55b84" :authors '(("Protesilaos Stavrou" . "info@protesilaos.com")) :maintainer '("Protesilaos Stavrou" . "info@protesilaos.com") :url "https://github.com/protesilaos/denote")
blob - /dev/null
blob + e0677608ee1d1c1cd666f866a131ce348564995e (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/denote.el
+;;; denote.el --- Simple notes with an efficient file-naming scheme -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022-2025 Free Software Foundation, Inc.
+
+;; Author: Protesilaos Stavrou <info@protesilaos.com>
+;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
+;; URL: https://github.com/protesilaos/denote
+;; Version: 4.0.0
+;; Package-Requires: ((emacs "28.1"))
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Denote aims to be a simple-to-use, focused-in-scope, and effective
+;; note-taking and file-naming tool for Emacs.
+;;
+;; Denote is based on the idea that files should follow a predictable
+;; and descriptive file-naming scheme. The file name must offer a
+;; clear indication of what the contents are about, without reference
+;; to any other metadata. Denote basically streamlines the creation
+;; of such files or file names while providing facilities to link
+;; between them (where those files are editable).
+;;
+;; Denote's file-naming scheme is not limited to "notes". It can be used
+;; for all types of file, including those that are not editable in Emacs,
+;; such as videos. Naming files in a consistent way makes their
+;; filtering and retrieval considerably easier. Denote provides relevant
+;; facilities to rename files, regardless of file type.
+;;
+;; The manual describes all the technicalities about the file-naming
+;; scheme, points of entry to creating new notes, commands to check
+;; links between notes, and more: ;; <https://protesilaos.com/emacs/denote>.
+;; If you have the info manual available, evaluate:
+;;
+;; (info "(denote) Top")
+;;
+;; What follows is a general overview of its core core design
+;; principles (again: please read the manual for the technicalities):
+;;
+;; * Predictability :: File names must follow a consistent and
+;; descriptive naming convention (see the manual's "The file-naming
+;; scheme"). The file name alone should offer a clear indication of
+;; what the contents are, without reference to any other metadatum.
+;; This convention is not specific to note-taking, as it is pertinent
+;; to any form of file that is part of the user's long-term storage
+;; (see the manual's "Renaming files").
+;;
+;; * Composability :: Be a good Emacs citizen, by integrating with other
+;; packages or built-in functionality instead of re-inventing
+;; functions such as for filtering or greping. The author of Denote
+;; (Protesilaos, aka "Prot") writes ordinary notes in plain text
+;; (`.txt'), switching on demand to an Org file only when its expanded
+;; set of functionality is required for the task at hand (see the
+;; manual's "Points of entry").
+;;
+;; * Portability :: Notes are plain text and should remain portable.
+;; The way Denote writes file names, the front matter it includes in
+;; the note's header, and the links it establishes must all be
+;; adequately usable with standard Unix tools. No need for a databse
+;; or some specialised software. As Denote develops and this manual
+;; is fully fleshed out, there will be concrete examples on how to do
+;; the Denote-equivalent on the command-line.
+;;
+;; * Flexibility :: Do not assume the user's preference for a
+;; note-taking methodology. Denote is conceptually similar to the
+;; Zettelkasten Method, which you can learn more about in this
+;; detailed introduction: <https://zettelkasten.de/introduction/>.
+;; Notes are atomic (one file per note) and have a unique identifier.
+;; However, Denote does not enforce a particular methodology for
+;; knowledge management, such as a restricted vocabulary or mutually
+;; exclusive sets of keywords. Denote also does not check if the user
+;; writes thematically atomic notes. It is up to the user to apply
+;; the requisite rigor and/or creativity in pursuit of their preferred
+;; workflow (see the manual's "Writing metanotes").
+;;
+;; * Hackability :: Denote's code base consists of small and reusable
+;; functions. They all have documentation strings. The idea is to
+;; make it easier for users of varying levels of expertise to
+;; understand what is going on and make surgical interventions where
+;; necessary (e.g. to tweak some formatting). In this manual, we
+;; provide concrete examples on such user-level configurations (see
+;; the manual's "Keep a journal or diary").
+;;
+;; Now the important part... "Denote" is the familiar word, though it
+;; also is a play on the "note" concept. Plus, we can come up with
+;; acronyms, recursive or otherwise, of increasingly dubious utility
+;; like:
+;;
+;; + Don't Ever Note Only The Epiphenomenal
+;; + Denote Everything Neatly; Omit The Excesses
+;;
+;; But we'll let you get back to work. Don't Eschew or Neglect your
+;; Obligations, Tasks, and Engagements.
+
+;;; Code:
+
+(require 'seq)
+(require 'xref)
+(require 'dired)
+(eval-when-compile (require 'subr-x))
+
+(defgroup denote ()
+ "Simple notes with an efficient file-naming scheme."
+ :group 'files
+ :link '(info-link "(denote) Top")
+ :link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote"))
+
+;;;; User options
+
+;; About the autoload: (info "(elisp) File Local Variables")
+
+;;;###autoload (put 'denote-directory 'safe-local-variable (lambda (val) (or (stringp val) (eq val 'local) (eq val 'default-directory))))
+(defcustom denote-directory (expand-file-name "~/Documents/notes/")
+ "Directory for storing personal notes.
+
+If you intend to reference this variable in Lisp, consider using
+the function `denote-directory' instead."
+ :group 'denote
+ :safe (lambda (val) (or (stringp val) (eq val 'local) (eq val 'default-directory)))
+ :package-version '(denote . "2.0.0")
+ :link '(info-link "(denote) Maintain separate directories for notes")
+ :type 'directory)
+
+(define-obsolete-variable-alias 'denote-save-buffer-after-creation 'denote-save-buffers "3.0.0")
+
+(defcustom denote-save-buffers nil
+ "Control whether commands that handle new notes save their buffer outright.
+
+The default behaviour of commands such as `denote' (or related)
+is to not save the buffer they create. This gives the user the
+chance to review the text before writing it to a file. The user
+may choose to delete the unsaved buffer, thus not creating a new
+note.
+
+This option also applies to notes affected by the renaming
+commands (`denote-rename-file' and related).
+
+If this user option is set to a non-nil value, such buffers are
+saved automatically. The assumption is that the user who opts in
+to this feature is familiar with the `denote-rename-file'
+operation (or related) and knows it is reliable. Data loss may
+occur if the file is modified externally.
+
+Also see `denote-kill-buffers'."
+ :group 'denote
+ :package-version '(denote . "3.0.0")
+ :type 'boolean)
+
+(defcustom denote-kill-buffers nil
+ "Control whether creation or renaming commands kill their buffer.
+
+The default behaviour of creation or renaming commands such as
+`denote' or `denote-rename-file' is to not kill the buffer they
+create or modify at the end of their operation.
+
+If this user option is nil (the default), buffers affected by a
+creation or renaming command are not automatically killed.
+
+If set to `on-creation', new notes are automatically killed.
+
+If set to `on-rename', renamed notes are automatically killed.
+
+If set to t, new and renamed notes are killed.
+
+If a buffer is killed, it is also saved, as if `denote-save-buffers'
+were t. See its documentation.
+
+In all cases, if the buffer already existed before the Denote operation
+it is NOT automatically killed."
+ :group 'denote
+ :package-version '(denote . "3.1.0")
+ :type '(choice
+ (const :tag "Do not kill buffers" nil)
+ (const :tag "Kill after creation" on-creation)
+ (const :tag "Kill after rename" on-rename)
+ (const :tag "Kill after creation and rename" t)))
+
+;;;###autoload (put 'denote-known-keywords 'safe-local-variable #'listp)
+(defcustom denote-known-keywords
+ '("emacs" "philosophy" "politics" "economics")
+ "List of strings with predefined keywords for `denote'.
+Also see user options: `denote-infer-keywords',
+`denote-sort-keywords', `denote-file-name-slug-functions'."
+ :group 'denote
+ :safe #'listp
+ :package-version '(denote . "0.1.0")
+ :type '(repeat string))
+
+;;;###autoload (put 'denote-infer-keywords 'safe-local-variable (lambda (val) (or val (null val))))
+(defcustom denote-infer-keywords t
+ "Whether to infer keywords from existing notes' file names.
+
+When non-nil, search the file names of existing notes in the
+variable `denote-directory' for their keyword field and extract
+the entries as \"inferred keywords\". These are combined with
+`denote-known-keywords' and are presented as completion
+candidates while using `denote' and related commands
+interactively.
+
+If nil, refrain from inferring keywords. The aforementioned
+completion prompt only shows the `denote-known-keywords'. Use
+this if you want to enforce a restricted vocabulary.
+
+The user option `denote-excluded-keywords-regexp' can be used to
+exclude keywords that match a regular expression.
+
+Inferred keywords are specific to the value of the variable
+`denote-directory'. If a silo with a local value is used, as
+explained in that variable's doc string, the inferred keywords
+are specific to the given silo.
+
+For advanced Lisp usage, the function `denote-keywords' returns
+the appropriate list of strings."
+ :group 'denote
+ :safe (lambda (val) (or val (null val)))
+ :package-version '(denote . "0.1.0")
+ :type 'boolean)
+
+(defcustom denote-prompts '(title keywords)
+ "Specify the prompts followed by relevant Denote commands.
+
+Commands that prompt for user input to construct a Denote file name
+include, but are not limited to: `denote', `denote-signature',
+`denote-type', `denote-date', `denote-subdirectory',
+`denote-rename-file', `denote-dired-rename-files'.
+
+The value of this user option is a list of symbols, which includes any
+of the following:
+
+- `title': Prompt for the title of the new note.
+
+- `keywords': Prompts with completion for the keywords of the new note.
+ Available candidates are those specified in the user option
+ `denote-known-keywords'. If the user option `denote-infer-keywords'
+ is non-nil, keywords in existing note file names are included in the
+ list of candidates. The `keywords' prompt uses `completing-read-multiple',
+ meaning that it can accept multiple keywords separated by a comma (or
+ whatever the value of `crm-separator' is).
+
+- `file-type': Prompts with completion for the file type of the new
+ note. Available candidates are those specified in the user option
+ `denote-file-type'. Without this prompt, `denote' uses the value of
+ the variable `denote-file-type'.
+
+- `subdirectory': Prompts with completion for a subdirectory in which to
+ create the note. Available candidates are the value of the user
+ option `denote-directory' and all of its subdirectories. Any
+ subdirectory must already exist: Denote will not create it.
+
+- `date': Prompts for the date of the new note. It will expect an input
+ like 2022-06-16 or a date plus time: 2022-06-16 14:30. Without the
+ `date' prompt, the `denote' command uses the `current-time'. (To
+ leverage the more sophisticated Org method, see the
+ `denote-date-prompt-use-org-read-date'.)
+
+- `template': Prompts for a KEY among `denote-templates'. The 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 for any
+ kind of workflow, such as a special tag to label the part1 and part2
+ of a large file that is split in half, or to add special contexts like
+ home and work, or even priorities like a, b, c. One other use-case is
+ to implement a sequencing scheme that makes notes have hierarchical
+ relationships. This is handled by our optional extension
+ denote-sequence.el, which is part of the denote package (read the
+ manual).
+
+The prompts occur in the given order.
+
+If the value of this user option is nil, no prompts are used. The
+resulting file name will consist of an identifier (i.e. the date and
+time) and a supported file type extension (per the variable
+`denote-file-type').
+
+Recall that Denote's standard file-naming scheme is defined as
+follows (read the manual for the technicalities):
+
+ DATE--TITLE__KEYWORDS.EXT
+
+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 (the default style).
+
+Finally, this user option only affects the interactive use of the
+`denote' or other relevant commands (advanced users can call it from
+Lisp). In Lisp usage, the behaviour is always what the caller
+specifies, based on the supplied arguments.
+
+Also see `denote-history-completion-in-prompts'.
+
+To change the order of the file name components, refer to
+`denote-file-name-components-order'."
+ :group 'denote
+ :package-version '(denote . "2.3.0")
+ :link '(info-link "(denote) The denote-prompts option")
+ :type '(radio (const :tag "Use no prompts" nil)
+ (set :tag "Available prompts" :greedy t
+ (const :tag "Title" title)
+ (const :tag "Keywords" keywords)
+ (const :tag "Date" date)
+ (const :tag "File type extension" file-type)
+ (const :tag "Subdirectory" subdirectory)
+ (const :tag "Template" template)
+ (const :tag "Signature" signature))))
+
+(defcustom denote-file-name-components-order '(identifier signature title keywords)
+ "Specify the order of the file name components.
+
+The value is a list of the following symbols:
+
+- `identifier': This is the combination of the date and time. When it
+ is the first on the list, it looks like \"20240519T073456\" and does
+ not have a component separator of its own due its unambiguous format.
+ When it is placed anywhere else in the file name, it is prefixed with
+ \"@@\", so it looks like \"@@20240519T073456\".
+
+- `signature': This is an arbitrary string that can be used to qualify
+ the file in some way, according to the user's methodology (e.g. to add
+ a sequence to notes). The string is always prefixed with the \"==\"
+ to remain unambiguous.
+
+- `title': This is an arbitrary string which describes the file. It is
+ always prefixed with \"--\" to be unambiguous.
+
+- `keywords': This is a series of one or more words that succinctly
+ group the file. Multiple keywords are separated by an underscore
+ prefixed to each of them. The file name component is always prefixed
+ with \"__\".
+
+All four symbols must appear exactly once. Duplicates are ignored. Any
+missing symbol is added automatically.
+
+Some examples:
+
+ (setq denote-file-name-components-order
+ \\='(identifier signature title keywords))
+ => 20240519T07345==hello--this-is-the-title__denote_testing.org
+
+ (setq denote-file-name-components-order
+ \\='(signature identifier title keywords))
+ => ==hello@@20240519T07345--this-is-the-title__denote_testing.org
+
+ (setq denote-file-name-components-order
+ \\='(title signature identifier keywords))
+ => --this-is-the-title==hello@@20240519T07345__denote_testing.org
+
+ (setq denote-file-name-components-order
+ \\='(keywords title signature identifier))
+ => __denote_testing--this-is-the-title==hello@@20240519T07345.org
+
+Also see the user option `denote-prompts', which affects which
+components are actually used in the order specified herein.
+
+Before deciding on this, please consider the longer-term implications
+of file names with varying patterns. Consistency makes things
+predictable and thus easier to find. So pick one order and never touch
+it again. When in doubt, leave the default file-naming scheme as-is.
+
+This user option should only be used to build a file name. Custom code
+should not have behaviors that depend on its value. The reason is that
+its value can change over time and Denote should be able to handle past
+and current notes."
+ :group 'denote
+ :package-version '(denote . "3.0.0")
+ ;; FIXME 2024-05-19: This technically works to display the user
+ ;; option in the Custom buffer and to show its current value, though
+ ;; it does not allow the user to modify it graphically: they have to
+ ;; switch to the Lisp expression. Find a way to present an
+ ;; interface that lets the user reorder those elements.
+ ;;
+ ;; Still, making this a defcustom helps with discoverability, as
+ ;; well as with the use of `setopt' and related.
+ :type '(list
+ (const :tag "Identifier component (date and time)" identifier)
+ (const :tag "File signature (text to qualify a file)" signature)
+ (const :tag "The title of the file" title)
+ (const :tag "Keywords of the file" keywords)))
+
+(defcustom denote-front-matter-components-present-even-if-empty-value '(title keywords date identifier)
+ "The components that are always present in front matter even when empty.
+
+Components are `title', `keywords', `signature', `date', `identifier'.
+
+Note that even though a component may be listed in this variable, it
+will not be present in the front matter if the corresponding line is not
+in the front matter template."
+ :group 'denote
+ :package-version '(denote . "4.0.0")
+ :type '(list
+ (const :tag "Title" title)
+ (const :tag "Keywords" keywords)
+ (const :tag "Signature" signature)
+ (const :tag "Date" date)
+ (const :tag "Identifier" identifier)))
+
+(defcustom denote-sort-keywords t
+ "Whether to sort keywords in new files.
+
+When non-nil, the keywords of `denote' are sorted with
+`string-collate-lessp' regardless of the order they were inserted at the
+minibuffer prompt.
+
+If nil, show the keywords in their given order."
+ :group 'denote
+ :package-version '(denote . "0.1.0")
+ :type 'boolean)
+
+(defcustom denote-file-type nil
+ "The file type extension for new notes.
+
+By default (a nil value), the file type is that of Org mode.
+Though the `org' symbol can be specified for the same effect.
+
+When the value is the symbol `markdown-yaml', the file type is
+that of Markdown mode and the front matter uses YAML notation.
+Similarly, `markdown-toml' is Markdown but has TOML syntax in the
+front matter.
+
+When the value is `text', the file type is that of Text mode.
+
+Any other non-nil value is the same as the default.
+
+NOTE: Expert users can change the supported file-types by editing
+the value of `denote-file-types'. That variable, which is not a
+user option, controls the behaviour of all file-type-aware
+functions (creating notes, renaming them, inserting front matter,
+formatting a link, etc.). Consult its documentation for the
+technicalities."
+ :type '(choice
+ (const :tag "Unspecified (defaults to Org)" nil)
+ (const :tag "Org mode (default)" org)
+ (const :tag "Markdown (YAML front matter)" markdown-yaml)
+ (const :tag "Markdown (TOML front matter)" markdown-toml)
+ (const :tag "Plain text" text))
+ :package-version '(denote . "0.6.0")
+ :group 'denote)
+
+(defcustom denote-date-format nil
+ "Date format in the front matter (file header) of new notes.
+
+When nil (the default value), use a file-type-specific
+format (also check the user option `denote-file-type'):
+
+- For Org, an inactive timestamp is used, such as [2022-06-30 Wed
+ 15:31].
+
+- For Markdown, the RFC3339 standard is applied:
+ 2022-06-30T15:48:00+03:00.
+
+- For plain text, the format is that of ISO 8601: 2022-06-30.
+
+If the value is a string, ignore the above and use it instead.
+The string must include format specifiers for the date. These
+are described in the doc string of `format-time-string'."
+ :type '(choice
+ (const :tag "Use appropiate format for each file type" nil)
+ (string :tag "Custom format for `format-time-string'"))
+ :package-version '(denote . "0.2.0")
+ :group 'denote)
+
+(defcustom denote-date-prompt-use-org-read-date nil
+ "Whether to use `org-read-date' in date prompts.
+
+If non-nil, use `org-read-date'. If nil, input the date as a
+string, as described in `denote'.
+
+This option is relevant when `denote-prompts' includes a `date'
+and/or when the user invokes the command `denote-date'."
+ :group 'denote
+ :package-version '(denote . "0.6.0")
+ :type 'boolean)
+
+(defcustom denote-org-store-link-to-heading nil
+ "Determine whether `org-store-link' links to the current Org heading.
+
+[ Remember that what `org-store-link' does is merely collect a link. To
+ actually insert it, use the command `org-insert-link'. Note that
+ `org-capture' uses `org-store-link' internally when it needs to store
+ a link. ]
+
+When the value is nil, the Denote handler for `org-store-link' produces
+links only to the current file (by using the file's identifier). For
+example:
+
+ [[denote:20240118T060608][Some test]]
+
+If the value is `context', the link consists of the file's identifier
+and the text of the current heading, like this:
+
+ [[denote:20240118T060608::*Heading text][Some test::Heading text]].
+
+However, if there already exists a CUSTOM_ID property for the current
+heading, this is always given priority and is used instead of the
+context.
+
+If the value is `id' or, for backward-compatibility, any other non-nil
+value, then Denote will use the standard Org mechanism of the CUSTOM_ID
+property to create a unique link to the heading. If the heading does
+not have a CUSTOM_ID, it creates it and includes it in its PROPERTIES
+drawer. If a CUSTOM_ID exists, it takes it as-is. The result is like
+this:
+
+ [[denote:20240118T060608::#h:eed0fb8e-4cc7-478f][Some test::Heading text]]
+
+The value of the CUSTOM_ID is determined by the Org user option
+`org-id-method'. The sample shown above uses the default UUID
+infrastructure (though I deleted a few characters to not get
+complaints from the byte compiler about long lines in the doc
+string...).
+
+Note that this option does not affect how Org behaves with regard to
+`org-id-link-to-org-use-id'. If that user option is set to create ID
+properties, then those will be created by Org even if the Denote link
+handler will take care to not use/store the ID value. Concretely, users
+who never want ID properties under their headings should keep
+`org-id-link-to-org-use-id' in its nil value.
+
+Context links are easier to break than those with a CUSTOM_ID in cases
+where either the heading text changes or there is another heading that
+matches that text. The potential advantage of context links is that
+they do not require a PROPERTIES drawer.
+
+When visiting a link to a heading, Org opens the Denote file and then
+navigates to that heading.
+
+[ This feature only works in Org mode files, as other file types
+ do not have a linking mechanism that handles unique identifiers
+ for headings or other patterns to jump to. If `org-store-link'
+ is invoked in one such file, it captures only the Denote
+ identifier of the file, even if this user option is set to a
+ non-nil value. ]"
+ :group 'denote
+ :package-version '(denote . "4.0.0")
+ :type '(choice (const :tag "No link to heading (default)" nil)
+ (const :tag "Link to the context" context)
+ (const :tag "Link wtih CUSTOM_ID, creating it if needed" id)))
+
+(defcustom denote-templates nil
+ "Alist of content templates for new notes.
+A template is arbitrary text that Denote will add to a newly
+created note right below the front matter.
+
+Templates are expressed as a (KEY . VALUE) association.
+
+- The KEY is the name which identifies the template. It is an
+ arbitrary symbol, such as `report', `memo', `statement'.
+
+- The VALUE is either a string or the symbol of a function.
+
+ - If it is a string, it is ordinary text that Denote will insert
+ as-is. It can contain newline characters to add spacing. The
+ manual of Denote contains examples on how to use the `concat'
+ function, beside writing a generic string.
+
+ - If it is a function, it is called without arguments and is expected
+ to return a string. Denote will call the function and insert the
+ result in the buffer.
+
+The user can choose a template either by invoking the command
+`denote-template' or by changing the user option `denote-prompts'
+to always prompt for a template when calling the `denote'
+command."
+ :type '(alist :key-type symbol :value-type (choice string function))
+ :package-version '(denote . "3.1.0")
+ :link '(info-link "(denote) The denote-templates option")
+ :group 'denote)
+
+(make-obsolete-variable 'denote-rename-no-confirm 'denote-rename-confirmations "3.0.0")
+
+(defcustom denote-rename-confirmations '(rewrite-front-matter modify-file-name)
+ "Make renaming commands prompt for confirmations.
+
+This affects the behaviour of renaming commands. The value is either
+nil, in which case no confirmation is ever requested, or a list of
+symbols among the following:
+
+- `modify-file-name' means that renaming commands will ask for
+ confirmation before modifying the file name.
+
+- `rewrite-front-matter' means that renaming commands will ask for
+ confirmation before rewritting the front matter.
+
+- `add-front-matter' means that renaming commands will ask for
+ confirmation before adding new front matter to the file.
+
+The default behaviour of the `denote-rename-file' command (and others
+like it) is to ask for an affirmative answer as a final step before
+changing the file name and, where relevant, inserting or updating the
+corresponding front matter.
+
+Specialized commands that build on top of `denote-rename-file' (or
+related) may internally bind this user option to a non-nil value in
+order to perform their operation (e.g. `denote-dired-rename-files' goes
+through each marked Dired file, prompting for the information to use,
+but carries out the renaming without asking for confirmation)."
+ :group 'denote
+ :type '(radio (const :tag "Disable all confirmations" nil)
+ (set :tag "Available confirmations" :greedy t
+ (const :tag "Add front matter" add-front-matter)
+ (const :tag "Rewrite front matter" rewrite-front-matter)
+ (const :tag "Modify file name" modify-file-name))))
+
+(defcustom denote-excluded-directories-regexp nil
+ "Regular expression of directories to exclude from all operations.
+Omit matching directories from file prompts and also exclude them
+from all functions that check the contents of the variable
+`denote-directory'. The regexp needs to match only the name of
+the directory, not its full path.
+
+File prompts are used by several commands, such as `denote-link'
+and `denote-subdirectory'.
+
+Functions that check for files include `denote-directory-files'
+and `denote-directory-subdirectories'.
+
+The match is performed with `string-match-p'."
+ :group 'denote
+ :package-version '(denote . "1.2.0")
+ :type 'string)
+
+(defcustom denote-excluded-keywords-regexp nil
+ "Regular expression of keywords to not infer.
+Keywords are inferred from file names and provided at relevant
+prompts as completion candidates when the user option
+`denote-infer-keywords' is non-nil.
+
+The match is performed with `string-match-p'."
+ :group 'denote
+ :package-version '(denote . "1.2.0")
+ :type 'string)
+
+(defcustom denote-excluded-files-regexp nil
+ "Regular expression of files that are excluded from Denote file prompts.
+Files are provided for completion when using commands like `denote-link'
+and `denote-open-or-create'.
+
+The match is performed with `string-match-p' on the full file path."
+ :group 'denote
+ :package-version '(denote . "3.0.0")
+ :type 'string)
+
+(defcustom denote-after-new-note-hook nil
+ "Normal hook that runs after the `denote' command.
+This also covers all convenience functions that call `denote'
+internally, such as `denote-signature' and `denote-type' (check
+the default value of the user option `denote-commands-for-new-notes')."
+ :group 'denote
+ :package-version '(denote . "2.1.0")
+ :link '(info-link "(denote) Standard note creation")
+ :type 'hook)
+
+(defcustom denote-after-rename-file-hook nil
+ "Normal hook called after a succesful Denote rename operation.
+This affects the behaviour of the commands `denote-rename-file',
+`denote-dired-rename-files', `denote-rename-file-using-front-matter',
+`denote-dired-rename-marked-files-with-keywords',
+`denote-dired-rename-marked-files-using-front-matter',
+`denote-keywords-add', `denote-keywords-remove', and any other
+command that builds on top of them."
+ :group 'denote
+ :package-version '(denote . "2.3.0")
+ :link '(info-link "(denote) Renaming files")
+ :type 'hook)
+
+(defcustom denote-region-after-new-note-functions nil
+ "Abnormal hook called after `denote-region'.
+Functions in this hook are called with two arguments,
+representing the beginning and end buffer positions of the region
+that was inserted in the new note. These are called only if
+`denote-region' is invoked while a region is active.
+
+A common use-case is to call `org-insert-structure-template'
+after a region is inserted. This case does not actually require
+the aforementioned arguments, in which case the function can
+simply declare them as ignored by prefixing the argument names
+with an underscore. For example, the following will prompt for a
+structure template as soon as `denote-region' is done:
+
+ (defun my-denote-region-org-structure-template (_beg _end)
+ (when (derived-mode-p \\='org-mode)
+ (activate-mark)
+ (call-interactively \\='org-insert-structure-template)))
+
+ (add-hook \\='denote-region-after-new-note-functions
+ #\\='my-denote-region-org-structure-template)"
+ :group 'denote
+ :package-version '(denote . "2.1.0")
+ :link '(info-link "(denote) Create a note with the region's contents")
+ :type 'hook)
+
+(defvar denote-prompts-with-history-as-completion
+ '(denote-title-prompt denote-signature-prompt denote-files-matching-regexp-prompt denote-query-link-prompt)
+ "Prompts that conditionally perform completion against their history.
+
+These are minibuffer prompts that ordinarily accept a free form string
+input, as opposed to matching against a predefined set.
+
+These prompts can optionally perform completion against their own
+minibuffer history when the user option `denote-history-completion-in-prompts'
+is set to a non-nil value.")
+
+(defcustom denote-history-completion-in-prompts t
+ "Toggle history completion in all `denote-prompts-with-history-as-completion'.
+
+When this user option is set to a non-nil value, use minibuffer history
+entries as completion candidates in `denote-prompts-with-history-as-completion'.
+Those will show previous inputs from their respective history as
+possible values to select, either to (i) re-insert them verbatim or (ii)
+with the intent to edit further (depending on the minibuffer user
+interface, one can select a candidate with TAB without exiting the
+minibuffer, as opposed to what RET normally does by selecting and
+exiting).
+
+When this user option is set to a nil value, all of the
+`denote-prompts-with-history-as-completion' do not use minibuffer
+completion: they just prompt for a string of characters. Their
+history is still available through all the standard ways of retrieving
+minibuffer history, such as with the command `previous-history-element'.
+
+History completion still allows arbitrary values to be provided as
+input: they do not have to match the available minibuffer completion
+candidates.
+
+Note that some prompts, like `denote-keywords-prompt', always use
+minibuffer completion, due to the specifics of their data.
+
+[ Consider enabling the built-in `savehist-mode' to persist minibuffer
+ histories between sessions.]
+
+Also see `denote-prompts'."
+ :type 'boolean
+ :package-version '(denote . "2.3.0")
+ :group 'denote)
+
+(defcustom denote-commands-for-new-notes
+ '(denote
+ denote-date
+ denote-subdirectory
+ denote-template
+ denote-type
+ denote-signature)
+ "List of commands for `denote-command-prompt' that create a new note.
+These are used by commands such as `denote-open-or-create-with-command'
+and `denote-link-after-creating-with-command'."
+ :group 'denote
+ :package-version '(denote . "2.1.0")
+ :link '(info-link "(denote) Choose which commands to prompt for")
+ :type '(repeat symbol))
+
+(defcustom denote-file-name-slug-functions
+ '((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.
+
+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 METHOD is the function to be used to format the given
+ component. This function should take a string as its parameter
+ and return the string formatted for the file name. In the case
+ of the `keyword' component, the function receives a SINGLE
+ string representing a single keyword and return it formatted
+ for the file name. Joining the keywords together is handled by
+ Denote.
+
+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
+`denote-sluggify-signature'.
+
+Remember that deviating from the default file-naming scheme of Denote
+will make things harder to search in the future, as files can/will have
+permutations that create uncertainty. The sluggification scheme and
+concomitant restrictions we impose by default are there for a very good
+reason: they are the distillation of years of experience. Here we give
+you what you wish, but bear in mind it may not be what you need. You
+have been warned."
+ :group 'denote
+ :package-version '(denote . "2.3.0")
+ :link '(info-link "(denote) User-defined sluggification of file name components")
+ :type '(alist :key (choice (const title)
+ (const signature)
+ (const keyword))
+ :value function))
+
+(define-obsolete-variable-alias
+ 'denote-link-button-action
+ 'denote-open-link-function
+ "4.0.0")
+
+(defcustom denote-open-link-function #'find-file-other-window
+ "Function to find the file of a Denote link.
+
+The default value is `find-file-other-window', with `find-file' because
+another common option. Users can provide a custom function which
+behaves like the other two.
+
+This is used in all non-Org buffers that have a link created by Denote.
+Org has its own mechanism, which you can learn more about by reading the
+documentation of the `org-open-at-point' command."
+ :group 'denote
+ :type '(choice (function :tag "Other window" find-file-other-window)
+ (function :tag "Current window" find-file)
+ (function :tag "Custom function"))
+ :package-version '(denote . "4.0.0"))
+
+(define-obsolete-variable-alias
+ 'denote-link-description-function
+ 'denote-link-description-format
+ "4.0.0")
+
+(defcustom denote-link-description-format #'denote-link-description-with-signature-and-title
+ "The format of a link description text.
+This determines how `denote-link' and related functions create a link
+description by default.
+
+The value can be either a function or a string. If it is a function, it
+is called with one argument, the file, and should return a string
+representing the link description.
+
+The default is a function that returns the active region or the title of
+the note (with the signature if present).
+
+If the value is a string, it treats specially the following specifiers:
+
+- The %t is the Denote TITLE in the front matter or the file name.
+- The %T is the Denote TITLE in the file name.
+- The %i is the Denote IDENTIFIER of the file.
+- The %I is the identifier converted to DAYNAME, DAYNUM MONTHNUM YEAR.
+- The %d is the same as %i (DATE mnemonic).
+- The %D is a \"do what I mean\" which behaves the same as %t and if
+ that returns nothing, it falls back to %I, then %i.
+- The %s is the Denote SIGNATURE of the file.
+- The %k is the Denote KEYWORDS of the file.
+- The %% is a literal percent sign.
+
+In addition, the following flags are available for each of the specifiers:
+
+- 0 :: Pad to the width, if given, with zeros instead of spaces.
+- - :: Pad to the width, if given, on the right instead of the left.
+- < :: Truncate to the width and precision, if given, on the left.
+- > :: Truncate to the width and precision, if given, on the right.
+- ^ :: Convert to upper case.
+- _ :: Convert to lower case.
+
+When combined all together, the above are written thus:
+
+ %<flags><width><precision>SPECIFIER-CHARACTER
+
+Any other text in the string it taken as-is. Users may want, for
+example, to include some text that makes Denote links stand out, such as
+a [D] prefix.
+
+If the region is active, its text is used as the link's description."
+ :type '(choice
+ (string :tag "String with treats format specifiers specially")
+ (function :tag "Custom function like `denote-link-description-with-signature-and-title'"))
+ :package-version '(denote . "4.0.0")
+ :group 'denote)
+
+;;;; Main variables
+
+;; For character classes, evaluate: (info "(elisp) Char Classes")
+
+(defconst denote-id-format "%Y%m%dT%H%M%S"
+ "Format of ID prefix of a note's filename.
+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 "==\\([^.]*?\\)\\(==.*\\|--.*\\|__.*\\|@@.*\\|\\..*\\)*$"
+ "Regular expression to match the SIGNATURE field in a file name.")
+
+(defconst denote-title-regexp "--\\([^.]*?\\)\\(==.*\\|__.*\\|@@.*\\|\\..*\\)*$"
+ "Regular expression to match the TITLE field in a file name.")
+
+(defconst denote-keywords-regexp "__\\([^.]*?\\)\\(==.*\\|--.*\\|__.*\\|@@.*\\|\\..*\\)*$"
+ "Regular expression to match the KEYWORDS field in a file name.")
+
+(make-obsolete-variable
+ 'denote-excluded-punctuation-extra-regexp
+ 'denote-file-name-slug-functions
+ "4.0.0")
+
+;;;; File helper functions
+
+(defun denote--completion-table (category candidates)
+ "Pass appropriate metadata CATEGORY to completion CANDIDATES."
+ (lambda (string pred action)
+ (if (eq action 'metadata)
+ `(metadata (category . ,category))
+ (complete-with-action action candidates string pred))))
+
+(defun denote--completion-table-no-sort (category candidates)
+ "Pass appropriate metadata CATEGORY to completion CANDIDATES.
+Like `denote--completion-table' but also disable sorting."
+ (lambda (string pred action)
+ (if (eq action 'metadata)
+ `(metadata (category . ,category)
+ (display-sort-function . ,#'identity))
+ (complete-with-action action candidates string pred))))
+
+(defun denote--default-directory-is-silo-p ()
+ "Return path to silo if `default-directory' is a silo."
+ (when-let* ((dir-locals (dir-locals-find-file default-directory))
+ ((alist-get 'denote-directory dir-local-variables-alist)))
+ (cond
+ ((listp dir-locals)
+ (car dir-locals))
+ ((stringp dir-locals)
+ dir-locals))))
+
+(defun denote--make-denote-directory ()
+ "Make the variable `denote-directory' and its parents, if needed."
+ (when (not (file-directory-p denote-directory))
+ (make-directory denote-directory :parents)))
+
+(defun denote-directory ()
+ "Return path of variable `denote-directory' as a proper directory.
+Custom Lisp code can `let' bind the variable `denote-directory'
+to override what this function returns."
+ (if-let* (((or (eq denote-directory 'default-directory) (eq denote-directory 'local)))
+ (silo-dir (denote--default-directory-is-silo-p)))
+ (progn
+ (display-warning
+ 'denote
+ "Silo value must be a string; `local' or `default-directory'are obsolete"
+ :error)
+ silo-dir)
+ (let ((denote-directory (file-name-as-directory (expand-file-name denote-directory))))
+ (denote--make-denote-directory)
+ denote-directory)))
+
+;; 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.")
+
+;;;;; Sluggification functions
+
+(defun denote-slug-keep-only-ascii (str)
+ "Remove all non-ASCII characters from STR and replace them with spaces.
+This is useful as a helper function to construct
+`denote-file-name-slug-functions'."
+ (let* ((ascii-range (seq-map
+ (lambda (character)
+ (if (and (>= character 33) (<= character 126))
+ character
+ 32)) ; empty space
+ str))
+ (characters (seq-filter #'characterp ascii-range)))
+ (mapconcat #'string characters)))
+
+(define-obsolete-function-alias
+ 'denote--slug-hyphenate
+ 'denote-slug-hyphenate
+ "4.0.0")
+
+(defun denote-slug-hyphenate (str)
+ "Replace spaces and underscores with hyphens in STR.
+Also replace multiple hyphens with a single one and remove any
+leading and trailing hyphen."
+ (replace-regexp-in-string
+ "^-\\|-$" ""
+ (replace-regexp-in-string
+ "-\\{2,\\}" "-"
+ (replace-regexp-in-string "_\\|\s+" "-" str))))
+
+(defun denote-slug-put-equals (str)
+ "Replace spaces and underscores with equals signs in STR.
+Also replace multiple equals signs with a single one and remove
+any leading and trailing signs."
+ (replace-regexp-in-string
+ "^=\\|=$" ""
+ (replace-regexp-in-string
+ "=\\{2,\\}" "="
+ (replace-regexp-in-string "_\\|\s+" "=" str))))
+
+(defun denote--remove-dot-characters (str)
+ "Remove dot characters from STR."
+ (replace-regexp-in-string "\\." "" str))
+
+(defun denote--trim-right-token-characters (str component)
+ "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 "[=@_-]+")))
+
+(defun denote--replace-consecutive-token-characters (str component)
+ "Replace consecutive characters with a single one in STR.
+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 when the default sluggification is disabled
+ (if (eq component 'title)
+ str
+ (replace-regexp-in-string
+ "-\\{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 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."
+ (let* ((slug-function (alist-get component denote-file-name-slug-functions))
+ (str-slug (cond ((eq component 'title)
+ (funcall (or slug-function #'denote-sluggify-title) str))
+ ((eq component 'keyword)
+ (replace-regexp-in-string
+ "_" ""
+ (funcall (or slug-function #'denote-sluggify-keyword) str)))
+ ((eq component 'signature)
+ (funcall (or slug-function #'denote-sluggify-signature) str)))))
+ (denote--trim-right-token-characters
+ (denote--replace-consecutive-token-characters
+ (denote--remove-dot-characters str-slug) component) component)))
+
+(defun denote-sluggify-title (str)
+ "Make STR an appropriate slug for title."
+ (downcase
+ (denote-slug-hyphenate
+ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/=]*" "" str))))
+
+(defun denote-sluggify-signature (str)
+ "Make STR an appropriate slug for signature."
+ (downcase
+ (denote-slug-put-equals
+ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/-]*" "" str))))
+
+(defun denote-sluggify-keyword (str)
+ "Sluggify STR while joining separate words."
+ (downcase
+ (replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/_ =-]*" "" str)))
+
+(defun denote-sluggify-keywords (keywords)
+ "Sluggify KEYWORDS, which is a list of strings."
+ (mapcar (lambda (keyword)
+ (denote-sluggify 'keyword keyword))
+ keywords))
+
+;;;;; Common helper functions
+
+(defun denote--file-empty-p (file)
+ "Return non-nil if FILE is empty."
+ (zerop (or (file-attribute-size (file-attributes file)) 0)))
+
+(defun denote-identifier-p (identifier)
+ "Return non-nil if IDENTIFIER string is a Denote identifier."
+ (string-match-p (format "\\`%s\\'" denote-id-regexp) identifier))
+
+(defun denote-file-has-identifier-p (file)
+ "Return non-nil if FILE has a Denote identifier."
+ (denote-retrieve-filename-identifier file))
+
+(defun denote-file-has-supported-extension-p (file)
+ "Return non-nil if FILE has supported extension.
+Also account for the possibility of an added .gpg suffix. Supported
+extensions are those implied by the variable `denote-file-type'."
+ (seq-some (lambda (e)
+ (string-suffix-p e file))
+ (denote-file-type-extensions-with-encryption)))
+
+(defun denote-file-is-in-denote-directory-p (file)
+ "Return non-nil if FILE is in the variable `denote-directory'."
+ (string-prefix-p (denote-directory) (expand-file-name file)))
+
+(defun denote-filename-is-note-p (filename)
+ "Return non-nil if FILENAME is a valid name for a Denote note.
+For our purposes, its path must be part of the variable
+`denote-directory', it must have a Denote identifier in its name, and
+use one of the extensions implied by the variable `denote-file-type'."
+ (and (denote-file-is-in-denote-directory-p filename)
+ (denote-file-has-identifier-p filename)
+ (denote-file-has-supported-extension-p filename)))
+
+(defun denote-file-is-note-p (file)
+ "Return non-nil if FILE is an actual Denote note.
+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)))
+
+(defun denote-file-has-denoted-filename-p (file)
+ "Return non-nil if FILE respects the file-naming scheme of Denote.
+
+This tests the rules of Denote's file-naming scheme. Sluggification is
+ignored. It is done by removing all file name components and validating
+what remains."
+ (let* ((initial-filename (file-name-nondirectory file))
+ (filename initial-filename)
+ (title (denote-retrieve-filename-title file))
+ (keywords-string (denote-retrieve-filename-keywords file))
+ (signature (denote-retrieve-filename-signature file))
+ (identifier (denote-retrieve-filename-identifier file)))
+ (when title
+ (setq filename (replace-regexp-in-string (concat "\\(--" (regexp-quote title) "\\).*\\'") "" filename nil nil 1)))
+ (when keywords-string
+ (setq filename (replace-regexp-in-string (concat "\\(__" (regexp-quote keywords-string) "\\).*\\'") "" filename nil nil 1)))
+ (when signature
+ (setq filename (replace-regexp-in-string (concat "\\(==" (regexp-quote signature) "\\).*\\'") "" filename nil nil 1)))
+ (when identifier
+ (if (string-match-p "@@" filename)
+ (setq filename (replace-regexp-in-string (concat "\\(@@" (regexp-quote identifier) "\\).*\\'") "" filename nil nil 1))
+ (setq filename (replace-regexp-in-string (concat "\\(" (regexp-quote identifier) "\\).*\\'") "" filename nil nil 1))))
+ ;; What remains should be the empty string or the file extension.
+ (and (not (string-prefix-p "." initial-filename))
+ (or (string-empty-p filename)
+ (string-prefix-p "." filename)))))
+
+(defun denote-file-has-signature-p (file)
+ "Return non-nil if FILE has a Denote identifier."
+ (denote-retrieve-filename-signature file))
+
+(defun denote-file-is-writable-and-supported-p (file)
+ "Return non-nil if FILE is writable and has supported extension."
+ ;; We do not want to test that the file is regular (exists) because we want
+ ;; this function to return t on files that are still unsaved.
+ (and (file-writable-p file)
+ (denote-file-has-supported-extension-p file)))
+
+(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."
+ (when-let* ((dir (denote-directory))
+ ((file-name-absolute-p file))
+ (file-name (expand-file-name file))
+ ((string-prefix-p dir file-name)))
+ (substring-no-properties file-name (length dir))))
+
+(defun denote-extract-id-from-string (string)
+ "Return existing Denote identifier in STRING, else nil."
+ (when (string-match denote-id-regexp string)
+ (match-string-no-properties 0 string)))
+
+(defun denote--exclude-directory-regexp-p (file)
+ "Return non-nil if FILE matches `denote-excluded-directories-regexp'."
+ (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)))))
+
+(defun denote--directory-all-files-recursively ()
+ "Return list of all files in variable `denote-directory'.
+Avoids traversing dotfiles (unconditionally) and whatever matches
+`denote-excluded-directories-regexp'."
+ (directory-files-recursively
+ (denote-directory)
+ directory-files-no-dot-files-regexp
+ :include-directories
+ #'denote--directory-files-recursively-predicate
+ :follow-symlinks))
+
+(defun denote--file-excluded-p (file)
+ "Return non-file if FILE matches `denote-excluded-files-regexp'."
+ (and denote-excluded-files-regexp
+ (string-match-p denote-excluded-files-regexp file)))
+
+(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
+are not backups."
+ (mapcar
+ #'expand-file-name
+ (seq-filter
+ (lambda (file)
+ (and (file-regular-p file)
+ (denote-file-has-identifier-p file)
+ (not (denote--file-excluded-p file))
+ (not (backup-file-name-p file))))
+ (denote--directory-all-files-recursively))))
+
+(defun denote-directory-files (&optional files-matching-regexp omit-current text-only exclude-regexp)
+ "Return list of absolute file paths in variable `denote-directory'.
+Files that match `denote-excluded-files-regexp' are excluded from the
+list.
+
+Files only need to have an identifier. The return value may thus
+include file types that are not implied by the variable
+`denote-file-type'.
+
+With optional FILES-MATCHING-REGEXP, restrict files to those
+matching the given regular expression.
+
+With optional OMIT-CURRENT as a non-nil value, do not include the
+current Denote file in the returned list.
+
+With optional TEXT-ONLY as a non-nil value, limit the results to
+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."
+ (let ((files (denote--directory-get-files)))
+ (when (and omit-current buffer-file-name (denote-file-has-identifier-p buffer-file-name))
+ (setq files (delete buffer-file-name files)))
+ (when files-matching-regexp
+ (setq files (seq-filter
+ (lambda (f)
+ (string-match-p files-matching-regexp (denote-get-file-name-relative-to-denote-directory f)))
+ files)))
+ (when text-only
+ (setq files (seq-filter #'denote-file-has-supported-extension-p files)))
+ (when exclude-regexp
+ (setq files (seq-remove
+ (lambda (file)
+ (string-match-p exclude-regexp file))
+ files)))
+ files))
+
+(defun denote-directory-subdirectories ()
+ "Return list of subdirectories in 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)))
+ (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)))
+
+;; 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.
+(defvar denote-encryption-file-extensions '(".gpg" ".age")
+ "List of strings specifying file extensions for encryption.")
+
+(defun denote-file-type-extensions-with-encryption ()
+ "Derive `denote-file-type-extensions' plus `denote-encryption-file-extensions'."
+ (let ((file-extensions (denote-file-type-extensions))
+ all)
+ (dolist (ext file-extensions)
+ (dolist (enc denote-encryption-file-extensions)
+ (push (concat ext enc) all)))
+ (append file-extensions all)))
+
+(defun denote-get-file-extension (file)
+ "Return extension of FILE with dot included.
+Account for `denote-encryption-file-extensions'. In other words,
+return something like .org.gpg if it is part of the file, else
+return .org."
+ (let ((outer-extension (file-name-extension file :period)))
+ (if-let* (((member outer-extension denote-encryption-file-extensions))
+ (file (file-name-sans-extension file))
+ (inner-extension (file-name-extension file :period)))
+ (concat inner-extension outer-extension)
+ outer-extension)))
+
+(defun denote-get-file-extension-sans-encryption (file)
+ "Return extension of FILE with dot included and without the encryption part.
+Build on top of `denote-get-file-extension' though always return
+something like .org even if the actual file extension is
+.org.gpg."
+ (let ((extension (denote-get-file-extension file)))
+ (if (string-match (regexp-opt denote-encryption-file-extensions) extension)
+ (substring extension 0 (match-beginning 0))
+ extension)))
+
+(defun denote-get-path-by-id (id)
+ "Return absolute path of ID string in `denote-directory-files'."
+ (let ((files
+ (seq-filter
+ (lambda (file)
+ (string= id (denote-retrieve-filename-identifier file)))
+ (denote-directory-files))))
+ (if (length< files 2)
+ (car files)
+ (seq-find
+ (lambda (file)
+ (let ((file-extension (denote-get-file-extension-sans-encryption file)))
+ (and (denote-file-has-supported-extension-p file)
+ (or (string= (denote--file-extension denote-file-type)
+ file-extension)
+ (string= ".org" file-extension)
+ (member file-extension (denote-file-type-extensions))))))
+ files))))
+
+(defun denote-get-relative-path-by-id (id &optional directory)
+ "Return relative path of ID string in `denote-directory-files'.
+The path is relative to DIRECTORY (default: ‘default-directory’)."
+ (when-let* ((path (denote-get-path-by-id id)))
+ (file-relative-name path directory)))
+
+(defvar denote-file-history nil
+ "Minibuffer history of `denote-file-prompt'.")
+
+(defalias 'denote--file-history 'denote-file-history
+ "Compatibility alias for `denote-file-history'.")
+
+(defvar denote-file-prompt-latest-input nil
+ "Latest input passed to `denote-file-prompt'.
+This is used for retrieving a value that is used to set a new default at
+the title prompt of `denote-open-or-create' and related commands.")
+
+(defvar denote-file-prompt-use-files-matching-regexp nil
+ "The `denote-file-prompt' FILES-MATCHING-REGEXP value.
+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)
+ "Prompt for file in variable `denote-directory'.
+Files that match `denote-excluded-files-regexp' are excluded from the
+list.
+
+With optional FILES-MATCHING-REGEXP, filter the candidates per
+the given regular expression.
+
+With optional PROMPT-TEXT, use it instead of the default call to
+select a file.
+
+With optional NO-REQUIRE-MATCH, accept the given input as-is.
+
+Return the absolute path to the matching file."
+ (let* ((default-directory (denote-directory))
+ (relative-files (mapcar
+ #'denote-get-file-name-relative-to-denote-directory
+ (denote-directory-files
+ (or denote-file-prompt-use-files-matching-regexp files-matching-regexp)
+ :omit-current)))
+ (prompt (format "%s in %s: "
+ (or prompt-text "Select FILE")
+ (propertize (denote-directory) 'face 'denote-faces-prompt-current-name)))
+ (input (completing-read
+ prompt
+ (denote--completion-table 'file relative-files)
+ nil (unless no-require-match :require-match)
+ nil 'denote-file-history))
+ (absolute-file (concat (denote-directory) input)))
+ ;; NOTE: This block is executed when no-require-match is t. It is useful
+ ;; for commands such as `denote-open-or-create` or similar.
+ (unless (file-exists-p absolute-file)
+ (setq denote-file-prompt-latest-input input)
+ (setq denote-file-history (delete input denote-file-history)))
+ ;; NOTE: We must always return an absolute path, even if it does not
+ ;; exist, because callers expect one. They handle a non-existent file
+ ;; appropriately.
+ absolute-file))
+
+;;;; The sort mechanism
+
+(defgroup denote-sort nil
+ "Sort Denote files based on a file name component."
+ :group 'denote
+ :link '(info-link "(denote) Top")
+ :link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote"))
+
+(defconst denote-sort-comparison-fallback-function #'string-collate-lessp
+ "String comparison function used by `denote-sort-files' subroutines.")
+
+(defconst denote-sort-components '(title keywords signature identifier)
+ "List of sorting keys applicable for `denote-sort-files' and related.")
+
+(defcustom denote-sort-identifier-comparison-function denote-sort-comparison-fallback-function
+ "Function to sort the DATE/IDENTIFIER component in file names.
+The function accepts two arguments and must return a non-nil value if
+the first argument is smaller than the second one."
+ :type 'function
+ :package-version '(denote . "4.0.0")
+ :group 'denote-sort)
+
+(defcustom denote-sort-title-comparison-function denote-sort-comparison-fallback-function
+ "Function to sort the TITLE component in file names.
+The function accepts two arguments and must return a non-nil value if
+the first argument is smaller than the second one."
+ :type 'function
+ :package-version '(denote . "3.1.0")
+ :group 'denote-sort)
+
+(defcustom denote-sort-keywords-comparison-function denote-sort-comparison-fallback-function
+ "Function to sort the KEYWORDS component in file names.
+The function accepts two arguments and must return a non-nil value if
+the first argument is smaller than the second one."
+ :type 'function
+ :package-version '(denote . "3.1.0")
+ :group 'denote-sort)
+
+(defcustom denote-sort-signature-comparison-function denote-sort-comparison-fallback-function
+ "Function to sort the SIGNATURE component in file names.
+The function accepts two arguments and must return a non-nil value if
+the first argument is smaller than the second one."
+ :type 'function
+ :package-version '(denote . "3.1.0")
+ :group 'denote-sort)
+
+(defcustom denote-sort-dired-extra-prompts '(sort-by-component reverse-sort)
+ "Determine what `denote-sort-dired' prompts for beside a search query.
+This concerns the additional prompts issued by `denote-sort-dired' about
+whether to sort by a given file name component and to then reverse the
+sort.
+
+The value is a list of symbols, which can include the symbols
+`sort-by-component', `reverse-sort', and `exclude-regexp'. The order is
+significant, with the leftmost symbol coming first.
+
+These symbols correspond to the following:
+
+- A choice to select the file name component to sort by.
+- A yes or no prompt on whether to reverse the sorting.
+- A string (or regular expression) of files to be excluded from the results.
+
+If the value is nil, skip all prompts. In this scenario, the sorting is
+done according to `denote-sort-dired-default-sort-component' and
+`denote-sort-dired-default-reverse-sort'."
+ :type '(radio (const :tag "Do not prompt for anything" nil)
+ (set :tag "Available prompts" :greedy t
+ (const :tag "Sort by file name component" sort-by-component)
+ (const :tag "Reverse the sort" reverse-sort)
+ (const :tag "Exclude files matching regexp" exclude-regexp)))
+ :package-version '(denote . "4.0.0")
+ :group 'denote-sort)
+
+(defcustom denote-sort-dired-default-sort-component 'identifier
+ "Set the default file name component to sort by.
+This is used only if `denote-sort-dired-extra-prompts' omits the
+minibuffer prompt for which file name component to sort by."
+ :type '(radio
+ (const :tag "Sort by identifier (default)" identifier)
+ (const :tag "Sort by title" title)
+ (const :tag "Sort by keywords" keywords)
+ (const :tag "Sort by signature" signature))
+ :package-version '(denote . "3.1.0")
+ :group 'denote-sort)
+
+(defcustom denote-sort-dired-default-reverse-sort nil
+ "If non-nil, reverse the sorting order by default.
+This is used only if `denote-sort-dired-extra-prompts' omits the
+minibuffer prompt that asks for a reverse sort or not."
+ :type 'boolean
+ :package-version '(denote . "3.1.0")
+ :group 'denote-sort)
+
+;; NOTE 2023-12-04: We can have compound sorting algorithms such as
+;; title+signature, but I want to keep this simple for the time being.
+;; Let us first hear from users to understand if there is a real need
+;; for such a feature.
+(defmacro denote-sort--define-lessp (component)
+ "Define function to sort by COMPONENT."
+ (let ((retrieve-fn (intern (format "denote-retrieve-filename-%s" component)))
+ (comparison-fn (intern (format "denote-sort-%s-comparison-function" component))))
+ `(defun ,(intern (format "denote-sort-%s-lessp" component)) (file1 file2)
+ ,(format
+ "Return smallest among FILE1, FILE2 based on their %s.
+The `%s' performs the comparison."
+ component comparison-fn)
+ (let* ((one (,retrieve-fn file1))
+ (two (,retrieve-fn file2))
+ (one-empty-p (or (null one) (string-empty-p one)))
+ (two-empty-p (or (null two) (string-empty-p two))))
+ (cond
+ (one-empty-p nil)
+ ((and (not one-empty-p) two-empty-p) one)
+ (t (funcall (or ,comparison-fn denote-sort-comparison-fallback-function) one two)))))))
+
+;; TODO 2023-12-04: Subject to the above NOTE, we can also sort by
+;; directory and by file length.
+(denote-sort--define-lessp identifier)
+(denote-sort--define-lessp title)
+(denote-sort--define-lessp keywords)
+(denote-sort--define-lessp signature)
+
+;;;###autoload
+(defun denote-sort-files (files component &optional reverse)
+ "Returned sorted list of Denote FILES.
+
+With COMPONENT as a symbol among `denote-sort-components',
+sort files based on the corresponding file name component.
+
+With COMPONENT as the symbol of a function, use it to perform the
+sorting. In this case, the function is called with two arguments, as
+described by `sort'.
+
+With COMPONENT as a nil value keep the original date-based
+sorting which relies on the identifier of each file name.
+
+With optional REVERSE as a non-nil value, reverse the sort order."
+ (let* ((files-to-sort (copy-sequence files))
+ (sort-fn (pcase component
+ ((pred functionp) component)
+ ('identifier #'denote-sort-identifier-lessp)
+ ('title #'denote-sort-title-lessp)
+ ('keywords #'denote-sort-keywords-lessp)
+ ('signature #'denote-sort-signature-lessp)))
+ (sorted-files (if sort-fn (sort files sort-fn) files-to-sort)))
+ (if reverse
+ (reverse sorted-files)
+ sorted-files)))
+
+(defun denote-sort-get-directory-files (files-matching-regexp sort-by-component &optional reverse omit-current exclude-regexp)
+ "Return sorted list of files in variable `denote-directory'.
+
+With FILES-MATCHING-REGEXP as a string limit files to those
+matching the given regular expression.
+
+With SORT-BY-COMPONENT as a symbol among `denote-sort-components',
+pass it to `denote-sort-files' to sort by the corresponding file
+name component.
+
+With optional REVERSE as a non-nil value, reverse the sort order.
+
+With optional OMIT-CURRENT, do not include the current file in
+the list.
+
+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."
+ (denote-sort-files
+ (denote-directory-files files-matching-regexp omit-current nil exclude-regexp)
+ sort-by-component
+ reverse))
+
+(defun denote-sort-get-links (files-matching-regexp sort-by-component current-file-type id-only &optional reverse exclude-regexp)
+ "Return sorted typographic list of links for FILES-MATCHING-REGEXP.
+
+With FILES-MATCHING-REGEXP as a string, match files stored in the
+variable `denote-directory'.
+
+With SORT-BY-COMPONENT as a symbol among `denote-sort-components',
+sort FILES-MATCHING-REGEXP by the given Denote file name
+component. If SORT-BY-COMPONENT is nil or an unknown non-nil
+value, default to the identifier-based sorting.
+
+With CURRENT-FILE-TYPE as a symbol among those specified in
+the variable `denote-file-type' (or the `car' of each element in
+`denote-file-types'), format the link accordingly. With a nil or
+unknown non-nil value, default to the Org notation.
+
+With ID-ONLY as a non-nil value, produce links that consist only
+of the identifier, thus deviating from CURRENT-FILE-TYPE.
+
+With optional REVERSE as a non-nil value, reverse the sort order.
+
+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."
+ (denote-link--prepare-links
+ (denote-sort-get-directory-files files-matching-regexp sort-by-component reverse exclude-regexp)
+ current-file-type
+ id-only))
+
+(defvar denote-sort-component-history nil
+ "Minibuffer history of `denote-sort-component-prompt'.")
+
+(defalias 'denote-sort--component-hist 'denote-sort-component-history
+ "Compatibility alias for `denote-sort-component-history'.")
+
+(defun denote-sort-component-prompt ()
+ "Prompt for sorting key among `denote-sort-components'."
+ (let ((default (car denote-sort-component-history)))
+ (intern
+ (completing-read
+ (format-prompt "Sort by file name component" default)
+ denote-sort-components nil :require-match
+ nil 'denote-sort-component-history default))))
+
+(defvar denote-sort-exclude-files-history nil
+ "Minibuffer history for `denote-sort-exclude-files-prompt'.")
+
+(defun denote-sort-exclude-files-prompt ()
+ "Prompt for regular expression of files to exclude."
+ ;; TODO 2024-12-03: Maybe use `read-regexp'? We do not use it
+ ;; elsewhere, so maybe this is fine.
+ (let ((default (car denote-sort-exclude-files-history)))
+ (read-string
+ (format-prompt "Exclude files matching REGEXP" default)
+ default 'denote-sort-exclude-files-history)))
+
+(defvar-local denote-sort--dired-buffer nil
+ "Buffer object of current `denote-sort-dired'.")
+
+(defun denote-sort-dired--prompts ()
+ "Return list of prompts per `denote-sort-dired-extra-prompts'."
+ (let (sort-by-component reverse-sort exclude-rx)
+ (dolist (prompt denote-sort-dired-extra-prompts)
+ (pcase prompt
+ ('sort-by-component (setq sort-by-component (denote-sort-component-prompt)))
+ ('reverse-sort (setq reverse-sort (y-or-n-p "Reverse sort? ")))
+ ('exclude-regexp (setq exclude-rx (denote-sort-exclude-files-prompt)))))
+ (list sort-by-component reverse-sort exclude-rx)))
+
+;;;###autoload
+(defun denote-sort-dired (files-matching-regexp sort-by-component reverse exclude-regexp)
+ "Produce Dired buffer with sorted files from variable `denote-directory'.
+When called interactively, prompt for FILES-MATCHING-REGEXP and,
+depending on the value of the user option `denote-sort-dired-extra-prompts',
+also prompt for SORT-BY-COMPONENT, REVERSE, and EXCLUDE-REGEXP.
+
+1. FILES-MATCHING-REGEXP limits the list of Denote files to
+ those matching the provided regular expression.
+
+2. SORT-BY-COMPONENT sorts the files by their file name component (one
+ among `denote-sort-components'). If it is nil, sorting is performed
+ according to the user option `denote-sort-dired-default-sort-component',
+ falling back to the identifier.
+
+3. REVERSE is a boolean to reverse the order when it is a non-nil value.
+ If `denote-sort-dired-extra-prompts' is configured to skip this
+ prompt, then the sorting is done according to the user option
+ `denote-sort-dired-default-reverse-sort', falling back to
+ nil (i.e. no reverse sort).
+
+4. EXCLUDE-REGEXP excludes the files that match the given regular
+ expression. This is done after FILES-MATCHING-REGEXP and
+ OMIT-CURRENT have been applied.
+
+When called from Lisp, the arguments are a string, a symbol among
+`denote-sort-components', a non-nil value, and a string, respectively."
+ (interactive
+ (append (list (denote-files-matching-regexp-prompt)) (denote-sort-dired--prompts)))
+ (let ((component (or sort-by-component
+ denote-sort-dired-default-sort-component
+ 'identifier))
+ (reverse-sort (or reverse
+ denote-sort-dired-default-reverse-sort
+ nil))
+ (exclude-rx (or exclude-regexp nil)))
+ (if-let* ((default-directory (denote-directory))
+ (files (denote-sort-get-directory-files files-matching-regexp component reverse-sort nil exclude-rx))
+ ;; NOTE 2023-12-04: Passing the FILES-MATCHING-REGEXP as
+ ;; buffer-name produces an error if the regexp contains a
+ ;; wildcard for a directory. I can reproduce this in emacs
+ ;; -Q and am not sure if it is a bug. Anyway, I will report
+ ;; it upstream, but even if it is fixed we cannot use it
+ ;; for now (whatever fix will be available for Emacs 30+).
+ (denote-sort-dired-buffer-name (format "Denote sort `%s' by `%s'" files-matching-regexp component))
+ (buffer-name (format "Denote sort by `%s' at %s" component (format-time-string "%T"))))
+ (let ((dired-buffer (dired (cons buffer-name (mapcar #'file-relative-name files)))))
+ (setq denote-sort--dired-buffer dired-buffer)
+ (with-current-buffer dired-buffer
+ (setq-local revert-buffer-function
+ (lambda (&rest _)
+ ;; FIXME 2025-01-04: Killing the buffer has
+ ;; the unintended side effect of affecting the
+ ;; window configuration when we call
+ ;; `denote-update-dired-buffers'.
+ (kill-buffer dired-buffer)
+ (denote-sort-dired files-matching-regexp component reverse-sort exclude-rx))))
+ buffer-name)
+ (message "No matching files for: %s" files-matching-regexp))))
+
+(defalias 'denote-dired 'denote-sort-dired
+ "Alias for `denote-sort-dired' command.")
+
+;;;; Keywords
+
+(defun denote-extract-keywords-from-path (path)
+ "Extract keywords from PATH and return them as a list of strings.
+PATH must be a Denote-style file name where keywords are prefixed
+with an underscore.
+
+If PATH has no such keywords, return nil.
+
+Also see `denote-retrieve-filename-keywords'."
+ (when-let* ((kws (denote-retrieve-filename-keywords path)))
+ (split-string kws "_" :omit-nulls)))
+
+(defalias 'denote-retrieve-filename-keywords-as-list 'denote-extract-keywords-from-path
+ "Alias for the function `denote-extract-keywords-from-path'")
+
+(define-obsolete-function-alias
+ 'denote--inferred-keywords
+ 'denote-infer-keywords-from-files
+ "4.0.0")
+
+(defun denote-infer-keywords-from-files (&optional files-matching-regexp)
+ "Return list of keywords in `denote-directory-files'.
+With optional FILES-MATCHING-REGEXP, only extract keywords from the
+matching files. Otherwise, do it for all files.
+
+Keep any duplicates. Users who do not want duplicates should refer to
+the functions `denote-keywords'."
+ (when-let* ((files (denote-directory-files files-matching-regexp))
+ (keywords (mapcan #'denote-extract-keywords-from-path files)))
+ (if-let* ((regexp denote-excluded-keywords-regexp))
+ (seq-remove
+ (lambda (k)
+ (string-match-p regexp k))
+ keywords)
+ keywords)))
+
+(defun denote-keywords (&optional files-matching-regexp)
+ "Return appropriate list of keyword candidates.
+If `denote-infer-keywords' is non-nil, infer keywords from existing
+notes and combine them into a list with `denote-known-keywords'. Else
+use only the latter.
+
+In the case of keyword inferrence, use optional FILES-MATCHING-REGEXP,
+to extract keywords only from the matching files. Otherwise, do it for
+all files.
+
+Filter inferred keywords with the user option `denote-excluded-keywords-regexp'."
+ (delete-dups
+ (if denote-infer-keywords
+ (append (denote-infer-keywords-from-files files-matching-regexp) denote-known-keywords)
+ denote-known-keywords)))
+
+(defvar denote-keyword-history nil
+ "Minibuffer history of inputted keywords.")
+
+(defalias 'denote--keyword-history 'denote-keyword-history
+ "Compatibility alias for `denote-keyword-history'.")
+
+(make-obsolete
+ 'denote-convert-file-name-keywords-to-crm
+ nil
+ "3.0.0: Keywords are always returned as a list")
+
+(defun denote--keywords-crm (keywords &optional prompt initial)
+ "Use `completing-read-multiple' for KEYWORDS.
+With optional PROMPT, use it instead of a generic text for file
+keywords. With optional INITIAL, add it to the minibuffer as
+initial input."
+ (delete-dups
+ (completing-read-multiple
+ (format-prompt (or prompt "New file KEYWORDS") nil)
+ keywords nil nil initial 'denote-keyword-history)))
+
+(defun denote-keywords-prompt (&optional prompt-text initial-keywords infer-from-files-matching-regexp)
+ "Prompt for one or more keywords.
+Read entries as separate when they are demarcated by the
+`crm-separator', which typically is a comma.
+
+With optional PROMPT-TEXT, use it to prompt the user for keywords. Else
+use a generic prompt. With optional INITIAL-KEYWORDS use them as the
+initial minibuffer text.
+
+With optional INFER-FROM-FILES-MATCHING-REGEXP, only infer keywords from
+files that match the given regular expression, per the function
+`denote-keywords'.
+
+Return an empty list if the minibuffer input is empty."
+ (denote--keywords-crm (denote-keywords infer-from-files-matching-regexp) prompt-text initial-keywords))
+
+(defun denote-keywords-sort (keywords)
+ "Sort KEYWORDS if `denote-sort-keywords' is non-nil.
+KEYWORDS is a list of strings, per `denote-keywords-prompt'."
+ (if denote-sort-keywords
+ (sort (copy-sequence keywords) #'string-collate-lessp)
+ keywords))
+
+(defun denote-keywords-combine (keywords)
+ "Combine KEYWORDS list of strings into a single string.
+Keywords are separated by the underscore character, per the
+Denote file-naming scheme."
+ (string-join keywords "_"))
+
+(defun denote--keywords-add-to-history (keywords)
+ "Append KEYWORDS to `denote-keyword-history'."
+ (mapc
+ (lambda (kw)
+ (add-to-history 'denote-keyword-history kw))
+ (delete-dups keywords)))
+
+;;;; File types
+
+(defvar denote-org-front-matter
+ "#+title: %s
+#+date: %s
+#+filetags: %s
+#+identifier: %s
+#+signature: %s
+\n"
+ "Org front matter.
+It is passed to `format' with arguments TITLE, DATE, KEYWORDS,
+ID. Advanced users are advised to consult Info node `(denote)
+Change the front matter format'.")
+
+(defvar denote-yaml-front-matter
+ "---
+title: %s
+date: %s
+tags: %s
+identifier: %s
+signature: %s
+---\n\n"
+ "YAML (Markdown) front matter.
+It is passed to `format' with arguments TITLE, DATE, KEYWORDS,
+ID. Advanced users are advised to consult Info node `(denote)
+Change the front matter format'.")
+
+(defvar denote-toml-front-matter
+ "+++
+title = %s
+date = %s
+tags = %s
+identifier = %s
+signature = %s
++++\n\n"
+ "TOML (Markdown) front matter.
+It is passed to `format' with arguments TITLE, DATE, KEYWORDS,
+ID. Advanced users are advised to consult Info node `(denote)
+Change the front matter format'.")
+
+(defvar denote-text-front-matter
+ "title: %s
+date: %s
+tags: %s
+identifier: %s
+signature: %s
+---------------------------\n\n"
+ "Plain text front matter.
+It is passed to `format' with arguments TITLE, DATE, KEYWORDS,
+ID. Advanced users are advised to consult Info node `(denote)
+Change the front matter format'.")
+
+(defun denote-format-string-for-md-front-matter (s)
+ "Surround string S with quotes.
+
+This can be used in `denote-file-types' to format front mattter."
+ (format "%S" s))
+
+(defun denote-trim-whitespace (s)
+ "Trim whitespace around string S.
+This can be used in `denote-file-types' to format front mattter."
+ (string-trim s))
+
+(defun denote--trim-quotes (s)
+ "Trim quotes around string S."
+ (let ((trims "[\"']+"))
+ (string-trim s trims trims)))
+
+(defun denote-trim-whitespace-then-quotes (s)
+ "Trim whitespace then quotes around string S.
+This can be used in `denote-file-types' to format front mattter."
+ (denote--trim-quotes (denote-trim-whitespace s)))
+
+(defun denote-format-string-for-org-front-matter (s)
+ "Return string S as-is for Org or plain text front matter."
+ s)
+
+(defun denote-format-keywords-for-md-front-matter (keywords)
+ "Format front matter KEYWORDS for markdown file type.
+KEYWORDS is a list of strings. Consult the `denote-file-types'
+for how this is used."
+ (format "[%s]" (mapconcat (lambda (k) (format "%S" k)) keywords ", ")))
+
+(defun denote-format-keywords-for-text-front-matter (keywords)
+ "Format front matter KEYWORDS for text file type.
+KEYWORDS is a list of strings. Consult the `denote-file-types'
+for how this is used."
+ (string-join keywords " "))
+
+(defun denote-format-keywords-for-org-front-matter (keywords)
+ "Format front matter KEYWORDS for org file type.
+KEYWORDS is a list of strings. Consult the `denote-file-types'
+for how this is used."
+ (if keywords
+ (format ":%s:" (string-join keywords ":"))
+ ""))
+
+(defun denote-extract-keywords-from-front-matter (keywords-string)
+ "Extract keywords list from front matter KEYWORDS-STRING.
+Split KEYWORDS-STRING into a list of strings.
+
+Consult the `denote-file-types' for how this is used."
+ (split-string keywords-string "[:,\s]+" t "[][ \"']+"))
+
+(defun denote-extract-date-from-front-matter (date-string)
+ "Extract date object from front matter DATE-STRING.
+
+Consult the `denote-file-types' for how this is used."
+ (let ((date-string (denote-trim-whitespace date-string)))
+ (if (string-empty-p date-string)
+ nil
+ (date-to-time date-string))))
+
+(defvar denote-file-types
+ '((org
+ :extension ".org"
+ :front-matter denote-org-front-matter
+ :title-key-regexp "^#\\+title\\s-*:"
+ :title-value-function denote-format-string-for-org-front-matter
+ :title-value-reverse-function denote-trim-whitespace
+ :keywords-key-regexp "^#\\+filetags\\s-*:"
+ :keywords-value-function denote-format-keywords-for-org-front-matter
+ :keywords-value-reverse-function denote-extract-keywords-from-front-matter
+ :signature-key-regexp "^#\\+signature\\s-*:"
+ :signature-value-function denote-format-string-for-org-front-matter
+ :signature-value-reverse-function denote-trim-whitespace
+ :identifier-key-regexp "^#\\+identifier\\s-*:"
+ :identifier-value-function denote-format-string-for-org-front-matter
+ :identifier-value-reverse-function denote-trim-whitespace
+ :date-key-regexp "^#\\+date\\s-*:"
+ :date-value-function denote-date-org-timestamp
+ :date-value-reverse-function denote-extract-date-from-front-matter
+ :link denote-org-link-format
+ :link-in-context-regexp denote-org-link-in-context-regexp)
+ (markdown-yaml
+ :extension ".md"
+ :front-matter denote-yaml-front-matter
+ :title-key-regexp "^title\\s-*:"
+ :title-value-function denote-format-string-for-md-front-matter
+ :title-value-reverse-function denote-trim-whitespace-then-quotes
+ :keywords-key-regexp "^tags\\s-*:"
+ :keywords-value-function denote-format-keywords-for-md-front-matter
+ :keywords-value-reverse-function denote-extract-keywords-from-front-matter
+ :signature-key-regexp "^signature\\s-*:"
+ :signature-value-function denote-format-string-for-md-front-matter
+ :signature-value-reverse-function denote-trim-whitespace-then-quotes
+ :identifier-key-regexp "^identifier\\s-*:"
+ :identifier-value-function denote-format-string-for-md-front-matter
+ :identifier-value-reverse-function denote-trim-whitespace-then-quotes
+ :date-key-regexp "^date\\s-*:"
+ :date-value-function denote-date-rfc3339
+ :date-value-reverse-function denote-extract-date-from-front-matter
+ :link denote-md-link-format
+ :link-in-context-regexp denote-md-link-in-context-regexp)
+ (markdown-toml
+ :extension ".md"
+ :front-matter denote-toml-front-matter
+ :title-key-regexp "^title\\s-*="
+ :title-value-function denote-format-string-for-md-front-matter
+ :title-value-reverse-function denote-trim-whitespace-then-quotes
+ :keywords-key-regexp "^tags\\s-*="
+ :keywords-value-function denote-format-keywords-for-md-front-matter
+ :keywords-value-reverse-function denote-extract-keywords-from-front-matter
+ :signature-key-regexp "^signature\\s-*="
+ :signature-value-function denote-format-string-for-md-front-matter
+ :signature-value-reverse-function denote-trim-whitespace-then-quotes
+ :identifier-key-regexp "^identifier\\s-*="
+ :identifier-value-function denote-format-string-for-md-front-matter
+ :identifier-value-reverse-function denote-trim-whitespace-then-quotes
+ :date-key-regexp "^date\\s-*="
+ :date-value-function denote-date-rfc3339
+ :date-value-reverse-function denote-extract-date-from-front-matter
+ :link denote-md-link-format
+ :link-in-context-regexp denote-md-link-in-context-regexp)
+ (text
+ :extension ".txt"
+ :front-matter denote-text-front-matter
+ :title-key-regexp "^title\\s-*:"
+ :title-value-function denote-format-string-for-org-front-matter
+ :title-value-reverse-function denote-trim-whitespace
+ :keywords-key-regexp "^tags\\s-*:"
+ :keywords-value-function denote-format-keywords-for-text-front-matter
+ :keywords-value-reverse-function denote-extract-keywords-from-front-matter
+ :signature-key-regexp "^signature\\s-*:"
+ :signature-value-function denote-format-string-for-org-front-matter
+ :signature-value-reverse-function denote-trim-whitespace
+ :identifier-key-regexp "^identifier\\s-*:"
+ :identifier-value-function denote-format-string-for-org-front-matter
+ :identifier-value-reverse-function denote-trim-whitespace
+ :date-key-regexp "^date\\s-*:"
+ :date-value-function denote-date-iso-8601
+ :date-value-reverse-function denote-extract-date-from-front-matter
+ :link denote-org-link-format
+ :link-in-context-regexp denote-org-link-in-context-regexp))
+ "Alist of variable `denote-file-type' and their format properties.
+
+Each element is of the form (SYMBOL PROPERTY-LIST). SYMBOL is one of
+those specified in the user option `denote-file-type' or an arbitrary
+symbol that defines a new file type.
+
+PROPERTY-LIST is a plist that consists of the following elements:
+
+- `:extension' is a string with the file extension including the
+ period.
+
+- `:date-function' is a function that can format a date. See the
+ functions `denote-date-iso-8601', `denote-date-rfc3339', and
+ `denote-date-org-timestamp'.
+
+- `:front-matter' is either a string passed to `format' or a
+ variable holding such a string. The `format' function accepts
+ four arguments, which come from `denote' in this order: TITLE,
+ DATE, KEYWORDS, IDENTIFIER. Read the doc string of `format' on
+ how to reorder arguments.
+
+- `:title-key-regexp' is a regular expression that is used to
+ retrieve the title line in a file. The first line matching
+ this regexp is considered the title line.
+
+- `:title-value-function' is the function used to format the raw
+ title string for inclusion in the front matter (e.g. to
+ surround it with quotes). Use the `identity' function if no
+ further processing is required.
+
+- `:title-value-reverse-function' is the function used to
+ retrieve the raw title string from the front matter. It
+ performs the reverse of `:title-value-function'.
+
+- `:keywords-key-regexp' is a regular expression used to retrieve
+ the keywords' line in the file. The first line matching this
+ regexp is considered the keywords' line.
+
+- `:keywords-value-function' is the function used to format the
+ keywords' list of strings as a single string, with appropriate
+ delimiters, for inclusion in the front matter.
+
+- `:keywords-value-reverse-function' is the function used to
+ retrieve the keywords' value from the front matter. It
+ performs the reverse of the `:keywords-value-function'.
+
+- `:link' is a string, or variable holding a string, that
+ specifies the format of a link. See the variables
+ `denote-org-link-format', `denote-md-link-format'.
+
+- `:link-in-context-regexp' is a regular expression that is used
+ to match the aforementioned link format. See the variables
+ `denote-org-link-in-context-regexp',`denote-md-link-in-context-regexp'.
+
+If the user option `denote-file-type' is nil, use the first element of
+this list for new note creation. The default is `org'.")
+
+(defun denote--file-extension (file-type)
+ "Return file type extension based on FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :extension))
+
+(defun denote--front-matter (file-type)
+ "Return front matter based on FILE-TYPE."
+ (let ((prop (plist-get
+ (alist-get file-type denote-file-types)
+ :front-matter)))
+ (if (symbolp prop)
+ (symbol-value prop)
+ prop)))
+
+(defun denote--title-key-regexp (file-type)
+ "Return the title key regexp associated to FILE-TYPE."
+ (or (plist-get
+ (alist-get file-type denote-file-types)
+ :title-key-regexp)
+ "^DenoteUserWantsEmptyFieldSoHandleIt")) ; Will not be found
+
+(defun denote--title-value-function (file-type)
+ "Convert title string to a front matter title, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :title-value-function))
+
+(defun denote--title-value-reverse-function (file-type)
+ "Convert front matter title to the title string, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :title-value-reverse-function))
+
+(defun denote--keywords-key-regexp (file-type)
+ "Return the keywords key regexp associated to FILE-TYPE."
+ (or (plist-get
+ (alist-get file-type denote-file-types)
+ :keywords-key-regexp)
+ "^DenoteUserWantsEmptyFieldSoHandleIt")) ; Will not be found
+
+(defun denote--keywords-value-function (file-type)
+ "Convert keywords' list to front matter keywords, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :keywords-value-function))
+
+(defun denote--keywords-value-reverse-function (file-type)
+ "Convert front matter keywords to keywords' list, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :keywords-value-reverse-function))
+
+(defun denote--signature-key-regexp (file-type)
+ "Return the signature key regexp associated to FILE-TYPE."
+ (or (plist-get
+ (alist-get file-type denote-file-types)
+ :signature-key-regexp)
+ "^DenoteUserWantsEmptyFieldSoHandleIt")) ; Will not be found
+
+(defun denote--signature-value-function (file-type)
+ "Convert signature string to front matter signature, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :signature-value-function))
+
+(defun denote--signature-value-reverse-function (file-type)
+ "Convert front matter signature to signature string, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :signature-value-reverse-function))
+
+(defun denote--identifier-key-regexp (file-type)
+ "Return the identifier key regexp associated to FILE-TYPE."
+ (or (plist-get
+ (alist-get file-type denote-file-types)
+ :identifier-key-regexp)
+ "^DenoteUserWantsEmptyFieldSoHandleIt")) ; Will not be found
+
+(defun denote--identifier-value-function (file-type)
+ "Convert identifier string to front matter identifier, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :identifier-value-function))
+
+(defun denote--identifier-value-reverse-function (file-type)
+ "Convert front matter identifier to identifier string, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :identifier-value-reverse-function))
+
+(defun denote--date-key-regexp (file-type)
+ "Return the date key regexp associated to FILE-TYPE."
+ (or (plist-get
+ (alist-get file-type denote-file-types)
+ :date-key-regexp)
+ "^DenoteUserWantsEmptyFieldSoHandleIt")) ; Will not be found
+
+(defun denote--date-value-function (file-type)
+ "Convert date object to front matter date, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :date-value-function))
+
+(defun denote--date-value-reverse-function (file-type)
+ "Convert front matter date to date object, per FILE-TYPE."
+ (plist-get
+ (alist-get file-type denote-file-types)
+ :date-value-reverse-function))
+
+(defun denote--link-format (file-type)
+ "Return link format extension based on FILE-TYPE."
+ (let ((prop (plist-get
+ (alist-get file-type denote-file-types)
+ :link)))
+ (if (symbolp prop)
+ (symbol-value prop)
+ prop)))
+
+(defun denote--link-in-context-regexp (file-type)
+ "Return link regexp in context based on FILE-TYPE."
+ (let ((prop (plist-get
+ (alist-get file-type denote-file-types)
+ :link-in-context-regexp)))
+ (if (symbolp prop)
+ (symbol-value prop)
+ prop)))
+
+(defun denote-file-type-extensions ()
+ "Return all file type extensions in `denote-file-types'."
+ (delete-dups
+ (mapcar (lambda (type)
+ (plist-get (cdr type) :extension))
+ denote-file-types)))
+
+(defun denote--file-type-keys ()
+ "Return all `denote-file-types' keys."
+ (delete-dups (mapcar #'car denote-file-types)))
+
+(defun denote--get-component-key-regexp-function (component)
+ "Return COMPONENT's key regexp function.
+
+COMPONENT can be one of `title', `keywords', `identifier', `date', `signature'."
+ (pcase component
+ ('title #'denote--title-key-regexp)
+ ('keywords #'denote--keywords-key-regexp)
+ ('signature #'denote--signature-key-regexp)
+ ('date #'denote--date-key-regexp)
+ ('identifier #'denote--identifier-key-regexp)))
+
+(defun denote--format-front-matter (title date keywords id signature filetype)
+ "Front matter for new notes.
+
+TITLE, SIGNATURE, and ID are strings. DATE is a date object. KEYWORDS
+is a list of strings. FILETYPE is one of the values of variable
+`denote-file-type'."
+ (let* ((fm (denote--front-matter filetype))
+ (title-value-function (denote--title-value-function filetype))
+ (keywords-value-function (denote--keywords-value-function filetype))
+ (id-value-function (denote--identifier-value-function filetype))
+ (signature-value-function (denote--signature-value-function filetype))
+ (title-string (if title-value-function (funcall title-value-function title) ""))
+ (date-string (denote--format-front-matter-date date filetype))
+ (keywords-string (if keywords-value-function (funcall keywords-value-function (denote-sluggify-keywords keywords)) ""))
+ (id-string (if id-value-function (funcall id-value-function id) ""))
+ (signature-string (if signature-value-function (funcall signature-value-function (denote-sluggify-signature signature)) ""))
+ (new-front-matter (if fm (format fm title-string date-string keywords-string id-string signature-string) "")))
+ ;; Remove lines with empty values if the corresponding component
+ ;; is not in `denote-front-matter-components-present-even-if-empty-value'.
+ (with-temp-buffer
+ (insert new-front-matter)
+ (dolist (component '(title date keywords signature identifier))
+ (let ((value (pcase component ('title title) ('keywords keywords) ('signature signature) ('date date) ('identifier id)))
+ (component-key-regexp-function (denote--get-component-key-regexp-function component)))
+ (goto-char (point-min))
+ (when (and (not (denote--component-has-value-p component value))
+ (not (memq component denote-front-matter-components-present-even-if-empty-value))
+ (re-search-forward (funcall component-key-regexp-function filetype) nil t 1))
+ (goto-char (line-beginning-position))
+ (delete-region (line-beginning-position) (line-beginning-position 2)))))
+ (buffer-string))))
+
+;;;; Front matter or content retrieval functions
+
+(defun denote-retrieve-filename-identifier (file)
+ "Extract identifier from FILE name, if present, else return nil.
+
+To create a new one from a date, refer to the function
+`denote-get-identifier'."
+ (let ((filename (file-name-nondirectory file)))
+ (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.
+(defun denote-retrieve-filename-identifier-with-error (file)
+ "Extract identifier from FILE name, if present, else signal an error."
+ (or (denote-retrieve-filename-identifier file)
+ (error "Cannot find `%s' as a file with a Denote identifier" file)))
+
+(defun denote-get-identifier (date)
+ "Convert DATE into a Denote identifier using `denote-id-format'.
+If DATE is nil, return an empty string as the identifier."
+ (if date
+ (format-time-string denote-id-format date)
+ ""))
+
+(defvar denote--used-ids nil
+ "Hash table of used identifiers.
+This variable should be set only for the duration of a command.
+It should stay nil otherwise.")
+
+(define-obsolete-function-alias
+ 'denote-create-unique-file-identifier
+ 'denote-get-identifier
+ "4.0.0")
+
+(defun denote-retrieve-filename-keywords (file)
+ "Extract keywords from FILE name, if present, else return nil.
+Return matched keywords as a single string.
+
+Also see `denote-extract-keywords-from-path' (alias
+`denote-retrieve-filename-keywords-as-list')."
+ (let ((filename (file-name-nondirectory file)))
+ (when (string-match denote-keywords-regexp filename)
+ (match-string 1 filename))))
+
+(defun denote-retrieve-filename-signature (file)
+ "Extract signature from FILE name, if present, else return nil."
+ (let ((filename (file-name-nondirectory file)))
+ (when (string-match denote-signature-regexp filename)
+ (match-string 1 filename))))
+
+(defun denote-retrieve-filename-title (file)
+ "Extract Denote title component from FILE name, else return nil."
+ (let ((filename (file-name-nondirectory file)))
+ (when (string-match denote-title-regexp filename)
+ (match-string 1 filename))))
+
+(defun denote--file-with-temp-buffer-subr (file)
+ "Return path to FILE or its buffer together with the appropriate function.
+Subroutine of `denote--file-with-temp-buffer'."
+ (let* ((buffer (get-file-buffer file))
+ (file-exists (file-exists-p file))
+ (buffer-modified (buffer-modified-p buffer)))
+ (cond
+ ((or (and file-exists
+ buffer
+ (not buffer-modified)
+ (not (eq buffer-modified 'autosaved)))
+ (and file-exists (not buffer)))
+ (cons #'insert-file-contents file))
+ (buffer
+ (cons #'insert-buffer buffer))
+ ;; (t
+ ;; (error "Cannot find anything about file `%s'" file))
+ )))
+
+(defmacro denote--file-with-temp-buffer (file &rest body)
+ "If FILE exists, insert its contents in a temp buffer and call BODY."
+ (declare (indent 1))
+ `(when-let* ((file-and-function (denote--file-with-temp-buffer-subr ,file)))
+ (with-temp-buffer
+ (funcall (car file-and-function) (cdr file-and-function))
+ (goto-char (point-min))
+ ,@body)))
+
+(defmacro denote--define-retrieve-front-matter (component scope)
+ "Define a function to retrieve front matter for COMPONENT given SCOPE.
+The COMPONENT is one of the file name components that has a
+corresponding front matter entry. SCOPE is a symbol of either `value'
+or `line', referring to what the function should retrieve."
+ (declare (indent 1))
+ `(defun ,(intern (format "denote-retrieve-front-matter-%s-%s" component scope)) (file file-type)
+ (when file-type
+ (denote--file-with-temp-buffer file
+ (when (re-search-forward (,(intern (format "denote--%s-key-regexp" component)) file-type) nil t 1)
+ ,(cond
+ ((eq scope 'value)
+ `(funcall (,(intern (format "denote--%s-value-reverse-function" component)) file-type)
+ (buffer-substring-no-properties (point) (line-end-position))))
+ ((eq scope 'line)
+ '(buffer-substring-no-properties (line-beginning-position) (line-end-position)))
+ (t (error "`%s' is not a known scope" scope))))))))
+
+(denote--define-retrieve-front-matter title value)
+(denote--define-retrieve-front-matter title line)
+(denote--define-retrieve-front-matter keywords value)
+(denote--define-retrieve-front-matter keywords line)
+(denote--define-retrieve-front-matter signature value)
+(denote--define-retrieve-front-matter signature line)
+(denote--define-retrieve-front-matter identifier value)
+(denote--define-retrieve-front-matter identifier line)
+(denote--define-retrieve-front-matter date value)
+(denote--define-retrieve-front-matter date line)
+
+;; These are private front matter retrieval functions, working with a content parameter
+
+(defmacro denote--define-retrieve-front-matter-from-content (component scope)
+ "Define a function to retrieve front matter for COMPONENT given SCOPE.
+The COMPONENT is one of the file name components that has a
+corresponding front matter entry. SCOPE is a symbol of either `value'
+or `line', referring to what the function should retrieve."
+ (declare (indent 1))
+ `(defun ,(intern (format "denote--retrieve-front-matter-%s-%s-from-content" component scope)) (content file-type)
+ (when file-type
+ (with-temp-buffer
+ (insert content)
+ (goto-char (point-min))
+ (when (re-search-forward (,(intern (format "denote--%s-key-regexp" component)) file-type) nil t 1)
+ ,(cond
+ ((eq scope 'value)
+ `(funcall (,(intern (format "denote--%s-value-reverse-function" component)) file-type)
+ (buffer-substring-no-properties (point) (line-end-position))))
+ ((eq scope 'line)
+ '(buffer-substring-no-properties (line-beginning-position) (line-end-position)))
+ (t (error "`%s' is not a known scope" scope))))))))
+
+(denote--define-retrieve-front-matter-from-content title value)
+(denote--define-retrieve-front-matter-from-content title line)
+(denote--define-retrieve-front-matter-from-content keywords value)
+(denote--define-retrieve-front-matter-from-content keywords line)
+(denote--define-retrieve-front-matter-from-content signature value)
+(denote--define-retrieve-front-matter-from-content signature line)
+(denote--define-retrieve-front-matter-from-content identifier value)
+(denote--define-retrieve-front-matter-from-content identifier line)
+(denote--define-retrieve-front-matter-from-content date value)
+(denote--define-retrieve-front-matter-from-content date line)
+
+(defalias 'denote-retrieve-title-value 'denote-retrieve-front-matter-title-value
+ "Alias for `denote-retrieve-front-matter-title-value'.")
+
+(defalias 'denote-retrieve-title-line 'denote-retrieve-front-matter-title-line
+ "Alias for `denote-retrieve-front-matter-title-line'.")
+
+(defalias 'denote-retrieve-keywords-value 'denote-retrieve-front-matter-keywords-value
+ "Alias for `denote-retrieve-front-matter-keywords-value'.")
+
+(defalias 'denote-retrieve-keywords-line 'denote-retrieve-front-matter-keywords-line
+ "Alias for `denote-retrieve-front-matter-keywords-line'.")
+
+(defun denote-retrieve-title-or-filename (file type)
+ "Return appropriate title for FILE given its TYPE.
+This is a wrapper for `denote-retrieve-front-matter-title-value' and
+`denote-retrieve-filename-title'."
+ (let ((has-denoted-filename (denote-file-has-denoted-filename-p file))
+ (has-supported-extension (denote-file-has-supported-extension-p file)))
+ (cond ((and has-denoted-filename has-supported-extension)
+ (or (denote-retrieve-front-matter-title-value file type)
+ (denote-retrieve-filename-title file)
+ ""))
+ (has-denoted-filename
+ (or (denote-retrieve-filename-title file) ""))
+ (t
+ (file-name-base file)))))
+
+(make-obsolete 'denote--retrieve-location-in-xrefs 'denote-retrieve-groups-xref-query "4.0.0")
+
+(define-obsolete-function-alias
+ 'denote--retrieve-group-in-xrefs
+ 'denote-retrieve-groups-xref-query
+ "4.0.0")
+
+(defun denote-retrieve-groups-xref-query (query &optional files-matching-regexp)
+ "Access location of xrefs for QUERY and group them per file.
+Limit the search to text files. With optional FILES-MATCHING-REGEXP,
+pass it to `denote-directory-files'."
+ (when-let* ((files (denote-directory-files files-matching-regexp nil :text-only))
+ (locations (mapcar #'xref-match-item-location (xref-matches-in-files query files))))
+ (mapcar #'xref-location-group locations)))
+
+(define-obsolete-function-alias
+ 'denote--retrieve-files-in-xrefs
+ 'denote-retrieve-files-xref-query
+ "4.0.0")
+
+(defun denote-retrieve-files-xref-query (query &optional files-matching-regexp)
+ "Return sorted, deduplicated file names with matches for QUERY in their contents.
+Limit the search to text files. With optional FILES-MATCHING-REGEXP,
+pass it to `denote-directory-files'."
+ (sort
+ (delete-dups
+ (denote-retrieve-groups-xref-query query files-matching-regexp))
+ #'string-collate-lessp))
+
+(defvar denote-query--last-files nil
+ "List of files matched by the last call to `denote-make-links-buffer'.")
+
+(defvar denote-query--last-query nil
+ "String of the last call to `denote-make-links-buffer'.")
+
+(defvar denote-query--omit-current t
+ "When non-nil `denote-make-links-buffer' omits the current file.")
+
+(defun denote-retrieve-xref-alist (query &optional files)
+ "Return xref alist of absolute file paths with location of matches for QUERY.
+Optional FILES can be a list of files to search for. It can also be a
+regular expression, which means to use the text files in the variable
+`denote-directory' that match that regexp.
+
+If FILES is not given, use all text files as returned by
+`denote-directory-files'."
+ (let ((xref-file-name-display 'abs))
+ (xref--analyze
+ (xref-matches-in-files
+ query
+ (if (and files (listp files))
+ files
+ (denote-directory-files files denote-query--omit-current :text-only))))))
+
+;;;; New note
+
+;;;;; Common helpers for new notes
+
+(defun denote-format-file-name (dir-path id keywords title extension signature)
+ "Format file name.
+DIR-PATH, ID, KEYWORDS, TITLE, EXTENSION and SIGNATURE are
+expected to be supplied by `denote' or equivalent command.
+
+DIR-PATH is a string pointing to a directory. It ends with a
+forward slash (the function `denote-directory' makes sure this is
+the case when returning the value of the variable `denote-directory').
+DIR-PATH cannot be nil or an empty string.
+
+ID is a string holding the identifier of the note. It can be an
+empty string, in which case its respective file name component is
+not added to the base file name.
+
+DIR-PATH and ID form the base file name.
+
+KEYWORDS is a list of strings that is reduced to a single string
+by `denote-keywords-combine'. KEYWORDS can be an empty list or
+a nil value, in which case the relevant file name component is
+not added to the base file name.
+
+TITLE and SIGNATURE are strings. They can be an empty string, in
+which case their respective file name component is not added to
+the base file name.
+
+EXTENSION is a string that contains a dot followed by the file
+type extension. It can be an empty string or a nil value, in
+which case it is not added to the base file name."
+ (cond
+ ((null dir-path)
+ (error "DIR-PATH must not be nil"))
+ ((string-empty-p dir-path)
+ (error "DIR-PATH must not be an empty string"))
+ ((not (string-suffix-p "/" dir-path))
+ (error "DIR-PATH does not end with a / as directories ought to")))
+ (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))))))
+ (when (string-empty-p 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-id-regexp "\\'") id)) ; This is always true for now.
+ (setq file-name (substring file-name 2)))
+ (concat dir-path file-name)))
+
+;; Adapted from `org-hugo--org-date-time-to-rfc3339' in the `ox-hugo'
+;; package: <https://github.com/kaushalmodi/ox-hugo>.
+(defun denote-date-rfc3339 (date)
+ "Format DATE using the RFC3339 specification."
+ (if date
+ (replace-regexp-in-string
+ "\\([0-9]\\{2\\}\\)\\([0-9]\\{2\\}\\)\\'" "\\1:\\2"
+ (format-time-string "%FT%T%z" date))
+ ""))
+
+(defun denote-date-org-timestamp (date)
+ "Format DATE using the Org inactive timestamp notation."
+ (if date
+ (format-time-string "[%F %a %R]" date)
+ ""))
+
+(defun denote-date-iso-8601 (date)
+ "Format DATE according to ISO 8601 standard."
+ (if date
+ (format-time-string "%F" date)
+ ""))
+
+(defun denote--format-front-matter-date (date file-type)
+ "Expand DATE in an appropriate format for FILE-TYPE."
+ (let ((format denote-date-format))
+ (cond
+ (format
+ (if date (format-time-string format date) ""))
+ ((when-let* ((fn (denote--date-value-function file-type)))
+ (funcall fn date)))
+ (t
+ (denote-date-org-timestamp date)))))
+
+(defun denote--prepare-note (title keywords date id directory file-type template signature)
+ "Prepare a new note file and return its path.
+
+Arguments TITLE, KEYWORDS, DATE, ID, DIRECTORY, FILE-TYPE,
+TEMPLATE, and SIGNATURE should be valid for note creation."
+ (let* ((path (denote-format-file-name
+ directory id keywords title (denote--file-extension file-type) signature))
+ (buffer (find-file path))
+ (header (denote--format-front-matter title date keywords id signature file-type)))
+ (when (file-regular-p path)
+ (user-error "A file named `%s' already exists" path))
+ (with-current-buffer buffer
+ (insert header)
+ (insert (cond
+ ((stringp template) template)
+ ((functionp template) (funcall template))
+ (t (user-error "Invalid template")))))
+ path))
+
+(defun denote--dir-in-denote-directory-p (directory)
+ "Return non-nil if DIRECTORY is in variable `denote-directory'."
+ (string-prefix-p (denote-directory) (expand-file-name directory)))
+
+(defun denote--valid-file-type (filetype)
+ "Return a valid filetype symbol given the argument FILETYPE.
+If none is found, the first element of `denote-file-types' is
+returned."
+ (let ((type (cond
+ ((stringp filetype) (intern filetype))
+ ((symbolp filetype) filetype)
+ (t (error "The `%s' is neither a string nor a symbol" filetype)))))
+ (cond ((memq type (denote--file-type-keys))
+ type)
+ ((null denote-file-types)
+ (user-error "At least one file type must be defined in `denote-file-types' to create a note"))
+ (t
+ (caar denote-file-types)))))
+
+(defun denote--date-add-current-time (date)
+ "Add current time to DATE, if necessary.
+The idea is to turn 2020-01-15 into 2020-01-15 16:19 so that the
+hour and minute component is not left to 00:00.
+
+This reduces the burden on the user who would otherwise need to
+input that value in order to avoid the error of duplicate
+identifiers.
+
+It also addresses a difference between Emacs 28 and Emacs 29
+where the former does not read dates without a time component."
+ (if (<= (length date) 10)
+ (format "%s %s" date (format-time-string "%H:%M:%S" (current-time)))
+ date))
+
+(define-obsolete-function-alias
+ 'denote-parse-date
+ 'denote-valid-date-p
+ "4.0.0")
+
+(defun denote-valid-date-p (date)
+ "Return DATE as a valid date.
+A valid DATE is a value that can be parsed by either
+`decode-time' or `date-to-time'. Those functions signal an error
+if DATE is a value they do not recognise.
+
+If DATE is nil or an empty string, return nil."
+ (cond ((null date)
+ nil)
+ ((and (stringp date) (string-empty-p date))
+ nil)
+ ((and (or (numberp date) (listp date))
+ (decode-time date))
+ date)
+ (t ; non-empty strings (e.g. "2024-01-01", "2024-01-01 12:00", etc.)
+ (date-to-time (denote--date-add-current-time date)))))
+
+(defun denote--id-to-date (identifier)
+ "Convert IDENTIFIER string to YYYY-MM-DD."
+ (if (denote-identifier-p identifier)
+ (replace-regexp-in-string
+ "\\([0-9]\\{4\\}\\)\\([0-9]\\{2\\}\\)\\([0-9]\\{2\\}\\).*"
+ "\\1-\\2-\\3"
+ identifier)
+ (error "`%s' does not look like a Denote identifier per `denote-id-regexp'" identifier)))
+
+(defun denote--buffer-file-names ()
+ "Return file names of Denote buffers."
+ (delq nil
+ (mapcar
+ (lambda (buffer)
+ (when-let* (((buffer-live-p buffer))
+ (file (buffer-file-name buffer))
+ ((denote-filename-is-note-p file)))
+ file))
+ (buffer-list))))
+
+(defun denote--get-all-used-ids ()
+ "Return a hash-table of all used identifiers.
+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)))
+ (names (append file-names (denote--buffer-file-names))))
+ (dolist (name names)
+ (when-let* ((id (denote-retrieve-filename-identifier name)))
+ (puthash id t ids)))
+ ids))
+
+(defun denote--find-first-unused-id (id)
+ "Return the first unused id starting at ID.
+If ID is already used, increment it 1 second at a time until an
+available id is found."
+ (let ((used-ids (or denote--used-ids (denote--get-all-used-ids)))
+ (current-id id)
+ (iteration 0))
+ (while (gethash current-id used-ids)
+ ;; Prevent infinite loop if `denote-id-format' is misconfigured
+ (setq iteration (1+ iteration))
+ (when (>= iteration 10000)
+ (user-error "A unique identifier could not be found"))
+ (setq current-id (denote-get-identifier (time-add (date-to-time current-id) 1))))
+ current-id))
+
+(defvar denote-command-prompt-history nil
+ "Minibuffer history for `denote-command-prompt'.")
+
+(defalias 'denote--command-prompt-history 'denote-command-prompt-history
+ "Compatibility alias for `denote-command-prompt-history'.")
+
+(defun denote-command-prompt ()
+ "Prompt for command among `denote-commands-for-new-notes'."
+ (let ((default (car denote-command-prompt-history)))
+ (intern
+ (completing-read
+ (format-prompt "Run note-creating Denote command" default)
+ denote-commands-for-new-notes nil :require-match
+ nil 'denote-command-prompt-history default))))
+
+;;;;; The `denote' command and its prompts
+
+(defun denote--prompt-with-completion-p (fn)
+ "Return non-nil if FN prompt should perform completion.
+FN is one among `denote-prompts-with-history-as-completion' and performs
+completion when the user option `denote-history-completion-in-prompts'
+is non-nil."
+ (and denote-history-completion-in-prompts
+ (memq fn denote-prompts-with-history-as-completion)))
+
+(defvar denote-ignore-region-in-denote-command nil
+ "If non-nil, the region is ignored by the `denote' command.
+
+The `denote' command uses the region as the default title when
+prompted for a title. When this variable is non-nil, the
+`denote' command ignores the region. This variable is useful in
+commands that have their own way of handling the region.")
+
+(defvar denote-title-prompt-current-default nil
+ "Currently bound default title for `denote-title-prompt'.
+Set the value of this variable within the lexical scope of a
+command that needs to supply a default title before calling
+`denote-title-prompt'.")
+
+(defun denote--command-with-features (command force-use-file-prompt-as-default-title force-ignore-region force-save in-background)
+ "Execute file-creating COMMAND with specified features.
+
+COMMAND is the symbol of a file-creating command to call, such as
+`denote' or `denote-signature'.
+
+With non-nil FORCE-USE-FILE-PROMPT-AS-DEFAULT-TITLE, use the last
+item of `denote-file-history' as the default title of the title
+prompt. This is useful in a command such as `denote-link' where
+the entry of the file prompt can be reused as the default title.
+
+With non-nil FORCE-IGNORE-REGION, the region is ignore when
+creating the note, i.e. it will not be used as the initial title
+in a title prompt. Else, the value of
+`denote-ignore-region-in-denote-command' is respected.
+
+With non-nil FORCE-SAVE, the file is saved at the end of the note
+creation. Else, the value of `denote-save-buffers' is respected.
+
+With non-nil IN-BACKGROUND, the note creation happens in the
+background, i.e. the note's buffer will not be displayed after
+the note is created.
+
+Note that if all parameters except COMMAND are nil, this is
+equivalent to `(call-interactively command)'.
+
+The path of the newly created file is returned."
+ (let ((denote-save-buffers
+ (or force-save denote-save-buffers))
+ (denote-ignore-region-in-denote-command
+ (or force-ignore-region denote-ignore-region-in-denote-command))
+ (denote-title-prompt-current-default
+ (if force-use-file-prompt-as-default-title
+ denote-file-prompt-latest-input
+ denote-title-prompt-current-default))
+ (path))
+ (if in-background
+ (save-window-excursion
+ (setq path (call-interactively command)))
+ (setq path (call-interactively command)))
+ path))
+
+(defun denote--handle-save-and-kill-buffer (mode file initial-state)
+ "Save and kill buffer of FILE according to MODE and INITIAL-STATE.
+
+The values of `denote-save-buffers' and `denote-kill-buffers' are
+used to decide whether to save and/or kill the buffer visiting
+FILE.
+
+MODE is one of the symbols `creation' or `rename'.
+
+INITIAL-STATE is nil or one of the following symbols:
+`not-visited', `visited'. If a buffer was already visited at the
+beginning of a rename operation, it is NOT killed automatically.
+
+If a buffer needs to be killed, it is also automatically saved,
+no matter the value of `denote-save-buffers'."
+ (let* ((do-kill-buffer (and (not (eq initial-state 'visited))
+ (or (eq denote-kill-buffers t)
+ (and (eq mode 'creation)
+ (eq denote-kill-buffers 'on-creation))
+ (and (eq mode 'rename)
+ (eq denote-kill-buffers 'on-rename)))))
+ (do-save-buffer (or do-kill-buffer denote-save-buffers)))
+ (when-let* ((buffer (find-buffer-visiting file)))
+ (when do-save-buffer (with-current-buffer buffer (save-buffer)))
+ (when do-kill-buffer (kill-buffer buffer)))))
+
+(defvar denote-current-data nil
+ "Store the current unprocessed data passed to `denote'.
+This is an alist where each `car' is one among `title', `keywords',
+`signature', `directory', `date', `id', `file-type', `template'. The
+value each of them contains is the unprocessed input (e.g. the title
+before it is sluggified).
+
+This may be used by the hooks `denote-after-new-note-hook' and
+`denote-after-rename-file-hook' to access the relevant data.")
+
+(defvar denote-use-title nil
+ "The title to be used in a note creation command.
+See the documentation of `denote' for acceptable values. This variable
+is ignored if nil.
+
+Only ever `let' bind this, otherwise the title will always be the same
+and the title prompt will be skipped.")
+
+(defvar denote-use-keywords 'default
+ "The keywords to be used in a note creation command.
+See the documentation of `denote' for acceptable values. This variable
+is ignored if `default'.
+
+Only ever `let' bind this, otherwise the keywords will always be the same
+and the keywords prompt will be skipped.")
+
+(defvar denote-use-signature nil
+ "The signature to be used in a note creation command.
+See the documentation of `denote' for acceptable values. This variable
+is ignored if nil.
+
+Only ever `let' bind this, otherwise the signaturew will always be the same
+and the signature prompt will be skipped.")
+
+(defvar denote-use-file-type nil
+ "The title to be used in a note creation command.
+See the documentation of `denote' for acceptable values. This variable
+is ignored if nil.
+
+Only ever `let' bind this, otherwise the file type will always be the
+same.")
+
+(defvar denote-use-directory nil
+ "The directory to be used in a note creation command.
+See the documentation of `denote' for acceptable values. This variable
+is ignored if nil.
+
+Only ever `let' bind this, otherwise the directory will always be the
+same.")
+
+(defvar denote-use-date nil
+ "The date to be used in a note creation command.
+See the documentation of `denote' for acceptable values. This variable
+is ignored if nil.
+
+Only ever `let' bind this, otherwise the date will always be the same
+and the date prompt will be skipped.")
+
+(defvar denote-use-template nil
+ "The template to be used in a note creation command.
+See the documentation of `denote' for acceptable values. This variable
+is ignored if nil.
+
+Only ever `let' bind this, otherwise the template will always be the same
+and the template prompt will be skipped.")
+
+(defun denote--creation-get-note-data-from-prompts ()
+ "Retrieve the data necessary for note creation.
+
+The data elements are: title, keywords, file-type, directory,
+date, template and signature.
+
+It is retrieved from prompts according to `denote-prompts' and
+from `denote-use-*' variables. For example, if
+`denote-use-title' is set to a title, then no prompts happen for
+the title and the value of `denote-use-title' will be used
+instead."
+ (let (title keywords file-type directory date template signature)
+ (dolist (prompt denote-prompts)
+ (pcase prompt
+ ('title (unless denote-use-title
+ (setq title (denote-title-prompt
+ (when (and (not denote-ignore-region-in-denote-command)
+ (use-region-p))
+ (buffer-substring-no-properties
+ (region-beginning)
+ (region-end)))))))
+ ('keywords (when (eq denote-use-keywords 'default)
+ (setq keywords (denote-keywords-prompt))))
+ ('file-type (unless denote-use-file-type
+ (setq file-type (denote-file-type-prompt))))
+ ('subdirectory (unless denote-use-directory
+ (setq directory (denote-subdirectory-prompt))))
+ ('date (unless denote-use-date
+ (setq date (denote-date-prompt))))
+ ('template (unless denote-use-template
+ (setq template (denote-template-prompt))))
+ ('signature (unless denote-use-signature
+ (setq signature (denote-signature-prompt))))))
+ (list title keywords file-type directory date template signature)))
+
+(defun denote--creation-prepare-note-data (title keywords file-type directory date template signature)
+ "Return parameters in a valid form for file creation.
+
+The data is: TITLE, KEYWORDS, FILE-TYPE, DIRECTORY, DATE,
+TEMPLATE and SIGNATURE. The identifier is also returned.
+
+If a `denote-use-*' variable is set for a data, its value is used
+instead of that of the parameter."
+ (let* (;; Handle the `denote-use-*' variables
+ (title (or denote-use-title title))
+ (keywords (if (eq denote-use-keywords 'default) keywords denote-use-keywords))
+ (file-type (or denote-use-file-type file-type))
+ (directory (or denote-use-directory directory))
+ (date (or denote-use-date date))
+ (template (or denote-use-template template))
+ (signature (or denote-use-signature signature))
+ ;; Make the data valid
+ (title (or title ""))
+ (file-type (denote--valid-file-type (or file-type denote-file-type)))
+ (keywords (denote-keywords-sort keywords))
+ (date (denote-valid-date-p date))
+ (date (cond (date date)
+ ((or (eq denote-generate-identifier-automatically t)
+ (eq denote-generate-identifier-automatically 'on-creation))
+ (current-time))))
+ (id (denote-get-identifier date))
+ (id (if (string-empty-p id) id (denote--find-first-unused-id id)))
+ (date (if (string-empty-p id) nil (date-to-time id)))
+ (directory (if (and directory (denote--dir-in-denote-directory-p directory))
+ (file-name-as-directory directory)
+ (denote-directory)))
+ (template (if (or (stringp template) (functionp template))
+ template
+ (or (alist-get template denote-templates) "")))
+ (signature (or signature "")))
+ (list title keywords file-type directory date id template signature)))
+
+;;;###autoload
+(defun denote (&optional title keywords file-type directory date template signature)
+ "Create a new note with the appropriate metadata and file name.
+
+Run the `denote-after-new-note-hook' after creating the new note and
+return its path. Before returning the path, determine what needs to be
+done to the buffer, in accordance with the user option `denote-kill-buffers'.
+
+When called interactively, the metadata and file name are prompted
+according to the value of `denote-prompts'.
+
+When called from Lisp, all arguments are optional.
+
+- TITLE is a string or a function returning a string.
+
+- KEYWORDS is a list of strings. The list can be empty or the
+ value can be set to nil.
+
+- FILE-TYPE is a symbol among those described in the user option
+ `denote-file-type'.
+
+- DIRECTORY is a string representing the path to either the
+ value of the variable `denote-directory' or a subdirectory
+ thereof. The subdirectory must exist: Denote will not create
+ it. If DIRECTORY does not resolve to a valid path, the
+ variable `denote-directory' is used instead.
+
+- DATE is a string representing a date like 2022-06-30 or a date
+ and time like 2022-06-16 14:30. A nil value or an empty string
+ is interpreted as the `current-time'.
+
+- 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.
+
+- SIGNATURE is a string or a function returning a string."
+ (interactive (denote--creation-get-note-data-from-prompts))
+ (pcase-let* ((`(,title ,keywords ,file-type ,directory ,date ,id ,template ,signature)
+ (denote--creation-prepare-note-data title keywords file-type directory date template signature))
+ (note-path (denote--prepare-note title keywords date id directory file-type template signature)))
+ (denote--keywords-add-to-history keywords)
+ (setq denote-current-data
+ (list
+ (cons 'title title)
+ (cons 'keywords keywords)
+ (cons 'signature signature)
+ (cons 'directory directory)
+ (cons 'date date)
+ (cons 'id id)
+ (cons 'file-type file-type)
+ (cons 'template template)))
+ (run-hooks 'denote-after-new-note-hook)
+ (denote--handle-save-and-kill-buffer 'creation note-path nil)
+ note-path))
+
+(defvar denote-title-history nil
+ "Minibuffer history of `denote-title-prompt'.")
+
+(defalias 'denote--title-history 'denote-title-history
+ "Compatibility alias for `denote-title-history'.")
+
+(defmacro denote--with-conditional-completion (fn prompt history &optional initial-value default-value)
+ "Produce body of FN that may perform completion.
+Use PROMPT, HISTORY, INITIAL-VALUE, and DEFAULT-VALUE as arguments for
+the given minibuffer prompt."
+ `(if (denote--prompt-with-completion-p ,fn)
+ ;; NOTE 2023-10-27: By default SPC performs completion in the
+ ;; minibuffer. We do not want that, as the user should be able to
+ ;; input an arbitrary string, while still performing completion
+ ;; against their input history.
+ (minibuffer-with-setup-hook
+ (lambda ()
+ (use-local-map
+ (let ((map (make-composed-keymap nil (current-local-map))))
+ (define-key map (kbd "SPC") nil)
+ map)))
+ (completing-read ,prompt ,history nil nil ,initial-value ',history ,default-value))
+ (read-string ,prompt ,initial-value ',history ,default-value)))
+
+(defun denote-title-prompt (&optional initial-title prompt-text)
+ "Prompt for title string.
+
+With optional INITIAL-TITLE use it as the initial minibuffer
+text. With optional PROMPT-TEXT use it in the minibuffer instead
+of the default prompt.
+
+Previous inputs at this prompt are available for minibuffer completion
+if the user option `denote-history-completion-in-prompts' is set to a
+non-nil value."
+ (denote--with-conditional-completion
+ 'denote-title-prompt
+ (format-prompt (or prompt-text "New file TITLE") denote-title-prompt-current-default)
+ denote-title-history
+ (or initial-title denote-title-prompt-current-default)
+ denote-title-prompt-current-default))
+
+(defvar denote-file-type-history nil
+ "Minibuffer history of `denote-file-type-prompt'.")
+
+(defalias 'denote--file-type-history 'denote-file-type-history
+ "Compatibility alias for `denote-file-type-history'.")
+
+(defun denote-file-type-prompt ()
+ "Prompt for variable `denote-file-type'.
+Note that a non-nil value other than `text', `markdown-yaml', and
+`markdown-toml' falls back to an Org file type. We use `org'
+here for clarity."
+ (completing-read
+ "Select file TYPE: " (denote--file-type-keys) nil t
+ nil 'denote-file-type-history))
+
+(defvar denote-date-history nil
+ "Minibuffer history of `denote-date-prompt'.")
+
+(defalias 'denote--date-history 'denote-date-history
+ "Compatibility alias for `denote-date-history'.")
+
+(declare-function org-read-date "org" (&optional with-time to-time from-string prompt default-time default-input inactive))
+
+(defun denote--date-convert (date prefer-type)
+ "Determine how to convert DATE to PREFER-TYPE `:list' or `:string'."
+ (unless (memq prefer-type '(:list :string))
+ (error "The PREFER-TYPE must be either `:list' or `:string'"))
+ (cond ((eq prefer-type :list)
+ date)
+ ((eq prefer-type :string)
+ (if date (format-time-string "%F %T" date) ""))))
+
+(defun denote-date-prompt (&optional initial-date prompt-text)
+ "Prompt for date, expecting YYYY-MM-DD or that plus HH:MM.
+Use Org's more advanced date selection utility if the user option
+`denote-date-prompt-use-org-read-date' is non-nil.
+
+With optional INITIAL-DATE use it as the initial minibuffer
+text. With optional PROMPT-TEXT use it in the minibuffer instead
+of the default prompt.
+
+INITIAL-DATE is a string that can be processed by `denote-valid-date-p',
+a value that can be parsed by `decode-time' or nil."
+ (let ((initial-date (denote-valid-date-p initial-date)))
+ (if (and denote-date-prompt-use-org-read-date
+ (require 'org nil :no-error))
+ (let* ((time (org-read-date nil t nil prompt-text (denote--date-convert initial-date :list)))
+ (org-time-seconds (format-time-string "%S" time))
+ (cur-time-seconds (format-time-string "%S" (current-time))))
+ ;; When the user does not input a time, org-read-date defaults to 00 for seconds.
+ ;; When the seconds are 00, we add the current seconds to avoid identifier collisions.
+ (when (string-equal "00" org-time-seconds)
+ (setq time (time-add time (string-to-number cur-time-seconds))))
+ (format-time-string "%Y-%m-%d %H:%M:%S" time))
+ (read-string
+ (or prompt-text "DATE and TIME for note (e.g. 2022-06-16 14:30): ")
+ (denote--date-convert initial-date :string)
+ 'denote-date-history))))
+
+(defun denote-prompt-for-date-return-id (&optional initial-date prompt-text)
+ "Use `denote-date-prompt' and return it as `denote-id-format'.
+Optional INITIAL-DATE and PROMPT-TEXT have the same meaning as
+`denote-date-prompt'."
+ (denote-get-identifier
+ (denote-valid-date-p
+ (denote-date-prompt (denote-valid-date-p initial-date) prompt-text))))
+
+(defvar denote-subdirectory-history nil
+ "Minibuffer history of `denote-subdirectory-prompt'.")
+
+(defalias 'denote--subdir-history 'denote-subdirectory-history
+ "Compatibility alias for `denote-subdirectory-history'.")
+
+;; Making it a completion table is useful for packages that read the
+;; metadata, such as `marginalia' and `embark'.
+(defun denote--subdirs-completion-table (dirs)
+ "Match DIRS as a completion table."
+ (let* ((def (car denote-subdirectory-history))
+ (table (denote--completion-table 'file dirs))
+ (prompt (format-prompt "Select SUBDIRECTORY" def)))
+ (completing-read prompt table nil t nil 'denote-subdirectory-history def)))
+
+(defun denote-subdirectory-prompt ()
+ "Prompt for subdirectory of the variable `denote-directory'.
+The table uses the `file' completion category (so it works with
+packages such as `marginalia' and `embark')."
+ (let* ((root (directory-file-name (denote-directory)))
+ (subdirs (denote-directory-subdirectories))
+ (dirs (push root subdirs)))
+ (denote--subdirs-completion-table dirs)))
+
+(defvar denote-template-history nil
+ "Minibuffer history of `denote-template-prompt'.")
+
+(defalias 'denote--template-history 'denote-template-history
+ "Compatibility alias for `denote-template-history'.")
+
+(defun denote-template-prompt ()
+ "Prompt for template key in `denote-templates' and return its value."
+ (let ((templates denote-templates))
+ (alist-get
+ (intern
+ (completing-read
+ "Select TEMPLATE key: " (mapcar #'car templates)
+ nil t nil 'denote-template-history))
+ templates)))
+
+(defvar denote-signature-history nil
+ "Minibuffer history of `denote-signature-prompt'.")
+
+(defalias 'denote--signature-history 'denote-signature-history
+ "Compatibility alias for `denote-signature-history'.")
+
+(defun denote-signature-prompt (&optional initial-signature prompt-text)
+ "Prompt for signature string.
+With optional INITIAL-SIGNATURE use it as the initial minibuffer
+text. With optional PROMPT-TEXT use it in the minibuffer instead
+of the default prompt.
+
+Previous inputs at this prompt are available for minibuffer completion
+if the user option `denote-history-completion-in-prompts' is set to a
+non-nil value."
+ (when (and initial-signature (string-empty-p initial-signature))
+ (setq initial-signature nil))
+ (denote--with-conditional-completion
+ 'denote-signature-prompt
+ (format-prompt (or prompt-text "New file SIGNATURE") nil)
+ denote-signature-history
+ initial-signature))
+
+(defvar denote-files-matching-regexp-history nil
+ "Minibuffer history of `denote-files-matching-regexp-prompt'.")
+
+(defalias 'denote--files-matching-regexp-hist 'denote-files-matching-regexp-history
+ "Compatibility alias for `denote-files-matching-regexp-history'.")
+
+(defun denote-files-matching-regexp-prompt (&optional prompt-text)
+ "Prompt for REGEXP to filter Denote files by.
+With optional PROMPT-TEXT use it instead of a generic prompt."
+ (denote--with-conditional-completion
+ 'denote-files-matching-regexp-prompt
+ (format-prompt (or prompt-text "Match files with the given REGEXP") nil)
+ denote-files-matching-regexp-history))
+
+;;;;; Convenience commands as `denote' variants
+
+(defalias 'denote-create-note 'denote
+ "Alias for `denote' command.")
+
+(define-obsolete-function-alias
+ 'denote--add-prompts
+ 'denote-add-prompts
+ "3.0.0")
+
+(defun denote-add-prompts (additional-prompts)
+ "Add list of ADDITIONAL-PROMPTS to `denote-prompts'.
+This is best done inside of a `let' to create a wrapper function around
+`denote', `denote-rename-file', and generally any command that consults
+the value of `denote-prompts'."
+ (seq-union additional-prompts denote-prompts))
+
+;;;###autoload
+(defun denote-type ()
+ "Create note while prompting for a file type.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `file-type' prompt appended to its existing prompts."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts (denote-add-prompts '(file-type))))
+ (call-interactively #'denote)))
+
+(defalias 'denote-create-note-using-type 'denote-type
+ "Alias for `denote-type' command.")
+
+;;;###autoload
+(defun denote-date ()
+ "Create note while prompting for a date.
+
+The date can be in YEAR-MONTH-DAY notation like 2022-06-30 or
+that plus the time: 2022-06-16 14:30. When the user option
+`denote-date-prompt-use-org-read-date' is non-nil, the date
+prompt uses the more powerful Org+calendar system.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `date' prompt appended to its existing prompts."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts (denote-add-prompts '(date))))
+ (call-interactively #'denote)))
+
+(defalias 'denote-create-note-using-date 'denote-date
+ "Alias for `denote-date' command.")
+
+;;;###autoload
+(defun denote-subdirectory ()
+ "Create note while prompting for a subdirectory.
+
+Available candidates include the value of the variable
+`denote-directory' and any subdirectory thereof.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `subdirectory' prompt appended to its existing prompts."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts (denote-add-prompts '(subdirectory))))
+ (call-interactively #'denote)))
+
+(defalias 'denote-create-note-in-subdirectory 'denote-subdirectory
+ "Alias for `denote-subdirectory' command.")
+
+;;;###autoload
+(defun denote-template ()
+ "Create note while prompting for a template.
+
+Available candidates include the keys in the `denote-templates'
+alist. The value of the selected key is inserted in the newly
+created note after the front matter.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `template' prompt appended to its existing prompts."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts (denote-add-prompts '(template))))
+ (call-interactively #'denote)))
+
+(defalias 'denote-create-note-with-template 'denote-template
+ "Alias for `denote-template' command.")
+
+;;;###autoload
+(defun denote-signature ()
+ "Create note while prompting for a file signature.
+
+This is the equivalent of calling `denote' when `denote-prompts'
+has the `signature' prompt appended to its existing prompts."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts (denote-add-prompts '(signature))))
+ (call-interactively #'denote)))
+
+(defalias 'denote-create-note-using-signature 'denote-signature
+ "Alias for `denote-signature' command.")
+
+;;;###autoload
+(defun denote-region ()
+ "Call `denote' and insert therein the text of the active region.
+
+Note that, currently, `denote-save-buffers' and
+`denote-kill-buffers' are NOT respected. The buffer is not
+saved or killed at the end of `denote-region'."
+ (declare (interactive-only t))
+ (interactive)
+ (if-let* (((region-active-p))
+ ;; We capture the text early, otherwise it will be empty
+ ;; the moment `insert' is called.
+ (text (buffer-substring-no-properties (region-beginning) (region-end))))
+ (progn
+ (let ((denote-ignore-region-in-denote-command t)
+ ;; FIXME: Find a way to insert the region before the buffer is
+ ;; saved/killed by the creation command.
+ (denote-save-buffers nil)
+ (denote-kill-buffers nil))
+ (call-interactively 'denote))
+ (push-mark (point))
+ (insert text)
+ (run-hook-with-args 'denote-region-after-new-note-functions (mark) (point)))
+ (call-interactively 'denote)))
+
+;;;;; Other convenience commands
+
+;;;###autoload
+(defun denote-open-or-create (target)
+ "Visit TARGET file in variable `denote-directory'.
+If file does not exist, invoke `denote' to create a file. In that case,
+use the last input at the file prompt as the default value of the title
+prompt."
+ (interactive (list (denote-file-prompt nil "Select file (RET on no match to create it)" :no-require-match)))
+ (if (and target (file-exists-p target))
+ (find-file target)
+ (denote--command-with-features #'denote :use-last-input-as-def-title nil nil nil)))
+
+;;;###autoload
+(defun denote-open-or-create-with-command ()
+ "Like `denote-open-or-create' but use one of the `denote-commands-for-new-notes'."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((target (denote-file-prompt nil "Select file (RET on no match to create it)" :no-require-match)))
+ (if (and target (file-exists-p target))
+ (find-file target)
+ (denote--command-with-features (denote-command-prompt) :use-file-prompt-as-def-title nil nil nil))))
+
+;;;; Note modification
+
+;;;;; Common helpers for note modifications
+
+(defun denote--file-types-with-extension (extension)
+ "Return only the entries of `denote-file-types' with EXTENSION.
+See the format of `denote-file-types'."
+ (seq-filter (lambda (type)
+ (string-equal (plist-get (cdr type) :extension) extension))
+ denote-file-types))
+
+(defun denote--file-type-org-extra-p ()
+ "Return non-nil if this is an `org-capture' or Org Note buffer."
+ (and (derived-mode-p 'org-mode)
+ (or (and (bound-and-true-p org-capture-mode)
+ (string-match-p "\\`CAPTURE-.*" (buffer-name)))
+ (string-match-p "\\`\\*Org Note\\*" (buffer-name))
+ (null buffer-file-name))))
+
+(defun denote-file-type (file)
+ "Use the file extension to detect the file type of FILE.
+
+If more than one file type correspond to this file extension, use the
+first file type for which the :title-key-regexp in `denote-file-types'
+matches in the file.
+
+Return nil if the file type is not recognized."
+ (when-let* ((extension (denote-get-file-extension-sans-encryption file))
+ (types (denote--file-types-with-extension extension)))
+ (if (= (length types) 1)
+ (caar types)
+ (or (car (seq-find
+ (lambda (type)
+ (denote--regexp-in-file-p (plist-get (cdr type) :title-key-regexp) file))
+ types))
+ (caar types)))))
+
+(defun denote-filetype-heuristics (file)
+ "Return likely file type of FILE.
+If in the process of `org-capture', consider the file type to be that of
+Org. Otherwise, use the function `denote-file-type' to return the type."
+ (if (denote--file-type-org-extra-p)
+ 'org
+ (denote-file-type file)))
+
+(defun denote--revert-dired (buf)
+ "Revert BUF if appropriate.
+Do it if BUF is in Dired mode and is either part of the variable
+`denote-directory' or the `current-buffer'."
+ (let ((current (current-buffer)))
+ (with-current-buffer buf
+ (when (and (eq major-mode 'dired-mode)
+ (or (and default-directory (denote--dir-in-denote-directory-p default-directory))
+ (eq current buf)))
+ (revert-buffer)))))
+
+(defun denote-update-dired-buffers ()
+ "Update Dired buffers of variable `denote-directory'.
+Also revert the current Dired buffer even if it is not inside the
+variable `denote-directory'."
+ (mapc #'denote--revert-dired (buffer-list)))
+
+(defun denote-rename-file-and-buffer (old-name new-name)
+ "Rename file named OLD-NAME to NEW-NAME, updating buffer name.
+
+If the file exists on the file system, it is renamed. This
+function may be called when creating a new note and the file does
+not exist yet.
+
+If a buffer is visiting the file, its name is updated."
+ (unless (string= (expand-file-name old-name) (expand-file-name new-name))
+ (when (and (file-regular-p old-name)
+ (file-writable-p new-name))
+ (cond
+ ((derived-mode-p 'dired-mode)
+ (dired-rename-file old-name new-name nil))
+ ;; NOTE 2024-02-25: The `vc-rename-file' requires the file to be
+ ;; saved, but our convention is to not save the buffer after
+ ;; changing front matter unless we absolutely have to (allows
+ ;; users to do `diff-buffer-with-file', for example).
+ ((and denote-save-buffers (not (buffer-modified-p)) (vc-backend old-name))
+ (vc-rename-file old-name new-name))
+ (t
+ (rename-file old-name new-name nil))))
+ (when-let* ((buffer (find-buffer-visiting old-name)))
+ (with-current-buffer buffer
+ (set-visited-file-name new-name nil t)))))
+
+(define-obsolete-function-alias
+ 'denote--add-front-matter
+ 'denote-prepend-front-matter
+ "4.0.0")
+
+(defun denote-prepend-front-matter (file title keywords signature date id file-type)
+ "Prepend front matter to FILE.
+The TITLE, KEYWORDS, DATE, ID, SIGNATURE, and FILE-TYPE are passed from
+the renaming command and are used to construct a new front matter block
+if appropriate."
+ (when-let* ((new-front-matter (denote--format-front-matter title date keywords id signature file-type)))
+ (with-current-buffer (find-file-noselect file)
+ (goto-char (point-min))
+ (insert new-front-matter))))
+
+(defun denote--regexp-in-file-p (regexp file)
+ "Return t if REGEXP matches in the FILE."
+ (denote--file-with-temp-buffer file
+ (re-search-forward regexp nil t 1)))
+
+(defun denote-rewrite-keywords (file keywords file-type &optional save-buffer)
+ "Rewrite KEYWORDS in FILE outright according to FILE-TYPE.
+
+Do the same as `denote-rewrite-front-matter' for keywords,
+but do not ask for confirmation.
+
+With optional SAVE-BUFFER, save the buffer corresponding to FILE.
+
+This function is for use in the commands `denote-keywords-add',
+`denote-keywords-remove', `denote-dired-rename-files', or
+related."
+ (let* ((new-front-matter (denote--format-front-matter "" (current-time) keywords "" "" file-type))
+ (new-keywords-line (denote--retrieve-front-matter-keywords-line-from-content new-front-matter file-type)))
+ (with-current-buffer (find-file-noselect file)
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (when (re-search-forward (denote--keywords-key-regexp file-type) nil t 1)
+ (goto-char (line-beginning-position))
+ (insert new-keywords-line)
+ (delete-region (point) (line-end-position))
+ (when save-buffer (save-buffer))))))))
+
+(defun denote--component-has-value-p (component value)
+ "Return non-nil if COMPONENT has a non-nil/non-empty VALUE.
+
+COMPONENT can be one of `title', `keywords', `signature', `date',
+`identifier'.
+
+VALUE is the corresponding value to test.
+
+This function returns nil given an empty string title, signature or
+identifier. It also returns nil given a nil date or nil keywords."
+ (pcase component
+ ('title (not (string-empty-p value)))
+ ('keywords (not (null value)))
+ ('signature (not (string-empty-p (denote-sluggify-signature value))))
+ ('date (not (null value)))
+ ('identifier (not (string-empty-p value)))))
+
+(defun denote--get-old-and-new-front-matter-lines (file new-front-matter file-type)
+ "Return an alist of the old and new front-matter lines for each component.
+
+The FILE contains the old front matter lines.
+
+NEW-FRONT-MATTER is a the front matter with the new values, with the
+format given by FILE-TYPE."
+ `((title . ((old . ,(denote-retrieve-front-matter-title-line file file-type))
+ (new . ,(denote--retrieve-front-matter-title-line-from-content new-front-matter file-type))))
+ (keywords . ((old . ,(denote-retrieve-front-matter-keywords-line file file-type))
+ (new . ,(denote--retrieve-front-matter-keywords-line-from-content new-front-matter file-type))))
+ (signature . ((old . ,(denote-retrieve-front-matter-signature-line file file-type))
+ (new . ,(denote--retrieve-front-matter-signature-line-from-content new-front-matter file-type))))
+ (date . ((old . ,(denote-retrieve-front-matter-date-line file file-type))
+ (new . ,(denote--retrieve-front-matter-date-line-from-content new-front-matter file-type))))
+ (identifier . ((old . ,(denote-retrieve-front-matter-identifier-line file file-type))
+ (new . ,(denote--retrieve-front-matter-identifier-line-from-content new-front-matter file-type))))))
+
+(defun denote--get-front-matter-components-order (content file-type)
+ "Return the components in the order they appear in CONTENT given FILE-TYPE.
+
+Return a list containing the symbols `title', `signature', `keywords',
+`identifier' and `date' in the order that they appear in TEXT. TEXT can
+be any string. For example, it can be a front matter template or an
+entire file content."
+ (let ((components-with-line-numbers '()))
+ (with-temp-buffer
+ (insert content)
+ (goto-char (point-min))
+ (when (re-search-forward (denote--title-key-regexp file-type) nil t 1)
+ (push `(,(line-number-at-pos) . title) components-with-line-numbers))
+ (goto-char (point-min))
+ (when (re-search-forward (denote--keywords-key-regexp file-type) nil t 1)
+ (push `(,(line-number-at-pos) . keywords) components-with-line-numbers))
+ (goto-char (point-min))
+ (when (re-search-forward (denote--signature-key-regexp file-type) nil t 1)
+ (push `(,(line-number-at-pos) . signature) components-with-line-numbers))
+ (goto-char (point-min))
+ (when (re-search-forward (denote--date-key-regexp file-type) nil t 1)
+ (push `(,(line-number-at-pos) . date) components-with-line-numbers))
+ (goto-char (point-min))
+ (when (re-search-forward (denote--identifier-key-regexp file-type) nil t 1)
+ (push `(,(line-number-at-pos) . identifier) components-with-line-numbers)))
+ (mapcar #'cdr
+ (sort components-with-line-numbers (lambda (x y) (< (car x) (car y)))))))
+
+(defun denote--file-has-front-matter-p (file file-type)
+ "Return non-nil if FILE has at least one front-matter line, given FILE-TYPE.
+
+This is checked against its front matter definition. If the front matter
+definition has no lines, this function returns non-nil."
+ (let* ((front-matter (denote--front-matter file-type))
+ (file-content (with-current-buffer (find-file-noselect file) (buffer-string)))
+ (components-in-template (denote--get-front-matter-components-order front-matter file-type))
+ (components-in-file (denote--get-front-matter-components-order file-content file-type)))
+ (or (null components-in-template)
+ (seq-intersection components-in-template components-in-file))))
+
+(defun denote--get-front-matter-rewrite-prompt (final-components to-add to-remove to-modify old-and-new-front-matter-lines)
+ "Return the prompt for the front matter rewrite operation.
+
+FINAL-COMPONENTS is the list of components to handle at the end of the
+rewrite operation.
+
+TO-ADD, TO-REMOVE, and TO-MODIFY are the list of components that needs
+to be added, removed or modified.
+
+OLD-AND-NEW-FRONT-MATTER-LINES is an alist containing the old and new
+front matter lines."
+ (let ((prompt "Replace front matter?"))
+ (dolist (component final-components)
+ (let ((old-line (alist-get 'old (alist-get component old-and-new-front-matter-lines)))
+ (new-line (alist-get 'new (alist-get component old-and-new-front-matter-lines)))
+ (next-prompt ""))
+ (cond ((memq component to-remove)
+ (setq next-prompt (format "\n-%s\n"
+ (propertize old-line 'face 'denote-faces-prompt-old-name))))
+ ((memq component to-add)
+ (setq next-prompt (format "\n-%s\n"
+ (propertize new-line 'face 'denote-faces-prompt-new-name))))
+ ((memq component to-modify)
+ (setq next-prompt (format "\n-%s\n-%s\n"
+ (propertize old-line 'face 'denote-faces-prompt-old-name)
+ (propertize new-line 'face 'denote-faces-prompt-new-name)))))
+ (setq prompt (concat prompt next-prompt))))
+ (concat prompt "?")))
+
+(defun denote--get-final-components-for-rewrite (components-in-file components-in-template components-to-add)
+ "Return the final components to handle by a front matter rewrite operation.
+
+COMPONENTS-TO-ADD is the list of components that have to be added to
+COMPONENTS-IN-FILE to build the list of components that will need to be
+handled during a front matter rewrite operation.
+
+COMPONENTS-IN-TEMPLATE is the list of components in a front matter
+template. They are used to determine how the COMPONENTS-TO-ADD are
+added to COMPONENTS-IN-FILE.
+
+Example:
+ file = (title signature)
+ template = (title keywords date id signature)
+
+The date line is missing from the file. From the template, we find out
+that it needs to be added *after* a keywords line. Since we don't have
+one in the file, we keep looking for a line to add it *after* and find a
+title line. Had we not found the title line in the file, we would have
+searched for a line to insert it *before*. We would have inserted the
+date line before the signature line, for example.
+
+This is repeated until all missing components are added."
+ (let ((final-components (copy-sequence components-in-file)))
+ (dolist (component components-to-add)
+ (if-let* ((previous-components-in-template
+ (seq-take-while (lambda (x) (not (eq x component))) components-in-template))
+ (first-previous-component-in-file
+ (seq-find (lambda (x) (memq x final-components)) (reverse previous-components-in-template))))
+ ;; Insert after the existing element.
+ (let ((sublist final-components))
+ (while sublist
+ (if (not (eq (car sublist) first-previous-component-in-file))
+ (setq sublist (cdr sublist))
+ (push component (cdr sublist))
+ (setq sublist nil))))
+ (let* ((next-components-in-template
+ (cdr (seq-drop-while (lambda (x) (not (eq x component))) components-in-template)))
+ (first-next-component-in-file
+ (seq-find (lambda (x) (memq x final-components)) next-components-in-template)))
+ ;; Insert before the existing element. The intention is to
+ ;; modify final-components, but it does not work when push
+ ;; is called on sublist on the first iteration of the loop.
+ (if (eq (car final-components) first-next-component-in-file)
+ (push component final-components)
+ (let ((sublist final-components))
+ (while sublist
+ (if (not (eq (car sublist) first-next-component-in-file))
+ (setq sublist (cdr sublist))
+ (push component sublist)
+ (setq sublist nil))))))))
+ final-components))
+
+(defun denote-rewrite-front-matter (file title keywords signature date identifier file-type)
+ "Rewrite front matter of note after `denote-rename-file'.
+The FILE, TITLE, KEYWORDS, SIGNATURE, DATE, IDENTIFIER, and FILE-TYPE
+are given by the renaming command and are used to construct new front
+matter values if appropriate.
+
+If `denote-rename-confirmations' contains `rewrite-front-matter',
+prompt to confirm the rewriting of the front matter."
+ (let* ((front-matter (denote--front-matter file-type))
+ (file-content (with-current-buffer (find-file-noselect file) (buffer-string)))
+ (components-in-template (denote--get-front-matter-components-order front-matter file-type))
+ (components-in-file (denote--get-front-matter-components-order file-content file-type))
+ (components-to-add '())
+ (components-to-remove '())
+ (components-to-modify '())
+ (new-front-matter (denote--format-front-matter title date keywords identifier signature file-type))
+ (old-and-new-front-matter-lines (denote--get-old-and-new-front-matter-lines file new-front-matter file-type)))
+ ;; Build the lists of components to add, remove, modify.
+ (dolist (component '(title keywords signature identifier date))
+ ;; Ignore the component if it is not in the template. It is not added, removed or modified.
+ (when (memq component components-in-template)
+ (let ((value (pcase component ('title title) ('keywords keywords) ('signature signature) ('date date) ('identifier identifier))))
+ (cond ((and (not (memq component components-in-file))
+ (denote--component-has-value-p component value))
+ (push component components-to-add))
+ ((and (memq component components-in-file)
+ ;; The component can still be marked for modification.
+ (not (memq component denote-front-matter-components-present-even-if-empty-value))
+ (not (denote--component-has-value-p component value)))
+ (push component components-to-remove))
+ ((and (memq component components-in-file)
+ (not (string= (alist-get 'old (alist-get component old-and-new-front-matter-lines))
+ (alist-get 'new (alist-get component old-and-new-front-matter-lines)))))
+ (push component components-to-modify))))))
+ ;; There should be at least one component in the file and the template.
+ (when (and (seq-intersection components-in-file components-in-template)
+ (or components-to-add components-to-remove components-to-modify))
+ (when-let* ((final-components (denote--get-final-components-for-rewrite
+ components-in-file components-in-template components-to-add)))
+ (with-current-buffer (find-file-noselect file)
+ (when (or (not (memq 'rewrite-front-matter denote-rename-confirmations))
+ (y-or-n-p (denote--get-front-matter-rewrite-prompt
+ final-components
+ components-to-add components-to-remove components-to-modify
+ old-and-new-front-matter-lines)))
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ ;; Position point at the beginning of the first front matter line
+ (let ((first-component (car (seq-difference final-components components-to-add))))
+ (re-search-forward
+ (funcall (denote--get-component-key-regexp-function first-component) file-type) nil t 1)
+ (goto-char (line-beginning-position)))
+ ;; Do the modifications
+ (dolist (component final-components)
+ (let ((component-key-regexp-function (denote--get-component-key-regexp-function component))
+ (new-line (alist-get 'new (alist-get component old-and-new-front-matter-lines))))
+ (cond ((memq component components-to-remove)
+ (re-search-forward (funcall component-key-regexp-function file-type) nil t 1)
+ (delete-region (line-beginning-position) (line-beginning-position 2)))
+ ((memq component components-to-add)
+ (insert (concat new-line "\n")))
+ ((memq component components-to-modify)
+ (re-search-forward (funcall component-key-regexp-function file-type) nil t 1)
+ (goto-char (line-beginning-position))
+ (insert new-line)
+ (delete-region (point) (line-end-position))
+ (goto-char (line-beginning-position 2)))
+ (t
+ (goto-char (line-beginning-position 2))))))))))))))
+
+;;;;; The renaming commands and their prompts
+
+(defun denote--rename-dired-file-or-current-file-or-prompt ()
+ "Return Dired file at point or the current file, else prompt for one.
+Throw error if FILE is not regular, else return FILE."
+ (or (dired-get-filename nil t)
+ buffer-file-name
+ (let* ((file (buffer-file-name))
+ (format (if file
+ (format "Rename FILE Denote-style [%s]: " file)
+ "Rename FILE Denote-style: "))
+ (selected-file (read-file-name format nil file t nil)))
+ (if (or (file-directory-p selected-file)
+ (not (file-regular-p selected-file)))
+ (user-error "Only rename regular files")
+ selected-file))))
+
+(defun denote-rename-file-prompt (old-name new-name)
+ "Prompt to rename file named OLD-NAME to NEW-NAME.
+Return non-nil if the file should be renamed.
+
+If `denote-rename-confirmations' does not contain
+`modify-file-name', return t without prompting."
+ (or (not (memq 'modify-file-name denote-rename-confirmations))
+ (unless (string= (expand-file-name old-name) (expand-file-name new-name))
+ (y-or-n-p
+ (format "Rename %s to %s?"
+ (propertize (file-name-nondirectory old-name) 'face 'denote-faces-prompt-old-name)
+ (propertize (file-name-nondirectory new-name) 'face 'denote-faces-prompt-new-name))))))
+
+(defun denote-add-front-matter-prompt (file)
+ "Prompt to add a front-matter to FILE.
+Return non-nil if a new front matter should be added.
+
+If `denote-rename-confirmations' does not contain
+`add-front-matter', return t without prompting."
+ (or (not (memq 'add-front-matter denote-rename-confirmations))
+ (y-or-n-p
+ (format "Add new front matter to %s?"
+ (propertize (file-name-nondirectory file) 'face 'denote-faces-prompt-new-name)))))
+
+;; NOTE 2023-10-20: We do not need a user option for this, though it
+;; can be useful to have it as a variable.
+(defvar denote-rename-max-mini-window-height 0.33
+ "How much to enlarge `max-mini-window-height' for renaming operations.")
+
+(defun denote--generate-date-for-rename (file)
+ "Generate a date for FILE.
+
+Respect `denote-generate-identifier-automatically'."
+ (if (or (eq denote-generate-identifier-automatically t)
+ (eq denote-generate-identifier-automatically 'on-rename))
+ (or (file-attribute-modification-time (file-attributes file))
+ (current-time))
+ nil))
+
+(defun denote--rename-file (file title keywords signature date)
+ "Rename FILE according to the other parameters.
+Parameters TITLE, KEYWORDS, SIGNATURE and DATE are as described
+in `denote-rename-file' and are assumed to be valid (TITLE and
+SIGNATURE are strings, KEYWORDS is a list, etc.).
+
+This function only does the work necessary to rename a file
+according to its parameters. In particular, it does not prompt
+for anything. It is meant to be combined with
+`denote--rename-get-file-info-from-prompts-or-existing' to create
+a renaming command.
+
+Respect `denote-rename-confirmations', `denote-save-buffers' and
+`denote-kill-buffers'."
+ (let* ((initial-state (if (find-buffer-visiting file) 'visited 'not-visited))
+ (file-type (denote-filetype-heuristics file))
+ (keywords (denote-keywords-sort keywords))
+ (directory (file-name-directory file))
+ (extension (denote-get-file-extension file))
+ (date (or date (denote--generate-date-for-rename file)))
+ (old-id (or (denote-retrieve-filename-identifier file) ""))
+ (id (denote-get-identifier date))
+ (id (cond ((or (string-empty-p id) (string= old-id id))
+ id)
+ ((and (not (string-empty-p old-id)) (denote--file-has-backlinks-p file))
+ (user-error "The date cannot be modified because the file has backlinks"))
+ (t
+ (denote--find-first-unused-id id))))
+ (date (if (string-empty-p id) nil (date-to-time id)))
+ (new-name (denote-format-file-name directory id keywords title extension signature))
+ (max-mini-window-height denote-rename-max-mini-window-height))
+ (when (and (file-regular-p new-name)
+ (not (string= (expand-file-name file) (expand-file-name new-name))))
+ (user-error "The destination file `%s' already exists" new-name))
+ ;; Modify file name, buffer name, or both
+ (when (denote-rename-file-prompt file new-name)
+ (denote-rename-file-and-buffer file new-name))
+ ;; Handle front matter if new-name is of a supported type (rewrite or add front matter)
+ (when (and (denote-file-has-supported-extension-p file)
+ (denote-file-is-writable-and-supported-p new-name))
+ (if (denote--file-has-front-matter-p new-name file-type)
+ (denote-rewrite-front-matter new-name title keywords signature date id file-type)
+ (when (denote-add-front-matter-prompt new-name)
+ (denote-prepend-front-matter new-name title keywords signature date id file-type))))
+ (when (and denote--used-ids (not (string-empty-p id)))
+ (puthash id t denote--used-ids))
+ (denote--handle-save-and-kill-buffer 'rename new-name initial-state)
+ (setq denote-current-data
+ (list
+ (cons 'title title)
+ (cons 'keywords keywords)
+ (cons 'signature signature)
+ (cons 'directory directory)
+ (cons 'date date)
+ (cons 'id id)
+ (cons 'file-type file-type)
+ (cons 'template "")))
+ (run-hooks 'denote-after-rename-file-hook)
+ new-name))
+
+(defun denote--rename-get-file-info-from-prompts-or-existing (file)
+ "Retrieve existing info from FILE and prompt according to `denote-prompts'.
+
+It is meant to be combined with `denote--rename-file' to create
+renaming commands."
+ (let* ((file-in-prompt (propertize (file-relative-name file) 'face 'denote-faces-prompt-current-name))
+ (file-type (denote-filetype-heuristics file))
+ (id (or (denote-retrieve-filename-identifier file) ""))
+ (date (or (denote-valid-date-p id) (denote--generate-date-for-rename file)))
+ (title (or (denote-retrieve-title-or-filename file file-type) ""))
+ (keywords (denote-extract-keywords-from-path file))
+ (signature (or (denote-retrieve-filename-signature file) "")))
+ (dolist (prompt denote-prompts)
+ (pcase prompt
+ ('title
+ (setq title (denote-title-prompt
+ title
+ (format "Rename `%s' with TITLE (empty to remove)" file-in-prompt))))
+ ('keywords
+ (setq keywords (denote-keywords-prompt
+ (format "Rename `%s' with KEYWORDS (empty to remove)" file-in-prompt)
+ (string-join keywords ","))))
+ ('signature
+ (setq signature (denote-signature-prompt
+ signature
+ (format "Rename `%s' with SIGNATURE (empty to remove)" file-in-prompt))))
+ ('date
+ (setq date (denote-valid-date-p (denote-date-prompt
+ date
+ (format "Rename `%s' with DATE" file-in-prompt)))))))
+ (list title keywords signature date)))
+
+;;;###autoload
+(defun denote-rename-file (file title keywords signature date)
+ "Rename file and update existing front matter if appropriate.
+
+Always rename the file where it is located in the file system:
+never move it to another directory.
+
+If in Dired, consider FILE to be the one at point, else the
+current file, else prompt with minibuffer completion for one.
+When called from Lisp, FILE is a file system path represented as
+a string.
+
+If FILE has a Denote-compliant identifier, retain it while
+updating components of the file name referenced by the user
+option `denote-prompts'. By default, these are the TITLE and
+KEYWORDS. The SIGNATURE is another one. When called from Lisp,
+TITLE and SIGNATURE are strings, while KEYWORDS is a list of
+strings.
+
+If there is no identifier, create an identifier based on the
+following conditions:
+
+1. If the `denote-prompts' includes an entry for date prompts,
+ then prompt for DATE and take its input to produce a new
+ identifier. For use in Lisp, DATE must conform with
+ `denote-valid-date-p'.
+
+2. If DATE is nil (e.g. when `denote-prompts' does not include a
+ date entry), use the file attributes to determine the last
+ modified date of FILE and format it as an identifier.
+
+3. As a fallback, derive an identifier from the current date and
+ time.
+
+4. At any rate, if the resulting identifier is not unique among
+ the files in the variable `denote-directory', increment it
+ such that it becomes unique.
+
+In interactive use, and assuming `denote-prompts' includes a
+title entry, make the TITLE prompt have prefilled text in the
+minibuffer that consists of the current title of FILE. The
+current title is either retrieved from the front matter (such as
+the #+title in Org) or from the file name.
+
+Do the same for the SIGNATURE prompt, subject to `denote-prompts',
+by prefilling the minibuffer with the current signature of FILE,
+if any.
+
+Same principle for the KEYWORDS prompt: convert the keywords in
+the file name into a comma-separated string and prefill the
+minibuffer with it (the KEYWORDS prompt accepts more than one
+keywords, each separated by a comma, else the `crm-separator').
+
+For all prompts, interpret an empty input as an instruction to
+remove that file name component. For example, if a TITLE prompt
+is available and FILE is 20240211T093531--some-title__keyword1.org
+then rename FILE to 20240211T093531__keyword1.org.
+
+In interactive use, if there is no entry for a file name
+component in `denote-prompts', keep it as-is.
+
+When called from Lisp, the special symbol `keep-current' can be
+used for the TITLE, KEYWORDS, SIGNATURE and DATE parameters to
+keep them as-is.
+
+[ NOTE: Please check with your minibuffer user interface how to
+ provide an empty input. The Emacs default setup accepts the
+ empty minibuffer contents as they are, though popular packages
+ like `vertico' use the first available completion candidate
+ instead. For `vertico', the user must either move one up to
+ select the prompt and then type RET there with empty contents,
+ or use the command `vertico-exit-input' with empty contents.
+ That Vertico command is bound to M-RET as of this writing on
+ 2024-02-13 08:08 +0200. ]
+
+As a final step, ask for confirmation, showing the difference
+between old and new file names. Do not ask for confirmation if
+the user option `denote-rename-confirmations' does not contain
+the symbol `modify-file-name'.
+
+If FILE has front matter for TITLE and KEYWORDS, ask to rewrite
+their values in order to reflect the new input, unless
+`denote-rename-confirmations' lacks `rewrite-front-matter'. When
+the `denote-save-buffers' is nil (the default), do not save the
+underlying buffer, thus giving the user the option to
+double-check the result, such as by invoking the command
+`diff-buffer-with-file'. The rewrite of the TITLE and KEYWORDS
+in the front matter should not affect the rest of the front
+matter.
+
+If the file does not have front matter but is among the supported file
+types (per the user option `denote-file-type'), add front matter to the
+top of it and leave the buffer unsaved for further inspection. Save the
+buffer if `denote-save-buffers' is non-nil.
+
+When `denote-kill-buffers' is t or `on-rename', kill the buffer
+if it was not already being visited before the rename operation.
+
+For the front matter of each file type, refer to the variables:
+
+- `denote-org-front-matter'
+- `denote-text-front-matter'
+- `denote-toml-front-matter'
+- `denote-yaml-front-matter'
+
+Construct the file name in accordance with the user option
+`denote-file-name-components-order'.
+
+Run the `denote-after-rename-file-hook' after renaming FILE.
+
+This command is intended to (i) rename Denote files, (ii) convert
+existing supported file types to Denote notes, and (ii) rename
+non-note files (e.g. PDF) that can benefit from Denote's
+file-naming scheme.
+
+For a version of this command that works with multiple files
+one-by-one, use `denote-dired-rename-files'."
+ (interactive
+ (let* ((file (denote--rename-dired-file-or-current-file-or-prompt)))
+ (append (list file) (denote--rename-get-file-info-from-prompts-or-existing file))))
+ (let* ((file-type (denote-filetype-heuristics file))
+ (title (if (eq title 'keep-current)
+ (or (denote-retrieve-title-or-filename file file-type) "")
+ title))
+ (keywords (if (eq keywords 'keep-current)
+ (denote-extract-keywords-from-path file)
+ keywords))
+ (signature (if (eq signature 'keep-current)
+ (or (denote-retrieve-filename-signature file) "")
+ signature))
+ (date (if (eq date 'keep-current)
+ (denote-retrieve-filename-identifier file)
+ date))
+ ;; Make the data valid
+ (date (denote-valid-date-p date))
+ (new-name (denote--rename-file file title keywords signature date)))
+ (denote-update-dired-buffers)
+ new-name))
+
+(defun denote-rename-file-title ()
+ "Convenience command to change the title of a file.
+Like `denote-rename-file', but prompts only for the title.
+
+Add or remove a title in one go. Do this by prepopulating the
+minibuffer prompt with the existing title. The user can then modify it
+accordingly. An empty input means to remove the title altogether.
+
+Please check the documentation of `denote-rename-file' with regard to
+how a completion User Interface may accept an empty input."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts '(title)))
+ (call-interactively #'denote-rename-file)))
+
+(defun denote-rename-file-keywords ()
+ "Convenience command to change the keywords of a file.
+Like `denote-rename-file', but prompts only for keywords.
+
+Add or remove keywords in one go. Do this by prepopulating the
+minibuffer prompt with the existing keywords. The user can then insert
+the `crm-separator' (normally a comma), to write new keywords or edit
+what is in the prompt to rewrite them accordingly. An empty input means
+to remove all keywords.
+
+Please check the documentation of `denote-rename-file' with regard to
+how a completion User Interface may accept an empty input."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts '(keywords)))
+ (call-interactively #'denote-rename-file)))
+
+(defun denote-rename-file-date ()
+ "Convenience command to change the date of a file.
+Like `denote-rename-file', but prompts only for the date.
+
+Modify a date in one go."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts '(date)))
+ (call-interactively #'denote-rename-file)))
+
+(define-obsolete-function-alias 'denote-keywords-add 'denote-rename-file-keywords "3.0.0")
+(define-obsolete-function-alias 'denote-rename-add-keywords 'denote-rename-file-keywords "3.0.0")
+(define-obsolete-function-alias 'denote-keywords-remove 'denote-rename-file-keywords "3.0.0")
+(define-obsolete-function-alias 'denote-rename-rename-keywords 'denote-rename-file-keywords "3.0.0")
+
+(defun denote-rename-file-signature ()
+ "Convenience command to change the signature of a file.
+Like `denote-rename-file', but prompts only for the signature.
+
+Add or remove a signature in one go. Do this by prepopulating the
+minibuffer prompt with the existing signature. The user can then modify
+it accordingly. An empty input means to remove the signature
+altogether.
+
+Please check the documentation of `denote-rename-file' with regard to
+how a completion User Interface may accept an empty input."
+ (declare (interactive-only t))
+ (interactive)
+ (let ((denote-prompts '(signature)))
+ (call-interactively #'denote-rename-file)))
+
+(define-obsolete-function-alias 'denote-add-signature 'denote-rename-file-signature "3.0.0")
+(define-obsolete-function-alias 'denote-remove-signature 'denote-rename-file-signature "3.0.0")
+
+;;;###autoload
+(defun denote-dired-rename-files ()
+ "Rename Dired marked files same way as `denote-rename-file'.
+Rename each file in sequence, making all the relevant prompts.
+Unlike `denote-rename-file', do not prompt for confirmation of
+the changes made to the file: perform them outright (same as
+setting `denote-rename-confirmations' to a nil value)."
+ (declare (interactive-only t))
+ (interactive nil dired-mode)
+ (let ((denote--used-ids (denote--get-all-used-ids))
+ (denote-rename-confirmations nil))
+ (if-let* ((marks (dired-get-marked-files)))
+ (progn
+ (dolist (file marks)
+ (pcase-let ((`(,title ,keywords ,signature ,date)
+ (denote--rename-get-file-info-from-prompts-or-existing file)))
+ (denote--rename-file file title keywords signature date)))
+ (denote-update-dired-buffers))
+ (user-error "No marked files; aborting"))))
+
+(defalias 'denote-dired-rename-marked-files 'denote-dired-rename-files
+ "Alias for `denote-dired-rename-files'.")
+
+(defun denote-keywords--combine (combination-type user-input-keywords keywords)
+ "COMBINATION-TYPE is either `:add', `:remove' or `:replace'.
+
+USER-INPUT-KEYWORDS are new keywords collected from the end-user.
+KEYWORDS are the existing keywords for the underlying file.
+
+This function is an internal implementation function."
+ (cond
+ ((eq combination-type :add)
+ (seq-union keywords user-input-keywords))
+ ((eq combination-type :replace)
+ user-input-keywords)
+ ((eq combination-type :remove)
+ (seq-difference keywords user-input-keywords))
+ (t
+ (error "Unknown operation in denote-keywords--combine: %s"
+ combination-type))))
+
+(defun denote-dired-rename-marked-files--change-keywords (combination-type keywords-prompt)
+ "COMBINATION-TYPE is either `:add', `:remove' or `:replace'.
+
+KEYWORDS-PROMPT is the prompt we show the end-user, when taking keywords
+as input.
+
+This function is an internal implementation function."
+ (if-let* ((marks (dired-get-marked-files)))
+ (let ((denote-prompts '())
+ (denote-rename-confirmations nil)
+ (user-input-keywords (denote-keywords-prompt keywords-prompt))
+ (denote--used-ids (denote--get-all-used-ids)))
+ (dolist (file marks)
+ (pcase-let* ((`(,title ,keywords ,signature ,date)
+ (denote--rename-get-file-info-from-prompts-or-existing file))
+ (new-keywords (denote-keywords-sort (denote-keywords--combine combination-type user-input-keywords keywords))))
+ (denote--rename-file file title new-keywords signature date)))
+ (denote-update-dired-buffers))
+ (user-error "No marked files; aborting")))
+
+;;;###autoload
+(defun denote-dired-rename-marked-files-with-keywords ()
+ "Rename marked files in Dired to a Denote file name by writing keywords.
+
+Specifically, do the following:
+
+- retain the file's existing name and make it the TITLE field,
+ per Denote's file-naming scheme;
+
+- sluggify the TITLE, according to our conventions (check the
+ user option `denote-file-name-slug-functions');
+
+- prepend an identifier to the TITLE;
+
+- preserve the file's extension, if any;
+
+- prompt once for KEYWORDS and apply the user's input to the
+ corresponding field in the file name, rewriting any keywords
+ that may exist while removing keywords that do exist if
+ KEYWORDS is empty;
+
+- add or rewrite existing front matter to the underlying file, if it is
+ recognized as a Denote note (per the user option `denote-file-type'),
+ such that it includes the new keywords.
+
+Construct the file name in accordance with the user option
+`denote-file-name-components-order'.
+
+Run the `denote-after-rename-file-hook' after renaming is done.
+
+Also see the specialized commands to only add or remove keywords:
+
+- `denote-dired-rename-marked-files-add-keywords'.
+- `denote-dired-rename-marked-files-remove-keywords'."
+ (declare (interactive-only t))
+ (interactive nil dired-mode)
+ (denote-dired-rename-marked-files--change-keywords
+ :replace "Rename marked files with KEYWORDS, overwriting existing (empty to ignore/remove)"))
+
+;;;###autoload
+(defun denote-dired-rename-marked-files-add-keywords ()
+ "Like `denote-dired-rename-marked-files-with-keywords' to only add keywords."
+ (declare (interactive-only t))
+ (interactive nil dired-mode)
+ (denote-dired-rename-marked-files--change-keywords
+ :add "Add KEYWORDS to marked files"))
+
+;;;###autoload
+(defun denote-dired-rename-marked-files-remove-keywords ()
+ "Like `denote-dired-rename-marked-files-with-keywords' to only remove keywords."
+ (declare (interactive-only t))
+ (interactive nil dired-mode)
+ (denote-dired-rename-marked-files--change-keywords
+ :remove "Remove KEYWORDS from marked files"))
+
+;;;###autoload
+(defun denote-rename-file-using-front-matter (file)
+ "Rename FILE using its front matter as input.
+When called interactively, FILE is the variable `buffer-file-name' or
+the Dired file at point, which is subsequently inspected for the
+requisite front matter. It is thus implied that the FILE has a file
+type that is supported by Denote, per the user option `denote-file-type'.
+
+The values of `denote-rename-confirmations',
+`denote-save-buffers' and `denote-kill-buffers' are respected.
+
+Only the front matter lines that appear in the front matter template (as
+defined in `denote-file-types') will be handled.
+
+To change the identifier (date) of the note with this command, the
+identifier line (if present) of the front matter must be modified.
+Modifying the date line has no effect.
+
+While this command generally does not modify the front matter, there are
+exceptions. The value of the `date' line will follow that of the
+`identifier' line. If they are both in the front matter template and
+the `date' line is missing, it will be added again. Similarly, if they
+are both in the front matter template and the `date' line is present and
+the `identifier' line has been removed, the `date' line will be removed
+as well. Also, if the keywords are out of order and
+`denote-sort-keywords' is non-nil, they will be sorted. There will be a
+prompt for this if `denote-rename-confirmations' contains
+`rewrite-front-matter'.
+
+Construct the file name in accordance with the user option
+`denote-file-name-components-order'."
+ (interactive (list (or (dired-get-filename nil t) buffer-file-name)))
+ (unless (denote-file-is-writable-and-supported-p file)
+ (user-error "The file is not writable or does not have a supported file extension"))
+ (let ((file-type (denote-filetype-heuristics file)))
+ (unless (denote--file-has-front-matter-p file file-type)
+ (user-error "The file does not appear to have a front matter"))
+ (let* ((front-matter-template (denote--front-matter file-type))
+ (components-in-template (denote--get-front-matter-components-order front-matter-template file-type))
+ (title (if (memq 'title components-in-template)
+ (or (denote-retrieve-front-matter-title-value file file-type) "")
+ (or (denote-retrieve-filename-title file) "")))
+ (keywords (if (memq 'keywords components-in-template)
+ (denote-retrieve-front-matter-keywords-value file file-type)
+ (denote-retrieve-filename-keywords-as-list file)))
+ (signature (if (memq 'signature components-in-template)
+ (or (denote-retrieve-front-matter-signature-value file file-type) "")
+ (or (denote-retrieve-filename-signature file) "")))
+ ;; We need to use the identifier because the date line may
+ ;; not contain all the information. For example,
+ ;; "2024-01-01" does not have the time of the note.
+ (date (if (memq 'identifier components-in-template)
+ (when-let* ((id-value (denote-retrieve-front-matter-identifier-value file file-type)))
+ (denote-valid-date-p id-value))
+ (denote-valid-date-p (or (denote-retrieve-filename-identifier file) "")))))
+ (denote--rename-file file title keywords signature date)
+ (denote-update-dired-buffers))))
+
+;;;###autoload
+(defun denote-dired-rename-marked-files-using-front-matter ()
+ "Call `denote-rename-file-using-front-matter' over the Dired marked files.
+Refer to the documentation of that command for the technicalities.
+
+Marked files must count as notes for the purposes of Denote, which means
+that they at least have an identifier in their file name and use a
+supported file type, per the user option `denote-file-type'. Files that
+do not meet this criterion are ignored because Denote cannot know if
+they have front matter and what that may be."
+ (interactive nil dired-mode)
+ (if-let* ((marks (seq-filter
+ (lambda (m)
+ (and (file-regular-p m)
+ (denote-file-is-writable-and-supported-p m)
+ (denote-file-has-identifier-p m)))
+ (dired-get-marked-files))))
+ (let ((denote--used-ids (denote--get-all-used-ids)))
+ (dolist (file marks)
+ (denote-rename-file-using-front-matter file))
+ (denote-update-dired-buffers))
+ (user-error "No marked Denote files; aborting")))
+
+;;;;; Creation of front matter
+
+(make-obsolete 'denote-add-front-matter nil "Use `denote-rename-file' or related. Starting with version 4.0.0.")
+
+;;;###autoload
+(defun denote-change-file-type-and-front-matter (file new-file-type)
+ "Change file type of FILE and add an appropriate front matter.
+
+If in Dired, consider FILE to be the one at point, else the
+current file, else prompt with minibuffer completion for one.
+
+Add a front matter in the format of the NEW-FILE-TYPE at the
+beginning of the file.
+
+Retrieve the title of FILE from a line starting with a title
+field in its front matter, depending on the previous file
+type (e.g. #+title for Org). The same process applies for
+keywords.
+
+As a final step, ask for confirmation, showing the difference
+between old and new file names.
+
+Important note: No attempt is made to modify any other elements
+of the file. This needs to be done manually.
+
+Construct the file name in accordance with the user option
+`denote-file-name-components-order'."
+ (interactive
+ (list
+ (denote--rename-dired-file-or-current-file-or-prompt)
+ (denote--valid-file-type (or (denote-file-type-prompt) denote-file-type))))
+ (let* ((initial-state (if (find-buffer-visiting file) 'visited 'not-visited))
+ (dir (file-name-directory file))
+ (old-file-type (denote-filetype-heuristics file))
+ (id (or (denote-retrieve-filename-identifier file) ""))
+ (date (if (string-empty-p id) nil (date-to-time id)))
+ (title (or (denote-retrieve-title-or-filename file old-file-type) ""))
+ (keywords (denote-retrieve-front-matter-keywords-value file old-file-type))
+ (signature (or (denote-retrieve-filename-signature file) ""))
+ (new-extension (denote--file-extension new-file-type))
+ (new-name (denote-format-file-name dir id keywords title new-extension signature))
+ (max-mini-window-height denote-rename-max-mini-window-height))
+ (when (denote-rename-file-prompt file new-name)
+ (denote-rename-file-and-buffer file new-name)
+ (denote-update-dired-buffers)
+ (when (and (denote-file-is-writable-and-supported-p new-name)
+ (denote-add-front-matter-prompt new-name))
+ (denote-prepend-front-matter new-name title keywords signature date id new-file-type)
+ (denote--handle-save-and-kill-buffer 'rename new-name initial-state)))))
+
+;;;; The Denote faces
+
+(defgroup denote-faces ()
+ "Faces for Denote."
+ :group 'denote)
+
+(defface denote-faces-link '((t :inherit link))
+ "Face used to style Denote links in the buffer."
+ :group 'denote-faces
+ :package-version '(denote . "0.5.0"))
+
+(defface denote-faces-query-link '((t :inherit link-visited))
+ "Face used to style Denote query links in the buffer."
+ :group 'denote-faces
+ :package-version '(denote . "4.0.0"))
+
+(defface denote-faces-subdirectory '((t :inherit bold))
+ "Face for subdirectory of file name.
+This should only ever needed in the backlinks' buffer (or
+equivalent), not in Dired."
+ :group 'denote-faces
+ :package-version '(denote . "0.2.0"))
+
+(defface denote-faces-date '((t :inherit font-lock-variable-name-face))
+ "Face for file name date in Dired buffers.
+This is the part of the identifier that covers the year, month,
+and day."
+ :group 'denote-faces
+ :package-version '(denote . "0.1.0"))
+
+(defface denote-faces-time '((t :inherit denote-faces-date))
+ "Face for file name time in Dired buffers.
+This is the part of the identifier that covers the hours, minutes,
+and seconds."
+ :group 'denote-faces
+ :package-version '(denote . "0.1.0"))
+
+(defface denote-faces-title nil
+ "Face for file name title in Dired buffers."
+ :group 'denote-faces
+ :package-version '(denote . "0.1.0"))
+
+(defface denote-faces-year '((t :inherit denote-faces-date))
+ "Face for file name year in Dired buffers.
+This is the part of the identifier that covers the year, month, and day."
+ :group 'denote-faces
+ :package-version '(denote . "2.3.0"))
+
+(defface denote-faces-month '((t :inherit denote-faces-date))
+ "Face for file name month in Dired buffers.
+This is the part of the identifier that covers the year, month, and day."
+ :group 'denote-faces
+ :package-version '(denote . "2.3.0"))
+
+(defface denote-faces-day '((t :inherit denote-faces-date))
+ "Face for file name day in Dired buffers.
+This is the part of the identifier that covers the year, month, and day."
+ :group 'denote-faces
+ :package-version '(denote . "2.3.0"))
+
+(defface denote-faces-hour '((t :inherit denote-faces-date))
+ "Face for file name hours in Dired buffers.
+This is the part of the identifier that covers the hours, minutes,
+and seconds."
+ :group 'denote-faces
+ :package-version '(denote . "2.3.0"))
+
+(defface denote-faces-minute '((t :inherit denote-faces-date))
+ "Face for file name minutes in Dired buffers.
+This is the part of the identifier that covers the hours, minutes,
+and seconds."
+ :group 'denote-faces
+ :package-version '(denote . "2.3.0"))
+
+(defface denote-faces-second '((t :inherit denote-faces-date))
+ "Face for file name seconds in Dired buffers.
+This is the part of the identifier that covers the hours, minutes,
+and seconds."
+ :group 'denote-faces
+ :package-version '(denote . "2.3.0"))
+
+(defface denote-faces-extension '((t :inherit shadow))
+ "Face for file extension type in Dired buffers."
+ :group 'denote-faces
+ :package-version '(denote . "0.1.0"))
+
+(defface denote-faces-keywords '((t :inherit font-lock-builtin-face))
+ "Face for file name keywords in Dired buffers."
+ :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")
+ (((class color) (min-colors 88) (background dark))
+ :foreground "gray30")
+ (t :inherit shadow))
+ "Face for file name delimiters in Dired buffers."
+ :group 'denote-faces
+ :package-version '(denote . "0.1.0"))
+
+(defface denote-faces-time-delimiter '((t :inherit shadow))
+ "Face for the delimiter between date and time in Dired buffers."
+ :group 'denote-faces
+ :package-version '(denote . "2.1.0"))
+
+;; The following matchers must obey the doc of `font-lock-keywords':
+;; - Have one parameter, the limit of the search
+;; - Set match-data (and restore it on failure)
+;; - Move point after the match (or restore it on failure).
+;; - Return t on success and nil on failure. re-search-forward returns (point) on success. It may be better to do the same.
+
+(defun denote-faces-dired-file-name-matcher (limit)
+ "Find the file name in a Dired line, not looking beyond LIMIT."
+ (let ((initial-match-data (match-data))
+ (initial-point (point))
+ (line-found nil))
+ ;; Find the next non empty line that contains a Dired file name
+ (while (and (not line-found)
+ (re-search-forward "^.+$" limit t))
+ ;; dired-move-to-filename moves the point even if it returns nil
+ (let ((saved-point (point)))
+ (if (and (dired-move-to-filename)
+ (save-match-data
+ (denote-file-has-denoted-filename-p (buffer-substring (point) (line-end-position)))))
+ (setq line-found t)
+ (goto-char saved-point))))
+ (if line-found
+ (let ((beginning-point (point)))
+ (goto-char (match-end 0))
+ (set-match-data (list beginning-point (match-end 0)))
+ (point))
+ (goto-char initial-point)
+ (set-match-data initial-match-data)
+ nil)))
+
+(defun denote-faces-directory-matcher (limit)
+ "Match the directory in a Dired line, not looking beyond LIMIT."
+ (let ((initial-match-data (match-data))
+ (initial-point (point)))
+ (if (re-search-forward "\\(?1:.*/\\)[^/]*$" limit t)
+ (progn
+ (goto-char (match-end 1))
+ (set-match-data (list (match-beginning 1) (match-end 1)))
+ (point))
+ (goto-char initial-point)
+ (set-match-data initial-match-data)
+ nil)))
+
+(defun denote-faces-signature-matcher (limit)
+ "Match the signature in a Dired line, not looking beyond LIMIT."
+ (let ((initial-match-data (match-data))
+ (initial-point (point)))
+ (if (or (re-search-forward "==\\(?1:[^/]*?\\)\\(@@\\|--\\|__\\|==\\|\\.\\)[^/]*$" limit t)
+ (re-search-forward "==\\(?1:[^/]*\\)$" limit t))
+ (progn
+ (goto-char (match-end 1))
+ (set-match-data (list (match-beginning 1) (match-end 1)))
+ (point))
+ (goto-char initial-point)
+ (set-match-data initial-match-data)
+ nil)))
+
+(defun denote-faces-identifier-matcher (limit)
+ "Match a general identifier in a Dired line, not looking beyond LIMIT."
+ (let ((initial-match-data (match-data))
+ (initial-point (point)))
+ (if (or (re-search-forward "@@\\(?1:[^/]*?\\)\\(@@\\|--\\|__\\|==\\|\\.\\)[^/]*$" limit t)
+ (re-search-forward "@@\\(?1:[^/]*\\)$" limit t))
+ (progn
+ (goto-char (match-end 1))
+ (set-match-data (list (match-beginning 1) (match-end 1)))
+ (point))
+ (goto-char initial-point)
+ (set-match-data initial-match-data)
+ nil)))
+
+(defun denote-faces-title-matcher (limit)
+ "Match the title in a Dired line, not looking beyond LIMIT."
+ (let ((initial-match-data (match-data))
+ (initial-point (point)))
+ (if (or (re-search-forward "--\\(?1:[^/]*?\\)\\(@@\\|__\\|==\\|\\.\\)[^/]*$" limit t)
+ (re-search-forward "--\\(?1:[^/]*\\)$" limit t))
+ (progn
+ (goto-char (match-end 1))
+ (set-match-data (list (match-beginning 1) (match-end 1)))
+ (point))
+ (goto-char initial-point)
+ (set-match-data initial-match-data)
+ nil)))
+
+(defun denote-faces-keywords-matcher (limit)
+ "Match the keywords in a Dired line, not looking beyond LIMIT."
+ (let ((initial-match-data (match-data))
+ (initial-point (point)))
+ (if (or (re-search-forward "__\\(?1:[^/]*?\\)\\(@@\\|--\\|__\\|==\\|\\.\\)[^/]*$" limit t)
+ (re-search-forward "__\\(?1:[^/]*\\)$" limit t))
+ (progn
+ (goto-char (match-end 1))
+ (set-match-data (list (match-beginning 1) (match-end 1)))
+ (point))
+ (goto-char initial-point)
+ (set-match-data initial-match-data)
+ nil)))
+
+(defconst denote-faces-matchers
+ `((denote-faces-directory-matcher
+ (goto-char (match-beginning 0))
+ (goto-char (match-end 0))
+ (0 'denote-faces-subdirectory nil t))
+ ;; Identifier with format 00000000T000000
+ ("\\(?1:[0-9]\\{4\\}\\)\\(?2:[0-9]\\{2\\}\\)\\(?3:[0-9]\\{2\\}\\)\\(?7:T\\)\\(?4:[0-9]\\{2\\}\\)\\(?5:[0-9]\\{2\\}\\)\\(?6:[0-9]\\{2\\}\\)"
+ (goto-char (match-beginning 0)) ; pre-form, executed before looking for the first identifier
+ (goto-char (match-end 0)) ; post-form, executed after all matches (identifiers here) are found
+ (1 'denote-faces-year nil t)
+ (2 'denote-faces-month nil t)
+ (3 'denote-faces-day nil t)
+ (4 'denote-faces-hour nil t)
+ (5 'denote-faces-minute nil t)
+ (6 'denote-faces-second nil t)
+ (7 'denote-faces-delimiter nil t))
+ ;; Identifier with general format (not yet possible)
+ (denote-faces-identifier-matcher
+ (goto-char (match-beginning 0))
+ (goto-char (match-end 0))
+ (0 'denote-faces-date nil t))
+ ;; Title
+ (denote-faces-title-matcher
+ (goto-char (match-beginning 0))
+ (goto-char (match-end 0))
+ (0 'denote-faces-title nil t))
+ ;; Keywords
+ (denote-faces-keywords-matcher
+ (goto-char (match-beginning 0))
+ (goto-char (match-end 0))
+ (0 'denote-faces-keywords nil t))
+ ;; Signature
+ (denote-faces-signature-matcher
+ (goto-char (match-beginning 0))
+ (goto-char (match-end 0))
+ (0 'denote-faces-signature nil t))
+ ;; Delimiters
+ ("\\(@@\\|--\\|__\\|==\\)"
+ (goto-char (match-beginning 0))
+ (goto-char (match-end 0))
+ (0 'denote-faces-delimiter nil t))
+ ;; Extension
+ ("\\..*$"
+ (goto-char (match-beginning 0))
+ (goto-char (match-end 0))
+ (0 'denote-faces-extension nil t)))
+ "Matchers for fontification of file names.")
+
+(defconst denote-faces-file-name-keywords-for-dired
+ `((denote-faces-dired-file-name-matcher ,@denote-faces-matchers))
+ "Keywords for fontification of file names.")
+
+(make-obsolete-variable 'denote-faces-file-name-keywords-for-backlinks nil "4.0.0")
+
+(defface denote-faces-prompt-old-name '((t :inherit error))
+ "Face for the old name shown in the prompt of `denote-rename-file' etc."
+ :group 'denote-faces
+ :package-version '(denote . "2.2.0"))
+
+(defface denote-faces-prompt-new-name '((t :inherit success))
+ "Face for the new name shown in the prompt of `denote-rename-file' etc."
+ :group 'denote-faces
+ :package-version '(denote . "2.2.0"))
+
+(defface denote-faces-prompt-current-name '((t :inherit denote-faces-prompt-old-name))
+ "Face for the current file shown in the prompt of `denote-rename-file' etc."
+ :group 'denote-faces
+ :package-version '(denote . "2.2.0"))
+
+;;;; Fontification in Dired
+
+(defgroup denote-dired ()
+ "Integration between Denote and Dired."
+ :group 'denote)
+
+(defcustom denote-dired-directories (list denote-directory)
+ "List of directories where `denote-dired-mode' should apply to.
+For this to take effect, add `denote-dired-mode-in-directories',
+to the `dired-mode-hook'.
+
+If `denote-dired-directories-include-subdirectories' is non-nil,
+also apply the effect to all subdirectories of those specified in
+the list."
+ :type '(repeat directory)
+ :package-version '(denote . "0.1.0")
+ :link '(info-link "(denote) Fontification in Dired")
+ :group 'denote-dired)
+
+(defcustom denote-dired-directories-include-subdirectories nil
+ "If non-nil `denote-dired-directories' also affects all subdirectories.
+Otherwise `denote-dired-directories' works only with exact matches."
+ :package-version '(denote . "2.2.0")
+ :link '(info-link "(denote) Fontification in Dired")
+ :type 'boolean
+ :group 'denote-dired)
+
+;; FIXME 2022-08-12: Make `denote-dired-mode' work with diredfl. This
+;; may prove challenging.
+
+(defun denote-dired-add-font-lock (&rest _)
+ "Append `denote-faces-file-name-keywords' to font lock keywords."
+ ;; NOTE 2023-10-28: I tried to add the first argument and then
+ ;; experimented with various combinations of keywords, such as
+ ;; `(,@dired-font-lock-keywords ,@denote-faces-file-name-keywords).
+ ;; None of them could be unset upon disabling `denote-dired-mode'.
+ ;; As such, I am using the `when' here.
+ (when (derived-mode-p 'dired-mode)
+ (font-lock-add-keywords nil denote-faces-file-name-keywords-for-dired t)))
+
+(defun denote-dired-remove-font-lock (&rest _)
+ "Remove `denote-faces-file-name-keywords' from font lock keywords."
+ ;; See NOTE in `denote-dired-add-font-lock'.
+ (when (derived-mode-p 'dired-mode)
+ (font-lock-remove-keywords nil denote-faces-file-name-keywords-for-dired)))
+
+(declare-function wdired-change-to-wdired-mode "wdired")
+(declare-function wdired-finish-edit "wdired")
+
+;;;###autoload
+(define-minor-mode denote-dired-mode
+ "Fontify all Denote-style file names.
+Add this or `denote-dired-mode-in-directories' to
+`dired-mode-hook'."
+ :global nil
+ :group 'denote-dired
+ (if denote-dired-mode
+ (progn
+ (denote-dired-add-font-lock)
+ (advice-add #'wdired-change-to-wdired-mode :after #'denote-dired-add-font-lock)
+ (advice-add #'wdired-finish-edit :after #'denote-dired-add-font-lock))
+ (denote-dired-remove-font-lock)
+ (advice-remove #'wdired-change-to-wdired-mode #'denote-dired-add-font-lock)
+ (advice-remove #'wdired-finish-edit #'denote-dired-add-font-lock))
+ (font-lock-flush (point-min) (point-max)))
+
+(defun denote-dired--modes-dirs-as-dirs ()
+ "Return `denote-dired-directories' as directories.
+The intent is to basically make sure that however a path is
+written, it is always returned as a directory."
+ (mapcar
+ (lambda (dir)
+ (file-name-as-directory (file-truename dir)))
+ denote-dired-directories))
+
+;;;###autoload
+(defun denote-dired-mode-in-directories ()
+ "Enable `denote-dired-mode' in `denote-dired-directories'.
+Add this function to `dired-mode-hook'.
+
+If `denote-dired-directories-include-subdirectories' is non-nil,
+also enable it in all subdirectories."
+ (when-let* ((dirs (denote-dired--modes-dirs-as-dirs))
+ ;; Also include subdirs
+ ((or (member (file-truename default-directory) dirs)
+ (and denote-dired-directories-include-subdirectories
+ (seq-some
+ (lambda (dir)
+ (string-prefix-p dir (file-truename default-directory)))
+ dirs)))))
+ (denote-dired-mode 1)))
+
+;;;; The linking facility
+
+;;;;; Link to note
+
+(defvar denote-org-link-format "[[denote:%s][%s]]"
+ "Format of Org link to note.
+The value is passed to `format' with IDENTIFIER and TITLE
+arguments, in this order.
+
+Also see `denote-org-link-in-context-regexp'.")
+
+(defvar denote-md-link-format "[%2$s](denote:%1$s)"
+ "Format of Markdown link to note.
+The %N$s notation used in the default value is for `format' as
+the supplied arguments are IDENTIFIER and TITLE, in this order.
+
+Also see `denote-md-link-in-context-regexp'.")
+
+(defvar denote-id-only-link-format "[[denote:%s]]"
+ "Format of identifier-only link to note.
+The value is passed to `format' with IDENTIFIER as its sole
+argument.
+
+Also see `denote-id-only-link-in-context-regexp'.")
+
+(defvar denote-org-link-in-context-regexp
+ (concat "\\[\\[" "denote:"
+ "\\(?1:[^][]*?\\)"
+ "\\(?:::.*\\)?" "]"
+ "\\[" "\\(?2:" ".*?" "\\)" "]]")
+ "Regexp to match an Org link in its context.
+The format of such links is `denote-org-link-format'.")
+
+(defvar denote-md-link-in-context-regexp
+ (concat "\\[" "\\(?2:" ".*?" "\\)" "]"
+ "(denote:" "\\(?1:[^][]*?\\)" ")")
+ "Regexp to match a Markdown link in its context.
+The format of such links is `denote-md-link-format'.")
+
+(defvar denote-id-only-link-in-context-regexp
+ (concat "\\[\\[" "denote:" "\\(?1:[^][]*?\\)" "]]")
+ "Regexp to match an identifier-only link in its context.
+The format of such links is `denote-id-only-link-format'." )
+
+(defun denote-format-link (file description file-type id-only &optional include-date)
+ "Prepare link to FILE using DESCRIPTION.
+
+FILE-TYPE and ID-ONLY are used to get the format of the link.
+See the `:link' property of `denote-file-types'.
+
+With optional INCLUDE-DATE, convert the identifier using
+`denote--id-to-date' and append it to DESCRIPTION."
+ (let* ((identifier (denote-retrieve-filename-identifier file))
+ (desc (if include-date
+ (format "%s (%s)" description (denote--id-to-date identifier))
+ description)))
+ (format
+ (cond
+ ((or id-only (null description) (string-empty-p description))
+ denote-id-only-link-format)
+ ;; NOTE 2024-05-20: If there is no file type, we want to use the
+ ;; Org format because it is still a usable link with the help of
+ ;; the command `org-open-at-point-global'.
+ ((null file-type)
+ (denote--link-format 'org))
+ (t
+ (denote--link-format file-type)))
+ identifier
+ desc)))
+
+(defun denote-link-description-with-signature-and-title (file)
+ "Return link description for FILE.
+
+- If the region is active, use it as the description.
+
+- If FILE has a signature, then format the description as a sequence of
+ the signature text and the title with two spaces between them.
+
+- If FILE does not have a signature, then use its title as the
+ description.
+
+This is useful as the value of the user option
+`denote-link-description-function'."
+ (let* ((file-type (denote-filetype-heuristics file))
+ (signature (denote-retrieve-filename-signature file))
+ (title (denote-retrieve-title-or-filename file file-type))
+ (region-text (denote--get-active-region-content)))
+ (cond
+ (region-text region-text)
+ ((and signature title) (format "%s %s" signature title))
+ (title (format "%s" title))
+ (signature (format "%s" signature))
+ (t ""))))
+
+(defun denote--get-active-region-content ()
+ "Return the text of the active region, else nil."
+ (when-let* (((region-active-p))
+ (beg (region-beginning))
+ (end (region-end)))
+ (string-trim (buffer-substring-no-properties beg end))))
+
+(defun denote--delete-active-region-content ()
+ "Delete the content of the active region, if any."
+ (when-let* (((region-active-p))
+ (beg (region-beginning))
+ (end (region-end)))
+ (delete-region beg end)))
+
+(defun denote-get-link-description (file)
+ "Return a link description for FILE.
+
+If `denote-link-description-format' is a function, call it with FILE as
+an argument. The function should return a string, representing the link
+description.
+
+If the user option `denote-link-description-format' is a string, parse
+it to substitute any format specifiers therein with their respective
+values (see the documentation of that user option). If the region is
+active, use it as the description."
+ (cond
+ ((functionp denote-link-description-format)
+ (funcall denote-link-description-format file))
+ ((stringp denote-link-description-format)
+ (if-let* ((region (denote--get-active-region-content)))
+ region
+ (let ((type (denote-filetype-heuristics file)))
+ (string-trim
+ (format-spec denote-link-description-format
+ (list (cons ?t (cond
+ ((denote-retrieve-front-matter-title-value file (denote-filetype-heuristics file)))
+ ((denote-retrieve-filename-title file))
+ (t "")))
+ (cons ?T (or (denote-retrieve-filename-title file) ""))
+ (cons ?i (or (denote-retrieve-filename-identifier file) ""))
+ ;; TODO 2025-04-03: Maybe we can have something like `denote-date-format' here,
+ ;; but I think we are okay with a hardcoded value.
+ (cons ?I (or (when-let* ((id (denote-retrieve-filename-identifier file))
+ (_ (denote-valid-date-p id)))
+ (format-time-string "%A, %e %B %Y" (date-to-time (denote--id-to-date id))))
+ ""))
+ (cons ?D (cond
+ ((denote-retrieve-front-matter-title-value file type))
+ ((denote-retrieve-filename-title file))
+ ((when-let* ((id (denote-retrieve-filename-identifier file)))
+ (if (denote-valid-date-p id)
+ (format-time-string "%A, %e %B %Y" (date-to-time (denote--id-to-date id)))
+ id)))
+ (t "")))
+ (cons ?d (or (denote-retrieve-filename-identifier file) ""))
+ (cons ?s (or (denote-retrieve-filename-signature file) ""))
+ (cons ?k (or (denote-retrieve-filename-keywords file) ""))
+ (cons ?% "%"))
+ 'delete)))))
+ (t
+ (error "The `denote-link-description-format' must be a function or string"))))
+
+(define-obsolete-function-alias
+ 'denote--link-get-description
+ 'denote-get-link-description
+ "4.0.0")
+
+;;;###autoload
+(defun denote-link (file file-type description &optional id-only)
+ "Create link to FILE note in variable `denote-directory' with DESCRIPTION.
+
+When called interactively, prompt for FILE using completion. In this
+case, derive FILE-TYPE from the current buffer. FILE-TYPE is used to
+determine the format of the link.
+
+Return the DESCRIPTION of the link in the format specified by
+`denote-link-description-format'. The default is to return the text of
+the active region or the title of the note (plus the signature if
+present).
+
+With optional ID-ONLY as a non-nil argument, such as with a universal
+prefix (\\[universal-argument]), insert links with just the identifier
+and no further description. In this case, the link format is always
+[[denote:IDENTIFIER]].
+
+If the DESCRIPTION is empty, format the link the same as with ID-ONLY.
+
+When called from Lisp, FILE is a string representing a full file system
+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"))
+ (file-type (denote-filetype-heuristics buffer-file-name))
+ (description (when (file-exists-p file)
+ (denote-get-link-description file))))
+ (list file file-type description current-prefix-arg)))
+ (unless (or (denote--file-type-org-extra-p)
+ (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name)))
+ (user-error "The current file type is not recognized by Denote"))
+ (unless (file-exists-p file)
+ (user-error "The linked file does not exist"))
+ (denote--delete-active-region-content)
+ (insert (denote-format-link file description file-type id-only)))
+
+(defalias 'denote-insert-link 'denote-link
+ "Alias for `denote-link' command.")
+
+(make-obsolete 'denote-link-with-signature nil " 4.0.0: Use the `denote-link-description-format'.")
+
+(defun denote-link--collect-identifiers (regexp)
+ "Return collection of identifiers in buffer matching REGEXP."
+ (let (matches)
+ (save-excursion
+ (goto-char (point-min))
+ (while (or (re-search-forward regexp nil t)
+ (re-search-forward denote-id-only-link-in-context-regexp nil t))
+ (push (match-string-no-properties 1) matches)))
+ matches))
+
+(defun denote-link--expand-identifiers (regexp)
+ "Expend identifiers matching REGEXP into file paths."
+ (let ((files (denote-directory-files))
+ found-files)
+ (dolist (file files)
+ (dolist (i (denote-link--collect-identifiers regexp))
+ (when (string= i (denote-retrieve-filename-identifier file))
+ (push file found-files))))
+ found-files))
+
+(defvar denote-link-find-file-history nil
+ "History for `denote-find-link'.")
+
+(defalias 'denote-link--find-file-history 'denote-link-find-file-history
+ "Compatibility alias for `denote-link-find-file-history'.")
+
+(defun denote-select-linked-file-prompt (files)
+ "Prompt for linked file among FILES."
+ (let ((file-names (mapcar #'denote-get-file-name-relative-to-denote-directory files)))
+ (completing-read
+ "Find linked file: "
+ (denote--completion-table 'file file-names)
+ nil t nil 'denote-link-find-file-history)))
+
+(define-obsolete-function-alias
+ 'denote-link--find-file-prompt
+ 'denote-select-linked-file-prompt
+ "3.0.0")
+
+(defun denote-link-return-links (&optional file)
+ "Return list of links in current or optional FILE.
+Also see `denote-link-return-backlinks'."
+ (when-let* ((current-file (or file (buffer-file-name)))
+ ((denote-file-has-supported-extension-p current-file))
+ (file-type (denote-filetype-heuristics current-file))
+ (regexp (denote--link-in-context-regexp file-type))
+ (files (denote-directory-files))
+ (file-identifiers
+ (with-temp-buffer
+ (insert-file-contents current-file)
+ (denote-link--collect-identifiers regexp)))
+ (file-identifiers-hash-table (make-hash-table :test 'equal)))
+ (dolist (id file-identifiers)
+ (puthash id t file-identifiers-hash-table))
+ (let ((found-files))
+ (dolist (file files)
+ (when (gethash (denote-retrieve-filename-identifier file) file-identifiers-hash-table)
+ (push file found-files)))
+ found-files)))
+
+(defalias 'denote-link-return-forelinks 'denote-link-return-links
+ "Alias for `denote-link-return-links'.")
+
+;;;###autoload
+(defun denote-find-link ()
+ "Use minibuffer completion to visit linked file.
+Also see `denote-find-backlink'."
+ (declare (interactive-only t))
+ (interactive)
+ (find-file
+ (concat
+ (denote-directory)
+ (denote-select-linked-file-prompt
+ (or (denote-link-return-links)
+ (user-error "No links found"))))))
+
+;;;###autoload
+(defun denote-link-after-creating (&optional id-only)
+ "Create new note in the background and link to it directly.
+
+Use `denote' interactively to produce the new note. Its doc
+string explains which prompts will be used and under what
+conditions.
+
+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'.
+
+For a variant of this, see `denote-link-after-creating-with-command'.
+
+IMPORTANT NOTE: Normally, `denote' does not save the buffer it
+produces for the new note. This is a safety precaution to not
+write to disk unless the user wants it (e.g. the user may choose
+to kill the buffer, thus cancelling the creation of the note).
+However, for this command the creation of the note happens in the
+background and the user may miss the step of saving their buffer.
+We thus have to save the buffer in order to (i) establish valid
+links, and (ii) retrieve whatever front matter from the target
+file. Though see `denote-save-buffer-after-creation'."
+ (interactive "P")
+ (unless (or (denote--file-type-org-extra-p)
+ (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* ((type (denote-filetype-heuristics (buffer-file-name)))
+ (path (denote--command-with-features #'denote nil nil :save :in-background))
+ (description (denote-get-link-description path)))
+ (denote-link path type description id-only)))
+
+;;;###autoload
+(defun denote-link-after-creating-with-command (command &optional id-only)
+ "Like `denote-link-after-creating' but prompt for note-making COMMAND.
+Use this to, for example, call `denote-signature' so that the
+newly created note has a signature as part of its file name.
+
+Optional ID-ONLY has the same meaning as in the command
+`denote-link-after-creating'."
+ (interactive
+ (list
+ (denote-command-prompt)
+ current-prefix-arg))
+ (unless (or (denote--file-type-org-extra-p)
+ (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* ((type (denote-filetype-heuristics (buffer-file-name)))
+ (path (denote--command-with-features command nil nil :save :in-background))
+ (description (denote-get-link-description path)))
+ (denote-link path type description id-only)))
+
+;;;###autoload
+(defun denote-link-or-create (target &optional id-only)
+ "Use `denote-link' on TARGET file, creating it if necessary.
+
+If TARGET file does not exist, call `denote-link-after-creating' which
+runs the `denote' command interactively to create the file. The
+established link will then be targeting that new file. In that case,
+use the last input at the file prompt as the default value of the title
+prompt.
+
+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)))
+ (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)))
+ (unless (or (denote--file-type-org-extra-p)
+ (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name)))
+ (user-error "The current file type is not recognized by Denote"))
+ (denote-link target
+ (denote-filetype-heuristics (buffer-file-name))
+ (denote-get-link-description target)
+ id-only))
+
+(defalias 'denote-link-to-existing-or-new-note 'denote-link-or-create
+ "Alias for `denote-link-or-create' command.")
+
+;;;;; Links' buffer (query links and backlinks using `denote-query-mode')
+
+(define-obsolete-function-alias
+ 'denote-backlinks-mode
+ 'denote-query-mode
+ "4.0.0")
+
+(declare-function outline-cycle "outline" (&optional event))
+(declare-function outline-cycle-buffer "outline" (&optional level))
+(declare-function outline-next-heading "outline" ())
+(declare-function outline-previous-heading "outline" ())
+
+(defvar denote-query-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "a" #'outline-cycle-buffer)
+ (define-key map "f" #'denote-query-focus-last-search)
+ (define-key map "k" #'outline-previous-heading)
+ (define-key map "j" #'outline-next-heading)
+ (define-key map "o" #'delete-other-windows)
+ (define-key map "s" #'denote-grep)
+ (define-key map "v" #'outline-cycle)
+ (define-key map "x" #'denote-query-exclude-files)
+ (define-key map "i" #'denote-query-only-include-files)
+ (define-key map "l" #'recenter-current-error)
+ (define-key map "X" #'denote-query-exclude-files-with-keywords)
+ (define-key map "I" #'denote-query-only-include-files-with-keywords)
+ (define-key map "G" #'denote-query-clear-all-filters)
+ map)
+ "Keymap for `denote-query-mode' buffers.")
+
+(define-derived-mode denote-query-mode xref--xref-buffer-mode "Denote Query"
+ "Major mode for queries found in the variable `denote-directory'.
+This is used by the commands `denote-backlinks', `denote-grep',
+`denote-query-contents-link', among others."
+ :interactive nil
+ (setq-local outline-minor-mode-use-buttons 'in-margins)
+ (outline-minor-mode 1))
+
+(make-obsolete 'denote-link--backlink-find-file nil "4.0.0")
+(make-obsolete 'denote-link--display-buffer nil "4.0.0")
+(make-obsolete 'denote-backlinks-mode-next nil "4.0.0")
+(make-obsolete 'denote-backlinks-mode-previous nil "4.0.0")
+(make-obsolete 'denote-backlinks-toggle-context nil "4.0.0")
+(make-obsolete-variable 'denote-backlinks-mode-map nil "4.0.0")
+
+(define-obsolete-function-alias
+ 'denote-link--prepare-backlinks
+ 'denote-make-links-buffer
+ "4.0.0")
+
+(make-obsolete-variable 'denote-backlinks-show-context nil "4.0.0")
+
+(define-obsolete-variable-alias
+ 'denote-link-backlinks-display-buffer-action
+ 'denote-backlinks-display-buffer-action
+ "3.1.0")
+
+(defgroup denote-query ()
+ "Integration between Denote and Xref for grep/query/backlink buffers."
+ :group 'denote)
+
+(defcustom denote-backlinks-display-buffer-action
+ '((display-buffer-reuse-mode-window display-buffer-below-selected)
+ (mode . denote-query-mode)
+ (window-height . fit-window-to-buffer))
+ "The action used to display the current file's backlinks buffer.
+
+The value has the form (FUNCTION . ALIST), where FUNCTION is
+either an \"action function\", a list thereof, or possibly an
+empty list. ALIST is a list of \"action alist\" which may be
+omitted (or be empty).
+
+Sample configuration to display the buffer in a side window on
+the left of the Emacs frame:
+
+ (setq denote-backlinks-display-buffer-action
+ (quote ((display-buffer-reuse-window display-buffer-in-side-window)
+ (side . left)
+ (slot . 99)
+ (window-width . 0.3)
+ (dedicated . t)
+ (preserve-size . (t . t)))))
+
+See Info node `(elisp) Displaying Buffers' for more details
+and/or the documentation string of `display-buffer'."
+ :risky t
+ :type `(choice
+ (alist :key-type
+ (choice :tag "Condition"
+ regexp
+ (function :tag "Matcher function"))
+ :value-type ,display-buffer--action-custom-type)
+ (function :tag "Custom function to return an action alist"))
+ :package-version '(denote . "3.1.0")
+ :group 'denote-query)
+
+(defcustom denote-query-links-display-buffer-action
+ '((display-buffer-reuse-mode-window display-buffer-below-selected)
+ (mode . (denote-query-mode dired))
+ (window-height . 0.3)
+ (preserve-size . (t . t)))
+ "The action used to display query links.
+This is the same as `denote-backlinks-display-buffer-action'. Refer to
+its documentation for the technicalities."
+ :risky t
+ :type `(choice
+ (alist :key-type
+ (choice :tag "Condition"
+ regexp
+ (function :tag "Matcher function"))
+ :value-type ,display-buffer--action-custom-type)
+ (function :tag "Custom function to return an action alist"))
+ :package-version '(denote . "4.0.0")
+ :group 'denote-query)
+
+(defcustom denote-query-format-heading-function #'identity
+ "Function used to construct headings for files matched by a query.
+
+It is called with a single argument, the path to the note file, and it
+should always return a string."
+ :package-version '(denote . "4.0.0")
+ :link '(info-link "(denote) Use denote-grep to search inside files")
+ :group 'denote-query
+ :type 'function)
+
+(defcustom denote-query-untitled-string "[Untitled]"
+ "String to use as heading for untitled notes in links' buffer.
+
+Used only by `denote-query-extract-title'."
+ :package-version '(denote . "4.0.0")
+ :link '(info-link "(denote) Use denote-grep to search inside files")
+ :group 'denote-query
+ :type 'string)
+
+(defun denote-query-extract-title (file)
+ "Extract note title from FILE front matter.
+
+When no title is found, return title found in FILE name.
+
+When that doesn't work, return `denote-grep-untitled-string'.
+
+Intended to be used as `denote-query-format-heading-function'."
+ (if-let* ((type (denote-filetype-heuristics file))
+ (title (denote-retrieve-title-or-filename file type))
+ (_ (not (string-blank-p title))))
+ title
+ denote-query-untitled-string))
+
+;; NOTE 2025-03-24: The `&rest' is there because we used to have an
+;; extra SHOW-CONTEXT parameter. This way we do not break anybody's
+;; code, even if we slightly modify the behaviour.
+(defun denote-make-links-buffer (query &optional files buffer-name display-buffer-action &rest _)
+ "Create links' buffer called BUFFER-NAME for QUERY.
+
+Optional FILES can be a list of files to search for. It can also be a
+regexp, which limits the files accordingly per `denote-directory-files'.
+
+Optional DISPLAY-BUFFER-ACTION is a `display-buffer' action and
+concomitant alist, such as `denote-backlinks-display-buffer-action'."
+ (let* ((inhibit-read-only t)
+ (file buffer-file-name)
+ (buffer (or buffer-name (format-message "Denote query for `%s'" query)))
+ ;; We retrieve results in absolute form and change the
+ ;; absolute path to a relative path below. We could add a
+ ;; suitable function and the results would be automatically
+ ;; in relative form, but eventually notes may not be all
+ ;; under a common directory (or project).
+ (xref-alist (denote-retrieve-xref-alist query files))
+ (dir (denote-directory)))
+ (unless xref-alist
+ (error "No matches for query `%s'" query))
+ ;; Update internal variables
+ (setq denote-query--last-files nil)
+ (setq denote-query--last-query query)
+ (dolist (x xref-alist)
+ (let* ((file-xref (car x))
+ (file
+ ;; NOTE: Unfortunately, the car of the xref construct is
+ ;; not reliable; sometimes it's absolute, sometimes it
+ ;; is not
+ (if (file-name-absolute-p file-xref)
+ file-xref
+ (xref-location-group
+ (xref-match-item-location (car (last x)))))))
+ ;; Add to current set of files
+ (push file denote-query--last-files)
+ ;; Format heading
+ (setf (car x) (funcall denote-query-format-heading-function file))))
+ (delete-dups denote-query--last-files)
+ ;; Insert results
+ (with-current-buffer (get-buffer-create buffer)
+ (erase-buffer)
+ (denote-query-mode)
+ ;; In the links' buffer, the values of variables set in a
+ ;; `.dir-locals.el` do not apply. We need to set
+ ;; `denote-directory' here because the buttons depend on it.
+ ;; Moreover, its value is overwritten after enabling the major
+ ;; mode, so it needs to be set after.
+ (setq-local denote-directory dir)
+ (setq overlay-arrow-position nil)
+ (goto-char (point-min))
+ (xref--insert-xrefs xref-alist)
+ (goto-char (point-min))
+ (setq-local revert-buffer-function
+ (lambda (_ignore-auto _noconfirm)
+ (when-let* ((buffer-file-name file))
+ (denote-make-links-buffer query files buffer-name display-buffer-action)))))
+ (display-buffer buffer display-buffer-action)))
+
+(defvar denote-query-links-buffer-function #'denote-make-links-buffer
+ "Function to make an Xref buffer showing query link results.
+It accepts the same arguments as `denote-make-links-buffer'.")
+
+(defun denote-query-focus-last-search (query)
+ "Search QUERY in the content of files which matched the last search.
+\"Last search\" here means any call to `denote-grep',
+`denote-backlinks', `denote-query-contents-link', or, generally, any
+command that relies on the `denote-make-links-buffer'."
+ (interactive (list (denote-grep-query-prompt :focused)) denote-query-mode)
+ (unless (derived-mode-p 'denote-query-mode)
+ (user-error "Only use this command inside the `denote-query-mode'"))
+ (denote-make-links-buffer
+ query denote-query--last-files
+ nil '(display-buffer-same-window))
+ (message "Searching `%s' in files matched previously" query))
+
+(defun denote-query-exclude-files (regexp)
+ "Exclude files whose name matches REGEXP from current search buffer.
+
+This is useful even if you don't know regular expressions, given the
+Denote file-naming scheme. For instance, to exclude notes with the
+keyword \"philosophy\" from current search buffer, type
+‘\\<denote-query-mode-map>\\[denote-query-exclude-files] _philosophy
+RET’.
+
+Internally, this works by generating a new call to
+`denote-make-links-buffer' with the same QUERY as the last one, but with
+a set of files gotten from checking REGEXP against last matched files.
+
+When called from Lisp, REGEXP can be a list; in that case, it should be
+a list of fixed strings (NOT regexps) to check against last matched
+files. Files that match any of the strings get excluded. Internally,
+the list is processed using `regexp-opt'. For an example of this usage,
+see `denote-query-exclude-files-with-keywords'."
+ (interactive (list (denote-grep-file-regexp-prompt)) denote-query-mode)
+ (unless (derived-mode-p 'denote-query-mode)
+ (user-error "Only use this command inside the `denote-query-mode'"))
+ (let (final-files)
+ (dolist (file denote-query--last-files)
+ (unless (string-match
+ ;; Support list of strings as REGEXP
+ (if (listp regexp)
+ (regexp-opt regexp)
+ regexp)
+ file)
+ (push file final-files)))
+ (if final-files
+ (denote-make-links-buffer denote-query--last-query final-files)
+ (user-error "No remaining files when applying that filter"))
+ (message "Excluding files matching `%s'" regexp)))
+
+(defun denote-query-only-include-files (regexp)
+ "Exclude file names not matching REGEXP from current query buffer.
+
+See `denote-query-exclude-files' for details, including the behaviour
+when REGEXP is a list."
+ (interactive (list (denote-grep-file-regexp-prompt :include)) denote-query-mode)
+ (unless (derived-mode-p 'denote-query-mode)
+ (user-error "Only use this command inside the `denote-query-mode'"))
+ (let (final-files)
+ (dolist (file denote-query--last-files)
+ (when (string-match
+ ;; Support list of strings as REGEXP
+ (if (listp regexp)
+ (regexp-opt regexp)
+ regexp)
+ file)
+ (push file final-files)))
+ (if final-files
+ (denote-make-links-buffer denote-query--last-query final-files)
+ (user-error "No remaining files when applying that filter"))
+ (message "Only including files matching `%s'" regexp)))
+
+(defun denote-query-exclude-files-with-keywords (keywords)
+ "Exclude files with KEYWORDS from current query buffer.
+
+KEYWORDS should be a list of keywords (without underscore).
+
+Interactively, KEYWORDS are read from the minibuffer using
+`completing-read-multiple', which see."
+ (interactive
+ (list (denote-keywords-prompt "Exclude files with keywords"))
+ denote-query-mode)
+ (unless (derived-mode-p 'denote-query-mode)
+ (user-error "Only use this command inside the `denote-query-mode'"))
+ (denote-query-exclude-files
+ (mapcar (lambda (kw) (concat "_" kw)) keywords)))
+
+(defun denote-query-only-include-files-with-keywords (keywords)
+ "Exclude files without KEYWORDS from current query buffer.
+
+See `denote-query-exclude-files-with-keywords' for details."
+ (interactive
+ (list (denote-keywords-prompt "Only include files with keywords"))
+ denote-query-mode)
+ (unless (derived-mode-p 'denote-query-mode)
+ (user-error "Only use this command inside the `denote-query-mode'"))
+ (denote-query-only-include-files
+ (mapcar (lambda (kw) (concat "_" kw)) keywords)))
+
+(defun denote-query-clear-all-filters ()
+ "Run last search with the full set of files in the variable `denote-directory'.
+
+This effectively gets ride of any interactive filter applied (by the
+means of e.g. `denote-query-exclude-files')."
+ (interactive nil denote-query-mode)
+ (unless (derived-mode-p 'denote-query-mode)
+ (user-error "Only use this command inside the `denote-query-mode'"))
+ (denote-make-links-buffer denote-query--last-query)
+ (message "Cleared all filters"))
+
+;;;;;; Additional features for searching file contents
+
+(defvar denote-grep-history nil
+ "Minibuffer history of content searches performed by `denote-grep'.
+Also see `denote-grep-file-regexp-history'.")
+
+(defcustom denote-grep-display-buffer-action
+ '((display-buffer-same-window)
+ (mode . denote-query-mode))
+ "The action used to display search results from `denote-grep'.
+This is the same as `denote-backlinks-display-buffer-action'. Refer to
+its documentation for the technicalities."
+ :risky t
+ :type `(choice
+ (alist :key-type
+ (choice :tag "Condition"
+ regexp
+ (function :tag "Matcher function"))
+ :value-type ,display-buffer--action-custom-type)
+ (function :tag "Custom function to return an action alist"))
+ :package-version '(denote . "4.0.0")
+ :group 'denote-query)
+
+(defun denote-grep-query-prompt (&optional type)
+ "Prompt for a grep query in the minibuffer.
+
+The prompt assumes a search in all files, unless TYPE is non-nil.
+
+TYPE can be one of :focused (for a focused search (a search among
+matching files), see `denote-query-focus-last-search'), :dired (for a
+search in marked Dired files, see `denote-grep-marked-dired-files') or
+:region (for a search in files referenced in region, see
+`denote-grep-files-referenced-in-region').
+
+TYPE only affects the prompt, not the returned value."
+ (read-string
+ (cond ((eq type :focused)
+ "Search (only files matched last): ")
+ ((eq type :dired)
+ "Search (only marked dired files): ")
+ ((eq type :region)
+ "Search (only files referenced in region): ")
+ (t "Search (all Denote files): "))
+ nil 'denote-grep-history))
+
+(defvar denote-grep-file-regexp-history nil
+ "Minibuffer history for `denote-grep' commands asking for a file regexp.
+Also see `denote-grep-history'.")
+
+(defun denote-grep-file-regexp-prompt (&optional include)
+ "Prompt for a file regexp in the minibuffer.
+
+The prompt assumes the user wants to exclude files, unless INCLUDE is
+non-nil."
+ (read-string
+ (if (not include)
+ "Exclude file names matching: "
+ "Only include file names matching: ")
+ nil 'denote-grep-file-regexp-history))
+
+;;;###autoload
+(defun denote-grep (query)
+ "Search QUERY in the content of Denote files.
+QUERY should be a regular expression accepted by `xref-search-program'.
+
+The files to search for are those returned by `denote-directory-files'
+with a non-nil TEXT-ONLY argument.
+
+Results are put in a buffer which allows folding and further
+filtering (see the manual for details).
+
+You can insert a link to a grep search in any note by using the command
+`denote-query-contents-link'."
+ (interactive (list (denote-grep-query-prompt)))
+ (let (denote-query--omit-current)
+ (denote-make-links-buffer query nil nil denote-grep-display-buffer-action)))
+
+;;;###autoload
+(defun denote-grep-marked-dired-files (query)
+ "Do the equivalent of `denote-grep' for QUERY in marked Dired files."
+ (interactive (list (denote-grep-query-prompt :dired)))
+ (if-let* ((files (dired-get-marked-files)))
+ (denote-make-links-buffer query files nil denote-grep-display-buffer-action)
+ (user-error "No marked files")))
+
+(defun denote-grep--get-files-referenced-in-region (start end)
+ "Return a list with all Denote files referenced between START and END.
+START and END are buffer positions, as integers. A reference to a file
+is the mere presence of its identifier.
+
+Return a list with the absoulte path of referenced files."
+ (let (id-list)
+ (save-excursion
+ (save-restriction
+ (narrow-to-region start end)
+ (goto-char (point-min))
+ (while (re-search-forward denote-id-regexp nil t)
+ (push (denote-get-path-by-id (match-string 0)) id-list))))
+ id-list))
+
+;;;###autoload
+(defun denote-grep-files-referenced-in-region (query start end)
+ "Perform `denote-grep' QUERY in files referenced between START and END.
+When called interactively, prompt for QUERY. Also get START and END as
+the buffer positions that delimit the marked region. When called from
+Lisp, QUERY is a string, while START and END are buffer positions, as
+integers.
+
+Find references to files by their identifier. This includes links with
+just the identifier (as described in `denote-link' and related), links
+written by an Org dynamic block (see the `denote-org' package), or even
+file listings such as those of `dired' and the command-line `ls' program."
+ (interactive
+ (if (region-active-p)
+ (list
+ (denote-grep-query-prompt :region)
+ (region-beginning)
+ (region-end))
+ (user-error "No region is active; aborting")))
+ (if-let* ((files (denote-grep--get-files-referenced-in-region start end)))
+ (denote-make-links-buffer query files nil denote-grep-display-buffer-action)
+ (user-error "No files referenced in region")))
+
+;;;;;; Backlinks
+
+(defun denote--backlinks-get-buffer-name (file id)
+ "Format a buffer name for `denote-backlinks'.
+Use FILE to detect a suitable title with which to name the buffer. Else
+use the ID."
+ (if-let* ((type (denote-filetype-heuristics file))
+ (title (denote-retrieve-front-matter-title-value file type)))
+ (format "*Denote FILE backlinks for %S*" title)
+ (format "*Denote FILE backlinks for %s*" id)))
+
+;;;###autoload
+(defun denote-backlinks ()
+ "Produce a buffer with backlinks to the current note.
+
+Show the names of files linking to the current file. Include the
+context of each link if the user option `denote-backlinks-show-context'
+is non-nil.
+
+Place the buffer below the current window or wherever the user option
+`denote-backlinks-display-buffer-action' specifies."
+ (interactive)
+ (if-let* ((file buffer-file-name))
+ (when-let* ((identifier (denote-retrieve-filename-identifier-with-error file)))
+ (funcall denote-query-links-buffer-function
+ identifier nil
+ (denote--backlinks-get-buffer-name file identifier)
+ denote-backlinks-display-buffer-action))
+ (user-error "Buffer `%s' is not associated with a file" (current-buffer))))
+
+(defalias 'denote-show-backlinks-buffer 'denote-backlinks
+ "Alias for `denote-backlinks' command.")
+
+
+(defun denote-link-return-backlinks (&optional file)
+ "Return list of backlinks in current or optional FILE.
+Also see `denote-link-return-links'."
+ (when-let* ((current-file (or file (buffer-file-name)))
+ (id (denote-retrieve-filename-identifier-with-error current-file)))
+ (delete current-file (denote-retrieve-files-xref-query id))))
+
+;; TODO 2024-09-04: Instead of using `denote-link-return-backlinks' we
+;; should have a function that does not try to find all backlinks but
+;; simply exits as soon as it finds one.
+(defun denote--file-has-backlinks-p (file)
+ "Return non-nil if FILE has backlinks."
+ (not (zerop (length (denote-link-return-backlinks file)))))
+
+;;;###autoload
+(defun denote-find-backlink ()
+ "Use minibuffer completion to visit backlink to current file.
+Alo see `denote-find-link'."
+ (declare (interactive-only t))
+ (interactive)
+ (find-file
+ (denote-get-path-by-id
+ (denote-extract-id-from-string
+ (denote-select-linked-file-prompt
+ (or (denote-link-return-backlinks)
+ (user-error "No backlinks found")))))))
+
+;;;;;; Query links
+
+(defvar denote-query-link-history nil
+ "Minibuffer history of `denote-query-link-prompt'.")
+
+(defun denote-query-link-prompt (&optional initial-query prompt-text)
+ "Prompt for query string.
+With optional INITIAL-QUERY use it as the initial minibuffer text. With
+optional PROMPT-TEXT use it in the minibuffer instead of the default
+prompt.
+
+Previous inputs at this prompt are available for minibuffer completion
+if the user option `denote-history-completion-in-prompts' is set to a
+non-nil value."
+ (when (and initial-query (string-empty-p initial-query))
+ (setq initial-query nil))
+ (denote--with-conditional-completion
+ 'denote-query-link-prompt
+ (format-prompt (or prompt-text "Query for") nil)
+ denote-query-link-history
+ initial-query))
+
+(defconst denote-query-link-types '(query-contents query-filenames)
+ "Types of query links.")
+
+;; NOTE 2025-03-27: Should we expose a user option for this? And/or
+;; should we add a DESCRIPTION parameter to `denote--format-query-link'?
+;;
+;; What would make for a good default description in that scenario?
+;; Maybe "QC:query text here" and "QF:query text here" for
+;; `query-contents' and `query-filenames' respectively.
+(defvar denote-query-description-prefix ""
+ "Prefix string for query links to format their description text.
+The description text constists of the value of this variable followed by
+the query")
+
+(defun denote--format-query-link (type query file-type)
+ "Format QUERY link of TYPE for the given FILE-TYPE.
+Return an error if TYPE is not one among the symbols specified in
+`denote-query-link-types'.
+
+If FILE-TYPE is nil, use that of Org."
+ (unless (memq type denote-query-link-types)
+ (error "Type `%s' is not one among `denote-query-link-types'" type))
+ (format (or (denote--link-format file-type) (denote--link-format 'org))
+ (format "%s:%s" type query)
+ (format "%s%s" denote-query-description-prefix query)))
+
+;;;###autoload
+(defun denote-query-contents-link (query)
+ "Insert query link for file contents.
+Prompt for QUERY or use the text of the active region. When the user
+follows this link, place any matches in a separate buffer (using the
+built-in Xref mechanism). This is the equivalent of a Unix grep command
+across the variable `denote-directory'."
+ (interactive
+ (list
+ (or (denote--get-active-region-content)
+ (denote-query-link-prompt nil "Query in file CONTENTS"))))
+ (unless (or (denote--file-type-org-extra-p)
+ (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name)))
+ (user-error "The current file type is not recognized by Denote"))
+ (denote--delete-active-region-content)
+ (insert (denote--format-query-link 'query-contents query (denote-filetype-heuristics buffer-file-name))))
+
+;;;###autoload
+(defun denote-query-filenames-link (query)
+ "Insert query link for file names.
+Prompt for QUERY or use the text of the active region. When the user
+follows this link, place any matches in a separate buffer (using the
+built-in Dired mechanism). This is the equivalent of a Unix find
+command across the variable `denote-directory'."
+ (interactive
+ (list
+ (or (denote--get-active-region-content)
+ (denote-query-link-prompt nil "Query in file NAMES"))))
+ (unless (or (denote--file-type-org-extra-p)
+ (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name)))
+ (user-error "The current file type is not recognized by Denote"))
+ (denote--delete-active-region-content)
+ (insert (denote--format-query-link 'query-filenames query (denote-filetype-heuristics buffer-file-name))))
+
+(defvar denote--query-last-dired-buffer nil
+ "Buffer object produced by the last query for file names.")
+
+(defun denote--act-on-query-link (query)
+ "Act on QUERY link.
+QUERY is a string of the form TYPE:SEARCH, where TYPE is one among
+`denote-query-link-types' while SEARCH is the regular expression to
+search for."
+ (cond
+ ((string-prefix-p "query-contents:" query)
+ (setq query (replace-regexp-in-string "query-contents:" "" query))
+ (funcall denote-query-links-buffer-function query nil nil denote-query-links-display-buffer-action))
+ ((string-prefix-p "query-filenames:" query)
+ (setq query (replace-regexp-in-string "query-filenames:" "" query))
+ ;; NOTE 2025-03-27: I do not think we need to add another
+ ;; parameter to `denote-sort-dired' for handling the
+ ;; `display-buffer'. This is a special case, but we can always
+ ;; change it later if the need arises.
+ ;;
+ ;; Here we handle the buffer and window state to make it behave
+ ;; like the Xref buffer. Otherwise, Dired does not reuse its
+ ;; buffer (which is generally okay).
+ (let ((buffer (save-window-excursion (denote-sort-dired query nil nil nil))))
+ (when (bufferp denote--query-last-dired-buffer)
+ (when-let* ((window (get-buffer-window denote--query-last-dired-buffer))
+ (_ (window-live-p window)))
+ (delete-window window))
+ (kill-buffer denote--query-last-dired-buffer))
+ (display-buffer buffer denote-query-links-display-buffer-action)
+ (setq denote--query-last-dired-buffer buffer)))
+ (t
+ (error "Cannot open `%s' of unknown link type" query))))
+
+;;;;; Link buttons
+
+(make-obsolete 'denote-link--find-file-at-button nil "4.0.0")
+
+(make-obsolete
+ 'denote-link-buttonize-buffer
+ 'denote-fontify-links-mode
+ "Use the `denote-fontify-links-mode', as it works better than buttonization. Since 3.0.0")
+
+;; NOTE 2025-03-24: This does not work for query links because of how
+;; `markdown-follow-link-at-point' is implemented to always check for
+;; links.
+(defun denote-link-markdown-follow (link)
+ "Function to open Denote file present in LINK.
+To be assigned to `markdown-follow-link-functions'."
+ (when (ignore-errors (string-match denote-id-regexp link))
+ (funcall denote-open-link-function
+ (denote-get-path-by-id (match-string 0 link)))))
+
+(eval-after-load 'markdown-mode
+ '(add-hook 'markdown-follow-link-functions #'denote-link-markdown-follow))
+
+;;;;; Link fontification
+
+;; TODO 2024-06-19: We need to bind RET and maybe even C-c C-o to a
+;; command that opens the link at point. Then we may also rename this
+;; keymap.
+(defvar denote-link-mouse-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map [mouse-2] #'denote-link-open-at-mouse)
+ (define-key map [mouse-3] #'denote-link-open-at-mouse)
+ (define-key map [follow-link] 'mouse-face)
+ map)
+ "Keymap for mouse actions over fontified Denote links.")
+
+(defun denote--link-open-at-point-subr ()
+ "Open link at point."
+ (let ((query (get-text-property (point) 'denote-link-query-part)))
+ (if-let* ((path (denote-get-path-by-id query)))
+ (funcall denote-open-link-function path)
+ (denote--act-on-query-link query))))
+
+(defun denote-link-open-at-point ()
+ "Open Denote link at point."
+ (interactive)
+ (denote--link-open-at-point-subr))
+
+(defun denote-link-open-at-mouse (ev)
+ "Open Denote link for mouse EV click."
+ (interactive "e")
+ (mouse-set-point ev)
+ (denote--link-open-at-point-subr))
+
+(defun denote-get-link-face (query)
+ "Return appropriate face for QUERY."
+ (if (denote-identifier-p (string-trim-right query ":[^/]+.*"))
+ 'denote-faces-link
+ 'denote-faces-query-link))
+
+(defun denote--fontify-links-subr (query limit)
+ "Do the work of the font-lock match for QUERY up to LIMIT.
+Implementation based on the function `org-activate-links'."
+ (catch :exit
+ (while (re-search-forward query limit t)
+ (save-match-data ; to return the matches to font-lock
+ (let* ((start (match-beginning 0))
+ (end (match-end 0))
+ (visible-start (or (match-beginning 2) start))
+ (visible-end (or (match-end 2) end))
+ (query (match-string-no-properties 1)))
+ (let* ((properties `( face ,(denote-get-link-face query)
+ mouse-face highlight
+ keymap ,denote-link-mouse-map
+ denote-link-query-part ,query
+ help-echo query
+ htmlize-link (:uri ,query)
+ font-lock-multiline t))
+ (non-sticky-props
+ '(rear-nonsticky (mouse-face highlight keymap invisible intangible help-echo htmlize-link)))
+ (face-property 'link)
+ (hidden (append '(invisible 'denote-link) properties)))
+ (remove-text-properties start end '(invisible nil))
+ (add-text-properties start visible-start hidden)
+ (add-face-text-property start end face-property)
+ (add-text-properties visible-start visible-end properties)
+ (add-text-properties visible-end end hidden)
+ (dolist (pos (list end visible-start visible-end))
+ (add-text-properties (1- pos) pos non-sticky-props)))
+ (throw :exit t)))) ; signal success
+ nil))
+
+(defun denote-fontify-links (limit)
+ "Provide font-lock matcher to fontify links up to LIMIT."
+ (when-let* ((type (denote-filetype-heuristics (buffer-file-name))))
+ (denote--fontify-links-subr (denote--link-in-context-regexp type) limit)))
+
+(define-obsolete-function-alias
+ 'denote-get-identifier-at-point
+ 'denote-get-link-identifier-or-query-term-at-point
+ "4.0.0")
+
+(defun denote-get-link-identifier-or-query-term-at-point (&optional point)
+ "Return the Denote identifier or query term at point or optional POINT."
+ (when-let* ((position (or point (point)))
+ (face-at-point (get-text-property position 'face))
+ ((or (eq face-at-point 'denote-faces-link)
+ (member 'denote-faces-link face-at-point))))
+ (or (get-text-property position 'denote-link-query-part)
+ (when-let* ((link-data (get-text-property position 'htmlize-link))
+ (link (cadr link-data)))
+ (string-match denote-id-regexp link)
+ (match-string-no-properties 0 link)))))
+
+(defun denote--get-link-file-path-at-point (&optional point)
+ "Return link to the Denote file path at point or optional POINT.
+To be used as a `thing-at' provider."
+ (when-let* ((position (or point (point)))
+ (id (get-text-property position 'denote-link-query-part))
+ (path (denote-get-path-by-id id)))
+ (concat "file:" path)))
+
+(defvar thing-at-point-provider-alist)
+
+;;;###autoload
+(defun denote-fontify-links-mode-maybe ()
+ "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-fontify-links-mode)))
+
+;;;###autoload
+(define-minor-mode denote-fontify-links-mode
+ "A minor mode to fontify and fold Denote links.
+
+Enabled this mode only when the current buffer is a Denote note and the
+major mode is not `org-mode' (or derived therefrom). Consider using
+`denote-fontify-links-mode-maybe' for this purpose."
+ :init-value nil
+ :global nil
+ :group 'denote
+ (require 'thingatpt)
+ (if denote-fontify-links-mode
+ (progn
+ (add-to-invisibility-spec 'denote-link)
+ (font-lock-add-keywords nil '((denote-fontify-links)))
+ (setq-local thing-at-point-provider-alist
+ (append thing-at-point-provider-alist
+ '((url . denote--get-link-file-path-at-point)))))
+ (remove-from-invisibility-spec 'denote-link)
+ (font-lock-remove-keywords nil '((denote-fontify-links)))
+ (setq-local thing-at-point-provider-alist
+ (delete
+ '(url . denote--get-link-file-path-at-point)
+ thing-at-point-provider-alist)))
+ (font-lock-update))
+
+;;;;; Add links matching regexp
+
+(defvar denote-link--prepare-links-format "- %s\n"
+ "Format specifiers for `denote-add-links'.")
+
+(make-obsolete-variable 'denote-link-add-links-sort nil "3.1.0")
+
+(defun denote-link--prepare-links (files current-file-type id-only &optional no-sort include-date)
+ "Prepare links to FILES from CURRENT-FILE-TYPE.
+When ID-ONLY is non-nil, use a generic link format.
+
+With optional NO-SORT do not try to sort the inserted lines.
+Otherwise sort lines while accounting for `denote-link-add-links-sort'.
+
+Optional INCLUDE-DATE has the same meaning as in `denote-format-link'."
+ (let ((links))
+ (dolist (file files)
+ (let* ((description (denote-get-link-description file))
+ (link (denote-format-link file description current-file-type id-only include-date))
+ (link-as-list-item (format denote-link--prepare-links-format link)))
+ (push link-as-list-item links)))
+ (if no-sort
+ (nreverse links)
+ (sort links #'string-collate-lessp))))
+
+(defun denote-link--insert-links (files current-file-type &optional id-only no-sort include-date)
+ "Insert at point a typographic list of links matching FILES.
+
+With CURRENT-FILE-TYPE as a symbol among those specified in variable
+`denote-file-type' (or the `car' of each element in `denote-file-types'),
+format the link accordingly. With a nil or unknown non-nil value,
+default to the Org notation.
+
+With ID-ONLY as a non-nil value, produce links that consist only
+of the identifier, thus deviating from CURRENT-FILE-TYPE.
+
+Optional NO-SORT is passed to `denote-link--prepare-links'.
+
+Optional INCLUDE-DATE has the same meaning as in `denote-format-link'."
+ (when-let* ((links (denote-link--prepare-links files current-file-type id-only no-sort include-date)))
+ (dolist (link links)
+ (insert link))))
+
+;;;###autoload
+(defun denote-add-links (regexp &optional id-only)
+ "Insert links to all files whose file names match REGEXP.
+Use this command to reference multiple files at once. Particularly
+useful for the creation of metanotes (read the manual for more on the
+matter).
+
+Optional ID-ONLY has the same meaning as in `denote-link': it
+inserts links with just the identifier."
+ (interactive
+ (list
+ (denote-files-matching-regexp-prompt "Insert links to files matching REGEXP")
+ current-prefix-arg))
+ (unless (or (denote--file-type-org-extra-p)
+ (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)))
+ (denote-link--insert-links files file-type id-only)
+ (message "No links matching `%s'" regexp))))
+
+;;;;; Link to file with matching contents
+
+;;;###autoload
+(defun denote-link-to-file-with-contents (query &optional id-only)
+ "Link to a file whose contents match QUERY.
+This is similar to `denote-link', except that the file prompt is limited
+to files matching QUERY. Optional ID-ONLY has the same meaning as in
+`denote-link'."
+ (interactive
+ (list (denote-query-link-prompt nil "Files whose contents include QUERY")))
+ (if-let* ((files (denote-retrieve-files-xref-query query))
+ ;; NOTE 2025-03-29: Maybe we should have a named prompt
+ ;; for this case, but I think we do not need it right now.
+ (file (completing-read
+ (format "Select FILE with contents `%s': "
+ (propertize query 'face 'denote-faces-prompt-current-name))
+ (denote--completion-table 'file files)
+ nil t nil 'denote-file-history)))
+ (denote-link file
+ (denote-filetype-heuristics buffer-file-name)
+ (denote-get-link-description file)
+ id-only)
+ (user-error "No files include the query `%s' in their contents" query)))
+
+;;;###autoload
+(defun denote-link-to-all-files-with-contents (query &optional id-only)
+ "Link to all files whose contents match QUERY.
+This is similar to `denote-add-links', except it searches inside file
+contents, not file names. Optional ID-ONLY has the same meaning as in
+`denote-link' and `denote-add-links'."
+ (interactive
+ (list (denote-query-link-prompt nil "Files whose contents include QUERY")))
+ (if-let* ((files (denote-retrieve-files-xref-query query)))
+ (denote-link--insert-links files (denote-filetype-heuristics buffer-file-name) id-only)
+ (user-error "No files include the query `%s' in their contents" query)))
+
+;;;;; Links from Dired marks
+
+;; NOTE 2022-07-21: I don't think we need a history for this one.
+(defun denote-link--buffer-file-prompt (buffer-file-names)
+ "Select file from BUFFER-FILE-NAMES of Denote notes."
+ (let ((relative-buffer-file-names (mapcar #'denote-get-file-name-relative-to-denote-directory buffer-file-names)))
+ (concat (denote-directory)
+ (completing-read
+ "Select open note to add links to: "
+ (denote--completion-table 'file relative-buffer-file-names)
+ nil t))))
+
+(defun denote-link--map-over-notes ()
+ "Return list of `denote-file-has-denoted-filename-p' from Dired marked items."
+ (seq-filter (lambda (file) (and (denote-file-has-denoted-filename-p file)
+ (denote-file-has-identifier-p file)))
+ (dired-get-marked-files)))
+
+;;;###autoload
+(defun denote-link-dired-marked-notes (files buffer &optional id-only)
+ "Insert Dired marked FILES as links in BUFFER.
+
+FILES conform with the Denote file-naming scheme, such that they can be
+linked to using the `denote:' link type.
+
+The BUFFER is one which visits a Denote note file. If there are
+multiple BUFFER candidates in buffers, prompt with completion for
+one among them. If there is none, throw an error.
+
+With optional ID-ONLY as a prefix argument, insert links with
+just the identifier (same principle as with `denote-link').
+
+This command is meant to be used from a Dired buffer."
+ (interactive
+ (if (derived-mode-p 'dired-mode)
+ (list
+ (denote-link--map-over-notes)
+ (let ((file-names (denote--buffer-file-names)))
+ (find-buffer-visiting
+ (cond
+ ((null file-names)
+ (user-error "No buffers visiting Denote notes"))
+ ((eq (length file-names) 1)
+ (car file-names))
+ (t
+ (denote-link--buffer-file-prompt file-names)))))
+ current-prefix-arg)
+ (user-error "This command only works inside a Dired buffer"))
+ dired-mode)
+ (when (null files)
+ (user-error "No note files to link to"))
+ (unless (buffer-live-p buffer)
+ (error "The buffer `%s' is not live" buffer))
+ (let ((body (lambda ()
+ (unless (or (denote--file-type-org-extra-p)
+ (and buffer-file-name (denote-file-has-supported-extension-p buffer-file-name)))
+ (user-error "The target file's type is not recognized by Denote"))
+ (when (y-or-n-p (format "Create links at point in `%s'?" buffer))
+ (denote-link--insert-links files (denote-filetype-heuristics buffer-file-name) id-only)
+ (message "Added links to `%s'; displaying it now"
+ ;; TODO 2024-12-26: Do we need our face here? I think
+ ;; not, but let me keep a note of it.
+ (propertize (format "%s" buffer) 'face 'success))))))
+ (if-let* ((window (get-buffer-window buffer))
+ ((window-live-p window)))
+ (with-selected-window window (funcall body))
+ (with-current-buffer buffer (funcall body))
+ (display-buffer-below-selected buffer nil))))
+
+(defalias 'denote-dired-link-marked-notes 'denote-link-dired-marked-notes
+ "Alias for `denote-link-dired-marked-notes' command.")
+
+;;;; Define menu
+
+(defvar denote--menu-contents
+ '(["Create a note" denote
+ :help "Create a new note in the `denote-directory'"]
+ ["Create a note with given file type" denote-type
+ :help "Create a new note with a given file type in the `denote-directory'"]
+ ["Create a note in subdirectory" denote-subdirectory
+ :help "Create a new note in a subdirectory of the `denote-directory'"]
+ ["Create a note with date" denote-date
+ :help "Create a new note with a given date in the `denote-directory'"]
+ ["Create a note with signature" denote-signature
+ :help "Create a new note with a given signature in the `denote-directory'"]
+ ["Open a note or create it if missing" denote-open-or-create
+ :help "Open an existing note in the `denote-directory' or create it if missing"]
+ ["Open a note or create it with the chosen command" denote-open-or-create-with-command
+ :help "Open an existing note or create it with the chosen command if missing"]
+ "---"
+ ["Rename a file" denote-rename-file
+ :help "Rename file interactively"
+ :enable (derived-mode-p 'dired-mode 'text-mode)]
+ ["Rename this file using its front matter" denote-rename-file-using-front-matter
+ :help "Rename the current file using its front matter as input"
+ :enable (derived-mode-p 'text-mode)]
+ ["Rename Dired marked files interactively" denote-dired-rename-files
+ :help "Rename marked files in Dired by prompting for all file name components"
+ :enable (derived-mode-p 'dired-mode)]
+ ["Rename Dired marked files with keywords" denote-dired-rename-marked-files-with-keywords
+ :help "Rename marked files in Dired by prompting for keywords"
+ :enable (derived-mode-p 'dired-mode)]
+ ["Rename Dired marked files using their front matter" denote-dired-rename-marked-files-using-front-matter
+ :help "Rename marked files in Dired using their front matter as input"
+ :enable (derived-mode-p 'dired-mode)]
+ "---"
+ ["Insert a direct link" denote-link
+ :help "Insert link to a file in the `denote-directory'"
+ :enable (derived-mode-p 'text-mode)]
+ ["Insert a direct link to file with contents" denote-link-to-file-with-contents
+ :help "Insert link to a file in the `denote-directory' whose contents include a query"
+ :enable (derived-mode-p 'text-mode)]
+ ["Insert a query link for file contents" denote-query-contents-link
+ :help "Insert query link searching for file contents in the `denote-directory'"
+ :enable (derived-mode-p 'text-mode)]
+ ["Insert a query link for file names" denote-query-filenames-link
+ :help "Insert query link searching for file names in the `denote-directory'"
+ :enable (derived-mode-p 'text-mode)]
+ "---"
+ ["Insert links to file names matching regexp" denote-add-links
+ :help "Insert links to file names in the `denote-directory' matching regexp"
+ :enable (derived-mode-p 'text-mode)]
+ ["Insert links to files whose contents match regexp" denote-link-to-all-files-with-contents
+ :help "Insert links to file in the `denote-directory' whose contents match regexp"
+ :enable (derived-mode-p 'text-mode)]
+ ["Insert Dired marked files as links" denote-link-dired-marked-notes
+ :help "Rename marked files in Dired as links in a Denote buffer"
+ :enable (derived-mode-p 'dired-mode)]
+ ["Show file backlinks" denote-backlinks
+ :help "Insert link to a file in the `denote-directory'"
+ :enable (derived-mode-p 'text-mode)]
+ ["Link to existing note or newly created one" denote-link-or-create
+ :help "Insert a link to an existing file, else create it and link to it"
+ :enable (derived-mode-p 'text-mode)]
+ ["Create note in the background and link to it directly" denote-link-after-creating
+ :help "Create new note and link to it from the current file"
+ :enable (derived-mode-p 'text-mode)]
+ ["Create note in the background with chosen command and link to it directly" denote-link-after-creating-with-command
+ :help "Create new note with the chosen command and link to it from the current file"
+ :enable (derived-mode-p 'text-mode)]
+ "---"
+ ["Generate sorted and filtered Dired listing" denote-sort-dired
+ :help "Generate a sorted and filtered Dired listing of files in the `denote-directory'"]
+ "---"
+ ["Highlight Dired file names" denote-dired-mode
+ :help "Apply colors to Denote file name components in Dired"
+ :enable (derived-mode-p 'dired-mode)
+ :style toggle
+ :selected (bound-and-true-p denote-dired-mode)])
+ "Contents of the Denote menu.")
+
+(defun denote--menu-bar-enable ()
+ "Enable Denote menu bar."
+ (define-key-after global-map [menu-bar denote]
+ (easy-menu-binding
+ (easy-menu-create-menu "Denote" denote--menu-contents) "Denote")
+ "Tools"))
+
+;; Enable Denote menu bar by default
+(denote--menu-bar-enable)
+
+;;;###autoload
+(define-minor-mode denote-menu-bar-mode "Show Denote menu bar."
+ :global t
+ :init-value t
+ (if denote-menu-bar-mode
+ (denote--menu-bar-enable)
+ (define-key global-map [menu-bar denote] nil)))
+
+(defun denote-context-menu (menu _click)
+ "Populate MENU with Denote commands at CLICK."
+ (define-key menu [denote-separator] menu-bar-separator)
+ (let ((easy-menu (make-sparse-keymap "Denote")))
+ (easy-menu-define nil easy-menu nil
+ denote--menu-contents)
+ (dolist (item (reverse (lookup-key easy-menu [menu-bar])))
+ (when (consp item)
+ (define-key menu (vector (car item)) (cdr item)))))
+ menu)
+
+;;;; Register `denote:' custom Org hyperlink
+
+(declare-function org-link-open-as-file "ol" (path arg))
+
+(defun denote-link--ol-resolve-link-to-target (link &optional full-data)
+ "Resolve LINK to target file, with or without additioanl file-search terms.
+With optional FULL-DATA return a list in the form of (path query file-search)."
+ (let* ((file-search (and (string-match "::\\(.*\\)\\'" link)
+ (match-string 1 link)))
+ (query (if (and file-search (not (string-empty-p file-search)))
+ (substring link 0 (match-beginning 0))
+ link))
+ (path (denote-get-path-by-id query)))
+ (cond
+ (full-data
+ (list path query file-search))
+ ((and file-search (not (string-empty-p file-search)))
+ (concat path "::" file-search))
+ (t (or path query)))))
+
+;;;###autoload
+(defun denote-link-ol-follow (link)
+ "Find file of type `denote:' matching LINK.
+LINK is the identifier of the note, optionally followed by a file search
+option akin to that of standard Org `file:' link types. Read Info
+node `(org) Query Options'.
+
+If LINK is not an identifier, then it is not pointing to a file but to a
+query of file contents or file names (see the commands
+`denote-query-contents-link' and `denote-query-filenames-link').
+
+Uses the function `denote-directory' to establish the path to the file."
+ (if-let* ((match (denote-link--ol-resolve-link-to-target link))
+ (_ (file-exists-p (string-trim-right match ":[^/]+.*"))))
+ (org-link-open-as-file match nil)
+ (denote--act-on-query-link match)))
+
+;;;###autoload
+(defun denote-link-ol-complete ()
+ "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)))
+ (concat "denote:" (denote-retrieve-filename-identifier file))
+ (user-error "No files in `denote-directory'")))
+
+(declare-function org-link-store-props "ol.el" (&rest plist))
+(defvar org-store-link-plist)
+
+(declare-function org-entry-put "org" (pom property value))
+(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
+(declare-function org-id-new "org-id" (&optional prefix))
+
+(defun denote-link-ol-get-id ()
+ "Get the CUSTOM_ID of the current entry.
+If the entry already has a CUSTOM_ID, return it as-is, else
+create a new one."
+ (let* ((pos (point))
+ (id (org-entry-get pos "CUSTOM_ID")))
+ (if (and (stringp id) (string-match-p "\\S-" id))
+ id
+ (setq id (org-id-new "h"))
+ (org-entry-put pos "CUSTOM_ID" id)
+ id)))
+
+(declare-function org-get-heading "org" (no-tags no-todo no-priority no-comment))
+
+(defun denote-link-ol-get-heading ()
+ "Get current Org heading text."
+ (org-get-heading :no-tags :no-todo :no-priority :no-comment))
+
+(defun denote-link-format-heading-description (file-text heading-text)
+ "Return description for FILE-TEXT with HEADING-TEXT at the end."
+ (format "%s::%s" file-text heading-text))
+
+;;;###autoload
+(defun denote-link-ol-store (&optional interactive?)
+ "Handler for `org-store-link' adding support for denote: links.
+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-id (denote-retrieve-filename-identifier file))
+ (description (denote-get-link-description file)))
+ (let ((heading-links (and denote-org-store-link-to-heading
+ (derived-mode-p 'org-mode)
+ (denote--org-capture-link-specifiers-p)))
+ (heading (denote-link-ol-get-heading)))
+ (org-link-store-props
+ :type "denote"
+ :description (if (and heading-links heading)
+ (denote-link-format-heading-description
+ description
+ heading)
+ description)
+ :link (cond
+ ((when-let* ((id (org-entry-get (point) "CUSTOM_ID")))
+ (format "denote:%s::#%s" file-id id)))
+ ((and heading-links (eq denote-org-store-link-to-heading 'context) heading)
+ (format "denote:%s::*%s" file-id heading))
+ ((and heading-links heading)
+ (format "denote:%s::#%s" file-id (denote-link-ol-get-id)))
+ (t
+ (concat "denote:" file-id))))
+ org-store-link-plist))))
+
+;;;###autoload
+(defun denote-link-ol-export (link description format)
+ "Export a `denote:' link from Org files.
+The LINK, DESCRIPTION, and FORMAT are handled by the export
+backend."
+ (pcase-let* ((`(,path ,query ,file-search) (denote-link--ol-resolve-link-to-target link :full-data))
+ (anchor (when path (file-relative-name (file-name-sans-extension path))))
+ (desc (cond
+ (description)
+ (file-search (format "denote:%s::%s" query file-search))
+ (t (concat "denote:" query)))))
+ (if path
+ (pcase format
+ ('html (if file-search
+ (format "<a href=\"%s.html%s\">%s</a>" anchor file-search desc)
+ (format "<a href=\"%s.html\">%s</a>" anchor desc)))
+ ('latex (format "\\href{%s}{%s}" (replace-regexp-in-string "[\\{}$%&_#~^]" "\\\\\\&" path) desc))
+ ('texinfo (format "@uref{%s,%s}" path desc))
+ ('ascii (format "[%s] <denote:%s>" desc path))
+ ('md (format "[%s](%s)" desc path))
+ (_ path))
+ (format-message "[[Denote query for `%s']]" query))))
+
+(defun denote-link-ol-help-echo (_window _object position)
+ "Echo the full file path of the identifier at POSITION."
+ (when-let* ((htmlize-link (get-text-property position 'htmlize-link))
+ (string (plist-get htmlize-link :uri))
+ (identifier (replace-regexp-in-string "denote:\\(.*?\\)\\(#.*\\)?" "\\1" string))
+ (path (denote-get-path-by-id identifier)))
+ path))
+
+;; The `eval-after-load' part with the quoted lambda is adapted from
+;; Elfeed: <https://github.com/skeeto/elfeed/>.
+
+;;;###autoload
+(eval-after-load 'org
+ `(funcall
+ ;; The extra quote below is necessary because uncompiled closures
+ ;; do not evaluate to themselves. The quote is harmless for
+ ;; byte-compiled function objects.
+ ',(lambda ()
+ (with-no-warnings
+ (org-link-set-parameters
+ "denote"
+ :follow #'denote-link-ol-follow
+ :face #'denote-get-link-face
+ :help-echo #'denote-link-ol-help-echo
+ :complete #'denote-link-ol-complete
+ :store #'denote-link-ol-store
+ :export #'denote-link-ol-export)))))
+
+;;;; Glue code for org-capture
+
+(defgroup denote-org-capture ()
+ "Integration between Denote and Org Capture."
+ :group 'denote)
+
+(defcustom denote-org-capture-specifiers "%l\n%i\n%?"
+ "String with format specifiers for `org-capture-templates'.
+Check that variable's documentation for the details.
+
+The string can include arbitrary text. It is appended to new
+notes via the `denote-org-capture' function. Every new note has
+the standard front matter we define."
+ :type 'string
+ :package-version '(denote . "0.1.0")
+ :group 'denote-org-capture)
+
+(defun denote--org-capture-link-specifiers-p ()
+ "Return non-nil if `denote-org-capture-specifiers' uses link specifiers."
+ (when (stringp denote-org-capture-specifiers)
+ (string-match-p "%^?[aAlL]" denote-org-capture-specifiers)))
+
+(defvar denote-last-path nil "Store last path.")
+
+;;;###autoload
+(defun denote-org-capture ()
+ "Create new note through `org-capture-templates'.
+Use this as a function that returns the path to the new file.
+The file is populated with Denote's front matter. It can then be
+expanded with the usual specifiers or strings that
+`org-capture-templates' supports.
+
+This function obeys `denote-prompts', but it ignores `file-type',
+if present: it always sets the Org file extension for the created
+note to ensure that the capture process works as intended,
+especially for the desired output of the
+`denote-org-capture-specifiers' (which can include arbitrary
+text).
+
+Consult the manual for template samples."
+ (pcase-let* ((denote-prompts (remove 'file-type denote-prompts)) ; Do not prompt for file-type. We use org.
+ (`(,title ,keywords _ ,directory ,date ,template ,signature)
+ (denote--creation-get-note-data-from-prompts))
+ (`(,title ,keywords _ ,directory ,date ,id ,template ,signature)
+ (denote--creation-prepare-note-data title keywords 'org directory date template signature))
+ (front-matter (denote--format-front-matter title date keywords id signature 'org))
+ (template-string (cond ((stringp template) template)
+ ((functionp template) (funcall template))
+ (t (user-error "Invalid template")))))
+ (setq denote-last-path
+ (denote-format-file-name directory id keywords title ".org" signature))
+ (when (file-regular-p denote-last-path)
+ (user-error "A file named `%s' already exists" denote-last-path))
+ (denote--keywords-add-to-history keywords)
+ (concat front-matter template-string denote-org-capture-specifiers)))
+
+;; TODO 2023-12-02: Maybe simplify `denote-org-capture-with-prompts'
+;; by passing a single PROMPTS that is the same value as `denote-prompts'?
+
+;;;###autoload
+(defun denote-org-capture-with-prompts (&optional title keywords subdirectory date template)
+ "Like `denote-org-capture' but with optional prompt parameters.
+
+When called without arguments, do not prompt for anything. Just
+return the front matter with title and keyword fields empty and
+the date and identifier fields specified. Also make the file
+name consist of only the identifier plus the Org file name
+extension.
+
+Otherwise produce a minibuffer prompt for every non-nil value
+that corresponds to the TITLE, KEYWORDS, SUBDIRECTORY, DATE, and
+TEMPLATE arguments. The prompts are those used by the standard
+`denote' command and all of its utility commands.
+
+When returning the contents that fill in the Org capture
+template, the sequence is as follows: front matter, TEMPLATE, and
+then the value of the user option `denote-org-capture-specifiers'.
+
+Important note: in the case of SUBDIRECTORY actual subdirectories
+must exist---Denote does not create them. Same principle for
+TEMPLATE as templates must exist and are specified in the user
+option `denote-templates'."
+ (let ((denote-prompts '()))
+ (when template (push 'template denote-prompts))
+ (when date (push 'date denote-prompts))
+ (when subdirectory (push 'subdirectory denote-prompts))
+ (when keywords (push 'keywords denote-prompts))
+ (when title (push 'title denote-prompts))
+ (denote-org-capture)))
+
+(defun denote-org-capture-delete-empty-file ()
+ "Delete file if capture with `denote-org-capture' is aborted."
+ (when-let* ((file denote-last-path)
+ ((denote--file-empty-p file)))
+ (delete-file denote-last-path)))
+
+(add-hook 'org-capture-after-finalize-hook #'denote-org-capture-delete-empty-file)
+
+;;;; The `denote-rename-buffer-mode'
+
+(defgroup denote-rename-buffer nil
+ "Rename Denote buffers to be shorter and easier to read."
+ :group 'denote
+ :link '(info-link "(denote) Top")
+ :link '(url-link :tag "Homepage" "https://protesilaos.com/emacs/denote"))
+
+(defvaralias 'denote-buffer-has-backlinks-string 'denote-rename-buffer-backlinks-indicator
+ "Alias for `denote-rename-buffer-backlinks-indicator'.")
+
+(defcustom denote-rename-buffer-backlinks-indicator " <-->"
+ "A string used to indicate that a buffer has backlinks pointing to it."
+ :type 'string
+ :package-version '(denote . "3.1.0")
+ :group 'denote-rename-buffer)
+
+(defcustom denote-rename-buffer-format "[D] %D%b"
+ "The format of the buffer name `denote-rename-buffer' should use.
+The value is a string that treats specially the following specifiers:
+
+- The %t is the Denote TITLE in the front matter or the file name.
+- The %T is the Denote TITLE in the file name.
+- The %i is the Denote IDENTIFIER of the file.
+- The %I is the identifier converted to DAYNAME, DAYNUM MONTHNUM YEAR.
+- The %d is the same as %i (DATE mnemonic).
+- The %D is a \"do what I mean\" which behaves the same as %t and if
+ that returns nothing, it falls back to %I, then %i.
+- The %s is the Denote SIGNATURE of the file.
+- The %k is the Denote KEYWORDS of the file.
+- The %b inserts `denote-rename-buffer-backlinks-indicator'.
+- The %% is a literal percent sign.
+
+In addition, the following flags are available for each of the specifiers:
+
+- 0 :: Pad to the width, if given, with zeros instead of spaces.
+- - :: Pad to the width, if given, on the right instead of the left.
+- < :: Truncate to the width and precision, if given, on the left.
+- > :: Truncate to the width and precision, if given, on the right.
+- ^ :: Convert to upper case.
+- _ :: Convert to lower case.
+
+When combined all together, the above are written thus:
+
+ %<flags><width><precision>SPECIFIER-CHARACTER
+
+Any other string it taken as-is. Users may want, for example, to
+include some text that makes Denote buffers stand out, such as
+a [D] prefix."
+ :type 'string
+ :package-version '(denote . "4.0.0")
+ :group 'denote-rename-buffer)
+
+(defcustom denote-rename-buffer-function #'denote-rename-buffer
+ "Symbol of function that is called to rename the Denote file buffer.
+The default `denote-rename-buffer' function uses the pattern
+described in `denote-rename-buffer-format'.
+
+Users can set this variable to an arbitrary function that does
+something else. The function is called without arguments from
+the `find-file-hook' and `denote-after-new-note-hook'.
+
+A nil value for this variable means that the title of the Denote
+buffer will be used, if available."
+ :type '(choice
+ (const :tag "Rename using the `denote-rename-buffer-format'" denote-rename-buffer)
+ (function :tag "Use a custom renaming function"))
+ :package-version '(denote . "2.1.0")
+ :group 'denote-rename-buffer)
+
+(defun denote-rename-buffer--format (buffer)
+ "Parse the BUFFER through the `denote-rename-buffer-format'."
+ (when-let* ((file (buffer-file-name buffer)))
+ (let ((type (denote-filetype-heuristics file))
+ (should-show-backlink-indicator (and ; only do search if format contains "%b"
+ (string-match-p "%b" denote-rename-buffer-format)
+ (denote--file-has-backlinks-p file))))
+ (string-trim
+ (format-spec denote-rename-buffer-format
+ (list (cons ?t (cond
+ ((denote-retrieve-front-matter-title-value file type))
+ ((denote-retrieve-filename-title file))
+ (t "")))
+ (cons ?T (or (denote-retrieve-filename-title file) ""))
+ (cons ?b (if should-show-backlink-indicator denote-rename-buffer-backlinks-indicator ""))
+ (cons ?i (or (denote-retrieve-filename-identifier file) ""))
+ ;; TODO 2025-04-03: Maybe we can have something like `denote-date-format' here,
+ ;; but I think we are okay with a hardcoded value.
+ (cons ?I (or (when-let* ((id (denote-retrieve-filename-identifier file))
+ (_ (denote-valid-date-p id)))
+ (format-time-string "%A, %e %B %Y" (date-to-time (denote--id-to-date id))))
+ ""))
+ (cons ?d (or (denote-retrieve-filename-identifier file) ""))
+ (cons ?D (cond
+ ((denote-retrieve-front-matter-title-value file type))
+ ((denote-retrieve-filename-title file))
+ ((when-let* ((id (denote-retrieve-filename-identifier file)))
+ (if (denote-valid-date-p id)
+ (format-time-string "%A, %e %B %Y" (date-to-time (denote--id-to-date id)))
+ id)))
+ (t "")))
+ (cons ?s (or (denote-retrieve-filename-signature file) ""))
+ (cons ?k (or (denote-retrieve-filename-keywords file) ""))
+ (cons ?% "%"))
+ 'delete)))))
+
+(defun denote-rename-buffer (&optional buffer)
+ "Rename current buffer or optional BUFFER with `denote-rename-buffer-format'.
+The symbol of this function is the default value of the user
+option `denote-rename-buffer-function' and is thus used by the
+`denote-rename-buffer-mode'."
+ (when-let* ((file (buffer-file-name buffer))
+ ((denote-file-has-identifier-p file))
+ (new-name (denote-rename-buffer--format (or buffer (current-buffer))))
+ ((not (string-blank-p new-name))))
+ (rename-buffer new-name :unique)))
+
+(defun denote-rename-buffer--fallback (&optional buffer)
+ "Fallback to rename BUFFER or `current-buffer'.
+This is called if `denote-rename-buffer-rename-function' is nil."
+ (let ((denote-rename-buffer-format "%t"))
+ (denote-rename-buffer buffer)))
+
+(defun denote-rename-buffer-rename-function-or-fallback ()
+ "Call `denote-rename-buffer-function' or its fallback to rename with title.
+Add this to `find-file-hook' and `denote-after-new-note-hook'."
+ (funcall (or denote-rename-buffer-function #'denote-rename-buffer--fallback)))
+
+;;;###autoload
+(define-minor-mode denote-rename-buffer-mode
+ "Automatically rename Denote buffers to be easier to read.
+A buffer is renamed upon visiting the underlying file. This
+means that existing buffers are not renamed until they are
+visited again in a new buffer (files are visited with the command
+`find-file' or related)."
+ :global t
+ (if denote-rename-buffer-mode
+ (progn
+ (add-hook 'denote-after-new-note-hook #'denote-rename-buffer-rename-function-or-fallback)
+ (add-hook 'denote-after-rename-file-hook #'denote-rename-buffer-rename-function-or-fallback)
+ (add-hook 'find-file-hook #'denote-rename-buffer-rename-function-or-fallback))
+ (remove-hook 'denote-after-new-note-hook #'denote-rename-buffer-rename-function-or-fallback)
+ (remove-hook 'denote-after-rename-file-hook #'denote-rename-buffer-rename-function-or-fallback)
+ (remove-hook 'find-file-hook #'denote-rename-buffer-rename-function-or-fallback)))
+
+(provide 'denote)
+;;; denote.el ends here
blob - /dev/null
blob + dd891fd99baccd292a21829dfff25ae0881f2082 (mode 644)
Binary files /dev/null and elpa/denote-4.0.0/denote.info differ
blob - /dev/null
blob + f87165c99de687bfbadf03e7d99cf25a1a5a2530 (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/dir
+This is the file .../info/dir, which contains the
+topmost node of the Info hierarchy, called (dir)Top.
+The first time you invoke Info you start off looking at this node.
+
+File: dir, Node: Top This is the top of the INFO tree
+
+ This (the Directory node) gives a menu of major topics.
+ Typing "q" exits, "H" lists all Info commands, "d" returns here,
+ "h" gives a primer for first-timers,
+ "mEmacs<Return>" visits the Emacs manual, etc.
+
+ In Emacs, you can click mouse button 2 on a menu item or cross reference
+ to select it.
+
+* Menu:
+
+Emacs misc features
+* Denote: (denote). Simple notes with an efficient file-naming
+ scheme.
blob - /dev/null
blob + 1025c345c029f57bf021a7791a18ed79ab2099a0 (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0/tests/denote-test.el
+;;; denote-test.el --- Unit tests for Denote -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023-2025 Free Software Foundation, Inc.
+
+;; Author: Protesilaos Stavrou <info@protesilaos.com>
+;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
+;; URL: https://github.com/protesilaos/denote
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Tests for Denote. Note that we are using Shorthands in this file,
+;; so the "dt-" prefix really is "denote-test-". Evaluate the
+;; following to learn more:
+;;
+;; (info "(elisp) Shorthands")
+
+;;; Code:
+
+(require 'ert)
+
+;;;; Tests for denote.el
+
+(require 'denote)
+
+(ert-deftest dt-denote--make-denote-directory ()
+ "Test that `denote--make-denote-directory' creates the directory."
+ (should (null (denote--make-denote-directory))))
+
+(ert-deftest dt-denote-directory ()
+ "Test that variable `denote-directory' returns an absolute directory name."
+ (let ((path (denote-directory)))
+ (should (and (file-directory-p path)
+ (file-name-absolute-p path)))))
+
+(ert-deftest dt-denote-sluggify-title ()
+ "Test that `denote-sluggify-title' removes punctuation from the string.
+Concretely, remove anything specified in `denote-sluggify-title'."
+ (should (equal (denote-sluggify-title "this-is-!@#test")
+ "this-is-test")))
+
+(ert-deftest dt-denote-slug-keep-only-ascii ()
+ "Test that `denote-slug-keep-only-ascii' removes non-ASCII characters."
+ (should (equal
+ (denote-slug-keep-only-ascii "There are no-ASCII : characters | here 😀")
+ "There are no-ASCII characters here ")))
+
+(ert-deftest dt-denote-slug-hyphenate ()
+ "Test that `denote-slug-hyphenate' hyphenates the string.
+Also replace multiple hyphens with a single one and remove any
+leading and trailing hyphen."
+ (should (equal (denote-slug-hyphenate "__ This is a test __ ")
+ "This-is-a-test")))
+
+(ert-deftest dt-denote-sluggify ()
+ "Test that `denote-sluggify' sluggifies the string.
+To sluggify is to (i) downcase, (ii) hyphenate, (iii) de-punctuate, and (iv) remove spaces from the string."
+ (should (equal (denote-sluggify 'title " ___ !~!!$%^ This iS a tEsT ++ ?? ")
+ "this-is-a-test")))
+
+(ert-deftest Ddenote--slug-put-equals ()
+ "Test that `denote-slug-put-equals' replaces spaces/underscores with =.
+Otherwise do the same as what is described in
+`dt-denote-slug-hyphenate'.
+
+The use of the equals sign is for the SIGNATURE field of the
+Denote file name."
+ (should (equal (denote-slug-put-equals "__ This is a test __ ")
+ "This=is=a=test")))
+
+(ert-deftest dt-denote-sluggify-signature ()
+ "Test that `denote-sluggify-signature' sluggifies the string for file signatures.
+This is like `dt-denote-sluggify', except that it also
+accounts for what we describe in `dt-denote-slug-put-equals'."
+ (should (equal (denote-sluggify-signature "--- ___ !~!!$%^ This -iS- a tEsT ++ ?? ")
+ "this=is=a=test")))
+
+(ert-deftest dt-denote-sluggify-keyword ()
+ "Test that `denote-sluggify-keyword' sluggifies the string while joining words.
+In this context, to join words is to elimitate any space or
+delimiter between them.
+
+Otherwise, this is like `dt-denote-sluggify'."
+ (should (equal (denote-sluggify-keyword "--- ___ !~!!$%^ This iS a - tEsT ++ ?? ")
+ "thisisatest")))
+
+(ert-deftest dt-denote-sluggify-keywords ()
+ "Test that `denote-sluggify-keywords' sluggifies a list of strings.
+The function also account for the value of the user option
+`denote-allow-multi-word-keywords'."
+ (should
+ (equal (denote-sluggify-keywords '("one !@# --- one" " two" "__ three __"))
+ '("oneone" "two" "three"))))
+
+(ert-deftest dt-denote--file-empty-p ()
+ "Test that `denote--file-empty-p' returns non-nil on empty file."
+ ;; (should (null (denote--file-empty-p user-init-file))
+ (should (let ((file (make-temp-file "denote-test")))
+ (prog1
+ (denote--file-empty-p file)
+ (delete-file file)))))
+
+(ert-deftest dt-denote-file-is-note-p ()
+ "Test that `denote-file-is-note-p' checks that files is a Denote note.
+For our purposes, a note must note be a directory, must satisfy
+`file-regular-p', its path must be part of the variable
+`denote-directory', it must have a Denote identifier in its name,
+and use one of the extensions implied by the variable `denote-file-type'."
+ (should (let* ((tmp (temporary-file-directory))
+ (denote-directory tmp)
+ (file (concat tmp "20230522T154900--test__keyword.txt")))
+ (with-current-buffer (find-file-noselect file)
+ (write-file file))
+ (prog1
+ (denote-file-is-note-p file)
+ (delete-file file)))))
+
+(ert-deftest dt-denote-file-has-identifier-p ()
+ "Test that `denote-file-has-identifier-p' checks for a Denote identifier."
+ (should (denote-file-has-identifier-p "20230522T154900--test__keyword.txt"))
+ (should (null (denote-file-has-identifier-p "T154900--test__keyword.txt"))))
+
+(ert-deftest dt-denote-file-has-signature-p ()
+ "Test that `denote-file-has-signature-p' checks for a Denote signature."
+ (should (denote-file-has-signature-p "20230522T154900==sig--test__keyword.txt"))
+ (should (null (denote-file-has-signature-p "20230522T154900--test__keyword.txt"))))
+
+(ert-deftest dt-denote-file-has-supported-extension-p ()
+ "Test that `denote-file-has-supported-extension-p' matches a supported extension."
+ (should
+ (member
+ (file-name-extension "20230522T154900==sig--test__keyword.txt" :period)
+ (denote-file-type-extensions-with-encryption)))
+ (should
+ (null
+ (member
+ (file-name-extension "20230522T154900==sig--test__keyword" :period)
+ (denote-file-type-extensions-with-encryption)))))
+
+(ert-deftest dt-denote-file-type-extensions ()
+ "Test that `denote-file-type-extensions' returns file extensions.
+We check for the common file type extensions, though the user can
+theoretically set `denote-file-types' to nil and handle things on
+their own. We do not have to test for that scenario, because
+such a user will be redefining large parts of Denote's behaviour
+with regard to file types."
+ (let ((extensions (denote-file-type-extensions)))
+ (should (or (member ".md" extensions)
+ (member ".org" extensions)
+ (member ".txt" extensions)))))
+
+(ert-deftest dt-denote-file-type-extensions-with-encryption ()
+ "Test that `denote-file-type-extensions-with-encryption' covers encryption.
+Extend what we do in `dt-denote-file-type-extensions'."
+ (let ((extensions (denote-file-type-extensions-with-encryption)))
+ (should (or (member ".md" extensions)
+ (member ".org" extensions)
+ (member ".txt" extensions)
+ (member ".md.gpg" extensions)
+ (member ".org.gpg" extensions)
+ (member ".txt.gpg" extensions)
+ (member ".md.age" extensions)
+ (member ".org.age" extensions)
+ (member ".txt.age" extensions)))))
+
+(ert-deftest dt-denote--format-front-matter ()
+ "Test that `denote--format-front-matter' formats front matter correctly.
+To make the test reproducible, set `denote-date-format' to a value that
+does not involve the time zone."
+ (let ((denote-date-format "%Y-%m-%d")
+ (denote-front-matter-components-present-even-if-empty-value '(title keywords signature date identifier)))
+ (should (and (equal (denote--format-front-matter "" (date-to-time "20240101T120000") '("") "" "" 'text)
+ (mapconcat #'identity
+ '("title: "
+ "date: 2024-01-01"
+ "tags: "
+ "identifier: "
+ "signature: "
+ "---------------------------\n\n")
+ "\n"))
+
+ (equal
+ (denote--format-front-matter
+ "Some test" (date-to-time "2023-06-05") '("one" "two")
+ "20230605T102234" "sig" 'text)
+ (mapconcat #'identity
+ '("title: Some test"
+ "date: 2023-06-05"
+ "tags: one two"
+ "identifier: 20230605T102234"
+ "signature: sig"
+ "---------------------------\n\n")
+ "\n"))))
+
+ (should (and (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'org)
+ (mapconcat #'identity
+ '("#+title: "
+ "#+date: 2024-01-01"
+ "#+filetags: "
+ "#+identifier: "
+ "#+signature: "
+ "\n")
+ "\n"))
+
+ (equal
+ (denote--format-front-matter
+ "Some test" (date-to-time "2023-06-05") '("one" "two")
+ "20230605T102234" "sig" 'org)
+ (mapconcat #'identity
+ '("#+title: Some test"
+ "#+date: 2023-06-05"
+ "#+filetags: :one:two:"
+ "#+identifier: 20230605T102234"
+ "#+signature: sig"
+ "\n")
+ "\n"))))
+
+ (should (and (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'markdown-yaml)
+ (mapconcat #'identity
+ '("---"
+ "title: \"\""
+ "date: 2024-01-01"
+ "tags: []"
+ "identifier: \"\""
+ "signature: \"\""
+ "---"
+ "\n")
+ "\n"))
+
+ (equal
+ (denote--format-front-matter
+ "Some test" (date-to-time "2023-06-05") '("one" "two")
+ "20230605T102234" "sig" 'markdown-yaml)
+ (mapconcat #'identity
+ '("---"
+ "title: \"Some test\""
+ "date: 2023-06-05"
+ "tags: [\"one\", \"two\"]"
+ "identifier: \"20230605T102234\""
+ "signature: \"sig\""
+ "---"
+ "\n")
+ "\n"))))
+
+ (should (and (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'markdown-toml)
+ (mapconcat #'identity
+ '("+++"
+ "title = \"\""
+ "date = 2024-01-01"
+ "tags = []"
+ "identifier = \"\""
+ "signature = \"\""
+ "+++"
+ "\n")
+ "\n"))
+
+ (equal
+ (denote--format-front-matter
+ "Some test" (date-to-time "2023-06-05") '("one" "two")
+ "20230605T102234" "sig" 'markdown-toml)
+ (mapconcat #'identity
+ '("+++"
+ "title = \"Some test\""
+ "date = 2023-06-05"
+ "tags = [\"one\", \"two\"]"
+ "identifier = \"20230605T102234\""
+ "signature = \"sig\""
+ "+++"
+ "\n")
+ "\n"))))))
+
+(ert-deftest dt-denote-format-file-name ()
+ "Test that `denote-format-file-name' returns all expected paths."
+ (let* ((title "Some test")
+ (id (format-time-string denote-id-format (denote-valid-date-p "2023-11-28 05:53:11")))
+ (denote-directory "/tmp/test-denote")
+ (kws '("one" "two")))
+ (should-error (denote-format-file-name
+ nil
+ id
+ kws
+ title
+ (denote--file-extension 'org)
+ ""))
+
+ (should-error (denote-format-file-name
+ ""
+ id
+ kws
+ title
+ (denote--file-extension 'org)
+ ""))
+
+ (should-error (denote-format-file-name
+ denote-directory ; notice this is the `let' bound value without the suffix
+ id
+ kws
+ title
+ (denote--file-extension 'org)
+ ""))
+
+ (should-error (denote-format-file-name
+ (denote-directory)
+ ""
+ nil
+ ""
+ (denote--file-extension 'org)
+ ""))
+
+ (should (equal (denote-format-file-name
+ (denote-directory)
+ nil
+ kws
+ title
+ (denote--file-extension 'org)
+ "")
+ "/tmp/test-denote/--some-test__one_two.org"))
+
+ (should (equal (denote-format-file-name
+ (denote-directory)
+ ""
+ kws
+ title
+ (denote--file-extension 'org)
+ "")
+ "/tmp/test-denote/--some-test__one_two.org"))
+
+ (should (equal (denote-format-file-name
+ (denote-directory)
+ "0123456"
+ kws
+ title
+ (denote--file-extension 'org)
+ "")
+ "/tmp/test-denote/@@0123456--some-test__one_two.org"))
+
+ (should (equal (denote-format-file-name
+ (denote-directory)
+ id
+ kws
+ title
+ (denote--file-extension 'org)
+ "")
+ "/tmp/test-denote/20231128T055311--some-test__one_two.org"))
+
+ (should (equal (denote-format-file-name
+ (denote-directory)
+ id
+ nil
+ ""
+ (denote--file-extension 'org)
+ "")
+ "/tmp/test-denote/20231128T055311.org"))
+
+ (should (equal (denote-format-file-name
+ (denote-directory)
+ id
+ nil
+ nil
+ (denote--file-extension 'org)
+ nil)
+ "/tmp/test-denote/20231128T055311.org"))
+
+ (should (equal (denote-format-file-name
+ (denote-directory)
+ id
+ kws
+ title
+ (denote--file-extension 'org)
+ "sig")
+ "/tmp/test-denote/20231128T055311==sig--some-test__one_two.org"))))
+
+(ert-deftest dt-denote-get-file-extension ()
+ "Test that `denote-get-file-extension' gets the correct file extension."
+ (should (and (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing") "")
+ (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org") ".org")
+ (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.gpg") ".org.gpg")
+ (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.age") ".org.age"))))
+
+(ert-deftest dt-denote-get-file-extension-sans-encryption ()
+ "Test that `denote-get-file-extension-sans-encryption' gets the file extension without encryption."
+ (should (and (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing") "")
+ (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org") ".org")
+ (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.gpg") ".org")
+ (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.age") ".org"))))
+
+(ert-deftest dt-denote-filetype-heuristics ()
+ "Test that `denote-filetype-heuristics' gets the correct file type."
+ (should (and (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing") nil)
+ (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org") 'org)
+ (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.gpg") 'org)
+ (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.age") 'org)
+ (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt") 'text)
+ (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.gpg") 'text)
+ (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.age") 'text)
+ ;; NOTE 2023-10-11: It returns `markdown-yaml' as a fallback. In
+ ;; an actual file, it reads the file contents to determine what
+ ;; it is and can return `markdown-toml'. In principle, we should
+ ;; be testing this here, though I prefer to keep things simple.
+ (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md") 'markdown-yaml)
+ (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.gpg") 'markdown-yaml)
+ (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.age") 'markdown-yaml))))
+
+(ert-deftest dt-denote-get-identifier ()
+ "Test that `denote-get-identifier' returns an identifier."
+ (should (and (equal (denote-get-identifier nil) "")
+ (equal (denote-get-identifier 1705644188) "20240119T080308")
+ (equal (denote-get-identifier '(26026 4251)) "20240119T080307"))))
+
+(ert-deftest dt-denote-retrieve-filename-identifier ()
+ "Test that `denote-retrieve-filename-identifier' returns only the identifier."
+ (should (and (null
+ (denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing.org"))
+ (equal
+ (denote-retrieve-filename-identifier "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")
+ "20240610T194654")
+ (equal
+ (denote-retrieve-filename-identifier "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
+ "20240610T194654")
+ (equal
+ (denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org")
+ "20240610T194654")
+ (equal
+ (denote-retrieve-filename-identifier "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org")
+ "20240610T194654")
+ (equal
+ (denote-retrieve-filename-identifier "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
+ "20240610T194654")
+ (equal
+ (denote-retrieve-filename-identifier "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
+ "20240610T194654"))))
+
+(ert-deftest dt-denote-retrieve-filename-title ()
+ "Test that `denote-retrieve-filename-title' returns only the title."
+ (should (and (null
+ (denote-retrieve-filename-title "/path/to/testing/20240610T194654__denote_testing.org"))
+ (equal
+ (denote-retrieve-filename-title "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")
+ "this-is-a-test-reordered")
+ (equal
+ (denote-retrieve-filename-title "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
+ "this-is-a-test-reordered")
+ (equal
+ (denote-retrieve-filename-title "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org")
+ "this-is-a-test-reordered")
+ (equal
+ (denote-retrieve-filename-title "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org")
+ "this-is-a-test-reordered")
+ (equal
+ (denote-retrieve-filename-title "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
+ "this-is-a-test-reordered")
+ (equal
+ (denote-retrieve-filename-title "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
+ "this-is-a-test-reordered"))))
+
+(ert-deftest dt-denote-retrieve-filename-keywords ()
+ "Test that `denote-retrieve-filename-keywords' returns only the keywords."
+ (should (and (null
+ (denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered.org"))
+ (equal
+ (denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")
+ "denote_testing")
+ (equal
+ (denote-retrieve-filename-keywords "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
+ "denote_testing")
+ (equal
+ (denote-retrieve-filename-keywords "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org")
+ "denote_testing")
+ (equal
+ (denote-retrieve-filename-keywords "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org")
+ "denote_testing")
+ (equal
+ (denote-retrieve-filename-keywords "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
+ "denote_testing")
+ (equal
+ (denote-retrieve-filename-keywords "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
+ "denote_testing"))))
+
+(ert-deftest dt-denote-retrieve-filename-signature ()
+ "Test that `denote-retrieve-filename-signature' returns only the signature."
+ (should (and (null
+ (denote-retrieve-filename-signature "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org"))
+ (equal
+ (denote-retrieve-filename-signature "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
+ "signature")
+ (equal
+ (denote-retrieve-filename-signature "/path/to/testing/--this-is-a-test-reordered==signature__denote_testing@@20240610T194654.org")
+ "signature")
+ (equal
+ (denote-retrieve-filename-signature "/path/to/testing/__denote_testing--this-is-a-test-reordered==signature@@20240610T194654.org")
+ "signature")
+ (equal
+ (denote-retrieve-filename-signature "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered==signature.org")
+ "signature")
+ (equal
+ (denote-retrieve-filename-signature "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
+ "signature"))))
+
+(ert-deftest dt-denote-identifier-p ()
+ "Test that `denote-identifier-p' works for Denote identifiers."
+ (should (and (denote-identifier-p "20240901T090910")
+ (null (denote-identifier-p "20240901T090910-not-identifier-format")))))
+
+(ert-deftest dt-denote--id-to-date ()
+ "Test that `denote--id-to-date' returns the date from an identifier."
+ (should (equal (denote--id-to-date "20240901T090910") "2024-09-01"))
+ (should-error (denote--id-to-date "20240901T090910-not-identifier-format")))
+
+(ert-deftest dt-denote--date-convert ()
+ "Test that `denote--date-convert' works with dates."
+ (should (and
+ (equal (denote--date-convert '(26454 45206 461174 657000) :list)
+ '(26454 45206 461174 657000))
+
+ (equal (denote--date-convert '(26454 45206 461174 657000) :string)
+ "2024-12-09 10:55:50")
+
+ (equal (denote--date-convert nil :string)
+ "")
+
+ (equal (denote--date-convert nil :list)
+ nil)))
+ (should-error (denote--date-convert '(26454 45206 461174 657000) :not-valid-type))
+ (should-error (denote--date-convert nil :not-valid-type)))
+
+(provide 'denote-test)
+;;; denote-test.el ends here
+
+;; Local Variables:
+;; read-symbol-shorthands: (("dt" . "denote-test-"))
+;; End:
blob - /dev/null
blob + 02a9d0609cc8457e6e5090424ec533b0e65a7c26 (mode 644)
--- /dev/null
+++ elpa/denote-4.0.0.signed
+Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2025-04-15T23:10:04+0200 using EDDSA
\ No newline at end of file
blob - /dev/null
blob + b25586baccfaf0140555f8c08a091c50ef46582d (mode 644)
--- /dev/null
+++ elpa/denote-org-0.1.1/.elpaignore
+COPYING
+doclicense.texi
blob - /dev/null
blob + 3fff0b808826c4f93d106ceea1e95751f6c6cd6c (mode 644)
--- /dev/null
+++ elpa/denote-org-0.1.1/README-elpa
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+ DENOTE-ORG: EXTENSIONS TO BETTER INTEGRATE ORG
+ WITH DENOTE
+
+ Protesilaos Stavrou
+ info@protesilaos.com
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+
+This manual, written by Protesilaos Stavrou, describes the customization
+options for the Emacs package called `denote' (or `denote.el'), and
+provides every other piece of information pertinent to it.
+
+The documentation furnished herein corresponds to stable version 0.1.0,
+released on 2025-04-15. Any reference to a newer feature which does not
+yet form part of the latest tagged commit, is explicitly marked as such.
+
+Current development target is 0.2.0-dev.
+
+⁃ Package name (GNU ELPA): `denote-org'
+⁃ Official manual: <https://protesilaos.com/emacs/denote-org>
+⁃ Git repository: <https://github.com/protesilaos/denote-org>
+⁃ Backronym: Denote… Ordinarily Restricts Gyrations.
+
+If you are viewing the README.org version of this file, please note that
+the GNU ELPA machinery automatically generates an Info manual out of it.
+
+Table of Contents
+─────────────────
+
+1. COPYING
+2. Overview
+3. Use Org dynamic blocks
+.. 1. Org dynamic blocks to insert links
+.. 2. The Org dynamic block to insert missing links only
+.. 3. The Org dynamic block to insert backlinks
+.. 4. Org dynamic block to insert file contents
+.. 5. Org dynamic block to insert Org files as headings
+4. Create a note from the current Org subtree
+5. Insert link to an Org file with a further pointer to a heading
+.. 1. Backlinks for Org headings
+6. Convert `denote:' links to `file:' links in Org and vice versa
+7. Installation
+.. 1. GNU ELPA package
+.. 2. Manual installation
+8. Sample configuration
+9. Acknowledgements
+10. GNU Free Documentation License
+11. Indices
+.. 1. Function index
+.. 2. Variable index
+.. 3. Concept index
+
+
+1 COPYING
+═════════
+
+ Copyright (C) 2022-2025 Free Software Foundation, Inc.
+
+ Permission is granted to copy, distribute and/or modify
+ this document under the terms of the GNU Free
+ Documentation License, Version 1.3 or any later version
+ published by the Free Software Foundation; with no
+ Invariant Sections, with the Front-Cover Texts being “A
+ GNU Manual,” and with the Back-Cover Texts as in (a)
+ below. A copy of the license is included in the section
+ entitled “GNU Free Documentation License.”
+
+ (a) The FSF’s Back-Cover Text is: “You have the freedom to
+ copy and modify this GNU manual.”
+
+
+2 Overview
+══════════
+
+ The `denote-org' package contains extra features that better integrate
+ Denote with Org mode. These used to be available as part of the main
+ `denote' package in a file called `denote-org-extras.el', but now live
+ in this standalone package to main things easier to maintain and
+ understand.
+
+ With `denote-org', users have Org-specific extensions such as dynamic
+ blocks, links to headings, and splitting an Org subtree into its own
+ standalone file. The following sections cover the technicalities.
+
+
+3 Use Org dynamic blocks
+════════════════════════
+
+ Denote can optionally integrate with Org mode’s “dynamic blocks”
+ facility. This means that it can use special blocks that are evaluated
+ with `C-c C-x C-u' (`org-dblock-update') to generate their contents.
+ The following subsections describe the types of Org dynamic blocks
+ provided by Denote.
+
+ • [Org dynamic blocks to insert links or backlinks]
+ • [Org dynamic block to insert file contents]
+
+ A dynamic block gets its contents by evaluating a function that
+ corresponds to the type of block. The block type and its parameters
+ are stated in the opening `#+BEGIN' line. Typing `C-c C-x C-u'
+ (`org-dblock-update') with point on that line runs (or re-runs) the
+ associated function with the given parameters and populates the
+ block’s contents accordingly.
+
+ Dynamic blocks are particularly useful for metanote entries that
+ reflect on the status of earlier notes (read the Denote manual’s
+ section about writing metanotes).
+
+ The Org manual describes the technicalities of Dynamic Blocks.
+ Evaluate:
+
+ ┌────
+ │ (info "(org) Dynamic Blocks")
+ └────
+
+
+[Org dynamic blocks to insert links or backlinks] See section 3.1
+
+[Org dynamic block to insert file contents] See section 3.4
+
+3.1 Org dynamic blocks to insert links
+──────────────────────────────────────
+
+ The `denote-links' block can be inserted at point with the command
+ `denote-org-dblock-insert-links' or by manually including the
+ following in an Org file:
+
+ ┌────
+ │ #+BEGIN: denote-links :regexp "YOUR REGEXP HERE" :not-regexp :excluded-dirs-regexp nil :sort-by-component nil :reverse-sort nil :id-only nil :include-date nil
+ │
+ │ #+END:
+ └────
+
+
+ All the parameters except for `:regexp' are optional.
+
+ The `denote-links' block is also registered as an option for the
+ command `org-dynamic-block-insert-dblock'.
+
+ Type `C-c C-x C-u' (`org-dblock-update') with point on the `#+BEGIN'
+ line to update the block.
+
+ • The `:regexp' parameter is mandatory. Its value is a string and its
+ behaviour is the same as that of the standard `denote-add-links'
+ command (part of the main `denote' package). Concretely, it produces
+ a typographic list of links to files matching the giving regular
+ expression. The value of the `:regexp' parameter may also be of the
+ form read by the `rx' macro (Lisp notation instead of a string), as
+ explained in the Emacs Lisp Reference Manual (evaluate this code to
+ read the documentation: `(info "(elisp) Rx Notation")'). Note that
+ you do not need to write an actual regular expression to get
+ meaningful results: even something like `_journal' will work to
+ include all files that have a `journal' keyword.
+
+ • The `:not-regexp' parameter is optional. It is a regular expression
+ that applies after `:regexp' to filter out the matching files.
+
+ • The `:excluded-dirs-regexp' is a string that contains a word or
+ regular expression that matches against directory files names
+ to-be-excluded from the results. This has the same meaning as
+ setting the `denote-excluded-directories-regexp' user option (which
+ is part of the main `denote' package). The user option has a global
+ effect, which is overridden locally in the dynamic block. When the
+ value of `:excluded-dirs-regexp' is nil (the default), the value of
+ `denote-excluded-directories-regexp' is used (which is also nil by
+ default, meaning that all directories are included). When the value
+ of `excluded-dirs-regexp' is `t' or some other symbol, then the
+ `denote-excluded-directories-regexp' is ignored altogether. This is
+ useful in the scenario where the user option is set to exclude some
+ directories but the dynamic blocks wants to lift that restriction.
+
+ • The `:sort-by-component' parameter is optional. It sorts the files
+ by the given Denote file name component. The value it accepts is an
+ unquoted symbol among `title', `keywords', `signature',
+ `identifier'. When using the command
+ `denote-org-dblock-insert-files', this parameter is automatically
+ inserted together with the (`:regexp' parameter) and the user is
+ prompted for a file name component.
+
+ • The `:reverse-sort' parameter is optional. It reverses the order in
+ which files appear in. This is meaningful even without the presence
+ of the parameter `:sort-by-component', though it also combines with
+ it.
+
+ • The `:id-only' parameter is optional. It accepts a `t' value, in
+ which case links are inserted without a description text but only
+ with the identifier of the given file. This has the same meaning as
+ with the `denote-link' command and related facilities (read the
+ Denote manual’s section about linking to other files in the
+ `denote-directory').
+
+ • The `:include-date' parameter controls whether to display the date
+ of the file name after the title. This is done when its value is
+ `t'. By default (a nil value), no date is shown.
+
+ • An optional `:block-name' parameter can be specified with a string
+ value to add a `#+name' to the results. This is useful for further
+ processing using Org facilities (a feature that is outside Denote’s
+ purview).
+
+ In some workflows, users may want to have a separate block to see what
+ other links they are missing since they last updated the dynamic
+ block. We cover that case as well ([The Org dynamic block to insert
+ missing links only]).
+
+
+[The Org dynamic block to insert missing links only] See section 3.2
+
+
+3.2 The Org dynamic block to insert missing links only
+──────────────────────────────────────────────────────
+
+ The `denote-missing-links' block is available with the command
+ `denote-org-dblock-insert-missing-links'. It is like the
+ aforementioned `denote-links' block, except it only lists links to
+ files that are not present in the current buffer ([Org dynamic blocks
+ to insert links]). The parameters are otherwise the same and are all
+ optional except for `:regexp':
+
+ ┌────
+ │ #+BEGIN: denote-missing-links :regexp "YOUR REGEXP HERE" :excluded-dirs-regexp nil :sort-by-component nil :reverse-sort nil :id-only nil :include-date nil
+ │
+ │ #+END:
+ └────
+
+
+ The `denote-missing-links' block is also registered as an option for
+ the command `org-dynamic-block-insert-dblock'.
+
+ Remember to type `C-c C-x C-u' (`org-dblock-update') with point on the
+ `#+BEGIN' line to update the block.
+
+
+[Org dynamic blocks to insert links] See section 3.1
+
+
+3.3 The Org dynamic block to insert backlinks
+─────────────────────────────────────────────
+
+ Apart from links to files matching a regular expression, we can also
+ produce a list of backlinks to the current file. The dynamic block can
+ be inserted at point with the command
+ `denote-org-dblock-insert-backlinks' or by manually writing this in an
+ Org file:
+
+ ┌────
+ │ #+BEGIN: denote-backlinks :excluded-dirs-regexp nil :sort-by-component nil :reverse-sort nil :id-only nil :this-heading-only nil :include-date nil
+ │
+ │ #+END:
+ └────
+
+
+ The `denote-backlinks' block is also registered as an option for the
+ command `org-dynamic-block-insert-dblock'.
+
+ Remember to type `C-c C-x C-u' (`org-dblock-update') with point on the
+ `#+BEGIN' line to update the block.
+
+ The parameters recognised by this dynamic block are almost the same as
+ that for inserting links ([Org dynamic blocks to insert links]). They
+ are all optional in this case and there is no parameter expecting a
+ regular expression for matching files to link to.
+
+ Additionally, the `denote-backlinks' block also recognises the
+ `:this-heading-only' parameter. It determines if the backlinks are
+ about the file or the heading under which the dynamic block is
+ inserted ([Backlinks for Org headings]). When this parameter is
+ omitted or nil (the default), then the backlinks are about the whole
+ file, but if this parameter has a `t' value then the backlinks are
+ specifically for the heading ([Insert link to an Org file with a
+ further pointer to a heading]).
+
+
+[Org dynamic blocks to insert links] See section 3.1
+
+[Backlinks for Org headings] See section 5.1
+
+[Insert link to an Org file with a further pointer to a heading] See
+section 5
+
+
+3.4 Org dynamic block to insert file contents
+─────────────────────────────────────────────
+
+ Denote can optionally use Org’s dynamic blocks facility to produce a
+ section that lists entire file contents ([Use Org dynamic blocks]).
+ This works by instructing Org to match a regular expression of Denote
+ files, the same way we do with Denote links (read the Denote manual’s
+ section about inserting links that match a regular expression).
+
+ This is useful to, for example, compile a dynamically concatenated
+ list of scattered thoughts on a given topic, like `^2023.*_emacs' for
+ a long entry that incorporates all the notes written in 2023 with the
+ keyword `emacs'.
+
+ To produce such a block, call the command
+ `denote-org-dblock-insert-files' or manually write the following block
+ in an Org file and then type `C-c C-x C-u' (`org-dblock-update') on
+ the `#+BEGIN' line to run it (do it again to recalculate the block):
+
+ ┌────
+ │ #+BEGIN: denote-files :regexp "YOUR REGEXP HERE" :not-regexp nil :sort-by-component nil :reverse-sort nil :no-front-matter nil :file-separator nil :add-links nil
+ │
+ │ #+END:
+ └────
+
+
+ All parameters are optional except for `:regexp'.
+
+ The `denote-files' block is also registered as an option for the
+ command `org-dynamic-block-insert-dblock'.
+
+ Remember to type `C-c C-x C-u' (`org-dblock-update') with point on the
+ `#+BEGIN' line to update the block.
+
+ To fully control the output, include these additional optional
+ parameters, which are described further below:
+
+ • The `:regexp' parameter is mandatory. Its value is a string,
+ representing a regular expression to match Denote file names. Its
+ value may also be an `rx' expression instead of a string, as noted
+ in the previous section ([Org dynamic blocks to insert links or
+ backlinks]). Note that you do not need to write an actual regular
+ expression to get meaningful results: even something like `_journal'
+ will work to include all files that have a `journal' keyword.
+
+ • The `:not-regexp' parameter is optional. It is a regular expression
+ that applies after `:regexp' to filter out the matching files.
+
+ • The `:excluded-dirs-regexp' is a string that contains a word or
+ regular expression that matches against directory files names
+ to-be-excluded from the results. This has the same meaning as
+ setting the `denote-excluded-directories-regexp' user option (which
+ is part of the main `denote' package). The user option has a global
+ effect, which is overridden locally in the dynamic block. When the
+ value of `:excluded-dirs-regexp' is nil (the default), the value of
+ `denote-excluded-directories-regexp' is used (which is also nil by
+ default, meaning that all directories are included). When the value
+ of `excluded-dirs-regexp' is `t' or some other symbol, then the
+ `denote-excluded-directories-regexp' is ignored altogether. This is
+ useful in the scenario where the user option is set to exclude some
+ directories but the dynamic blocks wants to lift that restriction.
+
+ • The `:sort-by-component' parameter is optional. It sorts the files
+ by the given Denote file name component. The value it accepts is an
+ unquoted symbol among `title', `keywords', `signature',
+ `identifier'. When using the command
+ `denote-org-dblock-insert-files', this parameter is automatically
+ inserted together with the (`:regexp' parameter) and the user is
+ prompted for a file name component.
+
+ • The `:reverse-sort' parameter is optional. It reverses the order in
+ which files appear in. This is meaningful even without the presence
+ of the parameter `:sort-by-component', though it also combines with
+ it.
+
+ • The `:file-separator' parameter is optional. If it is omitted, then
+ Denote will use no separator between the files it inserts. If the
+ value is `t' the `denote-org-dblock-file-contents-separator' is
+ applied at the end of each file: it introduces some empty lines and
+ a horizontal rule between them to visually distinguish individual
+ files. If the `:file-separator' value is a string, it is used as the
+ file separator (e.g. use `"\n"' to insert just one empty new line).
+
+ • The `:no-front-matter' parameter is optional. When set to a `t'
+ value, Denote tries to remove front matter from the files it is
+ inserting in the dynamic block. The technique used to perform this
+ operation is by removing all lines from the top of the file until
+ the first empty line. This works with the default front matter that
+ Denote adds, but is not 100% reliable with all sorts of user-level
+ modifications and edits to the file. When the `:no-front-matter' is
+ set to a natural number, Denote will omit that many lines from the
+ top of the file.
+
+ • The `:add-links' parameter is optional. When it is set to a `t'
+ value, all files are inserted as a typographic list and are indented
+ accordingly. The first line in each list item is a link to the file
+ whose contents are inserted in the following lines. When the value
+ is `id-only', then links are inserted without a description text but
+ only with the identifier of the given file. This has the same
+ meaning as with the `denote-link' command and related facilities
+ (those are explained at length in the Denote manual). Remember that
+ Org can fold the items in a typographic list the same way it does
+ with headings. So even long files can be presented in this format
+ without much trouble.
+
+ • An optional `:block-name' parameter can be specified with a string
+ value to add a `#+name' to the results. This is useful for further
+ processing using Org facilities (a feature that is outside Denote’s
+ purview).
+
+
+[Use Org dynamic blocks] See section 3
+
+[Org dynamic blocks to insert links or backlinks] See section 3.1
+
+
+3.5 Org dynamic block to insert Org files as headings
+─────────────────────────────────────────────────────
+
+ [ IMPORTANT NOTE: This dynamic block only works with Org files,
+ because it has to assume the Org notation in order to insert each
+ file’s contents as its own heading. ]
+
+ As a variation of the previously covered block that inserts file
+ contents, we have the `denote-org-dblock-insert-files-as-headings'
+ command ([Org dynamic block to insert file contents]). It Turn the
+ `#+title' of each file into a top-level heading. Then it increments
+ all original headings in the file by one, so that they become
+ subheadings of what once was the `#+title'. Similarly, the
+ `#+filetags' of each file as tags for the top-level heading (what was
+ the `#+title').
+
+ Because of how it is meant to work, this dynamic block only works with
+ Org files.
+
+ In its simplest form, this dynamic block looks like this, with
+ `:regexp' as the only mandatory parameter:
+
+ ┌────
+ │ #+BEGIN: denote-files-as-headings :regexp "YOUR REGEXP HERE"
+ │
+ │ #+END:
+ └────
+
+
+ Though when you use the command
+ `denote-org-dblock-insert-files-as-headings' you get all the
+ parameters included:
+
+ ┌────
+ │ #+BEGIN: denote-files-as-headings :regexp "YOUR REGEXP HERE" :not-regexp nil :excluded-dirs-regexp nil :sort-by-component title :reverse-sort nil :add-links t
+ │
+ │ #+END:
+ └────
+
+
+ • The `:regexp' parameter is mandatory. Its value is a string,
+ representing a regular expression to match Denote file names. Its
+ value may also be an `rx' expression instead of a string, as noted
+ in the previous section ([Org dynamic blocks to insert links or
+ backlinks]). Note that you do not need to write an actual regular
+ expression to get meaningful results: even something like `_journal'
+ will work to include all files that have a `journal' keyword.
+
+ • The `:not-regexp' parameter is optional. It is a regular expression
+ that applies after `:regexp' to filter out the matching files.
+
+ • The `:excluded-dirs-regexp' is a string that contains a word or
+ regular expression that matches against directory files names
+ to-be-excluded from the results. This has the same meaning as
+ setting the `denote-excluded-directories-regexp' user option (which
+ is part of the main `denote' package)). The user option has a global
+ effect, which is overridden locally in the dynamic block. When the
+ value of `:excluded-dirs-regexp' is nil (the default), the value of
+ `denote-excluded-directories-regexp' is used (which is also nil by
+ default, meaning that all directories are included). When the value
+ of `excluded-dirs-regexp' is `t' or some other symbol, then the
+ `denote-excluded-directories-regexp' is ignored altogether. This is
+ useful in the scenario where the user option is set to exclude some
+ directories but the dynamic blocks wants to lift that restriction.
+
+ • The `:sort-by-component' parameter is optional. It sorts the files
+ by the given Denote file name component. The value it accepts is an
+ unquoted symbol among `title', `keywords', `signature',
+ `identifier'. When using the command
+ `denote-org-dblock-insert-files', this parameter is automatically
+ inserted together with the (`:regexp' parameter) and the user is
+ prompted for a file name component.
+
+ • The `:reverse-sort' parameter is optional. It reverses the order in
+ which files appear in. This is meaningful even without the presence
+ of the parameter `:sort-by-component', though it also combines with
+ it.
+
+ • The `:add-links' parameter is optional. When it is set to a `t'
+ value, all the top-level headings (those that were the `#+title' of
+ each file) are generated as links, pointing to the original file.
+ This has the same meaning as with the `denote-link' command and
+ related facilities (those are explained at length in the Denote
+ manual).
+
+ • An optional `:block-name' parameter can be specified with a string
+ value to add a `#+name' to the results. This is useful for further
+ processing using Org facilities (a feature that is outside Denote’s
+ purview).
+
+
+[Org dynamic block to insert file contents] See section 3.4
+
+[Org dynamic blocks to insert links or backlinks] See section 3.1
+
+
+4 Create a note from the current Org subtree
+════════════════════════════════════════════
+
+ In Org parlance, an entry with all its subheadings and other contents
+ is a “subtree”. Denote can operate on the subtree to extract it from
+ the current file and create a new file out of it. One such workflow is
+ to collect thoughts in a single document and produce longer standalone
+ notes out of them upon review.
+
+ The command `denote-org-extract-org-subtree' is used for this
+ purpose. It creates a new Denote note using the current Org subtree.
+ In doing so, it removes the subtree from its current file and moves
+ its contents into a new file. This command is part of the optional
+ `denote-org.el' extension, which is part of the `denote' package. It
+ is loaded automatically as soon as one of its commands is invoked.
+
+ The text of the subtree’s heading becomes the `#+title' of the new
+ note. Everything else is inserted as-is.
+
+ If the heading has any tags, they are used as the keywords of the new
+ note. If the Org file has any `#+filetags' they are taken as well
+ (Org’s `#+filetags' are inherited by the headings). If none of these
+ are true and the user option `denote-prompts' includes an entry for
+ keywords, then `denote-org-extract-org-subtree' prompts for
+ keywords. Else the new note has no keywords.
+
+ If the heading has a `PROPERTIES' drawer, it is retained for further
+ review.
+
+ If the heading’s `PROPERTIES' drawer includes a `DATE' or `CREATED'
+ property, or there exists a `CLOSED' statement with a timestamp value,
+ use that to derive the date (or date and time) of the new note (if
+ there is only a date, the time is taken as 00:00). If more than one of
+ these is present, the order of preference is `DATE', then `CREATED',
+ then `CLOSED'. If none of these is present, the current time is used.
+ If the `denote-prompts' includes an entry for a date, then the command
+ prompts for a date at this stage (also see
+ `denote-date-prompt-use-org-read-date').
+
+ For the rest, it consults the value of the user option
+ `denote-prompts' in the following scenaria:
+
+ • To optionally prompt for a subdirectory, otherwise it produces the
+ new note in the `denote-directory'.
+ • To optionally prompt for a file signature, otherwise to not use any.
+
+ The new note is an Org file regardless of the user option
+ `denote-file-type'.
+
+
+5 Insert link to an Org file with a further pointer to a heading
+════════════════════════════════════════════════════════════════
+
+ As part of the optional `denote-org.el' extension, the command
+ `denote-org-link-to-heading' prompts for a link to an Org file and
+ then asks for a heading therein, using minibuffer completion. Once the
+ user provides input at the two prompts, the command inserts a link at
+ point which has the following pattern:
+ `[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading
+ text]]'.
+
+ Because only Org files can have links to individual headings, the
+ command `denote-org-link-to-heading' prompts only for Org files
+ (i.e. files which include the `.org' extension). Remember that Denote
+ works with many file types (read the Denote manual’s section about the
+ file-naming scheme).
+
+ This feature is similar to the concept of the user option
+ `denote-org-store-link-to-heading' (which is part of the main `denote'
+ package). It is, however, interactive and differs in the
+ directionality of the action. With that user option, the command
+ `org-store-link' will generate a `CUSTOM_ID' for the current heading
+ (or capture the value of one as-is), giving the user the option to
+ then call `org-insert-link' wherever they see fit. By contrast, the
+ command `denote-org-link-to-heading' prompts for a file, then a
+ heading, and inserts the link at point.
+
+ Just as with files, it is possible to show backlinks for the given
+ heading ([Backlinks for Org headings]).
+
+
+[Backlinks for Org headings] See section 5.1
+
+5.1 Backlinks for Org headings
+──────────────────────────────
+
+ The optional `denote-org.el' can generate Denote links to individual
+ headings ([Insert link to an Org file with a further pointer to a
+ heading]). It is then possible to produce a corresponding backlinks
+ buffer with the command `denote-org-backlinks-for-heading'. The
+ resulting buffer behaves the same way as the standard backlinks buffer
+ we provide (read the Denote manual’s section about the backlinks
+ buffer). An Org dynamic block with backlinks to the current heading
+ is also an option ([Org dynamic blocks to insert links or backlinks]).
+
+
+[Insert link to an Org file with a further pointer to a heading] See
+section 5
+
+[Org dynamic blocks to insert links or backlinks] See section 3.1
+
+
+6 Convert `denote:' links to `file:' links in Org and vice versa
+════════════════════════════════════════════════════════════════
+
+ Sometimes the user needs to translate all `denote:' link types to
+ their `file:' equivalent. This may be because some other tool does not
+ recognise `denote:' links (or other custom links types—which are a
+ standard feature of Org, by the way). The user thus needs to (i)
+ either make a copy of their Denote note or edit the existing one, and
+ (ii) convert all links to the generic `file:' link type that
+ external/other programs understand.
+
+ The optional extension `denote-org.el' contains two commands that are
+ relevant for this use-case:
+
+ Convert `denote:' links to `file:' links
+ The command `denote-org-convert-links-to-file-type' goes through
+ the buffer to find all `denote:' links. It gets the identifier
+ of the link and resolves it to the actual file system path. It
+ then replaces the match so that the link is written with the
+ `file:' type and then the file system path. The optional search
+ terms and/or link description are preserved ([Insert link to an
+ Org file with a further pointer to a heading]).
+
+ Convert `file:' links to `denote:' links
+ The command `denote-org-convert-links-to-denote-type' behaves
+ like the one above. The difference is that it finds the file
+ system path and converts it into its identifier.
+
+
+[Insert link to an Org file with a further pointer to a heading] See
+section 5
+
+
+7 Installation
+══════════════
+
+
+
+
+7.1 GNU ELPA package
+────────────────────
+
+ The package is available as `denote-org'. Simply do:
+
+ ┌────
+ │ M-x package-refresh-contents
+ │ M-x package-install
+ └────
+
+
+ And search for it.
+
+ GNU ELPA provides the latest stable release. Those who prefer to
+ follow the development process in order to report bugs or suggest
+ changes, can use the version of the package from the GNU-devel ELPA
+ archive. Read:
+ <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>.
+
+
+7.2 Manual installation
+───────────────────────
+
+ Assuming your Emacs files are found in `~/.emacs.d/', execute the
+ following commands in a shell prompt:
+
+ ┌────
+ │ cd ~/.emacs.d
+ │
+ │ # Create a directory for manually-installed packages
+ │ mkdir manual-packages
+ │
+ │ # Go to the new directory
+ │ cd manual-packages
+ │
+ │ # Clone this repo, naming it "denote-org"
+ │ git clone https://github.com/protesilaos/denote-org denote-org
+ └────
+
+ Finally, in your `init.el' (or equivalent) evaluate this:
+
+ ┌────
+ │ ;; Make Elisp files in that directory available to the user.
+ │ (add-to-list 'load-path "~/.emacs.d/manual-packages/denote-org")
+ └────
+
+ Everything is in place to set up the package.
+
+
+8 Sample configuration
+══════════════════════
+
+ ┌────
+ │ (use-package denote-org
+ │ :ensure t
+ │ :commands
+ │ ;; I list the commands here so that you can discover them more
+ │ ;; easily. You might want to bind the most frequently used ones to
+ │ ;; the `org-mode-map'.
+ │ ( denote-org-link-to-heading
+ │ denote-org-backlinks-for-heading
+ │
+ │ denote-org-extract-org-subtree
+ │
+ │ denote-org-convert-links-to-file-type
+ │ denote-org-convert-links-to-denote-type
+ │
+ │ denote-org-dblock-insert-files
+ │ denote-org-dblock-insert-links
+ │ denote-org-dblock-insert-backlinks
+ │ denote-org-dblock-insert-missing-links
+ │ denote-org-dblock-insert-files-as-headings))
+ └────
+
+
+9 Acknowledgements
+══════════════════
+
+ Denote Org is meant to be a collective effort. Every bit of help
+ matters.
+
+ Author/maintainer
+ Protesilaos Stavrou.
+
+
+10 GNU Free Documentation License
+═════════════════════════════════
+
+
+11 Indices
+══════════
+
+11.1 Function index
+───────────────────
+
+
+11.2 Variable index
+───────────────────
+
+
+11.3 Concept index
+──────────────────
blob - /dev/null
blob + 3bbde61a60b27bcd655963f17f77f0eb2a675032 (mode 644)
--- /dev/null
+++ elpa/denote-org-0.1.1/README.md
+# denote-org: Extras to integrate Org mode with Denote (like Org dynamic blocks)
+
+The `denote-org` package contains extra features that better integrate
+Denote with Org mode. These used to be available as part of the main
+`denote` package in a file called `denote-org-extras.el`, but now live
+in this standalone package to main things easier to maintain and
+understand.
+
+With `denote-org`, users have Org-specific extensions such as dynamic
+blocks, links to headings, and splitting an Org subtree into its own
+standalone file. This package's official manual covers the
+technicalities.
+
++ Package name (GNU ELPA): `denote-org`
++ Official manual: <https://protesilaos.com/emacs/denote-org>
++ Git repository: <https://github.com/protesilaos/denote-org>
++ Backronym: Denote... Ordinarily Restricts Gyrations.
blob - /dev/null
blob + c43f9b8cc549eb3ebefb559193fd172721c4fae0 (mode 644)
--- /dev/null
+++ elpa/denote-org-0.1.1/README.org
+#+title: denote-org: Extensions to better integrate Org with Denote
+#+author: Protesilaos Stavrou
+#+email: info@protesilaos.com
+#+language: en
+#+options: ':t toc:nil author:t email:t num:t
+#+startup: content
+#+macro: stable-version 0.1.0
+#+macro: release-date 2025-04-15
+#+macro: development-version 0.2.0-dev
+#+export_file_name: denote-org.texi
+#+texinfo_filename: denote-org.info
+#+texinfo_dir_category: Emacs misc features
+#+texinfo_dir_title: Denote Org: (denote-org)
+#+texinfo_dir_desc: Extensions to better integrate Org with Denote
+#+texinfo_header: @set MAINTAINERSITE @uref{https://protesilaos.com,maintainer webpage}
+#+texinfo_header: @set MAINTAINER Protesilaos Stavrou
+#+texinfo_header: @set MAINTAINEREMAIL @email{info@protesilaos.com}
+#+texinfo_header: @set MAINTAINERCONTACT @uref{mailto:info@protesilaos.com,contact the maintainer}
+
+#+texinfo: @insertcopying
+
+This manual, written by Protesilaos Stavrou, describes the customization
+options for the Emacs package called ~denote~ (or =denote.el=), and
+provides every other piece of information pertinent to it.
+
+The documentation furnished herein corresponds to stable version
+{{{stable-version}}}, released on {{{release-date}}}. Any reference to
+a newer feature which does not yet form part of the latest tagged
+commit, is explicitly marked as such.
+
+Current development target is {{{development-version}}}.
+
++ Package name (GNU ELPA): ~denote-org~
++ Official manual: <https://protesilaos.com/emacs/denote-org>
++ Git repository: <https://github.com/protesilaos/denote-org>
++ Backronym: Denote... Ordinarily Restricts Gyrations.
+
+If you are viewing the README.org version of this file, please note that
+the GNU ELPA machinery automatically generates an Info manual out of it.
+
+#+toc: headlines 8 insert TOC here, with eight headline levels
+
+* COPYING
+:PROPERTIES:
+:COPYING: t
+:CUSTOM_ID: h:copying
+:END:
+
+Copyright (C) 2022-2025 Free Software Foundation, Inc.
+
+#+begin_quote
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.3 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, with the Front-Cover Texts being “A GNU Manual,” and
+with the Back-Cover Texts as in (a) below. A copy of the license is
+included in the section entitled “GNU Free Documentation License.”
+
+(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and
+modify this GNU manual.”
+#+end_quote
+
+* Overview
+:PROPERTIES:
+:CUSTOM_ID: h:6157c29a-31e6-4ba7-b114-57f94a21d460
+:END:
+
+The ~denote-org~ package contains extra features that better integrate
+Denote with Org mode. These used to be available as part of the main
+~denote~ package in a file called =denote-org-extras.el=, but now live
+in this standalone package to main things easier to maintain and
+understand.
+
+With ~denote-org~, users have Org-specific extensions such as dynamic
+blocks, links to headings, and splitting an Org subtree into its own
+standalone file. The following sections cover the technicalities.
+
+* Use Org dynamic blocks
+:PROPERTIES:
+:CUSTOM_ID: h:8b542c50-dcc9-4bca-8037-a36599b22779
+:END:
+
+Denote can optionally integrate with Org mode's "dynamic blocks"
+facility. This means that it can use special blocks that are evaluated
+with =C-c C-x C-u= (~org-dblock-update~) to generate their contents.
+The following subsections describe the types of Org dynamic blocks
+provided by Denote.
+
+- [[#h:50160fae-6515-4d7d-9737-995ad925e64b][Org dynamic blocks to insert links or backlinks]]
+- [[#h:f15fa143-5036-416f-9bff-1bcabbb03456][Org dynamic block to insert file contents]]
+
+A dynamic block gets its contents by evaluating a function that
+corresponds to the type of block. The block type and its parameters
+are stated in the opening =#+BEGIN= line. Typing =C-c C-x C-u=
+(~org-dblock-update~) with point on that line runs (or re-runs) the
+associated function with the given parameters and populates the
+block's contents accordingly.
+
+Dynamic blocks are particularly useful for metanote entries that
+reflect on the status of earlier notes (read the Denote manual's
+section about writing metanotes).
+
+The Org manual describes the technicalities of Dynamic Blocks.
+Evaluate:
+
+#+begin_src emacs-lisp
+(info "(org) Dynamic Blocks")
+#+end_src
+
+** Org dynamic blocks to insert links
+:PROPERTIES:
+:CUSTOM_ID: h:50160fae-6515-4d7d-9737-995ad925e64b
+:END:
+
+#+findex: denote-org-dblock-insert-links
+The =denote-links= block can be inserted at point with the command
+~denote-org-dblock-insert-links~ or by manually including the
+following in an Org file:
+
+: #+BEGIN: denote-links :regexp "YOUR REGEXP HERE" :not-regexp :excluded-dirs-regexp nil :sort-by-component nil :reverse-sort nil :id-only nil :include-date nil
+:
+: #+END:
+
+All the parameters except for =:regexp= are optional.
+
+The =denote-links= block is also registered as an option for the
+command ~org-dynamic-block-insert-dblock~.
+
+Type =C-c C-x C-u= (~org-dblock-update~) with point on the =#+BEGIN=
+line to update the block.
+
+- The =:regexp= parameter is mandatory. Its value is a string and its
+ behaviour is the same as that of the standard ~denote-add-links~
+ command (part of the main ~denote~ package). Concretely, it produces
+ a typographic list of links to files matching the giving regular
+ expression. The value of the =:regexp= parameter may also be of the
+ form read by the ~rx~ macro (Lisp notation instead of a string), as
+ explained in the Emacs Lisp Reference Manual (evaluate this code to
+ read the documentation: =(info "(elisp) Rx Notation")=). Note that
+ you do not need to write an actual regular expression to get
+ meaningful results: even something like =_journal= will work to
+ include all files that have a =journal= keyword.
+
+- The =:not-regexp= parameter is optional. It is a regular expression
+ that applies after =:regexp= to filter out the matching files.
+
+- The =:excluded-dirs-regexp= is a string that contains a word or
+ regular expression that matches against directory files names
+ to-be-excluded from the results. This has the same meaning as
+ setting the ~denote-excluded-directories-regexp~ user option (which
+ is part of the main ~denote~ package). The user option has a global
+ effect, which is overridden locally in the dynamic block. When the
+ value of =:excluded-dirs-regexp= is nil (the default), the value of
+ ~denote-excluded-directories-regexp~ is used (which is also nil by
+ default, meaning that all directories are included). When the value
+ of =excluded-dirs-regexp= is ~t~ or some other symbol, then the
+ ~denote-excluded-directories-regexp~ is ignored altogether. This is
+ useful in the scenario where the user option is set to exclude some
+ directories but the dynamic blocks wants to lift that restriction.
+
+- The =:sort-by-component= parameter is optional. It sorts the files
+ by the given Denote file name component. The value it accepts is an
+ unquoted symbol among =title=, =keywords=, =signature=, =identifier=.
+ When using the command ~denote-org-dblock-insert-files~, this
+ parameter is automatically inserted together with the (=:regexp=
+ parameter) and the user is prompted for a file name component.
+
+- The =:reverse-sort= parameter is optional. It reverses the order in
+ which files appear in. This is meaningful even without the presence
+ of the parameter =:sort-by-component=, though it also combines with
+ it.
+
+- The =:id-only= parameter is optional. It accepts a ~t~ value, in
+ which case links are inserted without a description text but only
+ with the identifier of the given file. This has the same meaning as
+ with the ~denote-link~ command and related facilities (read the
+ Denote manual's section about linking to other files in the
+ ~denote-directory~).
+
+- The =:include-date= parameter controls whether to display the date
+ of the file name after the title. This is done when its value is
+ ~t~. By default (a nil value), no date is shown.
+
+- An optional =:block-name= parameter can be specified with a string
+ value to add a =#+name= to the results. This is useful for further
+ processing using Org facilities (a feature that is outside Denote's
+ purview).
+
+In some workflows, users may want to have a separate block to see what
+other links they are missing since they last updated the dynamic
+block. We cover that case as well ([[#h:1a81e255-0510-4ee0-bc3a-374de048ef46][The Org dynamic block to insert missing links only]]).
+
+** The Org dynamic block to insert missing links only
+:PROPERTIES:
+:CUSTOM_ID: h:1a81e255-0510-4ee0-bc3a-374de048ef46
+:END:
+
+#+findex: denote-org-dblock-insert-missing-links
+The =denote-missing-links= block is available with the command
+~denote-org-dblock-insert-missing-links~. It is like the
+aforementioned =denote-links= block, except it only lists links to
+files that are not present in the current buffer ([[#h:50160fae-6515-4d7d-9737-995ad925e64b][Org dynamic blocks to insert links]]).
+The parameters are otherwise the same and are all optional except for
+=:regexp=:
+
+: #+BEGIN: denote-missing-links :regexp "YOUR REGEXP HERE" :excluded-dirs-regexp nil :sort-by-component nil :reverse-sort nil :id-only nil :include-date nil
+:
+: #+END:
+
+The =denote-missing-links= block is also registered as an option for the
+command ~org-dynamic-block-insert-dblock~.
+
+Remember to type =C-c C-x C-u= (~org-dblock-update~) with point on the
+=#+BEGIN= line to update the block.
+
+** The Org dynamic block to insert backlinks
+:PROPERTIES:
+:CUSTOM_ID: h:f9a97859-1deb-47dd-bdae-52f8b424ff46
+:END:
+
+#+findex: denote-org-dblock-insert-backlinks
+Apart from links to files matching a regular expression, we can also
+produce a list of backlinks to the current file. The dynamic block can
+be inserted at point with the command ~denote-org-dblock-insert-backlinks~
+or by manually writing this in an Org file:
+
+: #+BEGIN: denote-backlinks :excluded-dirs-regexp nil :sort-by-component nil :reverse-sort nil :id-only nil :this-heading-only nil :include-date nil
+:
+: #+END:
+
+The =denote-backlinks= block is also registered as an option for the
+command ~org-dynamic-block-insert-dblock~.
+
+Remember to type =C-c C-x C-u= (~org-dblock-update~) with point on the
+=#+BEGIN= line to update the block.
+
+The parameters recognised by this dynamic block are almost the same as
+that for inserting links ([[#h:50160fae-6515-4d7d-9737-995ad925e64b][Org dynamic blocks to insert links]]). They
+are all optional in this case and there is no parameter expecting a
+regular expression for matching files to link to.
+
+Additionally, the ~denote-backlinks~ block also recognises the
+=:this-heading-only= parameter. It determines if the backlinks are
+about the file or the heading under which the dynamic block is inserted
+([[#h:604bf92a-908a-485c-98b8-37ccae559afd][Backlinks for Org headings]]). When this parameter is omitted or nil
+(the default), then the backlinks are about the whole file, but if
+this parameter has a ~t~ value then the backlinks are specifically for
+the heading ([[#h:fc1ad245-ec08-41be-8d1e-7153d99daf02][Insert link to an Org file with a further pointer to a heading]]).
+
+** Org dynamic block to insert file contents
+:PROPERTIES:
+:CUSTOM_ID: h:f15fa143-5036-416f-9bff-1bcabbb03456
+:END:
+
+Denote can optionally use Org's dynamic blocks facility to produce a
+section that lists entire file contents ([[#h:8b542c50-dcc9-4bca-8037-a36599b22779][Use Org dynamic blocks]]).
+This works by instructing Org to match a regular expression of Denote
+files, the same way we do with Denote links (read the Denote manual's
+section about inserting links that match a regular expression).
+
+This is useful to, for example, compile a dynamically concatenated
+list of scattered thoughts on a given topic, like =^2023.*_emacs= for
+a long entry that incorporates all the notes written in 2023 with the
+keyword =emacs=.
+
+#+findex: denote-org-dblock-insert-files
+To produce such a block, call the command ~denote-org-dblock-insert-files~
+or manually write the following block in an Org file and then type
+ =C-c C-x C-u= (~org-dblock-update~) on the =#+BEGIN= line to run it
+(do it again to recalculate the block):
+
+: #+BEGIN: denote-files :regexp "YOUR REGEXP HERE" :not-regexp nil :sort-by-component nil :reverse-sort nil :no-front-matter nil :file-separator nil :add-links nil
+:
+: #+END:
+
+All parameters are optional except for =:regexp=.
+
+The =denote-files= block is also registered as an option for the
+command ~org-dynamic-block-insert-dblock~.
+
+Remember to type =C-c C-x C-u= (~org-dblock-update~) with point on the
+=#+BEGIN= line to update the block.
+
+To fully control the output, include these additional optional
+parameters, which are described further below:
+
+- The =:regexp= parameter is mandatory. Its value is a string,
+ representing a regular expression to match Denote file names. Its
+ value may also be an ~rx~ expression instead of a string, as noted
+ in the previous section ([[#h:50160fae-6515-4d7d-9737-995ad925e64b][Org dynamic blocks to insert links or backlinks]]).
+ Note that you do not need to write an actual regular expression to
+ get meaningful results: even something like =_journal= will work to
+ include all files that have a =journal= keyword.
+
+- The =:not-regexp= parameter is optional. It is a regular expression
+ that applies after =:regexp= to filter out the matching files.
+
+- The =:excluded-dirs-regexp= is a string that contains a word or
+ regular expression that matches against directory files names
+ to-be-excluded from the results. This has the same meaning as
+ setting the ~denote-excluded-directories-regexp~ user option (which
+ is part of the main ~denote~ package). The user option has a global
+ effect, which is overridden locally in the dynamic block. When the
+ value of =:excluded-dirs-regexp= is nil (the default), the value of
+ ~denote-excluded-directories-regexp~ is used (which is also nil by
+ default, meaning that all directories are included). When the value
+ of =excluded-dirs-regexp= is ~t~ or some other symbol, then the
+ ~denote-excluded-directories-regexp~ is ignored altogether. This is
+ useful in the scenario where the user option is set to exclude some
+ directories but the dynamic blocks wants to lift that restriction.
+
+- The =:sort-by-component= parameter is optional. It sorts the files
+ by the given Denote file name component. The value it accepts is an
+ unquoted symbol among =title=, =keywords=, =signature=, =identifier=.
+ When using the command ~denote-org-dblock-insert-files~, this
+ parameter is automatically inserted together with the (=:regexp=
+ parameter) and the user is prompted for a file name component.
+
+- The =:reverse-sort= parameter is optional. It reverses the order in
+ which files appear in. This is meaningful even without the presence
+ of the parameter =:sort-by-component=, though it also combines with
+ it.
+
+#+vindex: denote-org-dblock-file-contents-separator
+- The =:file-separator= parameter is optional. If it is omitted, then
+ Denote will use no separator between the files it inserts. If the
+ value is ~t~ the ~denote-org-dblock-file-contents-separator~ is
+ applied at the end of each file: it introduces some empty lines and
+ a horizontal rule between them to visually distinguish individual
+ files. If the =:file-separator= value is a string, it is used as the
+ file separator (e.g. use ="\n"= to insert just one empty new line).
+
+- The =:no-front-matter= parameter is optional. When set to a ~t~
+ value, Denote tries to remove front matter from the files it is
+ inserting in the dynamic block. The technique used to perform this
+ operation is by removing all lines from the top of the file until
+ the first empty line. This works with the default front matter that
+ Denote adds, but is not 100% reliable with all sorts of user-level
+ modifications and edits to the file. When the =:no-front-matter= is
+ set to a natural number, Denote will omit that many lines from the
+ top of the file.
+
+- The =:add-links= parameter is optional. When it is set to a ~t~
+ value, all files are inserted as a typographic list and are indented
+ accordingly. The first line in each list item is a link to the file
+ whose contents are inserted in the following lines. When the value
+ is =id-only=, then links are inserted without a description text but
+ only with the identifier of the given file. This has the same
+ meaning as with the ~denote-link~ command and related facilities
+ (those are explained at length in the Denote manual). Remember that
+ Org can fold the items in a typographic list the same way it does
+ with headings. So even long files can be presented in this format
+ without much trouble.
+
+- An optional =:block-name= parameter can be specified with a string
+ value to add a =#+name= to the results. This is useful for further
+ processing using Org facilities (a feature that is outside Denote's
+ purview).
+
+** Org dynamic block to insert Org files as headings
+:PROPERTIES:
+:CUSTOM_ID: h:d6254a12-b762-4096-a5de-66a0d423e204
+:END:
+
+[ IMPORTANT NOTE: This dynamic block only works with Org files,
+ because it has to assume the Org notation in order to insert each
+ file's contents as its own heading. ]
+
+#+findex: denote-org-dblock-insert-files-as-headings
+As a variation of the previously covered block that inserts file
+contents, we have the ~denote-org-dblock-insert-files-as-headings~
+command ([[#h:f15fa143-5036-416f-9bff-1bcabbb03456][Org dynamic block to insert file contents]]). It Turn the
+=#+title= of each file into a top-level heading. Then it increments
+all original headings in the file by one, so that they become
+subheadings of what once was the =#+title=. Similarly, the
+=#+filetags= of each file as tags for the top-level heading
+(what was the =#+title=).
+
+Because of how it is meant to work, this dynamic block only works with
+Org files.
+
+In its simplest form, this dynamic block looks like this, with
+=:regexp= as the only mandatory parameter:
+
+: #+BEGIN: denote-files-as-headings :regexp "YOUR REGEXP HERE"
+:
+: #+END:
+
+Though when you use the command ~denote-org-dblock-insert-files-as-headings~
+you get all the parameters included:
+
+: #+BEGIN: denote-files-as-headings :regexp "YOUR REGEXP HERE" :not-regexp nil :excluded-dirs-regexp nil :sort-by-component title :reverse-sort nil :add-links t
+:
+: #+END:
+
+- The =:regexp= parameter is mandatory. Its value is a string,
+ representing a regular expression to match Denote file names. Its
+ value may also be an ~rx~ expression instead of a string, as noted
+ in the previous section ([[#h:50160fae-6515-4d7d-9737-995ad925e64b][Org dynamic blocks to insert links or backlinks]]).
+ Note that you do not need to write an actual regular expression to
+ get meaningful results: even something like =_journal= will work to
+ include all files that have a =journal= keyword.
+
+- The =:not-regexp= parameter is optional. It is a regular expression
+ that applies after =:regexp= to filter out the matching files.
+
+- The =:excluded-dirs-regexp= is a string that contains a word or
+ regular expression that matches against directory files names
+ to-be-excluded from the results. This has the same meaning as
+ setting the ~denote-excluded-directories-regexp~ user option (which
+ is part of the main ~denote~ package)). The user option has a global
+ effect, which is overridden locally in the dynamic block. When the
+ value of =:excluded-dirs-regexp= is nil (the default), the value of
+ ~denote-excluded-directories-regexp~ is used (which is also nil by
+ default, meaning that all directories are included). When the value
+ of =excluded-dirs-regexp= is ~t~ or some other symbol, then the
+ ~denote-excluded-directories-regexp~ is ignored altogether. This is
+ useful in the scenario where the user option is set to exclude some
+ directories but the dynamic blocks wants to lift that restriction.
+
+- The =:sort-by-component= parameter is optional. It sorts the files
+ by the given Denote file name component. The value it accepts is an
+ unquoted symbol among =title=, =keywords=, =signature=, =identifier=.
+ When using the command ~denote-org-dblock-insert-files~, this
+ parameter is automatically inserted together with the (=:regexp=
+ parameter) and the user is prompted for a file name component.
+
+- The =:reverse-sort= parameter is optional. It reverses the order in
+ which files appear in. This is meaningful even without the presence
+ of the parameter =:sort-by-component=, though it also combines with
+ it.
+
+- The =:add-links= parameter is optional. When it is set to a ~t~
+ value, all the top-level headings (those that were the =#+title= of
+ each file) are generated as links, pointing to the original file.
+ This has the same meaning as with the ~denote-link~ command and
+ related facilities (those are explained at length in the Denote manual).
+
+- An optional =:block-name= parameter can be specified with a string
+ value to add a =#+name= to the results. This is useful for further
+ processing using Org facilities (a feature that is outside Denote's
+ purview).
+
+* Create a note from the current Org subtree
+:PROPERTIES:
+:CUSTOM_ID: h:d0c7cb79-21e5-4176-a6af-f4f68578c8dd
+:END:
+
+In Org parlance, an entry with all its subheadings and other contents
+is a "subtree". Denote can operate on the subtree to extract it from
+the current file and create a new file out of it. One such workflow is
+to collect thoughts in a single document and produce longer standalone
+notes out of them upon review.
+
+#+findex: denote-org-extract-org-subtree
+The command ~denote-org-extract-org-subtree~ is used for this
+purpose. It creates a new Denote note using the current Org subtree.
+In doing so, it removes the subtree from its current file and moves
+its contents into a new file. This command is part of the optional
+=denote-org.el= extension, which is part of the ~denote~
+package. It is loaded automatically as soon as one of its commands is
+invoked.
+
+The text of the subtree's heading becomes the =#+title= of the new
+note. Everything else is inserted as-is.
+
+If the heading has any tags, they are used as the keywords of the new
+note. If the Org file has any =#+filetags= they are taken as well
+(Org's =#+filetags= are inherited by the headings). If none of these
+are true and the user option ~denote-prompts~ includes an entry for
+keywords, then ~denote-org-extract-org-subtree~ prompts for
+keywords. Else the new note has no keywords.
+
+If the heading has a =PROPERTIES= drawer, it is retained for further
+review.
+
+If the heading's =PROPERTIES= drawer includes a =DATE= or =CREATED=
+property, or there exists a =CLOSED= statement with a timestamp value,
+use that to derive the date (or date and time) of the new note (if
+there is only a date, the time is taken as 00:00). If more than one of
+these is present, the order of preference is =DATE=, then =CREATED=,
+then =CLOSED=. If none of these is present, the current time is used.
+If the ~denote-prompts~ includes an entry for a date, then the command
+prompts for a date at this stage (also see ~denote-date-prompt-use-org-read-date~).
+
+For the rest, it consults the value of the user option
+~denote-prompts~ in the following scenaria:
+
+- To optionally prompt for a subdirectory, otherwise it produces the
+ new note in the ~denote-directory~.
+- To optionally prompt for a file signature, otherwise to not use any.
+
+The new note is an Org file regardless of the user option
+~denote-file-type~.
+
+* Insert link to an Org file with a further pointer to a heading
+:PROPERTIES:
+:CUSTOM_ID: h:fc1ad245-ec08-41be-8d1e-7153d99daf02
+:END:
+
+#+findex: denote-org-link-to-heading
+As part of the optional =denote-org.el= extension, the command
+~denote-org-link-to-heading~ prompts for a link to an Org file
+and then asks for a heading therein, using minibuffer completion. Once
+the user provides input at the two prompts, the command inserts a link
+at point which has the following pattern: =[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading text]]=.
+
+Because only Org files can have links to individual headings, the
+command ~denote-org-link-to-heading~ prompts only for Org files
+(i.e. files which include the =.org= extension). Remember that Denote
+works with many file types (read the Denote manual's section about the
+file-naming scheme).
+
+This feature is similar to the concept of the user option
+~denote-org-store-link-to-heading~ (which is part of the main ~denote~
+package). It is, however, interactive and differs in the
+directionality of the action. With that user option, the command
+~org-store-link~ will generate a =CUSTOM_ID= for the current heading
+(or capture the value of one as-is), giving the user the option to
+then call ~org-insert-link~ wherever they see fit. By contrast, the
+command ~denote-org-link-to-heading~ prompts for a file, then a
+heading, and inserts the link at point.
+
+Just as with files, it is possible to show backlinks for the given
+heading ([[#h:604bf92a-908a-485c-98b8-37ccae559afd][Backlinks for Org headings]]).
+
+** Backlinks for Org headings
+:PROPERTIES:
+:CUSTOM_ID: h:604bf92a-908a-485c-98b8-37ccae559afd
+:END:
+
+The optional =denote-org.el= can generate Denote links to
+individual headings ([[#h:fc1ad245-ec08-41be-8d1e-7153d99daf02][Insert link to an Org file with a further pointer to a heading]]).
+It is then possible to produce a corresponding backlinks buffer with
+the command ~denote-org-backlinks-for-heading~. The resulting
+buffer behaves the same way as the standard backlinks buffer we
+provide (read the Denote manual's section about the backlinks buffer).
+An Org dynamic block with backlinks to the current heading is also an
+option ([[#h:50160fae-6515-4d7d-9737-995ad925e64b][Org dynamic blocks to insert links or backlinks]]).
+
+* Convert =denote:= links to =file:= links in Org and vice versa
+:PROPERTIES:
+:CUSTOM_ID: h:ed220cac-7dcb-4bb7-9243-1bb85e452e5f
+:END:
+
+Sometimes the user needs to translate all =denote:= link types to
+their =file:= equivalent. This may be because some other tool does not
+recognise =denote:= links (or other custom links types---which are a
+standard feature of Org, by the way). The user thus needs to (i)
+either make a copy of their Denote note or edit the existing one, and
+(ii) convert all links to the generic =file:= link type that
+external/other programs understand.
+
+The optional extension =denote-org.el= contains two commands
+that are relevant for this use-case:
+
+#+findex: denote-org-convert-links-to-file-type
++ Convert =denote:= links to =file:= links :: The command
+ ~denote-org-convert-links-to-file-type~ goes through the
+ buffer to find all =denote:= links. It gets the identifier of the
+ link and resolves it to the actual file system path. It then
+ replaces the match so that the link is written with the =file:= type
+ and then the file system path. The optional search terms and/or link
+ description are preserved ([[#h:fc1ad245-ec08-41be-8d1e-7153d99daf02][Insert link to an Org file with a further pointer to a heading]]).
+
+#+findex: denote-org-convert-links-to-denote-type
++ Convert =file:= links to =denote:= links :: The command
+ ~denote-org-convert-links-to-denote-type~ behaves like the
+ one above. The difference is that it finds the file system path and
+ converts it into its identifier.
+
+* Installation
+:PROPERTIES:
+:CUSTOM_ID: h:installation
+:END:
+#+cindex: Installation instructions
+
+** GNU ELPA package
+:PROPERTIES:
+:CUSTOM_ID: h:gnu-elpa-package
+:END:
+
+The package is available as =denote-org=. Simply do:
+
+: M-x package-refresh-contents
+: M-x package-install
+
+And search for it.
+
+GNU ELPA provides the latest stable release. Those who prefer to follow
+the development process in order to report bugs or suggest changes, can
+use the version of the package from the GNU-devel ELPA archive. Read:
+https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/.
+
+** Manual installation
+:PROPERTIES:
+:CUSTOM_ID: h:manual-installation
+:END:
+
+Assuming your Emacs files are found in =~/.emacs.d/=, execute the
+following commands in a shell prompt:
+
+#+begin_src sh
+cd ~/.emacs.d
+
+# Create a directory for manually-installed packages
+mkdir manual-packages
+
+# Go to the new directory
+cd manual-packages
+
+# Clone this repo, naming it "denote-org"
+git clone https://github.com/protesilaos/denote-org denote-org
+#+end_src
+
+Finally, in your =init.el= (or equivalent) evaluate this:
+
+#+begin_src emacs-lisp
+;; Make Elisp files in that directory available to the user.
+(add-to-list 'load-path "~/.emacs.d/manual-packages/denote-org")
+#+end_src
+
+Everything is in place to set up the package.
+
+* Sample configuration
+:PROPERTIES:
+:CUSTOM_ID: h:sample-configuration
+:END:
+#+cindex: Package configuration
+
+#+begin_src emacs-lisp
+(use-package denote-org
+ :ensure t
+ :commands
+ ;; I list the commands here so that you can discover them more
+ ;; easily. You might want to bind the most frequently used ones to
+ ;; the `org-mode-map'.
+ ( denote-org-link-to-heading
+ denote-org-backlinks-for-heading
+
+ denote-org-extract-org-subtree
+
+ denote-org-convert-links-to-file-type
+ denote-org-convert-links-to-denote-type
+
+ denote-org-dblock-insert-files
+ denote-org-dblock-insert-links
+ denote-org-dblock-insert-backlinks
+ denote-org-dblock-insert-missing-links
+ denote-org-dblock-insert-files-as-headings))
+#+end_src
+
+* Acknowledgements
+:PROPERTIES:
+:CUSTOM_ID: h:acknowledgements
+:END:
+#+cindex: Contributors
+
+Denote Org is meant to be a collective effort. Every bit of help matters.
+
++ Author/maintainer :: Protesilaos Stavrou.
+
+* GNU Free Documentation License
+:PROPERTIES:
+:APPENDIX: t
+:CUSTOM_ID: h:gnu-free-documentation-license
+:END:
+
+#+texinfo: @include doclicense.texi
+
+#+begin_export html
+<pre>
+
+ GNU Free Documentation License
+ Version 1.3, 3 November 2008
+
+
+ Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
+ <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall
+subject (or to related matters) and contains nothing that could fall
+directly within that overall subject. (Thus, if the Document is in
+part a textbook of mathematics, a Secondary Section may not explain
+any mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+The "publisher" means any person or entity that distributes copies of
+the Document to the public.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no
+other conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to
+give them a chance to provide you with an updated version of the
+Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other
+documents released under this License, and replace the individual
+copies of this License in the various documents with a single copy
+that is included in the collection, provided that you follow the rules
+of this License for verbatim copying of each of the documents in all
+other respects.
+
+You may extract a single document from such a collection, and
+distribute it individually under this License, provided you insert a
+copy of this License into the extracted document, and follow this
+License in all other respects regarding verbatim copying of that
+document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense, or distribute it is void, and
+will automatically terminate your rights under this License.
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, receipt of a copy of some or all of the same material does
+not give you any rights to use it.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions of the
+GNU Free Documentation License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in
+detail to address new problems or concerns. See
+https://www.gnu.org/licenses/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation. If the Document
+specifies that a proxy can decide which future versions of this
+License can be used, that proxy's public statement of acceptance of a
+version permanently authorizes you to choose that version for the
+Document.
+
+11. RELICENSING
+
+"Massive Multiauthor Collaboration Site" (or "MMC Site") means any
+World Wide Web server that publishes copyrightable works and also
+provides prominent facilities for anybody to edit those works. A
+public wiki that anybody can edit is an example of such a server. A
+"Massive Multiauthor Collaboration" (or "MMC") contained in the site
+means any set of copyrightable works thus published on the MMC site.
+
+"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
+license published by Creative Commons Corporation, a not-for-profit
+corporation with a principal place of business in San Francisco,
+California, as well as future copyleft versions of that license
+published by that same organization.
+
+"Incorporate" means to publish or republish a Document, in whole or in
+part, as part of another Document.
+
+An MMC is "eligible for relicensing" if it is licensed under this
+License, and if all works that were first published under this License
+somewhere other than this MMC, and subsequently incorporated in whole or
+in part into the MMC, (1) had no cover texts or invariant sections, and
+(2) were thus incorporated prior to November 1, 2008.
+
+The operator of an MMC Site may republish an MMC contained in the site
+under CC-BY-SA on the same site at any time before August 1, 2009,
+provided the MMC is eligible for relicensing.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.3
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+</pre>
+#+end_export
+
+#+html: <!--
+
+* Indices
+:PROPERTIES:
+:CUSTOM_ID: h:indices
+:END:
+
+** Function index
+:PROPERTIES:
+:INDEX: fn
+:CUSTOM_ID: h:function-index
+:END:
+
+** Variable index
+:PROPERTIES:
+:INDEX: vr
+:CUSTOM_ID: h:variable-index
+:END:
+
+** Concept index
+:PROPERTIES:
+:INDEX: cp
+:CUSTOM_ID: h:concept-index
+:END:
+
+#+html: -->
blob - /dev/null
blob + 6fa6116de64b21b9817bd575e6957eece875fd5b (mode 644)
--- /dev/null
+++ elpa/denote-org-0.1.1/denote-org-autoloads.el
+;;; denote-org-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*-
+;; Generated by the `loaddefs-generate' function.
+
+;; This file is part of GNU Emacs.
+
+;;; Code:
+
+(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
+
+
+
+;;; Generated autoloads from denote-org.el
+
+(autoload 'denote-org-link-to-heading "denote-org" "\
+Link to file and then specify a heading to extend the link to.
+
+The resulting link has the following pattern:
+
+[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading text]].
+
+Because only Org files can have links to individual headings,
+limit the list of possible files to those which include the .org
+file extension (remember that Denote works with many file types,
+per the user option `denote-file-type').
+
+The user option `denote-org-store-link-to-heading'
+determined whether the `org-store-link' function can save a link
+to the current heading. Such links look the same as those of
+this command, though the functionality defined herein is
+independent of it.
+
+To only link to a file, use the `denote-link' command.
+
+Also see `denote-org-backlinks-for-heading'." '(org-mode))
+(function-put 'denote-org-link-to-heading 'interactive-only 't)
+(autoload 'denote-org-backlinks-for-heading "denote-org" "\
+Produce backlinks for the current heading.
+This otherwise has the same behaviour as `denote-backlinks'---refer to
+that for the details.
+
+Also see `denote-org-link-to-heading'." t)
+(autoload 'denote-org-extract-org-subtree "denote-org" "\
+Create new Denote note using the current Org subtree as input.
+Remove the subtree from its current file and move its contents into a
+new Denote file (a subtree is a heading with all of its contents,
+including subheadings).
+
+Take the text of the subtree's top level heading and use it as the title
+of the new note.
+
+If the heading has any tags, use them as the keywords of the new note.
+If the Org file has any #+filetags use them as well (Org's filetags are
+inherited by the headings). If none of these are true and the user
+option `denote-prompts' includes an entry for keywords, then prompt for
+keywords. Else do not include any keywords.
+
+If the heading has a PROPERTIES drawer, retain it for further review.
+
+If the heading's PROPERTIES drawer includes a DATE or CREATED property,
+or there exists a CLOSED statement with a timestamp value, use that to
+derive the date (or date and time) of the new note (if there is only a
+date, the time is taken as 00:00). If more than one of these is
+present, the order of preference is DATE, then CREATED, then CLOSED. If
+none of these is present, use the current time. If the `denote-prompts'
+includes an entry for a date, then prompt for a date at this stage (also
+see `denote-date-prompt-use-org-read-date').
+
+For the rest, consult the value of the user option `denote-prompts' in
+the following scenaria:
+
+- Optionally prompt for a subdirectory, otherwise produce the new note
+ in the variable `denote-directory'.
+
+- Optionally prompt for a file signature, otherwise do not use one.
+
+Make the new note an Org file regardless of the value of the user option
+`denote-file-type'." '(org-mode))
+(autoload 'denote-org-convert-links-to-file-type "denote-org" "\
+Convert denote: links to file: links in the current Org buffer.
+Ignore all other link types. Also ignore links that do not
+resolve to a file in the variable `denote-directory'." '(org-mode))
+(autoload 'denote-org-convert-links-to-denote-type "denote-org" "\
+Convert file: links to denote: links in the current Org buffer.
+Ignore all other link types. Also ignore file: links that do not
+point to a file with a Denote file name." '(org-mode))
+(autoload 'denote-org-dblock-insert-links "denote-org" "\
+Create Org dynamic block to insert Denote links matching REGEXP.
+
+(fn REGEXP)" '(org-mode))
+(eval-after-load 'org '(progn (org-dynamic-block-define "denote-links" 'denote-org-dblock-insert-links)))
+(autoload 'org-dblock-write:denote-links "denote-org" "\
+Function to update `denote-links' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block.
+
+(fn PARAMS)")
+(autoload 'denote-org-dblock-insert-missing-links "denote-org" "\
+Create Org dynamic block to insert Denote links matching REGEXP.
+
+(fn REGEXP)" '(org-mode))
+(eval-after-load 'org '(progn (org-dynamic-block-define "denote-missing-links" 'denote-org-dblock-insert-missing-links)))
+(autoload 'org-dblock-write:denote-missing-links "denote-org" "\
+Function to update `denote-links' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block.
+
+(fn PARAMS)")
+(autoload 'denote-org-dblock-insert-backlinks "denote-org" "\
+Create Org dynamic block to insert Denote backlinks to current file." '(org-mode))
+(eval-after-load 'org '(progn (org-dynamic-block-define "denote-backlinks" 'denote-org-dblock-insert-backlinks)))
+(autoload 'org-dblock-write:denote-backlinks "denote-org" "\
+Function to update `denote-backlinks' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block.
+
+(fn PARAMS)")
+(autoload 'denote-org-dblock-insert-files "denote-org" "\
+Create Org dynamic block to insert Denote files matching REGEXP.
+Sort the files according to SORT-BY-COMPONENT, which is a symbol
+among `denote-sort-components'.
+
+(fn REGEXP SORT-BY-COMPONENT)" '(org-mode))
+(eval-after-load 'org '(progn (org-dynamic-block-define "denote-files" 'denote-org-dblock-insert-files)))
+(autoload 'org-dblock-write:denote-files "denote-org" "\
+Function to update `denote-files' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block.
+
+(fn PARAMS)")
+(autoload 'denote-org-dblock-insert-files-as-headings "denote-org" "\
+Create Org dynamic block to insert Denote Org files matching REGEXP.
+
+Turn the #+title of each file into a top-level heading. Then increment
+all original headings in the file by one, so that they become
+subheadings of what once was the #+title.
+
+Use the #+filetags of each file as tags for the top-level heading (what
+was the #+title).
+
+Sort the files according to SORT-BY-COMPONENT, which is a symbol
+among `denote-sort-components'.
+
+IMPORTANT NOTE: This dynamic block only works with Org files, because it
+has to assume the Org notation in order to insert each file's contents
+as its own heading.
+
+(fn REGEXP SORT-BY-COMPONENT)" '(org-mode))
+(eval-after-load 'org '(progn (org-dynamic-block-define "denote-files-as-headings" 'denote-org-dblock-insert-files-as-headings)))
+(autoload 'org-dblock-write:denote-files-as-headings "denote-org" "\
+Function to update `denote-files' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block.
+
+(fn PARAMS)")
+(register-definition-prefixes "denote-org" '("denote-org-"))
+
+;;; End of scraped data
+
+(provide 'denote-org-autoloads)
+
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; no-native-compile: t
+;; coding: utf-8-emacs-unix
+;; End:
+
+;;; denote-org-autoloads.el ends here
blob - /dev/null
blob + 8e1f48d7ad74b033b24c3cbe7e83108ec334769f (mode 644)
--- /dev/null
+++ elpa/denote-org-0.1.1/denote-org-pkg.el
+;; Generated package description from denote-org.el -*- mode: lisp-data; no-byte-compile: t -*-
+(define-package "denote-org" "0.1.1" "Denote extensions for Org mode" '((emacs "28.1") (denote "4.0.0")) :commit "0151c5a8ef525ce523c7f6c42f0c49f731ad2ff5" :authors '(("Protesilaos Stavrou" . "info@protesilaos.com")) :maintainer '("Protesilaos Stavrou" . "info@protesilaos.com") :url "https://github.com/protesilaos/denote-org")
blob - /dev/null
blob + 80f75f9fe5adaaa334566230c050bacb3a5655bc (mode 644)
--- /dev/null
+++ elpa/denote-org-0.1.1/denote-org.el
+;;; denote-org.el --- Denote extensions for Org mode -*- lexical-binding: t -*-
+
+;; Copyright (C) 2024-2025 Free Software Foundation, Inc.
+
+;; Author: Protesilaos Stavrou <info@protesilaos.com>
+;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
+;; URL: https://github.com/protesilaos/denote-org
+;; Version: 0.1.1
+;; Package-Requires: ((emacs "28.1") (denote "4.0.0"))
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Optional extensions to Denote that work specifically with Org mode.
+
+;;; Code:
+
+(require 'denote)
+(require 'org)
+
+(defgroup denote-org ()
+ "Optional extensions to Denote that work specifically with Org mode."
+ :group 'denote
+ :link '(info-link "(denote) top")
+ :link '(info-link "(denote-org) top")
+ :link '(url-link :tag "Denote homepage" "https://protesilaos.com/emacs/denote")
+ :link '(url-link :tag "Denote Org homepage" "https://protesilaos.com/emacs/denote-org"))
+
+;;;; Link to file and heading
+
+(defun denote-org--get-outline (file)
+ "Return `outline-regexp' headings and line numbers of FILE."
+ (with-current-buffer (find-file-noselect file)
+ (let ((outline-regexp (format "^\\(?:%s\\)" (or (bound-and-true-p outline-regexp) "\\*+ ")))
+ candidates)
+ (save-excursion
+ (goto-char (point-min))
+ (while (if (bound-and-true-p outline-search-function)
+ (funcall outline-search-function)
+ (re-search-forward outline-regexp nil t))
+ (push
+ ;; NOTE 2024-01-20: The -5 (minimum width) is a
+ ;; sufficiently high number to keep the alignment
+ ;; consistent in most cases. Larger files will simply
+ ;; shift the heading text in minibuffer, but this is not an
+ ;; issue anymore.
+ (format "%-5s %s"
+ (line-number-at-pos (point))
+ (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
+ candidates)
+ (goto-char (1+ (line-end-position)))))
+ (if candidates
+ (nreverse candidates)
+ (user-error "No outline")))))
+
+(define-obsolete-function-alias
+ 'denote-org--outline-prompt
+ 'denote-org-outline-prompt
+ "3.1.0")
+
+(defun denote-org-outline-prompt (&optional file)
+ "Prompt for outline among headings retrieved by `denote-org--get-outline'.
+With optional FILE use the outline of it, otherwise use that of
+the current file."
+ (let ((current-file (or file buffer-file-name)))
+ (completing-read
+ (format "Select heading inside `%s': "
+ (propertize (file-name-nondirectory current-file) 'face 'denote-faces-prompt-current-name))
+ (denote--completion-table-no-sort 'imenu (denote-org--get-outline current-file))
+ nil :require-match)))
+
+(defun denote-org--get-heading-and-id-from-line (line file)
+ "Return heading text and CUSTOM_ID from the given LINE in FILE."
+ (with-current-buffer (find-file-noselect file)
+ (save-excursion
+ (goto-char (point-min))
+ (forward-line (1- line))
+ (cons (denote-link-ol-get-heading)
+ (if (eq denote-org-store-link-to-heading 'context)
+ (org-entry-get (point) "CUSTOM_ID")
+ (denote-link-ol-get-id))))))
+
+(defun denote-org-format-link-with-heading (file heading-id description &optional format)
+ "Prepare link to FILE with HEADING-ID using DESCRIPTION.
+Optional FORMAT is the exact link pattern to use."
+ (when (region-active-p)
+ (setq description (buffer-substring-no-properties (region-beginning) (region-end)))
+ (denote--delete-active-region-content))
+ (format
+ (or format "[[denote:%s::#%s][%s]]")
+ (denote-retrieve-filename-identifier file)
+ heading-id
+ description))
+
+(define-obsolete-function-alias
+ 'denote-org-extras-link-to-heading
+ 'denote-org-link-to-heading
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-link-to-heading ()
+ "Link to file and then specify a heading to extend the link to.
+
+The resulting link has the following pattern:
+
+[[denote:IDENTIFIER::#ORG-HEADING-CUSTOM-ID]][Description::Heading text]].
+
+Because only Org files can have links to individual headings,
+limit the list of possible files to those which include the .org
+file extension (remember that Denote works with many file types,
+per the user option `denote-file-type').
+
+The user option `denote-org-store-link-to-heading'
+determined whether the `org-store-link' function can save a link
+to the current heading. Such links look the same as those of
+this command, though the functionality defined herein is
+independent of it.
+
+To only link to a file, use the `denote-link' command.
+
+Also see `denote-org-backlinks-for-heading'."
+ (declare (interactive-only t))
+ (interactive nil org-mode)
+ (unless (derived-mode-p 'org-mode)
+ (user-error "Links to headings only work between Org files"))
+ (let ((context-p (eq denote-org-store-link-to-heading 'context)))
+ (when-let* ((file (denote-file-prompt ".*\\.org"))
+ (file-text (denote-get-link-description file))
+ (heading (denote-org-outline-prompt file))
+ (line (string-to-number (car (split-string heading "\t"))))
+ (heading-data (denote-org--get-heading-and-id-from-line line file))
+ (heading-text (car heading-data))
+ (heading-id (if (and context-p (null (cdr heading-data)))
+ heading-text
+ (cdr heading-data)))
+ (description (denote-link-format-heading-description file-text heading-text)))
+ (insert
+ (denote-org-format-link-with-heading
+ file
+ heading-id
+ description
+ (when (equal heading-text heading-id)
+ "[[denote:%s::*%s][%s]]"))))))
+
+;;;; Heading backlinks
+
+(defun denote-org--get-file-id-and-heading-id-or-context ()
+ "Return link to current file and heading.
+If a CUSTOM_ID is present and the value of the user option
+`denote-org-store-link-to-heading' is set to `context', then return a
+regexp that matches both the CUSTOM_ID and the context of the current
+heading. This looks like:
+
+ \\(ID::*HEADING-TEXT\\|ID::#HEADING-ID\\)
+
+If CUSTOM_ID is present but `denote-org-store-link-to-heading' is not
+set to `context', then return a patternf of the following form:
+
+ ID::#HEADING-ID"
+ (when-let* ((id (denote-retrieve-filename-identifier-with-error buffer-file-name)))
+ (let ((context-p (eq denote-org-store-link-to-heading 'context))
+ (heading-id (org-entry-get (point) "CUSTOM_ID")))
+ (cond
+ ((and context-p heading-id)
+ (format "\\(%s::%s%s\\|#%s\\)" id (shell-quote-argument "*") (denote-link-ol-get-heading) heading-id))
+ (context-p
+ (concat id "::" (shell-quote-argument "*") (denote-link-ol-get-heading)))
+ (heading-id
+ (concat id "::#" heading-id))
+ (t
+ (error "No way to get link to a heading at point in file `%s'" buffer-file-name))))))
+
+(defun denote-org--get-backlinks-buffer-name (text)
+ "Format a buffer name for `denote-org-backlinks-for-heading' with TEXT."
+ (format "*Denote HEADING backlinks for %S*" text))
+
+(defun denote-org--get-backlinks-for-heading (file-and-heading-id)
+ "Get backlinks to FILE-AND-HEADING-ID as a list of strings."
+ (when-let* ((files (denote-directory-files nil :omit-current :text-only))
+ (xref-file-name-display 'abs)
+ (matches-in-files (xref-matches-in-files file-and-heading-id files))
+ (xref-alist (xref--analyze matches-in-files)))
+ (mapcar
+ (lambda (x)
+ (denote-get-file-name-relative-to-denote-directory (car x)))
+ xref-alist)))
+
+(define-obsolete-function-alias
+ 'denote-org-extras-backlinks-for-heading
+ 'denote-org-backlinks-for-heading
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-backlinks-for-heading ()
+ "Produce backlinks for the current heading.
+This otherwise has the same behaviour as `denote-backlinks'---refer to
+that for the details.
+
+Also see `denote-org-link-to-heading'."
+ (interactive)
+ (when-let* ((heading-id (denote-org--get-file-id-and-heading-id-or-context))
+ (heading-text (substring-no-properties (denote-link-ol-get-heading))))
+ (denote-link--prepare-backlinks heading-id ".*\\.org" (denote-org--get-backlinks-buffer-name heading-text))))
+
+;;;; Extract subtree into its own note
+
+(defun denote-org--get-heading-date ()
+ "Try to return a timestamp for the current Org heading.
+This can be used as the value for the DATE argument of the
+`denote' command."
+ (when-let* ((pos (point))
+ (timestamp (or (org-entry-get pos "DATE")
+ (org-entry-get pos "CREATED")
+ (org-entry-get pos "CLOSED"))))
+ (date-to-time timestamp)))
+
+(define-obsolete-function-alias
+ 'denote-org-extras-extract-org-subtree
+ 'denote-org-extract-org-subtree
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-extract-org-subtree ()
+ "Create new Denote note using the current Org subtree as input.
+Remove the subtree from its current file and move its contents into a
+new Denote file (a subtree is a heading with all of its contents,
+including subheadings).
+
+Take the text of the subtree's top level heading and use it as the title
+of the new note.
+
+If the heading has any tags, use them as the keywords of the new note.
+If the Org file has any #+filetags use them as well (Org's filetags are
+inherited by the headings). If none of these are true and the user
+option `denote-prompts' includes an entry for keywords, then prompt for
+keywords. Else do not include any keywords.
+
+If the heading has a PROPERTIES drawer, retain it for further review.
+
+If the heading's PROPERTIES drawer includes a DATE or CREATED property,
+or there exists a CLOSED statement with a timestamp value, use that to
+derive the date (or date and time) of the new note (if there is only a
+date, the time is taken as 00:00). If more than one of these is
+present, the order of preference is DATE, then CREATED, then CLOSED. If
+none of these is present, use the current time. If the `denote-prompts'
+includes an entry for a date, then prompt for a date at this stage (also
+see `denote-date-prompt-use-org-read-date').
+
+For the rest, consult the value of the user option `denote-prompts' in
+the following scenaria:
+
+- Optionally prompt for a subdirectory, otherwise produce the new note
+ in the variable `denote-directory'.
+
+- Optionally prompt for a file signature, otherwise do not use one.
+
+Make the new note an Org file regardless of the value of the user option
+`denote-file-type'."
+ (interactive nil org-mode)
+ (unless (derived-mode-p 'org-mode)
+ (user-error "Headings can only be extracted from Org files"))
+ (if-let* ((text (org-get-entry))
+ (heading (denote-link-ol-get-heading)))
+ (let ((tags (org-get-tags))
+ (date (denote-org--get-heading-date))
+ subdirectory
+ signature)
+ (dolist (prompt denote-prompts)
+ (pcase prompt
+ ('keywords (when (not tags) (setq tags (denote-keywords-prompt))))
+ ('subdirectory (setq subdirectory (denote-subdirectory-prompt)))
+ ('date (when (not date) (setq date (denote-date-prompt))))
+ ('signature (setq signature (denote-signature-prompt)))))
+ (delete-region (org-entry-beginning-position)
+ (save-excursion (org-end-of-subtree t) (point)))
+ (denote heading tags 'org subdirectory date text signature))
+ (user-error "No subtree to extract; aborting")))
+
+;;;; Convert links from `:denote' to `:file' and vice versa
+
+(defun denote-org--get-link-type-regexp (type)
+ "Return regexp for Org link TYPE.
+TYPE is a symbol of either `file' or `denote'.
+
+The regexp consists of four groups. Group 1 is the link type, 2
+is the target, 3 is the target's search terms, and 4 is the
+description."
+ (let ((group-1))
+ (pcase type
+ ('denote (setq group-1 "denote"))
+ ('file (setq group-1 "file"))
+ (_ (error "`%s' is an unknown link type" type)))
+ (format "\\[\\[\\(?1:%s:\\)\\(?:\\(?2:.*?\\)\\(?3:::.*\\)?\\]\\|\\]\\)\\(?4:\\[\\(?:.*?\\)\\]\\)?\\]" group-1)))
+
+(defun denote-org--get-path (id)
+ "Return file path to ID according to `org-link-file-path-type'."
+ (if (or (eq org-link-file-path-type 'adaptive)
+ (eq org-link-file-path-type 'relative))
+ (denote-get-relative-path-by-id id)
+ (denote-get-path-by-id id)))
+
+(define-obsolete-function-alias
+ 'denote-org-extras-convert-links-to-file-type
+ 'denote-org-convert-links-to-file-type
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-convert-links-to-file-type ()
+ "Convert denote: links to file: links in the current Org buffer.
+Ignore all other link types. Also ignore links that do not
+resolve to a file in the variable `denote-directory'."
+ (interactive nil org-mode)
+ (if (derived-mode-p 'org-mode)
+ (save-excursion
+ (let ((count 0))
+ (goto-char (point-min))
+ (while (re-search-forward (denote-org--get-link-type-regexp 'denote) nil :no-error)
+ (let* ((id (match-string-no-properties 2))
+ (search (or (match-string-no-properties 3) ""))
+ (desc (or (match-string-no-properties 4) ""))
+ (file (save-match-data (denote-org--get-path id))))
+ (when file
+ (let ((new-text (if desc
+ (format "[[file:%s%s]%s]" file search desc)
+ (format "[[file:%s%s]]" file search))))
+ (replace-match new-text :fixed-case :literal)
+ (setq count (1+ count))))))
+ (message "Converted %d `denote:' links to `file:' links" count)))
+ (user-error "The current file is not using Org mode")))
+
+(define-obsolete-function-alias
+ 'denote-org-extras-convert-links-to-denote-type
+ 'denote-org-convert-links-to-denote-type
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-convert-links-to-denote-type ()
+ "Convert file: links to denote: links in the current Org buffer.
+Ignore all other link types. Also ignore file: links that do not
+point to a file with a Denote file name."
+ (interactive nil org-mode)
+ (if (derived-mode-p 'org-mode)
+ (save-excursion
+ (let ((count 0))
+ (goto-char (point-min))
+ (while (re-search-forward (denote-org--get-link-type-regexp 'file) nil :no-error)
+ (let* ((file (match-string-no-properties 2))
+ (search (or (match-string-no-properties 3) ""))
+ (desc (or (match-string-no-properties 4) ""))
+ (id (save-match-data (denote-retrieve-filename-identifier file))))
+ (when id
+ (let ((new-text (if desc
+ (format "[[denote:%s%s]%s]" id search desc)
+ (format "[[denote:%s%s]]" id search))))
+ (replace-match new-text :fixed-case :literal)
+ (setq count (1+ count))))))
+ (message "Converted %d `file:' links to `denote:' links" count)))
+ (user-error "The current file is not using Org mode")))
+
+;;;; Org dynamic blocks
+
+;; NOTE 2024-01-22 12:26:13 +0200: The following is copied from the
+;; now-deleted denote-org-dblock.el. Its original author was Elias
+;; Storms <elias.storms@gmail.com>, with substantial contributions and
+;; further developments by me (Protesilaos).
+
+;; This section defines Org dynamic blocks using the facility described
+;; in the Org manual. Evaluate this:
+;;
+;; (info "(org) Dynamic Blocks")
+;;
+;; The dynamic blocks defined herein are documented at length in the
+;; Denote manual. See the following node and its subsections:
+;;
+;; (info "(denote) Use Org dynamic blocks")
+
+;;;;; Common helper functions
+
+(defun denote-org-dblock--files (files-matching-regexp &optional sort-by-component reverse exclude-regexp)
+ "Return list of FILES-MATCHING-REGEXP in variable `denote-directory'.
+SORT-BY-COMPONENT, REVERSE, EXCLUDE-REGEXP have the same meaning as
+`denote-sort-get-directory-files'. If both are nil, do not try to
+perform any sorting.
+
+Also see `denote-org-dblock--files-missing-only'."
+ (cond
+ ((and sort-by-component reverse)
+ (denote-sort-get-directory-files files-matching-regexp sort-by-component reverse :omit-current exclude-regexp))
+ (sort-by-component
+ (denote-sort-get-directory-files files-matching-regexp sort-by-component nil :omit-current exclude-regexp))
+ (reverse
+ (denote-sort-get-directory-files files-matching-regexp :no-component-specified reverse :omit-current exclude-regexp))
+ (t
+ (denote-directory-files files-matching-regexp :omit-current nil exclude-regexp))))
+
+(defun denote-org-dblock--get-missing-links (regexp)
+ "Return list of missing links to all notes matching REGEXP.
+Missing links are those for which REGEXP does not have a match in
+the current buffer."
+ (let ((found-files (denote-directory-files regexp :omit-current))
+ (linked-files (denote-link--expand-identifiers denote-org-link-in-context-regexp)))
+ (if-let* ((final-files (seq-difference found-files linked-files)))
+ final-files
+ (message "All links matching `%s' are present" regexp)
+ '())))
+
+(defun denote-org-dblock--files-missing-only (files-matching-regexp &optional sort-by-component reverse)
+ "Return list of missing links to FILES-MATCHING-REGEXP.
+SORT-BY-COMPONENT and REVERSE have the same meaning as
+`denote-sort-files'. If both are nil, do not try to perform any
+sorting.
+
+Also see `denote-org-dblock--files'."
+ (denote-sort-files
+ (denote-org-dblock--get-missing-links files-matching-regexp)
+ sort-by-component
+ reverse))
+
+;;;;; Dynamic block to insert links
+
+(define-obsolete-function-alias
+ 'denote-org-extras-dblock-insert-links
+ 'denote-org-dblock-insert-links
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-dblock-insert-links (regexp)
+ "Create Org dynamic block to insert Denote links matching REGEXP."
+ (interactive
+ (list
+ (denote-files-matching-regexp-prompt))
+ org-mode)
+ (org-create-dblock (list :name "denote-links"
+ :regexp regexp
+ :not-regexp nil
+ :excluded-dirs-regexp nil
+ :sort-by-component nil
+ :reverse-sort nil
+ :id-only nil
+ :include-date nil))
+ (org-update-dblock))
+
+;; NOTE 2024-03-30: This is how the autoload is done in org.el.
+;;;###autoload
+(eval-after-load 'org
+ '(progn
+ (org-dynamic-block-define "denote-links" 'denote-org-dblock-insert-links)))
+
+;; TODO 2024-12-04: Maybe we can do this for anything that deals with
+;; regular expressions that users provide? I prefer not to do the
+;; work if nobody wants it, though I am mentioning this here just in
+;; case.
+(defun denote-org--parse-rx (regexp)
+ "Parse REGEXP as an `rx' argument or string and return string."
+ (cond
+ ((null regexp)
+ nil)
+ ((listp regexp)
+ (rx-to-string regexp))
+ ((stringp regexp)
+ regexp)
+ (t
+ (error "Regexp `%s' is neither a list nor a string" regexp))))
+
+;;;###autoload
+(defun org-dblock-write:denote-links (params)
+ "Function to update `denote-links' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block."
+ (let* ((rx (denote-org--parse-rx (plist-get params :regexp)))
+ (not-rx (denote-org--parse-rx (plist-get params :not-regexp)))
+ (sort (plist-get params :sort-by-component))
+ (reverse (plist-get params :reverse-sort))
+ (include-date (plist-get params :include-date))
+ (block-name (plist-get params :block-name))
+ (denote-excluded-directories-regexp (or (plist-get params :excluded-dirs-regexp)
+ denote-excluded-directories-regexp))
+ (files (denote-org-dblock--files rx sort reverse not-rx)))
+ (when block-name (insert "#+name: " block-name "\n"))
+ (denote-link--insert-links files 'org (plist-get params :id-only) :no-other-sorting include-date)
+ (join-line))) ; remove trailing empty line
+
+;;;;; Dynamic block to insert missing links
+
+;; TODO 2024-12-03: Do we need the :not-regexp here? I think yes,
+;; though I prefer to have a user of this kind of dblock send me their
+;; feedback.
+
+(define-obsolete-function-alias
+ 'denote-org-extras-dblock-insert-missing-links
+ 'denote-org-dblock-insert-missing-links
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-dblock-insert-missing-links (regexp)
+ "Create Org dynamic block to insert Denote links matching REGEXP."
+ (interactive
+ (list
+ (denote-files-matching-regexp-prompt))
+ org-mode)
+ (org-create-dblock (list :name "denote-missing-links"
+ :regexp regexp
+ :excluded-dirs-regexp nil
+ :sort-by-component nil
+ :reverse-sort nil
+ :id-only nil
+ :include-date nil))
+ (org-update-dblock))
+
+;; NOTE 2024-03-30: This is how the autoload is done in org.el.
+;;;###autoload
+(eval-after-load 'org
+ '(progn
+ (org-dynamic-block-define "denote-missing-links" 'denote-org-dblock-insert-missing-links)))
+
+;;;###autoload
+(defun org-dblock-write:denote-missing-links (params)
+ "Function to update `denote-links' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block."
+ (let* ((rx (denote-org--parse-rx (plist-get params :regexp)))
+ (sort (plist-get params :sort-by-component))
+ (reverse (plist-get params :reverse-sort))
+ (include-date (plist-get params :include-date))
+ (block-name (plist-get params :block-name))
+ (denote-excluded-directories-regexp (or (plist-get params :excluded-dirs-regexp)
+ denote-excluded-directories-regexp))
+ (files (denote-org-dblock--files-missing-only rx sort reverse)))
+ (when block-name (insert "#+name: " block-name "\n"))
+ (denote-link--insert-links files 'org (plist-get params :id-only) :no-other-sorting include-date)
+ (join-line))) ; remove trailing empty line
+
+;;;;; Dynamic block to insert backlinks
+
+(defun denote-org-dblock--maybe-sort-backlinks (files sort-by-component reverse)
+ "Sort backlink FILES if SORT-BY-COMPONENT and/or REVERSE is non-nil."
+ (cond
+ ((and sort-by-component reverse)
+ (denote-sort-files files sort-by-component reverse))
+ (sort-by-component
+ (denote-sort-files files sort-by-component))
+ (reverse
+ (denote-sort-files files :no-component-specified reverse))
+ (t
+ files)))
+
+;; TODO 2024-12-03: Do we need the :not-regexp here? I think yes,
+;; though I prefer to have a user of this kind of dblock send me their
+;; feedback.
+
+(define-obsolete-function-alias
+ 'denote-org-extras-dblock-insert-backlinks
+ 'denote-org-dblock-insert-backlinks
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-dblock-insert-backlinks ()
+ "Create Org dynamic block to insert Denote backlinks to current file."
+ (interactive nil org-mode)
+ (org-create-dblock (list :name "denote-backlinks"
+ :excluded-dirs-regexp nil
+ :sort-by-component nil
+ :reverse-sort nil
+ :id-only nil
+ :this-heading-only nil
+ :include-date nil))
+ (org-update-dblock))
+
+;; NOTE 2024-03-30: This is how the autoload is done in org.el.
+;;;###autoload
+(eval-after-load 'org
+ '(progn
+ (org-dynamic-block-define "denote-backlinks" 'denote-org-dblock-insert-backlinks)))
+
+;;;###autoload
+(defun org-dblock-write:denote-backlinks (params)
+ "Function to update `denote-backlinks' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block."
+ (when-let* ((files (if (plist-get params :this-heading-only)
+ (denote-org--get-backlinks-for-heading (denote-org--get-file-id-and-heading-id-or-context))
+ (denote-link-return-backlinks))))
+ (let* ((sort (plist-get params :sort-by-component))
+ (reverse (plist-get params :reverse-sort))
+ (include-date (plist-get params :include-date))
+ (denote-excluded-directories-regexp (or (plist-get params :excluded-dirs-regexp)
+ denote-excluded-directories-regexp))
+ (files (denote-org-dblock--maybe-sort-backlinks files sort reverse)))
+ (denote-link--insert-links files 'org (plist-get params :id-only) :no-other-sorting include-date)
+ (join-line)))) ; remove trailing empty line
+
+;;;;; Dynamic block to insert entire file contents
+
+(defun denote-org-dblock--get-file-contents (file &optional no-front-matter add-links)
+ "Insert the contents of FILE.
+With optional NO-FRONT-MATTER as non-nil, try to remove the front
+matter from the top of the file. If NO-FRONT-MATTER is a number,
+remove that many lines starting from the top. If it is any other
+non-nil value, delete from the top until the first blank line.
+
+With optional ADD-LINKS as non-nil, first insert a link to the
+file and then insert its contents. In this case, format the
+contents as a typographic list. If ADD-LINKS is `id-only', then
+insert links as `denote-link' does when supplied with an ID-ONLY
+argument."
+ (when (denote-file-is-note-p file)
+ (with-temp-buffer
+ (when add-links
+ (insert
+ (format "- %s\n\n"
+ (denote-format-link
+ file
+ (denote-get-link-description file)
+ 'org
+ (eq add-links 'id-only)))))
+ (let ((beginning-of-contents (point)))
+ (insert-file-contents file)
+ (when no-front-matter
+ (delete-region
+ (if (natnump no-front-matter)
+ (progn (forward-line no-front-matter) (line-beginning-position))
+ (1+ (re-search-forward "^$" nil :no-error 1)))
+ beginning-of-contents))
+ (when add-links
+ (indent-region beginning-of-contents (point-max) 2)))
+ (buffer-string))))
+
+(defvar denote-org-dblock-file-contents-separator
+ (concat "\n\n" (make-string 50 ?-) "\n\n\n")
+ "Fallback separator used by `denote-org-dblock-add-files'.")
+
+(defun denote-org-dblock--separator (separator)
+ "Return appropriate value of SEPARATOR for `denote-org-dblock-add-files'."
+ (cond
+ ((null separator) "")
+ ((stringp separator) separator)
+ (t denote-org-dblock-file-contents-separator)))
+
+(defun denote-org-dblock-add-files (regexp &optional separator no-front-matter add-links sort-by-component reverse excluded-dirs-regexp exclude-regexp)
+ "Insert files matching REGEXP.
+
+Seaprate them with the optional SEPARATOR. If SEPARATOR is nil,
+use the `denote-org-dblock-file-contents-separator'.
+
+If optional NO-FRONT-MATTER is non-nil try to remove the front
+matter from the top of the file. Do it by finding the first
+blank line, starting from the top of the buffer.
+
+If optional ADD-LINKS is non-nil, first insert a link to the file
+and then insert its contents. In this case, format the contents
+as a typographic list.
+
+If optional SORT-BY-COMPONENT is a symbol among `denote-sort-components',
+sort files matching REGEXP by the corresponding Denote file name
+component. If the symbol is not among `denote-sort-components',
+fall back to the default identifier-based sorting.
+
+If optional REVERSE is non-nil reverse the sort order.
+
+Optional EXCLUDED-DIRS-REGEXP is the `let' bound value of
+`denote-excluded-directories-regexp'. When nil, the original value of
+that user option is used.
+
+Optional EXCLUDE-REGEXP is a more general way to exclude files whose
+name matches the given regular expression."
+ (let* ((denote-excluded-directories-regexp (or excluded-dirs-regexp denote-excluded-directories-regexp))
+ (files (denote-org-dblock--files regexp sort-by-component reverse exclude-regexp))
+ (files-contents (mapcar
+ (lambda (file) (denote-org-dblock--get-file-contents file no-front-matter add-links))
+ files)))
+ (insert (string-join files-contents (denote-org-dblock--separator separator)))))
+
+(define-obsolete-function-alias
+ 'denote-org-extras-dblock-insert-files
+ 'denote-org-dblock-insert-files
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-dblock-insert-files (regexp sort-by-component)
+ "Create Org dynamic block to insert Denote files matching REGEXP.
+Sort the files according to SORT-BY-COMPONENT, which is a symbol
+among `denote-sort-components'."
+ (interactive
+ (list
+ (denote-files-matching-regexp-prompt)
+ (denote-sort-component-prompt))
+ org-mode)
+ (org-create-dblock (list :name "denote-files"
+ :regexp regexp
+ :not-regexp nil
+ :excluded-dirs-regexp nil
+ :sort-by-component sort-by-component
+ :reverse-sort nil
+ :no-front-matter nil
+ :file-separator nil
+ :add-links nil))
+ (org-update-dblock))
+
+;; NOTE 2024-03-30: This is how the autoload is done in org.el.
+;;;###autoload
+(eval-after-load 'org
+ '(progn
+ (org-dynamic-block-define "denote-files" 'denote-org-dblock-insert-files)))
+
+;;;###autoload
+(defun org-dblock-write:denote-files (params)
+ "Function to update `denote-files' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block."
+ (let* ((rx (denote-org--parse-rx (plist-get params :regexp)))
+ (not-rx (denote-org--parse-rx (plist-get params :not-regexp)))
+ (sort (plist-get params :sort-by-component))
+ (reverse (plist-get params :reverse-sort))
+ (block-name (plist-get params :block-name))
+ (separator (plist-get params :file-separator))
+ (no-f-m (plist-get params :no-front-matter))
+ (add-links (plist-get params :add-links))
+ (excluded-dirs (plist-get params :excluded-dirs-regexp)))
+ (when block-name (insert "#+name: " block-name "\n"))
+ (when rx (denote-org-dblock-add-files rx separator no-f-m add-links sort reverse excluded-dirs not-rx)))
+ (join-line)) ; remove trailing empty line
+
+;;;; Insert files as headings
+
+(defun denote-org-dblock--extract-regexp (regexp)
+ "Extract REGEXP from the buffer and trim it of surrounding spaces."
+ (string-trim
+ (save-excursion
+ (re-search-forward regexp nil :no-error)
+ (buffer-substring-no-properties (match-end 0) (line-end-position)))))
+
+(defun denote-org-dblock--get-file-contents-as-heading (file add-links)
+ "Insert the contents of Org FILE, formatting the #+title as a heading.
+With optional ADD-LINKS, make the title link to the original file."
+ (when-let* (((denote-file-is-note-p file))
+ (identifier (denote-retrieve-filename-identifier file))
+ (file-type (denote-filetype-heuristics file))
+ ((eq file-type 'org)))
+ (with-temp-buffer
+ (let ((beginning-of-contents (point))
+ title
+ tags)
+ (insert-file-contents file)
+ (setq title (denote-org-dblock--extract-regexp (denote--title-key-regexp file-type)))
+ (setq tags (denote-org-dblock--extract-regexp (denote--keywords-key-regexp file-type)))
+ (delete-region (1+ (re-search-forward "^$" nil :no-error 1)) beginning-of-contents)
+ (goto-char beginning-of-contents)
+ (when (and title tags)
+ (if add-links
+ (insert (format "* [[denote:%s][%s]] %s\n\n" identifier title tags))
+ (insert (format "* %s %s\n\n" title tags)))
+ (org-align-tags :all))
+ (while (re-search-forward "^\\(*+?\\) " nil :no-error)
+ (replace-match (format "*%s " "\\1"))))
+ (buffer-string))))
+
+(defun denote-org-dblock-add-files-as-headings (regexp &optional add-links sort-by-component reverse excluded-dirs-regexp exclude-regexp)
+ "Insert files matching REGEXP.
+
+If optional ADD-LINKS is non-nil, first insert a link to the file
+and then insert its contents. In this case, format the contents
+as a typographic list.
+
+If optional SORT-BY-COMPONENT is a symbol among `denote-sort-components',
+sort files matching REGEXP by the corresponding Denote file name
+component. If the symbol is not among `denote-sort-components',
+fall back to the default identifier-based sorting.
+
+If optional REVERSE is non-nil reverse the sort order.
+
+Optional EXCLUDED-DIRS-REGEXP is the `let' bound value of
+`denote-excluded-directories-regexp'. When nil, the original value of
+that user option is used.
+
+Optional EXCLUDE-REGEXP is a more general way to exclude files whose
+name matches the given regular expression."
+ (let* ((denote-excluded-directories-regexp (or excluded-dirs-regexp denote-excluded-directories-regexp))
+ (files (denote-org-dblock--files regexp sort-by-component reverse exclude-regexp))
+ (files-contents (mapcar
+ (lambda (file)
+ (denote-org-dblock--get-file-contents-as-heading file add-links))
+ files)))
+ (insert (string-join files-contents))))
+
+(define-obsolete-function-alias
+ 'denote-org-extras-dblock-insert-files-as-headings
+ 'denote-org-dblock-insert-files-as-headings
+ "As part of making `denote-org' a standalone package")
+
+;;;###autoload
+(defun denote-org-dblock-insert-files-as-headings (regexp sort-by-component)
+ "Create Org dynamic block to insert Denote Org files matching REGEXP.
+
+Turn the #+title of each file into a top-level heading. Then increment
+all original headings in the file by one, so that they become
+subheadings of what once was the #+title.
+
+Use the #+filetags of each file as tags for the top-level heading (what
+was the #+title).
+
+Sort the files according to SORT-BY-COMPONENT, which is a symbol
+among `denote-sort-components'.
+
+IMPORTANT NOTE: This dynamic block only works with Org files, because it
+has to assume the Org notation in order to insert each file's contents
+as its own heading."
+ (interactive
+ (list
+ (denote-files-matching-regexp-prompt)
+ (denote-sort-component-prompt))
+ org-mode)
+ (org-create-dblock (list :name "denote-files-as-headings"
+ :regexp regexp
+ :not-regexp nil
+ :excluded-dirs-regexp nil
+ :sort-by-component sort-by-component
+ :reverse-sort nil
+ :add-links nil))
+ (org-update-dblock))
+
+;; NOTE 2024-03-30: This is how the autoload is done in org.el.
+;;;###autoload
+(eval-after-load 'org
+ '(progn
+ (org-dynamic-block-define "denote-files-as-headings" 'denote-org-dblock-insert-files-as-headings)))
+
+;;;###autoload
+(defun org-dblock-write:denote-files-as-headings (params)
+ "Function to update `denote-files' Org Dynamic blocks.
+Used by `org-dblock-update' with PARAMS provided by the dynamic block."
+ (let* ((rx (denote-org--parse-rx (plist-get params :regexp)))
+ (not-rx (denote-org--parse-rx (plist-get params :not-regexp)))
+ (sort (plist-get params :sort-by-component))
+ (reverse (plist-get params :reverse-sort))
+ (block-name (plist-get params :block-name))
+ (add-links (plist-get params :add-links))
+ (excluded-dirs (plist-get params :excluded-dirs-regexp)))
+ (when block-name (insert "#+name: " block-name "\n"))
+ (when rx (denote-org-dblock-add-files-as-headings rx add-links sort reverse excluded-dirs not-rx)))
+ (join-line)) ; remove trailing empty line
+
+(provide 'denote-org)
+;;; denote-org.el ends here
blob - /dev/null
blob + f1fba7dac575b855475a6aecc1201ae5fcd2b7f1 (mode 644)
Binary files /dev/null and elpa/denote-org-0.1.1/denote-org.info differ
blob - /dev/null
blob + cf277daac78585fb73dccd9fcd5fed9303d556cc (mode 644)
--- /dev/null
+++ elpa/denote-org-0.1.1/dir
+This is the file .../info/dir, which contains the
+topmost node of the Info hierarchy, called (dir)Top.
+The first time you invoke Info you start off looking at this node.
+
+File: dir, Node: Top This is the top of the INFO tree
+
+ This (the Directory node) gives a menu of major topics.
+ Typing "q" exits, "H" lists all Info commands, "d" returns here,
+ "h" gives a primer for first-timers,
+ "mEmacs<Return>" visits the Emacs manual, etc.
+
+ In Emacs, you can click mouse button 2 on a menu item or cross reference
+ to select it.
+
+* Menu:
+
+Emacs misc features
+* Denote Org: (denote-org). Extensions to better integrate Org with Denote.
blob - /dev/null
blob + 707df4f9bdce70fe9deb2cf693b05bdfe88c6e4f (mode 644)
--- /dev/null
+++ elpa/denote-org-0.1.1.signed
+Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2025-04-19T23:10:04+0200 using EDDSA
\ No newline at end of file
blob - 5fcafacee76da44024a76a8368c10e684803ca8b
blob + b951843615cd8df89ec49b6397307b79232e5120
--- init.el
+++ init.el
(require 'xdg)
(require 'cl-lib)
+(setq lh/dir-documents
+ (expand-file-name
+ (cond ((eq system-type 'gnu/linux) (or (xdg-user-dir "DOCUMENTS") "~/Documents"))
+ ((eq system-type 'windows-nt) "~/Documents"))))
(setq lh/dir-data-home
(expand-file-name
(cond ((eq system-type 'gnu/linux) (or (xdg-data-home) "~/.local/share"))
("melpa-stable" . 1)
("melpa" . 0))
package-pinned-packages '((sly . "melpa"))
- package-selected-packages '(csv-mode elfeed magit nhexl-mode notmuch ob-restclient paredit
- paredit-menu restclient restclient-jq sly))
+ package-selected-packages '(csv-mode denote denote-org elfeed magit nhexl-mode notmuch
+ ob-restclient paredit paredit-menu restclient
+ restclient-jq sly))
(package-initialize)
;;;; Completions
(with-eval-after-load 'restclient
(require 'restclient-capf))
+;;;; org + denote
+(let* ((org-dir (expand-file-name "org" lh/dir-documents))
+ (denote-dir (expand-file-name "notes" org-dir)))
+ (make-directory org-dir t)
+ (make-directory denote-dir t)
+ (setopt org-directory org-dir
+ denote-directory denote-dir))
+
;;;; Misc
(setopt focus-follows-mouse t
+ mouse-autoselect-window 0.2
frame-resize-pixelwise t
scroll-conservatively 100
pixel-scroll-precision-mode t