This is a fork of Denote, developped by Protesilaos and many other contributors (me included).
This fork is mostly compatible with Denote. There are a few differences to be aware of. They are listed below.
The purpose of this fork is to provide a simplified version of the original project focusing on the composability and hackability of Denote. Those are core principles in both projects. The idea is to provide a solid core of composable functions that users can use to build their own personal note-taking system. As such, some convenience functions provided in upstream are not provided by this fork. There are many ways one can configure a note-taking system. The creation of the personalized commands specific to a user’s use cases are left to the user. Examples on how to configure Denote to one’s needs are given below in this manual.
Functionality-wise, there should not be a lot missing from upstream.
Many new features are also developed here that I intend to contribute back to Denote. The main ones are:
denote-directory
accepts a list of directories.- File name components (title, keywords, signature, identifier) can be reordered (or absent)
- Improved renaming commands.
denote-rename-file
supports relocating a file and changing its file type. The code of the renaming commands has been simplified in the process. - Variables to create custom denote commands (denote-forced-* variables).
I will be contributing what I can to Denote if it makes sense.
Like the code, part of this manual has been adapted from Protesilaos’s Denote manual.
Please contribute code and open issues in Denote if the feature is also available there and makes sense in the main project.
I would also prefer that you open issues, rather than provide code. I want to contribute back the code of this project to Denote and it requires FSF copyright assignment for code contributions.
Copyright (C) 2022-2024 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.”
I have removed a lot of code from Denote. Even if a lot of code has been removed from Denote, most functionalities have been retained. The few functionalities that have been removed were removed because users should be responsible for their configuration or it is extra functionalities.
I have disabled the default sluggification for keywords and
signature. Unlike titles, those components can be typed correctly from
the prompts by users. This functionality can be reinstated by user
through denote-file-name-slug-functions
.
Many internal functions that were involved in this task have been removed.
Reason for removal: Users should configure this themselves using more specialized tools and to match their specific needs.
Alternatives: standard keymaps, transient keymaps, hydra, etc.
The reason for their suppression is that I want to emphasize the composability of Denote while also simplifying the code base. I do not want to cut on useful functionalities, but the job of selecting an appropriate Emacs command is already handled well by various other built-in mechanisms or external packages. They include: standard prefix keymaps, transients, hydras, etc.
Reason for removal: Users should create these functions according to their specific needs and build a menu tailored to their needs (see previous entry).
This includes denote-signature
, denote-date
, denote-type
,
denote-link-with-signature
, etc.
Again, these provided useful functionalities, but they can easily be defined again in a user’s config according to one’s particular needs for his note-taking practices.
I think they may add confusion and hide the composability/hackability of Denote. It is harder to feel confident that a custom function is the right way to support one’s need when there is already a function that does something similar.
Some other functions like denote-link-with-signature
are more
involved, but still simple enough to be part of a user’s configuration
rather than in Denote.
Reason: This is legacy in Denote, removed here.
Previously, it was possible to set denote-directory
to
default-directory
(or local
) in a .dir-locals.el
. Denote has been
fixed and it does not mention this possibility anymore. I have removed
it here.
Aliases and obsolete names have been removed.
Reason for removal: What this variable does can controlled through the
user option denote-file-types
.
denote-directory
has gained the ability to receive a list of paths.
This means that it is now possible to have notes spread across various
locations. They do not need to be under a common directory.
Internally, the code has been adapted to work with a list of
directories. The function denote-directories
returns the value of
denote-directory
as a list. The function denote-directory
returns
the first element of denote-directory
for backward compatability with
how it worked before.
New notes are created under the first element of denote-directory
. You
can still choose to be prompted for a directory if you want. See
denote-prompts
.
These variables are provided if a user wants to create a custom denote
command that already has specific title, keywords, etc. We do not want
any prompting, just use the keyword specified.
Here is an example of the idea:
(defun my-denote-journal-command ()
(let ((denote-forced-keywords '("journal"))
(denote-forced-subdirectory "~/notes/journal/")
(denote-prompts '(title)))
(call-interactively #'denote)))
In this example, we have created a journaling command that always create entries in the “journal/” subdirectory with the “journal” keyword and we are prompted for a title.
The two low-level functions denote--rename-file
and
denote--rename-get-file-info-from-prompts-or-existing
are the only
necessary building blocks to handle all renaming commands.
With these, the code base is a lot cleaner. There is no duplicated code
anymore. All commands obey the variables denote-prompts
,
denote-forced-*
and denote-rename-no-confirm
in the same way.
It is now also simple to implement your own renaming commands! Maybe you
would like a denote-rename-meta-data
that only renames the keywords and
the signature (but not the title)? It is a trivial change away from
denote-rename-keywords
.
Additionally, denote-rename-file
(and all deriving from it) gains the
ability to change the file-type and the note’s directory.
All other commands have been refactored as convenience commands that use the 2 lower-level functions above as building blocks.
Here is the list of affected commands and their new status:
denote-rename-file
: Refactored/simplified. This is still the main command, but its implementation is now trivial.denote-dired-rename-marked-files
: Refactored/simplified.denote-dired-rename-marked-files-with-keywords
: Refactored/simplified as a convenience.denote-rename-file-using-front-matter
: Provided as a convenience. However, its optional parameters have been dropped. They were not consistent with other commands anyway and it easy to create custom commands around this functionality.denote-dired-rename-marked-files-using-front-matter
: Refactored/simplified as a convenience.denote-keywords-{add/remove}
: Replaced withdenote-rename-file-keywords
.denote-rename-{add/remove}-signature
: Replaced withdenote-rename-file-signature
.denote-change-file-type
: Integrated intodenote-rename-file
. A convenience commanddenote-rename-file-type
is also provided.
File name components (identifier, title, keywords and signature) can be in any order.
Also, an item that is absent from this list will not be part of the file name. It will only be in the front matter, if applicable.
Note that even though we allow identifiers to be absent from file names, it is highly recommended to keep it. Otherwise, some functionalities may not work as expected (renaming commands) or at all (link to such a note).
TODO: Improve fontification
This fork is only available as a git repository. You can clone it and
configure it in your init.el
.
A Denote note is a single file with a specific file name structure. Additionally, text files can have a front matter handled by Denote.
Here is an example:
File name: 20240101T112314==z2a--my-note-example__emacs__philosophy.org
Content:
#title: My note example (raw title, ie not sluggified) #keywords: emacs philosophy (same as file name) #date: 2024-01-01
The file name contains an identifier, a signature, a title and keywords. All components are optional.
Any file (pdf, video, image) can be converted to this file naming scheme.
Text files can be of any extension if Denote is configured accordingly.
Denote can be seen as a package that helps organize simple text files by giving them some structure. This structure is flexible and can be adapted to one’s needs. For example, Denote provides commands to add keywords to files, a signature, rename a note, etc.
Additionally, Denote provide a linking mechanism between notes. It works just like links in Org-mode.
While the above is the heart of Denote and could be considered sufficient for a note-taking system, using Denote comes with other nice features. In a Dired buffer, the components of your notes will stand out, for instance. These extras facilities are explained later in this manual.
The core of Denote is its note creation, renaming and linking commands.
Creation/renaming:
denote
Renaming:
denote-rename-file
Linking:
denote-link
Link navigation commands:
denote-find-link
denote-find-backlinks
denote-backlinks
Read their docstring.
File creation commands:
denote-open-or-create
denote-region
denote-org-capture
Note linking commands
denote-link-or-create
denote-link-after-creating
denote-add-links
denote-link-dired-marked-notes
Renaming functions:
denote-dired-rename-files
denote-dired-rename-marked-files-with-keywords
denote-rename-file-using-front-matter
denote-dired-rename-marked-files-using-front-matter
denote
, denote-link
and denote-rename-file
commands are already usable
as building blocks to create custom commands specialized for one’s use
cases. Explanations and examples are provided in the next section.
Options to build custom commands:
denote-prompts
denote-directories
denote-link-description-function
denote-file-type
General configuration options:
denote-file-types
denote-file-name-slug-functions
denote-templates
denote-excluded-directories-regexp
denote-after-new-note-hook
denote-rename-file-hook
Other options:
denote-save-buffers
denote-sort-keywords
denote-date-prompt-use-org-read-date
denote-backlinks-show-context
denote-rename-confirmations
denote-infer-keywords
denote-excluded-keywords-regexp
While Denote comes with sensible defaults, it can be configured to handle one’s specific use cases.
Following Denote’s philosophy, various commands can be created specific to one’s needs. Here is the general template for such a command
(defun my-denote-link ()
(interactive)
(let ((denote-prompts '(title signature))
(denote-file-type 'md-yaml)
(denote-link-description-function (lambda () "Link")))
(call-interactively #'denote-link-or-create)))
When a user calls this custom command, the denote-link-or-create
function is called and doing all the work. The link descriptions will
always be “Link”. If the linked file does not exist, a new
note (of type md-yaml) will be created.
This is a general example highlighting the power of Denote and how it can be configured to meet one’s requirements for note-taking.
(setq denote-directory '("~/notes" "~/music"))
(setq denote-prompts '(title keywords signature))
(defun denote-file-type ()
(interactive)
(let ((denote-prompts '(title keywords file-type)))
(call-interactively #'denote)))
(defun denote-file-signature ()
(interactive)
(let ((denote-prompts '(title keywords signature)))
(call-interactively #'denote)))
(defun denote-in-work-silo ()
(interactive)
(let ((denote-directory '("~/work/"))
(denote-prompts '(title keywords file-type)))
(call-interactively #'denote)))
(defun denote-signature-in-work-silo ()
(interactive)
(let ((denote-directory '("~/work/"))
(denote-prompts '(title keywords signature)))
(call-interactively #'denote)))
(defun my-journal-command ()
(interactive)
(let ((denote-prompts '(title))
(denote-forced-keywords '("journal"))
(denote-forced-directory '()))
(call-interactively #'denote)))
(let ((map global-map))
;; Creation commands
(define-key map (kbd "C-c n n") #'denote)
(define-key map (kbd "C-c n t") #'denote-file-type)
(define-key map (kbd "C-c n s") #'denote-signature)
(define-key map (kbd "C-c w n") #'denote-in-work-silo)
(define-key map (kbd "C-c w s") #'denote-signature-in-work-silo)
(define-key map (kbd "C-c n j") #'my-journal-command)
;; Other commands
(define-key map (kbd "C-c n i") #'denote-link)
(define-key map (kbd "C-c n b") #'denote-backlinks)
(define-key map (kbd "C-c n f f") #'denote-find-link)
(define-key map (kbd "C-c n f b") #'denote-find-backlink)
(define-key map (kbd "C-c n r") #'denote-rename-file))
denote-directory
can be set in a .dir-locals.el file.
Let’s say you have a project directory. Under that directory, you want to have ssubdirectory containing notes. Anywhere in your project, you want to have access to be able to create notes in that subdirectory.
The solution is simple. You can have a .dir-locals.el
file at the root
of your project and with this content:
((nil . ((denote-directory . ("~/path/to/project/notes/")))))
(setq denote-prompts '(title signature file-type))
(defun my-custom-command ()
(interactive)
(let ((denote-prompts '(title))
(denote-forced-keywords '("journal"))
(denote-forced-directory '()))
(call-interactively #'denote)))
It is even possible to fix the title to display today’s information. See the next example.
This command does not prompt and creates a note titled “20240330T111111–saturday-30-march-2024__journal.txt” in the “journal/” subdirectory. If a note for that date already exists, open it instead.
(defun my-journal-command ()
(interactive)
(let ((existing-entry (car (denote-directories-files
(concat (format-time-string "%Y%m%d" (current-time)) "T[0-9]\\{6\\}.*__journal")))
(denote-prompts '())
(denote-file-type 'text)
(denote-forced-title (format-time-string "%A %-d %B %Y" (current-time)))
(denote-forced-keywords '("journal"))
(denote-forced-directory "path/to/notes/journal/"))
(if existing-entry
(find-file existing-entry)
(call-interactively #'denote)))))
You can use standard keymaps, transient or external packages (for example Hydra) to create a menu of custom commands.
The descriptions of links can be configured with
denote-link-description-function
.
Links can be made to look like:
- “Link”
- “20240101T111111–My-note.org”
- “My file title”
- “My file title (keyword1, keyword2)”
- “sig1z3 My other title” (the default if signature is present)
- “My other title (sig1z3)”
- A prompt for the description
- etc.
Check denote-templates
.
See the main example above.
The front matter can be customized through the variable
denote-file-types
.
Lines can be removed and reordered.
Denote comes with 4 supported files types (org, md-yaml, md-toml and
txt), but other formats may be supported. They must be configured with
denote-file-types
.
By default, when a new note is created, the title that is typed is “sluggified”. Spaces are converted to hyphens and special characters are removed. The original title is preserved in the front matter of the file.
This sluggification can be configured with
denote-file-name-slug-functions
for each component (title, keyword,
signature).
Denote is meant to be a collective effort. Every bit of help matters.
- Author/maintainer of Denote
- Protesilaos Stavrou.
- Contributions to code or the manual
- Abin Simon, Adam Růžička, Alan Schmitt, Ashton Wiersdorf, Benjamin Kästner, Bruno Boal, Charanjit Singh, Clemens Radermacher, Colin McLear, Damien Cassou, Eduardo Grajeda, Elias Storms, Eshel Yaron, Florian, Glenna D., Graham Marlow, Hilde Rhyne, Ivan Sokolov, Jack Baty, Jean-Charles Bagneris, Jean-Philippe Gagné Guay, Joseph Turner, Jürgen Hötzel, Kaushal Modi, Kai von Fintel, Kostas Andreadis, Kyle Meyer, Marc Fargas, Matthew Lemon, Noboru Ota (nobiot), Norwid Behrnd, Peter Prevos, Philip Kaludercic, Quiliro Ordóñez, Stefan Monnier, Stefan Thesing, Thibaut Benjamin, Tomasz Hołubowicz, Vedang Manerikar, Wesley Harvey, ezchi, leinfink (Henrik), mentalisttraceur, relict007.
- Ideas and/or user feedback
- Abin Simon, Aditya Yadav, Alan Schmitt, Aleksandr Vityazev, Alfredo Borrás, Ashton Wiersdorf, Benjamin Kästner, Claudiu Tănăselia, Colin McLear, Damien Cassou, Elias Storms, Federico Stilman, Florian, Frédéric Willem Frank Ehmsen, Glenna D., Guo Yong, Hanspeter Gisler, Jack Baty, Jay Rajput, Jean-Charles Bagneris, Jens Östlund, Jeremy Friesen, Jonathan Sahar, Johan Bolmsjö, Jousimies, Juanjo Presa, Kai von Fintel, Kaushal Modi, M. Hadi Timachi, Mark Olson, Mirko Hernandez, Niall Dooley, Paul van Gelder, Peter Prevos, Peter Smith, Suhail Singh, Shreyas Ragavan, Stefan Thesing, Summer Emacs, Sven Seebeck, Taoufik, TJ Stankus, Viktor Haag, Wade Mealing, Yi Liu, Ypot, atanasj, babusri, doolio, drcxd, hpgisler, pRot0ta1p, rbenit68, relict007, 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.