diff options
Diffstat (limited to 'lib/doc/jao-org-notes.el')
-rw-r--r-- | lib/doc/jao-org-notes.el | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/lib/doc/jao-org-notes.el b/lib/doc/jao-org-notes.el new file mode 100644 index 0000000..a1b9dbe --- /dev/null +++ b/lib/doc/jao-org-notes.el @@ -0,0 +1,166 @@ +;;; jao-org-notes.el --- A simple system for org note taking -*- lexical-binding: t; -*- + +;; Copyright (C) 2020, 2021 jao + +;; Author: jao <mail@jao.io> +;; Keywords: tools + +;; 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: + +;; An org note per file, with consultable title and tags and a +;; backlinks approximation. + +;;; Code: + +(require 'org) +(require 'consult) + +(defvar jao-org-notes-dir (expand-file-name "notes" org-directory)) + +(defvar jao-org-notes--rg + (concat "rg --null --line-buffered --color=ansi --max-columns=250" + " --no-heading --line-number --smart-case" + " . -e \"^(#.(title|filetags): .*)ARG\" OPTS")) + +(defvar jao-org-notes--trx ":[0-9]+:#\\+\\(file\\)?\\(title\\|tags\\):") + +(defun jao-org-notes--clean-match (m) + (cons (replace-regexp-in-string jao-org-notes--trx " (\\2)" (car m)) (cdr m))) + +(defun jao-org-notes--matches (lines) + (let ((ms (consult--grep-matches lines))) + (mapcar #'jao-org-notes--clean-match ms))) + +(defun jao-org-notes--lookup (_ cands cand) + (or (cadr (assoc cand cands)) cand)) + +(defvar jao-org-notes--grep-history nil) + +(defun jao-org--grep (prompt &optional cat no-req) + (let ((default-directory (expand-file-name (or cat "") jao-org-notes-dir))) + (consult--read + (consult--async-command jao-org-notes--rg + (consult--async-transform jao-org-notes--matches)) + :prompt prompt + :lookup #'jao-org-notes--lookup + :initial consult-async-default-split + :add-history (concat consult-async-default-split (thing-at-point 'symbol)) + :require-match (not no-req) + :category 'jao-org-notes-lookup + :history '(:input jao-org-notes--grep-history) + :sort nil))) + +(defun jao-org-notes--cat () + (let* ((cats (seq-difference (directory-files jao-org-notes-dir) + '("." ".." "attic"))) + (cat (completing-read "Top level category: " cats))) + (cond ((file-exists-p (expand-file-name cat jao-org-notes-dir)) cat) + ((yes-or-no-p "New category, create?") cat) + (t (jao-roam--cat))))) + +(defun jao-org-notes--insert-title () + (let* ((cat (jao-org-notes--cat)) + (title (file-name-base (jao-org--grep "Title: " cat t))) + (title (replace-regexp-in-string "^#" "" title))) + (when (not (string-empty-p title)) + (let* ((base (replace-regexp-in-string " +" "-" (downcase title))) + (fname (expand-file-name (concat cat "/" base ".org") + jao-org-notes-dir)) + (exists? (file-exists-p fname))) + (find-file fname) + (when (not exists?) + (insert "#+title: " title "\n") + t))))) + +(defvar jao-org-notes--tags nil) + +(defun jao-org-notes--update-tags () + (setq jao-org-notes--tags + (org-global-tags-completion-table + (directory-files-recursively jao-org-notes-dir ".*\\.org")))) + +(defun jao-org-notes--read-tags () + (unless jao-org-notes--tags (jao-org-notes--update-tags)) + (completing-read-multiple "Tags: " jao-org-notes--tags)) + +(defun jao-org-notes--insert-tags () + (insert "#+filetags: " (mapconcat #'identity (jao-org-notes--read-tags) " ") "\n")) + +(defun jao-org-notes--insert-date () + (insert "#+date: ") + (org-insert-time-stamp (current-time)) + (insert "\n")) + +(defun jao-org-notes--template (k) + `(,k "Note" plain (file jao-org-notes-open-or-create) "\n- %a " + :immediate-finish t :jump-to-captured t)) + +;;;###autoload +(defun jao-org-notes-open () + "Search for a note file, matching tags and titles with completion." + (interactive) + (when-let (f (jao-org--grep "Search notes: ")) + (find-file f))) + +;;;###autoload +(defun jao-org-notes-open-or-create () + "Open or create a new note file, matching tags and titles with completion." + (interactive) + (when (jao-org-notes--insert-title) + (jao-org-notes--insert-date) + (jao-org-notes--insert-tags) + (jao-org-notes--update-tags) + (insert "#+link: ")) + (save-buffer) + (buffer-file-name)) + +;;;###autoload +(defun jao-org-notes-backlinks () + "Show a list of note files linking to the current one." + (interactive) + (consult-ripgrep jao-org-notes-dir + (concat "\\[\\[file:(.*/)?" (regexp-quote (buffer-name))))) + +;;;###autoload +(defun jao-org-notes-insert-tags () + "Insert a list of tags at point, with completing read." + (interactive) + (insert (mapconcat 'identity (jao-org-notes--read-tags) " "))) + +;;;###autoload +(defun jao-org-notes-insert-link () + "Select a note file (with completion) and insert a link to it." + (interactive) + (when-let (f (jao-org--grep "Notes file: ")) + (let ((rel-path (file-relative-name f default-directory)) + (title (with-current-buffer (find-file-noselect f) + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "^#\\+title: \\(.+\\)" nil t) + (match-string 1)))))) + (insert (format "[[file:%s][%s]]" rel-path title))))) + +;;;###autoload +(defun jao-org-notes-setup (mnemonic) + "Set up the notes system, providing a mnemonic character for its org template." + (setq org-capture-templates + (add-to-list 'org-capture-templates (jao-org-notes--template mnemonic))) + (add-to-list 'org-agenda-files jao-org-notes-dir) + (when (fboundp 'org-capture-upgrade-templates) + (org-capture-upgrade-templates org-capture-templates))) + +(provide 'jao-org-notes) +;;; jao-org-notes.el ends here |