1689 lines
56 KiB
Org Mode
1689 lines
56 KiB
Org Mode
work in progress
|
|
|
|
* ui
|
|
** theme
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package spacemacs-theme
|
|
:defer t
|
|
:config
|
|
(setq spacemacs-theme-custom-colors '((lnum . "#64707c"))))
|
|
|
|
(defvar nd/theme 'spacemacs-dark)
|
|
(defvar nd/theme-window-loaded nil)
|
|
(defvar nd/theme-terminal-loaded nil)
|
|
|
|
;; required for emacsclient/daemon setup
|
|
(if (daemonp)
|
|
(add-hook 'after-make-frame-functions
|
|
(lambda (frame)
|
|
(select-frame frame)
|
|
(if (window-system frame)
|
|
(unless nd/theme-window-loaded
|
|
(if nd/theme-terminal-loaded
|
|
(enable-theme nd/theme)
|
|
(load-theme nd/theme t))
|
|
(setq nd/theme-window-loaded t))
|
|
(unless nd/theme-terminal-loaded
|
|
(if nd/theme-window-loaded
|
|
(enable-theme nd/theme)
|
|
(load-theme nd/theme t))
|
|
(setq nd/theme-terminal-loaded t)))))
|
|
(progn
|
|
(load-theme nd/theme t)
|
|
(if (display-graphic-p)
|
|
(setq nd/theme-window-loaded t)
|
|
(setq nd/theme-terminal-loaded t))))
|
|
#+END_SRC
|
|
** modeline
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package spaceline
|
|
:ensure t
|
|
:config
|
|
(require 'spaceline-config)
|
|
(setq powerline-default-separator 'arrow
|
|
spaceline-buffer-size-p nil)
|
|
(spaceline-spacemacs-theme))
|
|
|
|
(line-number-mode 1)
|
|
(column-number-mode 1)
|
|
#+END_SRC
|
|
*** delight
|
|
Used to hide minor modes on the modeline
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package delight
|
|
:ensure t)
|
|
#+END_SRC
|
|
** clean the interface
|
|
No need for startup screen, tool/menu/scrollbars, or backups
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq inhibit-startup-screen t)
|
|
|
|
(tool-bar-mode -1)
|
|
(menu-bar-mode -1)
|
|
(scroll-bar-mode -1)
|
|
|
|
(setq make-backup-files nil)
|
|
(setq auto-save-default nil)
|
|
#+END_SRC
|
|
** other enhancements
|
|
*** popup windows
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq pop-up-windows nil) ; no popups (eg ediff)
|
|
#+END_SRC
|
|
*** line wrap
|
|
#+BEGIN_SRC emacs-lisp
|
|
(set-default 'truncate-lines t)
|
|
#+END_SRC
|
|
*** tab width
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq-default tab-width 4)
|
|
#+END_SRC
|
|
*** smooth scrolling
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq scroll-conservatively 100)
|
|
#+END_SRC
|
|
*** imagemagick
|
|
#+BEGIN_SRC emacs-lisp
|
|
(when (fboundp 'imagemagick-register-types)
|
|
(imagemagick-register-types))
|
|
#+END_SRC
|
|
*** yes-no prompt
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defalias 'yes-or-no-p 'y-or-n-p) ; eliminate yes or no prompt on killing procs
|
|
#+END_SRC
|
|
* packages
|
|
** beacon
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package beacon
|
|
:ensure t
|
|
:delight
|
|
:init
|
|
(beacon-mode 1))
|
|
#+END_SRC
|
|
** whichkey
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package which-key
|
|
:ensure t
|
|
:delight
|
|
:init
|
|
(which-key-mode))
|
|
#+END_SRC
|
|
** helm
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package helm
|
|
:ensure t
|
|
:delight
|
|
:init
|
|
(helm-mode 1)
|
|
:config
|
|
(setq helm-autoresize-max-height 0
|
|
helm-autoresize-max-height 40
|
|
helm-M-x-fuzzy-match t
|
|
helm-buffers-fuzzy-matching t
|
|
helm-recentf-fuzzy-match t
|
|
helm-semantic-fuzzy-match t
|
|
helm-imenu-fuzzy-match t
|
|
helm-scroll-amount 8)
|
|
(add-to-list 'display-buffer-alist
|
|
`(,(rx bos "*helm" (* not-newline) "*" eos)
|
|
(display-buffer-in-side-window)
|
|
(inhibit-same-window . t)
|
|
(window-height . 0.4)))
|
|
(helm-autoresize-mode 1)
|
|
(require 'helm-config))
|
|
#+END_SRC
|
|
** rainbow-delimiters
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package rainbow-delimiters
|
|
:ensure t
|
|
:delight
|
|
:hook
|
|
((prog-mode . rainbow-delimiters-mode)
|
|
(inferior-ess-mode . rainbow-delimiters-mode)
|
|
(ess-mode . rainbow-delimiters-mode)))
|
|
#+END_SRC
|
|
** ace-window
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package ace-window
|
|
:ensure t
|
|
:config
|
|
(setq aw-background t)
|
|
(set-face-attribute 'aw-leading-char-face nil
|
|
:foreground "#292b2e"
|
|
:background "#bc6ec5"
|
|
:height 1.0
|
|
:box nil))
|
|
#+END_SRC
|
|
** avy
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package avy
|
|
:ensure t
|
|
:config
|
|
(setq avy-background t))
|
|
#+END_SRC
|
|
** sudo edit
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package sudo-edit
|
|
:ensure t)
|
|
#+END_SRC
|
|
** undo tree
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package undo-tree
|
|
:ensure t
|
|
:delight
|
|
:config
|
|
(setq undo-tree-visualizer-diff t)
|
|
(global-undo-tree-mode))
|
|
#+END_SRC
|
|
** fill-column-indicator
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package fill-column-indicator
|
|
:ensure t
|
|
:config
|
|
(setq fci-rule-use-dashes t)
|
|
:hook
|
|
(prog-mode . fci-mode))
|
|
#+END_SRC
|
|
** rainbow
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package rainbow-mode
|
|
:ensure t)
|
|
#+END_SRC
|
|
* library
|
|
A place for duct tape code that I developed (or lovingly stole from others)
|
|
** macros
|
|
#+BEGIN_SRC emacs-lisp
|
|
;; lovingly stolen from aaron harris
|
|
(defmacro nd/with-advice (adlist &rest body)
|
|
"Execute BODY with temporary advice in ADLIST.
|
|
|
|
Each element of ADLIST should be a list of the form
|
|
(SYMBOL WHERE FUNCTION [PROPS])
|
|
suitable for passing to `advice-add'. The BODY is wrapped in an
|
|
`unwind-protect' form, so the advice will be removed even in the
|
|
event of an error or nonlocal exit."
|
|
(declare (debug ((&rest (&rest form)) body))
|
|
(indent 1))
|
|
`(progn
|
|
,@(mapcar (lambda (adform)
|
|
(cons 'advice-add adform))
|
|
adlist)
|
|
(unwind-protect (progn ,@body)
|
|
,@(mapcar (lambda (adform)
|
|
`(advice-remove ,(car adform) ,(nth 2 adform)))
|
|
adlist))))
|
|
#+END_SRC
|
|
** functions
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/filter-list-prefix (prefix str-list)
|
|
"Return a subset of tags-list whose first character matches prefix.
|
|
tags-list defaults to org-tag-alist if not given."
|
|
(seq-filter (lambda (i)
|
|
(and (stringp i)
|
|
(string-prefix-p prefix i)))
|
|
str-list))
|
|
|
|
(defun nd/move-key (keymap-from keymap-to key)
|
|
"Move KEY from KEYMAP-FROM keymap to KEYMAP-TO keymap."
|
|
(define-key keymap-to key (lookup-key keymap-from key))
|
|
(define-key keymap-from key nil))
|
|
#+END_SRC
|
|
** interactive
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/split-and-follow-horizontally ()
|
|
"Split window horizontally and move focus."
|
|
(interactive)
|
|
(split-window-below)
|
|
(balance-windows)
|
|
(other-window 1))
|
|
|
|
(defun nd/split-and-follow-vertically ()
|
|
"Split window vertically and move focus."
|
|
(interactive)
|
|
(split-window-right)
|
|
(balance-windows)
|
|
(other-window 1))
|
|
|
|
(defun nd/switch-to-previous-buffer ()
|
|
"Switch the buffer to the last opened buffer."
|
|
(interactive)
|
|
(switch-to-buffer (other-buffer (current-buffer) 1)))
|
|
|
|
(defun nd/config-reload ()
|
|
"Reloads ~/.emacs.d/conf.org at runtime."
|
|
(interactive)
|
|
(org-babel-load-file (expand-file-name "~/.emacs.d/conf.org")))
|
|
|
|
(defun nd/config-visit ()
|
|
"Opens the main conf.org file (the one that really matters)."
|
|
(interactive)
|
|
(find-file "~/.emacs.d/conf.org"))
|
|
|
|
(defun nd/kill-current-buffer ()
|
|
"Kill the current buffer."
|
|
(interactive)
|
|
(kill-buffer (current-buffer)))
|
|
|
|
(defun nd/close-all-buffers ()
|
|
"Kill all buffers without regard for their origin."
|
|
(interactive)
|
|
(mapc 'kill-buffer (buffer-list)))
|
|
#+END_SRC
|
|
* completion
|
|
** company
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package company
|
|
:ensure t
|
|
:config
|
|
(setq company-idle-delay 0
|
|
company-minimum-prefix-length 3))
|
|
#+END_SRC
|
|
** flycheck
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package flycheck
|
|
:ensure t
|
|
:hook
|
|
(prog-mode . flycheck-mode)
|
|
:config
|
|
(setq flycheck-check-syntax-automatically '(save
|
|
idle-change
|
|
mode-enabled)
|
|
flycheck-idle-change-delay 2
|
|
flycheck-error-list-minimum-level 'warning
|
|
flycheck-navigation-minimum-level 'warning))
|
|
#+END_SRC
|
|
** yasnippet
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package yasnippet
|
|
:ensure t)
|
|
|
|
(use-package yasnippet-snippets
|
|
:ensure t
|
|
:after yasnippet
|
|
:hook
|
|
((prog-mode . yas-minor-mode))
|
|
:config
|
|
(yas-reload-all))
|
|
#+END_SRC
|
|
* languages
|
|
** progmode
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'prog-mode-hook #'prettify-symbols-mode)
|
|
#+END_SRC
|
|
** elisp
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'emacs-lisp-mode-hook 'company-mode)
|
|
#+END_SRC
|
|
** ess
|
|
NOTES:
|
|
- ess is not considered part of prog-mode for some reason
|
|
- ess-mode requires a running R process for company to work
|
|
- flycheck requries r-lintr
|
|
#+begin_src emacs-lisp
|
|
(defun nd/init-ess-company ()
|
|
"Set the company modes for ess modes."
|
|
(setq-local company-backends '((company-R-objects company-R-args))))
|
|
|
|
(use-package ess
|
|
:ensure t
|
|
:init
|
|
(load "ess-site")
|
|
:hook
|
|
((ess-mode . flycheck-mode)
|
|
(ess-mode . company-mode)
|
|
(ess-mode . nd/init-ess-company)
|
|
(ess-mode . prettify-symbols-mode)
|
|
(ess-mode . fci-mode)
|
|
|
|
(inferior-ess-mode . company-mode)
|
|
(inferior-ess-mode . nd/init-ess-company)
|
|
(inferior-ess-mode . prettify-symbols-mode))
|
|
:config
|
|
(setq inferior-R-args "--quiet --no-save"
|
|
ess-history-file "session.Rhistory"
|
|
ess-history-directory (substitute-in-file-name "${XDG_CONFIG_HOME}/r/"))
|
|
(add-to-list 'evil-motion-state-modes 'ess-help-mode))
|
|
#+END_SRC
|
|
** python
|
|
#+BEGIN_SRC
|
|
(elpy-enable)
|
|
|
|
;; make python tabs 4 chars
|
|
(add-hook 'python-mode-hook
|
|
(lambda ()
|
|
(setq indent-tabs-mode t)
|
|
(setq tab-width 4)
|
|
(setq python-indent 4)))
|
|
|
|
#+END_SRC
|
|
** haskell
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package haskell-mode
|
|
:ensure t
|
|
:config
|
|
(setq haskell-compile-command "ghc -dynamic -Wall -ferror-spans -fforce-recomp -c %s"
|
|
haskell-interactive-popup-errors nil))
|
|
#+END_SRC
|
|
* magit
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package magit
|
|
:ensure t
|
|
:config
|
|
:delight auto-revert-mode
|
|
(setq magit-push-always-verify nil
|
|
git-commit-summary-max-length 50))
|
|
#+END_SRC
|
|
* org-mode
|
|
** major mode
|
|
*** general config
|
|
Enable some straightforward options:
|
|
- visual-line-mode: wrap text since I like to treat long lines as paragraphs
|
|
- org-indent-mode: indent each level for better visualization
|
|
- enable special behavior for header navigation, killing, and yanking (see these docs for details)
|
|
- logs should go in their own drawer called "LOGBOOK"
|
|
- DONE state should log the time
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package org
|
|
:delight
|
|
;; source of indent-mode required here
|
|
(org-indent-mode nil org-indent)
|
|
(visual-line-mode)
|
|
:hook
|
|
(org-mode . visual-line-mode)
|
|
:config
|
|
(setq org-startup-indented t
|
|
org-directory "~/Org"
|
|
org-modules '(org-habit org-protocol)
|
|
|
|
org-special-ctrl-a/e t
|
|
org-special-ctrl-k t
|
|
org-yank-adjusted-subtrees t
|
|
|
|
org-log-into-drawer "LOGBOOK"
|
|
org-log-done 'time)
|
|
|
|
(require 'org-protocol)
|
|
(run-at-time "00:59" 3600 'org-save-all-org-buffers))
|
|
#+END_SRC
|
|
*** bullets
|
|
These are just so much better to read
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package org-bullets
|
|
:ensure t
|
|
:hook
|
|
(org-mode . org-bullets-mode))
|
|
#+END_SRC
|
|
*** font height
|
|
The fonts in org headings bug me, make them smaller and less invasive
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'org-mode-hook
|
|
(lambda ()
|
|
(let ((heading-height 1.15))
|
|
(set-face-attribute 'org-level-1 nil :weight 'bold :height heading-height)
|
|
(set-face-attribute 'org-level-2 nil :weight 'semi-bold :height heading-height)
|
|
(set-face-attribute 'org-level-3 nil :weight 'normal :height heading-height)
|
|
(set-face-attribute 'org-level-4 nil :weight 'normal :height heading-height)
|
|
(set-face-attribute 'org-level-5 nil :weight 'normal :height heading-height))))
|
|
#+END_SRC
|
|
*** src blocks
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq org-src-window-setup 'current-window
|
|
org-src-fontify-natively t
|
|
org-edit-src-content-indentation 0)
|
|
|
|
(add-to-list 'org-structure-template-alist
|
|
'("el" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC"))
|
|
#+END_SRC
|
|
*** interactive commands
|
|
Some useful additional commands for org buffers
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/mark-subtree-keyword (new-keyword &optional exclude)
|
|
"marks all tasks in a subtree with keyword unless original keyword
|
|
is in the optional argument exclude"
|
|
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
|
|
(if (not (listp exclude))
|
|
(error "exlude must be a list if provided"))
|
|
(save-excursion
|
|
(while (< (point) subtree-end)
|
|
(let ((keyword (nd/is-todoitem-p)))
|
|
(if (and keyword (not (member keyword exclude)))
|
|
(org-todo new-keyword)))
|
|
(outline-next-heading)))))
|
|
|
|
(defun nd/mark-subtree-done ()
|
|
"marks all tasks in subtree as DONE unless they are already canc"
|
|
(interactive)
|
|
(nd/mark-subtree-keyword "DONE" '("CANC")))
|
|
|
|
(defun nd/org-clone-subtree-with-time-shift (n &optional shift)
|
|
"Like `org-clone-subtree-with-time-shift' except it resets checkboxes
|
|
and reverts all todo keywords to TODO"
|
|
(interactive "nNumber of clones to produce: ")
|
|
|
|
(let ((shift (or (org-entry-get nil "TIME_SHIFT" 'selective)
|
|
(read-from-minibuffer
|
|
"Date shift per clone (e.g. +1w, empty to copy unchanged): "))))
|
|
(condition-case err
|
|
(progn
|
|
(save-excursion
|
|
;; clone once and reset
|
|
(org-clone-subtree-with-time-shift 1 shift)
|
|
(org-forward-heading-same-level 1 t)
|
|
(org-reset-checkbox-state-subtree)
|
|
(nd/mark-subtree-keyword "TODO")
|
|
(call-interactively 'nd/org-log-delete)
|
|
(org-cycle)
|
|
;; clone reset tree again if we need more than one clone
|
|
(if (> n 1)
|
|
(let ((additional-trees (- n 1)))
|
|
(org-clone-subtree-with-time-shift additional-trees shift)
|
|
(dotimes (i additional-trees)
|
|
(org-forward-heading-same-level 1 t)
|
|
(org-cycle))))))
|
|
(error (message "%s" (error-message-string err))))))
|
|
|
|
(defun nd/org-log-delete ()
|
|
"Delete logbook drawer of subtree."
|
|
(interactive)
|
|
(save-excursion
|
|
(goto-char (org-log-beginning))
|
|
(when (save-excursion
|
|
(save-match-data
|
|
(beginning-of-line 0)
|
|
(search-forward-regexp org-drawer-regexp)
|
|
(goto-char (match-beginning 1))
|
|
(looking-at "LOGBOOK")))
|
|
(org-mark-element)
|
|
(delete-region (region-beginning) (region-end))
|
|
(org-remove-empty-drawer-at (point)))))
|
|
#+END_SRC
|
|
** column view
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq org-columns-default-format
|
|
"%25ITEM %4TODO %TAGS %5Effort{:} %OWNER(OWN)")
|
|
|
|
(set-face-attribute 'org-column nil :background "#1e2023")
|
|
;; org-columns-summary-types
|
|
#+END_SRC
|
|
** calfw
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package calfw
|
|
:ensure t
|
|
:config
|
|
(setq cfw:fchar-junction ?╋
|
|
cfw:fchar-vertical-line ?┃
|
|
cfw:fchar-horizontal-line ?━
|
|
cfw:fchar-left-junction ?┣
|
|
cfw:fchar-right-junction ?┫
|
|
cfw:fchar-top-junction ?┯
|
|
cfw:fchar-top-left-corner ?┏
|
|
cfw:fchar-top-right-corner ?┓))
|
|
|
|
(use-package calfw-org
|
|
:ensure t
|
|
:after calfw
|
|
:config
|
|
(setq cfw:org-agenda-schedule-args
|
|
'(:deadline :timestamp)))
|
|
#+END_SRC
|
|
** window splitting
|
|
Org mode is great and all, but the windows never show up in the right place. The solutions here are simple, but have the downside that the window sizing must be changed when tags/capture templates/todo items are changed. This is because the buffer size is not known at window creation time and I didn't feel like making a function to predict it
|
|
*** todo selection
|
|
I only need a teeny tiny window below my current window for todo selection
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/org-todo-position (buffer alist)
|
|
(let ((win (car (cl-delete-if-not
|
|
(lambda (window)
|
|
(with-current-buffer (window-buffer window)
|
|
(memq major-mode
|
|
'(org-mode org-agenda-mode))))
|
|
(window-list)))))
|
|
(when win
|
|
(let ((new (split-window win -4 'below)))
|
|
(set-window-buffer new buffer)
|
|
new))))
|
|
|
|
(defun nd/org-todo-window-advice (orig-fn)
|
|
"Advice to fix window placement in `org-fast-todo-selection'."
|
|
(let ((override '("\\*Org todo\\*" nd/org-todo-position)))
|
|
(add-to-list 'display-buffer-alist override)
|
|
(nd/with-advice
|
|
((#'org-switch-to-buffer-other-window :override #'pop-to-buffer))
|
|
(unwind-protect (funcall orig-fn)
|
|
(setq display-buffer-alist
|
|
(delete override display-buffer-alist))))))
|
|
|
|
(advice-add #'org-fast-todo-selection :around #'nd/org-todo-window-advice)
|
|
#+END_SRC
|
|
*** tag selection
|
|
By default, the tag selection window obliterates all but the current window...how disorienting :/
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/org-tag-window-advice (orig-fn current inherited table &optional todo-table)
|
|
"Advice to fix window placement in `org-fast-tags-selection'."
|
|
(nd/with-advice
|
|
((#'delete-other-windows :override #'ignore)
|
|
;; pretty sure I just got lucky here...
|
|
(#'split-window-vertically :override #'(lambda (&optional size)
|
|
(split-window-below (or size -9)))))
|
|
(unwind-protect (funcall orig-fn current inherited table todo-table))))
|
|
|
|
(advice-add #'org-fast-tag-selection :around #'nd/org-tag-window-advice)
|
|
#+END_SRC
|
|
*** capture
|
|
Capture should show up in the bottom of any currently active buffer
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/org-capture-position (buffer alist)
|
|
(let ((new (split-window (get-buffer-window) -14 'below)))
|
|
(set-window-buffer new buffer)
|
|
new))
|
|
|
|
(defun nd/org-capture-window-advice (orig-fn table title &optional prompt specials)
|
|
"Advice to fix window placement in `org-capture-select-template'."
|
|
(let ((override '("\\*Org Select\\*" nd/org-capture-position)))
|
|
(add-to-list 'display-buffer-alist override)
|
|
(nd/with-advice
|
|
((#'org-switch-to-buffer-other-window :override #'pop-to-buffer))
|
|
(unwind-protect (funcall orig-fn table title prompt specials)
|
|
(setq display-buffer-alist
|
|
(delete override display-buffer-alist))))))
|
|
|
|
(advice-add #'org-mks :around #'nd/org-capture-window-advice)
|
|
#+END_SRC
|
|
** gtd implementation
|
|
*** todo states
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq org-todo-keywords
|
|
'((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
|
|
(sequence "WAIT(w@/!)" "HOLD(h@/!)" "|" "CANC(c@/!)"))
|
|
|
|
org-todo-keyword-faces
|
|
'(("TODO" :foreground "light coral" :weight bold)
|
|
("NEXT" :foreground "khaki" :weight bold)
|
|
("DONE" :foreground "light green" :weight bold)
|
|
("WAIT" :foreground "orange" :weight bold)
|
|
("HOLD" :foreground "violet" :weight bold)
|
|
("CANC" :foreground "deep sky blue" :weight bold)))
|
|
#+END_SRC
|
|
*** tags
|
|
I use tags for agenda filtering. Very fast and simple. Each tag here starts with a symbol to define its group. Some groups are mutually exclusive, and each group has a different color. Any tag that is not part of these groups (eg some filetags in the few cases I use those) is easy to distinguish as it has the default tag color and is all caps.
|
|
|
|
There are several types of tags I use:
|
|
- location: a GTD context; these start with "@"
|
|
- tools: also a GTD context; these start with "#"
|
|
- attribute: useful flags for filtering; these start with "%"
|
|
- life areas: key areas of life which define priorities and goals; these start with "_"
|
|
|
|
NOTE: only these special chars; others make the tag chooser do weird things with the hotkey
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/add-tag-face (fg-name prefix)
|
|
"Adds list of cons cells to org-tag-faces with foreground set to fg-name.
|
|
Start and end specify the positions in org-tag-alist which define the tags
|
|
to which the faces are applied"
|
|
(dolist (tag (nd/filter-list-prefix prefix (mapcar #'car org-tag-alist)))
|
|
(push `(,tag . (:foreground ,fg-name)) org-tag-faces)))
|
|
|
|
(setq org-tag-alist
|
|
'((:startgroup)
|
|
("@errand" . ?e)
|
|
("@home" . ?h)
|
|
("@work" . ?w)
|
|
("@travel" . ?r)
|
|
(:endgroup)
|
|
|
|
("#laptop" . ?l)
|
|
("#tcult" . ?t)
|
|
("#phone" . ?p)
|
|
|
|
("%note" . ?n)
|
|
("%inc" . ?i)
|
|
("%subdiv" . ?s)
|
|
|
|
(:startgroup)
|
|
("_env" . ?E)
|
|
("_fin" . ?F)
|
|
("_int" . ?I)
|
|
("_met" . ?M)
|
|
("_phy" . ?H)
|
|
("_pro" . ?P)
|
|
("_rec" . ?R)
|
|
("_soc" . ?S)
|
|
(:endgroup)))
|
|
|
|
(setq org-tag-faces '())
|
|
|
|
(nd/add-tag-face "PaleGreen" "@")
|
|
(nd/add-tag-face "SkyBlue" "#")
|
|
(nd/add-tag-face "PaleGoldenrod" "%")
|
|
(nd/add-tag-face "violet" "_")
|
|
#+END_SRC
|
|
*** properties
|
|
Add some useful properties:
|
|
- PARENT_TYPE: used for special groups of timestamped entries (iterators and periodicals).
|
|
- TIME_SHIFT: usually in conjunction with PARENT_TYPE, used as a shorthand when cloning subtrees to shift the time by a specified amount
|
|
- OWNER and GOAL: not currently used
|
|
#+BEGIN_SRC emacs-lisp
|
|
(mapc (lambda (i) (add-to-list 'org-default-properties i))
|
|
'("PARENT_TYPE" "OWNER" "GOAL" "TIME_SHIFT"))
|
|
|
|
(setq org-global-properties
|
|
'(("PARENT_TYPE_ALL" . "periodical iterator")
|
|
("Effort_ALL" . "0:05 0:15 0:30 1:00 1:30 2:00 3:00 4:00 5:00 6:00"))
|
|
|
|
org-use-property-inheritance
|
|
'("PARENT_TYPE" "TIME_SHIFT"))
|
|
#+END_SRC
|
|
*** capture
|
|
#+BEGIN_SRC emacs-lisp
|
|
(let ((capfile "~/Org/capture.org"))
|
|
(setq org-capture-templates
|
|
`(("t" "todo" entry (file ,capfile)
|
|
"* TODO %?\ndeliverable: \n%U\n")
|
|
|
|
("n" "note" entry (file ,capfile)
|
|
"* %? :\\%note:\n%U\n")
|
|
|
|
("a" "appointment" entry (file ,capfile)
|
|
"* %?\n%U\n%^t\n")
|
|
|
|
("m" "multi-day" entry (file ,capfile)
|
|
"* TODO %?\n%U\n%^t--%^t\n")
|
|
|
|
("d" "deadline" entry (file ,capfile)
|
|
"* TODO %?\nDEADLINE: %^t\ndeliverable:\n%U\n")
|
|
|
|
("p" "org-protocol" entry (file ,capfile)
|
|
"* %^{Title} :\\%note:\n%u\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE"
|
|
:immediate-finish t)
|
|
|
|
("L" "org-protocol link" entry (file ,capfile)
|
|
"* %^{Title} :\\%note:\n[[%:link][%:description]]\n%U"
|
|
:immediate-finish t))))
|
|
#+END_SRC
|
|
*** refile
|
|
**** general
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq org-refile-targets '((nil :maxlevel . 9)
|
|
("~/Org/reference/idea.org" :maxlevel . 9)
|
|
(org-agenda-files :maxlevel . 9))
|
|
org-refile-use-outline-path t
|
|
org-outline-path-complete-in-steps nil
|
|
org-refile-allow-creating-parent-nodes 'confirm
|
|
org-indirect-buffer-display 'current-window)
|
|
#+END_SRC
|
|
**** exclude done states
|
|
No need to file under DONE or CANC states
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq org-refile-target-verify-function
|
|
(lambda () (not (member (nth 2 (org-heading-components)) org-done-keywords))))
|
|
#+END_SRC
|
|
*** clocking
|
|
#+BEGIN_SRC emacs-lisp
|
|
|
|
#+END_SRC
|
|
*** agenda
|
|
**** general config
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq org-agenda-files '("~/Org"
|
|
"~/Org/projects"
|
|
"~/Org/reference")
|
|
org-agenda-dim-blocked-tasks nil
|
|
org-agenda-compact-blocks t
|
|
org-agenda-window-setup 'current-window
|
|
|
|
org-habit-graph-column 50
|
|
|
|
org-agenda-start-on-weekday 0
|
|
org-agenda-span 'day
|
|
org-agenda-current-time-string "### -- NOW -- ###"
|
|
org-agenda-time-grid '((daily today remove-match)
|
|
(0800 1000 1200 1200 1400 1600)
|
|
"......" "-----------------"))
|
|
#+END_SRC
|
|
**** right align tags
|
|
the agenda does not do this by default...it's annoying
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'org-finalize-agenda-hook
|
|
(lambda () (setq org-agenda-tags-column (- 4 (window-width)))
|
|
(org-agenda-align-tags)))
|
|
#+END_SRC
|
|
**** holidays and birthdays
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq holiday-bahai-holidays nil
|
|
holiday-hebrew-holidays nil
|
|
holiday-oriental-holidays nil
|
|
holiday-islamic-holidays nil)
|
|
#+END_SRC
|
|
**** library
|
|
Since I am never totally satisfied with how the agenda works, I have a massive library of functions to filter and manipulate tasks (mostly for skip functions). I also have a few variables I set in order to toggle certain views
|
|
***** task helper functions
|
|
These are the building blocks for skip functions.
|
|
****** timestamps
|
|
Each of these returns the timestamp if found.
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/get-date-property (date-property)
|
|
"Helper function to get the date property and convert to a number.
|
|
If it does not have a date, it will return nil."
|
|
(let ((timestamp (org-entry-get nil date-property)))
|
|
(if timestamp (float-time (date-to-time timestamp)))))
|
|
|
|
(defun nd/heading-compare-timestamp (timestamp-fun
|
|
&optional ref-time future)
|
|
"helper function that returns the timestamp (returned by
|
|
timestamp-fun on the current header) if timestamp is futher back in
|
|
time compared to a ref-time (default to 0 which is now, where negative
|
|
is past an positive is future). If the future flag is set, returns
|
|
timestamp if it is in the future compared to ref-time. Returns nil if
|
|
no timestamp is found."
|
|
(let* ((timestamp (funcall timestamp-fun))
|
|
(ref-time (or ref-time 0)))
|
|
(if (and timestamp
|
|
(if future
|
|
(> (- timestamp (float-time)) ref-time)
|
|
(<= (- timestamp (float-time)) ref-time)))
|
|
timestamp)))
|
|
|
|
(defun nd/is-timestamped-heading-p ()
|
|
(nd/get-date-property "TIMESTAMP"))
|
|
|
|
(defun nd/is-scheduled-heading-p ()
|
|
(nd/get-date-property "SCHEDULED"))
|
|
|
|
(defun nd/is-deadlined-heading-p ()
|
|
(nd/get-date-property "DEADLINE"))
|
|
|
|
(defun nd/is-closed-heading-p ()
|
|
(nd/get-date-property "CLOSED"))
|
|
|
|
(defun nd/is-stale-heading-p ()
|
|
(nd/heading-compare-timestamp
|
|
(lambda () (let ((ts (org-entry-get nil "TIMESTAMP")))
|
|
(if (and ts (not (find ?+ ts)))
|
|
(float-time (date-to-time ts)))))))
|
|
|
|
(defun nd/is-fresh-heading-p ()
|
|
(nd/heading-compare-timestamp 'nd/is-timestamped-heading-p nil t))
|
|
|
|
(defvar nd/archive-delay-days 30
|
|
"the number of days to wait before tasks show up in the archive view")
|
|
|
|
(defun nd/is-archivable-heading-p ()
|
|
(nd/heading-compare-timestamp
|
|
'nd/is-closed-heading-p
|
|
(- (* 60 60 24 nd/archive-delay-days))))
|
|
#+END_SRC
|
|
****** task level testing
|
|
Each of these returns the keyword if true
|
|
Doubles as a way to further test the todostate in downstream functions
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/is-todoitem-p ()
|
|
(let ((keyword (nth 2 (org-heading-components))))
|
|
(if (member keyword org-todo-keywords-1)
|
|
keyword)))
|
|
|
|
(defun nd/is-project-p ()
|
|
(and (nd/heading-has-children 'nd/is-todoitem-p) (nd/is-todoitem-p)))
|
|
|
|
(defun nd/is-task-p ()
|
|
(and (not (nd/heading-has-children 'nd/is-todoitem-p)) (nd/is-todoitem-p)))
|
|
|
|
(defun nd/is-project-task-p ()
|
|
(and (nd/heading-has-parent 'nd/is-todoitem-p) (nd/is-task-p)))
|
|
|
|
(defun nd/is-atomic-task-p ()
|
|
(and (not (nd/heading-has-parent 'nd/is-todoitem-p)) (nd/is-task-p)))
|
|
#+END_SRC
|
|
****** property testing
|
|
Returns t is heading matches a certian set of properties
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/is-periodical-heading-p ()
|
|
(equal "periodical" (org-entry-get nil "PARENT_TYPE" t)))
|
|
|
|
(defun nd/is-iterator-heading-p ()
|
|
(equal "iterator" (org-entry-get nil "PARENT_TYPE" t)))
|
|
|
|
(defun nd/heading-has-effort-p ()
|
|
(org-entry-get nil "Effort"))
|
|
|
|
(defun nd/heading-has-context-p ()
|
|
(let ((tags (org-get-tags-at)))
|
|
(or (> (length (nd/filter-list-prefix "#" tags)) 0)
|
|
(> (length (nd/filter-list-prefix "@" tags)) 0))))
|
|
|
|
(defun nd/heading-has-tag-p (tag)
|
|
(member tag (org-get-tags-at)))
|
|
#+END_SRC
|
|
****** relational testing
|
|
Returns t if heading has certain relationship to other headings
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/heading-has-children (heading-test)
|
|
"returns t if heading has subheadings that return t when assessed with
|
|
heading-test function"
|
|
(let ((subtree-end (save-excursion (org-end-of-subtree t)))
|
|
has-children previous-point)
|
|
(save-excursion
|
|
(setq previous-point (point))
|
|
(outline-next-heading)
|
|
(while (and (not has-children)
|
|
(< previous-point (point) subtree-end))
|
|
(when (funcall heading-test)
|
|
(setq has-children t))
|
|
(setq previous-point (point))
|
|
(org-forward-heading-same-level 1 t)))
|
|
has-children))
|
|
|
|
(defun nd/heading-has-parent (heading-test)
|
|
"returns parent keyword if heading is in the immediate subtree of a heading
|
|
that evaluated to t with heading-test function"
|
|
(save-excursion (and (org-up-heading-safe) (funcall heading-test))))
|
|
|
|
(defun nd/has-discontinuous-parent ()
|
|
"returns t if heading has a parent which is not a
|
|
todoitem which in turn has a parent which is a todoitem"
|
|
(let ((has-todoitem-parent)
|
|
(has-non-todoitem-parent))
|
|
(save-excursion
|
|
(while (and (org-up-heading-safe)
|
|
(not has-todoitem-parent))
|
|
(if (nd/is-todoitem-p)
|
|
(setq has-todoitem-parent t)
|
|
(setq has-non-todoitem-parent t))))
|
|
(and has-todoitem-parent has-non-todoitem-parent)))
|
|
#+END_SRC
|
|
****** project level testing
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defconst nd/project-invalid-todostates
|
|
'("WAIT" "NEXT")
|
|
"projects cannot have these todostates")
|
|
|
|
(defmacro nd/compare-statuscodes (op sc1 sc2 sc-list)
|
|
`(,op (position ,sc1 ,sc-list) (position ,sc2 ,sc-list)))
|
|
|
|
(defun nd/decend-into-project (allowed-statuscodes trans-tbl get-task-status)
|
|
(let ((project-status (first allowed-statuscodes))
|
|
(breaker-status (car (last allowed-statuscodes)))
|
|
(previous-point))
|
|
;; (message "hi")
|
|
(save-excursion
|
|
(setq previous-point (point))
|
|
(outline-next-heading)
|
|
;; loop through subproject tasks until breaker-status found
|
|
(while (and (not (eq project-status breaker-status))
|
|
(> (point) previous-point))
|
|
(let ((keyword (nd/is-todoitem-p)))
|
|
(if keyword
|
|
(let ((new-status
|
|
;; if project then descend recursively
|
|
(if (nd/heading-has-children 'nd/is-todoitem-p)
|
|
(let ((n (nd/get-project-status)))
|
|
;; if project returns an allowed status
|
|
;; then use that
|
|
(or (and (member n allowed-statuscodes) n)
|
|
;; otherwise look up the value in the
|
|
;; translation table and return error
|
|
;; if not found
|
|
(nth (or (alist-get n trans-tbl)
|
|
(error (concat "status not found: " n)))
|
|
allowed-statuscodes)))
|
|
;; if not project then use user-defined function
|
|
;; to obtain status of task
|
|
(nth (funcall get-task-status keyword)
|
|
allowed-statuscodes))))
|
|
;; (message (format "%s" (concat "new status: " (symbol-name new-status))))
|
|
;; (message (format "%s" (concat "project status: " (symbol-name project-status))))
|
|
;; (message (format "%s" keyword))
|
|
(if (nd/compare-statuscodes > new-status project-status allowed-statuscodes)
|
|
(setq project-status new-status)))))
|
|
(setq previous-point (point))
|
|
(org-forward-heading-same-level 1 t)))
|
|
project-status))
|
|
|
|
(defun nd/get-project-status ()
|
|
(let ((keyword (nd/is-todoitem-p)))
|
|
;; these first three are easy because they only require
|
|
;; testing the project headline and nothing underneath
|
|
(cond
|
|
((nd/is-scheduled-heading-p) :scheduled-project)
|
|
((equal keyword "HOLD") :held)
|
|
((member keyword nd/project-invalid-todostates)
|
|
:invalid-todostate)
|
|
|
|
;; these require descending into the project subtasks
|
|
((equal keyword "CANC")
|
|
(nd/decend-into-project
|
|
'(:archivable :complete)
|
|
'((:stuck . 1)
|
|
(:held . 1)
|
|
(:waiting . 1)
|
|
(:active . 1)
|
|
(:scheduled-project . 1)
|
|
(:invalid-todostate . 1)
|
|
(:undone-complete . 1)
|
|
(:done-incomplete . 1))
|
|
(lambda (k)
|
|
(if (and (member k org-done-keywords)
|
|
(nd/is-archivable-heading-p)) 0 1))))
|
|
|
|
((equal keyword "DONE")
|
|
(nd/decend-into-project
|
|
'(:archivable :complete :done-incomplete)
|
|
'((:stuck . 2)
|
|
(:held . 2)
|
|
(:waiting . 2)
|
|
(:active . 2)
|
|
(:scheduled-project . 2)
|
|
(:invalid-todostate . 2)
|
|
(:undone-complete . 2))
|
|
(lambda (k)
|
|
(if (member k org-done-keywords)
|
|
(if (nd/is-archivable-heading-p) 0 1)
|
|
2))))
|
|
|
|
((equal keyword "TODO")
|
|
(nd/decend-into-project
|
|
'(:undone-complete :stuck :held :waiting :active)
|
|
'((:complete . 0)
|
|
(:archivable . 0)
|
|
(:scheduled-project . 1)
|
|
(:invalid-todostate . 1)
|
|
(:done-incomplete . 1))
|
|
(lambda (k)
|
|
(cond ((equal k "TODO") (if (nd/is-scheduled-heading-p) 4 1))
|
|
((equal k "HOLD") 2)
|
|
((equal k "WAIT") 3)
|
|
((equal k "NEXT") 4)
|
|
(t 0)))))
|
|
|
|
(t (error (concat "invalid keyword detected: " keyword))))))
|
|
#+END_SRC
|
|
****** iterator testing
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defconst nd/iter-future-time (* 7 24 60 60))
|
|
|
|
(defconst nd/iter-statuscodes '(:uninit :empty :active))
|
|
|
|
(defun nd/get-iterator-status ()
|
|
(let ((iter-status :uninit)
|
|
(subtree-end (save-excursion (org-end-of-subtree t))))
|
|
(save-excursion
|
|
(setq previous-point (point))
|
|
(outline-next-heading)
|
|
(while (and (not (eq iter-status :active))
|
|
(< (point) subtree-end))
|
|
(let ((keyword (nd/is-atomic-task-p))
|
|
(new-status))
|
|
(if keyword
|
|
(progn
|
|
(setq new-status (if (nd/heading-compare-timestamp
|
|
(lambda ()
|
|
(or (nd/is-scheduled-heading-p)
|
|
(nd/is-deadlined-heading-p)))
|
|
nd/iter-future-time t)
|
|
:active
|
|
:empty))
|
|
(if (nd/compare-statuscodes > new-status iter-status nd/iter-statuscodes)
|
|
(setq iter-status new-status)))))
|
|
(outline-next-heading)))
|
|
iter-status))
|
|
#+END_SRC
|
|
****** periodical testing
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defconst nd/peri-future-time nd/iter-future-time)
|
|
|
|
(defconst nd/peri-statuscodes '(:uninit :stale :fresh))
|
|
|
|
(defun nd/get-periodical-status ()
|
|
(let ((peri-status :uninit)
|
|
(subtree-end (save-excursion (org-end-of-subtree t))))
|
|
(save-excursion
|
|
(setq previous-point (point))
|
|
(outline-next-heading)
|
|
(while (and (not (eq peri-status :fresh))
|
|
(< (point) subtree-end))
|
|
(if (and (nd/is-periodical-heading-p)
|
|
(not (nd/heading-has-children 'nd/is-periodical-heading-p)))
|
|
(let ((new-status
|
|
(if (nd/heading-compare-timestamp
|
|
'nd/is-timestamped-heading-p
|
|
nd/iter-future-time t)
|
|
:fresh
|
|
:stale)))
|
|
(if (nd/compare-statuscodes > new-status peri-status nd/peri-statuscodes)
|
|
(setq peri-status new-status))))
|
|
(outline-next-heading)))
|
|
peri-status))
|
|
#+END_SRC
|
|
***** skip functions
|
|
These are the primary means we use to sort through tasks. Note that we could do this with
|
|
tags in the custom commands section but I find this easier to maintain and possibly faster.
|
|
****** helper skip functions and macros
|
|
Subunits for skip functions. Not meant to be used or called from the custom commands api
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/skip-heading ()
|
|
(save-excursion (or (outline-next-heading) (point-max))))
|
|
|
|
(defun nd/skip-subtree ()
|
|
(save-excursion (or (org-end-of-subtree t) (point-max))))
|
|
|
|
(defconst nd/project-skip-todostates
|
|
'("HOLD" "CANC")
|
|
"These keywords override all contents within their subtrees.
|
|
Currently used to tell skip functions when they can hop over
|
|
entire subtrees to save time and ignore tasks")
|
|
|
|
(defmacro nd/skip-heading-with (heading-fun test-fun)
|
|
"Skips headings accoring to certain characteristics. heading-fun
|
|
is a function that tests the heading and returns the todoitem keyword
|
|
on success. Test-fun is a function that further tests the identity of
|
|
the heading and may or may not use the keyword output supplied by
|
|
the heading-fun. This function will not skip if heading-fun and
|
|
test-fun return true"
|
|
`(save-restriction
|
|
(widen)
|
|
(let ((keyword (,heading-fun)))
|
|
(message keyword)
|
|
(if (not (and keyword ,test-fun))
|
|
(nd/skip-heading)))))
|
|
#+END_SRC
|
|
****** headings
|
|
Skip functions for headings which may or may
|
|
not be todo-items
|
|
|
|
Note in the case of stale headings that
|
|
I only care about those that are not part
|
|
of projects (projects will get taken care
|
|
of when the entire project is finished)
|
|
and those that are not DONE/CANC (as
|
|
those appear in the regular archive
|
|
section)
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/skip-headings-with-tags (pos-tags-list &optional neg-tags-list)
|
|
"Skips headings that have tags in pos-tags-list and also skips
|
|
tags that do not have tags in neg-tags-list"
|
|
(save-restriction
|
|
(widen)
|
|
(let ((header-tags (org-get-tags-at)))
|
|
(if (and (or (not pos-tags-list)
|
|
(intersection pos-tags-list header-tags :test 'equal))
|
|
(not (intersection neg-tags-list header-tags :test 'equal)))
|
|
(nd/skip-heading)))))
|
|
|
|
(defun nd/skip-non-stale-headings ()
|
|
(save-restriction
|
|
(widen)
|
|
(let ((keyword (nd/is-todoitem-p)))
|
|
(if (not
|
|
(and (nd/is-stale-heading-p)
|
|
(not (member keyword org-done-keywords))
|
|
(not (nd/heading-has-children 'nd/is-todoitem-p))
|
|
(not (nd/heading-has-parent 'nd/is-todoitem-p))))
|
|
(nd/skip-heading)))))
|
|
|
|
#+END_SRC
|
|
****** atomic tasks
|
|
By definition these have no parents, so
|
|
I don't need to worry about skipping over projects
|
|
any todo state is valid and we only sort by done/canc
|
|
#+BEGIN_SRC emacs-lisp
|
|
;; NOTE: this assumes that tags-todo will
|
|
;; filter out all done state tasks
|
|
(defun nd/skip-non-atomic-tasks ()
|
|
(save-excursion
|
|
(widen)
|
|
(if (not (nd/is-atomic-task-p))
|
|
(nd/skip-heading))))
|
|
|
|
(defun nd/skip-non-closed-atomic-tasks ()
|
|
(nd/skip-heading-with
|
|
nd/is-atomic-task-p
|
|
(and (member keyword org-done-keywords)
|
|
(not (nd/is-archivable-heading-p)))))
|
|
|
|
(defun nd/skip-non-archivable-atomic-tasks ()
|
|
(nd/skip-heading-with
|
|
nd/is-atomic-task-p
|
|
(and (member keyword org-done-keywords)
|
|
(nd/is-archivable-heading-p))))
|
|
#+END_SRC
|
|
****** periodicals
|
|
These are headers marked with PARENT_TYPE=periodical
|
|
property that have timestamped headers as children
|
|
which in turn may or may not have todo keywords.
|
|
They are to be refilled when all children are stale
|
|
Note that I only care about the parent headers
|
|
as the children should always show up in the agenda
|
|
simply because they have timestamps. Parents can be
|
|
either fresh (at least one child in the future) or
|
|
stale (all children in the past).
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/skip-non-periodical-parent-headers ()
|
|
(save-restriction
|
|
(widen)
|
|
(if (not (and (nd/is-periodical-heading-p)
|
|
(not (nd/heading-has-parent 'nd/is-periodical-heading-p))))
|
|
(nd/skip-heading))))
|
|
|
|
(defun nd/skip-non-periodical-untimestamped ()
|
|
(save-restriction
|
|
(widen)
|
|
(if (not (and (nd/is-periodical-heading-p)
|
|
(not (nd/is-timestamped-heading-p))
|
|
(not (nd/heading-has-children 'nd/is-periodical-heading-p))))
|
|
(nd/skip-heading))))
|
|
#+END_SRC
|
|
****** iterators
|
|
iterators are like projects but have additional status codes based on
|
|
when the iterator will run out
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/skip-non-iterator-parent-headers ()
|
|
(save-restriction
|
|
(widen)
|
|
(if (not (and (nd/is-iterator-heading-p)
|
|
(not (nd/heading-has-parent 'nd/is-iterator-heading-p))))
|
|
(nd/skip-heading))))
|
|
|
|
(defun nd/skip-non-iterator-unscheduled ()
|
|
(nd/skip-heading-with
|
|
nd/is-atomic-task-p
|
|
(not (or (nd/is-scheduled-heading-p)
|
|
(nd/is-deadlined-heading-p)))))
|
|
#+END_SRC
|
|
****** project tasks
|
|
Since these are part of projects I need to assess
|
|
if the parent project is skippable, in which case
|
|
I jump to the next subtree
|
|
Note that I only care about the keyword in these
|
|
cases because I don't archive these, I archive
|
|
their parent projects. The keywords I care about
|
|
are NEXT, WAIT, and HOLD because these are
|
|
definitive project tasks that require/inhibit
|
|
futher action. (TODO = stuck which I take care
|
|
of at the project level, and DONE/CANC = archivable
|
|
which is dealt with similarly)
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/skip-non-project-tasks ()
|
|
(save-restriction
|
|
(widen)
|
|
(let ((keyword (nd/is-todoitem-p)))
|
|
(if keyword
|
|
(if (nd/heading-has-children 'nd/is-todoitem-p)
|
|
(if (member keyword nd/project-skip-todostates)
|
|
(nd/skip-subtree)
|
|
(nd/skip-heading))
|
|
(if (not (nd/heading-has-parent 'nd/is-todoitem-p))
|
|
(nd/skip-heading)))
|
|
(nd/skip-heading)))))
|
|
#+END_SRC
|
|
****** header-level errors
|
|
Some headers are invalid under certain conditions
|
|
which I test here
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/skip-non-discontinuous-project-tasks ()
|
|
(nd/skip-heading-with
|
|
nd/is-todoitem-p
|
|
(nd/has-discontinuous-parent)))
|
|
|
|
(defun nd/skip-non-done-unclosed-todoitems ()
|
|
(nd/skip-heading-with
|
|
nd/is-todoitem-p
|
|
(and (member keyword org-done-keywords)
|
|
(not (nd/is-closed-heading-p)))))
|
|
|
|
(defun nd/skip-non-undone-closed-todoitems ()
|
|
(nd/skip-heading-with
|
|
nd/is-todoitem-p
|
|
(and (not (member keyword org-done-keywords))
|
|
(nd/is-closed-heading-p))))
|
|
|
|
(defun nd/skip-atomic-tasks-with-context ()
|
|
(nd/skip-heading-with
|
|
nd/is-atomic-task-p
|
|
(not (nd/heading-has-context-p))))
|
|
|
|
(defun nd/skip-project-tasks-with-context ()
|
|
(nd/skip-heading-with
|
|
nd/is-project-task-p
|
|
(not (nd/heading-has-context-p))))
|
|
|
|
(defun nd/skip-projects-with-context ()
|
|
(nd/skip-heading-with
|
|
nd/is-project-p
|
|
(not (nd/heading-has-context-p))))
|
|
|
|
(defun nd/skip-tasks-with-effort ()
|
|
(nd/skip-heading-with
|
|
nd/is-task-p
|
|
(not (nd/heading-has-effort-p))))
|
|
#+END_SRC
|
|
****** projects
|
|
Projects are handled quite simply. They have statuscodes
|
|
for which I test, and this can all be handled by one function.
|
|
Note that this is used for "normal" projects as well as iterators
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/skip-non-projects (&optional ignore-toplevel)
|
|
(save-restriction
|
|
(widen)
|
|
(let ((keyword (nd/is-project-p)))
|
|
(if keyword
|
|
(if (and nd/agenda-limit-project-toplevel
|
|
(not ignore-toplevel)
|
|
(nd/heading-has-parent 'nd/is-todoitem-p))
|
|
(nd/skip-subtree))
|
|
(nd/skip-heading)))))
|
|
#+END_SRC
|
|
***** variables
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defvar nd/agenda-limit-project-toplevel t
|
|
"If true, filter projects by all levels or top level only.")
|
|
|
|
(defvar nd/agenda-hide-incubator-tags t
|
|
"If true, don't show incubator headings.")
|
|
#+END_SRC
|
|
***** interactive view functions
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/toggle-project-toplevel-display ()
|
|
"Toggle all project headings and toplevel only headings in project blocks."
|
|
(interactive)
|
|
(setq nd/agenda-limit-project-toplevel (not nd/agenda-limit-project-toplevel))
|
|
(when (equal major-mode 'org-agenda-mode)
|
|
(org-agenda-redo))
|
|
(message "Showing %s project view in agenda"
|
|
(if nd/agenda-limit-project-toplevel "toplevel" "complete")))
|
|
|
|
(defun nd/org-agenda-filter-non-context ()
|
|
"Filter all tasks with context tags."
|
|
(interactive)
|
|
(let* ((tags-list (mapcar #'car org-tag-alist))
|
|
(context-tags (append
|
|
(nd/filter-list-prefix "@" tags-list)
|
|
(nd/filter-list-prefix "#" tags-list))))
|
|
(setq org-agenda-tag-filter
|
|
(mapcar (lambda (tag) (concat "-" tag)) context-tags))
|
|
(org-agenda-filter-apply org-agenda-tag-filter 'tag)))
|
|
#+END_SRC
|
|
**** agenda aesthetics
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq org-agenda-tags-todo-honor-ignore-options t)
|
|
|
|
(setq org-agenda-prefix-format
|
|
'((agenda . " %-12:c %-5:e %?-12t% s")
|
|
(timeline . " % s")
|
|
(todo . " %-12:c")
|
|
(tags . " %-12:c %-5:e ")
|
|
(search . " %-12:c")))
|
|
|
|
(defconst nd/org-agenda-todo-sort-order '("NEXT" "WAIT" "HOLD" "TODO"))
|
|
|
|
(setq org-agenda-cmp-user-defined
|
|
'(lambda (a b)
|
|
(let ((pa (- (length (member
|
|
(get-text-property 1 'todo-state a)
|
|
nd/org-agenda-todo-sort-order))))
|
|
(pb (- (length (member
|
|
(get-text-property 1 'todo-state b)
|
|
nd/org-agenda-todo-sort-order)))))
|
|
(cond ((or (null pa) (null pb)) nil)
|
|
((> pa pb) +1)
|
|
((< pa pb) -1)))))
|
|
#+END_SRC
|
|
**** custom commands
|
|
#+BEGIN_SRC emacs-lisp
|
|
(defun nd/org-agenda-filter-status (filter status-fun a-line)
|
|
"Filter for org-agenda-before-sorting-filter-function intended for
|
|
agenda project views (eg makes the assumption that all entries are
|
|
from projects in the original org buffer)
|
|
|
|
Will go to the original org buffer and determine the project status
|
|
after which it will check if status is in filter. If true, the flag
|
|
string in the prefix is replaced with the status and the status is
|
|
set as a text property for further sorting"
|
|
(let* ((m (get-text-property 1 'org-marker a-line))
|
|
(s (with-current-buffer (marker-buffer m)
|
|
(goto-char m)
|
|
(funcall status-fun))))
|
|
(if (member s filter)
|
|
(org-add-props (replace-regexp-in-string
|
|
"xxxx" (symbol-name s) a-line)
|
|
nil 'project-status s))))
|
|
|
|
(defun nd/org-agenda-sort-prop (prop order a b)
|
|
(let* ((ta (get-text-property 1 prop a))
|
|
(tb (get-text-property 1 prop b))
|
|
(pa (position ta order :test (if (stringp ta) #'equal)))
|
|
(pb (position tb order :test (if (stringp tb) #'equal))))
|
|
(cond ((or (null pa) (null pb)) nil)
|
|
((< pa pb) +1)
|
|
((> pa pb) -1))))
|
|
|
|
(defun nd/agenda-base-header-cmd (match header skip-fun)
|
|
`(tags
|
|
,match
|
|
((org-agenda-overriding-header ,header)
|
|
(org-agenda-skip-function ,skip-fun)
|
|
(org-agenda-sorting-strategy '(category-keep)))))
|
|
|
|
(defun nd/agenda-base-task-cmd (match header skip-fun &optional sort)
|
|
(or sort (setq sort ''(category-keep)))
|
|
`(tags-todo
|
|
,match
|
|
((org-agenda-overriding-header ,header)
|
|
(org-agenda-skip-function ,skip-fun)
|
|
(org-agenda-todo-ignore-with-date t)
|
|
(org-agenda-sorting-strategy ,sort))))
|
|
|
|
(let* ((actionable "-NA-REFILE-%inc")
|
|
(periodical "PARENT_TYPE=\"periodical\"")
|
|
(iterator "PARENT_TYPE=\"iterator\"")
|
|
(habit "STYLE=\"habit\"")
|
|
(task-match (concat actionable "-" periodical "-" habit "/!"))
|
|
(act-no-rep-match (concat actionable "-" periodical "-" iterator "-" habit "/!"))
|
|
(peri-match (concat actionable "+" periodical "-" iterator "-" habit))
|
|
(iter-match (concat actionable "-" periodical "+" iterator "-" habit "/!")))
|
|
|
|
(setq org-agenda-custom-commands
|
|
`(("t"
|
|
"Task View"
|
|
((agenda "" ((org-agenda-skip-function '(nd/skip-headings-with-tags '("%inc" "REFILE")))
|
|
(org-agenda-include-diary t)))
|
|
,(nd/agenda-base-task-cmd act-no-rep-match
|
|
"Project Tasks"
|
|
''nd/skip-non-project-tasks
|
|
''(user-defined-up category-keep))
|
|
,(nd/agenda-base-task-cmd act-no-rep-match
|
|
"Atomic Tasks"
|
|
''nd/skip-non-atomic-tasks)))
|
|
|
|
("p"
|
|
"Project View"
|
|
((tags-todo
|
|
,act-no-rep-match
|
|
((org-agenda-overriding-header
|
|
(concat (and
|
|
nd/agenda-limit-project-toplevel "Toplevel ")
|
|
"Projects"))
|
|
(org-agenda-skip-function '(nd/skip-non-projects))
|
|
(org-agenda-before-sorting-filter-function
|
|
(lambda (l) (nd/org-agenda-filter-status
|
|
'(:scheduled-project :invalid-todostate :undone-complete
|
|
:done-incomplete :stuck :waiting
|
|
:held :active)
|
|
'nd/get-project-status l)))
|
|
(org-agenda-cmp-user-defined
|
|
(lambda (a b) (nd/org-agenda-sort-prop
|
|
'project-status
|
|
'(:scheduled-project :invalid-todostate :undone-complete
|
|
:done-incomplete :stuck :waiting
|
|
:held :active)
|
|
a b)))
|
|
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
|
|
(org-agenda-sorting-strategy '(user-defined-down category-keep))))))
|
|
|
|
("P"
|
|
"Periodical View"
|
|
((tags
|
|
,(concat actionable "-" iterator "+" periodical "-" habit)
|
|
((org-agenda-overriding-header "Periodical Status")
|
|
(org-agenda-skip-function '(nd/skip-non-periodical-parent-headers))
|
|
(org-agenda-before-sorting-filter-function
|
|
(lambda (l) (nd/org-agenda-filter-status
|
|
nd/peri-statuscodes 'nd/get-periodical-status l)))
|
|
(org-agenda-cmp-user-defined
|
|
(lambda (a b) (nd/org-agenda-sort-prop
|
|
'project-status nd/peri-statuscodes a b)))
|
|
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
|
|
(org-agenda-sorting-strategy '(user-defined-down category-keep))))
|
|
,(nd/agenda-base-header-cmd "-NA-REFILE+PARENT_TYPE=\"periodical\""
|
|
"Untimestamped"
|
|
''nd/skip-non-periodical-untimestamped)))
|
|
|
|
("i"
|
|
"Iterator View"
|
|
((tags
|
|
"-NA-REFILE+PARENT_TYPE=\"iterator\""
|
|
((org-agenda-overriding-header "Iterator Status")
|
|
(org-agenda-skip-function '(nd/skip-non-iterator-parent-headers))
|
|
(org-agenda-before-sorting-filter-function
|
|
(lambda (l) (nd/org-agenda-filter-status nd/iter-statuscodes 'nd/get-iterator-status l)))
|
|
(org-agenda-cmp-user-defined
|
|
(lambda (a b) (nd/org-agenda-sort-prop 'project-status nd/iter-statuscodes a b)))
|
|
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
|
|
(org-agenda-sorting-strategy '(user-defined-down category-keep))))
|
|
,(nd/agenda-base-task-cmd "-NA-REFILE+PARENT_TYPE=\"iterator\"/!"
|
|
"Unscheduled or Undeaded"
|
|
''nd/skip-non-iterator-unscheduled)))
|
|
|
|
("I"
|
|
"Incubator View"
|
|
((agenda "" ((org-agenda-span 7)
|
|
(org-agenda-time-grid nil)
|
|
(org-agenda-entry-types '(:deadline :timestamp))))
|
|
,(nd/agenda-base-task-cmd "-NA-REFILE+%inc/!"
|
|
"Incubated Tasks"
|
|
''nd/skip-non-atomic-tasks)
|
|
(tags-todo
|
|
"-NA-REFILE+%inc/!"
|
|
((org-agenda-overriding-header
|
|
(concat (and
|
|
nd/agenda-limit-project-toplevel "Toplevel ")
|
|
"Incubated Projects"))
|
|
(org-agenda-skip-function '(nd/skip-non-projects))
|
|
(org-agenda-before-sorting-filter-function
|
|
(lambda (l) (nd/org-agenda-filter-status
|
|
'(:scheduled-project :invalid-todostate :undone-complete
|
|
:done-incomplete :stuck :waiting
|
|
:held :active)
|
|
'nd/get-project-status l)))
|
|
(org-agenda-cmp-user-defined
|
|
(lambda (a b) (nd/org-agenda-sort-prop
|
|
'project-status
|
|
'(:scheduled-project :invalid-todostate :undone-complete
|
|
:done-incomplete :stuck :waiting
|
|
:active :held)
|
|
a b)))
|
|
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
|
|
(org-agenda-sorting-strategy '(user-defined-down category-keep))))))
|
|
|
|
("r"
|
|
"Refile and Critical Errors"
|
|
((tags "REFILE"
|
|
((org-agenda-overriding-header "Tasks to Refile"))
|
|
(org-tags-match-list-sublevels nil))
|
|
,(nd/agenda-base-task-cmd task-match
|
|
"Discontinous Project"
|
|
''nd/skip-non-discontinuous-project-tasks)
|
|
,(nd/agenda-base-header-cmd task-match
|
|
"Undone Closed"
|
|
''nd/skip-non-undone-closed-todoitems)
|
|
,(nd/agenda-base-header-cmd (concat actionable "-" periodical)
|
|
"Done Unclosed"
|
|
''nd/skip-non-done-unclosed-todoitems)))
|
|
|
|
("A"
|
|
"Archivable Tasks and Projects"
|
|
(,(nd/agenda-base-header-cmd (concat actionable "-" periodical "-" habit)
|
|
"Archivable Atomic Tasks and Iterators"
|
|
''nd/skip-non-archivable-atomic-tasks)
|
|
,(nd/agenda-base-header-cmd (concat actionable "-" habit)
|
|
"Stale Tasks and Periodicals"
|
|
''nd/skip-non-stale-headings)
|
|
(tags-todo
|
|
,(concat actionable "-" periodical "-" iterator "-" habit)
|
|
((org-agenda-overriding-header
|
|
(concat (and nd/agenda-limit-project-toplevel "Toplevel ")
|
|
"Archivable Projects"))
|
|
(org-agenda-skip-function '(nd/skip-non-projects))
|
|
(org-agenda-before-sorting-filter-function
|
|
(lambda (l) (nd/org-agenda-filter-status '(:archivable) 'nd/get-project-status l)))
|
|
(org-agenda-cmp-user-defined
|
|
(lambda (a b) (nd/org-agenda-sort-prop 'project-status '(:archivable) a b)))
|
|
(org-agenda-prefix-format '((tags . " %-12:c %(format \"xxxx: \")")))
|
|
(org-agenda-sorting-strategy '(user-defined-down category-keep)))))))))
|
|
#+END_SRC
|
|
* mu4e
|
|
only for gmail now
|
|
** basic
|
|
#+BEGIN_SRC emacs-lisp
|
|
(require 'mu4e)
|
|
(setq mail-user-agent 'mu4e-user-agent
|
|
mu4e-maildir "/mnt/data/Mail"
|
|
mu4e-drafts-folder "/gmail/drafts"
|
|
mu4e-sent-folder "/gmail/sent"
|
|
mu4e-trash-folder "/gmail/trash"
|
|
|
|
mu4e-attachment-dir "~/Downloads"
|
|
|
|
mu4e-view-show-images t
|
|
mu4e-headers-show-target nil
|
|
|
|
message-kill-buffer-on-exit t
|
|
|
|
mu4e-change-filenames-when-moving t
|
|
|
|
mu4e-confirm-quit nil)
|
|
#+END_SRC
|
|
** smtp
|
|
#+BEGIN_SRC emacs-lisp
|
|
(require 'smtpmail)
|
|
(setq send-mail-function 'smtpmail-send-it
|
|
message-send-mail-function 'smtpmail-send-it
|
|
|
|
user-mail-address "natedwarshuis@gmail.com"
|
|
user-full-name "Nate Dwarshuis"
|
|
|
|
mu4e-sent-messages-behavior 'delete
|
|
|
|
starttls-use-gnutls t
|
|
smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil))
|
|
;; authinfo.gpg for credentials
|
|
smtpmail-default-smtp-server "smtp.gmail.com"
|
|
smtpmail-smtp-service 587
|
|
smtpmail-smtp-server "smtp.gmail.com")
|
|
#+END_SRC
|
|
* shell
|
|
#+begin_src emacs-lisp
|
|
(defvar nd-term-shell "/bin/bash")
|
|
(defadvice ansi-term (before force-bash)
|
|
(interactive (list nd-term-shell)))
|
|
(ad-activate 'ansi-term)
|
|
#+END_SRC
|
|
* ediff
|
|
#+BEGIN_SRC emacs-lisp
|
|
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
|
|
#+END_SRC
|
|
* keybindings
|
|
For the sake of my sanity, all bindings go here.
|
|
** evil
|
|
I like being evil. All package and custom bindings go here.
|
|
*** basic
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package evil
|
|
:ensure t
|
|
:init
|
|
;; this is required to make evil collection work
|
|
(setq evil-want-integration nil)
|
|
:config
|
|
(evil-mode 1)
|
|
;; some keys that should be in emacs-state instead of global
|
|
(nd/move-key global-map evil-emacs-state-map (kbd "C-s"))
|
|
(nd/move-key global-map evil-emacs-state-map (kbd "C-x C-;"))
|
|
(nd/move-key global-map evil-emacs-state-map (kbd "C-x C-l"))
|
|
(nd/move-key global-map evil-emacs-state-map (kbd "C-x C-u"))
|
|
(nd/move-key global-map evil-emacs-state-map (kbd "C-x C-z")))
|
|
#+END_SRC
|
|
*** evil-org
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package evil-org
|
|
:ensure t
|
|
:after (evil org)
|
|
:delight
|
|
:config
|
|
(add-hook 'org-mode-hook 'evil-org-mode)
|
|
(add-hook 'evil-org-mode-hook 'evil-org-set-key-theme)
|
|
|
|
(require 'evil-org-agenda)
|
|
(evil-org-agenda-set-keys)
|
|
;; some of the defaults bug me...
|
|
(evil-define-key 'motion org-agenda-mode-map
|
|
"t" 'nd/toggle-project-toplevel-display
|
|
"D" 'org-agenda-day-view
|
|
"W" 'org-agenda-week-view
|
|
"M" 'org-agenda-month-view
|
|
"Y" 'org-agenda-year-view
|
|
"ct" nil
|
|
"sC" 'nd/org-agenda-filter-non-context
|
|
"e" 'org-agenda-set-effort
|
|
"ce" nil))
|
|
#+END_SRC
|
|
*** evil-magit
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package evil-magit
|
|
:ensure t
|
|
:after (evil magit))
|
|
#+END_SRC
|
|
*** collection
|
|
#+BEGIN_SRC emacs-lisp
|
|
(use-package evil-collection
|
|
:ensure t
|
|
:after evil
|
|
:init
|
|
(setq evil-collection-modes-list '(dired flycheck company which-key
|
|
helm minibuffer mu4e ediff))
|
|
(setq evil-collection-setup-minibuffer t)
|
|
:config
|
|
(evil-collection-init))
|
|
#+END_SRC
|
|
**** dired
|
|
Dired makes new buffers by default. Use =find-alternate-file= to avoid this
|
|
#+BEGIN_SRC emacs-lisp
|
|
(put 'dired-find-alternate-file 'disabled nil)
|
|
|
|
(evil-define-key 'normal dired-mode-map
|
|
"a" 'dired-find-file
|
|
(kbd "<return>") 'dired-find-alternate-file
|
|
"^" (lambda () (interactive) (find-alternate-file "..")))
|
|
#+END_SRC
|
|
** local
|
|
*** org-mode
|
|
#+BEGIN_SRC emacs-lisp
|
|
(add-hook 'org-mode-hook
|
|
(lambda ()
|
|
(local-set-key (kbd "C-c C-x x") 'nd/mark-subtree-done)
|
|
(local-set-key (kbd "C-c C-x c") 'nd/org-clone-subtree-with-time-shift)))
|
|
|
|
(add-hook 'org-agenda-mode-hook
|
|
(lambda ()
|
|
(local-set-key (kbd "C-c C-c") 'org-agenda-set-tags)))
|
|
#+END_SRC
|
|
*** helm
|
|
#+BEGIN_SRC emacs-lisp
|
|
(define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action)
|
|
(define-key helm-map (kbd "C-<tab>") 'helm-select-action)
|
|
#+END_SRC
|
|
** global
|
|
#+BEGIN_SRC emacs-lisp
|
|
(global-set-key (kbd "<f1>") 'org-agenda)
|
|
(global-set-key (kbd "<f2>") 'org-capture)
|
|
(global-set-key (kbd "<f3>") 'cfw:open-org-calendar)
|
|
(global-set-key (kbd "<f12>") 'mu4e)
|
|
(global-set-key (kbd "C-<f12>") 'global-hl-line-mode)
|
|
(global-set-key (kbd "S-<f12>") 'display-line-numbers-mode)
|
|
|
|
(global-set-key (kbd "C-c e") 'nd/config-visit)
|
|
(global-set-key (kbd "C-c h") 'helm-command-prefix)
|
|
(global-set-key (kbd "C-c r") 'nd/config-reload)
|
|
(global-set-key (kbd "C-c s") 'sudo-edit)
|
|
|
|
(global-set-key (kbd "C-x 2") 'nd/split-and-follow-horizontally)
|
|
(global-set-key (kbd "C-x 3") 'nd/split-and-follow-vertically)
|
|
(global-unset-key (kbd "C-x c"))
|
|
(global-set-key (kbd "C-x k") 'nd/kill-current-buffer)
|
|
(global-set-key (kbd "C-x C-f") 'helm-find-files)
|
|
(global-set-key (kbd "C-x C-b") 'helm-buffers-list)
|
|
|
|
(global-set-key (kbd "C-M-s-k") 'nd/close-all-buffers)
|
|
|
|
(global-set-key (kbd "M-b") 'nd/switch-to-previous-buffer)
|
|
(global-set-key (kbd "M-o") 'ace-window)
|
|
(global-set-key (kbd "M-s") 'avy-goto-char)
|
|
(global-set-key (kbd "M-x") 'helm-M-x)
|
|
#+END_SRC
|