2018-03-21 21:59:12 -04:00
work in progress
2018-03-21 21:44:31 -04:00
* ui
** remove garbage
*** startup screen
#+BEGIN_SRC emacs-lisp
(setq inhibit-startup-screen t)
#+END_SRC
*** useless mouse widgets
#+BEGIN_SRC emacs-lisp
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
#+END_SRC
*** line wrap
#+BEGIN_SRC emacs-lisp
(set-default 'truncate-lines t)
#+END_SRC
*** autosave/backup files
#+BEGIN_SRC emacs-lisp
(setq make-backup-files nil)
(setq auto-save-default nil)
#+END_SRC
*** popup windows
#+BEGIN_SRC emacs-lisp
(setq pop-up-windows nil) ; no popups (eg ediff)
#+END_SRC
** pretty stuff
*** enable line/column numbering
#+BEGIN_SRC emacs-lisp
;; (global-linum-mode t)
(line-number-mode 1)
(column-number-mode 1)
#+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
*** pretty symbols
#+BEGIN_SRC emacs-lisp
(when window-system (global-prettify-symbols-mode t))
#+END_SRC
*** highlight current line
#+BEGIN_SRC emacs-lisp
(when window-system (global-hl-line-mode t))
#+END_SRC
** yes-no prompt enhancement
#+BEGIN_SRC emacs-lisp
(defalias 'yes-or-no-p 'y-or-n-p) ; eliminate yes or no prompt on killing procs
#+END_SRC
** theme color selection
need this to:
a) apply the gui theme if gui is loaded as client and
b) ensure that the reloaded theme is only applied to the current frame
NOTE: this only works if we start term after gui, and term has light bg. not big deal for now since I hardly ever use term as client
#+BEGIN_SRC emacs-lisp
(defvar my:theme 'spacemacs-dark)
(defvar my:theme-window-loaded nil)
(defvar my:theme-terminal-loaded nil)
(if (daemonp)
(add-hook 'after-make-frame-functions(lambda (frame)
(select-frame frame)
(if (window-system frame)
(unless my:theme-window-loaded
(if my:theme-terminal-loaded
(enable-theme my:theme)
(load-theme my:theme t))
(setq my:theme-window-loaded t))
(unless my:theme-terminal-loaded
(if my:theme-window-loaded
(enable-theme my:theme)
(load-theme my:theme t))
(setq my:theme-terminal-loaded t)))))
(progn
(load-theme my:theme t)
(if (display-graphic-p)
(setq my:theme-window-loaded t)
(setq my:theme-terminal-loaded t))))
#+END_SRC
* modeline
#+BEGIN_SRC emacs-lisp
(use-package spaceline
:ensure t
:config
(require 'spaceline-config)
(setq powerline-default-separator (quote arrow))
(spaceline-spacemacs-theme)
(setq spaceline-buffer-size-p nil))
#+END_SRC
** dashboard
#+BEGIN_SRC emacs-lisp
(use-package dashboard
:ensure t
:config
(dashboard-setup-startup-hook)
(setq dashboard-items '((recents . 10))))
#+END_SRC
* keybindings
2018-04-08 01:10:01 -04:00
** overrides
2018-03-21 21:44:31 -04:00
#+BEGIN_SRC emacs-lisp
2018-04-08 01:10:01 -04:00
(global-set-key (kbd "C-h a") 'apropos)
#+END_SRC
** modeless bindings
These are commands that should work in any mode. Make the assumption that function keys are pretty much free in any major/minor mode
#+BEGIN_SRC emacs-lisp
(global-set-key (kbd "<f1 >") 'org-agenda)
(global-set-key (kbd "<f2 >") 'org-capture)
(global-set-key (kbd "<f3 >") 'org-iswitchb)
2018-03-21 21:44:31 -04:00
#+END_SRC
* printing
**
* packages
** delight
#+BEGIN_SRC emacs-lisp
(use-package delight
:ensure t)
#+END_SRC
** 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
** ido
#+BEGIN_SRC emacs-lisp
(use-package ido
:ensure t
:bind
("C-x C-b" . 'ido-switch-buffer)
("C-x b" . 'ibuffer)
:config
(ido-mode 1)
(setq ido-everywhere t)
(setq ido-enable-flex-matching t)
(setq ido-max-directory-size 100000)
(setq ido-default-file-method 'selected-window)
(setq ido-default-buffer-method 'selected-window)
(use-package ido-vertical-mode
:ensure t
:init
(ido-vertical-mode 1)
(setq ido-vertical-define-keys 'C-n-and-C-p-only)))
;; (setq ido-file-extensions-order '(".org" ".txt" ".py" ".emacs" ".xml" ".el" ".ini" ".cfg" ".cnf"))
#+END_SRC
** smex
#+BEGIN_SRC emacs-lisp
(use-package smex
:ensure t
:init
(smex-initialize)
:bind
("M-x" . 'smex)
("M-X" . 'smex-major-mode-commands))
#+END_SRC
** rainbow-delimiters
#+BEGIN_SRC emacs-lisp
(use-package rainbow-delimiters
:ensure t
:delight
:init
(add-hook 'prog-mode-hook #'rainbow-delimiters-mode))
#+END_SRC
** ace-window
#+BEGIN_SRC emacs-lisp
(use-package ace-window
:ensure t
:bind ("M-o" . ace-window)
:config (setq aw-background nil))
#+END_SRC
** avy
#+BEGIN_SRC emacs-lisp
(use-package avy
:ensure t
:bind ("M-s" . avy-goto-char)
:config (setq avy-background t))
#+END_SRC
** sudo edit
#+BEGIN_SRC emacs-lisp
(use-package sudo-edit
:ensure t
:bind ("C-c s" . sudo-edit))
#+END_SRC
** typit
#+BEGIN_SRC emacs-lisp
(use-package typit
:init
:ensure t)
#+END_SRC
** calfw
#+BEGIN_SRC emacs-lisp
(use-package calfw
:init
:ensure t)
#+END_SRC
** evil
*** packages
#+BEGIN_SRC emacs-lisp
(use-package evil
:ensure t
:config
(evil-mode 1)
(use-package evil-org
:ensure t
:after org
:delight
:config
(add-hook 'org-mode-hook 'evil-org-mode)
(add-hook 'evil-org-mode-hook
(lambda ()
(evil-org-set-key-theme)))
(require 'evil-org-agenda)
(evil-org-agenda-set-keys)))
#+END_SRC
*** keybindings
vim is all about escape, not...ctrl+g???
+BEGIN_SRC emacs-lisp
(define-key evil-normal-state-map [escape] 'keyboard-quit)
(define-key evil-visual-state-map [escape] 'keyboard-quit)
;; since ctrl+g and evil make no sense
(defun nd/minibuffer-keyboard-quit ()
"Abort recursive edit.
In Delete Selection mode, if the mark is active, just deactivate it;
then it takes a second \\[keyboard-quit] to abort the minibuffer."
(interactive)
(if (and delete-selection-mode transient-mark-mode mark-active)
(setq deactivate-mark t)
(when (get-buffer "*Completions* ") (delete-windows-on "*Completions* "))
(abort-recursive-edit)))
(define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit)
#+END_SRC
** undo tree
#+BEGIN_SRC emacs-lisp
(use-package undo-tree
:ensure t
:delight
:config
(global-undo-tree-mode)
(setq undo-tree-visualizer-diff t))
#+END_SRC
* custom functions
** follow window splitting
#+BEGIN_SRC emacs-lisp
(defun split-and-follow-horizontally ()
(interactive)
(split-window-below)
(balance-windows)
(other-window 1))
(global-set-key (kbd "C-x 2") 'split-and-follow-horizontally)
(defun split-and-follow-vertically ()
(interactive)
(split-window-right)
(balance-windows)
(other-window 1))
(global-set-key (kbd "C-x 3") 'split-and-follow-vertically)
#+END_SRC
** config edit and reload
*** edit
#+BEGIN_SRC emacs-lisp
(defun config-visit ()
(interactive)
(find-file "~/.emacs.d/conf.org"))
(global-set-key (kbd "C-c e") 'config-visit)
#+END_SRC
*** reload
#+BEGIN_SRC emacs-lisp
(defun config-reload ()
"Reloads ~/.emacs.d/conf.org at runtime"
(interactive)
(org-babel-load-file (expand-file-name "~/.emacs.d/conf.org")))
(global-set-key (kbd "C-c r") 'config-reload)
#+END_SRC
** custom keybindings
*** delete whole line
#+BEGIN_SRC emacs-lisp
(global-set-key (kbd "C-S-w") 'fc/delete-whole-line)
(defun fc/delete-whole-line ()
"Delete the whole line without flooding the kill ring"
(interactive)
(delete-region (progn (forward-line 0) (point))
(progn (forward-line 1) (point))))
#+END_SRC
*** delete word forward
#+BEGIN_SRC emacs-lisp
(global-set-key (kbd "M-d") 'fc/delete-word-forward)
(defun fc/delete-word-forward (arg)
"Delete word forward without flooding the kill ring"
(interactive "p")
(delete-region (point) (progn (forward-word arg) (point))))
#+END_SRC
*** delete word backward
#+BEGIN_SRC emacs-lisp
(global-set-key (kbd "<M-backspace >") 'fc/delete-word-backward)
(defun fc/delete-word-backward (arg)
"Delete word backward without flooding the kill ring"
(interactive "p")
(delete-region (point) (progn (backward-word arg) (point))))
#+END_SRC
*** duplicate line
#+BEGIN_SRC emacs-lisp
(global-set-key (kbd "C-c C-d") 'fc/duplicate-current-line-or-region)
(defun fc/duplicate-current-line-or-region (arg)
"Duplicates the current line or region ARG times."
(interactive "p")
(let (beg end (origin (point)))
(if (and mark-active (> (point) (mark)))
(exchange-point-and-mark))
(setq beg (line-beginning-position))
(if mark-active
(exchange-point-and-mark))
(setq end (line-end-position))
(let ((region (buffer-substring-no-properties beg end)))
(dotimes (i arg)
(goto-char end)
(newline)
(insert region)
(setq end (point))))))
#+END_SRC
(goto-char (+ origin (* (length region) arg) arg)))))
* ess
#+begin_src emacs-lisp
(setq inferior-R-args "--quiet --no-save")
(load "ess-site")
(setq ess-history-file "session.Rhistory")
(setq ess-history-directory
(substitute-in-file-name "${XDG_CONFIG_HOME}/r/ "))
#+END_SRC
* languages
** 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
* org-mode
** basic
#+BEGIN_SRC emacs-lisp
(setq org-log-done t)
(setq org-src-window-setup 'current-window)
(setq org-startup-indented t)
(delight 'org-indent-mode)
(setq org-directory "~/Org")
#+END_SRC
** evil modes
#+BEGIN_SRC emacs-lisp
;;(add-hook 'org-capture-mode-hook 'evil-append)
#+END_SRC
** source snippets
*** emacs-lisp
#+BEGIN_SRC emacs-lisp
(add-to-list 'org-structure-template-alist
'("el" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC"))
#+END_SRC
** keyboard shortcuts
*** navigation
#+BEGIN_SRC emacs-lisp
(setq org-special-ctrl-a/e t)
(setq org-special-ctrl-k t)
(setq org-yank-adjusted-subtrees t)
#+END_SRC
** todo states
*** sequences
#+BEGIN_SRC emacs-lisp
(setq org-todo-keywords
2018-04-08 01:10:01 -04:00
'((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
(sequence "WAITING(w@/!)" "HOLD(h@/ !)" "|" "CANCELLED(c@/!)")))
2018-03-21 21:44:31 -04:00
#+END_SRC
*** colors
#+BEGIN_SRC emacs-lisp
(setq org-todo-keyword-faces
(quote (("TODO" :foreground "light coral" :weight bold)
("NEXT" :foreground "khaki" :weight bold)
("DONE" :foreground "light green" :weight bold)
("WAITING" :foreground "orange" :weight bold)
("HOLD" :foreground "violet" :weight bold)
("CANCELLED" :foreground "deep sky blue" :weight bold))))
#+END_SRC
2018-04-24 23:37:37 -04:00
** tags
I use tags for contexts (mostly). The "@" represents location contexts and a mutually exclusive as there is only one of me. The "#" contexts represent tools which must be available.
#+BEGIN_SRC emacs-lisp
(setq org-tag-alist '((:startgroup)
("@errand" . ?e)
("@work" . ?w)
("@home" . ?h)
("@travel" . ?f)
(:endgroup)
("#laptop" . ?L)
("#hood" . ?H)
2018-04-27 07:52:05 -04:00
("WORK" . ?W)
("PERSONAL" . ?P)
2018-04-24 23:37:37 -04:00
("NOTE" . ?N)
("FLAGGED" . ??)))
2018-04-08 01:10:01 -04:00
;; TODO I'm sure there is a better way to do this in lisp
(setq org-tag-faces
2018-04-24 23:37:37 -04:00
'(("@errand" . (:foreground "PaleGreen"))
("@work" . (:foreground "PaleGreen"))
("@home" . (:foreground "PaleGreen"))
("@travel" . (:foreground "PaleGreen"))
("#laptop" . (:foreground "SkyBlue"))
("#hood" . (:foreground "SkyBlue"))))
#+END_SRC
** properties
#+BEGIN_SRC emacs-lisp
;; this is basically the same as putting the properties at the top of all org files
(add-to-list 'org-default-properties "Project_Type")
(setq org-global-properties '(("Project_Type_ALL" . "series")))
;; TODO this may not be needed
(setq org-use-property-inheritance '("Project_Type"))
2018-03-21 21:44:31 -04:00
#+END_SRC
** capture templates
#+BEGIN_SRC emacs-lisp
(setq org-capture-templates
2018-04-13 01:46:47 -04:00
'(("t" "todo" entry (file "~/Org/capture.org") "* TODO %?\ndeliverable: \n%U\n")
2018-04-08 01:10:01 -04:00
("n" "note" entry (file "~/Org/capture.org") "* %? :NOTE:\n%U\n" )
("a" "appointment" entry (file "~/Org/capture.org") "* TODO %?\n%U\n%^t\n" )
("m" "multi-day" entry (file "~/Org/capture.org") "* TODO %?\n%U\n%^t--%^t\n" )
2018-04-13 01:46:47 -04:00
("d" "deadline" entry (file "~/Org/capture.org") "* TODO %?\nDEADLINE: %^t\ndeliverable:\n%U\n" )
2018-04-08 01:10:01 -04:00
("j" "journal" entry (file+datetree "~/Org/diary.org") "* %?\n%U\n")
("p" "org-protocol" entry (file+headline ,(concat org-directory "~/Org/capture.org") "Inbox")
"* %^{Title}\nSource: %u, %c\n #+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n\n%?")
("L" "org-protocol" entry (file+headline ,(concat org-directory "~/Org/capture.org") "Inbox")
"* %? [[%:link ][%:description ]] \nCaptured On: %U")
("h" "habit" entry (file "~/Org/capture.org")
"* NEXT %?\n%U\n%a\nSCHEDULED: %(format-time-string \"%<<%Y-%m-%d %a .+1d/3d >>\")\n:PROPERTIES:\n:STYLE: habit\n:REPEAT_TO_STATE: NEXT\n:END:\n")))
2018-03-21 21:44:31 -04:00
#+END_SRC
** refile
*** targets
#+BEGIN_SRC emacs-lisp
(setq org-refile-targets (quote ((nil :maxlevel . 9)
("~/Org/reference/idea.org" :maxlevel . 9)
(org-agenda-files :maxlevel . 9))))
#+END_SRC
*** completion
#+BEGIN_SRC emacs-lisp
(setq org-refile-use-outline-path t)
(setq org-outline-path-complete-in-steps nil)
(setq org-completion-use-ido t)
#+END_SRC
*** node creation
#+BEGIN_SRC emacs-lisp
2018-04-08 01:10:01 -04:00
(setq org-refile-allow-creating-parent-nodes 'confirm)
2018-03-21 21:44:31 -04:00
#+END_SRC
*** use current window
#+BEGIN_SRC emacs-lisp
(setq org-indirect-buffer-display 'current-window)
#+END_SRC
*** exclude done states
#+BEGIN_SRC emacs-lisp
(defun nd/verify-refile-target ()
"Exclude todo keywords with a done state from refile targets"
(not (member (nth 2 (org-heading-components)) org-done-keywords)))
(setq org-refile-target-verify-function 'nd/verify-refile-target)
#+END_SRC
2018-04-27 23:03:56 -04:00
** ui
*** bullets
#+BEGIN_SRC emacs-lisp
(use-package org-bullets
:ensure t
:config
(add-hook 'org-mode-hook (lambda () (org-bullets-mode))))
#+END_SRC
2018-03-21 21:44:31 -04:00
** agenda
*** basic config
#+BEGIN_SRC emacs-lisp
2018-04-27 23:03:56 -04:00
(setq org-agenda-files '("~/Org"
"~/Org/large_projects"
"~/Org/reference"))
;; (setq org-agenda-files '("~/Org/reference/agendatest.org"))
2018-03-21 21:44:31 -04:00
(setq org-agenda-dim-blocked-tasks nil)
(setq org-agenda-compact-blocks t)
#+END_SRC
2018-04-02 00:40:42 -04:00
*** task helper functions
These are the building blocks for skip functions.
2018-03-21 21:44:31 -04:00
#+BEGIN_SRC emacs-lisp
2018-03-24 00:50:01 -04:00
(defun nd/is-todoitem-p ()
2018-04-02 00:40:42 -04:00
"return todo keyword if present in headline (which defines the heading as a todoitem)
this is used to both test if a heading is a todoitem and retrieving the keyword"
2018-03-24 18:13:54 -04:00
(let ((keyword (nth 2 (org-heading-components))))
(if (member keyword org-todo-keywords-1)
keyword)))
2018-03-21 21:44:31 -04:00
2018-03-24 00:50:01 -04:00
(defun nd/is-project-p ()
2018-03-24 18:13:54 -04:00
"return todo keyword if heading is todoitem and has children"
2018-03-31 01:43:17 -04:00
(and (nd/heading-has-children) (nd/is-todoitem-p)))
2018-03-21 21:44:31 -04:00
2018-03-24 00:50:01 -04:00
(defun nd/is-task-p ()
2018-03-24 18:13:54 -04:00
"return todo keyword if heading is todoitem with no children"
2018-03-31 01:43:17 -04:00
(and (not (nd/heading-has-children)) (nd/is-todoitem-p)))
2018-03-24 00:50:01 -04:00
(defun nd/is-atomic-task-p ()
2018-03-24 18:13:54 -04:00
"return todo keyword if heading is task with no parents"
2018-03-31 01:43:17 -04:00
(and (not (nd/heading-has-parent)) (nd/is-task-p)))
2018-03-24 00:50:01 -04:00
2018-04-01 18:45:26 -04:00
(defun nd/is-project-task-p ()
2018-04-27 23:03:56 -04:00
"return todo keyword if heading is task with parents"
2018-04-01 18:45:26 -04:00
(and (nd/heading-has-parent) (nd/is-task-p)))
2018-03-24 18:13:54 -04:00
(defun nd/is-scheduled-heading-p ()
"return timestamp if headline is scheduled"
(org-entry-get nil "SCHEDULED"))
2018-04-24 23:37:37 -04:00
(defun nd/is-series-header-p ()
"return t if headline has property Project_Type=series"
(equal "series" (org-entry-get nil "Project_Type")))
2018-04-14 01:25:14 -04:00
(defun nd/is-closed-heading-p ()
"return timestamp if headline is closed"
(let ((timestamp (org-entry-get nil "CLOSED")))
(if timestamp (float-time (date-to-time timestamp)))))
(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 ()
"return timestamp if todoitem is closed and older than specified time"
(let ((timestamp (nd/is-closed-heading-p)))
;; NOTE we do not ensure that the todo state is in done keywords
;; this is to allow easier error correction in slip functions
(if (and timestamp (> (- (float-time) timestamp) (* 60 60 24 nd/archive-delay-days)))
timestamp)))
(defun nd/is-archivable-atomic-task-p ()
"return keyword if heading is an archivable task"
(and (nd/is-archivable-heading-p) (nd/is-atomic-task-p)))
(defun nd/is-archivable-project-p ()
"return keyword if heading is an archivable task"
(and (nd/is-archivable-heading-p) (nd/is-project-p)))
2018-03-24 18:13:54 -04:00
(defun nd/is-active-task-p ()
"return keyword if task is either NEXT or scheduled"
(let ((keyword (nd/is-task-p)))
(if (or (equal keyword "NEXT") (nd/is-scheduled-heading-p))
keyword)))
(defun nd/is-blocked-task-p ()
"return keyword if task is WAITING"
(equal (nd/is-task-p) "WAITING"))
2018-03-22 00:32:17 -04:00
2018-04-02 00:40:42 -04:00
(defun nd/heading-has-children ()
"returns t if heading has todoitems in its immediate subtree"
;; TODO make this more efficient (and accurate) by only testing
;; the level immediately below (if it exists)
(let ((has-children)
(subtree-end (save-excursion (org-end-of-subtree t))))
(save-excursion
(outline-next-heading)
(while (and (not has-children)
(< (point) subtree-end))
(when (nd/is-todoitem-p)
(setq has-children t))
;; (org-forward-heading-same-level 1 t)))
(outline-next-heading)))
has-children))
(defun nd/heading-has-parent ()
"returns parent keyword if heading is in the immediate subtree of a todoitem"
(save-excursion (and (org-up-heading-safe) (nd/is-todoitem-p))))
2018-04-27 23:03:56 -04:00
(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"
2018-04-08 21:54:20 -04:00
(let ((has-todoitem-parent)
(has-non-todoitem-parent))
(save-excursion
(while (and (org-up-heading-safe)
2018-04-13 01:46:47 -04:00
(not has-todoitem-parent))
2018-04-08 21:54:20 -04:00
(if (nd/is-todoitem-p)
(setq has-todoitem-parent t)
(setq has-non-todoitem-parent t))))
(and has-todoitem-parent has-non-todoitem-parent)))
(defconst nd/project-invalid-todostates
'("WAITING" "NEXT")
"projects cannot have these todostates")
2018-03-28 00:41:11 -04:00
;; project level testing
2018-04-27 07:52:05 -04:00
(defconst nd/project-statuscodes
'(:archivable
:complete
:stuck
:held
:waiting
:active
:done-incomplete
:undone-complete
:invalid-todostate
2018-04-27 09:58:08 -04:00
:scheduled-project)
2018-04-27 07:52:05 -04:00
"list of statuscodes to be used in assessing projects
Note they are listed in order of priority (eg items further
down the list override higher items")
(defmacro nd/compare-statuscodes (operator statuscode-1 statuscode-2)
"syntactic suger to compare statuscodes by position"
`(,operator (position ,statuscode-1 nd/project-statuscodes)
(position ,statuscode-2 nd/project-statuscodes)))
(defun nd/status< (statuscode-1 statuscode-2)
"returns t is statuscode-1 is lesser priority than statuscode-2"
(nd/compare-statuscodes < statuscode-1 statuscode-2))
(defun nd/status> (statuscode-1 statuscode-2)
"returns t is statuscode-1 is greater priority than statuscode-2"
(nd/compare-statuscodes > statuscode-1 statuscode-2))
(defun nd/status= (statuscode-1 statuscode-2)
"returns t is statuscode-1 is equal priority than statuscode-2"
(nd/compare-statuscodes = statuscode-1 statuscode-2))
2018-03-28 00:41:11 -04:00
(defun nd/descend-into-project ()
2018-04-27 07:52:05 -04:00
"returns statuscode of project and recursively descends into subprojects"
(let ((project-state :archivable)
2018-03-28 00:41:11 -04:00
(previous-point))
(save-excursion
(setq previous-point (point))
(outline-next-heading)
2018-04-27 07:52:05 -04:00
;; loop breaks if active or higher priority
;; note that all invalid statuscodes are higher
;; thus this function will only return the first
;; encountered error
(while (and (nd/status< project-state :active)
2018-03-28 00:41:11 -04:00
(> (point) previous-point))
2018-04-13 01:46:47 -04:00
(let ((keyword (nd/is-todoitem-p)))
2018-03-28 00:41:11 -04:00
(if keyword
2018-04-01 18:45:26 -04:00
(let ((cur-state
2018-04-13 01:46:47 -04:00
(if (nd/heading-has-children)
2018-04-27 07:52:05 -04:00
(cond ((member keyword nd/project-invalid-todostates) :invalid-todostate)
((nd/is-scheduled-heading-p) :scheduled-project)
((equal keyword "CANCELLED") (if (nd/is-archivable-heading-p)
:archivable
:complete))
((equal keyword "HOLD") :held)
2018-04-08 21:54:20 -04:00
(t (let ((child-statuscode (nd/descend-into-project)))
2018-04-27 07:52:05 -04:00
(cond ((equal keyword "TODO")
(if (nd/status> child-statuscode :complete)
child-statuscode
:undone-complete))
2018-04-24 23:37:37 -04:00
(t (case child-statuscode
2018-04-27 07:52:05 -04:00
(:complete :complete)
(:archivable (if (nd/is-archivable-heading-p)
:archivable
:complete))
(t (if (= child-statuscode :complete)
:complete
:done-imcomplete))))))))
(cond ((equal keyword "HOLD") :held)
((equal keyword "WAITING") :waiting)
((equal keyword "NEXT") :active)
((and (equal keyword "TODO") (nd/is-scheduled-heading-p)) :active)
((equal keyword "TODO") :stuck)
((nd/is-archivable-heading-p) :archivable)
(t :complete)))))
(if (nd/status> cur-state project-state)
2018-03-30 20:54:25 -04:00
(setq project-state cur-state)))))
2018-03-28 00:41:11 -04:00
(setq previous-point (point))
(org-forward-heading-same-level 1 t)))
project-state))
2018-04-27 23:03:56 -04:00
(defmacro nd/is-project-keyword-status-p (test-keyword operator statuscode)
2018-04-27 09:58:08 -04:00
"tests if a project has toplevel heading of top-keyword and
child status equal to status code and returns keyword if
both are true"
2018-04-27 23:03:56 -04:00
`(and
(equal ,keyword ,test-keyword)
(nd/compare-statuscodes ,operator (nd/descend-into-project) ,statuscode)))
2018-04-27 09:58:08 -04:00
2018-03-31 01:43:17 -04:00
(defun nd/is-project-status-p (statuscode)
2018-04-27 23:03:56 -04:00
"Returns t if project matches statuscode given.
Note that this assumes the headline being tested is a valid project"
(case statuscode
;; projects closed more than 30 days ago
;; note CANCELLED overrides all subtasks/projects
(:archivable
(if (nd/is-archivable-heading-p)
(or (equal keyword "CANCELLED")
(nd/is-project-keyword-status-p "DONE" = :archivable))))
;; projects closed less than 30 days ago
;; note CANCELLED overrides all subtasks/projects
(:complete
(if (not (nd/is-archivable-heading-p))
(or (equal keyword "CANCELLED")
(nd/is-project-keyword-status-p "DONE" = :complete))))
;; projects with no waiting, held, or active components
(:stuck
(nd/is-project-keyword-status-p "TODO" = :stuck))
;; held projects
;; note toplevel HOLD overrides all subtasks/projects
(:held
(or (equal keyword "HOLD")
(nd/is-project-keyword-status-p "TODO" = :held)))
;; projects with at least one waiting component
(:waiting
(nd/is-project-keyword-status-p "TODO" = :waiting))
;; projects with at least one active component
(:active
(nd/is-project-keyword-status-p "TODO" = :active))
;; projects marked DONE but still have undone subtasks
(:done-incomplete
(nd/is-project-keyword-status-p "DONE" > :complete))
;; projects marked TODO but all subtasks are done
(:undone-complete
(nd/is-project-keyword-status-p "TODO" < :stuck))
;; projects with invalid todo keywords
(:invalid-todostate
(member keyword nd/project-invalid-todostates))
;; projects with scheduled heading (only subtasks should be scheduled)
(:scheduled-project
(nd/is-scheduled-heading-p))
;; error if not known
(t (if (not (member statuscode nd/project-statuscodes))
(error "unknown statuscode")))))
2018-04-24 23:37:37 -04:00
#+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.
#+BEGIN_SRC emacs-lisp
2018-04-27 23:03:56 -04:00
;; helper functions
(defun nd/skip-item ()
(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" "CANCELLED")
"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-item)))))
2018-04-24 23:37:37 -04:00
2018-04-27 23:03:56 -04:00
;; atomic tasks
;; by definition these have no parents, so
;; we don't need to worry about skipping over projects
;; any todo state is valid and we only sort by done/cancelled
(defun nd/skip-non-unclosed-atomic-tasks ()
(nd/skip-heading-with
nd/is-atomic-task-p
(not (member keyword org-done-keywords))))
(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)))))
2018-04-24 23:37:37 -04:00
2018-04-27 23:03:56 -04:00
(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))))
;; 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, WAITING, and HOLD because these are
;; definitive project tasks that require/inhibit
;; futher action
(defun nd/skip-non-keyword-project-tasks (skip-keyword)
2018-04-24 23:37:37 -04:00
(save-restriction
(widen)
2018-04-27 23:03:56 -04:00
(let ((keyword (nd/is-todoitem-p)))
(if keyword
(if (nd/heading-has-children)
(if (member keyword nd/project-skip-todostates)
(nd/skip-subtree)
(nd/skip-item))
(if (not (and (nd/heading-has-parent)
(equal keyword skip-keyword)))
(nd/skip-item)))
(nd/skip-item)))))
2018-04-24 23:37:37 -04:00
2018-04-27 23:03:56 -04:00
;; task-level errors
2018-04-24 23:37:37 -04:00
(defun nd/skip-non-discontinuous-project-tasks ()
2018-04-27 23:03:56 -04:00
(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)))))
2018-04-24 23:37:37 -04:00
(defun nd/skip-non-undone-closed-todoitems ()
2018-04-27 23:03:56 -04:00
(nd/skip-heading-with
nd/is-todoitem-p
(and (not (member keyword org-done-keywords))
(nd/is-closed-heading-p))))
(defun nd/skip-non-series-atomic-tasks ()
(nd/skip-heading-with
nd/is-atomic-task-p
(nd/is-series-heading-p)))
2018-04-24 23:37:37 -04:00
;; projects
(defun nd/skip-projects-without-statuscode (statuscode)
(save-restriction
(widen)
2018-04-27 23:03:56 -04:00
(let ((keyword (nd/is-project-p)))
;; TODO there may be a way to skip over skippable projects
;; and save a few cycles. Not a huge deal, but would require
;; keeping the skippable line and then skipping over the others
;; in one fell swoop, not easy to do efficiently
(if keyword
(if (not (nd/is-project-status-p statuscode))
(if nd/agenda-limit-project-toplevel
(nd/skip-subtree)
(nd/skip-item)))
(nd/skip-item)))))
2018-04-24 23:37:37 -04:00
#+END_SRC
*** interactive view functions
#+BEGIN_SRC emacs-lisp
(defvar nd/agenda-limit-project-toplevel t
"used to filter projects by all levels or top-level only")
(defun nd/toggle-project-toplevel-display ()
(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/agenda-base-task-command (keyword skip-fun)
"shorter syntax to define task agenda commands"
2018-04-27 23:03:56 -04:00
`(tags
"-NA-REFILE/"
2018-04-24 23:37:37 -04:00
((org-agenda-overriding-header (concat ,keyword " Tasks"))
(org-agenda-skip-function ,skip-fun)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy '(category-keep)))))
(defun nd/agenda-base-project-command (match keyword statuscode)
"shorter syntax to define project agenda commands"
`(tags
,match
((org-agenda-overriding-header (concat
(and nd/agenda-limit-project-toplevel "Toplevel ")
,keyword
" Projects"))
2018-04-27 23:03:56 -04:00
(org-agenda-skip-function '(nd/skip-projects-without-statuscode ,statuscode))
2018-04-24 23:37:37 -04:00
(org-agenda-sorting-strategy '(category-keep)))))
2018-04-02 00:40:42 -04:00
#+END_SRC
2018-04-19 01:22:11 -04:00
*** custom commands
#+BEGIN_SRC emacs-lisp
(setq org-agenda-tags-todo-honor-ignore-options t)
(setq org-agenda-custom-commands
`(("t"
2018-04-24 23:37:37 -04:00
"Task View"
2018-04-19 01:22:11 -04:00
((agenda "" nil)
2018-04-27 23:03:56 -04:00
,(nd/agenda-base-task-command "Next Project" ''(nd/skip-non-keyword-project-tasks "NEXT"))
,(nd/agenda-base-task-command "Waiting Project" ''(nd/skip-non-keyword-project-tasks "WAITING"))
,(nd/agenda-base-task-command "Atomic" ''nd/skip-non-unclosed-atomic-tasks)
,(nd/agenda-base-task-command "Held Project" ''(nd/skip-non-keyword-project-tasks "HOLD"))))
2018-04-19 01:22:11 -04:00
("o"
"Project Overview"
2018-04-27 07:52:05 -04:00
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/ !" "Stuck" :stuck)
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/ !" "Waiting" :waiting)
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/ !" "Active" :active)
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/ !" "Held" :held)))
2018-04-19 01:22:11 -04:00
("r"
"Refile and errors"
2018-04-24 23:37:37 -04:00
((tags "REFILE"
((org-agenda-overriding-header "Tasks to Refile"))
(org-tags-match-list-sublevels nil))
2018-04-19 01:22:11 -04:00
,(nd/agenda-base-task-command "Discontinous Project" ''nd/skip-non-discontinuous-project-tasks)
2018-04-27 23:03:56 -04:00
,(nd/agenda-base-task-command "Undone Closed" ''nd/skip-non-undone-closed-todoitems)
,(nd/agenda-base-task-command "Done Unclosed" ''nd/skip-non-done-unclosed-todoitems)
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/ " "Undone Completed" :undone-complete)
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/ " "Done Incompleted" :done-incomplete)
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/ " "Invalid Todostate" :invalid-todostate)))
2018-04-24 23:37:37 -04:00
("s"
"Series projects"
2018-04-27 07:52:05 -04:00
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC+Project_Type=\"series\"/ !" "Active Series" :active)
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC+Project_Type=\"series\"/ !" "Empty Series" :complete)))
2018-04-19 01:22:11 -04:00
("A"
2018-04-24 23:37:37 -04:00
"Archivable Tasks and Projects"
2018-04-19 01:22:11 -04:00
((tags "-NA-REFILE/"
((org-agenda-overriding-header "Atomic Tasks to Archive")
(org-agenda-skip-function 'nd/skip-non-archivable-atomic-tasks)
2018-04-24 23:37:37 -04:00
(org-tags-match-list-sublevels nil)))
2018-04-27 07:52:05 -04:00
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC+Project_Type=\"series\"/ !" "Archivable Series" :archivable)
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC/ " "Archivable" :archivable)))))
2018-04-19 01:22:11 -04:00
#+END_SRC
2018-04-08 21:54:20 -04:00
*** keymap
2018-04-02 00:40:42 -04:00
#+BEGIN_SRC emacs-lisp
2018-04-08 21:54:20 -04:00
(evil-define-key 'motion org-agenda-mode-map "T" 'nd/toggle-project-toplevel-display)
2018-04-02 00:40:42 -04:00
#+END_SRC
2018-04-08 21:54:20 -04:00
*** views
**** calendar display
2018-04-02 00:40:42 -04:00
#+BEGIN_SRC emacs-lisp
2018-04-08 21:54:20 -04:00
(setq org-agenda-span 'day)
(setq org-agenda-time-grid (quote ((daily today remove-match)
#("----------------" 0 16 (org-heading t))
(0900 1100 1300 1500 1700))))
#+End_src
**** right align tags
the agenda does not do this by default...it's annoying
2018-04-02 00:40:42 -04:00
#+BEGIN_SRC emacs-lisp
2018-04-08 21:54:20 -04:00
(add-hook 'org-finalize-agenda-hook 'place-agenda-tags)
(defun place-agenda-tags ()
"Put the agenda tags by the right border of the agenda window."
(setq org-agenda-tags-column (- 4 (window-width)))
(org-agenda-align-tags))
#+END_SRC
*** auto exclusion
#+BEGIN_SRC emacs-lisp
(defun nd/org-auto-exclude-function (tag)
"Automatic task exclusion in the agenda with / RET"
(and (cond
((string= tag "hold")
t))
(concat "-" tag)))
2018-04-02 00:40:42 -04:00
2018-04-08 21:54:20 -04:00
(setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function)
2018-03-21 21:44:31 -04:00
#+END_SRC
** caldav
+BEGIN_SRC emacs-lisp
(use-package org-caldav
:ensure t
:config (org-caldav-url "https://portnoy4prez.yavin4.ch/nextcloud/remote.php/dav/calendars/petrucci4prez/concerts/ "
org-cladav-calendar-id "testorg"
org-caldav-inbox "~/Org/reference/testcal.org"))
#+END_SRC
** calfw
#+BEGIN_SRC emacs-lisp
(use-package calfw-org
:init
: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 ?┓))
#+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