;;; ~/.doom.d/config.el -*- lexical-binding: t; -*- (setq doom-theme 'gruvbox-dark-soft) ;; Set to 2 spaces (setq default-tab-width 2) (setq tab-width 2) (setq web-mode-markup-indent-offset 2) (setq web-mode-css-indent-offset 2) (setq web-mode-code-indent-offset 2) (setq js2-mode-hook '(lambda () (progn (set-variable 'js2-basic-offset 2)))) (setq css-mode-hook '(lambda () (progn (set-variable 'css-indent-offset 2)))) (setq typescript-mode-hook '(lambda () (progn (set-variable 'typescript-indent-level 2)))) ;; Maximize the window upon startup (add-to-list 'default-frame-alist '(fullscreen . maximized)) (setq doom-font (font-spec :family "Iosevka" :size 15)) (when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize)) ;; Make Monky communicate via cmdserver (setq monky-process-type 'cmdserver) ;; Map SPC-g-h to monky-status (map! :map magit-mode-map :leader "g h" 'monky-status) ;; Enable Mercurial support for git-gutter (setq git-gutter:handled-backends '(git hg)) ;; Enable editorconfig (editorconfig-mode 1) ;; mu4e config (set-email-account! "posteo" '((mu4e-sent-folder . "/Sent") (mu4e-drafts-folder . "/Drafts") (mu4e-trash-folder . "/Trash") (mu4e-refile-folder . "/INBOX") (smtpmail-smtp-user . "mokou@posteo.net") (user-mail-address . "mokou@posteo.de")) t) (map! :map global-map :leader "M" 'mu4e) ;; use https://github.com/grobian/html2text (setq mu4e-html2text-command "html2text") ;; Switch to rust-analyzer (setq rustic-lsp-server 'rust-analyzer) ;; Org configuration (after! org (add-to-list 'org-modules 'org-habit) (setq-default org-directory "~/code/personal/org") (setq-default org-default-notes-file "~/code/personal/org/capture.org") (setq-default org-todo-keywords (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)") (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" "MEETING")))) (setq-default org-todo-keyword-faces (quote (("TODO" :foreground "tomato" :weight bold) ("NEXT" :foreground "royal blue" :weight bold) ("DONE" :foreground "forest green" :weight bold) ("WAITING" :foreground "chocolate" :weight bold) ("HOLD" :foreground "orchid" :weight bold) ("CANCELLED" :foreground "yellow" :weight bold) ("MEETING" :foreground "gold" :weight bold) ("PHONE" :foreground "orange" :weight bold)))) (setq org-capture-templates (quote (("t" "todo" entry (file "~/code/personal/org/capture.org") "* TODO %?\n%U\n" :clock-in t :clock-resume t) ("r" "respond" entry (file "~/code/personal/org/capture.org") "* STRT Respond to %:from on %:subject\nSCHEDULED: %t\n%U\n%a\n" :clock-in t :clock-resume t :immediate-finish t) ("n" "note" entry (file "~/code/personal/org/capture.org") "* %? :NOTE:\n%U\n%a\n" :clock-in t :clock-resume t) ("w" "org-protocol" entry (file "~/code/personal/org/capture.org") "* TODO Review %c\n%U\n" :immediate-finish t) ("m" "meeting" entry (file "~/code/personal/org/capture.org") "* PROJ Meeting with %? :MEETING:\n%U" :clock-in t :clock-resume t)))) (setq org-refile-targets (quote ((nil :maxlevel . 9) (org-agenda-files :maxlevel . 9)))) (add-to-list 'ivy-completing-read-handlers-alist '(org-capture-refile . completing-read-default)) (org-clock-persistence-insinuate) (setq org-clock-in-switch-to-state 'cf/clock-in-to-next) (setq org-drawers (quote ("PROPERTIES" "LOGBOOK"))) (setq org-clock-into-drawer t) (setq org-clock-out-remove-zero-time-clocks t) (setq org-clock-out-when-done t) (setq org-clock-persist t) (setq org-clock-auto-clock-resolution (quote when-no-clock-is-running)) (setq org-clock-report-include-clocking-task t) (setq org-agenda-dim-blocked-tasks nil) (setq org-agenda-compact-blocks t) (setq org-agenda-custom-commands (quote (("N" "Notes" tags "NOTE" ((org-agenda-overriding-header "Notes") (org-tags-match-list-sublevels t))) (" " "Agenda" ((agenda "" nil) (tags "REFILE" ((org-agenda-overriding-header "Tasks to Refile") (org-tags-match-list-sublevels nil))) (tags-todo "-CANCELLED/!" ((org-agenda-overriding-header "Stuck Projects") (org-agenda-skip-function 'cf/skip-non-stuck-projects) (org-agenda-sorting-strategy '(category-keep)))) (tags-todo "-HOLD-CANCELLED/!" ((org-agenda-overriding-header "Projects") (org-agenda-skip-function 'cf/skip-non-projects) (org-tags-match-list-sublevels 'indented) (org-agenda-sorting-strategy '(category-keep)))) (tags-todo "-CANCELLED/!NEXT" ((org-agenda-overriding-header (concat "Project Next Tasks" (if cf/hide-scheduled-and-waiting-next-tasks "" " (including WAITING and SCHEDULED tasks)"))) (org-agenda-skip-function 'cf/skip-projects-and-habits-and-single-tasks) (org-tags-match-list-sublevels t) (org-agenda-todo-ignore-scheduled cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-todo-ignore-deadlines cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-todo-ignore-with-date cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-sorting-strategy '(todo-state-down effort-up category-keep)))) (tags-todo "-REFILE-CANCELLED-WAITING-HOLD/!" ((org-agenda-overriding-header (concat "Project Subtasks" (if cf/hide-scheduled-and-waiting-next-tasks "" " (including WAITING and SCHEDULED tasks)"))) (org-agenda-skip-function 'cf/skip-non-project-tasks) (org-agenda-todo-ignore-scheduled cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-todo-ignore-deadlines cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-todo-ignore-with-date cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-sorting-strategy '(category-keep)))) (tags-todo "-REFILE-CANCELLED-WAITING-HOLD/!" ((org-agenda-overriding-header (concat "Standalone Tasks" (if cf/hide-scheduled-and-waiting-next-tasks "" " (including WAITING and SCHEDULED tasks)"))) (org-agenda-skip-function 'cf/skip-project-tasks) (org-agenda-todo-ignore-scheduled cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-todo-ignore-deadlines cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-todo-ignore-with-date cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-sorting-strategy '(category-keep)))) (tags-todo "-CANCELLED+WAITING|HOLD/!" ((org-agenda-overriding-header (concat "Waiting and Postponed Tasks" (if cf/hide-scheduled-and-waiting-next-tasks "" " (including WAITING and SCHEDULED tasks)"))) (org-agenda-skip-function 'cf/skip-non-tasks) (org-tags-match-list-sublevels nil) (org-agenda-todo-ignore-scheduled cf/hide-scheduled-and-waiting-next-tasks) (org-agenda-todo-ignore-deadlines cf/hide-scheduled-and-waiting-next-tasks))) (tags "-REFILE/" ((org-agenda-overriding-header "Tasks to Archive") (org-agenda-skip-function 'cf/skip-non-archivable-tasks) (org-tags-match-list-sublevels nil)))) nil))))) (map! :map global-map :leader "a" 'org-agenda) (defun cf/clock-in-to-next (kw) (when (not (and (boundp 'org-capture-mode) org-capture-mode)) (cond ((and (member (org-get-todo-state) (list "TODO")) (cf/is-task-p)) "NEXT") ((and (member (org-get-todo-state) (list "NEXT")) (cf/is-project-p)) "TODO")))) (defun cf/find-project-task () (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 cf/is-project-p () (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 cf/is-task-p () (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 cf/is-project-subtree-p () (let ((task (save-excursion (org-back-to-heading 'invisible-ok) (point)))) (save-excursion (cf/find-project-task) (if (equal (point) task) nil t)))) (defun cf/is-subproject-p () (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 cf/list-sublevels-for-projects-indented () (if (marker-buffer org-agenda-restrict-begin) (setq org-tags-match-list-sublevels 'indented) (setq org-tags-match-list-sublevels nil)) nil) (defun cf/list-sublevels-for-projects () (if (marker-buffer org-agenda-restrict-begin) (setq org-tags-match-list-sublevels t) (setq org-tags-match-list-sublevels nil)) nil) (defvar cf/hide-scheduled-and-waiting-next-tasks t) (defun cf/toggle-next-task-display () (interactive) (setq cf/hide-scheduled-and-waiting-next-tasks (not cf/hide-scheduled-and-waiting-next-tasks)) (when (equal major-mode 'org-agenda-mode) (org-agenda-redo)) (message "%s WAITING and SCHEDULED NEXT Tasks" (if cf/hide-scheduled-and-waiting-next-tasks "Hide" "Show"))) (defun cf/skip-stuck-projects () (save-restriction (widen) (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) (if (cf/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 cf/skip-non-stuck-projects () ;; (cf/list-sublevels-for-projects-indented) (save-restriction (widen) (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) (if (cf/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 cf/skip-non-projects () ;; (cf/list-sublevels-for-projects-indented) (if (save-excursion (cf/skip-non-stuck-projects)) (save-restriction (widen) (let ((subtree-end (save-excursion (org-end-of-subtree t)))) (cond ((cf/is-project-p) nil) ((and (cf/is-project-subtree-p) (not (cf/is-task-p))) nil) (t subtree-end)))) (save-excursion (org-end-of-subtree t)))) (defun cf/skip-non-tasks () (save-restriction (widen) (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) (cond ((cf/is-task-p) nil) (t next-headline))))) (defun cf/skip-project-trees-and-habits () (save-restriction (widen) (let ((subtree-end (save-excursion (org-end-of-subtree t)))) (cond ((cf/is-project-p) subtree-end) ((org-is-habit-p) subtree-end) (t nil))))) (defun cf/skip-projects-and-habits-and-single-tasks () (save-restriction (widen) (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) (cond ((org-is-habit-p) next-headline) ((and cf/hide-scheduled-and-waiting-next-tasks (member "WAITING" (org-get-tags-at))) next-headline) ((cf/is-project-p) next-headline) ((and (cf/is-task-p) (not (cf/is-project-subtree-p))) next-headline) (t nil))))) (defun cf/skip-project-tasks-maybe () (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 ((cf/is-project-p) next-headline) ((org-is-habit-p) subtree-end) ((and (not limit-to-project) (cf/is-project-subtree-p)) subtree-end) ((and limit-to-project (cf/is-project-subtree-p) (member (org-get-todo-state) (list "NEXT"))) subtree-end) (t nil))))) (defun cf/skip-project-tasks () (save-restriction (widen) (let* ((subtree-end (save-excursion (org-end-of-subtree t)))) (cond ((cf/is-project-p) subtree-end) ((org-is-habit-p) subtree-end) ((cf/is-project-subtree-p) subtree-end) (t nil))))) (defun cf/skip-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 ((cf/is-project-p) next-headline) ((org-is-habit-p) subtree-end) ((and (cf/is-project-subtree-p) (member (org-get-todo-state) (list "NEXT"))) subtree-end) ((not (cf/is-project-subtree-p)) subtree-end) (t nil))))) (defun cf/skip-projects-and-habits () (save-restriction (widen) (let ((subtree-end (save-excursion (org-end-of-subtree t)))) (cond ((cf/is-project-p) subtree-end) ((org-is-habit-p) subtree-end) (t nil))))) (defun cf/skip-non-subprojects () (let ((next-headline (save-excursion (outline-next-heading)))) (if (cf/is-subproject-p) nil next-headline))) (defun cf/skip-non-archivable-tasks () (save-restriction (widen) ;; Consider only tasks with done todo headings as archivable candidates (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))) (subtree-end (save-excursion (org-end-of-subtree t)))) (if (member (org-get-todo-state) org-todo-keywords-1) (if (member (org-get-todo-state) org-done-keywords) (let* ((daynr (string-to-number (format-time-string "%d" (current-time)))) (a-month-ago (* 60 60 24 (+ daynr 1))) (last-month (format-time-string "%Y-%m-" (time-subtract (current-time) (seconds-to-time a-month-ago)))) (this-month (format-time-string "%Y-%m-" (current-time))) (subtree-is-current (save-excursion (forward-line 1) (and (< (point) subtree-end) (re-search-forward (concat last-month "\\|" this-month) subtree-end t))))) (if subtree-is-current subtree-end ; Has a date in this month or last month, skip it nil)) ; available to archive (or subtree-end (point-max))) next-headline))))