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-extras
withdenote-org
. -
denote-journal
: Replacedenote-journal-extras
withdenote-journal
. -
denote-silo
: Replacedenote-silo-extras
withdenote-silo
. -
denote-markdown
: Replacedenote-md-extras
withdenote-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-org
package. -
The
denote-dired
command (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-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
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-link
to insert a query link at point for “file contents”. It perform a search inside files in thedenote-directory
and put the results in adenote-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 adired
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
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-matter
is superseded bydenote-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 aPROPERTIES
drawer 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-link
and 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-notes
is an alias fordenote-link-dired-marked-notes
. -
The user option
denote-sort-dired-extra-prompts
control 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-function
variable 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-id
usedenote-get-file-extension-sans-encryption
instead 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 ifFILE
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 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-REGEXP
parameter. -
MODIFIED Function
denote-directory-files
: Returns a list of absolute file paths in variabledenote-directory
. It now accepts the optionalEXCLUDE-REGEXP
parameter. -
MODIFIED Function
denote-format-file-name
: Formats a file name. The way it treats itsID
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 thandenote-extract-keywords-from-path
, because of the many other functions with thedenote-retrieve-*
prefix. -
MODIFIED Function
denote-retrieve-filename-identifier
: Extracts the identifier fromFILE
name, if present, else returns nil. To create a new one from a date, refer to thedenote-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
: ConvertsDATE
into a Denote identifier usingdenote-id-format
. IfDATE
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, expectingYYYY-MM-DD
or 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-date
is non-nil. It now has the optional parametersINITIAL-DATE
andPROMPT-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 forQUERY
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 forQUERY
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 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-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 afterdenote-rename-file
(or related). TheFILE
,TITLE
,KEYWORDS
,SIGNATURE
,DATE
,IDENTIFIER
, andFILE-TYPE
arguments are given by the renaming command and are used to construct new front matter values if appropriate. Ifdenote-rename-confirmations
containsrewrite-front-matter
, prompt to confirm the rewriting of the front matter. Otherwise produce ay-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
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