branch: externals/denote commit a2e44974863d42528d0003c91fa4c00d79fecf6f Author: Protesilaos Stavrou <i...@protesilaos.com> Commit: Protesilaos Stavrou <i...@protesilaos.com>
Expand the "Extending Denote" section --- README.org | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 204 insertions(+), 28 deletions(-) diff --git a/README.org b/README.org index 17a7c91ca0..995e4d9cbb 100644 --- a/README.org +++ b/README.org @@ -1204,34 +1204,210 @@ described in its doc string." 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. - -- To search through notes, use =M-x grep=, =M-x find-name-dired=, =M-x - consult-find=, =M-x consult-grep=, and so on (the latter two are - provided by the =consult= package). - -- To quickly jump to the ~denote-directory~, visit it with =M-x - find-file= and then make a bookmark with =M-x bookmark-set=. Access - bookmarks with =M-x bookmark-jump=, =M-x consult-buffer= (from - =consult=), and the like. - -- Control the versioning of notes by turning the ~denote-directory~ into - a Git project. Consider the built-in project.el or the =projectile= - package, as well as the built-in VC framework and/or the =magit= - package. - -- It is possible to narrow the list of notes in Dired using a regular - expression or literal string. Do =M-x dired-mark-files-regexp RET - type-regexp-here RET t k=. The =t= will toggle the match so that it - marks all files that do not match the regexp and =k= will remove them - from the buffer (restore them by reverting the buffer). - -- A narrowed list of files can also be produced through the minibuffer, - with the help of the =embark= package. For example, =M-x find-file - RET path/to/denote-directory RET regexp embark-act embark-export=. - The final two commands, ~embark-act~ and ~embark-export~, are normally - bound to keys. The whole sequence will thus look like =C-x C-f path - RET regexp C-. E=. +specialised third-party packages. This section covers the details. + +** 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 to 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 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-link-find-file~ 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: + +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. + +** Use the consult-notes package +:PROPERTIES: +:CUSTOM_ID: h:8907f4bc-992a-45bc-a60e-267ed1ce9c2d +:END: + +If you are already using =consult= (which is a brilliant package), you +will probably like its =consult-notes= extension. It uses the familiar +mechanisms of Consult to filter searches via a prefix key. For example: + +#+begin_src emacs-lisp +(setq consult-notes-data-dirs + `(("Notes" ?n ,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 =n= and space, it narrows the list to just the +notes, while =b= does the same for books. + +Note that =consult-notes= is in its early stages of development. Expect +improvements in the near future (written on 2022-06-22 16:48 +0300). + +** Treat your notes as a project +:PROPERTIES: +:CUSTOM_ID: h:fad3eb08-ddc7-43e4-ba28-210d89668037 +:END: + +Emacs 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). * Installation :PROPERTIES: