;;; org-mode.el --- org-mode customizations -*- lexical-binding: t -*- ;; Author: Andrew Scott ;; Keywords: org, convenience, tools ;; URL: https://codeberg.org/andyscott/dotfiles ;; This file is not part of GNU Emacs ;; Copyright (c) 2024 Andrew Scott ;; MIT No Attribution ;; Permission is hereby granted, free of charge, to any person obtaining a copy of this ;; software and associated documentation files (the "Software"), to deal in the Software ;; without restriction, including without limitation the rights to use, copy, modify, ;; merge, publish, distribute, sublicense, and/or sell copies of the Software, and to ;; permit persons to whom the Software is furnished to do so. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, ;; INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A ;; PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ;;; Commentary: ;; My org-mode customizations. ;;; Code: (use-package citar :after (oc) :config (setopt citar-bibliography org-cite-global-bibliography citar-library-paths '("~/Nextcloud/Library/")) ;; (when (elpaca-installed-p 'citar-embark) ;; (setopt citar-at-point-function 'embark-act)) :hook (org-mode . citar-capf-setup)) (use-package citar-embark :after (citar embark) :config (citar-embark-mode)) (use-package citar-org-roam :after (citar org-roam) :config (setopt citar-org-roam-capture-template-key "r" citar-org-roam-note-title-template "${title} - ${author}" citar-org-roam-subdir "references") (citar-org-roam-mode)) (use-package citeproc :after (oc)) (use-feature oc :after (org) :config (setopt org-cite-export-processors '((latex biblatex) (t csl)) org-cite-global-bibliography '("~/Nextcloud/Library/bib/library.bib")) (when (elpaca-installed-p 'citar) (setopt org-cite-activate-processor 'citar org-cite-follow-processor 'citar org-cite-insert-processor 'citar))) (use-feature oc-csl :after (citeproc oc) :config (let ((zotero_styles (expand-file-name "Zotero/styles/" (or (getenv "XDG_DATA_HOME") "~/.local/share/")))) (if (file-directory-p zotero_styles) (setopt org-cite-csl-styles-dir zotero_styles)))) (use-package org :bind (:map global-map ("C-c a" . org-agenda) ("C-c c" . org-capture) ("C-c l" . org-store-link)) :config ;; General options TODO: Comment various options more descriptively (setopt org-agenda-block-separator nil ;; org-agenda-files (list org-directory) org-agenda-files (directory-files-recursively org-directory "org$") org-agenda-skip-deadline-if-done nil org-agenda-skip-scheduled-if-deadline-is-shown t org-agenda-skip-scheduled-if-done nil org-agenda-start-on-weekday nil org-agenda-tags-column 0 org-archive-location (expand-file-name "archive/%s_archive.org::" org-directory) org-blank-before-new-entry '((heading . auto) (plain-list-item . auto)) org-deadline-warning-days 5 org-enforce-todo-dependencies t org-fold-catch-invisible-edits 'show-and-error org-fontify-done-headline t org-fontify-quote-and-verse-blocks t org-fontify-todo-headline t org-goto-interface 'outline-path-completion org-hide-emphasis-markers t org-insert-heading-respect-content t org-log-done 'time org-log-into-drawer t org-log-redeadline 'time org-log-refile 'note org-log-reschedule 'time org-outline-path-complete-in-steps nil org-pretty-entities t org-refile-allow-creating-parent-nodes 'confirm org-refile-targets '((nil :maxlevel . 3) (org-agenda-files :maxlevel . 2)) org-refile-use-outline-path 'file org-return-follows-link t org-reverse-note-order t ; Add/refile notes at the beginning of an entry org-special-ctrl-a/e t org-src-preserve-indentation t org-startup-folded 'fold org-tags-column 0 org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "PROG(p)" "|" "DONE(d!)" "WONT(w@/!)") (sequence "IDEA(i)" "|")) org-todo-keyword-faces '(("TODO" . (dired-mark)) ("PROG" . (success)) ("NEXT" . (dired-special)) ("WONT" . (org-done))) org-todo-repeat-to-state "TODO") (setq org-capture-templates `( ("i" "Inbox" entry (file "roam/inbox.org") "* %?" :empty-lines 1 ) ("j" "Log Entry" entry (file+datetree "log.org") "** %?" :empty-lines 1) ("n" "Note" entry (file+headline "inbox.org" "Notes") "** %? :PROPERTIES: :CREATED: %U :END:" :empty-lines 1) ("t" "Todo" entry (file+headline "inbox.org" "Tasks") "** TODO [#B] %? :PROPERTIES: :CREATED: %U :END: %i\n%a Notes: " :empty-lines 1) ) ) ;; Headline appearance (custom-set-faces '(org-level-1 ((t (:inherit outline-1 :height 1.5)))) '(org-level-2 ((t (:inherit outline-2 :height 1.4)))) '(org-level-3 ((t (:inherit outline-3 :height 1.3)))) '(org-level-4 ((t (:inherit outline-4 :height 1.2)))) '(org-level-5 ((t (:inherit outline-5 :height 1.1))))) :hook (org-mode . org-indent-mode) :init (setopt org-directory "~/Nextcloud/Documents/org") (bind-keys :prefix-map as/org-prefix-map :prefix "C-c o" ("n" . org-metadown) ("p" . org-metaup) ("f" . org-shiftmetaright) ("b" . org-shiftmetaleft) ("v" . org-tags-view) ("y" . org-todo-yesterday) ("l" . org-toggle-link-display) ("C-n" . org-priority-down) ("C-p" . org-priority-up))) (use-feature org-agenda :after (org) :bind (:map org-agenda-mode-map ("C-c C-r" . as/org-agenda-reconcile-inbox-item)) :config (setopt org-agenda-start-with-log-mode '(clock closed state)) :init (defun as/org-agenda-reconcile-inbox-item () "Process and refile an org-agenda item" (interactive) (org-with-wide-buffer (org-agenda-set-tags) (org-agenda-priority) (if (y-or-n-p "Schedule item?") (org-agenda-schedule nil nil)) (org-agenda-refile nil nil t)))) (use-package org-appear :config (setopt org-appear-autoentities t org-appear-autolinks nil org-appear-autosubmarkers t org-appear-trigger 'on-change) :hook (org-mode . org-appear-mode)) (use-feature org-clock :after (org) :config (setopt org-clock-in-resume t org-clock-in-switch-to-state "PROG" org-clock-persist t org-clock-rounding-minutes 5)) (use-package org-edna :config (setopt org-edna-finder-use-cache t) :hook (org-mode . org-edna-mode)) (use-feature org-habit :after (org) ;; REVIEW: do I need to add to modules if it loads after org anyway? :init (add-to-list 'org-modules 'org-habit t)) (use-feature org-id :after (org) :config (setopt org-id-link-to-org-use-id t org-id-locations-file (locate-user-emacs-file "org-id-locations") org-id-method 'ts org-id-ts-format "%Y%m%dT%H%M%S.%2N%z") ;; REVIEW: do I need to add to modules if it loads after org anyway? :init (add-to-list 'org-modules 'org-id t)) (use-package org-modern :disabled t :config (setopt org-modern-block-name nil org-modern-priority nil org-modern-symbol "DejaVu Sans" org-modern-todo nil org-modern-tag nil org-modern-timestamp nil) :hook (org-mode . org-modern-mode) (org-agenda-finalize . org-modern-agenda)) (use-package org-noter :after (org) :config (setopt org-noter-notes-search-path (list (concat org-directory "/roam"))) (org-noter-enable-org-roam-integration)) (use-package org-pomodoro :after (org-clock) :commands (org-pomodoro) :config ;; (defun as/org-pomodoro-started-todo-update () ;; "Update todo state when starting a pomodoro" ;; (when (or (string= org-state "NEXT") ;; (string= org-state "TODO")) ;; (org-todo "PROG"))) ;; (defun as/org-todo-state-maybe-update-pomodoro () ;; "Update or start a pomodoro when changing todo state ;; REVIEW: This could be updated to always trigger a pomodoro change, like when ;; changing state to PROG. This has the added benefit of making the above function ;; redundent, as I could just use `org-todo' as a single start and end point for ;; all pomodoros" ;; (when (and (or (string= org-state "TODO") ;; (string= org-state "DONE")) ;; (org-pomodoro-active-p)) ;; (org-pomodoro-kill))) ;; (add-to-list 'org-after-todo-state-change-hook #'as/org-todo-state-change-maybe-kill-pomodoro) ;; (add-to-list 'org-pomodoro-started-hook #'as/org-pomodoro-started-todo-update) (setopt alert-user-configuration ; Send mesages to libnotify '((((:category . "org-pomodoro")) libnotify nil)) org-pomodoro-audio-player (executable-find "pw-cat") org-pomodoro-keep-killed-pomodoro-time t org-pomodoro-length 50 org-pomodoro-short-break-length 10 org-pomodoro-manual-break t) :init ;; (defun as/org-pomodoro-todo-state-trigger () ;; "Starts or stops a pomodoro when todo state changes" ;; (cond ;; ((when (string= org-state "PROG") ;; (if (org-pomodoro-active-p) ;; ((org-pomodoro-kill) ;; (org-pomodoro nil)) ;; (org-pomodoro nil))) ;; ((when (and (or (string= org-state "DONE") (string= org-state "TODO")) ;; (org-pomodoro-active-p)) ;; (org-pomodoro-kill)))))) ;; (with-eval-after-load 'org-clock ;; (add-to-list 'org-after-todo-state-change-hook #'as/org-pomodoro-todo-state-trigger)) ) (use-package org-roam :after (org) :bind (:map as/org-prefix-map ("r i" . org-roam-node-insert) ("r f" . org-roam-node-find) ("r c" . org-roam-capture) ;; REVIEW: Not sure if I really need these? ;; ("r b" . org-roam-buffer-toggle) ;; ("r d" . org-roam-buffer-display-dedicated) ;; ("r a a" . org-roam-alias-add) ;; ("r a r" . org-roam-alias-remove) ;; ("r r a" . org-roam-ref-add) ;; ("r r r" . org-roam-ref-remove) ) :config (setopt org-roam-database-connector 'sqlite-builtin) (setq org-roam-db-gc-threshold most-positive-fixnum) ; doesn't like the new `setopt' (add-to-list 'display-buffer-alist '("\\*org-roam\\*" (display-buffer-in-direction) (direction . right) (window-width . 0.33) (window-height . fit-window-to-buffer))) (add-to-list 'org-roam-capture-templates '("r" "reference" plain "%?" :target (file+head "references/${citar-citekey}.org" "#+title: Notes on: ${note-title}\n") :empty-lines 1 :unnarrowed t )) (add-to-list 'org-roam-capture-templates `("p" "project" plain (file ,(concat org-roam-directory "/templates/projects.org")) :target (file+head "projects/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+filetags: projects") :empty-lines 1 :unnarrowed t)) (org-roam-db-autosync-mode) :ensure (org-roam :files (:defaults "extensions/*")) :init (setopt org-roam-directory (file-name-as-directory (expand-file-name "roam" org-directory)))) (use-package org-super-agenda :hook (org-agenda-mode . org-super-agenda-mode) :init (setq org-agenda-custom-commands '(("w" "Work agenda" ((agenda "" ((org-agenda-prefix-format " %?-12t %s") (org-agenda-remove-tags t) (org-agenda-skip-function '(org-agenda-skip-entry-if 'todo '("WONT"))) (org-agenda-span 'day) (org-habit-show-habits nil))) (alltodo "" ((org-agenda-overriding-header "") (org-agenda-prefix-format " %?-12t %s") (org-agenda-remove-tags t) (org-super-agenda-groups '((:discard (:deadline future :scheduled future :category ("house" "me" "notes"))) (:name "Done" :log state :todo "DONE" :order 90) (:name "Overdue" :deadline past :scheduled past :order 1) (:name "Inbox" :category ("inbox" "phone_inbox") :order 9) (:name "Important" :priority "A" :order 0) (:name "In-Progress" :todo "PROG" :order 2) (:name "Next" :todo "NEXT" :order 3) (:name "Today" :deadline today :scheduled today :time-grid t :order 4) (:name "Research" :tag "research" :order 7) (:name "Project Backlog" :and (:todo "TODO" :tag "projects") :order 5) (:name "General Backlog" :and (:todo "TODO" :priority "B") :order 8) (:name "Less Important" :priority<= "C" :order 80) )))))) ("y" "My agenda" ((agenda "" ((org-agenda-prefix-format " %?-12t %s") (org-agenda-remove-tags t) (org-agenda-skip-function '(org-agenda-skip-entry-if 'todo '("WONT") 'regexp '("job"))) (org-agenda-span 3))) (alltodo "" ((org-agenda-overriding-header "") (org-agenda-prefix-format " %?-12t %s") (org-agenda-remove-tags t) (org-super-agenda-groups '((:discard (:not (:category ("house" "me" "notes")))) ;; (:discard ;; (:deadline future :scheduled future)) (:name "Overdue" :deadline past :scheduled past :order 1) (:name "Upcoming" :deadline future :scheduled future :order 99) (:name "Inbox" :category ("inbox" "phone_inbox") :order 8) (:name "Done" :todo "DONE" :order 99) (:name "Important" :priority "A" :order 0) (:name "In-Progress" :todo "PROG" :order 2) (:name "Next" :todo "NEXT" :order 3) (:name "Household" :tag "chores" :order 6) (:name "Routine" :and (:habit t :not (:tag "chores")) :order 5) (:name "Today" :deadline today :scheduled today :time-grid t :order 4) ;; (:name "Personal projects" ;; :categery "me" ;; :order 9) (:name "Research" :tag "research" :order 7) (:name "General Backlog" :and (:todo "TODO" :priority "B") :order 50) (:name "Less Important" :priority<= "C" :order 85) ))))))))) (use-package org-superstar ;; :disabled t :config (setopt org-indent-mode-turns-on-hiding-stars nil org-superstar-headline-bullets-list '("◉" "🞛" "○" "▷") org-superstar-leading-bullet ?\s org-superstar-special-todo-items t) :hook (org-mode . org-superstar-mode)) (use-package org-timeblock :bind (:map as/org-prefix-map ("t" . org-timeblock)) :config (setopt org-timeblock-new-task-time 'pick org-timeblock-show-future-repeats 'next org-timeblock-scale-options (cons 7 20) org-timeblock-span 3)) (provide 'init-org) ;;; org-mode.el ends here