Compare commits

..

No commits in common. "main" and "4.1.2" have entirely different histories.
main ... 4.1.2

5 changed files with 1199 additions and 2210 deletions

View file

@ -1,5 +1,5 @@
#+title: Change log of Denote
#+author: Protesilaos
#+author: Protesilaos Stavrou
#+email: info@protesilaos.com
#+language: en
#+options: ':t toc:nil author:t email:t num:t
@ -13,497 +13,6 @@ the manual: <https://protesilaos.com/emacs/denote>.
#+toc: headlines 1 insert TOC here, with one headline level
* Version 4.2.0 on 2026-05-20
:PROPERTIES:
:CUSTOM_ID: h:68f86f8a-928d-4a1e-b83c-62c648753965
:END:
This version brings several improvements to the core ~denote~ package
as well as all the Denote extensions I maintain. The core package is
stable, its feature set is rich, and the wider ecosystem of extensions
is growing.
Most of the changes documented herein are of interest to experienced
users who may be looking for ways to refine their workflow. I
recommend that new users start with the basics, as I explained them in
the original video demonstration of Denote or as they are documented
in the manual's section for newcomers:
- Webpage :: <https://protesilaos.com/emacs/denote#h:c54bedb4-5377-4dbd-853c-5870ace6eb33>.
- Info manual :: With the latest ~denote~ package installed, evaluate =(info "(denote) Getting started with Denote")=.
Remember that the release notes are true only at the time of
publication. The single source of truth is the official manual.
** Core Denote
:PROPERTIES:
:CUSTOM_ID: h:cc1daf10-2335-4f81-93f4-e5b29929a670
:END:
*** Overview of the new features
:PROPERTIES:
:CUSTOM_ID: h:7a5e8f4c-1d2b-4c3e-8a9d-0f1e2d3c4b5a
:END:
- The command ~denote-dired-focus~ will filter the results of an
existing ~denote-dired~ buffer. Use this to narrow down the results.
- In Org files, the =denote:= link type can now be previewed using the
built-in ~org-link-preview~ command, starting with Org version
=9.8.0=.
- The command ~denote-link-or-create-with-command~ extends the
existing convenience functions of the "do or create note" kind.
- The ~denote-file-prompt~ uses completion metadata to sort by most
recently accessed, group by directory or file extension, and cover
packages that display cosmetic icons alongside completion
candidates.
- Denote now enforces a controlled vocabulary for keywords when
~denote-infer-keywords~ is set to ~nil~, such that only the
~denote-known-keywords~ are provided as an option at the relevant
prompts.
- The mechanism for integrating Denote with ~org-capture~ now supports
prompting for an signature via ~denote-org-capture-with-prompts~
(the signature is an optional, free-form component of the Denote
file-naming scheme).
- Several packages that extend Denote are documented in the manual. If
you have a package for Denote, let me know and I will write a
section about it.
*** Focus a ~denote-dired~ buffer with ~denote-dired-focus~
:PROPERTIES:
:CUSTOM_ID: h:5e4d3c2b-1a0e-9d8c-7b6a-5f4e3d2c1b0a
:END:
The command ~denote-dired~ produces a Dired listing of file names that
match the given regular expressions. Users can benefit from the Denote
file-naming scheme to, for example, include all files that have the
keyword =_emacs=. In the resulting Dired buffer, the new command
~denote-dired-focus~ can then be invoked to further narrow down the
results, such as to only show files that have =2026= in their file
(with default settings, the date is part of the Denote identifier).
I implemented this feature in response to issue 693 by 82Kang:
<https://github.com/protesilaos/denote/issues/693>.
*** Improvements to the file prompt
:PROPERTIES:
:CUSTOM_ID: h:2b1a0e9d-8c7b-6a5f-4e3d-2c1b0a987654
:END:
Various Denote commands prompt for a file name: for instance,
~denote-link~ asks which file to link to. This file prompt is now
augmented with completion metadata that transform how files look and
how the information is organised.
Before, the prompt presented full file names like:
: 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
Those same file names are now transformed to look like this:
: 2022-06-10 initial-thoughts-on-the-zettelkasten-method notetaking
: 2022-06-10 define-custom-org-hyperlink-type denote_emacs_package
: 2022-06-10 on-hierarchy-and-taxis notetaking_philosophy
The files will be grouped by file extension or directory (if they are
in a subdirectory of the ~denote-directory~). Furthermore, they will
be sorted by most recently accessed.
The underlying file names are still available except that their
presentation is modified. This means that input at the minibuffer
prompt will still match everything they contain.
This completion metadata extends to the packages ~all-the-icons~ and
~nerd-icons~, which are now instructed to add the correct file icons
to the completion candidates: an Org file will have the unicorn icon
beside it, for example.
Users who do not like the new style can revert to the plain
presentation by setting ~denote-file-prompt-extra-metadata~ to ~nil~.
Advanced users who wish to set up the ~completion-category-overrides~
may target the ~denote-file~ completion category or, anyhow, modify
the ~denote-file-prompt-extra-metadata~.
*** Link to a file or create a new note using a specific command
:PROPERTIES:
:CUSTOM_ID: h:3c2b1a0e-9d8c-7b6a-5f4e-3d2c1b0a9876
:END:
Denote provides many "convenience wrapper" commands that do something
quickly which can also be achieved with minimal configuration. For
example, the ~denote~ command may be modified to also prompt for a
file type and so the ~denote-type~ command is like ~denote~ with the
addition of the file type prompt. Users can look at the source code of
~denote-type~ to write their own small variations (the manual provides
several examples as well).
The ~denote-open-or-create-with-command~ may then use those to
implement its specified behaviour of "open an existing file or create
it using a convenience wrapper command".
Same principle for the new ~denote-link-or-create-with-command~: it
makes possible the workflow of "link to an existing file or create a
new note with the given command".
Convenience wrappers are listed in the value of the user option
~denote-commands-for-new-notes~.
Thanks to Matthew Batson for building on top of existing functionality
to contribute ~denote-link-or-create-with-command~ in pull request
674: <https://github.com/protesilaos/denote/pull/674>. Matthew has
assigned copyright to the Free Software Foundation.
*** Preview =denote:= links in Org files
:PROPERTIES:
:CUSTOM_ID: h:4d3c2b1a-0e9d-8c7b-6a5f-4e3d2c1b0a98
:END:
Starting with Org version =9.8.0= custom link types such as =denote:=
can implement their own preview mechanism. In practice, this means
that =denote:= links pointing to image files will now work as expected
with ~org-link-preview~ (remember that the Denote file-naming scheme
can be applied to any file and is in no way specific to
note-taking---I use it for documents and videos, for example).
Thanks to Samuel W. Flint for the original contribution in pull request 683:
<https://github.com/protesilaos/denote/pull/683>, with further changes
by me. The original contribution is small, meaning that Samuel does
not need to assign copyright to the Free Software Foundation.
*** Signature support in Org capture
:PROPERTIES:
:CUSTOM_ID: h:1a0e9d8c-7b6a-5f4e-3d2c-1b0a98765432
:END:
The ~denote-org-capture-with-prompts~ function now supports the
signature file name component as an additional parameter. This
function is meant to be used in tandem with the ~org-capture~
mechanism, as shown in the manual.
Thanks to Tobias Lidman-Strauss for the contribution in merge request
2 on the GitLab mirror: <https://gitlab.com/protesilaos/denote/-/merge_requests/2>.
The change is small, meaning that Tobias does not need to assign
copyright to the Free Software Foundation.
*** The ~denote-fontify-links-mode~ is only relevant for =.txt= files
:PROPERTIES:
:CUSTOM_ID: h:0e9d8c7b-6a5f-4e3d-2c1b-0a9876543210
:END:
The =denote:= links are automatically highlighted as links in Org and
Markdown bufers. Users who prefer to write notes in plain =.txt= files
must enable the ~denote-fontify-links-mode~ to get the same effect.
I have revised ~denote-fontify-links-mode~ to only work with =.txt= as
its other users were not necessary. In the process, I have deprecated
the ~denote-fontify-links-mode-maybe~ function: just use the ~denote-fonftify-links-mode~.
The keys =RET= and =C-c C-o= open the link (same keys used by Org and
Markdown modes).
*** Growing ecosystem of Denote packages
:PROPERTIES:
:CUSTOM_ID: h:9d8c7b6a-5f4e-3d2c-1b0a-9876543210fe
:END:
In the Denote manual I mention packages that build on top of Denote.
There is one section for each package. The manual now includes the
following:
- ~denote-agenda~ (by Samuel W. Flint) :: Use Denote notes as Org agenda files.
- ~denote-journal-capture~ (by Samuel W. Flint) :: Enhanced journaling workflows.
- ~denote-lint~ (Peter Smith) :: Checks for inconsistencies in Denote file names and front matter.
- ~denote-project-notes~ (by Samuel W. Flint) :: Integrate Denote with Emacs' built-in project support.
- ~denote-regexp~ (by Samuel W. Flint) :: Search and link notes using regular expressions.
- ~denote-review~ (by Matto Fransen) :: A package for reviewing notes over time.
- ~denote-sections~ (by Samuel W. Flint) :: Manage sections within Denote notes.
- ~denote-wordcloud~ (by Alexander Kuzmin) :: Generate word clouds from Denote notes.
*** Miscellaneous
:PROPERTIES:
:CUSTOM_ID: h:8c7b6a5f-4e3d-2c1b-0a98-76543210fedc
:END:
- The command ~denote-dired~ (alias ~denote-sort-dired~) is refactored
to work as intended in all cases. Thanks to kilesduli for the
contribution in pull request 666: <https://github.com/protesilaos/denote/pull/666>.
Further changes by me, including the option to maintain many
separate ~denote-dired~ buffers, which I did in response to issue
693 by 82Kang: <https://github.com/protesilaos/denote/issues/693>.
- I have revised the ~denote-grep~ mechanism and all of its ancillary
functions and variables are revised in the interest of consistency
and maintainability. Thanks to gnuhack for contributing a macro that
was meant to streamline some commands. This was done in pull request
697: <https://github.com/protesilaos/denote/pull/697>. I eventually
changed lots of things so that the macro was not relevant anymore,
though mine was a change with a wider scope.
- The Org link storage mechanism (~denote-link-ol-store~) now works
correctly within ~org-capture~ buffers, allowing for more flexible
linking workflows.
- Following non-Denote Markdown links no longer result in an error
under certain circumstances. Thanks to bplubell for the contribution
in pull request 685: <https://github.com/protesilaos/denote/pull/685>.
The change is small, meaning that its author does not need to assign
copyright to the Free Software Foundation.
- Retrieving front matter is now more reliable, even when the buffer
is unsaved. Thanks to kilesduli for the contribution in pull request
672: <https://github.com/protesilaos/denote/pull/672>. Also thanks
to Jean-Philippe Gagné Guay for reviewing the change and for
reporting a problem with an earlier version of the code in issue
670: <https://github.com/protesilaos/denote/issues/670>. Further
changes by me.
- The various Denote rename commands that affect the front matter in
files no longer change existing spacing. I did this to address the
comment posted by Morten Kjeldgaard in issue 703: <https://github.com/protesilaos/denote/issues/703>.
- Updated the documentation to explain how to automatically encrypt
new notes when using a custom file type.
- Refined the internal helper functions for directory management and
identifier validation.
- Thanks to nescias for fixing three typos in the manual. This was
sent to me as a patch, which I installed as commit =c772378=.
*** Changes to the extensions of Denote I maintain
:PROPERTIES:
:CUSTOM_ID: h:6e7f8a9b-0c1d-2e3f-4a5b-6c7d8e9f0a1b
:END:
This is about packages I maintain. Some of them were originally part
of the denote.git repository, but I moved them out into their own
packages to make everything easier to reason about.
**** ~consult-denote~ version 0.5.0
:PROPERTIES:
:CUSTOM_ID: h:1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
:END:
- Package name (GNU ELPA): ~consult-denote~
- Official manual: <https://protesilaos.com/emacs/consult-denote>
- Change log: <https://protesilaos.com/emacs/consult-denote-changelog>
- Git repository: <https://github.com/protesilaos/consult-denote>
- Backronym: Consult-Orchestrated Navigation and Selection of
Unambiguous Targets...denote.
- References to the long-obsolete "denote-silo-extras-" prefix are
replaced by "denote-silo-". Thanks to IT Ascalium for the
contribution in pull request 20: <https://github.com/protesilaos/consult-denote/pull/20>.
The change is small, so its author does not need to assign copyright
to the Free Software Foundation.
- The ~consult-denote-file-prompt~ correctly handles relative file
paths when ~denote-directory~ is set to a list of directories. This
is what the underlying ~denote-file-prompt~ does (which I mentioned
above about its metadata). Thanks to Kai von Fintel for the
contribution in pull request 24: <https://github.com/protesilaos/consult-denote/pull/24>.
The change does not require copyright assignment.
**** ~denote-merge~ version 0.1.0
:PROPERTIES:
:CUSTOM_ID: h:c877c108-9d57-4b54-a16f-fbeb8c92fa16
:END:
- Git repository: <https://github.com/protesilaos/denote-merge>
- Official manual: <https://protesilaos.com/emacs/denote-merge>
- Backronym: Denote... Merging Eventually Reformats the Given Entries.
This is an optional extension to the ~denote~ package. It provides
commands and relevant user options to streamline the work of merging
contents from one Denote file to another. This is for users who
periodically review their notes to add, remove, or otherwise
consolidate their accumulated knowledge.
**** ~denote-journal~ version 0.3.0
:PROPERTIES:
:CUSTOM_ID: h:2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e
:END:
- 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.
- The user option ~denote-journal-keyword~ now supports a ~nil~ value,
allowing users to create journal entries without a specific keyword.
Thanks to nescias for sending me the patch via email, which I
installed as commit =d4cc501= in denote-journal.git. The change does
not require copyright assignment.
- Fixed an issue about how the function ~denote-directory-files~ was
used. Thanks to Donald Brady for reporting the bug in issue 656 on
the main Denote repository and to kamchy for confirming the problem:
<https://github.com/protesilaos/denote/issues/656>. The approach was
utlimately revised in denote.git courtesy of a change by
Jean-Philippe Gagné Guay in pull request 661:
<https://github.com/protesilaos/denote/pull/661>.
**** ~denote-markdown~ version 0.3.0
:PROPERTIES:
:CUSTOM_ID: h:3c4d5e6f-7a8b-9c0d-1e2f-3a4b5c6d7e8f
:END:
- 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.
- The package defines a =markdown-obsidian= file type which can be
used by relevant note-creating commands, such as ~denote~ or the
convenience wrapper ~denote-type~. This file type is updated to be
more robust, in accordance with some changes in core Denote (I am
not even documenting those, as they are not intended for users).
**** ~denote-org~ version 0.3.0
:PROPERTIES:
:CUSTOM_ID: h:4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f9a
:END:
- 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.
- The command ~denote-org-link-to-heading~ now supports linking to the
current file when called with a prefix argument. This way, a file
can have links between its headings. Thanks to Tonus for pointing
out that it was impossible to create a link inside the current file.
This was done in issue 17: <https://github.com/protesilaos/denote-org/issues/17>.
- A helper function for retrieving the backlinks of a heading is
updated to return full file paths. Thanks to Vedang Manerikar for
the contribution in pull request 20: <https://github.com/protesilaos/denote-org/pull/20>.
Vedang has assigned copyright to the Free Software Foundation.
- Another helper function is updated to conform with changes to core
Denote with regard to how the ~denote-link-description-format~ is
handled. Thanks to Jung Han for reporting the bug in issue 21:
<https://github.com/protesilaos/denote-org/issues/21>.
**** ~denote-silo~ version 0.3.0
:PROPERTIES:
:CUSTOM_ID: h:6f7a8b9c-0d1e-2f3a-4b5c6d7e8f9a0b1c
:END:
- 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.
The minibuffer prompt for silo directories uses the corrent completion
category (consistent with what I mentioned above about completion
metadata). Thanks to Wilf-bog for reporting an error with the
completion prompt in issue 1: <https://github.com/protesilaos/denote-silo/issues/1>.
**** ~denote-sequence~ version 0.3.0
:PROPERTIES:
:CUSTOM_ID: h:5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b
:END:
- 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.
This package deserved its own release notes, as I did a lot of work on
it. But as this file is already long, I will focus on the essentials:
- The ~denote-sequence-scheme~ used to support a =numeric= and
=alphanumeric= option. There now is a third one called
=alphanumeric-delimited=. It combines features from the other two
and may be better suited for especially long/intricate sequences.
- The ~denote-sequence-reparent~ command now works recursively to
produce the desired consequences to all descendants of a given
sequence note. Thanks to Peter Prevos for the contribution in pull
request 13, which further changes by me:
<https://github.com/protesilaos/denote-sequence/pull/13>.
- The command ~denote-sequence-view-hierarchy~ produces a bespoke
buffer with all the sequence notes that form a hierarchy. The buffer
displays file titles, the concomitant sequence, and file keywords.
Each level of depth is expressed by a number of spaces, controlled
by the user option ~denote-sequence-hierarchy-indentation~. In the
hierarchy buffer, there are commands that move to the next/previous
item, or forward/backward at the same level of depth. =RET= opens
the file at point, =TAB= folds/unfolds the tree. The user option
~denote-sequence-hierarchy-move-and-open~ controls whether motion
commands should automatically open the file, which by default
happens in the other window (users who modify the variable
~denote-open-link-function~ will get the specified behaviour in this
context as well). The ~denote-sequence-view-hierarchy~ can be called
with one or two prefix arguments to limit to a given sequence prefix
and/or level of depth (something that ~denote-sequence-dired~ also
supports). In short, this is a way to visualise your sequence notes
in a buffer that has a different presentation than Dired.
- Thanks to alan-w-255 for renaming and refining a prompt that is also
used in the hierarchy feature. This was done in pull request 15:
<https://github.com/protesilaos/denote-sequence/pull/15>. The change
is small, meaning that its author does not need to assign copyright
to the Free Software Foundation. Further refinements by me.
- Thanks to Nicolas Semrau for binding =q= to ~quit-window~ in the
~denote-sequence-hierarchy-mode-map~. This was done in pull request
20: <https://github.com/protesilaos/denote-sequence/pull/20>. The
change is small, meaning that Nicolas does not need to assign
copyright to the Free Software Foundation.
- The ~denote-sequence-file-prompt-extra-metadata~ is the functional
equivalent of the aforementioned ~denote-file-prompt-extra-metadata~.
- Thanks to liyingzhi for pointing out an inaccurate comment in the
docstring of ~denote-sequence-scheme~. This was done in issue 18:
<https://github.com/protesilaos/denote-sequence/issues/18>.
- The ~denote-sequence-dired~ is updated to align with the modalities
of ~denote-dired~, as noted above. Thanks to juh for reminding me
about the need for changes in issue 14: <https://github.com/protesilaos/denote-sequence/issues/14>.
- Thanks to Stefan Monnier for pointing out a stylistic mistake in an
older version of ~denote-sequence-dired~. This was done on the
emacs-devel mailing list: <https://lists.gnu.org/archive/html/emacs-devel/2025-11/msg01119.html>.
Also thanks to Stefan for telling me about some other compiler
warnings: <https://lists.gnu.org/archive/html/emacs-devel/2025-11/msg01119.html>.
*** Git commits
:PROPERTIES:
:CUSTOM_ID: h:7b6a5f4e-3d2c-1b0a-9876-543210fedcba
:END:
#+begin_src sh
~/Git/Projects/denote $ git shortlog 4.1.0..4.2.0 --summary --numbered
184 Protesilaos
4 duli
3 Jean-Philippe Gagné Guay
3 Matthew Batson
2 alvmts
2 gnuhack
1 Alvin Hsu
1 Matto Fransen
1 Samuel W. Flint
1 Tobias Lidman-Strauss
1 bplubell
1 gvalson
1 nescias
#+end_src
* Version 4.1.0 on 2025-10-17
:PROPERTIES:
:CUSTOM_ID: h:385847d5-4990-4c1f-9869-82113fe1b47f
@ -743,7 +252,7 @@ copyright to the Free Software Foundation.
#+begin_src sh
~/Git/Projects/denote $ git shortlog 4.0.0..4.1.0 --summary --numbered
156 Protesilaos
156 Protesilaos Stavrou
29 Jean-Philippe Gagné Guay
4 Lucas Quintana
2 James Kalyan
@ -863,7 +372,7 @@ core Denote as well as ~consult-denote~.
#+begin_src sh
~/Git/Projects/denote-journal $ git shortlog 0.1.0..0.2.0 --summary --numbered
35 Protesilaos
35 Protesilaos Stavrou
3 Alan Schmitt
2 Ettore Berardi
1 Abdelhak Bougouffa
@ -913,7 +422,7 @@ core Denote as well as ~consult-denote~.
#+begin_src sh
~/Git/Projects/denote-org $ git shortlog 0.1.0..0.2.0 --summary --numbered
42 Protesilaos
42 Protesilaos Stavrou
8 Peter Prevos
#+end_src
@ -957,7 +466,7 @@ core Denote as well as ~consult-denote~.
#+begin_src sh
~/Git/Projects/denote-sequence $ git shortlog 0.1.0..0.2.0 --summary --numbered
61 Protesilaos
61 Protesilaos Stavrou
1 Ashton Wiersdorf
1 Peter Prevos
1 Rory Molinari
@ -1525,7 +1034,7 @@ tiny.
#+begin_src
~/Git/Projects/denote $ git shortlog 3.1.0..4.0.0 --summary --numbered
470 Protesilaos
470 Protesilaos Stavrou
90 Jean-Philippe Gagné Guay
6 Kierin Bell
4 Alan Schmitt
@ -1545,29 +1054,29 @@ after the reorganisation I made. But we have to start from somewhere.
#+begin_src
~/Git/Projects/denote-journal $ git shortlog --summary --numbered
54 Protesilaos
54 Protesilaos Stavrou
2 Honza Pokorny
1 Vineet C. Kulkarni
#+end_src
#+begin_src
~/Git/Projects/denote-sequence $ git shortlog --summary --numbered
22 Protesilaos
22 Protesilaos Stavrou
#+end_src
#+begin_src
~/Git/Projects/denote-silo $ git shortlog --summary --numbered
17 Protesilaos
17 Protesilaos Stavrou
#+end_src
#+begin_src
~/Git/Projects/denote-org $ git shortlog --summary --numbered
15 Protesilaos
15 Protesilaos Stavrou
#+end_src
#+begin_src
~/Git/Projects/denote-markdown $ git shortlog --summary --numbered
11 Protesilaos
11 Protesilaos Stavrou
#+end_src
* Version 3.1.0 on 2024-09-04
@ -2078,7 +1587,7 @@ involved!
#+begin_src
~/Git/Projects/denote $ git shortlog 3.0.0..3.1.0 --summary --numbered
104 Protesilaos
104 Protesilaos Stavrou
7 Jean-Philippe Gagné Guay
3 Ashton Wiersdorf
1 Ad
@ -2509,7 +2018,7 @@ Just an overview of what we did. Thanks again to everyone involved.
#+begin_src sh
~/Git/Projects/denote $ git shortlog 2.3.0..3.0.0 --summary --numbered
169 Protesilaos
169 Protesilaos Stavrou
52 Jean-Philippe Gagné Guay
3 Al Haji-Ali
2 Alan Schmitt
@ -3224,7 +2733,7 @@ Just an overview of what we did. Thanks again to everyone involved.
#+begin_src sh
~/Git/Projects/denote $ git shortlog 2.2.0..2.3.0 --summary --numbered
246 Protesilaos
246 Protesilaos Stavrou
46 Jean-Philippe Gagné Guay
6 Vedang Manerikar
3 Joseph Turner
@ -3602,7 +3111,7 @@ Just an overview of what we did. Thanks again to everyone involved.
#+begin_src sh
~/Git/Projects/denote $ git shortlog 2.1.0..2.2.0 --summary --numbered
125 Protesilaos
125 Protesilaos Stavrou
17 Jean-Philippe Gagné Guay
2 Vedang Manerikar
1 Henrik Hörmann
@ -4231,7 +3740,7 @@ Just an overview of what we did. Every contribution matters.
#+begin_src
~/Git/Projects/denote $ git shortlog 2.0.0..2.1.0 --summary --numbered
153 Protesilaos
153 Protesilaos Stavrou
15 Jean-Philippe Gagné Guay
5 Vedang Manerikar
1 Alan Schmitt
@ -5587,7 +5096,7 @@ such a section):
#+begin_quote
Denote is meant to be a collective effort. Every bit of help matters.
+ Author/maintainer :: Protesilaos.
+ Author/maintainer :: Protesilaos Stavrou.
+ Contributions to code or the manual :: Abin Simon, Alan Schmitt,
Benjamin Kästner, Clemens Radermacher, Colin McLear, Damien Cassou,
@ -6813,7 +6322,7 @@ Gagné Guay, Kaushal Modi, and Shreyas Ragavan.
#+begin_example
commit f35ef05cb451f265213c3aafc1e62c425b1ff043
Author: Protesilaos <info@protesilaos.com>
Author: Protesilaos Stavrou <info@protesilaos.com>
Date: Sun Jul 3 17:34:38 2022 +0300
REMOVE support for 'id:' hyperlink types

