summaryrefslogtreecommitdiff
path: root/config.org
diff options
context:
space:
mode:
Diffstat (limited to 'config.org')
-rw-r--r--config.org152
1 files changed, 60 insertions, 92 deletions
diff --git a/config.org b/config.org
index ed8e7e6..c4eaced 100644
--- a/config.org
+++ b/config.org
@@ -390,17 +390,15 @@ short and phrase-like so summaries and completion annotations stay readable.
(format ":%s: %s\n" key value)
""))
- (defun ss/people--ensure-file ()
- "Create `ss/people-file' when it is missing."
- (make-directory (file-name-directory ss/people-file) t)
+ (defun ss/people--require-file ()
+ "Return `ss/people-file', signaling when it is unavailable."
(unless (file-exists-p ss/people-file)
- (with-temp-file ss/people-file
- (insert "#+title: People\n\n")))
+ (user-error "People file does not exist: %s" ss/people-file))
ss/people-file)
(defun ss/people-entries ()
"Return top-level people cards from `ss/people-file'."
- (let* ((file (ss/people--ensure-file))
+ (let* ((file (ss/people--require-file))
(attributes (file-attributes file))
(mtime (file-attribute-modification-time attributes)))
(unless (and ss/people--cache
@@ -507,7 +505,7 @@ short and phrase-like so summaries and completion annotations stay readable.
(unless (and buffer-file-name
(string= (file-truename buffer-file-name)
(file-truename ss/people-file)))
- (find-file (ss/people--ensure-file)))
+ (find-file (ss/people--require-file)))
(widen)
(goto-char (point-min))
(org-overview)
@@ -533,7 +531,7 @@ short and phrase-like so summaries and completion annotations stay readable.
(defun ss/people--open-entry (entry)
"Open the people rolodex file, then narrow to ENTRY for card view."
- (find-file (ss/people--ensure-file))
+ (find-file (ss/people--require-file))
(widen)
(let ((position (org-find-exact-headline-in-buffer
(ss/people--entry-name entry))))
@@ -659,7 +657,7 @@ short and phrase-like so summaries and completion annotations stay readable.
(user-error "A person card for %s already exists" name))
(when (string-empty-p abbrev)
(setq abbrev (ss/people-default-abbrev name)))
- (find-file (ss/people--ensure-file))
+ (find-file (ss/people--require-file))
(widen)
(goto-char (point-max))
(unless (bolp)
@@ -683,18 +681,29 @@ short and phrase-like so summaries and completion annotations stay readable.
(ss/people-reload)
(ss/people--open-entry (ss/people--entry-by-name name))))
+ (defun ss/people--clear-installed-abbrevs ()
+ "Remove people-specific abbrevs from the current local table."
+ (mapatoms
+ (lambda (symbol)
+ (when (abbrev-get symbol :ss/people)
+ (define-abbrev local-abbrev-table (symbol-name symbol) nil)))
+ local-abbrev-table))
+
(defun ss/people-install-abbrevs ()
"Install people abbrevs into the current buffer."
(unless (ss/people--source-buffer-p)
(setq-local local-abbrev-table (copy-abbrev-table local-abbrev-table))
+ (ss/people--clear-installed-abbrevs)
(dolist (entry (ss/people-entries))
(let ((name (ss/people--entry-name entry))
(abbrev (ss/people--entry-abbrev entry)))
- (define-abbrev local-abbrev-table
- (if (or (null abbrev) (string-empty-p abbrev))
- (ss/people-default-abbrev name)
- abbrev)
- name)))))
+ (abbrev-put
+ (define-abbrev local-abbrev-table
+ (if (or (null abbrev) (string-empty-p abbrev))
+ (ss/people-default-abbrev name)
+ abbrev)
+ name)
+ :ss/people t)))))
(defun ss/people-refresh-buffers ()
"Refresh people abbrevs in every prose buffer."
@@ -756,15 +765,13 @@ same root directory and rely on links for relationships.
** Org foundations
-The Org setup establishes the shared directories, ensures the PARA structure is
-present at startup, and provides a small helper for opening today's daily note
-with the standard section layout already in place. Agenda views stay focused on
-PARA notes, so project, area, and resource files can surface TODOs without
-pulling in daily or archived notes. A small directory helper keeps PARA
-subdirectories easy to create from the minibuffer before capturing into them.
-A curated =moc.org= in the Org root acts as the startup landing page and quick
-navigation surface. The config assumes that file already exists and opens it
-directly during startup rather than creating it on demand.
+The Org setup establishes the shared directories used by the workflow and
+provides helpers that open existing notes at point of use. Agenda views stay
+focused on PARA notes, so project, area, and resource files can surface TODOs
+without pulling in daily or archived notes. A curated =moc.org= in the Org root
+acts as the startup landing page and quick navigation surface. The config
+assumes that file already exists and opens it directly during startup rather
+than creating it on demand.
#+begin_src emacs-lisp
(use-package org
@@ -793,30 +800,29 @@ directly during startup rather than creating it on demand.
(defconst ss/moc-file (expand-file-name "moc.org" ss/org-directory)
"Central MOC note.")
- (defconst ss/org-note-directories
- (list ss/org-directory
- ss/org-daily-directory
- ss/org-projects-directory
- ss/org-areas-directory
- ss/org-resources-directory
- ss/org-archives-directory)
- "Directories that make up the note-taking workflow.")
-
(defconst ss/org-agenda-directories
(list ss/org-projects-directory
- ss/org-areas-directory)
+ ss/org-areas-directory
+ ss/org-resources-directory)
"Directories whose Org files feed the agenda.")
- (defconst ss/org-subdirectory-roots
- `(("projects" . ,ss/org-projects-directory)
- ("areas" . ,ss/org-areas-directory)
- ("resources" . ,ss/org-resources-directory))
- "Capture roots offered when creating note subdirectories.")
+ (defun ss/require-existing-directory (directory)
+ "Return DIRECTORY, signaling when it does not exist."
+ (unless (file-directory-p directory)
+ (user-error "Directory does not exist: %s" directory))
+ directory)
+
+ (defun ss/require-existing-file (file)
+ "Return FILE, signaling when it does not exist."
+ (unless (file-exists-p file)
+ (user-error "File does not exist: %s" file))
+ file)
(defun ss/denote-capture-in-directory (directory &optional keywords &rest prompts)
"Start a Denote Org capture in DIRECTORY with KEYWORDS and PROMPTS.
If PROMPTS is empty, rely on `denote-prompts'."
(let* ((prompt-for-keywords (memq :keywords prompts))
+ (directory (ss/require-existing-directory directory))
(denote-directory directory)
(denote-use-directory (unless (memq :subdirectory prompts) directory))
(denote-use-keywords
@@ -832,62 +838,28 @@ directly during startup rather than creating it on demand.
(memq :template prompts))
(denote-org-capture))))
- (defun ss/create-note-subdirectory ()
- "Create a PARA subdirectory using minibuffer completion."
- (interactive)
- (let* ((root-name (completing-read
- "Capture root: "
- (mapcar #'car ss/org-subdirectory-roots)
- nil t))
- (root (alist-get root-name ss/org-subdirectory-roots nil nil #'string=))
- (completion-extra-properties '(:category file))
- (candidates
- (sort
- (delete-dups
- (mapcar (lambda (path)
- (directory-file-name (file-relative-name path root)))
- (seq-filter
- #'file-directory-p
- (directory-files-recursively root directory-files-no-dot-files-regexp t t))))
- #'string<))
- (subdirectory (completing-read
- (format "Subdirectory in %s: " root-name)
- candidates
- nil nil))
- (target (expand-file-name subdirectory root))
- (existing (file-directory-p target)))
- (make-directory target t)
- (ss/refresh-org-agenda-files)
- (message "%s note directory: %s"
- (if existing "Using existing" "Created")
- target)))
-
(defun ss/refresh-org-agenda-files (&rest _)
"Refresh `org-agenda-files' from the current PARA directories.
Ignore any arguments passed by advice wrappers."
(require 'org-agenda)
(setq org-agenda-files
- (delete-dups
- (apply #'append
- (mapcar (lambda (directory)
- (if (file-directory-p directory)
- (directory-files-recursively directory org-agenda-file-regexp)
- nil))
- ss/org-agenda-directories)))))
+ (sort
+ (delete-dups
+ (apply #'append
+ (mapcar (lambda (directory)
+ (directory-files-recursively
+ (ss/require-existing-directory directory)
+ "\\.org\\'"))
+ ss/org-agenda-directories)))
+ #'string<)))
(defun ss/ensure-daily-note (&optional time)
- "Create the daily note for TIME when it does not exist.
- Return the path to the note."
+ "Return the existing daily note path for TIME."
(let* ((date (or time (current-time)))
(file (expand-file-name
(format-time-string "%Y-%m-%d.org" date)
ss/org-daily-directory)))
- (unless (file-exists-p file)
- (make-directory (file-name-directory file) t)
- (with-temp-file file
- (insert (format "#+title: %s\n\n* Tasks\n\n* Notes\n\n* Open Loops\n"
- (format-time-string "%Y-%m-%d" date)))))
- file))
+ (ss/require-existing-file file)))
(defun ss/open-todays-note ()
"Open today's daily Org note."
@@ -897,14 +869,16 @@ directly during startup rather than creating it on demand.
(defun ss/open-moc ()
"Open the central MOC note."
(interactive)
- (find-file ss/moc-file))
+ (find-file (ss/require-existing-file ss/moc-file)))
(defun ss/open-agenda ()
"Refresh agenda files and invoke `org-agenda'."
(interactive)
(call-interactively #'org-agenda))
:init
- (add-hook 'emacs-startup-hook (lambda () (find-file ss/moc-file)))
+ (add-hook 'emacs-startup-hook
+ (lambda ()
+ (find-file (ss/require-existing-file ss/moc-file))))
:bind (("C-c a" . ss/open-agenda)
("C-c c" . org-capture)
("C-c n M" . ss/open-moc)
@@ -913,7 +887,6 @@ directly during startup rather than creating it on demand.
("C-c n i" . ss/people-insert-name)
("C-c n I" . ss/people-insert-summary)
("C-c n L" . ss/people-report-by-location)
- ("C-c n m" . ss/create-note-subdirectory)
("C-c n d" . ss/open-todays-note)
("C-c n o" . ss/people-overview)
("C-c n O" . ss/people-report-by-role)
@@ -928,12 +901,7 @@ directly during startup rather than creating it on demand.
(setq-local org-hide-emphasis-markers t)
(font-lock-flush)
(font-lock-ensure)))
- (ss/refresh-org-agenda-files)
- (advice-add 'org-agenda :before #'ss/refresh-org-agenda-files)
- (mapc (lambda (directory)
- (make-directory directory t))
- ss/org-note-directories)
- (ss/people--ensure-file))
+ (advice-add 'org-agenda :before #'ss/refresh-org-agenda-files))
#+end_src
** Capture entry points