diff options
| author | Raul Benencia <id@rbenencia.name> | 2023-02-21 19:44:16 -0800 | 
|---|---|---|
| committer | Raul Benencia <id@rbenencia.name> | 2023-02-21 19:44:16 -0800 | 
| commit | 492049470fa3d486d909f50ddca59b335c8a44bb (patch) | |
| tree | 2a11a54593dc6f63c9985bb02b5d28ae2db2e3ba /.emacs.local.d/lisp/packages | |
| parent | 4cd3b3371dcf77171b98dc12f48da248b2f703b2 (diff) | |
emacs: reorganize org files
Diffstat (limited to '.emacs.local.d/lisp/packages')
| -rw-r--r-- | .emacs.local.d/lisp/packages/rul-org-agenda.el | 40 | ||||
| -rw-r--r-- | .emacs.local.d/lisp/packages/rul-org-journal.el | 16 | ||||
| -rw-r--r-- | .emacs.local.d/lisp/packages/rul-org-roam.el | 26 | ||||
| -rw-r--r-- | .emacs.local.d/lisp/packages/rul-org.el | 528 | 
4 files changed, 610 insertions, 0 deletions
diff --git a/.emacs.local.d/lisp/packages/rul-org-agenda.el b/.emacs.local.d/lisp/packages/rul-org-agenda.el new file mode 100644 index 0000000..f978ea7 --- /dev/null +++ b/.emacs.local.d/lisp/packages/rul-org-agenda.el @@ -0,0 +1,40 @@ +(require 'org) + +(global-set-key (kbd "<f12>") #'org-agenda) +(global-set-key (kbd "C-c a") #'org-agenda) + +;; AGENDA VIEW ;; + +(setq org-agenda-files my-org-agenda-files) +(setq org-agenda-custom-commands my-org-agenda-custom-commands) + +;; Do not dim blocked tasks +(setq org-agenda-compact-blocks nil) +(setq org-agenda-dim-blocked-tasks nil) +(setq org-agenda-block-separator 61) + +;; Agenda log mode items to display (closed and state changes by default) +(setq org-agenda-log-mode-items (quote (closed state))) + +; For tag searches ignore tasks with scheduled and deadline dates +(setq org-agenda-tags-todo-honor-ignore-options t) + +;; Credits: https://200ok.ch/posts/2022-02-13_integrating_org_mode_agenda_into_other_calendar_apps.html +(defun org-agenda-export-to-ics () +  ;; Run all custom agenda commands that have a file argument. +  (org-batch-store-agenda-views) + +  ;; Org mode correctly exports TODO keywords as VTODO events in ICS. +  ;; However, some proprietary calendars do not really work with +  ;; standards (looking at you Google), so VTODO is ignored and only +  ;; VEVENT is read. +  (with-current-buffer (find-file-noselect my-org-agenda-private-local-path) +    (goto-char (point-min)) +    (while (re-search-forward "VTODO" nil t) +      (replace-match "VEVENT")) +    (save-buffer)) + +  ;; Copy the ICS file to a remote server (Tramp paths work). +  (copy-file my-org-agenda-private-local-path my-org-agenda-private-remote-path t)) + +(provide 'rul-org-agenda) diff --git a/.emacs.local.d/lisp/packages/rul-org-journal.el b/.emacs.local.d/lisp/packages/rul-org-journal.el new file mode 100644 index 0000000..ab07a19 --- /dev/null +++ b/.emacs.local.d/lisp/packages/rul-org-journal.el @@ -0,0 +1,16 @@ +(use-package org-journal +  :ensure t +  :init +  ;; Change default prefix key; needs to be set before loading org-journal +  (setq org-journal-prefix-key "C-c j ") +  (setq org-journal-file-format my-org-journal-file-format) +  (setq org-journal-enable-agenda-integration t) +  :config +  (require 'rul-config-org) +  (setq org-journal-dir my-org-journal-dir +        org-journal-file-type my-org-journal-file-type +        org-journal-time-prefix my-org-journal-time-prefix +        org-journal-time-format my-org-journal-time-format +        org-journal-date-format "%A, %d %B %Y")) + +(provide 'rul-org-journal) diff --git a/.emacs.local.d/lisp/packages/rul-org-roam.el b/.emacs.local.d/lisp/packages/rul-org-roam.el new file mode 100644 index 0000000..0184459 --- /dev/null +++ b/.emacs.local.d/lisp/packages/rul-org-roam.el @@ -0,0 +1,26 @@ +(use-package org-roam +  :ensure t +  :after rul-org +  :custom +    (org-roam-directory my-org-roam-directory) +  :init +  (setq org-roam-v2-ack t) +  :bind (("C-c n l" . org-roam-buffer-toggle) +         ("C-c n f" . org-roam-node-find) +         ("C-c n i" . org-roam-node-insert)) +  :config +  (require 'rul-config-org) +  (org-roam-setup) +  (add-to-list 'display-buffer-alist +               '("\\*org-roam\\*" +                 (display-buffer-in-direction) +                 (direction . right) +                 (window-width . 0.33) +                 (window-height . fit-window-to-buffer))) +  (setq org-roam-mode-section-functions +        (list #'org-roam-backlinks-section +              #'org-roam-reflinks-section +              #'org-roam-unlinked-references-section)) +  ) + +(provide 'rul-org-roam) diff --git a/.emacs.local.d/lisp/packages/rul-org.el b/.emacs.local.d/lisp/packages/rul-org.el new file mode 100644 index 0000000..5c96906 --- /dev/null +++ b/.emacs.local.d/lisp/packages/rul-org.el @@ -0,0 +1,528 @@ +;; Debian packages: elpa-org elpa-org-bullets +;; Elpa packages: org-modern +(require 'org) +(require 'org-capture) +(require 'org-protocol) +(require 'org-habit) +(require 'org-bullets) + +(require 'org-modern) + +(require 'rul-config-org) +(require 'rul-org-agenda) + +(setq org-cycle-separator-lines 0) +(setq org-startup-indented t) +(setq org-hide-leading-stars nil) + +(add-hook 'org-mode-hook 'turn-off-auto-fill) +(add-hook 'org-mode-hook 'visual-line-mode) + +(setq org-startup-indented t +      org-bullets-bullet-list '(" ") ;; no bullets, needs org-bullets package +      org-pretty-entities nil +      org-hide-emphasis-markers t +      ;; show actually italicized text instead of /italicized text/ +      org-fontify-whole-heading-line t +      org-fontify-done-headline t +      org-fontify-quote-and-verse-blocks t) + +;; ORG BINDINGS ;; +(global-set-key (kbd "C-c l") #'org-store-link) +(global-set-key (kbd "C-c c") #'org-capture) +(global-set-key (kbd "C-c s") #'org-schedule) + +;; ORG STATES ;; +(setq org-todo-keywords +      (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)") +              (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" "MEETING")))) + +(setq org-use-fast-todo-selection t) + +(setq org-todo-state-tags-triggers +      (quote (("CANCELLED" ("CANCELLED" . t)) +              ("WAITING" ("WAITING" . t)) +              ("HOLD" ("WAITING") ("HOLD" . t)) +              (done ("WAITING") ("HOLD")) +              ("TODO" ("WAITING") ("CANCELLED") ("HOLD")) +              ("NEXT" ("WAITING") ("CANCELLED") ("HOLD")) +              ("DONE" ("WAITING") ("CANCELLED") ("HOLD"))))) + +(setq org-enforce-todo-dependencies t) +(setq org-log-done (quote time)) +(setq org-log-redeadline (quote time)) +(setq org-log-reschedule (quote time)) + +;; CAPTURE ;; +(setq org-default-notes-file my-org-refile-path) +(setq org-capture-templates +      (quote +       ( +        ("w" "Todo" entry (file+headline my-org-refile-path "Tasks") +         "* TODO " +         :empty-lines 1) +        ("m" +         "Capture incoming email" +         entry +         (file+headline my-org-refile-path "Incoming") +         "* TODO Re: %:description\n\n  Source: %u, %a\n" +         :empty-lines 1) +        ))) + +(add-hook 'org-capture-mode-hook 'delete-other-windows) +(setq my-org-protocol-flag nil) + +(defadvice org-capture-finalize (after delete-frame-at-end activate) +  "Delete frame at remember finalization" +  (progn (if my-org-protocol-flag (delete-frame)) +         (setq my-org-protocol-flag nil))) + +(defadvice org-capture-kill (after delete-frame-at-end activate) +  "Delete frame at remember abort" +  (progn (if my-org-protocol-flag (delete-frame)) +         (setq my-org-protocol-flag nil))) + +(defadvice org-protocol-capture (before set-org-protocol-flag activate) +  (setq my-org-protocol-flag t)) + + +;; REFILE ;; + +; Targets include this file and any file contributing to the agenda - up to 3 levels deep +(setq org-refile-targets +      '((nil :maxlevel . 3) +        (org-agenda-files :maxlevel . 3))) + +; Targets complete directly with IDO +(setq org-outline-path-complete-in-steps nil) + +; Allow refile to create parent tasks with confirmation +(setq org-refile-allow-creating-parent-nodes (quote confirm)) + +(defun bh/is-project-p () +  "Any task with a todo keyword subtask" +  (save-restriction +    (widen) +    (let ((has-subtask) +          (subtree-end (save-excursion (org-end-of-subtree t))) +          (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1))) +      (save-excursion +        (forward-line 1) +        (while (and (not has-subtask) +                    (< (point) subtree-end) +                    (re-search-forward "^\*+ " subtree-end t)) +          (when (member (org-get-todo-state) org-todo-keywords-1) +            (setq has-subtask t)))) +      (and is-a-task has-subtask)))) + +(defun bh/is-project-subtree-p () +  "Any task with a todo keyword that is in a project subtree. +Callers of this function already widen the buffer view." +  (let ((task (save-excursion (org-back-to-heading 'invisible-ok) +                              (point)))) +    (save-excursion +      (bh/find-project-task) +      (if (equal (point) task) +          nil +        t)))) + +(defun bh/is-task-p () +  "Any task with a todo keyword and no subtask" +  (save-restriction +    (widen) +    (let ((has-subtask) +          (subtree-end (save-excursion (org-end-of-subtree t))) +          (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1))) +      (save-excursion +        (forward-line 1) +        (while (and (not has-subtask) +                    (< (point) subtree-end) +                    (re-search-forward "^\*+ " subtree-end t)) +          (when (member (org-get-todo-state) org-todo-keywords-1) +            (setq has-subtask t)))) +      (and is-a-task (not has-subtask))))) + +(defun bh/is-subproject-p () +  "Any task which is a subtask of another project" +  (let ((is-subproject) +        (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1))) +    (save-excursion +      (while (and (not is-subproject) (org-up-heading-safe)) +        (when (member (nth 2 (org-heading-components)) org-todo-keywords-1) +          (setq is-subproject t)))) +    (and is-a-task is-subproject))) + +(defun bh/list-sublevels-for-projects-indented () +  "Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks. +  This is normally used by skipping functions where this variable is already local to the agenda." +  (if (marker-buffer org-agenda-restrict-begin) +      (setq org-tags-match-list-sublevels 'indented) +    (setq org-tags-match-list-sublevels nil)) +  nil) + +(defun bh/list-sublevels-for-projects () +  "Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks. +  This is normally used by skipping functions where this variable is already local to the agenda." +  (if (marker-buffer org-agenda-restrict-begin) +      (setq org-tags-match-list-sublevels t) +    (setq org-tags-match-list-sublevels nil)) +  nil) + +(defvar bh/hide-scheduled-and-waiting-next-tasks t) + +(defun bh/toggle-next-task-display () +  (interactive) +  (setq bh/hide-scheduled-and-waiting-next-tasks (not bh/hide-scheduled-and-waiting-next-tasks)) +  (when  (equal major-mode 'org-agenda-mode) +    (org-agenda-redo)) +  (message "%s WAITING and SCHEDULED NEXT Tasks" (if bh/hide-scheduled-and-waiting-next-tasks "Hide" "Show"))) + +(defun bh/skip-stuck-projects () +  "Skip trees that are not stuck projects" +  (save-restriction +    (widen) +    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) +      (if (bh/is-project-p) +          (let* ((subtree-end (save-excursion (org-end-of-subtree t))) +                 (has-next )) +            (save-excursion +              (forward-line 1) +              (while (and (not has-next) (< (point) subtree-end) (re-search-forward "^\\*+ NEXT " subtree-end t)) +                (unless (member "WAITING" (org-get-tags-at)) +                  (setq has-next t)))) +            (if has-next +                nil +              next-headline)) ; a stuck project, has subtasks but no next task +        nil)))) + +(defun bh/skip-non-stuck-projects () +  "Skip trees that are not stuck projects" +  ;; (bh/list-sublevels-for-projects-indented) +  (save-restriction +    (widen) +    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) +      (if (bh/is-project-p) +          (let* ((subtree-end (save-excursion (org-end-of-subtree t))) +                 (has-next )) +            (save-excursion +              (forward-line 1) +              (while (and (not has-next) (< (point) subtree-end) (re-search-forward "^\\*+ NEXT " subtree-end t)) +                (unless (member "WAITING" (org-get-tags-at)) +                  (setq has-next t)))) +            (if has-next +                next-headline +              nil)) ; a stuck project, has subtasks but no next task +        next-headline)))) + +(defun bh/skip-non-projects () +  "Skip trees that are not projects" +  ;; (bh/list-sublevels-for-projects-indented) +  (if (save-excursion (bh/skip-non-stuck-projects)) +      (save-restriction +        (widen) +        (let ((subtree-end (save-excursion (org-end-of-subtree t)))) +          (cond +           ((bh/is-project-p) +            nil) +           ((and (bh/is-project-subtree-p) (not (bh/is-task-p))) +            nil) +           (t +            subtree-end)))) +    (save-excursion (org-end-of-subtree t)))) + +(defun bh/skip-non-tasks () +  "Show non-project tasks. +Skip project and sub-project tasks, habits, and project related tasks." +  (save-restriction +    (widen) +    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) +      (cond +       ((bh/is-task-p) +        nil) +       (t +        next-headline))))) + +(defun bh/skip-project-trees-and-habits () +  "Skip trees that are projects" +  (save-restriction +    (widen) +    (let ((subtree-end (save-excursion (org-end-of-subtree t)))) +      (cond +       ((bh/is-project-p) +        subtree-end) +       ((org-is-habit-p) +        subtree-end) +       (t +        nil))))) + +(defun bh/skip-projects-and-habits-and-single-tasks () +  "Skip trees that are projects, tasks that are habits, single non-project tasks" +  (save-restriction +    (widen) +    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) +      (cond +       ((org-is-habit-p) +        next-headline) +       ((and bh/hide-scheduled-and-waiting-next-tasks +             (member "WAITING" (org-get-tags-at))) +        next-headline) +       ((bh/is-project-p) +        next-headline) +       ((and (bh/is-task-p) (not (bh/is-project-subtree-p))) +        next-headline) +       (t +        nil))))) + +(defun bh/skip-project-tasks-maybe () +  "Show tasks related to the current restriction. +When restricted to a project, skip project and sub project tasks, habits, NEXT tasks, and loose tasks. +When not restricted, skip project and sub-project tasks, habits, and project related tasks." +  (save-restriction +    (widen) +    (let* ((subtree-end (save-excursion (org-end-of-subtree t))) +           (next-headline (save-excursion (or (outline-next-heading) (point-max)))) +           (limit-to-project (marker-buffer org-agenda-restrict-begin))) +      (cond +       ((bh/is-project-p) +        next-headline) +       ((org-is-habit-p) +        subtree-end) +       ((and (not limit-to-project) +             (bh/is-project-subtree-p)) +        subtree-end) +       ((and limit-to-project +             (bh/is-project-subtree-p) +             (member (org-get-todo-state) (list "NEXT"))) +        subtree-end) +       (t +        nil))))) + +(defun bh/skip-project-tasks () +  "Show non-project tasks. +Skip project and sub-project tasks, habits, and project related tasks." +  (save-restriction +    (widen) +    (let* ((subtree-end (save-excursion (org-end-of-subtree t)))) +      (cond +       ((bh/is-project-p) +        subtree-end) +       ((org-is-habit-p) +        subtree-end) +       ((bh/is-project-subtree-p) +        subtree-end) +       (t +        nil))))) + +(defun bh/skip-non-project-tasks () +  "Show project tasks. +Skip project and sub-project tasks, habits, and loose non-project tasks." +  (save-restriction +    (widen) +    (let* ((subtree-end (save-excursion (org-end-of-subtree t))) +           (next-headline (save-excursion (or (outline-next-heading) (point-max))))) +      (cond +       ((bh/is-project-p) +        next-headline) +       ((org-is-habit-p) +        subtree-end) +       ((and (bh/is-project-subtree-p) +             (member (org-get-todo-state) (list "NEXT"))) +        subtree-end) +       ((not (bh/is-project-subtree-p)) +        subtree-end) +       (t +        nil))))) + +(defun bh/skip-projects-and-habits () +  "Skip trees that are projects and tasks that are habits" +  (save-restriction +    (widen) +    (let ((subtree-end (save-excursion (org-end-of-subtree t)))) +      (cond +       ((bh/is-project-p) +        subtree-end) +       ((org-is-habit-p) +        subtree-end) +       (t +        nil))))) + +(defun bh/skip-non-subprojects () +  "Skip trees that are not projects" +  (let ((next-headline (save-excursion (outline-next-heading)))) +    (if (bh/is-subproject-p) +        nil +      next-headline))) + +;; CLOCKING ;; +;; Resume clocking task when emacs is restarted +(org-clock-persistence-insinuate) +;; +;; Show lot of clocking history so it's easy to pick items off the C-F11 list +(setq org-clock-history-length 23) +;; Resume clocking task on clock-in if the clock is open +(setq org-clock-in-resume t) +;; Change tasks to NEXT when clocking in +(setq org-clock-in-switch-to-state 'bh/clock-in-to-next) +;; Separate drawers for clocking and logs +(setq org-drawers (quote ("PROPERTIES" "LOGBOOK"))) +;; Save clock data and state changes and notes in the LOGBOOK drawer +(setq org-clock-into-drawer t) +;; Sometimes I change tasks I'm clocking quickly - this removes clocked tasks with 0:00 duration +(setq org-clock-out-remove-zero-time-clocks t) +;; Clock out when moving task to a done state +(setq org-clock-out-when-done t) +;; Save the running clock and all clock history when exiting Emacs, load it on startup +(setq org-clock-persist t) +;; Do not prompt to resume an active clock +(setq org-clock-persist-query-resume nil) +;; Enable auto clock resolution for finding open clocks +(setq org-clock-auto-clock-resolution (quote when-no-clock-is-running)) +;; Include current clocking task in clock reports +(setq org-clock-report-include-clocking-task t) + + +(setq bh/keep-clock-running nil) + +(defun bh/clock-in-to-next (kw) +  "Switch a task from TODO to NEXT when clocking in. +Skips capture tasks, projects, and subprojects. +Switch projects and subprojects from NEXT back to TODO" +  (when (not (and (boundp 'org-capture-mode) org-capture-mode)) +    (cond +     ((and (member (org-get-todo-state) (list "TODO")) +           (bh/is-task-p)) +      "NEXT") +     ((and (member (org-get-todo-state) (list "NEXT")) +           (bh/is-project-p)) +      "TODO")))) + +(defun bh/find-project-task () +  "Move point to the parent (project) task if any" +  (save-restriction +    (widen) +    (let ((parent-task (save-excursion (org-back-to-heading 'invisible-ok) (point)))) +      (while (org-up-heading-safe) +        (when (member (nth 2 (org-heading-components)) org-todo-keywords-1) +          (setq parent-task (point)))) +      (goto-char parent-task) +      parent-task))) + +(defun bh/punch-in (arg) +  "Start continuous clocking and set the default task to the +selected task.  If no task is selected set the Organization task +as the default task." +  (interactive "p") +  (setq bh/keep-clock-running t) +  (if (equal major-mode 'org-agenda-mode) +      ;; +      ;; We're in the agenda +      ;; +      (let* ((marker (org-get-at-bol 'org-hd-marker)) +             (tags (org-with-point-at marker (org-get-tags-at)))) +        (if (and (eq arg 4) tags) +            (org-agenda-clock-in '(16)) +          (bh/clock-in-organization-task-as-default))) +    ;; +    ;; We are not in the agenda +    ;; +    (save-restriction +      (widen) +      ; Find the tags on the current task +      (if (and (equal major-mode 'org-mode) (not (org-before-first-heading-p)) (eq arg 4)) +          (org-clock-in '(16)) +        (bh/clock-in-organization-task-as-default))))) + +(defun bh/punch-out () +  (interactive) +  (setq bh/keep-clock-running nil) +  (when (org-clock-is-active) +    (org-clock-out)) +  (org-agenda-remove-restriction-lock)) + +(defun bh/clock-in-default-task () +  (save-excursion +    (org-with-point-at org-clock-default-task +      (org-clock-in)))) + +(defun bh/clock-in-parent-task () +  "Move point to the parent (project) task if any and clock in" +  (let ((parent-task)) +    (save-excursion +      (save-restriction +        (widen) +        (while (and (not parent-task) (org-up-heading-safe)) +          (when (member (nth 2 (org-heading-components)) org-todo-keywords-1) +            (setq parent-task (point)))) +        (if parent-task +            (org-with-point-at parent-task +              (org-clock-in)) +          (when bh/keep-clock-running +            (bh/clock-in-default-task))))))) + +(defvar bh/organization-task-id "eb155a82-92b2-4f25-a3c6-0304591af2f9") + +(defun bh/clock-in-organization-task-as-default () +  (interactive) +  (org-with-point-at (org-id-find bh/organization-task-id 'marker) +    (org-clock-in '(16)))) + +(defun bh/clock-out-maybe () +  (when (and bh/keep-clock-running +             (not org-clock-clocking-in) +             (marker-buffer org-clock-default-task) +             (not org-clock-resolving-clocks-due-to-idleness)) +    (bh/clock-in-parent-task))) + +(add-hook 'org-clock-out-hook 'bh/clock-out-maybe 'append) + + +;; ORG REPORTS ;; +; Set default column view headings: Task Effort Clock_Summary +(setq org-columns-default-format "%80ITEM(Task) %10Effort(Effort){:} %10CLOCKSUM") + +(defun my-org-clocktable-indent-string (level) +  (if (= level 1) +      "" +    (let ((str "^")) +      (while (> level 2) +        (setq level (1- level) +              str (concat str "--"))) +      (concat str "-> ")))) + +(advice-add 'org-clocktable-indent-string :override #'my-org-clocktable-indent-string) + +(setq org-clock-clocktable-default-properties '(:maxlevel 4 :scope file :formula %)) + +; global Effort estimate values +; global STYLE property values for completion +(setq org-global-properties (quote (("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00") +                                    ("STYLE_ALL" . "habit")))) + +;; TAGS ;; +; Tags with fast selection keys +(setq org-tag-alist (quote ((:startgroup) +                            ("@errand" . ?e) +                            ("@office" . ?o) +                            ("@home" . ?H) +                            (:endgroup) +                            ("WAITING" . ?w) +                            ("HOLD" . ?h) +                            ("PERSONAL" . ?P) +                            ("WORK" . ?W) +                            ("ORG" . ?O) +                            ("NOTE" . ?n) +                            ("CANCELLED" . ?c) +                            ("FLAGGED" . ??)))) + +(setq org-stuck-projects +      '("+LEVEL=2+PROJECT/-MAYBE-DONE" ("NEXT") ("@shop") +        "\\<IGNORE\\>")) + +; Allow setting single tags without the menu +(setq org-fast-tag-selection-single-key (quote expert)) + +;; org-modern +(add-hook 'org-mode-hook 'org-modern-mode) +(add-hook 'org-agenda-finalize-hook #'org-modern-agenda) + +(provide 'rul-org)  | 
