mirror of
https://github.com/protesilaos/denote.git
synced 2026-09-10 07:16:20 -04:00
7634 lines
336 KiB
Org Mode
7634 lines
336 KiB
Org Mode
#+title: denote: Simple notes with an efficient file-naming scheme
|
||
#+author: Protesilaos
|
||
#+email: info@protesilaos.com
|
||
#+language: en
|
||
#+options: ':t toc:nil author:t email:t num:t
|
||
#+startup: content
|
||
#+macro: stable-version 4.2.0
|
||
#+macro: release-date 2026-05-20
|
||
#+macro: development-version 4.3.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
|
||
#+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, 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-2026 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 configuration to get a feel for
|
||
some main features ([[#h:c54bedb4-5377-4dbd-853c-5870ace6eb33][Getting started with Denote]]). 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 docstring 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 plain text files (.txt), then you want to make the
|
||
;; Denote links clickable (Org mode and Markdown mode render links
|
||
;; as buttons right away and provide commands to open them)
|
||
(text-mode . denote-fontify-links-mode)
|
||
;; 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 docstring 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-keywords-to-not-infer-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
|
||
|
||
* Getting started with Denote
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:c54bedb4-5377-4dbd-853c-5870ace6eb33
|
||
:END:
|
||
|
||
At its core, Denote is a file-naming scheme ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]).
|
||
It is applies to new text files that Denote creates or to existing
|
||
files of any type (videos, PDFs, images, ...). Here is the big picture
|
||
view on how to get started with Denote:
|
||
|
||
1. Create new text files with =M-x denote= ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]).
|
||
2. Rename any file anywhere with =M-x denote-rename-file=. There are
|
||
many variations of this command, such as ~denote-dired-rename-marked-files~ ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]).
|
||
3. Link to another Denote file with a given file name using the
|
||
command =M-x denote-link= ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Add a single direct link using a file name prompt]]).
|
||
Or link to a file whose contents match a query with the command ~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]]).
|
||
4. From any file with a Denote file name, view backlinks with =M-x
|
||
denote-backlinks= ([[#h:c73f1f68-e214-49d5-b369-e694f6a5d708][The backlinks' buffer]]).
|
||
5. Instead of linking to a file, create a "link" to a search through
|
||
file contents with =M-x denote-query-contents-link=. This searches
|
||
the contents of text files in the ~denote-directory~. To only
|
||
search against file names (not file contents!), use =M-x
|
||
denote-query-filenames-link= ([[#h:d9a84289-2f73-4ef9-b4f0-9a0aa3e9bf0d][Add a query link]]).
|
||
6. To produce a Dired buffer with matching files and preferred
|
||
sorting, do =M-x denote-dired= ([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Display filtered and sorted files with ~denote-sort-dired~ or ~denote-dired~]]).
|
||
7. To search directly in the contents of your files, do =M-x
|
||
denote-grep= ([[#h:e71c9d14-7e88-4386-91d0-9ad249947077][Use ~denote-grep~ to search inside files]]).
|
||
8. To use shorter buffer names for Denote files, enable the ~denote-rename-buffer-mode~ ([[#h:3ca4db16-8f26-4d7d-b748-bac48ae32d69][Automatically rename Denote file buffers]]).
|
||
|
||
There are many more features documented in this manual. Also, we have
|
||
standalone packages that provide support for Org dynamic blocks (part
|
||
of the ~denote-org~ package), fully fledged journaling with
|
||
~denote-journal~, writing of "sequence notes" with ~denote-sequence~,
|
||
and many more ([[#h:08b14682-e73f-4b11-b2e2-be3b788c8572][Packages that build on Denote]])!
|
||
|
||
* 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).
|
||
|
||
A typical note produced by Denote looks like:
|
||
|
||
: 20240322T131856--some-title__topic1_topic2.org
|
||
|
||
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 docstring
|
||
for the technicalities.
|
||
|
||
#+findex: denote-create-note
|
||
In the interest of discoverability, ~denote~ is also available under the
|
||
alias ~denote-create-note~.
|
||
|
||
*** The ~denote-directory~ option
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:e2809afb-5c6c-4a20-9881-00c987bb5345
|
||
:END:
|
||
|
||
#+vindex: denote-directory
|
||
The Denote file-naming scheme can be applied to any file anywhere on
|
||
the file system ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]). Still, Denote needs a dedicated
|
||
directory to store the files it creates. To this end, the user option
|
||
~denote-directory~ specifies the directory, as a string, where new
|
||
files are created ([[#h:6a92a8b5-d766-42cc-8e5b-8dc255466a23][Standard note creation]]).
|
||
|
||
The default path is =~/Documents/notes/=. If the directory does not
|
||
exist, Denote will create it upon generating a new note.
|
||
|
||
The ~denote-directory~ is consulted by Denote commands that operate on
|
||
existing files, such as the linking mechanism and the search mechanism:
|
||
|
||
- [[#h:fc913d54-26c8-4c41-be86-999839e8ad31][Linking notes]]
|
||
- [[#h:e71c9d14-7e88-4386-91d0-9ad249947077][Use ~denote-grep~ to search inside files]]
|
||
|
||
In general, any command that needs to operate on existing Denote files
|
||
is expected to rely on the ~denote-directory~.
|
||
|
||
The value of ~denote-directory~ may also be a list of strings, each
|
||
pointing to a file system path. This allows users to maintain distinct
|
||
directories that can still be considered part of the same continuum,
|
||
as opposed to silos ([[#h:15719799-a5ff-4e9a-9f10-4ca03ef8f6c5][Maintain separate directory silos for notes]]).
|
||
Denote commands will otherwise behave the same way.
|
||
|
||
Users who choose to set ~denote-directory~ to a list of directories
|
||
may also want to include a directory prompt when creating new files
|
||
([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The ~denote-prompts~ option]]).
|
||
|
||
*** 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 directory or
|
||
subdirectory in which to create the note. Available candidates are
|
||
the value of the user option ~denote-directory~ and all of its
|
||
subdirectories ([[#h:e2809afb-5c6c-4a20-9881-00c987bb5345][The ~denote-directory~ option]]). 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]].
|
||
|
||
- =identifier=: Prompts with completion for the identifier of the new
|
||
note. It expects a string that has the format of ~denote-date-identifier-format~.
|
||
[ Also see: [[#h:197f83af-6149-4262-a186-14d0eab24f1e][A custom identifier that is a simple string given at the prompt each time]]. ]
|
||
|
||
- =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 its own 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]]):
|
||
|
||
: ID--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:
|
||
|
||
: ID.EXT
|
||
: ID--TITLE.EXT
|
||
: ID__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 "docstring")
|
||
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-buffers~ controls whether commands that
|
||
create new notes ([[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]) or rename existing files save their
|
||
buffer outright ([[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]). By default, when the value of this
|
||
user option is ~nil~, buffers are not saved. They are saved when the
|
||
value is non-~nil~.
|
||
|
||
[[#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~ and ~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 set to a non-~nil~ value ([[#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~.
|
||
|
||
Developers or advanced users who ~let~ bind the ~denote-use-template~
|
||
must consider setting ~denote-org-capture-specifiers~ to nil or an
|
||
appropriate string ([[#h:a8b9082c-8bd5-492d-8da7-4f41ee493d59][Predefined note values for developers or advanced users]]).
|
||
Otherwise the contents of the new note may contain duplicates.
|
||
|
||
[ 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=,
|
||
=TEMPLATE=, and =SIGNATURE= 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 described elsewhere in
|
||
this manual ([[#h:a8b9082c-8bd5-492d-8da7-4f41ee493d59][Predefined note values for developers or advanced users]]).
|
||
When there exists a binding for those 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:98c732ac-da0e-4ebd-a0e3-5c47f9075e51][Choose which commands to prompt for]]).
|
||
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=. It may also be a
|
||
list of related directories ([[#h:e2809afb-5c6c-4a20-9881-00c987bb5345][The ~denote-directory~ option]]). 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 and unrelated
|
||
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-keywords-to-not-infer-regexp
|
||
The user option ~denote-keywords-to-not-infer-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
|
||
|
||
Setting ~denote-infer-keywords~ to ~nil~ also makes the
|
||
~denote-keywords-prompt~ demand that the input matches one of the
|
||
keywords in ~denote-known-keywords~. In previous versions, the prompt
|
||
would accept arbitrary text.
|
||
|
||
** 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 changing only its identifier
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:10c16536-0535-4b4c-b0dc-d106bdc951a1
|
||
:END:
|
||
|
||
#+findex: denote-rename-file-identifier
|
||
The command ~denote-rename-file-identifier~ prompts for a new
|
||
identifier to apply to the given file. The current identifier is added
|
||
as the initial minibuffer text. An empty input means to remove the
|
||
identifier altogether.
|
||
|
||
An identifier that is already used in links cannot be modified,
|
||
otherwise all links will break.
|
||
|
||
** 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]]):
|
||
|
||
: @@ID==SIGNATURE--TITLE__KEYWORDS.EXTENSION
|
||
|
||
The =ID= field represents the identifier which, by default, is 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=. When it has this format
|
||
(the default), the delimiter (=@@=) is optional. The =ID= serves as
|
||
the unique identifier of each note and, as such, is also known as the
|
||
file's ID or identifier. The =ID= can be any string: it does not need
|
||
to be associated with a date ([[#h:3048f558-7d84-45d6-9ef2-53055483e801][Define a completely custom identifier scheme]]).
|
||
|
||
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 can contain any character, though the default is to have it
|
||
downcased and 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 docstring 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]].
|
||
|
||
All file name fields can contain any character, are optional and can
|
||
appear in any order. The following permutations are valid:
|
||
|
||
: ID.EXT
|
||
: ID--TITLE.EXT
|
||
: ID__KEYWORDS.EXT
|
||
: ID==SIGNATURE.EXT
|
||
: ID==SIGNATURE--TITLE.EXT
|
||
: ID==SIGNATURE--TITLE__KEYWORDS.EXT
|
||
: ID==SIGNATURE__KEYWORDS.EXT
|
||
|
||
Commands such as ~denote~ or ~denote-rename-file~ will prompt for the
|
||
file name fields configured in ~denote-prompts~.
|
||
|
||
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~: By default, 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 to 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. Note that even though all four
|
||
symbols must appear in the user option
|
||
~denote-file-name-components-order~, actual files may not contain a
|
||
specific component. For instance, a note without keywords or signature
|
||
is valid.
|
||
|
||
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 prompted for in creation or renaming commands
|
||
([[#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.
|
||
|
||
*** Retroactively reorder the components of Denote file names
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:c10d2b5f-edb7-4bdf-bc0d-a7c7c91064bf
|
||
:END:
|
||
|
||
While experimenting with the ~denote-file-name-components-order~ to
|
||
change the way Denote formats file names, you may end up with files
|
||
that look inconsistent. They still work the same way as before, but
|
||
you prefer to enforce the same order across your Denote files. The
|
||
following custom command does just that. It technically "renames" the
|
||
files it is applied to, though only insofar as reordering their Denote
|
||
file name components, but not changing the contents of those
|
||
components.
|
||
|
||
#+begin_src emacs-lisp
|
||
(defun my-denote-rename-all-to-reorder-components ()
|
||
"Call `denote-dired-rename-files' without any prompts.
|
||
In other words, preserve the value of each Denote file name component.
|
||
|
||
Use this command if you want to modify the user option
|
||
`denote-file-name-components-order' and then want your files to
|
||
retroactively follow that order."
|
||
(interactive)
|
||
(let ((denote-prompts nil))
|
||
(call-interactively 'denote-dired-rename-files)))
|
||
#+end_src
|
||
|
||
Simply go to a Dired buffer, mark the files you wish to operate on,
|
||
and call the command. You may also use the ~denote-dired~ command to
|
||
produce a filtered+sorted flat listing of your Denote files, mark
|
||
them, and then invoke ~my-denote-rename-all-to-reorder-components~
|
||
([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Display filtered and sorted files with ~denote-sort-dired~ or ~denote-dired~]]).
|
||
|
||
Video demonstration: <https://protesilaos.com/codelog/2025-07-09-emacs-denote-reorder-components/>.
|
||
|
||
DEVELOPMENT NOTE: If you have a more specific use-case, then we can
|
||
discuss it and expand the documentation we provide herein.
|
||
|
||
** Sluggification of file name components
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:ae8b19a1-7f67-4258-96b3-370a72c43f4e
|
||
:END:
|
||
|
||
File 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:
|
||
+ Many "special characters" are removed.
|
||
|
||
+ Input for a file title is hyphenated. The original value is
|
||
preserved in the note's content ([[#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:
|
||
|
||
: ID--TITLE__KEYWORDS.EXTENSION
|
||
|
||
When a signature is present, this becomes:
|
||
|
||
: ID==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.
|
||
|
||
** Define a completely custom identifier scheme
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:3048f558-7d84-45d6-9ef2-53055483e801
|
||
:END:
|
||
|
||
[ DEVELOPER NOTE: This is for advanced users. If you really need
|
||
custom identifiers but are not sure how to proceed, please ask and I
|
||
will help you. ]
|
||
|
||
By default, the Denote identifier consists of the date and time in a
|
||
compact format ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). Denote, however, can work
|
||
with identifiers of an arbitrary format, provided each identifier is
|
||
unique. Any string will do.
|
||
|
||
#+vindex: denote-get-identifier-function
|
||
The variable ~denote-get-identifier-function~ specifies the function
|
||
that Denote uses to derive a new identifier. The return value is a
|
||
string. This function is called while creating new files and when
|
||
renaming an existing file:
|
||
|
||
- [[#h:17896c8c-d97a-4faa-abf6-31df99746ca6][Points of entry]]
|
||
- [[#h:532e8e2a-9b7d-41c0-8f4b-3c5cbb7d4dca][Renaming files]]
|
||
|
||
#+vindex: denote-used-identifiers
|
||
The function assigned to ~denote-get-identifier-function~ takes two
|
||
arguments: an initial identifier and a date. The initial identifier is
|
||
used as a reference to derive a unique variant of it (e.g. to keep
|
||
incrementing seconds while keeping the rest of the date+time the
|
||
same). Existing identifiers are stored in the variable
|
||
~denote-used-identifiers~. If the initial identifier is nil or an
|
||
identifier cannot be derived from it, then the date can be used
|
||
instead. The date has the same format as ‘current-time’. When it is
|
||
nil, the ‘current-time’ is used.
|
||
|
||
In the following code block, we provide an example of an identifier
|
||
scheme that encodes information related to publications: the year, the
|
||
edition, the author or authors, the format (e.g. book or paper), and
|
||
the publisher. For users who maintain a library of academic works, for
|
||
example, this kind of identifier is more useful than the default
|
||
date+time scheme.
|
||
|
||
*** A custom identifier that is a simple incrementing number scheme
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:8b8a4fa9-09fe-4d5c-a003-8808fe292e8b
|
||
:END:
|
||
|
||
This is a sample implementation of custom Denote identifiers ([[#h:3048f558-7d84-45d6-9ef2-53055483e801][Define a completely custom identifier scheme]]).
|
||
|
||
In its simplest form, a custom Denote identifier can be an integer
|
||
that is incremented each time a new file name is produced. In the
|
||
following code block, ~my~denote-get-identifier~ is the function we
|
||
bind to the variable ~denote-get-identifier-function~, which then
|
||
makes Denote use the custom identifier scheme.
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq denote-get-identifier-function #'my-denote-get-identifier)
|
||
|
||
(defun my-denote--get-all-used-ids ()
|
||
(when-let* ((ids (make-hash-table :test 'equal))
|
||
(files (denote-directory-files nil nil nil nil :has-identifier))
|
||
(file-names (mapcar #'file-name-nondirectory 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 my-denote-get-identifier (initial-identifier _date)
|
||
(let ((identifiers (or denote-used-identifiers (my-denote--get-all-used-ids))))
|
||
(cond
|
||
((and initial-identifier (not (gethash initial-identifier identifiers)))
|
||
initial-identifier)
|
||
(t
|
||
(number-to-string
|
||
(let ((keys nil))
|
||
;; NOTE 2025-09-19: The `string-to-number' is done with the
|
||
;; assumption that this is always an integer. It will break
|
||
;; with the default Denote identifier scheme or, indeed,
|
||
;; anything that is not strictly numeric.
|
||
(when identifiers (maphash (lambda (key value) (push (string-to-number key) keys)) identifiers))
|
||
(if keys
|
||
(+ (apply #'max keys) 1)
|
||
1)))))))
|
||
|
||
|
||
|
||
;; And this is for testing purposes
|
||
|
||
;; (let ((denote-directory "/tmp/"))
|
||
;; (my-denote-get-identifier nil nil))
|
||
;;
|
||
;; (let* ((denote-used-identifiers (make-hash-table :test #'equal))
|
||
;; (_ (progn
|
||
;; (puthash "1" 1 denote-used-identifiers)
|
||
;; (puthash "2" 2 denote-used-identifiers)
|
||
;; (puthash "3" 3 denote-used-identifiers))))
|
||
;; (my-denote-get-identifier "1" nil))
|
||
#+end_src
|
||
|
||
An alternative for the above:
|
||
|
||
#+begin_src emacs-lisp
|
||
(defun my-denote--find-first-unused-id-as-number (id)
|
||
(while (gethash id denote-used-identifiers)
|
||
(setq id (number-to-string (1+ (string-to-number id)))))
|
||
id)
|
||
|
||
(defun my-denote-generate-identifier-as-number (initial-identifier _date)
|
||
(let ((denote-used-identifiers (or denote-used-identifiers (denote--get-all-used-ids))))
|
||
(cond (;; Always use the supplied initial-identifier if possible,
|
||
;; regardless of format.
|
||
(and initial-identifier
|
||
(not (gethash initial-identifier denote-used-identifiers)))
|
||
initial-identifier)
|
||
(;; If the supplied initial-identifier is already used, but
|
||
;; it has the right format, make is unique.
|
||
(and initial-identifier
|
||
(string-match-p "[1-9][0-9]*" initial-identifier))
|
||
(my-denote--find-first-unused-id-as-number initial-identifier))
|
||
(;; Else, the supplied initial-identifier is nil or it is
|
||
;; already used or it does not match the supplied
|
||
;; format. Ignore it and generate a valid identifier with
|
||
;; the right format.
|
||
t
|
||
(my-denote--find-first-unused-id-as-number "1")))))
|
||
#+end_src
|
||
|
||
*** A custom identifier that is a simple string given at the prompt each time
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:197f83af-6149-4262-a186-14d0eab24f1e
|
||
:END:
|
||
|
||
This is a sample implementation of custom Denote identifiers ([[#h:3048f558-7d84-45d6-9ef2-53055483e801][Define a completely custom identifier scheme]]).
|
||
|
||
While it is possible to implement a certain automatic scheme for the
|
||
Denote identifier, users may prefer to have a prompt each time that
|
||
asks them for a simple string which is then used as-is. This can be
|
||
done by modifying the user option ~denote-prompts~ to include the
|
||
=identifier= in the list of file name components to prompt for ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The ~denote-prompts~ option]]).
|
||
|
||
For example:
|
||
|
||
#+begin_src emacs-lisp
|
||
;; The default is '(title keywords)
|
||
(setq denote-prompts '(identifier title keywords))
|
||
#+end_src
|
||
|
||
With the above, each time Denote needs to produce a new file name it
|
||
will prompt for the identifier. Note that it is then up to the user to
|
||
guarantee the uniqueness of the resulting identifiers. With this code,
|
||
Denote will fall back to using the current date as the identifier if
|
||
the provided string is not yielding a unique match.
|
||
|
||
In practice, the identifier prompt is a useful tool for wrapper
|
||
functions that need to do something special in a given context. For
|
||
example, in a folder with some ancient photos the user wants to have
|
||
an identifier scheme that is =PLACE+PEOPLE+N= where it is easier to
|
||
prompt for that value each time than write some extra code to do the
|
||
work. To this end, here is a sample wrapper function of ~denote-rename-file~:
|
||
|
||
#+begin_src emacs-lisp
|
||
(defun my-denote-rename-file-with-identifier-prompt ()
|
||
(interactive)
|
||
(let ((denote-prompts '(identifier title keywords)))
|
||
(call-interactively 'denote-rename-file)))
|
||
#+end_src
|
||
|
||
*** A custom identifier to include the day of the week as a capital letter
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:5bd49896-b0d4-4176-ad7b-27285182bdb2
|
||
:END:
|
||
|
||
The default Denote identifier is a compact version of the date and
|
||
time. The letter =T= is placed between the two to delimit where the
|
||
date ends and the time starts. This =T= does not have any special
|
||
meaning in itself. Users may prefer to have other letters there to
|
||
represent the day of the week. For example, Monday is =A=, Tuesday is
|
||
=B=, and so on.
|
||
|
||
Here is a function that takes a number or numeric string argument and
|
||
returns the corresponding capital letter for the day of the week. We
|
||
will see further below how this function goes together with the rest
|
||
of the code necessary for a custom identifier scheme.
|
||
|
||
#+begin_src emacs-lisp
|
||
(defun my-denote-get-day-of-week-as-alpha (day)
|
||
"Return the corresponding capital letter for DAY.
|
||
DAY is a string holding a number, as the return value of the %u
|
||
specifier in `format-time-string'.
|
||
|
||
If DAY is a number, convert it to a string and then return its
|
||
corresponding letter."
|
||
(pcase (if (numberp day) (number-to-string day) day)
|
||
("1" "A")
|
||
("2" "B")
|
||
("3" "C")
|
||
("4" "D")
|
||
("5" "E")
|
||
("6" "F")
|
||
("7" "G")
|
||
(_ (error "The day `%S' is not among 1-7" day))))
|
||
#+end_src
|
||
|
||
What we then need is to have a variant of the standard ~denote-date-identifier-format~
|
||
where the letter =T= is replaced by the =%u= ~format-time-string~
|
||
specifier. That is what gives us the numeric representation of the day
|
||
of the week. Then we copy the default ~denote-generate-identifier-as-date~
|
||
and modify it to use our format, making the small adjustments
|
||
necessary to recreate the identifier as one string.
|
||
|
||
Putting it all together:
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq denote-get-identifier-function #'my-denote-generate-identifier-as-date)
|
||
|
||
(defun my-denote-get-day-of-week-as-alpha (day)
|
||
"Return the corresponding capital letter for DAY.
|
||
DAY is a string holding a number, as the return value of the %u
|
||
specifier in `format-time-string'.
|
||
|
||
If DAY is a number, convert it to a string and then return its
|
||
corresponding letter."
|
||
(pcase (if (numberp day) (number-to-string day) day)
|
||
("1" "A")
|
||
("2" "B")
|
||
("3" "C")
|
||
("4" "D")
|
||
("5" "E")
|
||
("6" "F")
|
||
("7" "G")
|
||
(_ (error "The day `%S' is not among 1-7" day))))
|
||
|
||
(defconst my-denote-date-identifier-format
|
||
(replace-regexp-in-string "T" " %u " denote-date-identifier-format t))
|
||
|
||
(defun my-denote-format-date (date)
|
||
"Format DATE using `my-denote-date-identifier-format'."
|
||
(pcase-let* ((identifier-string (format-time-string my-denote-date-identifier-format date))
|
||
(`(,date ,day-of-week ,time) (split-string identifier-string))
|
||
(day-as-letter (my-denote-get-day-of-week-as-alpha day-of-week)))
|
||
(concat date day-as-letter time)))
|
||
|
||
(defun my-denote-generate-identifier-as-date (initial-identifier date)
|
||
"Generate an identifier based on DATE.
|
||
|
||
If INITIAL-IDENTIFIER is not already used, return it. Else, if it is
|
||
possible to derive an identifier from it, return this identifier.
|
||
|
||
Else, use the DATE. If it is nil, use `current-time'.
|
||
|
||
Slightly modified version of `denote-generate-identifier-as-date'."
|
||
(let ((denote-used-identifiers (or denote-used-identifiers (denote--get-all-used-ids))))
|
||
(cond ((and initial-identifier
|
||
(not (gethash initial-identifier denote-used-identifiers)))
|
||
initial-identifier)
|
||
((and initial-identifier
|
||
(string-match-p denote-date-identifier-regexp initial-identifier)
|
||
(date-to-time initial-identifier))
|
||
(denote--find-first-unused-id-as-date initial-identifier))
|
||
(t
|
||
(denote--find-first-unused-id-as-date
|
||
(my-denote-format-date (or date (current-time))))))))
|
||
#+end_src
|
||
|
||
*** A custom identifier without the time separator
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:d238cd1f-a185-4dbe-a9f3-0dea29273e1f
|
||
:END:
|
||
|
||
The default Denote identifier consists of the date as =YYYYMMDD=, then
|
||
the letter =T=, and then the time as =HHMMSS=. Users who do not want
|
||
the letter =T= there can use the following:
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq denote-get-identifier-function #'my-denote-generate-identifier-without-time-separator)
|
||
|
||
(defconst my-denote-date-identifier-format-without-time-separator
|
||
(replace-regexp-in-string "T" "" denote-date-identifier-format))
|
||
|
||
(defun my-denote-generate-identifier-without-time-separator (initial-identifier date)
|
||
"Generate an identifier based on DATE.
|
||
|
||
If INITIAL-IDENTIFIER is not already used, return it. Else, if it is
|
||
possible to derive an identifier from it, return this identifier.
|
||
|
||
Else, use the DATE. If it is nil, use `current-time'.
|
||
|
||
Slightly modified version of `denote-generate-identifier-as-date'."
|
||
(let ((denote-used-identifiers (or denote-used-identifiers (denote--get-all-used-ids))))
|
||
(cond ((and initial-identifier
|
||
(not (gethash initial-identifier denote-used-identifiers)))
|
||
initial-identifier)
|
||
((and initial-identifier
|
||
(string-match-p denote-date-identifier-regexp initial-identifier)
|
||
(date-to-time initial-identifier))
|
||
(denote--find-first-unused-id-as-date initial-identifier))
|
||
(t
|
||
(denote--find-first-unused-id-as-date
|
||
(format-time-string my-denote-date-identifier-format-without-time-separator (or date (current-time))))))))
|
||
#+end_src
|
||
|
||
*** A custom identifier to encode publisher data
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:f92cd454-51f4-4403-9061-3b2efaef2851
|
||
:END:
|
||
|
||
This is a sample implementation of custom Denote identifiers ([[#h:3048f558-7d84-45d6-9ef2-53055483e801][Define a completely custom identifier scheme]]).
|
||
|
||
This custom identifier cannot be derived programmatically though, so
|
||
the function ~my-denote-get-identifier~ makes a series of prompts to
|
||
get the relevant data. Then it joins them all together with the
|
||
current date to create a unique identifier. The helper function
|
||
~my-denote-custom-identifier--make-unique-identifier~ makes sure to
|
||
make small tweaks to the "current date" part of this identifier so
|
||
that it is always unique.
|
||
|
||
We can always flesh out this example with, for example, prompts that
|
||
leverage ~completing-read~, though the point is to show how custom
|
||
identifiers can be implemented in full. Everything else Denote does
|
||
with identifiers, namely, links and backlinks, should work as
|
||
intended.
|
||
|
||
Good luck!
|
||
|
||
#+begin_src emacs-lisp
|
||
;; NOTE 2025-09-18: This is for testing the following custom code.
|
||
;; The dates will need to be updated to match the test day.
|
||
;;
|
||
;; (let* ((denote-used-identifiers (make-hash-table :test #'equal))
|
||
;; (_ (progn
|
||
;; (puthash "2000E1AprotFbookPmaxT20250918" t denote-used-identifiers)
|
||
;; (puthash "2000E1AprotFbookPmaxT20250919" t denote-used-identifiers)
|
||
;; (puthash "2000E1AprotFbookPmaxT20250920" t denote-used-identifiers))))
|
||
;; (my-denote-get-identifier "2000E1AprotFbookPmaxT20250918" nil))
|
||
;;
|
||
;; (let* ((denote-used-identifiers (make-hash-table :test #'equal))
|
||
;; (_ (progn
|
||
;; (puthash "2000E1AprotFbookPmaxT20250918" t denote-used-identifiers)
|
||
;; (puthash "2000E1AprotFbookPmaxT20250919" t denote-used-identifiers)
|
||
;; (puthash "2000E1AprotFbookPmaxT20250920" t denote-used-identifiers))))
|
||
;; (my-denote-custom-identifier--make-unique-identifier "2000E1AprotFbookPmax" (format-time-string "%Y%m%d") denote-used-identifiers))
|
||
|
||
|
||
|
||
|
||
;; The code herein is a custom function for use with `denote-get-identifier-function':
|
||
(setq denote-get-identifier-function #'my-denote-get-identifier)
|
||
|
||
(defun my-denote-custom-identifier--make-unique-identifier (base-identifier date-string identifiers)
|
||
(let* ((current-date-string date-string)
|
||
(id (format "%sT%s" base-identifier current-date-string)))
|
||
(while (gethash id identifiers)
|
||
(let* ((year (string-to-number (substring current-date-string 0 4)))
|
||
(month (string-to-number (substring current-date-string 4 6)))
|
||
(day (string-to-number (substring current-date-string 6 8)))
|
||
(time (encode-time 0 0 0 day month year))
|
||
(next-day-time (seconds-to-time (+ (time-to-seconds time) 86400)))
|
||
(next-date-string (format-time-string "%Y%m%d" next-day-time)))
|
||
(setq current-date-string next-date-string)
|
||
(setq id (format "%sT%s" base-identifier current-date-string))))
|
||
id))
|
||
|
||
(defvar my-denote-custom-identifier-year-history nil)
|
||
(defvar my-denote-custom-identifier-edition-history nil)
|
||
(defvar my-denote-custom-identifier-format-history nil)
|
||
(defvar my-denote-custom-identifier-publisher-history nil)
|
||
|
||
(defvar my-denote-custom-identifier-author-history nil
|
||
"Minibuffer history for `my-denote-custom-identifier-author-prompt'.")
|
||
|
||
;; TODO 2025-09-18: Flesh out the other prompts as well. Some can
|
||
;; benefit from a `completing-read' approach.
|
||
(defun my-denote-custom-identifier-author-prompt ()
|
||
"Prompt for one or more authors.
|
||
Multiple authors are separated by a space or comma."
|
||
(read-string "Author (space or comma for many): " nil 'my-denote-custom-identifier-author-history))
|
||
|
||
(defun my-denote-custom-identifier-author-format (author)
|
||
"Format AUTHOR to use an A delimiter before each name."
|
||
(let ((authors (split-string author "[ ,]+" t "[\s\f\t\n\r\v]+")))
|
||
(format "A%s" (mapconcat #'identity authors "A"))))
|
||
|
||
(defun my-denote-get-identifier (initial-identifier date)
|
||
(let ((denote-used-identifiers (or denote-used-identifiers (denote--get-all-used-ids))))
|
||
(cond
|
||
((and initial-identifier (not (gethash initial-identifier denote-used-identifiers)))
|
||
initial-identifier)
|
||
(t
|
||
(let ((base-id (concat
|
||
(read-string "Year: " nil 'my-denote-custom-identifier-year-history)
|
||
"E" (read-string "Edition: " nil 'my-denote-custom-identifier-edition-history)
|
||
(my-denote-custom-identifier-author-format (my-denote-custom-identifier-author-prompt))
|
||
"F" (read-string "Format: " nil 'my-denote-custom-identifier-format-history)
|
||
"P" (read-string "Publisher: " nil 'my-denote-custom-identifier-publisher-history))))
|
||
(my-denote-custom-identifier--make-unique-identifier
|
||
base-id
|
||
(format-time-string "%Y%m%d" (or date (current-time)))
|
||
denote-used-identifiers))))))
|
||
#+end_src
|
||
|
||
* 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 set to
|
||
the =org= symbol, which is the default):
|
||
|
||
#+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 docstring 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
|
||
#+findex: denote-link-to-all-files-with-regexp
|
||
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 docstring 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 ([[#h:98c732ac-da0e-4ebd-a0e3-5c47f9075e51][Choose which commands to prompt for]]). 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.
|
||
|
||
#+findex: denote-link-or-create-with-command
|
||
+ ~denote-link-or-create-with-command~ :: This command is the same
|
||
concept as ~denote-link-or-create~ except it prompts for the
|
||
specific command for creating a new note ([[#h:98c732ac-da0e-4ebd-a0e3-5c47f9075e51][Choose which commands to prompt for]]).
|
||
|
||
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 or the more powerful ~denote-find-backlink-with-location~
|
||
([[#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]]).
|
||
|
||
#+findex: denote-find-backlink-with-location
|
||
As an alternative to ~denote-find-backlink~, the command
|
||
~denote-find-backlink-with-location~ shows the complete results of all
|
||
backlinks in their context, grouped by file. It prompts to pick one
|
||
among them, using minibuffer completion, and then jumps to the
|
||
corresponding file at that exact location where the link is.
|
||
|
||
Think of the ~denote-find-backlink-with-location~ as the
|
||
minibuffer-centric variant of the backlinks buffer: the backlinks
|
||
buffer sticks around, whereas this one is only available for the
|
||
duration of the command. Depending on the scenario, one is more
|
||
appropriate than the other.
|
||
|
||
** 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 mode and Markdown mode
|
||
buffers ([[#h:5e5e3370-12ab-454f-ba09-88ff44214324][Adding a single link]]). This means that the given major mode
|
||
recognises the link and applies to it the relevant properties that
|
||
make it clickable/actionable. The generic ~text-mode~ (for =.txt=
|
||
files) does not have this capability, hence the ~denote-fontify-links-mode~:
|
||
it makes all Denote links actionable with the mouse, =RET=, or =C-c C-o=.
|
||
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)
|
||
#+end_src
|
||
|
||
Fontified links can be opened with =RET=, =C-c C-o=, or a mouse click
|
||
(=C-c C-o= is also the default for Org and Markdown).
|
||
|
||
** 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 two argument, the file and file type, 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).
|
||
|
||
For backward compatibility, the function can also take a single
|
||
parameter, the given file. In that case, it is responsible for
|
||
figuring out the file type in order to return the correct description.
|
||
|
||
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).
|
||
|
||
* Set the prefix of all Denote buffers
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:a3a1a2af-a0b8-420c-acb1-855a7d113e69
|
||
:END:
|
||
|
||
#+vindex: denote-buffer-name-prefix
|
||
The user option ~denote-buffer-name-prefix~ specifies the text used as
|
||
a prefix of all buffer names generated by Denote. This applies to the
|
||
buffer of query links, backlinks, ~denote-grep~, ~denote-dired~, as
|
||
well as those affected by ~denote-rename-buffer-mode~:
|
||
|
||
- [[#h:d9a84289-2f73-4ef9-b4f0-9a0aa3e9bf0d][Add a query link]].
|
||
- [[#h:c73f1f68-e214-49d5-b369-e694f6a5d708][The backlinks' buffer]].
|
||
- [[#h:e71c9d14-7e88-4386-91d0-9ad249947077][Use ~denote-grep~ to search inside files]].
|
||
- [[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Display filtered and sorted files with ~denote-sort-dired~ or ~denote-dired~]].
|
||
- [[#h:3ca4db16-8f26-4d7d-b748-bac48ae32d69][Automatically rename Denote file buffers]].
|
||
|
||
By default, the value of ~denote-buffer-name-prefix~ is ="[D] "=.
|
||
Users can set it to any string they want, such as ="<Denote> "=, or
|
||
even an empty string to not have any such prefix.
|
||
|
||
* Automatically rename Denote file 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~, combined with
|
||
the ~denote-buffer-name-prefix~ ([[#h:a3a1a2af-a0b8-420c-acb1-855a7d113e69][Set the prefix of all Denote buffers]]).
|
||
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. It also affects the ~denote-rename-buffer-mode~
|
||
([[#h:3ca4db16-8f26-4d7d-b748-bac48ae32d69][Automatically rename Denote file buffers]]).
|
||
|
||
The value of this user option is a string that gets combined with the
|
||
~denote-buffer-name-prefix~ ([[#h:a3a1a2af-a0b8-420c-acb1-855a7d113e69][Set the prefix of all Denote buffers]]).
|
||
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 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~.
|
||
|
||
** Focus the ~denote-dired~ buffer with ~denote-dired-focus~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:738b4895-00e3-47a5-a1de-bb0eeef93998
|
||
:END:
|
||
|
||
#+findex: denote-dired-focus
|
||
The command ~denote-dired-focus~ gives users the option to further
|
||
filter an existing Dired buffer that was produced by the
|
||
~denote-dired~ command ([[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Display filtered and sorted files with ~denote-sort-dired~ or ~denote-dired~]]).
|
||
|
||
Call ~denote-dired-focus~ from inside such a Dired buffer to further
|
||
limit its contents to files matching the given regular expression or
|
||
plain string. This is not a new query. It builds on top of the current
|
||
Dired buffer's file list to operate only on those files.
|
||
|
||
Call ~denote-dired-focus~ with a prefix argument (=C-u= by default) to
|
||
invert the logic: all the files that match the given regular
|
||
expression are removed from the Dired buffer.
|
||
|
||
** 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=, =signature=, =identifier=,
|
||
=random=, or =last-modified= with the ~denote-dired~ command (alias ~denote-sort-dired~),
|
||
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]]).
|
||
|
||
With the exception of =random= and =last-modified=, the sorting of
|
||
file name components is subject to user configuration:
|
||
|
||
#+vindex: denote-sort-identifier-comparison-function
|
||
- ~denote-sort-identifier-comparison-function~ :: The function to sort
|
||
by the file name identifier.
|
||
|
||
#+vindex: denote-sort-title-comparison-function
|
||
- ~denote-sort-title-comparison-function~ :: The function to sort by
|
||
the file name title.
|
||
|
||
#+vindex: denote-sort-keywords-comparison-function
|
||
- ~denote-sort-keywords-comparison-function~ :: The function to sort
|
||
by the file name keywords.
|
||
|
||
#+vindex: denote-sort-signature-comparison-function
|
||
- ~denote-sort-signature-comparison-function~ :: The function to sort
|
||
by the file name signature.
|
||
|
||
By default, all these user options use the same comparison 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:
|
||
|
||
#+vindex: denote-query-format-heading-function
|
||
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. File names
|
||
are formatted according to the function bound to the user option
|
||
~denote-query-format-heading-function~. By default, file names are
|
||
rendered relative to the ~denote-directory~.
|
||
|
||
- [[#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.
|
||
|
||
#+findex: denote-query-sort-last-search
|
||
- ~denote-query-sort-last-search~ :: Change the sorting of files from
|
||
the last search. The user option ~denote-query-sorting~ controlls
|
||
the default sort ([[#h:ec3d009a-518b-439f-a3e5-38342b1c13fe][Control the default sort of a query buffer]]).
|
||
|
||
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.
|
||
|
||
** Control the default sort of a query buffer
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:ec3d009a-518b-439f-a3e5-38342b1c13fe
|
||
:END:
|
||
|
||
#+vindex: denote-query-sorting
|
||
The user option ~denote-query-sorting~ controls the order of matching
|
||
files in a query buffer (buffers that use the ~denote-query-mode~. The
|
||
applies to the buffer that is produced by query links for file
|
||
contents, backlinks, and the ~denote-grep~ command:
|
||
|
||
- [[#h:d9a84289-2f73-4ef9-b4f0-9a0aa3e9bf0d][Add a query link]].
|
||
- [[#h:c73f1f68-e214-49d5-b369-e694f6a5d708][The backlinks' buffer]].
|
||
- [[#h:e71c9d14-7e88-4386-91d0-9ad249947077][Use ~denote-grep~ to search inside files]].
|
||
|
||
By default (a ~nil~ value), no sorting of matching files is performed.
|
||
Files are displayed in the order they are returned by the search
|
||
program (which often, but not always, means they are sorted by file
|
||
name).
|
||
|
||
#+vindex: denote-sort-components
|
||
The ~denote-query-sorting~ can be set to a symbol among those listed
|
||
in the value of the variable ~denote-sort-components~ to sort by the
|
||
given file name component ([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file-naming scheme]]). The symbol
|
||
=random= produces a random order each time, while =last-modified=
|
||
relies on the file system to sort by the modification time.
|
||
|
||
The ~denote-query-sorting~ can also be set to the symbol of a
|
||
function. In that case, the given function is used to perform the
|
||
sorting. This is for advanced users, who may refer to the
|
||
documentation of the ~denote-sort-files~ for the technicalities.
|
||
|
||
Whatever the value of ~denote-query-sorting~, users may change the
|
||
sorting of query buffers on demand by calling the command
|
||
~denote-query-sort-last-search~ ([[#h:435592bc-e896-429f-a599-9f1bcd5ab9b8][Interact with the links buffer]]).
|
||
|
||
* 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 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).
|
||
|
||
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-review~ package
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:46294e0c-4155-425f-abd8-b6f83bc29f53
|
||
:END:
|
||
|
||
The [[https://codeberg.org/mattof/denote-review][~denote-review~]] package by Matto Fransen implements a a simple
|
||
review process for Denote notes.
|
||
|
||
The package provides options to extend the frontmatter of some notes
|
||
with a ~reviewdate~ field. It also provides an ordered tabulated list
|
||
of notes and their review date. The user is prompted to select either
|
||
all notes in a directory or to narrow to a single keyword.
|
||
|
||
Select a note in this list to edit, or request a random note from the
|
||
list to edit. Update the ~reviewdate~ of the note with a single
|
||
command.
|
||
|
||
** 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).
|
||
|
||
** Use the ~mir~ package for incremental reading
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:0560dbf3-25a1-4eae-aa9e-5900d7e37a2e
|
||
:END:
|
||
|
||
The [[https://github.com/gvalson/mir.el][mir package]] builds on top of ~denote~, ~denote-sequence~ and
|
||
Emacs' SQLite support to provide a workflow for prioritizing,
|
||
scheduling and reading a massive amount of text and extracting the
|
||
most useful parts out of it. This is accomplished using a process
|
||
called incremental reading (for more information, see the package's
|
||
README). ~denote~ is used as a backbone for storing imported
|
||
documents such that they are easily modifiable, uniquely identifiable
|
||
and easy to genealogically query. In particular, the file's ID is
|
||
used to store scheduling information in the database and the keyword
|
||
component is used to build a tree-like hierarchy (i.e. a sequence of
|
||
notes) that makes it easy to see where the source of a given
|
||
extracted note is.
|
||
|
||
As of writing this (January 2026), ~mir~ is still in heavy development
|
||
and does not feature all the functionality that would make it a valid
|
||
replacement of other incremental reading tools. However, work is
|
||
underway to make it feature-complete.
|
||
|
||
** Use the ~denote-journal-capture~ package
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:0938f36c-92f7-4183-936d-96695b871987
|
||
:END:
|
||
|
||
The ~denote-journal-capture~ package by Samuel W. Flint extends the
|
||
~denote-journal~ package to work with the ~org-capture~ mechanism:
|
||
|
||
- [[#h:4a6d92dd-19eb-4fcc-a7b5-05ce04da3a92][Use the ~denote-journal~ package which was formerly =denote-journal-extras.el=]].
|
||
- [[#h:656c70cd-cf9a-4471-a0b5-4f0aaf60f881][Create note using Org capture]].
|
||
|
||
The ~denote-journal-capture~ documentation covers the technicalities:
|
||
<https://git.sr.ht/~swflint/denote-journal-capture>.
|
||
|
||
** Use the ~denote-agenda~ package
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:9838f36c-92f7-4183-936d-96695b871987
|
||
:END:
|
||
|
||
The ~denote-agenda~ package by Samuel W. Flint makes it easier to make
|
||
the ~org-agenda~ work with Denote files. The package provides various
|
||
ways to include files in the agenda, such as to pull in journal
|
||
entries from the current and future days. The package also defines
|
||
functions to filter the files, such as to exclude those that match a
|
||
given regular expression.
|
||
|
||
Read more from the package's documentation:
|
||
<https://git.sr.ht/~swflint/denote-agenda>.
|
||
|
||
** Use the ~denote-project-notes~ package
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:5d249c01-71ca-4583-959b-0a3368c30433
|
||
:END:
|
||
|
||
The ~denote-project-notes~ by Samuel W. Flint makes it possible to
|
||
associate a =project.el= project with Denote files. The idea is to add
|
||
directory-local variables to a project so that it can recognise note
|
||
files therein. ~denote-project-notes~ defines commands to display
|
||
project notes on the side.
|
||
|
||
Learn about the technicalities on the package's webpage:
|
||
<https://git.sr.ht/~swflint/denote-project-notes>.
|
||
|
||
** Use the ~denote-regexp~ package
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:1e3d1714-e444-4852-bf31-9059880e28b8
|
||
:END:
|
||
|
||
The ~denote-regexp~ package by Samuel W. Flint defines utilities to
|
||
write regular expressions that work with the Denote file-naming scheme
|
||
in light of the user option ~denote-file-name-components-order~
|
||
([[#h:dc8c40e0-233a-4991-9ad3-2cf5f05ef1cd][Change the order of file name components]]). The idea is to make it
|
||
easier to perform complex queries like:
|
||
|
||
#+begin_example emacs-lisp
|
||
(denote-regexp :keywords '(or "agenda" (and "project" "inprogress")))
|
||
#+end_example
|
||
|
||
The regular expressions produced by ~denote-regexp~ can be used
|
||
anywhere, such as in a Dired buffer to mark the matching files, in Org
|
||
dynamic blocks, with the ~denote-add-links~ command, and more:
|
||
|
||
- [[#h:9fe01e63-f34f-4479-8713-f162a5ca865e][Display filtered and sorted files with ~denote-sort-dired~ or ~denote-dired~]].
|
||
- [[#h:8b542c50-dcc9-4bca-8037-a36599b22779][Use Org dynamic blocks]].
|
||
- [[#h:9bec2c83-36ca-4951-aefc-7187c5463f90][Insert links to all files matching a query in their file name]].
|
||
|
||
The ~denote-regexp~ documentation covers the technicalities:
|
||
<https://git.sr.ht/~swflint/denote-regexp>.
|
||
|
||
** Use the ~denote-sections~ package
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:fa3c89c8-32f7-4224-bf31-edf7404a3e46
|
||
:END:
|
||
|
||
The ~denote-sections~ package by Samuel W. Flint is meant to provide a
|
||
sidebar that can display useful information about Denote files. As of
|
||
this writing (2026-04-27 22:47 +0300), the sidebar has one section for
|
||
backlinks. Learn more here: <https://git.sr.ht/~swflint/denote-sections>.
|
||
|
||
** Use the ~denote-wordcloud~ package
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:7fd65be3-7e1d-475f-8619-84b5ecb53c05
|
||
:END:
|
||
|
||
The ~denote-wordcloud~ package by Alexander Kuzmin presents a "word
|
||
cloud" for keywords in Denote files. Each keyword includes a count for
|
||
the number of times it is used. More frequently used keywords have a
|
||
larger font size. The word cloud is interactive: clicking on a keyword
|
||
opens a ~denote-dired~ buffer that matches all files with that
|
||
keyword.
|
||
|
||
The documentation of the package also includes a demonstration:
|
||
<https://codeberg.org/treflip/denote-wordcloud>.
|
||
|
||
** Use the ~denote-lint~ package
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:d4b42d2a-c626-4195-ac0e-68352d0b918a
|
||
:END:
|
||
|
||
The ~denote-lint~ package by Peter Smith provides facilities that
|
||
check all Denote files for correctness. This is about the consistency
|
||
between file name and front matter, the validity of links, and
|
||
related. The package defines multiple warning levels, each with its
|
||
own semantics. Learn more here: <https://github.com/psmith1303/denote-lint>.
|
||
|
||
* 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.
|
||
|
||
** Define a custom file type to automatically encrypt new notes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:647ea7d0-7168-4ef7-8043-40403d924789
|
||
:END:
|
||
|
||
#+vindex: denote-file-types
|
||
Denote can work with any file type. By default, it works with those
|
||
defined in the variable ~denote-file-types~. The documentation string
|
||
of that variable explains the technicalities.
|
||
|
||
Users who simply want to write files with something like a =.org.gpg=
|
||
extension can add the following to their configuration:
|
||
|
||
#+begin_src emacs-lisp
|
||
;; This is the same as the default `denote-file-types' entry for
|
||
;; `org', except for the :extension. I gave it a different name (the
|
||
;; `org-gpg'), to distinguish it from the default.
|
||
(add-to-list 'denote-file-types
|
||
'(org-gpg
|
||
:extension ".org.gpg"
|
||
: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-retrieval-format "[denote:%VALUE%]"
|
||
:link denote-org-link-format
|
||
:link-in-context-regexp denote-org-link-in-context-regexp))
|
||
#+end_src
|
||
|
||
With this in place, the command ~denote-type~ will include the
|
||
=org-gpg= file type in its minibuffer prompt for a file type
|
||
([[#h:887bdced-9686-4e80-906f-789e407f2e8f][Convenience commands for note creation]]). Same principle if the user
|
||
option ~denote-prompts~ is configured to include the =file-type=
|
||
prompt ([[#h:f9204f1f-fcee-49b1-8081-16a08a338099][The ~denote-prompts~ option]]).
|
||
|
||
** 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
|
||
(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 exclude-regexp)
|
||
"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)))
|
||
(pcase-let* ((`(,component . ,reverse-sort) (denote-sort-dired--get-sort-parameters sort-by-component reverse))
|
||
(relative-p (denote-has-single-denote-directory-p))
|
||
(files-fn `(lambda ()
|
||
(let* ((files (denote-sort-get-directory-files ,files-matching-regexp ',component ,reverse-sort))
|
||
(empty-files (seq-filter #'my-denote--note-has-no-contents-p files)))
|
||
(if relative-p
|
||
(mapcar #'file-relative-name empty-files)
|
||
empty-files)))))
|
||
(if-let* ((directory (if relative-p ; see comment in `denote-file-prompt'
|
||
(car (denote-directories))
|
||
(denote-directories-get-common-root)))
|
||
(files (funcall files-fn))
|
||
(buffer-name (funcall denote-sort-dired-buffer-name-function (format "%s (EMPTY)" files-matching-regexp) sort-by-component reverse-sort exclude-regexp)))
|
||
(denote-sort-dired--prepare-buffer directory files-fn 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 exclude-regexp)
|
||
"Like `denote-sort-dired' but omit 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)))
|
||
(pcase-let* ((`(,component . ,reverse-sort) (denote-sort-dired--get-sort-parameters sort-by-component reverse))
|
||
(relative-p (denote-has-single-denote-directory-p))
|
||
(files-fn `(lambda ()
|
||
(let* ((files (denote-sort-get-directory-files ,files-matching-regexp ',component ,reverse-sort))
|
||
(empty-files (seq-remove #'my-denote--note-has-no-contents-p files)))
|
||
(if relative-p
|
||
(mapcar #'file-relative-name empty-files)
|
||
empty-files)))))
|
||
(if-let* ((directory (if relative-p ; see comment in `denote-file-prompt'
|
||
(car (denote-directories))
|
||
(denote-directories-get-common-root)))
|
||
(files (funcall files-fn))
|
||
(buffer-name (funcall denote-sort-dired-buffer-name-function (format "%s (OMIT EMPTY)" files-matching-regexp) sort-by-component reverse-sort exclude-regexp)))
|
||
(denote-sort-dired--prepare-buffer directory files-fn 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)
|
||
(pcase-let ((`(,sort-by ,reverse ,exclude-rx) (denote-sort-dired--prompts)))
|
||
(my-denote-sort-dired-empty-files ".*" sort-by reverse exclude-rx)))
|
||
|
||
(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)
|
||
(pcase-let ((`(,sort-by ,reverse ,exclude-rx) (denote-sort-dired--prompts)))
|
||
(my-denote-sort-dired-without-empty-files ".*" sort-by reverse exclude-rx)))
|
||
#+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:
|
||
|
||
[ This is not needed given that we provide the ~denote-grep~ command
|
||
([[#h:e71c9d14-7e88-4386-91d0-9ad249947077][Use ~denote-grep~ to search inside files]]). ]
|
||
|
||
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-date-identifier-format
|
||
+ Variable ~denote-date-identifier-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-date-identifier-regexp
|
||
+ Variable ~denote-date-identifier-regexp~ :: Regular expression to match ~denote-date-identifier-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-date-identifier-p
|
||
+ Function ~denote-date-identifier-p~ :: Return non-nil if =IDENTIFIER=
|
||
string is a Denote date 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-keywords-to-not-infer-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-directories
|
||
+ Function ~denote-directories~ :: Return list of paths to the
|
||
variable ~denote-directory~.
|
||
|
||
#+findex: denote-directories-get-common-root
|
||
+ Function ~denote-directories-get-common-root~ :: Return the closest
|
||
common root of the ~denote-directories~.
|
||
|
||
#+findex: denote-directory-get-files
|
||
+ Function ~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.
|
||
|
||
#+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. With optional =HAS-IDENTIFIER= as
|
||
a non-~nil~ value, limit the results to files that have an
|
||
identifier.
|
||
|
||
#+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]]).
|
||
|
||
#+findex: denote-id-to-date
|
||
+ Function ~denote-id-to-date~ :: Convert =IDENTIFIER= to a date
|
||
string of the form =YYYY-MM-DD=.
|
||
|
||
** Predefined note values for developers or advanced users
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: h:a8b9082c-8bd5-492d-8da7-4f41ee493d59
|
||
:END:
|
||
|
||
In custom code, it is possible to override what the ~denote~ command
|
||
is reading by specifying a value for the following. In that case,
|
||
there will be no corresponding prompt.
|
||
|
||
#+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.
|
||
|
||
** 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
|
||
#+findex: denote-sluggify-and-apply-rules
|
||
+ Function ~denote-sluggify-and-apply-rules~ (alias ~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. Also enforce the rules of the file-naming scheme.
|
||
|
||
#+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-and-apply-rules
|
||
+ Function ~denote-sluggify-keywords-and-apply-rules~ :: Sluggify =KEYWORDS=, which is a list of strings
|
||
([[#h:ae8b19a1-7f67-4258-96b3-370a72c43f4e][Sluggification of file name components]]).
|
||
|
||
#+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-file-type
|
||
+ Function ~denote-file-type~ :: Use the file extension to detect the
|
||
file type of FILE. Do so in accordance with ~denote-file-types~. If
|
||
more than one file type correspond to this file extension, return
|
||
the first file type whose =:get-file-type-function= returns non-nil.
|
||
If =:get-file-type-function= is nil rely on the ~:title-key-regexp~
|
||
and return the first matching file type. Return nil if FILE is not
|
||
recognized.
|
||
|
||
#+vindex: denote-get-identifier-function
|
||
+ Variable ~denote-get-identifier-function~ :: The function to
|
||
generate an identifier as a non-empty string. The function takes two
|
||
arguments: an initial identifier and a date. The initial identifier
|
||
is used as a reference to derive a unique variant of it, if possible
|
||
(e.g. to keep incrementing seconds while keeping the rest of the
|
||
date+time the same). Existing identifiers are stored in the variable ~denote-used-identifiers~. If the initial identifier is nil or an
|
||
identifier cannot be derived from it, then the date can be used
|
||
instead. The date has the same format as ~current-time~. When it is
|
||
nil, the ~current-time~ is used.
|
||
|
||
#+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-identifier-generation-function~
|
||
variable.
|
||
|
||
#+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-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-identifier-prompt
|
||
+ Function ~denote-identifier-prompt~ :: Prompt for identifier string.
|
||
With optional =INITIAL-IDENTIFIER= 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-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-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.
|
||
|
||
#+findex: denote-get-file-type-markdown-yaml
|
||
+ Function ~denote-get-file-type-markdown-yaml~ :: Return =markdown-yaml= if FILE has YAML front matter. YAML front matter
|
||
starts with =---= on the first line. Consult the ~denote-file-types~
|
||
for how this is used.
|
||
|
||
#+findex: denote-get-file-type-markdown-toml
|
||
+ Function ~denote-get-file-type-markdown-toml~ :: Return
|
||
=markdown-toml= if FILE has TOML front matter. Consult the
|
||
~denote-file-types~ for how this is used. TOML front matter starts
|
||
with =+++= on the first line.
|
||
|
||
#+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. =:get-file-type-function= a function with one parameter, a given
|
||
file, that reads the file contents and returns the file type as a
|
||
symbol or nil. If there is no function, Denote will fall back to
|
||
a search for a title in the front matter (per =:title-key-regexp=,
|
||
which is described below).
|
||
|
||
3. =: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~.
|
||
|
||
4. =: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 docstring of ~format~ on how to reorder arguments.
|
||
|
||
5. =: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.
|
||
|
||
6. =: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.
|
||
|
||
7. =: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=.
|
||
|
||
8. =: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.
|
||
|
||
9. =: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.
|
||
|
||
10. =: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=.
|
||
|
||
11. =: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~.
|
||
|
||
12. =: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-get-links
|
||
+ Function ~denote-get-links~ :: Return list of links in current or
|
||
optional =FILE=. With optional =FILES= consider only those,
|
||
otherwise use the return value of ~denote-directory-files~. Also see ~denote-get-backlinks~.
|
||
|
||
#+findex: denote-get-backlinks
|
||
+ Function ~denote-get-backlinks~ :: Return list of backlinks in
|
||
current or optional =FILE=. Also see ~denote-get-links~.
|
||
|
||
#+findex: denote-link-description-with-signature-and-title
|
||
+ Function ~denote-link-description-with-signature-and-title~ :: Return
|
||
link description for =FILE= with =FILE-TYPE=. For backward
|
||
compatibility, =FILE-TYPE= is an optional parameter. If it is ~nil~,
|
||
then compute =FILE-TYPE= internally.
|
||
|
||
- 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.
|
||
|
||
- If none of the above works, return an empty string.
|
||
|
||
This function is useful as the value of the user option ~denote-link-description-format~ (which can optionally be bound to a
|
||
function).
|
||
|
||
** 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:
|
||
|
||
#+vindex: denote-rename-rewrite-front-matter
|
||
- Variable ~denote-rename-rewrite-front-matter~ :: When non-nil,
|
||
rewrite the front matter if appropriate. The purpose of this
|
||
variable is to be ~let~ bound to nil by a caller of the command ~denote-rename-file~ or related. This will have the effect of not
|
||
rewriting the file's front matter.
|
||
|
||
#+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, 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.
|
||
|
||
- 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 docstring 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.
|
||
|
||
+ Contributions to code or the manual :: Abdul-Lateef Haji-Ali, Abin
|
||
Simon, Adam Růžička, Alan Schmitt, Alexandre Rousseau, Alvin Hsu,
|
||
Ashish Panigrahi, 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, James Kalyan, 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 Batson, Matthew Lemon, Matto
|
||
Fransen, Noboru Ota (nobiot), Norwid Behrnd, Octavian, Peter Prevos,
|
||
Philip Kaludercic, Ryota, Samuel W. Flint, Quiliro Ordóñez, Stephen
|
||
R. Kifer, Stefan Monnier, Stefan Thesing, Thibaut Benjamin, Tobias
|
||
Lidman-Strauss, Tomasz Hołubowicz, TomoeMami , Vedang Manerikar,
|
||
Wesley Harvey, Yann Dutrieux, Zhenxu Xu, alvmts, arsaber101,
|
||
bplubell, bryanrinders, dulikiles, eum3l, experimental-code-86,
|
||
ezchi, gnuhack, gvalson, jarofromel, leinfink (Henrik), l-o-l-h
|
||
(Lincoln), mattyonweb, maxbrieiev, mentalisttraceur, nescias,
|
||
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, Bhargav Kulkarni, 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, Kevin Izevbigie, Kolmas, Lukas C. Bossert, M.
|
||
Hadi Timachi, Maikol Solis, Mark Olson, Michael Jones, Mirko
|
||
Hernandez, Morten Kjeldgaard, Niall Dooley, Nick Bell, Oliver Epper,
|
||
Paul van Gelder, Peter Prevos, Peter Smith, Riccardo Giannitrapani,
|
||
Rory Molinari, 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, 82Kang, atanasj, azegas, babusri,
|
||
bdillahu, coherentstate, doolio, duli, drcxd, elge70, elliottw,
|
||
fingerknight, GuutBoy, hpgisler, hyperfocus1337,johkneisl,
|
||
jtpavlock, juh, leafarbelm, mentalisttraceur, mjkalyan, oatmealm,
|
||
Pratik-Mishra-4979, pRot0ta1p, rbenit68, relict007, sarcom-sar,
|
||
sienic, skissue, sundar bp, wuzhihao, 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: -->
|