Skip to content

Simple notes for Emacs with an efficient file-naming scheme (we are close to the first stable release)

License

Notifications You must be signed in to change notification settings

jeanphilippegg/denote

 
 

Repository files navigation

Fork of Denote

This is a fork of Denote, developped by Protesilaos and many other contributors (me included).

This fork is mostly compatible with Denote. There are a few differences to be aware of. They are listed below.

The purpose of this fork is to provide a simplified version of the original project focusing on the composability and hackability of Denote. Those are core principles in both projects. The idea is to provide a solid core of composable functions that users can use to build their own personal note-taking system. As such, some convenience functions provided in upstream are not provided by this fork. There are many ways one can configure a note-taking system. The creation of the personalized commands specific to a user’s use cases are left to the user. Examples on how to configure Denote to one’s needs are given below in this manual.

Functionality-wise, there should not be a lot missing from upstream.

Many new features are also developed here that I intend to contribute back to Denote. The main ones are:

  • denote-directory accepts a list of directories.
  • File name components (title, keywords, signature, identifier) can be reordered (or absent)
  • Improved renaming commands. denote-rename-file supports relocating a file and changing its file type. The code of the renaming commands has been simplified in the process.
  • Variables to create custom denote commands (denote-forced-* variables).

I will be contributing what I can to Denote if it makes sense.

Like the code, part of this manual has been adapted from Protesilaos’s Denote manual.

Contributions

Please contribute code and open issues in Denote if the feature is also available there and makes sense in the main project.

I would also prefer that you open issues, rather than provide code. I want to contribute back the code of this project to Denote and it requires FSF copyright assignment for code contributions.

COPYING

Copyright (C) 2022-2024 Free Software Foundation, Inc.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with the Front-Cover Texts being “A GNU Manual,” and with the Back-Cover Texts as in (a) below. A copy of the license is included in the section entitled “GNU Free Documentation License.”

