The best thing about Emacs. Check out the manual or run org-info
.
;;; 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 mixed-pitch-mode "ext:mixed-pitch.el")
(declare-function org-roam-buffer-p "ext:org-roam.el")
(declare-function org-archive-subtree "ext:org-archive.el")
(declare-function project-prompt-project-dir "ext:project.el")
(declare-function project-root "ext:project.el")
(declare-function visual-fill-column-mode "ext:visual-fill-column.el")
(declare-function wal-advise-many "wal-useful.el")
(declare-function wal-append "wal-useful.el")
(declare-function wal-project-local-value "wal-workspace.el")
(declare-function wal-replace-in-alist "wal-fun.el")
(defvar org-agenda-window-setup)
(defvar org-archive-default-command)
(defvar org-clock-current-task)
(defvar org-log-note-marker)
(defvar org-roam-dailies-capture-templates)
(defvar org-roam-dailies-directory)
(defvar org-roam-directory)
(defvar org-super-agenda-groups)
(defvar partial-recall-meaningful-traits)
(defvar project-current-directory-override)
(defvar text-scale-mode-step)
;;;; 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)
(junk-expand org
"Additional Org-related packages."
:extras (org-habit-stats))
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!")
: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)))
;; 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)
;; Enforce dependencies.
(org-enforce-todo-checkbox-dependencies t)
(org-enforce-todo-dependencies t)
:bind
(:map org-mode-map
("M-p" . org-previous-visible-heading)
("M-n" . org-next-visible-heading)))
(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))))]))
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--then-rename-tab (&rest _)
"Rename the tab if we set up the window using tabs."
(when (eq org-agenda-window-setup 'other-tab)
(tab-bar-rename-tab "agenda")))
(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")))
(advice-add
'org-agenda-prepare-window :after
#'wal-org-agenda--then-rename-tab)
:custom
(org-agenda-hide-tags-regexp "^@")
(org-agenda-span 'day)
(org-agenda-window-setup 'other-tab)
(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)
:map org-agenda-mode-map
("C-c C-t" . org-agenda-todo-yesterday)
("<RET>" . org-agenda-goto)
("M-<RET>" . org-agenda-switch-to)))
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))
Allows for nicer grouping in the agenda view. The groups relate to custom groups and todo keywords.
(defvar wal-org-super-agenda-groups
'(;; (Re-)Schedule.
(:name "Schedule" :time-grid t :order 2)
(:name "Any time" :and (:scheduled today :not (:habit t)) :order 1)
(:name "Leftovers"
:and (:scheduled past
:todo t
:not (:habit t))
:order 3)
;; Items with deadlines.
(:name "Upcoming" :and (:scheduled nil :deadline future) :order 4)
(:name "Catch up" :deadline past :order 6)
(:name "Achieved" :and (:deadline today :todo "DONE") :order 8)
(:name "Don't forget" :and (:scheduled nil :deadline today) :order 0)
;; Habits.
(:order-multi (5 (:name "Education" :and (:habit t :tag "@education"))
(: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)))
;; Show blocked and those that are associated with today, discard the rest.
(:name "Blocked" :todo "BLOCKED" :order 7)
(:name "Today" :date today :order 1)
(:discard (:anything t))))
(defun wal-org-super-agenda--with-groups (fun &rest args)
"Call FUN with ARGS."
(let ((org-super-agenda-groups wal-org-super-agenda-groups))
(apply fun args)))
(use-package org-super-agenda
:demand t
:after org-agenda
:config
(org-super-agenda-mode)
(advice-add
'org-agenda-list :around
#'wal-org-super-agenda--with-groups)
:custom
(org-super-agenda-final-group-separator "\n")
:functions (org-super-agenda-mode))
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))
(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)
(transient-define-prefix wal-org-roam ()
"Run `org-roam' commands."
[["Capture"
("t" "today" org-roam-dailies-capture-today)]
["Find"
("f" "note" org-roam-node-find)
("T" "today" org-roam-dailies-goto-today)
("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-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)))
:wal-bind
(("p" . org-roam-capture)
("M-p" . org-roam-dailies-capture-today)
("C-p" . wal-org-roam))
:functions (org-roam-db-autosync-enable)
:defines (org-roam-buffer org-roam-v2-ack))
(defun wal-org-roam-dailies--with-first-template-only (func &rest args)
"Run dailies command FUNC with templates set to nil.
ARGS are passed to FUNC."
(let ((org-roam-dailies-capture-templates (seq-subseq
org-roam-dailies-capture-templates
0
1)))
(funcall func args)))
(defun wal-org-archive-subtree ()
"Archive normally.
If the current file is a dailies file, archive in a single location."
(interactive)
(let* ((file-name (buffer-file-name))
(dailies-file-p (and file-name
(string-match-p org-roam-dailies-directory file-name)))
(org-archive-location (if dailies-file-p
(expand-file-name "dailies_archive.org::* From %s" org-roam-directory)
org-archive-location)))
;; FIXME: In Emacs 30 this doesn't emit a warning.
(with-no-warnings
(call-interactively #'org-archive-subtree))))
(use-package org-roam-dailies
:after org-roam
:demand t
:config
(setq org-archive-default-command #'wal-org-archive-subtree)
;; Don't force template selection when just visiting a file.
(wal-advise-many
'wal-org-roam-dailies--with-first-template-only :around
'(org-roam-dailies-goto-date
org-roam-dailies-goto-today
org-roam-dailies-goto-tomorrow
org-roam-dailies-goto-next-note
org-roam-dailies-goto-yesterday
org-roam-dailies-goto-previous-note))
:custom
(org-roam-dailies-directory "tagebuch/")
(org-roam-dailies-capture-templates
'(("t" "default" entry
"* %?\n:PROPERTIES:\n:TASK: %K\n:END:"
:target (file+head "%<%Y-%m-%d>.org"
"#+title: %<%Y-%m-%d>\n")
:empty-lines 1)
("d" "task with deadline" entry
"* TODO %?\nDEADLINE:%t\n:PROPERTIES:\n:TASK: %K\n:END:"
:target (file+head "%<%Y-%m-%d>.org"
"#+title: %<%Y-%m-%d>\n")
:empty-lines 1))))
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))
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")
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 (or (and arg (project-prompt-project-dir))
project-current-directory-override)))
(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 M-c" . wal-org-capture-project-tasks))
:delight " cap")
Configure refiling headings. Reduces the depth for agenda files.
(defun wal-org-refile--maybe-use-default-directory (&optional arg &rest _rest)
"If ARG is 5, set `org-agenda-files' to the `default-directory'."
(declare-function org-refile "ext:org-refile.el")
(when (eq 5 (prefix-numeric-value arg))
(let ((org-agenda-files (list default-directory)))
(org-refile))))
(use-package org-refile
:config
(advice-add
'org-refile
:before-until #'wal-org-refile--maybe-use-default-directory)
:custom
(org-refile-targets
'((nil . (:maxlevel . 4))
(org-agenda-files . (:maxlevel . 3)))))
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))
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)
(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)))))
(truncate-string-to-width heading 12 0 nil t)))
(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))
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)))))
Add some user speed commands.
(use-package org-keys
:after org
:custom
(org-use-speed-commands t)
(org-return-follows-link t))
Modern look.
(use-package org-modern
:hook (org-mode . org-modern-mode)
:custom
(org-modern-hide-stars " ")
(org-modern-star '("◆" "◇" "►" "▻" "▸" "▹" "•")))
(provide 'wal-org)
;;; wal-org.el ends here