A pattern I really like in Notion is that you can a @
(at) mention any page with search as you type and autocomplete. I’d like to do something similar in org-mode so that I can quickly link related headlines.
Using the fantastic org-ql
library for Emacs, I integrated it into completions triggered by letters following the @
symbol. This translates the input after the @
symbol into a query and inserts a org-id
link when selecting a match.
(require 'org-ql)
(defun my/org-agenda-completions-at-point ()
"Function to be used as `completion-at-point' in Org mode."
(when (looking-back "@\\(\\(?:\\sw\\|\\s_\\|\\s-\\|\\s-:\\)+\\)")
(defvar heading-to-id (make-hash-table :test 'equal))
(let* ((start (match-beginning 1))
(end (point))
(input (match-string-no-properties 1))
(candidates (org-ql-select (org-agenda-files) (org-ql--query-string-to-sexp input)
:action (lambda ()
(let* ((heading (org-get-heading t))
(id (org-id-get (point))))
;; Avoid having to look
;; up the ID again since
;; we are visiting all
;; the locations with
;; org-ql anyway
(puthash heading id heading-to-id)
heading))))
(exit-function (lambda (heading status)
(when (eq status 'finished)
;; The +1 removes the @ symbol
(delete-char (- (+ (length heading) 1)))
(insert
(format "[[id:%s][%s]]" (gethash heading heading-to-id) heading))))))
(list start end candidates :exit-function exit-function))))
(defun my/org-agenda-completion-hook ()
"Configure org-mode for completion at point for org-agenda headlines."
(add-to-list 'completion-at-point-functions 'my/org-agenda-completions-at-point))
(add-hook 'org-mode-hook 'my/org-agenda-completion-hook)
See also:
- This personal infrastructure helps speed up my workflow when working on tasks
- Emacs is the ultimate editor building material
- The downside of using org-id links