Skip to main content

Projekt Medusa: Denote

Off with the head

Whoever has already looked through my projects will find that there is a project Medusa that I have set up for myself. In Emacs, there are functions that I only need very rarely, and then I often don't have the necessary command in mind. It is then quite helpful to have a hydra for this situation, and the project under which I combine all these hydras is called Medusa for me. Recently, I wanted to create a hydra for Denote, and I found that there were a few problems.

In general, I handle the hydras like this: I have two thumb buttons on my keyboard with which I control the hydras. On the left thumb is a rather general hydra that is intended for all functions that are not only needed in a specific mode. Here, you can imagine that the functions, that you normally reach with the prefix CTRL-x.

On the right thumb is a kind of context menu, i.e. the hydra that belongs to the respective mode I am currently in. You can imagine that there are all the functions that are accessible via CTRL-c as a prefix. Me treating the whole thing like a context menu, can also be seen in the fact that I have these hydras lying on the right mouse button at the same time.

No Mode, no gain

And here the problem already begins. Denote is an application that provides many commands to create and link loosely written notes with each other. However, since these notes can be written in all possible formats, the respective major mode of the format always applies. I write this blog, for example, in Org-Mode, which means that the major mode is not Denote but Org-Mode at the moment. The Package Major Mode Hydra therefore does not work.

However, Denote is also not a minor mode that can be turned on or off arbitrarily in all kinds of documents. A document is only a Denote document if it is located in a certain directory (silo) and follows a naming convention. And since Denote is not a minor mode, it also has no own keymap. But Emacs would not be Emacs if you couldn't do something about it.

We Build Ourselves a Minor Mode

It would be good if Denote had a minor mode that would be active whenever the active document is in a Denote silo. Since a silo is always also a directory, Emacs has a tool on board: directory-local variables.

The manual says:

The usual way to define directory-local variables is to place a file named .dir-locals.el in a directory. When Emacs opens a file in that directory or one of its subdirectories, the directory-local variables specified in .dir-locals.el are applied as if they were defined as file-local variables for that file. Emacs searches for .dir-locals.el, starting in the directory of the opened file, and moving upwards in the directory tree.

My file .dir-locals.el is located in the top directory of my silo, and looks like this:

((nil . ((denote-mode . t))))

This line ensures that the denote-mode is turned on for all files in this directory. This is a minor mode that Denote does not come with out of the box, but that we have to create ourselves first. This happens in the corresponding part of my Emacs configuration.

(define-minor-mode denote-mode
  "Denote is a simple note-taking tool for Emacs."
  :lighter " Note"
  :keymap (let ((map
                 (make-sparse-keymap)))
            (define-key map (kbd "C-l") 'denote-link-or-create)
            (define-key map (kbd "C-n") 'denote-link-after-creating)
            (define-key map (kbd "<f6>")(lambda () (interactive) (find-file "~/wiki")))
            map)) 

~:lighter specifies what is written in the mode line when the minor mode is active, and :keymap specifies which keymap should be used. In this case, an empty keymap is created using make-sparse-keymap, and filled with the most common commands I need. I probably could have left out this initial filling, since I'm going to do the final keybindings anyway, but I didn't find an example where an empty keymap was created.

The Actual Hydra

I then equip my freshly created keymap with the only important keys. <menu> is the key code that my right thumb button sends.

(define-key denote-mode-map (kbd "<menu>") #'medusa/denote/body)
(define-key denote-mode-map (kbd "<mouse-3>") #'medusa/denote/body)

When I click the right mouse button in a Denote silo, the following hydra appears:

nil

This is generated by the following code. I use the slightly shorter and nicer syntax that the Pretty Hydra package provides me with.

(pretty-hydra-define medusa/denote
  (:color blue :quit-key "<escape>" :title "Denote")
  ("Create" (
    ("n" denote "_n_ew note" )
    ("t" denote-type "other _t_ype" )
    ("d" denote-date  "other _d_ate" )
    ("s" denote-subdirectory  "other _s_ubdir" )
    ("T" denote-template  "with _T_emplate" )
    ("S" denote-signature  "with _S_ignature" ))
   "Link" (
    ("l" denote-link-or-create "_l_ink" )
    ("L" denote-link-or-create-with-command "_L_ink with command" )
    ("h" denote-org-extras-link-to-heading  "specific _h_eader" )
    ("r" denote-add-links "by _r_egexp" )
    ("d" denote-add-links "by _d_ired" )
    ("b" denote-backlinks "_b_acklinks" ))
   "Rename" (
    ("RF" denote-rename-file "Rename File")
    ("FT" denote-change-file-type-and-front-matter  "only FileType")
    ("UF" denote-rename-file-using-front-matter "use Frontmatter"))
   "Dyn. Block" (
    ("DL" denote-org-extras-dblock-insert-links "dyn. Links" )
    ("DB" denote-org-extras-dblock-insert-backlinks "dyn. Backlinks" ))
   "Convert links" (
    ("CF" denote-org-extras-convert-links-to-file-type "to File Type" )
    ("CD" denote-org-extras-convert-links-to-denote-type "to Denote Type" ))
  "Other" (
     ("?" (info "denote") "Help")
     ("<menu>" major-mode-hydra "Major Mode Hydra"))))
2024-05-03