View file

@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 2021 Protesilaos
Copyright (C) 2021 Protesilaos Stavrou
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) 2021 Protesilaos
<program> Copyright (C) 2021 Protesilaos Stavrou
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

File diff suppressed because it is too large Load diff

1495
denote.el

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,9 @@
;;; denote-test.el --- Unit tests for Denote -*- lexical-binding: t -*-
;; Copyright (C) 2023-2026 Free Software Foundation, Inc.
;; Copyright (C) 2023-2025 Free Software Foundation, Inc.
;; Author: Protesilaos <info@protesilaos.com>
;; Maintainer: Protesilaos <info@protesilaos.com>
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
;; URL: https://github.com/protesilaos/denote
;; This file is NOT part of GNU Emacs.
@ -37,68 +37,21 @@
(require 'denote)
(ert-deftest dt-denote-directories--get-paths ()
"Test that `denote-directories--get-paths' returns a list of paths."
(let ((path "~/Documents/notes"))
(should (equal
(denote-directories--get-paths path)
(list (file-name-as-directory (expand-file-name path))))))
(let ((paths (list "~/Documents/notes" "~/Documents/books")))
(should (equal
(denote-directories--get-paths paths)
(mapcar
(lambda (path)
(file-name-as-directory (expand-file-name path)))
paths)))
(should (seq-every-p
(lambda (path)
(string-suffix-p "/" path))
(denote-directories--get-paths paths)))
(should-not (null (denote-directories--get-paths paths))))
(should-error (denote-directories--get-paths 'hello))
(should-error (denote-directories--get-paths '(hello)))
(should-error (denote-directories--get-paths '(1 2 3))))
(ert-deftest dt-denote--make-denote-directory ()
"Test that `denote--make-denote-directory' creates the directory."
(let* ((names (list "denote-test-made-directory-1" "denote-test-made-directory-2"))
(directories (mapcar
(lambda (directory)
(file-name-as-directory (expand-file-name directory temporary-file-directory)))
names)))
(should-not (denote-directories--make-paths directories))
(should (seq-every-p #'file-name-directory directories))
(dolist (directory directories)
(delete-directory directory))))
(should (null (denote--make-denote-directory))))
(ert-deftest dt-denote-directories ()
"Test that `denote-directories' returns list of directories.
It does so by expanding the value of the user option `denote-directory'.
Also see `denote-test--denote--make-denote-directory',
`denote-test---denote-directories--get-paths'."
(let ((denote-directory "/tmp/denote-test-notes"))
(should
(equal
(denote-directories)
(list (file-name-as-directory (expand-file-name denote-directory)))))
(should (and (file-directory-p denote-directory)
(file-name-absolute-p denote-directory))))
(let ((denote-directory (list "/tmp/denote-test-notes-1" "/tmp/denote-test-notes-2")))
(should
(equal
(denote-directories)
(mapcar
(lambda (directory)
(file-name-as-directory (expand-file-name directory)))
denote-directory)))
(should (and (seq-every-p #'file-directory-p denote-directory)
(seq-every-p #'file-name-absolute-p denote-directory)))))
(ert-deftest dt-denote-directory ()
"Test that variable `denote-directory' returns an absolute directory name."
(let ((path (denote-directory)))
(should (and (file-directory-p path)
(file-name-absolute-p path)))))
(ert-deftest dt-denote-sluggify-title ()
"Test that `denote-sluggify-title' removes punctuation from the string.
Concretely, remove anything specified in `denote-sluggify-title'."
(should (equal (denote-sluggify-title "this-is-!@#test") "this-is-test")))
(should (equal (denote-sluggify-title "this-is-!@#test")
"this-is-test")))
(ert-deftest dt-denote-slug-keep-only-ascii ()
"Test that `denote-slug-keep-only-ascii' removes non-ASCII characters."
@ -110,89 +63,101 @@ Concretely, remove anything specified in `denote-sluggify-title'."
"Test that `denote-slug-hyphenate' hyphenates the string.
Also replace multiple hyphens with a single one and remove any
leading and trailing hyphen."
(should (equal (denote-slug-hyphenate "__ This is a test __ ") "This-is-a-test")))
(should (equal (denote-slug-hyphenate "__ This is a test __ ")
"This-is-a-test")))
(ert-deftest dt-denote-sluggify ()
"Test that `denote-sluggify' sluggifies the string.
To sluggify is to (i) downcase, (ii) hyphenate, (iii) de-punctuate, and (iv) remove spaces from the string."
(should (equal (denote-sluggify 'title " ___ !~!!$%^ This iS a tEsT ++ ?? ") "this-is-a-test")))
(should (equal (denote-sluggify 'title " ___ !~!!$%^ This iS a tEsT ++ ?? ")
"this-is-a-test")))
(ert-deftest Ddenote--slug-put-equals ()
"Test that `denote-slug-put-equals' replaces spaces/underscores with =.
Otherwise do the same as what is described in
`denote-test--denote-slug-hyphenate'.
`dt-denote-slug-hyphenate'.
The use of the equals sign is for the SIGNATURE field of the
Denote file name."
(should (equal (denote-slug-put-equals "__ This is a test __ ") "This=is=a=test")))
(should (equal (denote-slug-put-equals "__ This is a test __ ")
"This=is=a=test")))
(ert-deftest dt-denote-sluggify-signature ()
"Test that `denote-sluggify-signature' sluggifies the string for file signatures.
This is like `denote-test--denote-sluggify', except that it also
accounts for what we describe in `denote-test--denote-slug-put-equals'."
(should (equal (denote-sluggify-signature "--- ___ !~!!$%^ This -iS- a tEsT ++ ?? ") "this=is=a=test")))
This is like `dt-denote-sluggify', except that it also
accounts for what we describe in `dt-denote-slug-put-equals'."
(should (equal (denote-sluggify-signature "--- ___ !~!!$%^ This -iS- a tEsT ++ ?? ")
"this=is=a=test")))
(ert-deftest dt-denote-sluggify-keyword ()
"Test that `denote-sluggify-keyword' sluggifies the string while joining words.
In this context, to join words is to elimitate any space or
delimiter between them.
Otherwise, this is like `denote-test--denote-sluggify'."
(should (equal (denote-sluggify-keyword "--- ___ !~!!$%^ This iS a - tEsT ++ ?? ") "thisisatest")))
Otherwise, this is like `dt-denote-sluggify'."
(should (equal (denote-sluggify-keyword "--- ___ !~!!$%^ This iS a - tEsT ++ ?? ")
"thisisatest")))
(ert-deftest dt-denote-sluggify-keywords ()
"Test that `denote-sluggify-keywords' sluggifies a list of strings.
The function also account for the value of the user option
`denote-allow-multi-word-keywords'."
(should (equal (denote-sluggify-keywords '("one !@# --- one" " two" "__ three __")) '("oneone" "two" "three"))))
(ert-deftest dt-denote-slug-remove-accents ()
"Test that `denote-slug-remove-accents' removed diacretics."
(should (string= (denote-slug-remove-accents "ê") "e"))
(should (string= (denote-slug-remove-accents "ñ") "n")))
(should
(equal (denote-sluggify-keywords '("one !@# --- one" " two" "__ three __"))
'("oneone" "two" "three"))))
(ert-deftest dt-denote--file-empty-p ()
"Test that `denote--file-empty-p' returns non-nil on empty file."
(let ((file (make-temp-file "denote-test")))
(should (denote--file-empty-p file))
(with-current-buffer (find-file-noselect file)
(insert "Hello world, this is not empty anymore!")
(save-buffer))
(should-not (denote--file-empty-p file))
(delete-file file)))
;; (should (null (denote--file-empty-p user-init-file))
(should (let ((file (make-temp-file "denote-test")))
(prog1
(denote--file-empty-p file)
(delete-file file)))))
(ert-deftest dt-denote-file-has-denoted-filename-p ()
"Test that `denote-file-has-denoted-filename-p' validates a Denote file name."
(should (denote-file-has-denoted-filename-p "20230522T154900--test__keyword.txt"))
(should-not (denote-file-has-denoted-filename-p "hello")))
(ert-deftest dt-denote-file-is-note-p ()
"Test that `denote-file-is-note-p' checks that files is a Denote note.
For our purposes, a note must note be a directory, must satisfy
`file-regular-p', its path must be part of the variable
`denote-directory', it must have a Denote identifier in its name,
and use one of the extensions implied by the variable `denote-file-type'."
(should (let* ((tmp (temporary-file-directory))
(denote-directory tmp)
(file (concat tmp "20230522T154900--test__keyword.txt")))
(with-current-buffer (find-file-noselect file)
(write-file file))
(prog1
(denote-file-is-note-p file)
(delete-file file)))))
(ert-deftest dt-denote-file-has-identifier-p ()
"Test that `denote-file-has-identifier-p' checks for a Denote identifier."
(should (denote-file-has-identifier-p "20230522T154900--test__keyword.txt"))
(should-not (denote-file-has-identifier-p "T154900--test__keyword.txt")))
(should (null (denote-file-has-identifier-p "T154900--test__keyword.txt"))))
(ert-deftest dt-denote-file-has-signature-p ()
"Test that `denote-file-has-signature-p' checks for a Denote signature."
(should (denote-file-has-signature-p "20230522T154900==sig--test__keyword.txt"))
(should-not (denote-file-has-signature-p "20230522T154900--test__keyword.txt")))
(should (null (denote-file-has-signature-p "20230522T154900--test__keyword.txt"))))
(ert-deftest dt-denote-file-has-supported-extension-p ()
"Test that `denote-file-has-supported-extension-p' matches a supported extension."
(let ((extensions (denote-file-type-extensions-with-encryption)))
(should
(member
(file-name-extension "20230522T154900==sig--test__keyword.txt" :period)
extensions))
(should-not
(member
(file-name-extension "20230522T154900==sig--test__keyword" :period)
extensions))))
(should
(member
(file-name-extension "20230522T154900==sig--test__keyword.txt" :period)
(denote-file-type-extensions-with-encryption)))
(should
(null
(member
(file-name-extension "20230522T154900==sig--test__keyword" :period)
(denote-file-type-extensions-with-encryption)))))
(ert-deftest dt-denote-file-type-extensions ()
"Test that `denote-file-type-extensions' returns file extensions.
We check for the common file type extensions, though the user can set
`denote-file-types' to nil and handle things on their own or, anyhow,
modify that variable."
We check for the common file type extensions, though the user can
theoretically set `denote-file-types' to nil and handle things on
their own. We do not have to test for that scenario, because
such a user will be redefining large parts of Denote's behaviour
with regard to file types."
(let ((extensions (denote-file-type-extensions)))
(should (or (member ".md" extensions)
(member ".org" extensions)
@ -200,7 +165,7 @@ modify that variable."
(ert-deftest dt-denote-file-type-extensions-with-encryption ()
"Test that `denote-file-type-extensions-with-encryption' covers encryption.
Extend what we do in `denote-test--denote-file-type-extensions'."
Extend what we do in `dt-denote-file-type-extensions'."
(let ((extensions (denote-file-type-extensions-with-encryption)))
(should (or (member ".md" extensions)
(member ".org" extensions)
@ -218,186 +183,331 @@ To make the test reproducible, set `denote-date-format' to a value that
does not involve the time zone."
(let ((denote-date-format "%Y-%m-%d")
(denote-front-matter-components-present-even-if-empty-value '(title keywords signature date identifier)))
(should (equal (denote--format-front-matter "" (date-to-time "20240101T120000") '("") "" "" 'text)
(string-join
'("title: "
"date: 2024-01-01"
"tags: "
"identifier: "
"signature: "
"---------------------------\n\n")
"\n")))
(should (equal (denote--format-front-matter "Some test" (date-to-time "2023-06-05") '("one" "two") "20230605T102234" "sig" 'text)
(string-join
'("title: Some test"
"date: 2023-06-05"
"tags: one two"
"identifier: 20230605T102234"
"signature: sig"
"---------------------------\n\n")
"\n")))
(should (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'org)
(string-join
'("#+title: "
"#+date: 2024-01-01"
"#+filetags: "
"#+identifier: "
"#+signature: "
"\n")
"\n")))
(should (equal
(denote--format-front-matter
"Some test" (date-to-time "2023-06-05") '("one" "two")
"20230605T102234" "sig" 'org)
(string-join
'("#+title: Some test"
"#+date: 2023-06-05"
"#+filetags: :one:two:"
"#+identifier: 20230605T102234"
"#+signature: sig"
"\n")
"\n")))
(should (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'markdown-yaml)
(string-join
'("---"
"title: \"\""
"date: 2024-01-01"
"tags: []"
"identifier: \"\""
"signature: \"\""
"---"
"\n")
"\n")))
(should (equal (denote--format-front-matter "Some test" (date-to-time "2023-06-05") '("one" "two") "20230605T102234" "sig" 'markdown-yaml)
(string-join
'("---"
"title: \"Some test\""
"date: 2023-06-05"
"tags: [\"one\", \"two\"]"
"identifier: \"20230605T102234\""
"signature: \"sig\""
"---"
"\n")
"\n")))
(should (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'markdown-toml)
(string-join
'("+++"
"title = \"\""
"date = 2024-01-01"
"tags = []"
"identifier = \"\""
"signature = \"\""
"+++"
"\n")
"\n")))
(should (equal (denote--format-front-matter "Some test" (date-to-time "2023-06-05") '("one" "two") "20230605T102234" "sig" 'markdown-toml)
(string-join
'("+++"
"title = \"Some test\""
"date = 2023-06-05"
"tags = [\"one\", \"two\"]"
"identifier = \"20230605T102234\""
"signature = \"sig\""
"+++"
"\n")
"\n")))))
(should (and (equal (denote--format-front-matter "" (date-to-time "20240101T120000") '("") "" "" 'text)
(mapconcat #'identity
'("title: "
"date: 2024-01-01"
"tags: "
"identifier: "
"signature: "
"---------------------------\n\n")
"\n"))
(equal
(denote--format-front-matter
"Some test" (date-to-time "2023-06-05") '("one" "two")
"20230605T102234" "sig" 'text)
(mapconcat #'identity
'("title: Some test"
"date: 2023-06-05"
"tags: one two"
"identifier: 20230605T102234"
"signature: sig"
"---------------------------\n\n")
"\n"))))
(should (and (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'org)
(mapconcat #'identity
'("#+title: "
"#+date: 2024-01-01"
"#+filetags: "
"#+identifier: "
"#+signature: "
"\n")
"\n"))
(equal
(denote--format-front-matter
"Some test" (date-to-time "2023-06-05") '("one" "two")
"20230605T102234" "sig" 'org)
(mapconcat #'identity
'("#+title: Some test"
"#+date: 2023-06-05"
"#+filetags: :one:two:"
"#+identifier: 20230605T102234"
"#+signature: sig"
"\n")
"\n"))))
(should (and (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'markdown-yaml)
(mapconcat #'identity
'("---"
"title: \"\""
"date: 2024-01-01"
"tags: []"
"identifier: \"\""
"signature: \"\""
"---"
"\n")
"\n"))
(equal
(denote--format-front-matter
"Some test" (date-to-time "2023-06-05") '("one" "two")
"20230605T102234" "sig" 'markdown-yaml)
(mapconcat #'identity
'("---"
"title: \"Some test\""
"date: 2023-06-05"
"tags: [\"one\", \"two\"]"
"identifier: \"20230605T102234\""
"signature: \"sig\""
"---"
"\n")
"\n"))))
(should (and (equal (denote--format-front-matter "" (date-to-time "20240101T120000") nil "" "" 'markdown-toml)
(mapconcat #'identity
'("+++"
"title = \"\""
"date = 2024-01-01"
"tags = []"
"identifier = \"\""
"signature = \"\""
"+++"
"\n")
"\n"))
(equal
(denote--format-front-matter
"Some test" (date-to-time "2023-06-05") '("one" "two")
"20230605T102234" "sig" 'markdown-toml)
(mapconcat #'identity
'("+++"
"title = \"Some test\""
"date = 2023-06-05"
"tags = [\"one\", \"two\"]"
"identifier = \"20230605T102234\""
"signature = \"sig\""
"+++"
"\n")
"\n"))))))
(ert-deftest dt-denote-format-file-name ()
"Test that `denote-format-file-name' returns all expected paths."
(let* ((title "Some test")
(id (format-time-string denote-date-identifier-format (denote-valid-date-p "2023-11-28 05:53:11")))
(denote-directory "/tmp/test-denote")
(dir (car (denote-directories)))
(ext (denote--file-extension 'org))
(kws '("one" "two")))
(should-error (denote-format-file-name nil id kws title ext ""))
(should-error (denote-format-file-name "" id kws title ext ""))
;; NOTE that `denote-directory' is the `let' bound value without the suffix
(should-error (denote-format-file-name denote-directory id kws title ext ""))
(should-error (denote-format-file-name dir "" nil "" ext ""))
(should (equal (denote-format-file-name dir nil kws title ext "") "/tmp/test-denote/--some-test__one_two.org"))
(should (equal (denote-format-file-name dir "" kws title ext "") "/tmp/test-denote/--some-test__one_two.org"))
(should (equal (denote-format-file-name dir "0123456" kws title ext "") "/tmp/test-denote/@@0123456--some-test__one_two.org"))
(should (equal (denote-format-file-name dir id kws title ext "") "/tmp/test-denote/20231128T055311--some-test__one_two.org"))
(should (equal (denote-format-file-name dir id nil "" ext "") "/tmp/test-denote/20231128T055311.org"))
(should (equal (denote-format-file-name dir id nil nil ext nil) "/tmp/test-denote/20231128T055311.org"))
(should (equal (denote-format-file-name dir id kws title ext "sig") "/tmp/test-denote/20231128T055311==sig--some-test__one_two.org"))))
(should-error (denote-format-file-name
nil
id
kws
title
(denote--file-extension 'org)
""))
(should-error (denote-format-file-name
""
id
kws
title
(denote--file-extension 'org)
""))
(should-error (denote-format-file-name
denote-directory ; notice this is the `let' bound value without the suffix
id
kws
title
(denote--file-extension 'org)
""))
(should-error (denote-format-file-name
(denote-directory)
""
nil
""
(denote--file-extension 'org)
""))
(should (equal (denote-format-file-name
(denote-directory)
nil
kws
title
(denote--file-extension 'org)
"")
"/tmp/test-denote/--some-test__one_two.org"))
(should (equal (denote-format-file-name
(denote-directory)
""
kws
title
(denote--file-extension 'org)
"")
"/tmp/test-denote/--some-test__one_two.org"))
(should (equal (denote-format-file-name
(denote-directory)
"0123456"
kws
title
(denote--file-extension 'org)
"")
"/tmp/test-denote/@@0123456--some-test__one_two.org"))
(should (equal (denote-format-file-name
(denote-directory)
id
kws
title
(denote--file-extension 'org)
"")
"/tmp/test-denote/20231128T055311--some-test__one_two.org"))
(should (equal (denote-format-file-name
(denote-directory)
id
nil
""
(denote--file-extension 'org)
"")
"/tmp/test-denote/20231128T055311.org"))
(should (equal (denote-format-file-name
(denote-directory)
id
nil
nil
(denote--file-extension 'org)
nil)
"/tmp/test-denote/20231128T055311.org"))
(should (equal (denote-format-file-name
(denote-directory)
id
kws
title
(denote--file-extension 'org)
"sig")
"/tmp/test-denote/20231128T055311==sig--some-test__one_two.org"))))
(ert-deftest dt-denote-get-file-extension ()
"Test that `denote-get-file-extension' gets the correct file extension."
(should (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing") ""))
(should (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org") ".org"))
(should (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.gpg") ".org.gpg"))
(should (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.age") ".org.age")))
(should (and (equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing") "")
(equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org") ".org")
(equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.gpg") ".org.gpg")
(equal (denote-get-file-extension "20231010T105034--some-test-file__denote_testing.org.age") ".org.age"))))
(ert-deftest dt-denote-get-file-extension-sans-encryption ()
"Test that `denote-get-file-extension-sans-encryption' gets the file extension without encryption."
(should (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing") ""))
(should (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org") ".org"))
(should (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.gpg") ".org"))
(should (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.age") ".org")))
;; TODO 2026-04-06: For the `denote-file-type'.
(should (and (equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing") "")
(equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org") ".org")
(equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.gpg") ".org")
(equal (denote-get-file-extension-sans-encryption "20231010T105034--some-test-file__denote_testing.org.age") ".org"))))
(ert-deftest dt-denote-filetype-heuristics ()
"Test that `denote-filetype-heuristics' gets the correct file type."
(should-not (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing"))
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org") 'org))
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.gpg") 'org))
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.age") 'org))
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt") 'text))
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.gpg") 'text))
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.age") 'text))
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md")
(caar (denote--file-types-with-extension ".md"))))
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.gpg")
(caar (denote--file-types-with-extension ".md"))))
(should (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.age")
(caar (denote--file-types-with-extension ".md")))))
(should (and (eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing") nil)
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org") 'org)
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.gpg") 'org)
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.org.age") 'org)
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt") 'text)
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.gpg") 'text)
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.txt.age") 'text)
;; NOTE 2023-10-11: It returns `markdown-yaml' as a fallback. In
;; an actual file, it reads the file contents to determine what
;; it is and can return `markdown-toml'. In principle, we should
;; be testing this here, though I prefer to keep things simple.
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md") 'markdown-yaml)
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.gpg") 'markdown-yaml)
(eq (denote-filetype-heuristics "20231010T105034--some-test-file__denote_testing.md.age") 'markdown-yaml))))
(ert-deftest dt-denote-retrieve-filename-identifier ()
"Test that `denote-retrieve-filename-identifier' returns only the identifier."
(should (null (denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing.org")))
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org") "20240610T194654"))
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org") "20240610T194654"))
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org") "20240610T194654"))
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org") "20240610T194654"))
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "20240610T194654"))
(should (equal (denote-retrieve-filename-identifier "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "20240610T194654")))
(should (and (null
(denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing.org"))
(equal
(denote-retrieve-filename-identifier "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")
"20240610T194654")
(equal
(denote-retrieve-filename-identifier "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
"20240610T194654")
(equal
(denote-retrieve-filename-identifier "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org")
"20240610T194654")
(equal
(denote-retrieve-filename-identifier "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org")
"20240610T194654")
(equal
(denote-retrieve-filename-identifier "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
"20240610T194654")
(equal
(denote-retrieve-filename-identifier "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
"20240610T194654"))))
(ert-deftest dt-denote-retrieve-filename-title ()
"Test that `denote-retrieve-filename-title' returns only the title."
(should (null (denote-retrieve-filename-title "/path/to/testing/20240610T194654__denote_testing.org")))
(should (equal (denote-retrieve-filename-title "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org") "this-is-a-test-reordered"))
(should (equal (denote-retrieve-filename-title "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org") "this-is-a-test-reordered"))
(should (equal (denote-retrieve-filename-title "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org") "this-is-a-test-reordered"))
(should (equal (denote-retrieve-filename-title "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org") "this-is-a-test-reordered"))
(should (equal (denote-retrieve-filename-title "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "this-is-a-test-reordered"))
(should (equal (denote-retrieve-filename-title "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "this-is-a-test-reordered")))
(should (and (null
(denote-retrieve-filename-title "/path/to/testing/20240610T194654__denote_testing.org"))
(equal
(denote-retrieve-filename-title "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")
"this-is-a-test-reordered")
(equal
(denote-retrieve-filename-title "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
"this-is-a-test-reordered")
(equal
(denote-retrieve-filename-title "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org")
"this-is-a-test-reordered")
(equal
(denote-retrieve-filename-title "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org")
"this-is-a-test-reordered")
(equal
(denote-retrieve-filename-title "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
"this-is-a-test-reordered")
(equal
(denote-retrieve-filename-title "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
"this-is-a-test-reordered"))))
(ert-deftest dt-denote-retrieve-filename-keywords ()
"Test that `denote-retrieve-filename-keywords' returns only the keywords."
(should (null (denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered.org")))
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org") "denote_testing"))
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org") "denote_testing"))
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org") "denote_testing"))
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org") "denote_testing"))
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "denote_testing"))
(should (equal (denote-retrieve-filename-keywords "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "denote_testing")))
(should (and (null
(denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered.org"))
(equal
(denote-retrieve-filename-keywords "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")
"denote_testing")
(equal
(denote-retrieve-filename-keywords "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
"denote_testing")
(equal
(denote-retrieve-filename-keywords "/path/to/testing/--this-is-a-test-reordered__denote_testing@@20240610T194654.org")
"denote_testing")
(equal
(denote-retrieve-filename-keywords "/path/to/testing/__denote_testing--this-is-a-test-reordered@@20240610T194654.org")
"denote_testing")
(equal
(denote-retrieve-filename-keywords "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
"denote_testing")
(equal
(denote-retrieve-filename-keywords "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
"denote_testing"))))
(ert-deftest dt-denote-retrieve-filename-signature ()
"Test that `denote-retrieve-filename-signature' returns only the signature."
(should (null (denote-retrieve-filename-signature "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org")))
(should (equal (denote-retrieve-filename-signature "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org") "signature"))
(should (equal (denote-retrieve-filename-signature "/path/to/testing/--this-is-a-test-reordered==signature__denote_testing@@20240610T194654.org") "signature"))
(should (equal (denote-retrieve-filename-signature "/path/to/testing/__denote_testing--this-is-a-test-reordered==signature@@20240610T194654.org") "signature"))
(should (equal (denote-retrieve-filename-signature "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered==signature.org") "signature"))
(should (equal (denote-retrieve-filename-signature "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org") "signature")))
(should (and (null
(denote-retrieve-filename-signature "/path/to/testing/20240610T194654--this-is-a-test-reordered__denote_testing.org"))
(equal
(denote-retrieve-filename-signature "/path/to/testing/20240610T194654==signature--this-is-a-test-reordered__denote_testing.org")
"signature")
(equal
(denote-retrieve-filename-signature "/path/to/testing/--this-is-a-test-reordered==signature__denote_testing@@20240610T194654.org")
"signature")
(equal
(denote-retrieve-filename-signature "/path/to/testing/__denote_testing--this-is-a-test-reordered==signature@@20240610T194654.org")
"signature")
(equal
(denote-retrieve-filename-signature "/path/to/testing/__denote_testing@@20240610T194654--this-is-a-test-reordered==signature.org")
"signature")
(equal
(denote-retrieve-filename-signature "/path/to/testing/==signature__denote_testing@@20240610T194654--this-is-a-test-reordered.org")
"signature"))))
(ert-deftest dt-denote-date-identifier-p ()
"Test that `denote-date-identifier-p' works for Denote identifiers."
(should (denote-date-identifier-p "20240901T090910"))
(should-not (denote-date-identifier-p "20240901T090910-not-identifier-format")))
(ert-deftest dt-denote-identifier-p ()
"Test that `denote-identifier-p' works for Denote identifiers."
(should (and (denote-identifier-p "20240901T090910")
(null (denote-identifier-p "20240901T090910-not-identifier-format")))))
(ert-deftest dt-denote-id-to-date ()
"Test that `denote-id-to-date' returns the date from an identifier."
@ -406,75 +516,21 @@ does not involve the time zone."
(ert-deftest dt-denote--date-convert ()
"Test that `denote--date-convert' works with dates."
(should (equal (denote--date-convert '(26454 45206 461174 657000) :list) '(26454 45206 461174 657000)))
(should (equal (denote--date-convert '(26454 45206 461174 657000) :string) "2024-12-09 10:55:50"))
(should (equal (denote--date-convert nil :string) ""))
(should (equal (denote--date-convert nil :list) nil))
(should (and
(equal (denote--date-convert '(26454 45206 461174 657000) :list)
'(26454 45206 461174 657000))
(equal (denote--date-convert '(26454 45206 461174 657000) :string)
"2024-12-09 10:55:50")
(equal (denote--date-convert nil :string)
"")
(equal (denote--date-convert nil :list)
nil)))
(should-error (denote--date-convert '(26454 45206 461174 657000) :not-valid-type))
(should-error (denote--date-convert nil :not-valid-type)))
;; TODO 2026-02-23: Add support for Windows paths. But I need somebody who has a Windows machine...
(ert-deftest dt-denote--get-common-root-directory ()
"Test that `denote--get-common-root-directory' returns the right path."
(should (string=
(denote--get-common-root-directory
'("/home/prot/Documents/notes/"
"/home/prot/Documents/notes/attachments"))
"/home/prot/Documents/notes/"))
(should (string=
(denote--get-common-root-directory
'("/home/prot/Documents/"
"/home/prot/Documents/notes/attachments"))
"/home/prot/Documents/"))
(should (string=
(denote--get-common-root-directory
'("/home/prot/Books/"
"/home/prot/Documents/notes/"))
"/home/prot/"))
(should (string=
(denote--get-common-root-directory
'("/tmp/notes/"
"/home/prot/Documents/notes/"))
"/"))
(should (string=
(denote--get-common-root-directory
'("/home/prot/Documents/notes/"))
"/home/prot/Documents/notes/"))
(should-error (denote--get-common-root-directory '("~/Documents/notes/"))))
(ert-deftest dt-denote-query--keywords-as-regexp ()
"Test that `denote-query--keywords-as-regexp' works as intended."
(should (string= (denote-query--keywords-as-regexp '("one" "two")) "_\\(?:one\\|two\\)"))
(should-error (denote-query--keywords-as-regexp "one"))
(should-error (denote-query--keywords-as-regexp '("one" 1))))
(ert-deftest dt-denote-file-type ()
"Test that function `denote-file-type' does what it is meant to."
(let* ((create-file-fn (lambda (name extension)
(expand-file-name
(format "denote-test-file-type-%s.%s" name extension)
(temporary-file-directory))))
(modify-file-fn (lambda (file contents)
(with-current-buffer (find-file-noselect file)
(erase-buffer)
(insert contents)
(save-buffer))))
(yaml-file (funcall create-file-fn "yaml" "md"))
(toml-file (funcall create-file-fn "toml" "md"))
(org-file (funcall create-file-fn "org" "org")))
(progn
(funcall modify-file-fn yaml-file denote-yaml-front-matter)
(should (eq (denote-file-type yaml-file) 'markdown-yaml)))
(progn
(funcall modify-file-fn toml-file denote-toml-front-matter)
(should (eq (denote-file-type toml-file) 'markdown-toml)))
(progn
(funcall modify-file-fn org-file denote-org-front-matter)
(should (eq (denote-file-type org-file) 'org)))
(should-not (denote-file-type "test"))
(let ((denote-file-types nil))
(should-not (denote-file-type "test.md")))))
(provide 'denote-test)
;;; denote-test.el ends here