summaryrefslogtreecommitdiffhomepage
path: root/lib/doc/jao-org-notes.el
diff options
context:
space:
mode:
Diffstat (limited to 'lib/doc/jao-org-notes.el')
-rw-r--r--lib/doc/jao-org-notes.el171
1 files changed, 105 insertions, 66 deletions
diff --git a/lib/doc/jao-org-notes.el b/lib/doc/jao-org-notes.el
index 738c938..bd82543 100644
--- a/lib/doc/jao-org-notes.el
+++ b/lib/doc/jao-org-notes.el
@@ -1,6 +1,6 @@
;;; jao-org-notes.el --- A simple system for org note taking -*- lexical-binding: t; -*-
-;; Copyright (C) 2020, 2021, 2022 jao
+;; Copyright (C) 2020, 2021, 2022, 2024 jao
;; Author: jao <mail@jao.io>
;; Keywords: tools
@@ -26,36 +26,64 @@
;;; Code:
(require 'org)
(require 'consult)
+(require 'jao-shell)
(defvar jao-org-notes-dir (expand-file-name "notes" org-directory))
-(defun jao-org-notes--rg (str)
+(defun jao-org-notes-list ()
+ (directory-files-recursively jao-org-notes-dir "\\.org$"))
+
+(defun jao-org-notes--rg-cmd (rgx &rest args)
`("rg" "--null" "--line-buffered" "--color=never" "--max-columns=250"
- "--no-heading" "--line-number" "--smart-case" "." "-e"
- ,(format "^(#.(title|filetags): .*)%s" str)))
+ "--type=org" "--line-number" "--no-heading" "--smart-case"
+ ,@args ,default-directory "-e" ,rgx))
+
+(defun jao-org-notes--rg-title-or-tags (str)
+ (let* ((m (string-match "^\\([^/]+\\)/\\(.*\\)" str))
+ (d (or (and m (match-string 1 str)) ""))
+ (str (if m (match-string 2 str) str))
+ (default-directory
+ (if (file-directory-p d) (expand-file-name d) default-directory))
+ (ts (mapconcat #'identity (split-string str "[:,]+" t) ":|"))
+ (rgx (format "^#.(title: .*%s|(tags:.*(%s:)))" str ts)))
+ (jao-org-notes--rg-cmd rgx "-m" "2")))
(defun jao-org-notes--clean-match (m)
- (cons (format "%s %s"
- (replace-regexp-in-string "^\\./" "" (car m))
- (replace-regexp-in-string "[0-9]+:#\\+\\(file\\)?\\(title\\|tags\\):"
- " (\\2)" (cadr m)))
- (expand-file-name (car m) default-directory)))
+ (list (format "%s %s"
+ (replace-regexp-in-string default-directory "" (car m) nil t)
+ (replace-regexp-in-string "[0-9]+:#\\+\\(title\\|tags\\):"
+ "" (cadr m)))
+ (expand-file-name (car m) default-directory)
+ (string-to-number (cadr m))))
(defun jao-org-notes--matches (lines)
(mapcar (lambda (l) (jao-org-notes--clean-match (split-string l "\0" t))) lines))
+(defun jao-org-notes--grep-rx (rx &rest rg-args)
+ (let ((default-directory jao-org-notes-dir))
+ (jao-org-notes--matches
+ (apply #'jao-shell-cmd-lines (apply #'jao-org-notes--rg-cmd rx rg-args)))))
+
(defvar jao-org-notes--grep-history nil)
-(defun jao-org--grep (prompt &optional cat no-req)
+(defun jao-org-notes--consult-group (m transform)
+ (or (and transform m)
+ (and (string-match-p "^[^:]+ + :" m) "tags")
+ "titles"))
+
+(defun jao-org-notes--consult-rg (prompt &optional cat no-req cmd)
(let ((default-directory (expand-file-name (or cat "") jao-org-notes-dir)))
(consult--read
- (consult--async-command #'jao-org-notes--rg
+ (consult--async-command #'jao-org-notes--rg-title-or-tags
(consult--async-transform jao-org-notes--matches))
:prompt prompt
:initial (consult--async-split-initial "")
:add-history (concat (consult--async-split-initial (thing-at-point 'symbol)))
:require-match (not no-req)
:category 'jao-org-notes-lookup
+ :group 'jao-org-notes--consult-group
+ :lookup (lambda (cand cands &rest _)
+ (or (cadr (assoc cand cands)) (substring cand 1)))
:history '(:input jao-org-notes--grep-history))))
(defun jao-org-notes-cats ()
@@ -64,15 +92,15 @@
(defun jao-org-notes--cat ()
(let* ((cat (completing-read "Top level category: " (jao-org-notes-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)))))
+ ((yes-or-no-p "New category, create?") cat))))
(defun jao-org-notes--insert-title ()
(let* ((cat (jao-org-notes--cat))
- (title (file-name-base (jao-org--grep "Title: " cat t)))
+ (title (file-name-base (jao-org-notes--consult-rg "Title: " cat t)))
(title (replace-regexp-in-string "^#" "" title)))
(when (not (string-empty-p title))
(let* ((base (replace-regexp-in-string " +" "-" (downcase title)))
+ (base (replace-regexp-in-string "[^-[:alnum:][:digit:]]" "" base))
(fname (expand-file-name (concat cat "/" base ".org")
jao-org-notes-dir))
(exists? (file-exists-p fname)))
@@ -81,85 +109,88 @@
(insert "#+title: " title "\n")
t)))))
-(defvar jao-org-notes--tags nil)
-(defvar jao-org-notes-tags-cache-file "~/.emacs.d/cache/tags.eld")
-
-(defun jao-org-notes--save-tags ()
- (with-current-buffer (find-file-noselect jao-org-notes-tags-cache-file)
- (delete-region (point-min) (point-max))
- (print jao-org-notes--tags (current-buffer))
- (let ((message-log-max nil)
- (inhibit-message t))
- (save-buffer))))
+(defun jao-org-notes--find-tag (tag)
+ (jao-org-notes--grep-rx (format "^#.tags:.*:%s:" tag) "-m" "1"))
-(defun jao-org-notes--read-tags-cache ()
- (let ((b (find-file-noselect jao-org-notes-tags-cache-file)))
- (with-current-buffer b (goto-char (point-min)))
- (setq jao-org-notes--tags (read b))))
+(defvar jao-org-notes--tags nil)
+(defvar jao-org-notes--tag-history nil)
(defun jao-org-notes--read-tags ()
- (unless jao-org-notes--tags (jao-org-notes--read-tags-cache))
- (let* ((tags (completing-read-multiple "Tags: " jao-org-notes--tags))
- (new-tags (seq-difference tags jao-org-notes--tags)))
- (when new-tags
- (setq jao-org--notes-tags
- (sort (append new-tags jao-org-notes--tags) #'string<))
- (jao-org-notes--save-tags))
+ (let* ((tags (completing-read-multiple "Tags: " jao-org-notes--tags nil nil nil
+ 'jao-org-notes--tag-history)))
+ (setq jao-org-notes--tags (seq-union jao-org-notes--tags tags #'string=))
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\n %i"
- :jump-to-captured t))
+ `(,k "Note" plain (file jao-org-notes-create)
+ "%(if %:url \"#+link: %:url\" \"\")\n\n- %a\n %i"))
+
+(defun jao-org-notes-all-tags ()
+ (let ((tags nil))
+ (dolist (m (jao-org-notes--find-tag ".*"))
+ (setq tags (seq-union tags (cdr (split-string (car m) ":" t)))))
+ (sort tags #'string<)))
+
+(defun jao-org-notes-find-for-pdf (&optional file-name)
+ "Given a PDF file name, find its org notes counterpart."
+ (let* ((file-name (or file-name buffer-file-name))
+ (bn (file-name-base file-name))
+ (rx (format "%s\\.org$" (regexp-quote bn)))
+ (pred (lambda () (string-prefix-p jao-org-notes-dir buffer-file-name))))
+ (save-some-buffers nil pred)
+ (or (car (directory-files-recursively jao-org-notes-dir rx))
+ (let* ((d (completing-read "Notes subdir: " (jao-org-notes-cats) nil t))
+ (d (file-name-as-directory d)))
+ (expand-file-name (concat d bn ".org") jao-org-notes-dir)))))
-;;;###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: "))
+ (when-let (f (jao-org-notes--consult-rg "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."
+(defun jao-org-notes-consult-tags ()
+ "Search for a note file, matching all tags with completion."
+ (interactive)
+ (let* ((tags (jao-org-notes--read-tags))
+ (init (concat "^..tags: " (mapconcat #'identity tags " "))))
+ (consult-ripgrep jao-org-notes-dir init)))
+
+(defun jao-org-notes-consult-ripgrep (&optional initial cat)
+ (interactive)
+ (consult-ripgrep (expand-file-name (or cat "") jao-org-notes-dir) initial))
+
+(defun jao-org-notes-create ()
+ "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))
+ (org-insert-time-stamp (current-time) t t "#+date: " "\n")
+ (insert "#+tags: :"
+ (mapconcat #'identity (jao-org-notes--read-tags) ":")
+ ":\n"))
(save-buffer)
(buffer-file-name))
-;;;###autoload
-(defun jao-org-notes-grep (&optional initial)
- "Perform a grep search on all org notes body, via consult-ripgrep."
- (interactive)
- (consult-ripgrep jao-org-notes-dir initial))
-
-;;;###autoload
(defun jao-org-notes-backlinks ()
"Show a list of note files linking to the current one."
(interactive)
- (jao-org-notes-search (concat "\\[\\[file:\\(.*/\\)?" (buffer-name))))
+ (if-let* ((res (jao-org-notes--grep-rx
+ (concat "\\[file:.*" (regexp-quote (buffer-name)) "\\]\\[")))
+ (file (completing-read "File: " res nil t nil))
+ (entry (assoc file res)))
+ (progn (find-file (cadr entry))
+ (when-let (line (caddr entry)) (goto-line line)))
+ (message "Nobody links here!")))
-;;;###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) " ")))
+ (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: "))
+ (when-let (f (jao-org-notes--consult-rg "Notes file: "))
(let ((rel-path (file-relative-name f default-directory))
(title (with-current-buffer (find-file-noselect f)
(save-excursion
@@ -168,11 +199,19 @@
(match-string 1))))))
(insert (format "[[file:%s][%s]]" rel-path title)))))
+(defun jao-org-notes-stats ()
+ (interactive)
+ (message "%d notes, %d tags in %s"
+ (length (jao-org-notes-list))
+ (length jao-org--notes-tags)
+ jao-org-notes-dir))
+
;;;###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-capture-templates (jao-org-notes--template mnemonic))
+ jao-org-notes--tags (jao-org-notes-all-tags))
(when (fboundp 'org-capture-upgrade-templates)
(org-capture-upgrade-templates org-capture-templates)))