;;; +org-capture.el -*- lexical-binding: t; -*- ;;; Code: (require 'cl-lib) (require 'acdw) ;; We don't require `org-capture' here because I'll have to require this library ;; to init.el /before/ org-capture is fully needed. But I do need to declare ;; `org-capture-templates'. (defvar org-capture-templates nil) (defun +org-capture--get (key &optional list) "Find KEY in LIST, or return nil. LIST defaults to `org-capture-templates'." (alist-get key (or list org-capture-templates) nil nil #'equal)) ;; Set it up as a generic value. Based on the one for `alist-get'. (gv-define-expander +org-capture--get (lambda (do key &optional alist) (setq alist (or alist org-capture-templates)) (macroexp-let2 macroexp-copyable-p k key (gv-letplace (getter setter) alist (macroexp-let2 nil p `(assoc ,k ,getter 'equal) (funcall do `(cdr ,p) (lambda (v) (macroexp-let2 nil v v (let ((set-exp `(if ,p (setcdr ,p ,v) ,(funcall setter `(cons (setq ,p (cons ,k ,v)) ,getter))))) `(progn ,set-exp ,v)))))))))) (defun +org-capture-sort (&optional list) "Sort LIST by string keys. LIST is a symbol and defaults to `org-capture-templates'." (setq list (or list 'org-capture-templates)) (set list (sort (symbol-value list) (lambda (a b) (string< (car a) (car b)))))) (defun +org-capture-sort-after-init (&optional list) "Sort LIST with `+org-capture-sort' after Emacs init." (+ensure-after-init #'+org-capture-sort)) ;;;###autoload (defun +org-capture-templates-setf (key value &optional list sort-after) "Add KEY to LIST, using `setf'. LIST is a symbol and defaults to `org-capture-templates' -- so this function sets values on a list that's structured as such. Thus, KEY is a string key. If it's longer than one character, this function will search LIST for each successive run of characters before the final, ensuring sub-lists exist of the form (CHARS DESCRIPTION). For example, if KEY is \"abc\", first a LIST item of the form (a DESCRIPTION), if non-existant, will be added to the list (with a default description), then an item of the form (\"ab\" DESCRIPTION), before adding (KEY VALUE) to the LIST. VALUE is the template or group header required for `org-capture-templates', which see. SORT-AFTER, when set to t, will call `+org-capture-templates-sort' after setting, to ensure org can properly process the variable." ;; LIST defaults to `org-capture-templates' (declare (indent 2)) (unless list (setq list 'org-capture-templates)) ;; Ensure VALUE is a list to cons properly (unless (listp value) (setq value (list value))) (when (> (length key) 1) ;; Check for existence of groups. (let ((expected (cl-loop for i from 1 to (1- (length key)) collect (substring key 0 i) into keys finally return keys))) (cl-loop for ek in expected if (not (+org-capture--get ek (symbol-value list))) do (setf (+org-capture--get ek (symbol-value list)) (list (format "(Group %s)" ek)))))) (prog1 ;; Set KEY to VALUE (setf (+org-capture--get key (symbol-value list)) value) ;; Sort after, maybe (when sort-after (+org-capture-sort list)))) (defun +org-template--ensure-path (keys &optional list) "Ensure path of keys exists in `org-capture-templates'." (unless list (setq list 'org-capture-templates)) (when (> (length key) 1) ;; Check for existence of groups. (let ((expected (cl-loop for i from 1 to (1- (length key)) collect (substring key 0 i) into keys finally return keys))) (cl-loop for ek in expected if (not (+org-capture--get ek (symbol-value list))) do (setf (+org-capture--get ek (symbol-value list)) (list (format "(Group %s)" ek))))))) (defcustom +org-capture-default-type 'entry "Default template for `org-capture-templates'." :type '(choice (const :tag "Entry" entry) (const :tag "Item" item) (const :tag "Check Item" checkitem) (const :tag "Table Line" table-line) (const :tag "Plain Text" plain))) (defcustom +org-capture-default-target "" "Default target for `org-capture-templates'." ;; TODO: type ) (defcustom +org-capture-default-template nil "Default template for `org-capture-templates'." ;; TODO: type ) (defun +org-define-capture-templates-group (keys description) "Add a group title to `org-capture-templates'." (setf (+org-capture--get keys org-capture-templates) (list description))) ;; [[https://github.com/cadadr/configuration/blob/39813a771286e542af3aa333172858532c3bb257/emacs.d/gk/gk-org.el#L1573][from cadadr]] (defun +org-define-capture-template (keys description &rest args) "Define a capture template and necessary antecedents. ARGS is a plist, which in addition to the additional options `org-capture-templates' accepts, takes the following and places them accordingly: :type, :target, and :template. Each of these corresponds to the same field in `org-capture-templates's docstring, which see. Likewise with KEYS and DESCRIPTION, which are passed separately to the function. This function will also create all the necessary intermediate capture keys needed for `org-capture'; that is, if KEYS is \"wcp\", entries for \"w\" and \"wc\" will both be ensured in `org-capture-templates'." (declare (indent 2)) ;; Check for existence of parent groups (when (> (length keys) 1) (let ((expected (cl-loop for i from 1 to (1- (length keys)) collect (substring 0 i) into keys finally return keys))) (cl-loop for ek in expected if (not (+org-capture--get ek org-capture-templates)) do (+org-define-capture-templates-group ek (format "(Group %s)" ek))))) (if (null args) ;; Add the title (+org-define-capture-templates-group keys description) ;; Add the capture template. (setf (+org-capture--get keys org-capture-templates) (append (list (or (plist-get args :type) +org-capture-default-type) (or ( plist-get args :target) +org-capture-default-target) (or (plist-get args :template) +org-capture-default-template)) (cl-loop for (key val) on args by #'cddr unless (member key '(:type :target :template)) append (list key val)))))) (provide '+org-capture) ;;; +org-capture.el ends here