diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-13 15:25:52 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-13 15:25:52 -0500 |
| commit | c3514440eb3de3101576baa5c3c592c0d908f70b (patch) | |
| tree | 6534504d1a90a9c014a09c446f2f384677e80e10 /modules | |
| parent | e076815895ed452e021ae4c1986eca1f0e67aa41 (diff) | |
| download | dotemacs-c3514440eb3de3101576baa5c3c592c0d908f70b.tar.gz dotemacs-c3514440eb3de3101576baa5c3c592c0d908f70b.zip | |
feat(org-agenda): use project name as todo.org category
The %c column on agenda blocks rendered every project's todo.org as "todo:" -- org defaults the buffer category to the filename without extension, so every entry looked alike. An org-mode-hook now overrides org-category with the parent directory's basename (stripping a single leading dot, so ~/.emacs.d/todo.org reads as "emacs.d") whenever a todo.org file opens and its category is still the filename default. Explicit #+CATEGORY: keywords still win.
14 tests in test-org-agenda-config-category.el cover the helper's normal/boundary/error paths and the hook's override + explicit-category-preserved cases.
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/org-agenda-config.el | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/modules/org-agenda-config.el b/modules/org-agenda-config.el index 70c7fb6d..2c30db0f 100644 --- a/modules/org-agenda-config.el +++ b/modules/org-agenda-config.el @@ -90,6 +90,50 @@ (local-set-key (kbd "s-<left>") #'org-agenda-todo-previousset)))) +;; ----------------------- Project-name Category Override --------------------- +;; The default `org-category' for a todo.org buffer is "todo" (the filename +;; without extension), which renders as "todo:" in every agenda `%c' column +;; and tells the reader nothing useful when every project has its own +;; todo.org. Substitute the parent-directory basename instead, so +;; `~/.emacs.d/todo.org' shows "emacs.d:" and `~/projects/foo/todo.org' shows +;; "foo:". Files that aren't named todo.org are left alone, and a user-set +;; `#+CATEGORY:' (which leaves `org-category' at a non-default value) wins. + +(defun cj/--org-todo-category-from-file (path) + "Return the project category for a todo.org PATH, or nil if not applicable. +For a file named todo.org, returns the basename of its parent +directory with a single leading dot stripped (so `~/.emacs.d/todo.org' +yields \"emacs.d\", not \".emacs.d\"). For any other file -- or for a +PATH that is nil, empty, or has no usable parent directory -- returns +nil so the org default category applies." + (when (and (stringp path) + (not (string-empty-p path)) + (string= "todo.org" (file-name-nondirectory path))) + (let* ((dir (file-name-directory path)) + (parent (and dir + (file-name-nondirectory + (directory-file-name dir)))) + (clean (and parent + (if (and (> (length parent) 1) + (eq ?. (aref parent 0))) + (substring parent 1) + parent)))) + (and clean (not (string-empty-p clean)) clean)))) + +(defun cj/--org-set-todo-category () + "Set buffer-local `org-category' to the project name for a todo.org buffer. +Runs from `org-mode-hook'. Only overrides when `org-category' is still +the default-from-filename (\"todo\"), so an explicit `#+CATEGORY:' in +the file keeps precedence." + (when (and buffer-file-name + (boundp 'org-category) + (stringp org-category) + (string= "todo" org-category)) + (when-let* ((project (cj/--org-todo-category-from-file buffer-file-name))) + (setq-local org-category project)))) + +(add-hook 'org-mode-hook #'cj/--org-set-todo-category) + ;; ------------------------ Org Agenda File List Cache ------------------------- ;; Cache agenda file list to avoid expensive directory scanning on every view. ;; The TTL+building cache lifecycle is provided by `cj-cache.el'. |
