protesilaos.denote/denote-org-dblock.el
2022-12-16 10:11:56 +02:00

149 lines
5.9 KiB
EmacsLisp

;;; denote-org-dblock.el --- Org Dynamic blocks for denote.el -*- lexical-binding: t -*-
;; Copyright (C) 2022 Free Software Foundation, Inc.
;; Author: Elias Storms <elias.storms@gmail.com>
;; Maintainer: Denote Development <~protesilaos/denote@lists.sr.ht>
;; URL: https://git.sr.ht/~protesilaos/denote
;; Mailing-List: https://lists.sr.ht/~protesilaos/denote
;; This file is NOT part of GNU Emacs.
;; 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
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; This file provides a specialized Org-mode extension to Denote: it
;; introduces Org Dynamic blocks that collect links to Denote notes
;; based on a provided regexp. In short, this automates
;; 'denote-link-add-links'.
;;
;; For more information, read the commented code below or refer to the
;; Denote manual
;;; Code:
(require 'denote)
(require 'org)
;;; Org-mode Dynamic blocks
;;;; Dynamic block to search links
;; Org-mode has Dynamic blocks the content of which can be computed
;; dynamically based on their header. This functionality can be
;; leveraged to create automated lists of links to specific notes
;; (similar to 'denote-link-add-links', but with the added benefit
;; that the list can be updated easily).
;;
;; A dynamic block of the 'denote-links' type looks like this:
;;
;; #+BEGIN: denote-links :regexp "denote"
;;
;; #+END:
;;
;; With point at the #+BEGIN: line, pressing 'C-c C-c' will replace the
;; contents of the block with links to notes matching the search
;; ':regexp'. See also the denote manual on 'denote-link-add-links'.
;;
;; Inserting a block can be done via the Org-mode entry point
;; 'org-dynamic-block-insert-dblock' and selecting 'denote-links' from
;; the list, or directly by calling 'denote-org-dblock-insert-links'.
;;
;;
;; Org Dynamic blocks of the denote-links type can have the follwoing
;; arguments (in any order):
;; 1. :regexp "string" -- the search input (required)
;; 2. :missing-only t -- to only include missing links
;; 3. :reverse t -- reverse sort order (or don't, when nil)
;; 4. :block-name "n" -- to include a name for later processing
;;
;; By default ':missing-only t' is included as a parameter in the
;; block's header, so that only "missing links" are included (i.e.,
;; links to notes that the current buffer doesn't already link to).
;; Remove this parameter or set to 'nil' to include all matching
;; notes.
;;
;; With ':reverse' the value of 'denote-link-add-links-sort' can be
;; let-bound specifically for this list of links. For more
;; information, see the documentation of this variable.
;;
;; With ':block-name "string"' include a '#+NAME: string' line in the
;; Dynamic block. This allows use of the Dynamic block output as input
;; for further computation, e.g. in Org source blocks.
;;;###autoload
(defun denote-org-dblock-insert-links (regexp)
"Create Org dynamic block to insert Denote links matching REGEXP."
(interactive
(list
(read-regexp "Search for notes matching REGEX: " nil 'denote-link--add-links-history)))
(org-create-dblock (list :name "denote-links"
:regexp regexp
:missing-only 't))
(org-update-dblock))
(org-dynamic-block-define "denote-links" 'denote-org-dblock-insert-links)
;; By using the `org-dblock-write:' format, Org-mode knows how to
;; compute the dynamic block. Inner workings of this function copied
;; from `denote-link-add-links'.
(defun org-dblock-write:denote-links (params)
"Function to update `denote-links' Org Dynamic blocks.
Used by `org-dblock-update' with PARAMS provided by the dynamic block."
(let ((regexp (plist-get params :regexp))
(missing-only (plist-get params :missing-only))
(block-name (plist-get params :block-name))
(denote-link-add-links-sort (plist-get params :reverse))
(current-file (buffer-file-name)))
(when block-name
(insert "#+name: " block-name "\n"))
(if missing-only
(progn
(denote-link-add-missing-links regexp)
(join-line)) ;; remove trailing empty line left by denote-link--prepare-links
(when-let ((files (delete current-file
(denote-directory-files-matching-regexp regexp))))
(insert (denote-link--prepare-links files current-file nil))
(join-line))))) ;; remove trailing empty line
;;;; Dynamic block for backlinks
;; Similarly, we can create a 'denote-backlinks' block that inserts
;; links to notes that link to the current note.
;; Note that this block type doesn't take any additional parameters
;; (such as ':missing-only').
;;;###autoload
(defun denote-org-dblock-insert-backlinks ()
"Insert new Org dynamic block to include backlinks."
(interactive)
(org-create-dblock (list :name "denote-backlinks"))
(org-update-dblock))
(org-dynamic-block-define "denote-backlinks" 'denote-org-dblock-insert-backlinks)
(defun org-dblock-write:denote-backlinks (_params)
"Function to update `denote-backlinks' Org Dynamic blocks.
Used by `org-dblock-update' with PARAMS provided by the dynamic block."
(when-let* ((file (buffer-file-name))
(id (denote-retrieve-filename-identifier file))
(files (delete file (denote--retrieve-files-in-xrefs id))))
(insert (denote-link--prepare-links files file nil))
(join-line))) ;; remove trailing empty line
(provide 'denote-org-dblock)
;;; denote-org-dblock.el ends here