Emacs: Denote version 4.0.0
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 constistent 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:
- 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.
Below are the release notes.
Version 4.0.0 on 2025-04-15
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
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 ofdenote-org-extraswithdenote-org.
- 
    denote-journal: Replacedenote-journal-extraswithdenote-journal.
- 
    denote-silo: Replacedenote-silo-extraswithdenote-silo.
- 
    denote-markdown: Replacedenote-md-extraswithdenote-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”
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-orgpackage.
- 
    The denote-diredcommand (aliasdenote-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-modeand 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
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
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-linkto insert a query link at point for “file contents”. It perform a search inside files in thedenote-directoryand put the results in adenote-query-modebuffer.
- 
    Query file names: Use the denote-query-filenames-linkto insert a query link for “file names”. It performs the query against file names (not contents!) and puts the results in adiredbuffer.
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
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
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
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:
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
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:
(setq denote-link-description-format "%t")
The documentation of this user option covers all the format specifiers and further details.
Miscellaneous changes for all users
- 
    The command denote-add-front-matteris superseded bydenote-rename-fileand 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-functionspecifies the function used by Denote to open the file of a direct link.
- 
    The user option denote-org-store-link-to-headingcan now be set to form generic context links without aPROPERTIESdrawer and correspondingCUSTOM_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 tonil, which is what we were doing for most of Denote’s history. This means that, by default,org-store-linkand anything building on top of it will create a link only to the current Denote file, likedenote: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-notesis an alias fordenote-link-dired-marked-notes.
- 
    The user option denote-sort-dired-extra-promptscontrol whatdenote-dired(aliasdenote-sort-dired) prompts for. It accepts either a nil value or a list of symbols amongsort-by-component,reverse-sort, andexclude-regexp. The order those symbols appear in the list is significant, with the leftmost coming first.
- 
    There is a new denote-sort-identifier-comparison-functionvariable which determines how identifier-based sorting should be done by default. It complements the existingdenote-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-idusedenote-get-file-extension-sans-encryptioninstead ofdenote-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
The following have been added or modified.
- 
    NEW Function denote-file-has-denoted-filename-p: Return non-nil ifFILErespects 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 indenote-directory-files. With optionalFILES-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 functionsdenote-keywords.
- 
    MODIFIED Function denote-keywords: Returns an appropriate list of keyword candidates, while accounting for the value of the user optiondenote-infer-keywords. It now also accepts the optionalFILES-MATCHING-REGEXPparameter.
- 
    MODIFIED Function denote-directory-files: Returns a list of absolute file paths in variabledenote-directory. It now accepts the optionalEXCLUDE-REGEXPparameter.
- 
    MODIFIED Function denote-format-file-name: Formats a file name. The way it treats itsIDparameter 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 thandenote-extract-keywords-from-path, because of the many other functions with thedenote-retrieve-*prefix.
- 
    MODIFIED Function denote-retrieve-filename-identifier: Extracts the identifier fromFILEname, if present, else returns nil. To create a new one from a date, refer to thedenote-get-identifierfunction. Thanks to Jean-Philippe Gagné Guay for the pull request 476: https://github.com/protesilaos/denote/pull/476.
- 
    MODIFIED Function denote-get-identifier: ConvertsDATEinto a Denote identifier usingdenote-id-format. IfDATEis 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, expectingYYYY-MM-DDor that plusHH:MM(or evenHH:MM:SS). Can also use Org’s more advanced date selection utility if the user optiondenote-date-prompt-use-org-read-dateis non-nil. It now has the optional parametersINITIAL-DATEandPROMPT-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 forQUERYand group them per file. Limit the search to text files.
- 
    NEW Function denote-retrieve-files-xref-query: Returns sorted, deduplicated file names with matches forQUERYin their contents. Limits the search to text files.
- 
    NEW Function denote-retrieve-xref-alist: Returns xref alist of files with the location of matches forQUERY. With optionalFILES-MATCHING-REGEXP, it limits the list of files accordingly (perdenote-directory-files). At all times, it limits the search to text files.
- 
    NEW Function denote-prepend-front-matter: Prepend front matter toFILE. TheTITLE,KEYWORDS,DATE,ID,SIGNATURE, andFILE-TYPEare 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 afterdenote-rename-file(or related). TheFILE,TITLE,KEYWORDS,SIGNATURE,DATE,IDENTIFIER, andFILE-TYPEarguments are given by the renaming command and are used to construct new front matter values if appropriate. Ifdenote-rename-confirmationscontainsrewrite-front-matter, prompt to confirm the rewriting of the front matter. Otherwise produce ay-or-n-pprompt 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
denote-journal integrates nicely with M-x calendar
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: 
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
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
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
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
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
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
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
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.
~/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
The following are not accurate because they only reflect the changes after the reorganisation I made. But we have to start from somewhere.
~/Git/Projects/denote-journal $ git shortlog  --summary --numbered
    54	Protesilaos Stavrou
     2	Honza Pokorny
     1	Vineet C. Kulkarni
~/Git/Projects/denote-sequence $ git shortlog  --summary --numbered
    22	Protesilaos Stavrou
~/Git/Projects/denote-silo $ git shortlog  --summary --numbered
    17	Protesilaos Stavrou
~/Git/Projects/denote-org $ git shortlog  --summary --numbered
    15	Protesilaos Stavrou
~/Git/Projects/denote-markdown $ git shortlog  --summary --numbered
    11	Protesilaos Stavrou