(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and modify this GNU manual.”

Differences with the original project

Removed code

I have removed a lot of code from Denote. Even if a lot of code has been removed from Denote, most functionalities have been retained. The few functionalities that have been removed were removed because users should be responsible for their configuration or it is extra functionalities.

Removed: sluggification utility functions

I have disabled the default sluggification for keywords and signature. Unlike titles, those components can be typed correctly from the prompts by users. This functionality can be reinstated by user through denote-file-name-slug-functions.

Many internal functions that were involved in this task have been removed.

Removed: The *-with-command commands

Reason for removal: Users should configure this themselves using more specialized tools and to match their specific needs.

Alternatives: standard keymaps, transient keymaps, hydra, etc.

The reason for their suppression is that I want to emphasize the composability of Denote while also simplifying the code base. I do not want to cut on useful functionalities, but the job of selecting an appropriate Emacs command is already handled well by various other built-in mechanisms or external packages. They include: standard prefix keymaps, transients, hydras, etc.

Removed: All convenience functions

Reason for removal: Users should create these functions according to their specific needs and build a menu tailored to their needs (see previous entry).

This includes denote-signature, denote-date, denote-type, denote-link-with-signature, etc.

Again, these provided useful functionalities, but they can easily be defined again in a user’s config according to one’s particular needs for his note-taking practices.

I think they may add confusion and hide the composability/hackability of Denote. It is harder to feel confident that a custom function is the right way to support one’s need when there is already a function that does something similar.

Some other functions like denote-link-with-signature are more involved, but still simple enough to be part of a user’s configuration rather than in Denote.

Removed: Legacy .dir-locals.el mechanism with default-directory

Reason: This is legacy in Denote, removed here.

Previously, it was possible to set denote-directory to default-directory (or local) in a .dir-locals.el. Denote has been fixed and it does not mention this possibility anymore. I have removed it here.

Removed: Aliases and obsolete names

Aliases and obsolete names have been removed.

Removed: denote-date-format variable

Reason for removal: What this variable does can controlled through the user option denote-file-types.

New features

denote-directory accepts a list of directories

denote-directory has gained the ability to receive a list of paths. This means that it is now possible to have notes spread across various locations. They do not need to be under a common directory.

Internally, the code has been adapted to work with a list of directories. The function denote-directories returns the value of denote-directory as a list. The function denote-directory returns the first element of denote-directory for backward compatability with how it worked before.

New notes are created under the first element of denote-directory. You can still choose to be prompted for a directory if you want. See denote-prompts.

Variables denote-forced-title, denote-forced-keywords, etc.

These variables are provided if a user wants to create a custom denote command that already has specific title, keywords, etc. We do not want any prompting, just use the keyword specified.

Here is an example of the idea:

(defun my-denote-journal-command ()
  (let ((denote-forced-keywords '("journal"))
        (denote-forced-subdirectory "~/notes/journal/")
        (denote-prompts '(title)))
    (call-interactively #'denote)))

In this example, we have created a journaling command that always create entries in the “journal/” subdirectory with the “journal” keyword and we are prompted for a title.

Improved renaming-commands (many simplifications and convenience commands provided)

The two low-level functions denote--rename-file and denote--rename-get-file-info-from-prompts-or-existing are the only necessary building blocks to handle all renaming commands.

With these, the code base is a lot cleaner. There is no duplicated code anymore. All commands obey the variables denote-prompts, denote-forced-* and denote-rename-no-confirm in the same way.

It is now also simple to implement your own renaming commands! Maybe you would like a denote-rename-meta-data that only renames the keywords and the signature (but not the title)? It is a trivial change away from denote-rename-keywords.

Additionally, denote-rename-file (and all deriving from it) gains the ability to change the file-type and the note’s directory.

All other commands have been refactored as convenience commands that use the 2 lower-level functions above as building blocks.

Here is the list of affected commands and their new status:

  • denote-rename-file: Refactored/simplified. This is still the main command, but its implementation is now trivial.
  • denote-dired-rename-marked-files: Refactored/simplified.
  • denote-dired-rename-marked-files-with-keywords: Refactored/simplified as a convenience.
  • denote-rename-file-using-front-matter: Provided as a convenience. However, its optional parameters have been dropped. They were not consistent with other commands anyway and it easy to create custom commands around this functionality.
  • denote-dired-rename-marked-files-using-front-matter: Refactored/simplified as a convenience.
  • denote-keywords-{add/remove}: Replaced with denote-rename-file-keywords.
  • denote-rename-{add/remove}-signature: Replaced with denote-rename-file-signature.
  • denote-change-file-type: Integrated into denote-rename-file. A convenience command denote-rename-file-type is also provided.

denote-file-name-components controls the order and presence of the file name components

File name components (identifier, title, keywords and signature) can be in any order.

Also, an item that is absent from this list will not be part of the file name. It will only be in the front matter, if applicable.

Note that even though we allow identifiers to be absent from file names, it is highly recommended to keep it. Otherwise, some functionalities may not work as expected (renaming commands) or at all (link to such a note).

TODO: Improve fontification

Installation

This fork is only available as a git repository. You can clone it and configure it in your init.el.

Core of Denote and use cases

What is a Denote note?

A Denote note is a single file with a specific file name structure. Additionally, text files can have a front matter handled by Denote.

Here is an example:

File name: 20240101T112314==z2a--my-note-example__emacs__philosophy.org

Content:

#title:     My note example   (raw title, ie not sluggified)
#keywords:    emacs philosophy   (same as file name)
#date:      2024-01-01

The file name contains an identifier, a signature, a title and keywords. All components are optional.

Any file (pdf, video, image) can be converted to this file naming scheme.

Text files can be of any extension if Denote is configured accordingly.

Denote can be seen as a package that helps organize simple text files by giving them some structure. This structure is flexible and can be adapted to one’s needs. For example, Denote provides commands to add keywords to files, a signature, rename a note, etc.

Additionally, Denote provide a linking mechanism between notes. It works just like links in Org-mode.

While the above is the heart of Denote and could be considered sufficient for a note-taking system, using Denote comes with other nice features. In a Dired buffer, the components of your notes will stand out, for instance. These extras facilities are explained later in this manual.

Core of Denote (creation, renaming and linking)

The core of Denote is its note creation, renaming and linking commands.

Creation/renaming:

  • denote

Renaming:

  • denote-rename-file

Linking:

  • denote-link

Link navigation commands:

  • denote-find-link
  • denote-find-backlinks
  • denote-backlinks

Other useful commands

Read their docstring.

File creation commands:

  • denote-open-or-create
  • denote-region
  • denote-org-capture

Note linking commands

  • denote-link-or-create
  • denote-link-after-creating
  • denote-add-links
  • denote-link-dired-marked-notes

Renaming functions:

  • denote-dired-rename-files
  • denote-dired-rename-marked-files-with-keywords
  • denote-rename-file-using-front-matter
  • denote-dired-rename-marked-files-using-front-matter

Building blocks for custom commands

denote, denote-link and denote-rename-file commands are already usable as building blocks to create custom commands specialized for one’s use cases. Explanations and examples are provided in the next section.

Configuration options

Options to build custom commands:

  • denote-prompts
  • denote-directories
  • denote-link-description-function
  • denote-file-type

General configuration options:

  • denote-file-types
  • denote-file-name-slug-functions
  • denote-templates
  • denote-excluded-directories-regexp
  • denote-after-new-note-hook
  • denote-rename-file-hook

Other options:

  • denote-save-buffers
  • denote-sort-keywords
  • denote-date-prompt-use-org-read-date
  • denote-backlinks-show-context
  • denote-rename-confirmations
  • denote-infer-keywords
  • denote-excluded-keywords-regexp

Configuring Denote for specific use cases

While Denote comes with sensible defaults, it can be configured to handle one’s specific use cases.

Following Denote’s philosophy, various commands can be created specific to one’s needs. Here is the general template for such a command

(defun my-denote-link ()
  (interactive)
  (let ((denote-prompts '(title signature))
        (denote-file-type 'md-yaml)
        (denote-link-description-function (lambda () "Link")))
    (call-interactively #'denote-link-or-create)))

When a user calls this custom command, the denote-link-or-create function is called and doing all the work. The link descriptions will always be “Link”. If the linked file does not exist, a new note (of type md-yaml) will be created.

Main example and sample configuration

This is a general example highlighting the power of Denote and how it can be configured to meet one’s requirements for note-taking.

(setq denote-directory '("~/notes" "~/music"))
(setq denote-prompts '(title keywords signature))

(defun denote-file-type ()
  (interactive)
  (let ((denote-prompts '(title keywords file-type)))
    (call-interactively #'denote)))

(defun denote-file-signature ()
  (interactive)
  (let ((denote-prompts '(title keywords signature)))
    (call-interactively #'denote)))

(defun denote-in-work-silo ()
  (interactive)
  (let ((denote-directory '("~/work/"))
        (denote-prompts '(title keywords file-type)))
    (call-interactively #'denote)))

(defun denote-signature-in-work-silo ()
  (interactive)
  (let ((denote-directory '("~/work/"))
        (denote-prompts '(title keywords signature)))
    (call-interactively #'denote)))

(defun my-journal-command ()
  (interactive)
  (let ((denote-prompts '(title))
        (denote-forced-keywords '("journal"))
        (denote-forced-directory '()))
    (call-interactively #'denote)))

(let ((map global-map))
  ;; Creation commands
  (define-key map (kbd "C-c n n") #'denote)
  (define-key map (kbd "C-c n t") #'denote-file-type)
  (define-key map (kbd "C-c n s") #'denote-signature)
  (define-key map (kbd "C-c w n") #'denote-in-work-silo)
  (define-key map (kbd "C-c w s") #'denote-signature-in-work-silo)
  (define-key map (kbd "C-c n j") #'my-journal-command)

  ;; Other commands
  (define-key map (kbd "C-c n i") #'denote-link)
  (define-key map (kbd "C-c n b") #'denote-backlinks)
  (define-key map (kbd "C-c n f f") #'denote-find-link)
  (define-key map (kbd "C-c n f b") #'denote-find-backlink)
  (define-key map (kbd "C-c n r") #'denote-rename-file))

Use case: Two silos (home and work) and .dir-locals.el

denote-directory can be set in a .dir-locals.el file.

Use case: A directory of notes inside a project (using .dir-locals.el)

Let’s say you have a project directory. Under that directory, you want to have ssubdirectory containing notes. Anywhere in your project, you want to have access to be able to create notes in that subdirectory.

The solution is simple. You can have a .dir-locals.el file at the root of your project and with this content:

((nil . ((denote-directory . ("~/path/to/project/notes/")))))

Use case: Always prompt for title, signature and file-type (in that order)

(setq denote-prompts '(title signature file-type))

Use case: Prompt for title, but fix the subdirectory to “journal/” and the keywords to ‘(“journal”)

(defun my-custom-command ()
  (interactive)
  (let ((denote-prompts '(title))
        (denote-forced-keywords '("journal"))
        (denote-forced-directory '()))
    (call-interactively #'denote)))

It is even possible to fix the title to display today’s information. See the next example.

Use case: Maintain a journal

This command does not prompt and creates a note titled “20240330T111111–saturday-30-march-2024__journal.txt” in the “journal/” subdirectory. If a note for that date already exists, open it instead.

(defun my-journal-command ()
  (interactive)
  (let ((existing-entry (car (denote-directories-files
                               (concat (format-time-string "%Y%m%d" (current-time)) "T[0-9]\\{6\\}.*__journal")))
        (denote-prompts '())
        (denote-file-type 'text)
        (denote-forced-title (format-time-string "%A %-d %B %Y" (current-time)))
        (denote-forced-keywords '("journal"))
        (denote-forced-directory "path/to/notes/journal/"))
    (if existing-entry
        (find-file existing-entry)
      (call-interactively #'denote)))))

Use case: Organize a menu of Denote custom commands

You can use standard keymaps, transient or external packages (for example Hydra) to create a menu of custom commands.

Use case: Custom link description

The descriptions of links can be configured with denote-link-description-function.

Links can be made to look like:

  • “Link”
  • “20240101T111111–My-note.org”
  • “My file title”
  • “My file title (keyword1, keyword2)”
  • “sig1z3 My other title” (the default if signature is present)
  • “My other title (sig1z3)”
  • A prompt for the description
  • etc.

Use case: Pre-fill the content of your notes with a template

Check denote-templates.

Use case: Composing everything together

See the main example above.

Use case: Customize the front matter

The front matter can be customized through the variable denote-file-types.

Lines can be removed and reordered.

Use case: Add a new file type

Denote comes with 4 supported files types (org, md-yaml, md-toml and txt), but other formats may be supported. They must be configured with denote-file-types.

Use case: Apply a function on file name components (sluggification)

By default, when a new note is created, the title that is typed is “sluggified”. Spaces are converted to hyphens and special characters are removed. The original title is preserved in the front matter of the file.

This sluggification can be configured with denote-file-name-slug-functions for each component (title, keyword, signature).

Acknowledgements

Denote is meant to be a collective effort. Every bit of help matters.

Author/maintainer of Denote
Protesilaos Stavrou.
Contributions to code or the manual
Abin Simon, Adam Růžička, Alan Schmitt, Ashton Wiersdorf, Benjamin Kästner, Bruno Boal, Charanjit Singh, Clemens Radermacher, Colin McLear, Damien Cassou, Eduardo Grajeda, Elias Storms, Eshel Yaron, Florian, Glenna D., Graham Marlow, Hilde Rhyne, Ivan Sokolov, Jack Baty, Jean-Charles Bagneris, Jean-Philippe Gagné Guay, Joseph Turner, Jürgen Hötzel, Kaushal Modi, Kai von Fintel, Kostas Andreadis, Kyle Meyer, Marc Fargas, Matthew Lemon, Noboru Ota (nobiot), Norwid Behrnd, Peter Prevos, Philip Kaludercic, Quiliro Ordóñez, Stefan Monnier, Stefan Thesing, Thibaut Benjamin, Tomasz Hołubowicz, Vedang Manerikar, Wesley Harvey, ezchi, leinfink (Henrik), mentalisttraceur, relict007.
Ideas and/or user feedback
Abin Simon, Aditya Yadav, Alan Schmitt, Aleksandr Vityazev, Alfredo Borrás, Ashton Wiersdorf, Benjamin Kästner, Claudiu Tănăselia, Colin McLear, Damien Cassou, Elias Storms, Federico Stilman, Florian, Frédéric Willem Frank Ehmsen, Glenna D., Guo Yong, Hanspeter Gisler, Jack Baty, Jay Rajput, Jean-Charles Bagneris, Jens Östlund, Jeremy Friesen, Jonathan Sahar, Johan Bolmsjö, Jousimies, Juanjo Presa, Kai von Fintel, Kaushal Modi, M. Hadi Timachi, Mark Olson, Mirko Hernandez, Niall Dooley, Paul van Gelder, Peter Prevos, Peter Smith, Suhail Singh, Shreyas Ragavan, Stefan Thesing, Summer Emacs, Sven Seebeck, Taoufik, TJ Stankus, Viktor Haag, Wade Mealing, Yi Liu, Ypot, atanasj, babusri, doolio, drcxd, hpgisler, pRot0ta1p, rbenit68, relict007, sienic, sundar bp.

Special thanks to Peter Povinec who helped refine the file-naming scheme, which is the cornerstone of this project.

Special thanks to Jean-Philippe Gagné Guay for the numerous contributions to the code base.

GNU Free Documentation License

About

Simple notes for Emacs with an efficient file-naming scheme (we are close to the first stable release)

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Emacs Lisp 100.0%