changeset 641: |
48bcbca019e6 |
parent: |
6c0e4a44c082
|
child: |
f58f3b88c49e |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Sun, 08 Sep 2024 20:11:35 -0400 |
permissions: |
-rw-r--r-- |
description: |
org-project-info dblock update |
1 ;;; lib/inbox.el --- Inbox API -*- lexical-binding: t -*- 3 ;; Copyright (C) 2023 Richard Westhaver 5 ;; Keywords: maint, tools, outlines, extensions 7 ;; This program is free software; you can redistribute it and/or modify 8 ;; it under the terms of the GNU General Public License as published by 9 ;; the Free Software Foundation, either version 3 of the License, or 10 ;; (at your option) any later version. 12 ;; This program is distributed in the hope that it will be useful, 13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 ;; GNU General Public License for more details. 17 ;; You should have received a copy of the GNU General Public License 18 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 22 ;; This is the elisp interface to the CC Inbox system. The main 23 ;; interface is the inbox.org file which manages personal tasks. 25 ;; Users may use `org-capture' to insert tasks and notes into their 26 ;; own `org-inbox-file' and refactor them to a more sensible 27 ;; destination with `org-refile'. 33 ;; (require 'uml-mode) 40 (defcustom org-inbox-file 41 (concat (file-name-as-directory org-directory) "inbox.org") 42 "Custom inbox file location." 46 (defcustom org-inbox-date-start-format "<%Y-%m-%d %a>" 47 "Format of DATE_START property timestamp for week headings. See 48 `org-time-stamp-formats' for accepted values." 52 (defvar org-inbox-buffer-name "*Inbox*" 53 "The name of the org-inbox buffer.") 55 (defvar org-inbox-config-buffer-name "*Inbox Config*" 56 "Then name of the org-inbox configuration buffer.") 58 (defvar org-inbox-properties 59 '("NEXT" "PREV" "FROM" "TO" "OWNER" "PROJECT" "BLOCKER" "VERSION")) 61 (defvar org-inbox-db-schema 62 '(id file node edge contents properties schedule)) 65 (setq org-id-link-to-org-use-id t 66 org-protocol-default-template-key "L") 69 (setq org-capture-templates 70 `(("i" "inbox-item" entry (file ,org-inbox-file) 73 ("t" "inbox-task" entry (file ,org-inbox-file) "* TODO %^{item}\n") 74 ("n" "inbox-note" entry (file ,org-inbox-file) "* NOTE %^{item}\n%a") 75 ("l" "inbox-link" entry (file ,org-inbox-file) 77 ("L" "inbox-protocol-link" entry (file ,org-inbox-file) 78 "* LINK [[%:link][%:description]]\n%:initial" :empty-lines 1) 79 ("w" "inbox-web-link" entry (file ,org-inbox-file) 83 (org-web-tools-insert-link-for-url (org-web-tools--get-first-url)))) 84 ("1" "current-task-item" item (clock) "%i%?") 85 ("2" "current-task-checkbox" checkitem (clock) "%i%?") 86 ("3" "current-task-region" plain (clock) "%i" :immediate-finish t :empty-lines 1) 87 ("4" "current-task-kill" plain (clock) "%c" :immediate-finish t :empty-lines 1) 88 ("l" "log" item (file+headline "log.org" "log") "%U %?" :prepend t) 89 ("s" "secret" table-line (file+function "krypt" org-ask-location) "| %^{key} | %^{val} |" :immediate-finish t :kill-buffer t) 90 ("N" "note-item" plain (file+function "notes.org" org-ask-location) "%?"))) 92 (add-hook 'org-after-todo-state-change-hook #'org-id-get-create) 93 (add-hook 'org-after-todo-state-change-hook #'org-expiry-insert-created) 95 (setq org-default-notes-file (join-paths org-directory "inbox.org") 96 org-capture-use-agenda-date t 97 org-archive-location "archive.org::") 100 ;; `org-archive-all-done' doesn't work the way we want. This function 101 ;; will archive all done tasks in the current subtree, or the whole file 102 ;; if prefix arg is given. 103 (defun org-archive-done (&optional scope) 104 "archive all tasks with todo-state of 'DONE' or 'NOPE'." 108 (org-archive-subtree) 109 (setq org-map-continue-from (org-element-property :begin (org-element-at-point)))) 110 "/+DONE|NOPE" scope)) 112 (defun org-children-done () 113 "Mark all sub-tasks in this heading as 'DONE'." 117 (unless (= (org-current-level) 1) 121 (defmacro with-inbox-buffer (&rest body) 123 (with-current-buffer (find-file org-inbox-file) 126 (defun org-sort-todo-priority () 127 "Sorting function used by `org-sort' to sort by todo order 128 followed by priority. Returns a pair of numbers (TODO . PRIO)." 129 (let* ((elt (cadr (org-element-at-point))) 130 (todo (when-let ((kw (plist-get elt :todo-keyword))) 132 (substring-no-properties kw)))) 133 (prio (pcase (plist-get elt :priority) 139 ;; FIXME todo states shouldn't be hardcoded 141 ((null todo) (setq res (cons 3 prio))) 142 ((string= todo "WATCH") (setq res (cons 3 prio))) 143 ((string= todo "WAIT") (setq res (cons 1 prio))) 144 ((string= todo "HOLD") (setq res (cons 1 prio))) 145 ((string= todo "WIP") (setq res (cons 1 prio))) 146 ((string= todo "GOTO") (setq res (cons 2 prio))) 147 ((string= todo "TODO") (setq res (cons 2 prio))) 148 ((string= todo "RESEARCH") (setq res (cons 3 prio))) 149 ((string= todo "DONE") (setq res (cons 4 prio))) 150 ((string= todo "NOPE") (setq res (cons 4 prio)))) 151 (unless res (setq res (cons 0 prio))) 154 (defun org-sort-compare-todo-priority (a b) 155 "Given two cons consisting of (TODO . PRIO), return t if A 156 should come before B." 157 (message "a: %S b: %S" a b) 159 ((< (car a) (car b)) t) 160 ((> (car a) (car b)) nil) 163 ((< (cdr a) (cdr b)) t) 164 ((> (cdr a) (cdr b)) nil))))) 167 (defun org-inbox-sort () 168 "Sort the current heading by todo order followed by priority." 171 (org-sort-entries nil ?f #'org-sort-todo-priority #'org-sort-compare-todo-priority))) 173 (defun org-inbox-compact () 174 "Assign missing IDs and creation dates, archive DONE tasks." 177 (org-id-update-id-locations) 178 (org-id-add-to-headlines-in-file) 180 (org-map-entries #'org-expiry-insert-created) 183 (defun org-inbox-open () 184 "Open `org-inbox-file' or switch to its buffer if already open." 186 (if-let ((inbox (get-buffer org-inbox-buffer-name))) 187 (switch-to-buffer inbox) 188 (find-file org-inbox-file) 189 (rename-buffer org-inbox-buffer-name))) 191 (defun org-inbox-close () 192 "Close the org-inbox and associated buffers." 194 (when-let ((inbox (get-buffer org-inbox-buffer-name))) 195 (kill-buffer inbox))) 200 (defun org-dblock-write:summary (params) 201 "Generate a file or heading summary section.") 203 (defun org-summary () 204 "Insert or update a summary section.") 207 (defcustom org-project-info-order '(details status churn log files) 208 "Order in which sections of the 'project-info' dblock will appear." 212 (defun org-dblock-write:project-info (params) 213 "Generate a project-info section. 215 The following keyword parameters can be passed to the info dynamic block: 217 :location Set or override the project location which is inferred by 218 checking for a LOCATION property in the current tree, followed 219 by the value of the `project-current' function. 221 :branch Set or override the project branch to display info for. Default 222 branch name is 'default'. 224 :files When nil don't include the files table. 225 :churn When nil don't include the vc churn report. 226 :log when nil don't include the vc log. 227 :status when nil don't include vc status. 228 :details When nil don't include the project details section." 229 (let ((location (or (when-let ((param (plist-get params :location))) 230 (cl-coerce param 'string)) 231 (org-entry-get (point) "LOCATION") 232 (when-let ((kw (org-collect-keywords '("LOCATION")))) 234 (project-root (project-current)))) 236 (files (if-let ((val (plist-member params :files))) 239 (churn (if-let ((val (plist-member params :churn))) 242 (status (if-let ((val (plist-member params :log))) 245 (log (if-let ((val (plist-member params :status))) 248 (details (if-let ((val (plist-member params :details))) 251 (message "Generating info for project: %s" location) 252 (let* ((project (project-current nil location)) 253 (project-name (project-name project)) 254 (project-root (project-root project))) 255 (dolist (i org-project-info-order) 257 ('details (when details 258 (message "building project details...") 259 (insert "#+CALL: project-details() :dir " project-root "\n") 260 (org-babel-execute-maybe) 262 ('status (when status 263 (message "building project status...") 264 (insert "#+CALL: hg-status() :dir " project-root "\n"))) 266 (message "building project vc churn...") 267 (insert "#+CALL: hg-churn() :dir " project-root "\n"))) 269 (message "building project vc log..."))) 271 (message "building project file table...") 272 (insert "#+CALL: project-files() :dir " project-root "\n"))))) 273 (org-babel-execute-region point (point))))) 275 (defun org-project-info () 276 "Insert or update a project-info dblock." 278 (if (re-search-forward (rx bol "#+BEGIN:" (+ space) "project-info") nil t) 280 (if (fboundp 'org-fold-show-entry) 281 (org-fold-show-entry) 282 (with-no-warnings (org-show-entry))) 284 (org-create-dblock (list :name "project-info"))) 287 (defun org-inbox-configure-dblock () 288 "Configure the current org-inbox-dblock at point." 290 (with-demoted-errors "Error: %S" 291 (let* ((beginning (org-beginning-of-dblock)) 292 (parameters (org-prepare-dblock))) 293 (org-inbox-show-config-buffer (current-buffer) beginning parameters)))) 296 (defun org-inbox-show-config (&optional buffer position parameters) 298 (switch-to-buffer org-inbox-config-buffer-name) 301 (widget-insert "\n\n") 302 (widget-create 'push-button 303 :notify (lambda(_widget &rest _ignore) 304 (with-current-buffer buffer 309 (propertize "Apply" 'face 'font-lock-comment-face)) 311 (widget-create 'push-button 312 :notify (lambda (_widget &rest _ignore) 314 (propertize "Cancel" 'face 'font-lock-string-face)) 315 (use-local-map widget-keymap) 319 ;; inbox.el ends here