Skip to content

Latest commit

 

History

History
968 lines (762 loc) · 26.4 KB

wal-org.org

File metadata and controls

968 lines (762 loc) · 26.4 KB

Org Mode

The best thing about Emacs. Check out the manual or run org-info.

Header

;;; wal-org.el --- Org. -*- lexical-binding: t -*-

;;; Commentary:
;;
;; Provide org packages/configurations.

;;; Code:

(eval-when-compile
  (require 'wal-useful nil t)
  (require 'wal-package nil t)
  (require 'wal-key-bindings nil t)
  (require 'wal-bridge nil t))

(declare-function consult-org-agenda "wal-complete.el")
(declare-function general-define-key "ext:general.el")
(declare-function mixed-pitch-mode "ext:mixed-pitch.el")
(declare-function org-clock-goto "ext:org-clock.el")
(declare-function org-roam-buffer-p "ext:org-roam.el")
(declare-function project-prompt-project-dir "ext:project.el")
(declare-function project-root "ext:project.el")
(declare-function transient-prefix "ext:transient.el")
(declare-function transient-setup "ext:transient.el")
(declare-function visual-fill-column-mode "ext:visual-fill-column.el")
(declare-function wal-append "wal-useful.el")
(declare-function wal-insert "wal-useful.el")
(declare-function wal-key-combo-for-leader "wal-key-bindings.el")
(declare-function wal-message-in-a-bottle "wal-useful.el")
(declare-function wal-project-local-value "wal-workspace.el")
(declare-function wal-replace-in-alist "wal-fun.el")
(declare-function wal-truncate "wal-useful.el")
(declare-function wal-univ-p "wal-useful.el")
(declare-function wdb-nearby "wal-useful.el")
(declare-function adjunct "wal-key-bindings.el")

(defvar org-clock-current-task)
(defvar org-log-note-this-command)
(defvar org-log-note-marker)
(defvar partial-recall-meaningful-traits)
(defvar project-current-directory-override)
(defvar text-scale-mode-step)
(defvar visual-fill-column-width)
(defvar visual-fill-column-center-text)
(defvar wal-project-parent-project)

;;;; Customization:

(defgroup wal-org nil
  "Change settings used for org packages."
  :group 'wal
  :tag "Org")

(defcustom wal-org-agenda-file ".agenda"
  "The index of the agenda.
This variable is relative to the `org-directory'."
  :group 'wal-org
  :type 'string)

Packages

(junk-expand org
  "Additional Org-related packages."
  :extras (org-habit-stats))

org

If not the reason why you came to Emacs, probably the one that makes you stay. Org is a structured plain-text format that can be manipulated to work for document authoring, task-planning and more. Read the manual in Emacs or on the web.

(defun wal-first-require-ox-md (&rest _args)
  "Advise to require `ox-md' before export dispatch."
  (require 'ox-md nil t))

(defun wal-org-hide-emphasis-markers (&optional show)
  "Hide emphasis markers.

If SHOW is t, show them instead."
  (interactive "P")

  (defvar org-hide-emphasis-markers)

  (setq org-hide-emphasis-markers (not show))
  (font-lock-flush))

(defvar wal-org--unsaved nil)

(defun wal-org--first-record-buffer (&rest _)
  "Record the buffer from the log marker."
  (and-let* (((boundp 'org-log-note-marker))
             (marker org-log-note-marker)
             (buffer (marker-buffer marker)))

    (setq wal-org--unsaved buffer)))

(defun wal-org--then-save-unsaved-buffer (&rest _)
  "Save the buffer for which a note has been taken."
  (when wal-org--unsaved

    (with-current-buffer wal-org--unsaved
      (save-buffer))

    (setq wal-org--unsaved nil)))

(use-package org
  :commands (org-add-note org-find-exact-heading-in-directory)

  :init
  (harpoon org-mode
    :checker disabled
    :messages ("Organize! Seize the means of production!")
    :functions (auto-fill-mode)
    :bind t)

  :config
  ;; TEMP: Getting missing face errors otherwise.
  (require 'org-indent)

  ;; Require `ox-md' before calling dispatch as it might not be loaded.
  (advice-add
   'org-export-dispatch :before
   #'wal-first-require-ox-md)

  (advice-add
   'org-add-log-setup :after
   #'wal-org--first-record-buffer)

  (advice-add
   'org-store-log-note :after
   #'wal-org--then-save-unsaved-buffer)

  ;; Create register file if it doesn't yet exist
  (let ((register (expand-file-name wal-org-agenda-file org-directory)))

    (unless (file-exists-p register)
      (make-empty-file register)))

  (with-no-warnings
    (wal-transient-define-major org-mode ()
      "Access `org-mode' commands."
      [["Edit"
        ("w" "cut subtree" org-cut-subtree
         :inapt-if-not org-at-heading-p)
        ("y" "paste subtree" org-paste-subtree)
        ("n" "add note" org-add-note)
        ("." "toggle timestamp" org-toggle-timestamp-type
         :inapt-if-not (lambda () (org-at-timestamp-p 'inactive)))
        ("s" "sort" org-sort
         :inapt-if-not (lambda () (or (org-at-item-p) (org-at-heading-p))))]

       ["Footnotes"
        ("f" "add footnote" org-footnote-new
         :inapt-if org-in-src-block-p)
        ("n" "normalize footnotes" org-footnote-normalize)]]

      [["Visibility"
        ("c" "show content" org-content)
        ("a" "show all" org-fold-show-all)
        ("i" "toggle indentation" org-indent-mode)
        ("v" "visual line" visual-line-mode)
        ("m" "hide emphasis markers" wal-org-hide-emphasis-markers)]

       ["Help"
        ("h" "node info" org-info-find-node)]]))

  :custom
  ;; Make it look nice and tidy.
  (org-adapt-indentation nil)
  (org-ellipsis "")
  (org-startup-with-inline-images t)
  (org-startup-folded 'nofold)
  (org-cycle-separator-lines 1)

  ;; Logging.
  (org-log-done 'time)
  (org-log-into-drawer t)

  ;; Set up directories.
  (org-default-notes-file (expand-file-name "notes.org" org-directory))
  (org-agenda-files (expand-file-name wal-org-agenda-file org-directory))

  ;; Be sure to add archive tag with `org-toggle-archive-tag'.
  (org-archive-location "::* Archived")

  ;; Adapt keywords, tags and speed commands.
  (org-todo-keywords
   '((sequence "TODO(t)" "IN PROGRESS(p)" "WAITING(w@/!)" "BLOCKED(b@/@)" "|" "DONE(d)" "CANCELED(c@/!)")))
  (org-tag-persistent-alist
   '((:startgroup)
     ("depth")
     (:grouptags)
     ("@immersive")
     ("@process")
     (:endgroup)

     (:startgroup)
     ("context")
     (:grouptags)
     ("@work")
     ("@home")
     ("@away")
     (:endgroup)

     (:startgroup)
     ("characteristic")
     (:grouptags)
     ("@unbillable")
     ("@repeated")
     ("@intermittent")
     (:endgroup)

     (:startgroup)
     ("energy")
     (:grouptags)
     ("@easy")
     ("@average")
     ("@challenge")
     (:endgroup)

     (:startgroup)
     ("category")
     (:grouptags)
     ("@development")
     ("@talk")
     ("@contribution")
     ("@wellbeing")
     ("@education")
     ("@chore")
     (:endgroup)))

  ;; Warn late about upcoming deadlines.
  (org-deadline-warning-days 2)

  ;; Use group energy to identify projects.
  (org-stuck-projects '("+energy/-ARCHIVE" ("TODO" "IN PROGRESS") nil ""))

  ;; Show archived items.
  (org-sparse-tree-open-archived-trees t)

  :bind
  (:map org-mode-map
   ("M-p" . org-previous-visible-heading)
   ("M-n" . org-next-visible-heading)))

org-habit-stats

(use-package org-habit-stats
  :defer 3
  :after org-agenda

  :config
  (transient-append-suffix 'org-mode-major '(1 -1)
    '["Habits"
      ("S" "stats" org-habit-stats-view-habit-at-point
       :inapt-if-not (lambda () (org-is-habit-p (point))))]))

org-agenda

Plan your day, week and year. This adapts the agenda view to show what I need day-to-day and adds a consult buffer source.

(defun wal-agenda-buffer-p (buffer)
  "Check if BUFFER contributes to the agenda."
  (declare-function org-agenda-file-p "ext:org.el")

  (org-agenda-file-p (buffer-file-name buffer)))

(defun wal-org-agenda-take-note (&optional arg)
  "Take a note for an agenda item.

This prefers the currently clocked item unless ARG is passed.

Otherwise (or if there is no clocked item) this prompts to select
the item first."
  (interactive "P")

  (declare-function org-clock-goto "ext:org-clock.el")

  (let ((current org-clock-current-task))

    (save-window-excursion
      (if (and current
               (not arg))
          (org-clock-goto)
        (consult-org-agenda))
      (org-add-note))))

(use-package org-agenda
  :config
  (with-eval-after-load 'partial-recall
    (parallel-mirror wal-agenda-buffer-p :type boolean)
    (put 'parallel-mirror-wal-agenda-buffer-p 'partial-recall-non-meaningful-explainer "Agenda buffer")
    (add-to-list 'partial-recall-meaningful-traits 'parallel-mirror-wal-agenda-buffer-p))

  (wal-replace-in-alist 'org-agenda-prefix-format '((agenda . "  %?-12t%?-12c%? s%?b")))

  :custom
  (org-agenda-hide-tags-regexp "^@")
  (org-agenda-span 'day)
  (org-agenda-restore-windows-after-quit t)
  (org-agenda-time-leading-zero t)
  (org-agenda-log-mode-items '(clock))
  (org-agenda-start-with-clockreport-mode t)
  (org-agenda-start-with-log-mode t)
  (org-agenda-clockreport-parameter-plist
   '(:link t
     :maxlevel 3
     :fileskip0 t
     :emphasize t
     :match "-@unbillable"))

  :bind
  (("C-c a" . org-agenda)
   ("C-c n" . wal-org-agenda-take-note)
   :map org-agenda-mode-map
   ("C-c C-t" . org-agenda-todo-yesterday)))

org-habit

Habits are a special kind of todo to keep track of what you keep doing/forgetting to do.

(use-package org-habit
  :custom
  (org-habit-show-habits-only-for-today nil)
  (org-habit-graph-column 70))

org-super-agenda

Allows for nicer grouping in the agenda view. The groups relate to custom groups and todo keywords.

(use-package org-super-agenda
  :demand t
  :after org-agenda

  :config
  (org-super-agenda-mode)

  :custom
  (org-super-agenda-groups
   '((:name "Schedule" :time-grid t)
     (:name "Unscheduled"
      :and (:scheduled nil
            :not (:tag "@intermittent" :todo "BLOCKED")))
     (:name "Leftovers"
      :and (:todo ("IN PROGRESS" "WAITING")
            :scheduled past
            :not (:tag "@repeated" :tag "@education")))
     (:name "Blocked" :todo "BLOCKED")
     (:name "Education" :and (:habit t :tag "@education"))

     ;; Habits.
     (:name "Contribution"
      :and (:habit t
            :tag "@contribution"))
     (:name "Well-being"
      :and (:habit t
            :tag "@wellbeing"))
     (:name "Chores"
      :and (:habit t
            :tag "@chore"))
     (:name "Other habits"
            :habit t)

     ;; Discard the rest.
     (:discard (:anything t))))
  (org-super-agenda-final-group-separator "\n")

  :functions (org-super-agenda-mode))

org-roam

Trying to organize my thoughts using Zettelkästen. This package allows you to create a web of interconnected nodes of org files.

This adds a function to refile only within org-roam files.

Note that you will need to install sqlite3 manually.

(junk-expand org-roam
  "Note rhizome."
  :packages (org-roam)
  :extras (org-roam-ui))

(defun wal-org-refile (&optional arg)
  "Refile using ARG, but use `org-roam-directory' for its files.

If called with numeric prefix `5', set variable
`org-agenda-files' to the `org-roam-directory'."
  (interactive "P")

  (declare-function org-refile "ext:org.el")
  (defvar org-roam-directory)

  (cond
   ((and (org-roam-buffer-p) (not (equal arg 5)))
    (let ((org-agenda-files (list org-roam-directory)))

      (org-refile arg)))

   ((equal arg 5)
    (org-refile))

   (t (org-refile arg))))

(use-package org-roam
  :wal-ways t
  :if (executable-find "sqlite3")

  :commands
  (org-roam-buffer-display-dedicated
   org-roam-capture
   org-roam-node-create
   org-roam-node-find
   org-roam-node-read
   wal-org-roam)

  :init
  (setq org-roam-v2-ack t)

  :config
  ;; Show roam buffer on the right.
  (wdb-nearby org-roam-buffer :side 'right :no-other t)

  ;; Refile differently for these files.
  (wal-replace-in-alist 'org-speed-commands '(("w" . wal-org-refile)))

  (transient-define-prefix wal-org-roam ()
    "Run `org-roam' commands."
    [["Capture"
      ("c" "node" org-roam-capture)
      ("t" "today" org-roam-dailies-capture-today)]
     ["Find"
      ("f" "node" org-roam-node-find)
      ("d" "daily" org-roam-dailies-goto-date)
      ("D" "daily directory" org-roam-dailies-find-directory)]
     ["Actions"
      ("b" "toggle roam buffer" org-roam-buffer-toggle)
      ("w" "roam refile" org-roam-refile
       :inapt-if-not-mode 'org-mode)
      ("i" "insert node" org-roam-node-insert
       :inapt-if-not-mode 'org-mode)
      ("@" "add tag" org-roam-tag-add
       :inapt-if-not-mode 'org-mode)]
     ["Visualization"
      ("g" "graph" org-roam-graph)]])

  (org-roam-db-autosync-enable)

  :custom
  ;; Setup directories and file names.
  (org-roam-directory (expand-file-name "zettelkasten" org-directory))
  (org-roam-dailies-directory "tagebuch/")
  (org-roam-extract-new-file-path "${slug}.org")

  ;; Simple capture templates.
  (org-roam-capture-templates
   '(("d" "default" plain "%?"
      :target (file+head "${slug}.org"
                         "#+title: ${title}\n")
      :unnarrowed t)))
  (org-roam-dailies-capture-templates
   '(("d" "default" entry
      "* %?\n:PROPERTIES:\n:CREATED_AT: %U\n:TASK: %k\n:END:"
      :target (file+head "%<%Y-%m-%d>.org"
                         "#+title: %<%Y-%m-%d>\n")
      :empty-lines 1)))

  :wal-bind
  (("\\" . org-roam-capture)
   ("M-\\" . wal-org-roam))

  :functions (org-roam-db-autosync-enable)
  :defines (org-roam-buffer org-roam-v2-ack))

org-roam-ui

Fancy UI for org-roam.

(use-package org-roam-ui
  :defer 3
  :after org-roam

  :config
  (transient-append-suffix 'wal-org-roam '(0 3 0)
    '("u" "ui" org-roam-ui-mode)))

org-tree-slide

Turn any org-mode buffer into a presentation. The custom functions make sure that content is centered and only code retains fixed pitch.

(defun wal-relative-column-width (&optional target-width)
  "Get the relative column width of TARGET-WIDTH."
  (let ((width (or target-width 160))
        (scale (if (and (boundp 'text-scale-mode-amount)
                        (numberp text-scale-mode-amount))
                   (expt text-scale-mode-step text-scale-mode-amount)
                 1)))

    (ceiling (/ width scale))))

(defun wal-org-tree-slide-toggle-visibility ()
  "Toggle visibility of cursor."
  (interactive)

  (if cursor-type
      (setq cursor-type nil)
    (setq cursor-type t)))

(defun wal-org-tree-slide-play ()
  "Hook into `org-tree-slide-play'."
  (setq-local visual-fill-column-width (wal-relative-column-width 160)
              visual-fill-column-center-text t
              cursor-type nil)
  (visual-fill-column-mode 1)

  (mixed-pitch-mode 1)

  (wal-org-hide-emphasis-markers))

(defun wal-org-tree-slide-stop ()
  "Hook into `org-tree-slide-stop'."
  (setq-local visual-fill-column-width nil
              visual-fill-column-center-text nil
              cursor-type t
              org-hide-emphasis-markers nil)
  (visual-fill-column-mode -1)

  (declare-function outline-show-all "ext:outline.el")

  (outline-show-all)

  (mixed-pitch-mode -1)

  (text-scale-adjust 0)

  (wal-org-hide-emphasis-markers t))

(defun wal-org-tree-slide-text-scale ()
  "Hook into `text-scale-mode-hook' for `org-tree-slide'."
  (when (and (boundp 'org-tree-slide-mode) org-tree-slide-mode)
    (wal-org-tree-slide-play)))

(use-package org-tree-slide
  :after org

  :hook
  ((org-tree-slide-play . wal-org-tree-slide-play)
   (org-tree-slide-stop . wal-org-tree-slide-stop)
   (text-scale-mode . wal-org-tree-slide-text-scale))

  :init
  (transient-append-suffix 'org-mode-major '(1 -1)
    '["Presentation"
      ("p" "present" org-tree-slide-mode)])

  :custom
  (org-tree-slide-never-touch-face t)
  (org-tree-slide-cursor-init nil)
  (org-tree-slide-activate-message "We're on a road to nowhere")
  (org-tree-slide-deactivate-message "Take you here, take you there")
  (org-tree-slide-indicator '(:next "   >>>" :previous "<<<" :content "< Here is where time is on our side >"))

  :bind
  (:map org-tree-slide-mode-map
   ("q" . org-tree-slide-mode) ; To close it again.
   ("n" . org-tree-slide-move-next-tree)
   ("p" . org-tree-slide-move-previous-tree)
   ("i" . text-scale-increase)
   ("d" . text-scale-decrease)
   ("v" . wal-org-tree-slide-toggle-visibility))

  :defines (org-tree-slide-mode-map))

org-src

Editing source blocks in Org files.

Loads a few more languages and disables native tabs in source blocks.

(use-package org-src
  :after org

  :config
  (wal-append
   'org-src-lang-modes
   '(("dockerfile" . dockerfile)
     ("conf" . conf)
     ("markdown" . markdown)
     ("fish" . fish)))

  (transient-append-suffix 'org-mode-major '(0 0 -1)
    '("e" "edit source" org-edit-src-code
      :inapt-if-not org-in-src-block-p))

  :custom
  (org-src-tab-acts-natively nil)
  (org-edit-src-content-indentation 0)

  :bind
  (:map org-src-mode-map
   ("C-c C-c" . org-edit-src-exit))

  :delight " osc")

org-capture

If you want to just write a quick note or todo for yourself, org-capture is your friend. This adds the concept of project tasks that are collected in distinct files under a desired heading. They can be created using one of the custom templates. The others are for taking notes related to the currently clocked task and one for dailies (although org-roam is preferred for these).

(defvar-local wal-org-capture-tasks-heading "Tasks")
(put 'wal-org-capture-tasks-heading 'safe-local-variable #'stringp)

(defvar-local wal-org-capture-tasks-file nil)
(put 'wal-org-capture-tasks-file 'safe-local-variable #'stringp)

(defun wal-org-capture--find-project-tasks-heading (&optional arg)
  "Find the heading of a project's tasks.

The project is the current project unless ARG is t."
  (declare-function org-find-exact-heading-in-directory "ext:org.el")
  (declare-function org-find-exact-headline-in-buffer "ext:org.el")

  (let ((project-current-directory-override (and arg (project-prompt-project-dir))))

    (if-let* ((project (project-current t))
              (root (project-root project))
              (heading (wal-project-local-value 'wal-org-capture-tasks-heading project))
              (marker (or (and-let* ((file (wal-project-local-value 'wal-org-capture-tasks-file project))
                                     (canonicalized (and (boundp 'org-directory)
                                                         (expand-file-name file org-directory)))
                                     (buffer (and (file-exists-p canonicalized)
                                                  (find-file-noselect canonicalized))))
                            (org-find-exact-headline-in-buffer heading buffer))
                          (org-find-exact-heading-in-directory heading (or (wal-project-local-value 'wal-project-parent-project) root)))))
        marker
      (user-error "Couldn't find heading '%s'" wal-org-capture-tasks-heading))))

(defun wal-org-capture-locate-project-tasks (&optional other-project)
  "Locate project tasks.

If OTHER-PROJECT is t, do that for another project."
  (let ((marker (wal-org-capture--find-project-tasks-heading other-project)))

    (set-buffer (marker-buffer marker))
    (goto-char (marker-position marker))))

(defun wal-org-capture-project-tasks (&optional goto)
  "Go to project tasks.

See `org-capture' for the usage of GOTO."
  (interactive "P")

  (org-capture goto "p"))

(use-package org-capture
  :custom
  (org-capture-templates
   `(("c" "clocking task" plain
      (clock)
      "\n%?\n"
      :unnarrowed t)
     ("d" "daily note" plain
      (file+olp+datetree ,(concat org-directory "/dailies.org"))
      "%i\n%?"
      :empty-lines-before 1
      :empty-lines-after 1)
     ("t" "new project task" entry
      (function wal-org-capture-locate-project-tasks)
      "* TODO %?\n\n%i"
      :empty-lines-before 1
      :empty-lines-after 1
      :before-finalize (org-set-tags-command))
     ("T" "new project task (other project)" entry
      (function (lambda () (wal-org-capture-locate-project-tasks t)))
      "* TODO %?\n\n%i"
      :empty-lines-before 1
      :empty-lines-after 1
      :before-finalize (org-set-tags-command))
     ("p" "project tasks" plain
      (function wal-org-capture-locate-project-tasks)
      ""
      :unnarrowed t)))
  (org-capture-bookmark nil) ; Prevents countless edit buffers since we annotate bookmarks.

  :bind
  (("C-c c" . org-capture)
   ("C-c v" . wal-org-capture-project-tasks))

  :delight " cap")

org-refile

Configure refiling headings. Reduces the depth for agenda files.

(use-package org-refile
  :custom
  (org-refile-targets
   '((nil . (:maxlevel . 4))
     (org-agenda-files . (:maxlevel . 3)))))

org-babel

Source block interaction.

Loads a few more languages and doesn’t require confirmation of block evaluation.

(use-package ob
  :config
  (wal-append
   'org-babel-load-languages
   '((shell . t)
     (python . t)
     (latex . t)
     (js . t)))

  :custom
  (org-confirm-babel-evaluate nil))

org-clock

You know the drill. Clock in, clock out. Makes sure that headings with a todo keyword are set to in progress when clocked into. Also adds a command to ignore continuous clocking as well as one to add a note to the clocked task.

(defvar-local wal-org-clock-in-progress-state "IN PROGRESS"
  "The state signifying a task is in progress.")
(put 'wal-org-clock-in-progress-state 'safe-local-variable #'stringp)

(defun wal-org-clock-in-switch-to-state (todo-state)
  "Only switch state to IN PROGRESS if TODO-STATE was given."
  (defvar org-todo-keywords-1)

  (when (and todo-state
             (member wal-org-clock-in-progress-state org-todo-keywords-1))
    wal-org-clock-in-progress-state))

(defun wal-org-clock-out-switch-to-state (todo-state)
  "Switch from TODO-STATE to a user-selected state.

The possible states is reduced to those following the current
state if that state is known."
  (defvar org-todo-keywords-1)
  (defvar org-clock-current-task)

  (and-let* (todo-state
             (keywords org-todo-keywords-1)
             (keywords (if (member todo-state keywords)
                           (seq-subseq keywords (seq-position keywords todo-state))
                         keywords))

             (task (or org-clock-current-task "Current task"))
             (prompt (format "Switch `%s' from %s to: " task todo-state)))

    (completing-read prompt keywords nil t)))

(defun wal-org-clock-heading ()
  "Render a truncated heading for modeline."
  (declare-function org-link-display-format "ext:org-link.el")
  (declare-function org-get-heading "ext:org.el")

  (let ((heading (org-link-display-format
	              (org-no-properties (org-get-heading t t t t)))))

    (wal-truncate heading 12)))

(defun wal-org-clock-in-from-now ()
  "Force `org-clock-in' without continuous logging."
  (defvar org-clock-continuously)
  (declare-function org-clock-in "ext:org-clock.el")

  (let ((org-clock-continuously nil))

    (org-clock-in)))

(defun wal-org-clock-kill-current-task ()
  "Insert the current task."
  (interactive)

  (unless org-clock-current-task
    (user-error "No current task"))

  (let ((no-props (substring-no-properties org-clock-current-task)))

    (kill-new no-props)
    (message "Added '%s' to kill ring" no-props)))

(use-package org-clock
  :after org

  :init
  (org-clock-persistence-insinuate)

  :config
  (with-eval-after-load 'org-keys
    (add-to-list 'org-speed-commands '("N" . wal-org-clock-in-from-now)))

  :custom
  ;; We want a continuous, persistent clock.
  (org-clock-continuously t)
  (org-clock-persist 'clock)

  ;; Resolve after idling.
  (org-clock-idle-time 120)

  ;; Switch state conditionally and resume.
  (org-clock-in-switch-to-state 'wal-org-clock-in-switch-to-state)
  (org-clock-in-resume t)

  ;; Switch state conditionally and remove zero clocks.
  (org-clock-out-switch-to-state 'wal-org-clock-out-switch-to-state)
  (org-clock-out-remove-zero-time-clocks t)

  (org-clock-report-include-clocking-task t)

  ;; Truncate overly long tasks.
  (org-clock-heading-function #'wal-org-clock-heading))

org-duration

Set up durations for a 40-hour week.

(use-package org-duration
  :after org

  :config
  ;; 40h working week, one month of vacation.
  (wal-replace-in-alist
    'org-duration-units
    `(("d" . ,(* 60 8))
      ("w" . ,(* 60 8 5))
      ("m" . ,(* 60 8 5 4))
      ("y" . ,(* 60 8 5 4 11)))))

org-keys

Add some user speed commands.

(use-package org-keys
  :after org

  :custom
  (org-use-speed-commands t)
  (org-return-follows-link t))

org-modern

Modern look.

(use-package org-modern
  :hook (org-mode . org-modern-mode)

  :custom
  (org-modern-hide-stars " ")
  (org-modern-star '("" "" "" "" "" "" "")))

Footer

(provide 'wal-org)

;;; wal-org.el ends here