aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaúl Benencia <rul@kalgan.cc>2019-06-23 11:43:30 -0700
committerRaúl Benencia <rul@kalgan.cc>2019-06-23 13:32:01 -0700
commit284c8327c95bb0c71b111ebf95723a35a478295c (patch)
treefaea9db4b1bba00d73d33b2065ed102f88b76b2c
Add emacs config
-rw-r--r--.emacs48
-rw-r--r--.emacs.local.d/elisp/beancount.el653
-rw-r--r--.emacs.local.d/general.el165
-rw-r--r--.emacs.local.d/modes/anzu.el8
-rw-r--r--.emacs.local.d/modes/auto-complete.el13
-rw-r--r--.emacs.local.d/modes/auto-fill.el4
-rw-r--r--.emacs.local.d/modes/beancount.el5
-rw-r--r--.emacs.local.d/modes/company.el29
-rw-r--r--.emacs.local.d/modes/dashboard.el11
-rw-r--r--.emacs.local.d/modes/doom-modeline.el5
-rw-r--r--.emacs.local.d/modes/ecb.el21
-rw-r--r--.emacs.local.d/modes/eshell.el85
-rw-r--r--.emacs.local.d/modes/flycheck.el12
-rw-r--r--.emacs.local.d/modes/flyspell.el12
-rw-r--r--.emacs.local.d/modes/go-lang.el23
-rw-r--r--.emacs.local.d/modes/helm.el22
-rw-r--r--.emacs.local.d/modes/ibuffer.el35
-rw-r--r--.emacs.local.d/modes/ido.el19
-rw-r--r--.emacs.local.d/modes/imenu.el12
-rw-r--r--.emacs.local.d/modes/latex.el9
-rw-r--r--.emacs.local.d/modes/linum.el3
-rw-r--r--.emacs.local.d/modes/magit.el15
-rw-r--r--.emacs.local.d/modes/mail-mode.el10
-rw-r--r--.emacs.local.d/modes/markdown.el5
-rw-r--r--.emacs.local.d/modes/mu4e.el61
-rw-r--r--.emacs.local.d/modes/notmuch.el105
-rw-r--r--.emacs.local.d/modes/org.el676
-rw-r--r--.emacs.local.d/modes/package.el7
-rw-r--r--.emacs.local.d/modes/projectile.el3
-rw-r--r--.emacs.local.d/modes/smex.el15
-rw-r--r--.emacs.local.d/modes/sml.el7
-rw-r--r--.emacs.local.d/modes/sublimity.el8
-rw-r--r--.emacs.local.d/modes/themes.el6
-rw-r--r--.emacs.local.d/modes/whitespace.el1
-rw-r--r--.emacs.local.d/modes/writeroom.el27
-rw-r--r--README.md15
36 files changed, 2155 insertions, 0 deletions
diff --git a/.emacs b/.emacs
new file mode 100644
index 0000000..e250c9c
--- /dev/null
+++ b/.emacs
@@ -0,0 +1,48 @@
+;; Load up the general config
+
+;; Added by Package.el. This must come before configurations of
+;; installed packages. Don't delete this line. If you don't want it,
+;; just comment it out by adding a semicolon to the start of the line.
+;; You may delete these explanatory comments.
+(package-initialize)
+
+(load-file "~/.emacs.local.d/general.el")
+
+;; Modes
+;;(load-file "~/.emacs.local.d/modes/auto-complete.el")
+;;(load-file "~/.emacs.local.d/modes/ecb.el")
+
+;;(load-file "~/.emacs.local.d/modes/linum.el")
+;;(load-file "~/.emacs.local.d/modes/notmuch.el")
+
+(load-file "~/.emacs.local.d/modes/anzu.el")
+(load-file "~/.emacs.local.d/modes/auto-fill.el")
+(load-file "~/.emacs.local.d/modes/company.el")
+(load-file "~/.emacs.local.d/modes/dashboard.el")
+(load-file "~/.emacs.local.d/modes/doom-modeline.el")
+(load-file "~/.emacs.local.d/modes/eshell.el")
+(load-file "~/.emacs.local.d/modes/flyspell.el")
+(load-file "~/.emacs.local.d/modes/go-lang.el")
+(load-file "~/.emacs.local.d/modes/ibuffer.el")
+(load-file "~/.emacs.local.d/modes/helm.el")
+(load-file "~/.emacs.local.d/modes/ido.el")
+(load-file "~/.emacs.local.d/modes/imenu.el")
+(load-file "~/.emacs.local.d/modes/latex.el")
+(load-file "~/.emacs.local.d/modes/mail-mode.el")
+(load-file "~/.emacs.local.d/modes/markdown.el")
+(load-file "~/.emacs.local.d/modes/magit.el")
+(load-file "~/.emacs.local.d/modes/org.el")
+(load-file "~/.emacs.local.d/modes/package.el")
+(load-file "~/.emacs.local.d/modes/projectile.el")
+(load-file "~/.emacs.local.d/modes/smex.el")
+(load-file "~/.emacs.local.d/modes/sml.el")
+(load-file "~/.emacs.local.d/modes/sublimity.el")
+(load-file "~/.emacs.local.d/modes/whitespace.el")
+(load-file "~/.emacs.local.d/modes/writeroom.el")
+
+(load-file "~/.emacs.local.d/modes/themes.el")
+
+;; Extra Debian packages: elpa-yaml-mode
+;; Extra packages to install from Elpa: puppet-mode
+
+(provide '.emacs)
diff --git a/.emacs.local.d/elisp/beancount.el b/.emacs.local.d/elisp/beancount.el
new file mode 100644
index 0000000..7ca2a02
--- /dev/null
+++ b/.emacs.local.d/elisp/beancount.el
@@ -0,0 +1,653 @@
+;;; beancount.el --- A minor mode that can be used to edit beancount input files.
+
+;; Copyright (C) 2013 Martin Blais <blais@furius.ca>
+;; Copyright (C) 2015 Free Software Foundation, Inc.
+
+;; Version: 0
+;; Author: Martin Blais <blais@furius.ca>
+;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
+
+;; This file is not part of GNU Emacs.
+
+;; This package is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This package is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this package. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; TODO: Add a flymake rule, using bean-check
+
+;;; Code:
+
+(autoload 'ido-completing-read "ido")
+(require 'font-lock)
+
+(defgroup beancount ()
+ "Editing mode for Beancount files."
+ :group 'beancount)
+
+(defconst beancount-timestamped-directive-names
+ '("balance"
+ "open"
+ "close"
+ "pad"
+ "document"
+ "note"
+ ;; The ones below are not followed by an account name.
+ "event"
+ "price"
+ "commodity"
+ "query"
+ "txn")
+ "Directive names that can appear after a date.")
+
+(defconst beancount-nontimestamped-directive-names
+ '("pushtag"
+ "poptag"
+ "option"
+ "include"
+ "plugin")
+ "Directive names that can appear after a date.")
+
+(defvar beancount-directive-names
+ (append beancount-nontimestamped-directive-names
+ beancount-timestamped-directive-names)
+ "A list of the directive names.")
+
+(defconst beancount-tag-chars "[:alnum:]-_/.")
+
+(defconst beancount-account-categories
+ '("Assets" "Liabilities" "Equity" "Income" "Expenses"))
+
+(defconst beancount-account-chars "[:alnum:]-_:")
+
+(defconst beancount-option-names
+ ;; This list is kept in sync with the options defined in
+ ;; beancount/parser/options.py.
+ ;; Note: We should eventually build a tool that spits out the current list
+ ;; automatically.
+ '("title"
+ "name_assets"
+ "name_liabilities"
+ "name_equity"
+ "name_income"
+ "name_expenses"
+ "bookin_algorithm"
+ "bookin_method"
+ "account_previous_balances"
+ "account_previous_earnings"
+ "account_previous_conversions"
+ "account_current_earnings"
+ "account_current_conversions"
+ "account_rounding"
+ "conversion_currency"
+ "inferred_tolerance_default"
+ "inferred_tolerance_multiplier"
+ "infer_tolerance_from_cost"
+ "documents"
+ "operating_currency"
+ "render_commas"
+ "plugin_processing_mode"
+ "plugin"
+ "long_string_maxlines"
+ ))
+
+(defvar beancount-font-lock-keywords
+ `(;; Reserved keywords
+ (,(regexp-opt beancount-directive-names) . font-lock-keyword-face)
+
+ ;; Tags & Links
+ ("[#\\^][A-Za-z0-9\-_/.]+" . font-lock-type-face)
+
+ ;; Date
+ ("[0-9][0-9][0-9][0-9][-/][0-9][0-9][-/][0-9][0-9]" . font-lock-constant-face)
+
+ ;; Account
+ ("\\([A-Z][A-Za-z0-9\-]+:\\)+\\([A-Z][A-Za-z0-9\-]+\\)" . font-lock-builtin-face)
+
+ ;; Txn Flags
+ ("! " . font-lock-warning-face)
+
+ ;; Number + Currency
+ ;;; ("\\([\\-+]?[0-9,]+\\(\\.[0-9]+\\)?\\)\\s-+\\([A-Z][A-Z0-9'\.]\\{1,10\\}\\)" . )
+ ))
+
+
+(defvar beancount-mode-map-prefix [(control c)]
+ "The prefix key used to bind Beancount commands in Emacs")
+
+(defvar beancount-mode-map
+ (let ((map (make-sparse-keymap))
+ (p beancount-mode-map-prefix))
+ (define-key map (vconcat p [(\')]) #'beancount-insert-account)
+ (define-key map (vconcat p [(control g)]) #'beancount-transaction-set-flag)
+ (define-key map (vconcat p [(r)]) #'beancount-init-accounts)
+ (define-key map (vconcat p [(l)]) #'beancount-check)
+ (define-key map (vconcat p [(q)]) #'beancount-query)
+ (define-key map (vconcat p [(x)]) #'beancount-context)
+ (define-key map (vconcat p [(k)]) #'beancount-linked)
+ (define-key map (vconcat p [(p)]) #'beancount-insert-prices)
+ (define-key map (vconcat p [(\;)]) #'beancount-align-to-previous-number)
+ (define-key map (vconcat p [(\:)]) #'beancount-align-numbers)
+
+ ;; FIXME: Binding TAB breaks expected org-mode behavior to fold/unfold. We
+ ;; need to find a better solution.
+ ;;(define-key map [?\t] #'beancount-tab)
+ map))
+
+(defvar beancount-mode-syntax-table
+ (let ((st (make-syntax-table)))
+ (modify-syntax-entry ?\" "\"\"" st)
+ (modify-syntax-entry ?\; "<" st)
+ (modify-syntax-entry ?\n ">" st)
+ st))
+
+(defun beancount--goto-bob () (goto-char (point-min)))
+
+;;;###autoload
+(define-minor-mode beancount-mode
+ "A minor mode to help editing Beancount files.
+This can be used within other text modes, in particular, org-mode
+is great for sectioning large files with many transactions.
+
+\\{beancount-mode-map}"
+ :init-value nil
+ :lighter " Beancount"
+ :group 'beancount
+
+ ;; The following is mostly lifted from lisp-mode.
+
+ (set (make-local-variable 'paragraph-ignore-fill-prefix) t)
+ (set (make-local-variable 'fill-paragraph-function) #'lisp-fill-paragraph)
+
+ (set (make-local-variable 'comment-start) ";; ")
+
+ ;; Look within the line for a ; following an even number of backslashes
+ ;; after either a non-backslash or the line beginning.
+ (set (make-local-variable 'comment-start-skip)
+ "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\);+ *")
+ ;; Font lock mode uses this only when it KNOWS a comment is starting.
+ ;; FIXME: Why bother?
+ (set (make-local-variable 'font-lock-comment-start-skip) ";+ *")
+ ;; Default to `;;' in comment-region.
+ (set (make-local-variable 'comment-add) 1)
+
+ ;; Org-mode sets both of these to `org-comment-or-uncomment-region',
+ ;; which doesn't know about our ";" comments.
+ (kill-local-variable 'comment-region-function)
+ (kill-local-variable 'uncomment-region-function)
+
+ ;; No tabs by default.
+ (set (make-local-variable 'indent-tabs-mode) nil)
+
+ (add-hook 'completion-at-point-functions
+ #'beancount-completion-at-point nil t)
+ (set (make-local-variable 'completion-ignore-case) t)
+
+ ;; Customize font-lock for beancount.
+ ;;
+ (set-syntax-table beancount-mode-syntax-table)
+ (when (fboundp 'syntax-ppss-flush-cache)
+ (syntax-ppss-flush-cache (point-min))
+ (set (make-local-variable 'syntax-begin-function) #'beancount--goto-bob))
+ ;; Force font-lock to use the syntax-table to find strings-and-comments,
+ ;; regardless of what the "host major mode" decided.
+ (set (make-local-variable 'font-lock-keywords-only) nil)
+ ;; Important: you have to use 'nil for the mode here because in certain major
+ ;; modes (e.g. org-mode) the font-lock-keywords is a buffer-local variable.
+ (if beancount-mode
+ (font-lock-add-keywords nil beancount-font-lock-keywords)
+ (font-lock-remove-keywords nil beancount-font-lock-keywords))
+ (if (fboundp 'font-lock-flush)
+ (font-lock-flush)
+ (with-no-warnings (font-lock-fontify-buffer)))
+
+ (when beancount-mode
+ (beancount-init-accounts))
+ )
+
+(defvar beancount-accounts nil
+ "A list of the accounts available in this buffer.
+This is a cache of the value computed by `beancount-get-accounts'.")
+(make-variable-buffer-local 'beancount-accounts)
+
+(defun beancount-init-accounts ()
+ "Initialize or reset the list of accounts."
+ (interactive)
+ (setq beancount-accounts (beancount-get-accounts))
+ (message "Accounts updated."))
+
+(defvar beancount-date-regexp "[0-9][0-9][0-9][0-9][-/][0-9][0-9][-/][0-9][0-9]"
+ "A regular expression to match dates.")
+
+(defvar beancount-account-regexp
+ (concat (regexp-opt beancount-account-categories)
+ "\\(?::[[:upper:]][" beancount-account-chars "]+\\)")
+ "A regular expression to match account names.")
+
+(defvar beancount-number-regexp "[-+]?[0-9,]+\\(?:\\.[0-9]*\\)?"
+ "A regular expression to match decimal numbers in beancount.")
+
+(defvar beancount-currency-regexp "[A-Z][A-Z-_'.]*"
+ "A regular expression to match currencies in beancount.")
+
+(defun beancount-tab ()
+ "Try to use the right meaning of TAB."
+ (interactive)
+ (let ((cdata (beancount-completion-at-point)))
+ (if cdata
+ ;; There's beancount-specific completion at point.
+ (call-interactively #'completion-at-point)
+ (let* ((beancount-mode nil)
+ (fallback (key-binding (this-command-keys))))
+ (if (commandp fallback)
+ (command-execute fallback))))))
+
+(defun beancount-tags (prefix)
+ "Return list of all tags starting with PREFIX in current buffer.
+Excludes tags appearing on the current line."
+ (unless (string-match "\\`[#^]" prefix)
+ (error "Unexpected prefix to search tags: %S" prefix))
+ (let ((found ())
+ (re (concat prefix "[" beancount-tag-chars "]*")))
+ (save-excursion
+ (forward-line 0)
+ (while (re-search-backward re nil t)
+ (push (match-string 0) found)))
+ ;; Ignore tags on current line.
+ (save-excursion
+ (forward-line 1)
+ (while (re-search-forward re nil t)
+ (push (match-string 0) found)))
+ (delete-dups found)))
+
+(defconst beancount-txn-regexp
+ ;; For the full definition of a flag, see the rule that emits FLAG in
+ ;; beancount/parser/lexer.l. For this, let's assume that it's a single char
+ ;; that's neither a space nor a lower-case letter. This should be updated as
+ ;; the parser is improved.
+ "^[0-9-/]+ +\\(?:txn +\\)?[^ [:lower:]]\\($\\| \\)")
+
+(defun beancount-inside-txn-p ()
+ ;; FIXME: The doc doesn't actually say how the legs of a transaction can be
+ ;; layed out. We assume that they all start with some space on the line.
+ (save-excursion
+ (forward-line 0)
+ (while (and (looking-at "[ \t]") (not (bobp)))
+ (forward-line -1))
+ (looking-at beancount-txn-regexp)))
+
+(defun beancount-completion-at-point ()
+ "Return the completion data relevant for the text at point."
+ (let ((bp (buffer-substring (line-beginning-position) (point))))
+ (cond
+ ((string-match "\\`[a-z]*\\'" bp)
+ ;; A directive starting at BOL (hence non-timestamped).
+ (list (line-beginning-position)
+ (save-excursion (skip-chars-forward "a-z") (point))
+ '("pushtag" "poptag")))
+
+ ((string-match
+ (concat "\\`option +\\(\"[a-z_]*\\)?\\'")
+ bp)
+ (list (- (point)
+ (if (match-end 1) (- (match-end 1) (match-beginning 1)) 0))
+ (save-excursion (skip-chars-forward "a-z_")
+ (if (looking-at "\"") (forward-char 1))
+ (point))
+ (mapcar (lambda (s) (concat "\"" s "\"")) beancount-option-names)))
+
+ ((string-match
+ (concat "\\`poptag +\\(#[" beancount-tag-chars "]*\\)?\\'")
+ bp)
+ (list (- (point)
+ (if (match-end 1) (- (match-end 1) (match-beginning 1)) 0))
+ (save-excursion (skip-chars-forward beancount-tag-chars) (point))
+ (save-excursion
+ (let ((opened ()))
+ (while (re-search-backward
+ (concat "^pushtag +\\(#[" beancount-tag-chars "]+\\)")
+ nil t)
+ (push (match-string 1) opened))
+ opened))))
+
+ ((string-match "\\`[0-9-/]+ +\\([[:alpha:]]*\\'\\)" bp)
+ ;; A timestamped directive.
+ (list (- (point) (- (match-end 1) (match-beginning 1)))
+ (save-excursion (skip-chars-forward "[:alpha:]") (point))
+ beancount-timestamped-directive-names))
+
+ ((and (beancount-inside-txn-p)
+ (string-match (concat "\\`[ \t]+\\(["
+ beancount-account-chars "]*\\)\\'")
+ bp))
+ ;; Hopefully, an account name. We don't force the partially-written
+ ;; account name to start with a capital, so that it's possible to use
+ ;; substring completion and also so we can rely on completion to put the
+ ;; right capitalization (thanks to completion-ignore-case).
+ (list (- (point) (- (match-end 1) (match-beginning 1)))
+ (save-excursion (skip-chars-forward beancount-account-chars)
+ (point))
+ #'beancount-account-completion-table))
+
+ ((string-match (concat "\\`[0-9-/]+ +\\("
+ (regexp-opt beancount-timestamped-directive-names)
+ "\\) +\\([" beancount-account-chars "]*\\'\\)")
+ bp)
+ (list (- (point) (- (match-end 2) (match-beginning 2)))
+ (save-excursion (skip-chars-forward beancount-account-chars)
+ (point))
+ (if (equal (match-string 1 bp) "open")
+ (append
+ (mapcar (lambda (c) (concat c ":")) beancount-account-categories)
+ beancount-accounts)
+ #'beancount-account-completion-table)))
+
+ ((string-match (concat "[#^][" beancount-tag-chars "]*\\'") bp)
+ (list (- (point) (- (match-end 0) (match-beginning 0)))
+ (save-excursion (skip-chars-forward beancount-tag-chars) (point))
+ (completion-table-dynamic #'beancount-tags))))))
+
+(defun beancount-hash-keys (hashtable)
+ "Extract all the keys of the given hashtable. Return a sorted list."
+ (let (rlist)
+ (maphash (lambda (k _v) (push k rlist)) hashtable)
+ (sort rlist 'string<)))
+
+(defun beancount-get-accounts ()
+ "Heuristically obtain a list of all the accounts used in all the postings.
+We ignore patterns seen the line 'exclude-line'. If ALL is non-nil, look
+for account names in postings as well (default is to look at the @defaccount
+declarations only."
+ (let ((accounts (make-hash-table :test 'equal)))
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward beancount-account-regexp nil t)
+ (puthash (match-string-no-properties 0) nil accounts)))
+ (sort (beancount-hash-keys accounts) 'string<)))
+
+(defcustom beancount-use-ido t
+ "If non-nil, use ido-style completion rather than the standard completion."
+ :type 'boolean)
+
+(defun beancount-account-completion-table (string pred action)
+ (if (eq action 'metadata)
+ '(metadata (category . beancount-account))
+ (complete-with-action action beancount-accounts string pred)))
+
+;; Default to substring completion for beancount accounts.
+(defconst beancount--completion-overrides
+ '(beancount-account (styles basic partial-completion substring)))
+(cond
+ ((boundp 'completion-category-defaults)
+ (add-to-list 'completion-category-defaults beancount--completion-overrides))
+ ((and (boundp 'completion-category-overrides)
+ (not (assq 'beancount-account completion-category-overrides)))
+ (push beancount--completion-overrides completion-category-overrides)))
+
+(defun beancount-insert-account (account-name)
+ "Insert one of the valid account names in this file.
+Uses ido niceness according to `beancount-use-ido'."
+ (interactive
+ (list
+ (if beancount-use-ido
+ ;; `ido-completing-read' is too dumb to understand functional
+ ;; completion tables!
+ (ido-completing-read "Account: " beancount-accounts
+ nil nil (thing-at-point 'word))
+ (completing-read "Account: " #'beancount-account-completion-table
+ nil t (thing-at-point 'word)))))
+ (let ((bounds (bounds-of-thing-at-point 'word)))
+ (when bounds
+ (delete-region (car bounds) (cdr bounds))))
+ (insert account-name))
+
+
+(defun beancount-transaction-set-flag ()
+ (interactive)
+ (save-excursion
+ (backward-paragraph 1)
+ (forward-line 1)
+ (while (search-forward "!" (line-end-position) t)
+ (replace-match "*"))))
+
+
+(defmacro beancount-for-line-in-region (begin end &rest exprs)
+ "Iterate over each line in region until an empty line is encountered."
+ `(save-excursion
+ (let ((end-marker (copy-marker ,end)))
+ (goto-char ,begin)
+ (beginning-of-line)
+ (while (and (not (eobp)) (< (point) end-marker))
+ (beginning-of-line)
+ (progn ,@exprs)
+ (forward-line 1)
+ ))))
+
+
+(defun beancount-max-accounts-width (begin end)
+ "Return the minimum widths of a list of account names on a list
+of lines. Initial whitespace is ignored."
+ (let* (widths)
+ (beancount-for-line-in-region
+ begin end
+ (let* ((line (thing-at-point 'line)))
+ (when (string-match
+ (concat "^[ \t]*\\(" beancount-account-regexp "\\)") line)
+ (push (length (match-string 1 line)) widths))))
+ (apply 'max widths)))
+
+
+(defun beancount-align-numbers (begin end &optional requested-currency-column)
+ "Align all numbers in the given region. CURRENCY-COLUMN is the character
+at which to align the beginning of the amount's currency. If not specified, use
+the smallest columns that will align all the numbers. With a prefix argument,
+align with the fill-column."
+ (interactive "r")
+
+ ;; With a prefix argument, align with the fill-column.
+ (when current-prefix-arg
+ (setq requested-currency-column fill-column))
+
+ ;; Loop once in the region to find the length of the longest string before the
+ ;; number.
+ (let (prefix-widths
+ number-widths
+ (number-padding " "))
+ (beancount-for-line-in-region
+ begin end
+ (let ((line (thing-at-point 'line)))
+ (when (string-match (concat "\\(.*?\\)"
+ "[ \t]+"
+ "\\(" beancount-number-regexp "\\)"
+ "[ \t]+"
+ beancount-currency-regexp)
+ line)
+ (push (length (match-string 1 line)) prefix-widths)
+ (push (length (match-string 2 line)) number-widths)
+ )))
+
+ (when prefix-widths
+ ;; Loop again to make the adjustments to the numbers.
+ (let* ((number-width (apply 'max number-widths))
+ (number-format (format "%%%ss" number-width))
+ ;; Compute rightmost column of prefix.
+ (max-prefix-width (apply 'max prefix-widths))
+ (max-prefix-width
+ (if requested-currency-column
+ (max (- requested-currency-column (length number-padding) number-width 1)
+ max-prefix-width)
+ max-prefix-width))
+ (prefix-format (format "%%-%ss" max-prefix-width))
+ )
+
+ (beancount-for-line-in-region
+ begin end
+ (let ((line (thing-at-point 'line)))
+ (when (string-match (concat "^\\([^\"]*?\\)"
+ "[ \t]+"
+ "\\(" beancount-number-regexp "\\)"
+ "[ \t]+"
+ "\\(.*\\)$")
+ line)
+ (delete-region (line-beginning-position) (line-end-position))
+ (let* ((prefix (match-string 1 line))
+ (number (match-string 2 line))
+ (rest (match-string 3 line)) )
+ (insert (format prefix-format prefix))
+ (insert number-padding)
+ (insert (format number-format number))
+ (insert " ")
+ (insert rest)))))))))
+
+
+(defun beancount-align-to-previous-number ()
+ "Align postings under the point's paragraph.
+This function looks for a posting in the previous transaction to
+determine the column at which to align the transaction, or otherwise
+the fill column, and align all the postings of this transaction to
+this column."
+ (interactive)
+ (let* ((begin (save-excursion
+ (beancount-beginning-of-directive)
+ (point)))
+ (end (save-excursion
+ (goto-char begin)
+ (forward-paragraph 1)
+ (point)))
+ (currency-column (or (beancount-find-previous-alignment-column)
+ fill-column)))
+ (beancount-align-numbers begin end currency-column)))
+
+
+(defun beancount-beginning-of-directive ()
+ "Move point to the beginning of the enclosed or preceding directive."
+ (beginning-of-line)
+ (while (and (> (point) (point-min))
+ (not (looking-at
+ "[0-9][0-9][0-9][0-9][\-/][0-9][0-9][\-/][0-9][0-9]")))
+ (forward-line -1)))
+
+
+(defun beancount-find-previous-alignment-column ()
+ "Find the preceding column to align amounts with.
+This is used to align transactions at the same column as that of
+the previous transaction in the file. This function merely finds
+what that column is and returns it (an integer)."
+ ;; Go hunting for the last column with a suitable posting.
+ (let (column)
+ (save-excursion
+ ;; Go to the beginning of the enclosing directive.
+ (beancount-beginning-of-directive)
+ (forward-line -1)
+
+ ;; Find the last posting with an amount and a currency on it.
+ (let ((posting-regexp (concat
+ "\\s-+"
+ beancount-account-regexp "\\s-+"
+ beancount-number-regexp "\\s-+"
+ "\\(" beancount-currency-regexp "\\)"))
+ (balance-regexp (concat
+ beancount-date-regexp "\\s-+"
+ "balance" "\\s-+"
+ beancount-account-regexp "\\s-+"
+ beancount-number-regexp "\\s-+"
+ "\\(" beancount-currency-regexp "\\)")))
+ (while (and (> (point) (point-min))
+ (not (or (looking-at posting-regexp)
+ (looking-at balance-regexp))))
+ (forward-line -1))
+ (when (or (looking-at posting-regexp)
+ (looking-at balance-regexp))
+ (setq column (- (match-beginning 1) (point))))
+ ))
+ column))
+
+(defvar beancount-install-dir nil
+ "Directory in which Beancount's source is located.
+Only useful if you have not installed Beancount properly in your PATH.")
+
+(defvar beancount-check-program "bean-check"
+ "Program to run to run just the parser and validator on an
+ input file.")
+
+(defvar compilation-read-command)
+
+(defun beancount--run (prog &rest args)
+ (let ((process-environment
+ (if beancount-install-dir
+ `(,(concat "PYTHONPATH=" beancount-install-dir)
+ ,(concat "PATH="
+ (expand-file-name "bin" beancount-install-dir)
+ ":"
+ (getenv "PATH"))
+ ,@process-environment)
+ process-environment))
+ (compile-command (mapconcat (lambda (arg)
+ (if (stringp arg)
+ (shell-quote-argument arg) ""))
+ (cons prog args)
+ " ")))
+ (call-interactively 'compile)))
+
+(defun beancount-check ()
+ "Run `beancount-check-program'."
+ (interactive)
+ (let ((compilation-read-command nil))
+ (beancount--run beancount-check-program
+ (file-relative-name buffer-file-name))))
+
+(defvar beancount-query-program "bean-query"
+ "Program to run to run just the parser and validator on an
+ input file.")
+
+(defun beancount-query ()
+ "Run bean-query."
+ (interactive)
+ ;; Don't let-bind compilation-read-command this time, since the default
+ ;; command is incomplete.
+ (beancount--run beancount-query-program
+ (file-relative-name buffer-file-name) t))
+
+
+(defvar beancount-doctor-program "bean-doctor"
+ "Program to run the doctor commands.")
+
+(defun beancount-context ()
+ "Get the \"context\" from `beancount-doctor-program'."
+ (interactive)
+ (let ((compilation-read-command nil))
+ (beancount--run beancount-doctor-program "context"
+ (file-relative-name buffer-file-name)
+ (number-to-string (line-number-at-pos)))))
+
+
+(defun beancount-linked ()
+ "Get the \"linked\" info from `beancount-doctor-program'."
+ (interactive)
+ (let ((compilation-read-command nil))
+ (beancount--run beancount-doctor-program "linked"
+ (file-relative-name buffer-file-name)
+ (number-to-string (line-number-at-pos)))))
+
+(defvar beancount-price-program "bean-price"
+ "Program to run the price fetching commands.")
+
+(defun beancount-insert-prices ()
+ "Run bean-price on the current file and insert the output inline."
+ (interactive)
+ (call-process beancount-price-program nil t nil
+ (file-relative-name buffer-file-name)))
+
+
+(provide 'beancount)
+;;; beancount.el ends here
diff --git a/.emacs.local.d/general.el b/.emacs.local.d/general.el
new file mode 100644
index 0000000..558490b
--- /dev/null
+++ b/.emacs.local.d/general.el
@@ -0,0 +1,165 @@
+(message nil);; Debian packages: elpa-use-package elpa-fill-column-indicator
+
+;; This is only needed once, near the top of the file
+(eval-when-compile
+ (require 'use-package))
+
+;; Added by Package.el. This must come before configurations of
+;; installed packages. Don't delete this line. If you don't want it,
+;; just comment it out by adding a semicolon to the start of the line.
+;; You may delete these explanatory comments.
+(package-initialize)
+
+(add-to-list 'package-archives
+ '("melpa-stable" . "https://stable.melpa.org/packages/") t)
+
+(add-to-list 'load-path "~/.emacs.local.d/elisp")
+
+;; ------
+;; Require misc stuff
+;; ------
+(require 'fill-column-indicator)
+
+;; Place backups in ~/.backups/ directory, like a civilized program.
+;; ------
+(if (file-directory-p "~/.backup")
+ (setq backup-directory-alist '(("." . "~/.backup")))
+ (message "Directory does not exist: ~/.backup"))
+
+(filesets-init)
+
+(setq backup-by-copying t ; Don't delink hardlinks
+ delete-old-versions t ; Clean up the backups
+ version-control t ; Use version numbers on backups,
+ kept-new-versions 3 ; keep some new versions
+ kept-old-versions 2) ; and some old ones, too
+
+;;; backup/autosave - old studd, just in case
+;; (defvar backup-dir (expand-file-name "~/.emacs.d/backup/"))
+;; (defvar autosave-dir (expand-file-name "~/.emacs.d/autosave/"))
+;; (setq backup-directory-alist (list (cons ".*" backup-dir)))
+;; (setq auto-save-list-file-prefix autosave-dir)
+;; (setq auto-save-file-name-transforms `((".*" ,autosave-dir t)))
+
+
+;; ---------
+;; Generic keybindings
+;; ---------
+(global-set-key (kbd "C-c d") 'diff-buffer-with-file)
+(global-set-key (kbd "C-c R") 'revert-buffer)
+
+(global-set-key (kbd "C-<next>") 'next-buffer)
+(global-set-key (kbd "C-<prior>") 'previous-buffer)
+
+;; compile
+(global-set-key [f12] 'compile)
+
+;; ------
+;; General config BS
+;; ------
+
+(setq fill-column 79)
+
+;; Make sure that pressing middle mouse button pastes right at point,
+;; not where the mouse cursor is.
+(setq mouse-yank-at-point t)
+(setq column-number-mode 1)
+(setq line-number-mode 1)
+(setq-default indent-tabs-mode nil)
+(setq-default tab-width 4)
+
+(setq-default c-basic-offset 4)
+
+;; ------
+;; Initialize some things
+;; ------
+
+
+(setenv "TMPDIR" (concat (getenv "HOME") "/tmp"))
+(server-start)
+
+;; ------
+;; Terminal / window specific stuff
+;; ------
+
+;; Don't minimize my emacs! Honestly wtf
+(when window-system
+ (progn
+ (global-unset-key (kbd "C-z"))
+ (setq scroll-bar-mode nil)
+ (tool-bar-mode nil)
+ (menu-bar-mode nil)))
+
+;; ------
+;; Helper for compilation.
+;; ------
+;; Close the compilation window if there was no error at all.
+(defun compilation-exit-autoclose (status code msg)
+ ;; If M-x compile exists with a 0
+ (when (and (eq status 'exit) (zerop code))
+ ;; then bury the *compilation* buffer, so that C-x b doesn't go there
+ (bury-buffer)
+ ;; and delete the *compilation* window
+ (delete-window (get-buffer-window (get-buffer "*compilation*"))))
+ ;; Always return the anticipated result of compilation-exit-message-function
+ (cons msg code))
+;; Specify my function (maybe I should have done a lambda function)
+(setq compilation-exit-message-function 'compilation-exit-autoclose)
+(setq compilation-read-command nil)
+
+;; Themes
+(add-to-list 'custom-theme-load-path "~/.emacs.d/themes/")
+
+;; Remove scratch message
+(setq initial-scratch-message "")
+
+;; Turn of scroll bar
+(when (featurep 'scroll-bar) (scroll-bar-mode -1))
+
+;; Ask y or n instead of yes or no
+(defalias 'yes-or-no-p 'y-or-n-p)
+
+;; Fancier buffer selection
+(global-set-key (kbd "C-x C-b") 'bs-show)
+
+;;; Stefan Monnier <foo at acm.org>. It is the opposite of fill-paragraph
+(defun unfill-paragraph (&optional region)
+ "Takes a multi-line paragraph and makes it into a single line of text."
+ (interactive (progn (barf-if-buffer-read-only) '(t)))
+ (let ((fill-column (point-max))
+ ;; This would override `fill-column' if it's an integer.
+ (emacs-lisp-docstring-fill-column t))
+ (fill-paragraph nil region)))
+
+;; Handy key definition
+(define-key global-map "\M-Q" 'unfill-paragraph)
+
+(setq custom-file "~/.emacs.d/custom.el")
+(load custom-file 'noerror)
+
+(menu-bar-mode -1)
+(tool-bar-mode -1)
+
+
+;; Start maximized
+(add-to-list 'default-frame-alist '(fullscreen . maximized))
+
+;; Only flash the mode line
+(setq ring-bell-function
+ (lambda ()
+ (let ((orig-fg (face-foreground 'mode-line)))
+ (set-face-foreground 'mode-line "#F2804F")
+ (run-with-idle-timer 0.1 nil
+ (lambda (fg) (set-face-foreground 'mode-line fg))
+ orig-fg))))
+
+;; Highlight parens
+(setq show-paren-delay 0)
+(show-paren-mode 1)
+
+;; Use default brwoser
+;;(setq browse-url-browser-function 'browse-url-generic browse-url-generic-program "chromium")
+
+;; Mutt support.
+(setq auto-mode-alist (append '((".*tmp/mutt.*" . mail-mode)) auto-mode-alist))
+(setq auto-mode-alist (append '((".*tmp/neomutt.*" . mail-mode)) auto-mode-alist))
diff --git a/.emacs.local.d/modes/anzu.el b/.emacs.local.d/modes/anzu.el
new file mode 100644
index 0000000..5b1cc64
--- /dev/null
+++ b/.emacs.local.d/modes/anzu.el
@@ -0,0 +1,8 @@
+;; Debian packages: elpa-anzu
+
+(use-package anzu
+ :ensure t
+ :bind (("M-%" . anzu-query-replace)
+ ("C-M-%" . anzu-query-replace-regexp))
+ :config
+ (global-anzu-mode))
diff --git a/.emacs.local.d/modes/auto-complete.el b/.emacs.local.d/modes/auto-complete.el
new file mode 100644
index 0000000..c536b37
--- /dev/null
+++ b/.emacs.local.d/modes/auto-complete.el
@@ -0,0 +1,13 @@
+;; auto-complete
+(require 'auto-complete)
+(require 'auto-complete-config)
+
+;; Debian packages: auto-complete-el
+
+(add-to-list 'ac-dictionary-directories "/usr/share/auto-complete/dict/")
+
+(ac-config-default)
+(defun auto-complete-mode-maybe ()
+ "No maybe for you. Only AC!"
+ (unless (minibufferp (current-buffer))
+ (auto-complete-mode 1)))
diff --git a/.emacs.local.d/modes/auto-fill.el b/.emacs.local.d/modes/auto-fill.el
new file mode 100644
index 0000000..dad2831
--- /dev/null
+++ b/.emacs.local.d/modes/auto-fill.el
@@ -0,0 +1,4 @@
+;; auto-fill mode
+(add-hook 'text-mode-hook 'turn-on-auto-fill)
+(global-set-key (kbd "C-c q") 'auto-fill-mode)
+
diff --git a/.emacs.local.d/modes/beancount.el b/.emacs.local.d/modes/beancount.el
new file mode 100644
index 0000000..ca5c7c6
--- /dev/null
+++ b/.emacs.local.d/modes/beancount.el
@@ -0,0 +1,5 @@
+(require 'beancount)
+
+;; Automatically open .beancount files in beancount-mode.
+(add-to-list 'auto-mode-alist '("\\.beancount$" . beancount-mode))
+
diff --git a/.emacs.local.d/modes/company.el b/.emacs.local.d/modes/company.el
new file mode 100644
index 0000000..dc80b33
--- /dev/null
+++ b/.emacs.local.d/modes/company.el
@@ -0,0 +1,29 @@
+;; Debian packages: elpa-company
+;; Elpa packages: company-quickhelp
+
+(use-package company
+ :ensure t
+ :defer t
+ :init (global-company-mode)
+ :config
+ (progn
+ ;; Use Company for completion
+ (bind-key [remap completion-at-point] #'company-complete company-mode-map)
+
+ (setq company-tooltip-align-annotations t
+ ;; Easy navigation to candidates with M-<n>
+ company-show-numbers t)
+ (setq company-dabbrev-downcase nil))
+ :diminish company-mode)
+
+(use-package company-quickhelp ; Documentation popups for Company
+ :ensure t
+ :defer t
+ :init (add-hook 'global-company-mode-hook #'company-quickhelp-mode))
+
+(use-package company-go
+ :ensure t
+ :defer t
+ :init
+ (with-eval-after-load 'company
+ (add-to-list 'company-backends 'company-go)))
diff --git a/.emacs.local.d/modes/dashboard.el b/.emacs.local.d/modes/dashboard.el
new file mode 100644
index 0000000..1dc81d3
--- /dev/null
+++ b/.emacs.local.d/modes/dashboard.el
@@ -0,0 +1,11 @@
+;; Elpa packages: dashboard
+
+(use-package dashboard
+ :ensure t
+ :diminish dashboard-mode
+ :config
+ (setq dashboard-banner-logo-title "Only the educated are free.")
+ (setq dashboard-items '((recents . 10)
+ (projects . 5)
+ (bookmarks . 10)))
+ (dashboard-setup-startup-hook))
diff --git a/.emacs.local.d/modes/doom-modeline.el b/.emacs.local.d/modes/doom-modeline.el
new file mode 100644
index 0000000..c3b6cc2
--- /dev/null
+++ b/.emacs.local.d/modes/doom-modeline.el
@@ -0,0 +1,5 @@
+;; Elpa packages: doom-modeline
+
+(use-package doom-modeline
+ :ensure t
+ :hook (after-init . doom-modeline-mode))
diff --git a/.emacs.local.d/modes/ecb.el b/.emacs.local.d/modes/ecb.el
new file mode 100644
index 0000000..146a75f
--- /dev/null
+++ b/.emacs.local.d/modes/ecb.el
@@ -0,0 +1,21 @@
+;;; activate ecb
+(require 'ecb)
+(require 'ecb-autoloads)
+
+(setq ecb-layout-name "left13")
+(setq ecb-show-sources-in-directories-buffer 'always)
+
+;;; activate and deactivate ecb
+(global-set-key (kbd "C-x C-;") 'ecb-activate)
+(global-set-key (kbd "C-x C-'") 'ecb-deactivate)
+;;; show/hide ecb window
+(global-set-key (kbd "C-;") 'ecb-show-ecb-windows)
+(global-set-key (kbd "C-'") 'ecb-hide-ecb-windows)
+;;; quick navigation between ecb windows
+(global-set-key (kbd "C-)") 'ecb-goto-window-edit1)
+(global-set-key (kbd "C-!") 'ecb-goto-window-directories)
+(global-set-key (kbd "C-@") 'ecb-goto-window-sources)
+(global-set-key (kbd "C-#") 'ecb-goto-window-methods)
+(global-set-key (kbd "C-$") 'ecb-goto-window-compilation)
+
+
diff --git a/.emacs.local.d/modes/eshell.el b/.emacs.local.d/modes/eshell.el
new file mode 100644
index 0000000..c9be093
--- /dev/null
+++ b/.emacs.local.d/modes/eshell.el
@@ -0,0 +1,85 @@
+(setq eshell-history-size 512)
+(setq eshell-prompt-regexp "^.*> ")
+
+(require 'em-hist) ; So the history vars are defined
+(if (boundp 'eshell-save-history-on-exit)
+ (setq eshell-save-history-on-exit t)) ; Don't ask, just save
+(if (boundp 'eshell-ask-to-save-history)
+ (setq eshell-ask-to-save-history 'always)) ; For older(?) version
+
+(defun eshell/ef (fname-regexp &optional dir)
+ (ef fname-regexp (or dir default-directory)))
+
+
+;;; ---- path manipulation
+
+(defun pwd-repl-home (pwd)
+ (interactive)
+ (let* ((home (expand-file-name (getenv "HOME")))
+ (home-len (length home)))
+ (if (and
+ (>= (length pwd) home-len)
+ (equal home (substring pwd 0 home-len)))
+ (concat "~" (substring pwd home-len))
+ pwd)))
+
+(defun curr-dir-git-branch-string (pwd)
+ "Returns current git branch as a string, or the empty string if
+PWD is not in a git repo (or the git command is not found)."
+ (interactive)
+ (when (and (eshell-search-path "git")
+ (locate-dominating-file pwd ".git"))
+ (let ((git-output (shell-command-to-string (concat "git branch | grep '\\*' | sed -e 's/^\\* //'"))))
+ (concat "[g:"
+ (if (> (length git-output) 0)
+ (substring git-output 0 -1)
+ "(no branch)")
+ "] "))))
+
+(defun curr-dir-svn-string (pwd)
+ (interactive)
+ (when (and (eshell-search-path "svn")
+ (locate-dominating-file pwd ".svn"))
+ (concat "[s:"
+ (cond ((string-match-p "/trunk\\(/.*\\)?" pwd)
+ "trunk")
+ ((string-match "/branches/\\([^/]+\\)\\(/.*\\)?" pwd)
+ (match-string 1 pwd))
+ (t
+ "(no branch)"))
+ "] ")))
+
+(setq eshell-prompt-function
+ (lambda ()
+ (concat
+ (or (curr-dir-git-branch-string (eshell/pwd))
+ (curr-dir-svn-string (eshell/pwd)))
+ ((lambda (p-lst)
+ (if (> (length p-lst) 3)
+ (concat
+ (mapconcat (lambda (elm) (if (zerop (length elm)) ""
+ (substring elm 0 1)))
+ (butlast p-lst 3)
+ "/")
+ "/"
+ (mapconcat (lambda (elm) elm)
+ (last p-lst 3)
+ "/"))
+ (mapconcat (lambda (elm) elm)
+ p-lst
+ "/")))
+ (split-string (pwd-repl-home (eshell/pwd)) "/"))
+ "> ")))
+
+;; ; From http://www.emacswiki.org/cgi-bin/wiki.pl/EshellWThirtyTwo
+;; ; Return nil, otherwise you'll see the return from w32-shell-execute
+;; (defun eshell/open (file)
+;; "Invoke (w32-shell-execute \"Open\" FILE) and substitute slashes for
+;; backslashes"
+;; (w32-shell-execute "Open" (substitute ?\\ ?/ (expand-file-name file)))
+;; nil)
+
+(add-hook 'eshell-mode-hook
+ (lambda ()
+ (local-set-key "\C-c\C-q" 'eshell-kill-process)
+ (local-set-key "\C-c\C-k" 'compile)))
diff --git a/.emacs.local.d/modes/flycheck.el b/.emacs.local.d/modes/flycheck.el
new file mode 100644
index 0000000..45571e2
--- /dev/null
+++ b/.emacs.local.d/modes/flycheck.el
@@ -0,0 +1,12 @@
+;; Debian-packages: elpa-flycheck python3-proselint
+
+(flycheck-define-checker proselint
+ "A linter for prose."
+ :command ("proselint" source-inplace)
+ :error-patterns
+ ((warning line-start (file-name) ":" line ":" column ": "
+ (id (one-or-more (not (any " "))))
+ (message) line-end))
+ :modes (text-mode markdown-mode gfm-mode org-mode))
+
+(add-to-list 'flycheck-checkers 'proselint)
diff --git a/.emacs.local.d/modes/flyspell.el b/.emacs.local.d/modes/flyspell.el
new file mode 100644
index 0000000..8cf27b8
--- /dev/null
+++ b/.emacs.local.d/modes/flyspell.el
@@ -0,0 +1,12 @@
+(defcustom flyspell-delayed-commands nil
+ "List of commands that are \"delayed\" for Flyspell mode.
+After these commands, Flyspell checking is delayed for a short time,
+whose length is specified by `flyspell-delay'."
+ :group 'flyspell
+ :type '(repeat (symbol)))
+
+(setq ispell-dictionary "en")
+(setq flyspell-default-dictionary "en")
+
+(setq flyspell-issue-welcome-flag nil)
+(setq-default ispell-list-command "list")
diff --git a/.emacs.local.d/modes/go-lang.el b/.emacs.local.d/modes/go-lang.el
new file mode 100644
index 0000000..5d43e16
--- /dev/null
+++ b/.emacs.local.d/modes/go-lang.el
@@ -0,0 +1,23 @@
+;; Debian packages: elpa-go-mode
+;; Elpa packages: go-eldoc
+
+(use-package go-mode
+ :ensure t
+ :init
+ (progn
+ (setq gofmt-command "goimports")
+ (add-hook 'before-save-hook 'gofmt-before-save)
+ (bind-key [remap find-tag] #'godef-jump))
+ :config
+ (add-hook 'go-mode-hook 'electric-pair-mode)
+ (add-hook 'go-mode-hook 'my-go-mode-hook))
+
+(use-package go-eldoc
+ :ensure t
+ :defer
+ :init
+ (add-hook 'go-mode-hook 'go-eldoc-setup))
+
+;; Define function to call when go-mode loads
+(defun my-go-mode-hook ()
+ (set 'compile-command "go build -v && go test -v && go vet"))
diff --git a/.emacs.local.d/modes/helm.el b/.emacs.local.d/modes/helm.el
new file mode 100644
index 0000000..55a4c19
--- /dev/null
+++ b/.emacs.local.d/modes/helm.el
@@ -0,0 +1,22 @@
+(use-package helm
+ :bind (("M-x" . helm-M-x)
+ ("M-<f5>" . helm-find-files)
+ ([f10] . helm-buffers-list)
+ ([S-f10] . helm-recentf))
+ :config
+ (setq helm-mode-fuzzy-match t)
+ (setq helm-completion-in-region-fuzzy-match t)
+ )
+
+
+(setq helm-M-x-fuzzy-match t
+ helm-bookmark-show-location t
+ helm-buffers-fuzzy-matching t
+ helm-completion-in-region-fuzzy-match t
+ helm-file-cache-fuzzy-match t
+ helm-imenu-fuzzy-match t
+ helm-mode-fuzzy-match t
+ helm-locate-fuzzy-match t
+ helm-quick-update t
+ helm-recentf-fuzzy-match t
+ helm-semantic-fuzzy-match t)
diff --git a/.emacs.local.d/modes/ibuffer.el b/.emacs.local.d/modes/ibuffer.el
new file mode 100644
index 0000000..d5198d8
--- /dev/null
+++ b/.emacs.local.d/modes/ibuffer.el
@@ -0,0 +1,35 @@
+;; Debian packages: elpa-ibuffer-vc
+
+(use-package ibuffer ; Better buffer list
+ :bind (([remap list-buffers] . ibuffer))
+ ;; Show VC Status in ibuffer
+ :config (setq ibuffer-formats
+ '((mark modified read-only vc-status-mini " "
+ (name 18 18 :left :elide)
+ " "
+ (size 9 -1 :right)
+ " "
+ (mode 16 16 :left :elide)
+ " "
+ (vc-status 16 16 :left)
+ " "
+ filename-and-process)
+ (mark modified read-only " "
+ (name 18 18 :left :elide)
+ " "
+ (size 9 -1 :right)
+ " "
+ (mode 16 16 :left :elide)
+ " " filename-and-process)
+ (mark " "
+ (name 16 -1)
+ " " filename))))
+
+(use-package ibuffer-vc ; Group buffers by VC project and status
+ :ensure t
+ :defer t
+ :init (add-hook 'ibuffer-hook
+ (lambda ()
+ (ibuffer-vc-set-filter-groups-by-vc-root)
+ (unless (eq ibuffer-sorting-mode 'alphabetic)
+ (ibuffer-do-sort-by-alphabetic)))))
diff --git a/.emacs.local.d/modes/ido.el b/.emacs.local.d/modes/ido.el
new file mode 100644
index 0000000..8b475f9
--- /dev/null
+++ b/.emacs.local.d/modes/ido.el
@@ -0,0 +1,19 @@
+;; Debian packages: elpa-flx-ido elpa-ido-vertical-mode elpa-ido-completing-read+
+
+(require 'ido)
+(ido-mode t)
+
+(require 'flx-ido)
+(ido-mode 1)
+(ido-everywhere 1)
+(flx-ido-mode 1)
+;; disable ido faces to see flx highlights.
+(setq ido-enable-flex-matching t)
+(setq ido-use-faces nil)
+
+(require 'ido-vertical-mode)
+(ido-vertical-mode 1)
+(setq ido-vertical-define-keys 'C-n-and-C-p-only)
+
+(require 'ido-completing-read+)
+
diff --git a/.emacs.local.d/modes/imenu.el b/.emacs.local.d/modes/imenu.el
new file mode 100644
index 0000000..1a2b29b
--- /dev/null
+++ b/.emacs.local.d/modes/imenu.el
@@ -0,0 +1,12 @@
+;; Debian packages: elpa-imenu-list
+(use-package imenu-list
+ :ensure t
+ :bind ("C-." . imenu-list-minor-mode)
+ :config
+ (setq imenu-list-focus-after-activation t)
+ (setq imenu-list-size 0.2)
+ (setq imenu-list-position 'left)
+ (add-hook 'go-mode-hook #'imenu-list-minor-mode))
+
+
+
diff --git a/.emacs.local.d/modes/latex.el b/.emacs.local.d/modes/latex.el
new file mode 100644
index 0000000..de4de1f
--- /dev/null
+++ b/.emacs.local.d/modes/latex.el
@@ -0,0 +1,9 @@
+(add-hook 'latex-mode-hook 'flyspell-mode)
+(setq TeX-PDF-mode t)
+
+(defun pdfevince ()
+ (add-to-list 'TeX-output-view-style
+ '("^pdf$" "." "evince %o %(outpage)")))
+
+(add-hook 'LaTeX-mode-hook 'pdfevince t) ; AUCTeX LaTeX mode
+
diff --git a/.emacs.local.d/modes/linum.el b/.emacs.local.d/modes/linum.el
new file mode 100644
index 0000000..8df9986
--- /dev/null
+++ b/.emacs.local.d/modes/linum.el
@@ -0,0 +1,3 @@
+;(require 'linum-relative)
+
+(add-hook 'go-mode-hook #'linum-mode)
diff --git a/.emacs.local.d/modes/magit.el b/.emacs.local.d/modes/magit.el
new file mode 100644
index 0000000..e1ca63f
--- /dev/null
+++ b/.emacs.local.d/modes/magit.el
@@ -0,0 +1,15 @@
+;; Debian packages: elpa-magit elpa-magithub
+
+(use-package magit
+ :ensure t
+ :defer t
+ :bind (("C-x g" . magit-status))
+ :config
+ (progn
+ (defun inkel/magit-log-edit-mode-hook ()
+ (flyspell-mode t)
+ (turn-on-auto-fill))
+ (defadvice magit-status (around magit-fullscreen activate)
+ (window-configuration-to-register :magit-fullscreen)
+ ad-do-it
+ (delete-other-windows))))
diff --git a/.emacs.local.d/modes/mail-mode.el b/.emacs.local.d/modes/mail-mode.el
new file mode 100644
index 0000000..c10cd81
--- /dev/null
+++ b/.emacs.local.d/modes/mail-mode.el
@@ -0,0 +1,10 @@
+(add-to-list 'auto-mode-alist '("/mutt" . mail-mode))
+(add-hook 'mail-mode-hook
+ (lambda ()
+ (font-lock-add-keywords nil
+ '(("^[ \t]*>[ \t]*>[ \t]*>.*$"
+ (0 'compilation-error))
+ ("^[ \t]*>[ \t]*>.*$"
+ (0 'compilation-column-number))
+ ("^[ \t]*>.*$"
+ (0 'comint-highlight-prompt))))))
diff --git a/.emacs.local.d/modes/markdown.el b/.emacs.local.d/modes/markdown.el
new file mode 100644
index 0000000..f035509
--- /dev/null
+++ b/.emacs.local.d/modes/markdown.el
@@ -0,0 +1,5 @@
+(autoload 'markdown-mode "markdown-mode.el"
+ "Major mode for editing Markdown files" t)
+
+(setq auto-mode-alist
+ (cons '("\\.mdwn" . markdown-mode) auto-mode-alist))
diff --git a/.emacs.local.d/modes/mu4e.el b/.emacs.local.d/modes/mu4e.el
new file mode 100644
index 0000000..da8b7c8
--- /dev/null
+++ b/.emacs.local.d/modes/mu4e.el
@@ -0,0 +1,61 @@
+(require 'mu4e)
+
+;; sending mail
+(setq message-send-mail-function 'message-send-mail-with-sendmail
+ sendmail-program "/home/lur/bin/te-msmtp"
+ user-mail-address "raul@thousandeyes.com"
+ user-full-name "Raúl Benencia")
+
+(setq mu4e-user-mail-address-list (list "raul@thousandeyes.com"))
+
+(setq message-kill-buffer-on-exit t)
+;; Use fancy chars
+(setq mu4e-use-fancy-chars t)
+;; don't save message to Sent Messages, Gmail/IMAP takes care of this
+(setq mu4e-sent-messages-behavior 'delete)
+(setq mu4e-update-interval 60) ;; update every 5 minutes
+
+;; use 'fancy' non-ascii characters in various places in mu4e
+;;(setq mu4e-use-fancy-chars t)
+
+(setq relevant-maildirs " (maildir:/INBOX OR maildir:/jira OR maildir:/news OR maildir:/git)")
+(mu4e-alert-enable-notifications)
+(mu4e-alert-set-default-style 'libnotify)
+(setq mu4e-alert-interesting-mail-query
+ (concat "flag:unread"
+ " AND NOT flag:trashed"
+ " AND" relevant-maildirs))
+
+(mu4e-alert-set-default-style 'libnotify)
+;;(add-hook 'after-init-hook #'mu4e-alert-enable-notifications)
+(add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display)
+
+(setq mu4e-bookmarks
+ `(,(make-mu4e-bookmark
+ :name "INBOX"
+ :query "maildir:/INBOX"
+ :key ?i)
+ ,(make-mu4e-bookmark
+ :name "Unread messages"
+ :query (concat "flag:unread AND NOT flag:trashed AND" relevant-maildirs)
+ :key ?u)
+ ,(make-mu4e-bookmark
+ :name "Today's messages"
+ :query (concat "date:today..now AND" relevant-maildirs)
+ :key ?t)
+ ,(make-mu4e-bookmark
+ :name "Last 7 days"
+ :query (concat "date:7d..now AND" relevant-maildirs)
+ :key ?w)
+ ,(make-mu4e-bookmark
+ :name "Today's unread logs "
+ :query (concat "date:today..now flag:unread AND NOT" relevant-maildirs)
+ :key ?l)
+ ,(make-mu4e-bookmark
+ :name "Today's logs "
+ :query (concat "date:today..now AND NOT maildir:/fim AND NOT" relevant-maildirs)
+ :key ?l))
+)
+
+;; (require 'mu4e-maildirs-extension)
+;; (mu4e-maildirs-extension)
diff --git a/.emacs.local.d/modes/notmuch.el b/.emacs.local.d/modes/notmuch.el
new file mode 100644
index 0000000..f5096c8
--- /dev/null
+++ b/.emacs.local.d/modes/notmuch.el
@@ -0,0 +1,105 @@
+;; --------
+;; notmuch mode
+;; --------
+(require 'notmuch)
+
+;; This should be upstream
+(require 'notmuch-show)
+(require 'notmuch-tag)
+(defun notmuch-tree-show-message-in ()
+ "Show the current message (in split-pane)."
+ (interactive)
+ (let ((id (notmuch-tree-get-message-id))
+ (inhibit-read-only t)
+ buffer)
+ (when id
+ ;; We close and reopen the window to kill off un-needed buffers
+ ;; this might cause flickering but seems ok.
+ (notmuch-tree-close-message-window)
+ (setq notmuch-tree-message-window
+ (split-window-vertically (/ (window-height) 4)))
+ (with-selected-window notmuch-tree-message-window
+ ;; Since we are only displaying one message do not indent.
+ (let ((notmuch-show-indent-messages-width 0)
+ (notmuch-show-only-matching-messages t))
+ (setq buffer (notmuch-show id))))
+ ;; We need the `let' as notmuch-tree-message-window is buffer local.
+ (let ((window notmuch-tree-message-window))
+ (with-current-buffer buffer
+ (setq notmuch-tree-message-window window)
+ (add-hook 'kill-buffer-hook 'notmuch-tree-message-window-kill-hook)))
+ (when notmuch-show-mark-read-tags
+ (notmuch-tree-tag-update-display notmuch-show-mark-read-tags)
+ (notmuch-tree-tag notmuch-show-mark-read-tags))
+ (setq notmuch-tree-message-buffer buffer))))
+;; End upstream
+
+(define-key notmuch-show-mode-map "S"
+ (lambda ()
+ "mark message as spam"
+ (interactive)
+ (notmuch-show-tag (list "+spam" "-inbox" "-unread"))))
+
+(define-key notmuch-search-mode-map "S"
+ (lambda (&optional beg end)
+ "mark thread as spam"
+ (interactive (notmuch-search-interactive-region))
+ (notmuch-search-tag (list "+spam" "-inbox" "-unread") beg end)))
+
+(define-key notmuch-search-mode-map "R"
+ (lambda (&optional beg end)
+ "mark thread as read"
+ (interactive (notmuch-search-interactive-region))
+ (notmuch-search-tag (list "-unread") beg end)
+ (notmuch-search-next-thread)))
+
+(define-key notmuch-search-mode-map (kbd "RET")
+ (lambda ()
+ "Show the selected thread with notmuch-tree if it has more
+than one email. Use notmuch-show otherwise."
+ (interactive)
+ (if (= (plist-get (notmuch-search-get-result) :total) 1)
+ (notmuch-search-show-thread)
+ (notmuch-tree (notmuch-search-find-thread-id)
+ notmuch-search-query-string
+ nil
+ (notmuch-prettify-subject (notmuch-search-find-subject))))))
+
+(setq notmuch-folders '(("inbox" . "tag:inbox")
+ ("debian-announce" . "tag:inbox AND tag:debian-announce")
+ ("debian-devel" . "tag:inbox AND tag:debian-devel")
+ ("debian-haskell" . "tag:inbox AND tag:debian-haskell")
+ ))
+
+(defun color-inbox-if-unread () (interactive)
+ (save-excursion
+ (goto-char (point-min))
+ (let ((cnt (car (process-lines "notmuch" "count" "tag:inbox and tag:unread"))))
+ (when (> (string-to-number cnt) 0)
+ (save-excursion
+ (when (search-forward "inbox" (point-max) t)
+ (let* ((overlays (overlays-in (match-beginning 0) (match-end 0)))
+ (overlay (car overlays)))
+ (when overlay
+ (overlay-put overlay 'face '((:inherit bold) (:foreground "green")))))))))))
+(add-hook 'notmuch-hello-refresh-hook 'color-inbox-if-unread)
+
+(defun my-notmuch-show-view-as-patch ()
+ "View the the current message as a patch."
+ (interactive)
+ (let* ((id (notmuch-show-get-message-id))
+ (subject (concat "Subject: " (notmuch-show-get-subject) "\n"))
+ (diff-default-read-only t)
+ (buf (get-buffer-create (concat "*notmuch-patch-" id "*")))
+ (map (make-sparse-keymap)))
+ (define-key map "q" 'notmuch-kill-this-buffer)
+ (switch-to-buffer buf)
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (insert subject)
+ (insert (notmuch-get-bodypart-internal id 1 nil)))
+ (set-buffer-modified-p nil)
+ (diff-mode)
+ (lexical-let ((new-ro-bind (cons 'buffer-read-only map)))
+ (add-to-list 'minor-mode-overriding-map-alist new-ro-bind))
+ (goto-char (point-min))))
diff --git a/.emacs.local.d/modes/org.el b/.emacs.local.d/modes/org.el
new file mode 100644
index 0000000..f01718d
--- /dev/null
+++ b/.emacs.local.d/modes/org.el
@@ -0,0 +1,676 @@
+;; Debian packages: elpa-org elpa-org-bullets
+
+(require 'org-capture)
+(require 'org-protocol)
+(require 'org-habit)
+(require 'org-bullets)
+
+;; --------
+;; Org mode
+;; --------
+
+(require 'org)
+
+(setq org-agenda-files '("~/src/git/org/"))
+(setq org-cycle-separator-lines 0)
+(setq org-startup-indented t)
+(setq org-hide-leading-stars nil)
+
+(setq org-startup-indented t
+ org-bullets-bullet-list '(" ") ;; no bullets, needs org-bullets package
+ org-ellipsis "  " ;; folding symbol
+ org-pretty-entities t
+ org-hide-emphasis-markers t
+ ;; show actually italicized text instead of /italicized text/
+ org-agenda-block-separator ""
+ org-fontify-whole-heading-line t
+ org-fontify-done-headline t
+ org-fontify-quote-and-verse-blocks t)
+
+
+;; ORG BINDINGS ;;
+(global-set-key (kbd "<f12>") 'org-agenda)
+(global-set-key (kbd "<f5>") 'bh/org-todo)
+(global-set-key (kbd "<f8>") 'org-clock-goto)
+
+(global-set-key (kbd "<f9> I") 'bh/punch-in)
+(global-set-key (kbd "<f9> O") 'bh/punch-out)
+
+
+;; ORG STATES ;;
+(setq org-todo-keywords
+ (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
+ (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" "MEETING"))))
+
+
+(setq org-use-fast-todo-selection t)
+
+(setq org-todo-state-tags-triggers
+ (quote (("CANCELLED" ("CANCELLED" . t))
+ ("WAITING" ("WAITING" . t))
+ ("HOLD" ("WAITING") ("HOLD" . t))
+ (done ("WAITING") ("HOLD"))
+ ("TODO" ("WAITING") ("CANCELLED") ("HOLD"))
+ ("NEXT" ("WAITING") ("CANCELLED") ("HOLD"))
+ ("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))
+
+(setq org-enforce-todo-dependencies t)
+(setq org-log-done (quote time))
+(setq org-log-redeadline (quote time))
+(setq org-log-reschedule (quote time))
+
+;; CAPTURE ;;
+(setq org-default-notes-file "~/src/git/org/refile.org")
+
+;; I use C-c c to start capture mode
+(global-set-key (kbd "C-c c") 'org-capture)
+
+;; Capture templates for: TODO tasks, Notes, appointments, phone calls, meetings, and org-protocol
+(setq org-capture-templates
+ (quote (("t" "todo" entry (file "~/src/git/org/refile.org")
+ "* TODO %?\n%U\n%a\n" :clock-in t :clock-resume t)
+ ("r" "respond" entry (file "~/src/git/org/refile.org")
+ "* NEXT 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 "~/src/git/org/refile.org")
+ "* %? :NOTE:\n%U\n%a\n" :clock-in t :clock-resume t)
+ ("j" "Journal" entry (file+datetree "~/src/git/org/diary.org")
+ "* %?\n%U\n" :clock-in t :clock-resume t)
+ ("w" "org-protocol" entry (file "~/src/git/org/refile.org")
+ "* TODO Review %c\n%U\n" :immediate-finish t)
+ ("m" "Meeting" entry (file "~/src/git/org/refile.org")
+ "* MEETING with %? :MEETING:\n%U" :clock-in t :clock-resume t)
+ ("p" "Phone call" entry (file "~/src/git/org/refile.org")
+ "* PHONE %? :PHONE:\n%U" :clock-in t :clock-resume t)
+ ("h" "Habit" entry (file "~/src/git/org/refile.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"))))
+
+;; Remove empty LOGBOOK drawers on clock out
+(defun bh/remove-empty-drawer-on-clock-out ()
+ (interactive)
+ (save-excursion
+ (beginning-of-line 0)
+ (org-remove-empty-drawer-at "LOGBOOK" (point))))
+
+(add-hook 'org-clock-out-hook 'bh/remove-empty-drawer-on-clock-out 'append)
+
+(add-hook 'org-capture-mode-hook 'delete-other-windows)
+(setq my-org-protocol-flag nil)
+(defadvice org-capture-finalize (after delete-frame-at-end activate)
+ "Delete frame at remember finalization"
+ (progn (if my-org-protocol-flag (delete-frame))
+ (setq my-org-protocol-flag nil)))
+
+(defadvice org-capture-kill (after delete-frame-at-end activate)
+ "Delete frame at remember abort"
+ (progn (if my-org-protocol-flag (delete-frame))
+ (setq my-org-protocol-flag nil)))
+
+(defadvice org-protocol-capture (before set-org-protocol-flag activate)
+ (setq my-org-protocol-flag t))
+
+
+;; REFILE ;;
+
+; Targets include this file and any file contributing to the agenda - up to 9 levels deep
+(setq org-refile-targets (quote ((nil :maxlevel . 9)
+ (org-agenda-files :maxlevel . 9))))
+
+; Exclude DONE state tasks from refile targets
+(defun bh/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 'bh/verify-refile-target)
+
+; Targets complete directly with IDO
+(setq org-outline-path-complete-in-steps nil)
+
+; Allow refile to create parent tasks with confirmation
+(setq org-refile-allow-creating-parent-nodes (quote confirm))
+
+;; AGENDA VIEW ;;
+
+
+;; Do not dim blocked tasks
+(setq org-agenda-dim-blocked-tasks nil)
+
+;; Compact the block agenda view
+(setq org-agenda-compact-blocks t)
+
+;; Custom agenda command definitions
+(setq org-agenda-custom-commands
+ (quote (("N" "Notes" tags "NOTE"
+ ((org-agenda-overriding-header "Notes")
+ (org-tags-match-list-sublevels t)))
+ ("h" "Habits" tags-todo "STYLE=\"habit\""
+ ((org-agenda-overriding-header "Habits")
+ (org-agenda-sorting-strategy
+ '(todo-state-down effort-up category-keep))))
+ (" " "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 'bh/skip-non-stuck-projects)
+ (org-agenda-sorting-strategy
+ '(category-keep))))
+ (tags-todo "-HOLD-CANCELLED/!"
+ ((org-agenda-overriding-header "Projects")
+ (org-agenda-skip-function 'bh/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 bh/hide-scheduled-and-waiting-next-tasks
+ ""
+ " (including WAITING and SCHEDULED tasks)")))
+ (org-agenda-skip-function 'bh/skip-projects-and-habits-and-single-tasks)
+ (org-tags-match-list-sublevels t)
+ (org-agenda-todo-ignore-scheduled bh/hide-scheduled-and-waiting-next-tasks)
+ (org-agenda-todo-ignore-deadlines bh/hide-scheduled-and-waiting-next-tasks)
+ (org-agenda-todo-ignore-with-date bh/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 bh/hide-scheduled-and-waiting-next-tasks
+ ""
+ " (including WAITING and SCHEDULED tasks)")))
+ (org-agenda-skip-function 'bh/skip-non-project-tasks)
+ (org-agenda-todo-ignore-scheduled bh/hide-scheduled-and-waiting-next-tasks)
+ (org-agenda-todo-ignore-deadlines bh/hide-scheduled-and-waiting-next-tasks)
+ (org-agenda-todo-ignore-with-date bh/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 bh/hide-scheduled-and-waiting-next-tasks
+ ""
+ " (including WAITING and SCHEDULED tasks)")))
+ (org-agenda-skip-function 'bh/skip-project-tasks)
+ (org-agenda-todo-ignore-scheduled bh/hide-scheduled-and-waiting-next-tasks)
+ (org-agenda-todo-ignore-deadlines bh/hide-scheduled-and-waiting-next-tasks)
+ (org-agenda-todo-ignore-with-date bh/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 bh/hide-scheduled-and-waiting-next-tasks
+ ""
+ " (including WAITING and SCHEDULED tasks)")))
+ (org-agenda-skip-function 'bh/skip-non-tasks)
+ (org-tags-match-list-sublevels nil)
+ (org-agenda-todo-ignore-scheduled bh/hide-scheduled-and-waiting-next-tasks)
+ (org-agenda-todo-ignore-deadlines bh/hide-scheduled-and-waiting-next-tasks)))
+ (tags "-REFILE/"
+ ((org-agenda-overriding-header "Tasks to Archive")
+ (org-agenda-skip-function 'bh/skip-non-archivable-tasks)
+ (org-tags-match-list-sublevels nil))))
+ nil))))
+
+(defun bh/skip-non-archivable-tasks ()
+ "Skip trees that are not available for archiving"
+ (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)
+ nil
+ (or subtree-end (point-max)))
+ next-headline))))
+
+(defun bh/org-auto-exclude-function (tag)
+ "Automatic task exclusion in the agenda with / RET"
+ (and (cond
+ ((string= tag "hold")
+ t))
+ (concat "-" tag)))
+
+(setq org-agenda-auto-exclude-function 'bh/org-auto-exclude-function)
+(setq org-agenda-span 'day)
+(setq org-deadline-warning-days 30)
+
+
+(defun bh/is-project-p ()
+ "Any task with a todo keyword subtask"
+ (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 bh/is-project-subtree-p ()
+ "Any task with a todo keyword that is in a project subtree.
+Callers of this function already widen the buffer view."
+ (let ((task (save-excursion (org-back-to-heading 'invisible-ok)
+ (point))))
+ (save-excursion
+ (bh/find-project-task)
+ (if (equal (point) task)
+ nil
+ t))))
+
+(defun bh/is-task-p ()
+ "Any task with a todo keyword and no subtask"
+ (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 bh/is-subproject-p ()
+ "Any task which is a subtask of another project"
+ (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 bh/list-sublevels-for-projects-indented ()
+ "Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks.
+ This is normally used by skipping functions where this variable is already local to the agenda."
+ (if (marker-buffer org-agenda-restrict-begin)
+ (setq org-tags-match-list-sublevels 'indented)
+ (setq org-tags-match-list-sublevels nil))
+ nil)
+
+(defun bh/list-sublevels-for-projects ()
+ "Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks.
+ This is normally used by skipping functions where this variable is already local to the agenda."
+ (if (marker-buffer org-agenda-restrict-begin)
+ (setq org-tags-match-list-sublevels t)
+ (setq org-tags-match-list-sublevels nil))
+ nil)
+
+(defvar bh/hide-scheduled-and-waiting-next-tasks t)
+
+(defun bh/toggle-next-task-display ()
+ (interactive)
+ (setq bh/hide-scheduled-and-waiting-next-tasks (not bh/hide-scheduled-and-waiting-next-tasks))
+ (when (equal major-mode 'org-agenda-mode)
+ (org-agenda-redo))
+ (message "%s WAITING and SCHEDULED NEXT Tasks" (if bh/hide-scheduled-and-waiting-next-tasks "Hide" "Show")))
+
+(defun bh/skip-stuck-projects ()
+ "Skip trees that are not stuck projects"
+ (save-restriction
+ (widen)
+ (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
+ (if (bh/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 bh/skip-non-stuck-projects ()
+ "Skip trees that are not stuck projects"
+ ;; (bh/list-sublevels-for-projects-indented)
+ (save-restriction
+ (widen)
+ (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
+ (if (bh/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 bh/skip-non-projects ()
+ "Skip trees that are not projects"
+ ;; (bh/list-sublevels-for-projects-indented)
+ (if (save-excursion (bh/skip-non-stuck-projects))
+ (save-restriction
+ (widen)
+ (let ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (cond
+ ((bh/is-project-p)
+ nil)
+ ((and (bh/is-project-subtree-p) (not (bh/is-task-p)))
+ nil)
+ (t
+ subtree-end))))
+ (save-excursion (org-end-of-subtree t))))
+
+(defun bh/skip-non-tasks ()
+ "Show non-project tasks.
+Skip project and sub-project tasks, habits, and project related tasks."
+ (save-restriction
+ (widen)
+ (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
+ (cond
+ ((bh/is-task-p)
+ nil)
+ (t
+ next-headline)))))
+
+(defun bh/skip-project-trees-and-habits ()
+ "Skip trees that are projects"
+ (save-restriction
+ (widen)
+ (let ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (cond
+ ((bh/is-project-p)
+ subtree-end)
+ ((org-is-habit-p)
+ subtree-end)
+ (t
+ nil)))))
+
+(defun bh/skip-projects-and-habits-and-single-tasks ()
+ "Skip trees that are projects, tasks that are habits, single non-project tasks"
+ (save-restriction
+ (widen)
+ (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
+ (cond
+ ((org-is-habit-p)
+ next-headline)
+ ((and bh/hide-scheduled-and-waiting-next-tasks
+ (member "WAITING" (org-get-tags-at)))
+ next-headline)
+ ((bh/is-project-p)
+ next-headline)
+ ((and (bh/is-task-p) (not (bh/is-project-subtree-p)))
+ next-headline)
+ (t
+ nil)))))
+
+(defun bh/skip-project-tasks-maybe ()
+ "Show tasks related to the current restriction.
+When restricted to a project, skip project and sub project tasks, habits, NEXT tasks, and loose tasks.
+When not restricted, skip project and sub-project tasks, habits, and project related tasks."
+ (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
+ ((bh/is-project-p)
+ next-headline)
+ ((org-is-habit-p)
+ subtree-end)
+ ((and (not limit-to-project)
+ (bh/is-project-subtree-p))
+ subtree-end)
+ ((and limit-to-project
+ (bh/is-project-subtree-p)
+ (member (org-get-todo-state) (list "NEXT")))
+ subtree-end)
+ (t
+ nil)))))
+
+(defun bh/skip-project-tasks ()
+ "Show non-project tasks.
+Skip project and sub-project tasks, habits, and project related tasks."
+ (save-restriction
+ (widen)
+ (let* ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (cond
+ ((bh/is-project-p)
+ subtree-end)
+ ((org-is-habit-p)
+ subtree-end)
+ ((bh/is-project-subtree-p)
+ subtree-end)
+ (t
+ nil)))))
+
+(defun bh/skip-non-project-tasks ()
+ "Show project tasks.
+Skip project and sub-project tasks, habits, and loose 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
+ ((bh/is-project-p)
+ next-headline)
+ ((org-is-habit-p)
+ subtree-end)
+ ((and (bh/is-project-subtree-p)
+ (member (org-get-todo-state) (list "NEXT")))
+ subtree-end)
+ ((not (bh/is-project-subtree-p))
+ subtree-end)
+ (t
+ nil)))))
+
+(defun bh/skip-projects-and-habits ()
+ "Skip trees that are projects and tasks that are habits"
+ (save-restriction
+ (widen)
+ (let ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (cond
+ ((bh/is-project-p)
+ subtree-end)
+ ((org-is-habit-p)
+ subtree-end)
+ (t
+ nil)))))
+
+(defun bh/skip-non-subprojects ()
+ "Skip trees that are not projects"
+ (let ((next-headline (save-excursion (outline-next-heading))))
+ (if (bh/is-subproject-p)
+ nil
+ next-headline)))
+
+;; CLOCKING ;;
+;; Resume clocking task when emacs is restarted
+(org-clock-persistence-insinuate)
+;;
+;; Show lot of clocking history so it's easy to pick items off the C-F11 list
+(setq org-clock-history-length 23)
+;; Resume clocking task on clock-in if the clock is open
+(setq org-clock-in-resume t)
+;; Change tasks to NEXT when clocking in
+(setq org-clock-in-switch-to-state 'bh/clock-in-to-next)
+;; Separate drawers for clocking and logs
+(setq org-drawers (quote ("PROPERTIES" "LOGBOOK")))
+;; Save clock data and state changes and notes in the LOGBOOK drawer
+(setq org-clock-into-drawer t)
+;; Sometimes I change tasks I'm clocking quickly - this removes clocked tasks with 0:00 duration
+(setq org-clock-out-remove-zero-time-clocks t)
+;; Clock out when moving task to a done state
+(setq org-clock-out-when-done t)
+;; Save the running clock and all clock history when exiting Emacs, load it on startup
+(setq org-clock-persist t)
+;; Do not prompt to resume an active clock
+(setq org-clock-persist-query-resume nil)
+;; Enable auto clock resolution for finding open clocks
+(setq org-clock-auto-clock-resolution (quote when-no-clock-is-running))
+;; Include current clocking task in clock reports
+(setq org-clock-report-include-clocking-task t)
+
+
+(setq bh/keep-clock-running nil)
+
+(defun bh/clock-in-to-next (kw)
+ "Switch a task from TODO to NEXT when clocking in.
+Skips capture tasks, projects, and subprojects.
+Switch projects and subprojects from NEXT back to TODO"
+ (when (not (and (boundp 'org-capture-mode) org-capture-mode))
+ (cond
+ ((and (member (org-get-todo-state) (list "TODO"))
+ (bh/is-task-p))
+ "NEXT")
+ ((and (member (org-get-todo-state) (list "NEXT"))
+ (bh/is-project-p))
+ "TODO"))))
+
+(defun bh/find-project-task ()
+ "Move point to the parent (project) task if any"
+ (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 bh/punch-in (arg)
+ "Start continuous clocking and set the default task to the
+selected task. If no task is selected set the Organization task
+as the default task."
+ (interactive "p")
+ (setq bh/keep-clock-running t)
+ (if (equal major-mode 'org-agenda-mode)
+ ;;
+ ;; We're in the agenda
+ ;;
+ (let* ((marker (org-get-at-bol 'org-hd-marker))
+ (tags (org-with-point-at marker (org-get-tags-at))))
+ (if (and (eq arg 4) tags)
+ (org-agenda-clock-in '(16))
+ (bh/clock-in-organization-task-as-default)))
+ ;;
+ ;; We are not in the agenda
+ ;;
+ (save-restriction
+ (widen)
+ ; Find the tags on the current task
+ (if (and (equal major-mode 'org-mode) (not (org-before-first-heading-p)) (eq arg 4))
+ (org-clock-in '(16))
+ (bh/clock-in-organization-task-as-default)))))
+
+(defun bh/punch-out ()
+ (interactive)
+ (setq bh/keep-clock-running nil)
+ (when (org-clock-is-active)
+ (org-clock-out))
+ (org-agenda-remove-restriction-lock))
+
+(defun bh/clock-in-default-task ()
+ (save-excursion
+ (org-with-point-at org-clock-default-task
+ (org-clock-in))))
+
+(defun bh/clock-in-parent-task ()
+ "Move point to the parent (project) task if any and clock in"
+ (let ((parent-task))
+ (save-excursion
+ (save-restriction
+ (widen)
+ (while (and (not parent-task) (org-up-heading-safe))
+ (when (member (nth 2 (org-heading-components)) org-todo-keywords-1)
+ (setq parent-task (point))))
+ (if parent-task
+ (org-with-point-at parent-task
+ (org-clock-in))
+ (when bh/keep-clock-running
+ (bh/clock-in-default-task)))))))
+
+(defvar bh/organization-task-id "eb155a82-92b2-4f25-a3c6-0304591af2f9")
+
+(defun bh/clock-in-organization-task-as-default ()
+ (interactive)
+ (org-with-point-at (org-id-find bh/organization-task-id 'marker)
+ (org-clock-in '(16))))
+
+(defun bh/clock-out-maybe ()
+ (when (and bh/keep-clock-running
+ (not org-clock-clocking-in)
+ (marker-buffer org-clock-default-task)
+ (not org-clock-resolving-clocks-due-to-idleness))
+ (bh/clock-in-parent-task)))
+
+(add-hook 'org-clock-out-hook 'bh/clock-out-maybe 'append)
+
+
+;; ORG REPORTS ;;
+; Set default column view headings: Task Effort Clock_Summary
+(setq org-columns-default-format "%80ITEM(Task) %10Effort(Effort){:} %10CLOCKSUM")
+
+(defun my-org-clocktable-indent-string (level)
+ (if (= level 1)
+ ""
+ (let ((str "^"))
+ (while (> level 2)
+ (setq level (1- level)
+ str (concat str "--")))
+ (concat str "-> "))))
+
+(advice-add 'org-clocktable-indent-string :override #'my-org-clocktable-indent-string)
+
+(setq org-clock-clocktable-default-properties '(:maxlevel 4 :scope file :formula %))
+
+; global Effort estimate values
+; global STYLE property values for completion
+(setq org-global-properties (quote (("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00")
+ ("STYLE_ALL" . "habit"))))
+
+;; Agenda log mode items to display (closed and state changes by default)
+(setq org-agenda-log-mode-items (quote (closed state)))
+
+;; TAGS ;;
+; Tags with fast selection keys
+(setq org-tag-alist (quote ((:startgroup)
+ ("@errand" . ?e)
+ ("@office" . ?o)
+ ("@home" . ?H)
+ (:endgroup)
+ ("WAITING" . ?w)
+ ("HOLD" . ?h)
+ ("PERSONAL" . ?P)
+ ("WORK" . ?W)
+ ("ORG" . ?O)
+ ("NOTE" . ?n)
+ ("CANCELLED" . ?c)
+ ("FLAGGED" . ??))))
+
+; Allow setting single tags without the menu
+(setq org-fast-tag-selection-single-key (quote expert))
+
+; For tag searches ignore tasks with scheduled and deadline dates
+(setq org-agenda-tags-todo-honor-ignore-options t)
+
+(require 'ox-extra)
+(ox-extras-activate '(ignore-headlines))
+
+(add-to-list 'org-latex-classes
+ '("memoir-article"
+ "\\documentclass[11pt,oneside,article]{memoir}
+ [PACKAGES]
+ \\usepackage{memoir-article-style}
+ [NO-DEFAULT-PACKAGES]"
+ ("\\section{%s}" . "\\section*{%s}")
+ ("\\subsection{%s}" . "\\subsection*{%s}")
+ ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
+ ("\\paragraph{%s}" . "\\paragraph*{%s}")
+ ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
diff --git a/.emacs.local.d/modes/package.el b/.emacs.local.d/modes/package.el
new file mode 100644
index 0000000..acbfc21
--- /dev/null
+++ b/.emacs.local.d/modes/package.el
@@ -0,0 +1,7 @@
+(require 'package) ;; You might already have this line
+
+(add-to-list 'package-archives
+ '("melpa-stable" . "https://stable.melpa.org/packages/") t)
+
+(package-initialize) ;; You might already have this line
+
diff --git a/.emacs.local.d/modes/projectile.el b/.emacs.local.d/modes/projectile.el
new file mode 100644
index 0000000..f5f8097
--- /dev/null
+++ b/.emacs.local.d/modes/projectile.el
@@ -0,0 +1,3 @@
+;; Debian packages: elpa-projectile
+
+(projectile-global-mode)
diff --git a/.emacs.local.d/modes/smex.el b/.emacs.local.d/modes/smex.el
new file mode 100644
index 0000000..d724452
--- /dev/null
+++ b/.emacs.local.d/modes/smex.el
@@ -0,0 +1,15 @@
+;; Debian packages: elpa-smex
+;; Elpa packages: helm-smex
+
+(require 'smex) ; Not needed if you use package.el
+(smex-initialize) ; Can be omitted. This might cause a (minimal) delay
+ ; when Smex is auto-initialized on its first run.
+
+(require 'helm-smex)
+(global-set-key [remap execute-extended-command] #'helm-smex)
+(global-set-key (kbd "M-X") #'helm-smex-major-mode-commands)
+
+;; (global-set-key (kbd "M-x") 'smex)
+;; (global-set-key (kbd "M-X") 'smex-major-mode-commands)
+;; This is your old M-x.
+(global-set-key (kbd "C-c C-c M-x") 'execute-extended-command)
diff --git a/.emacs.local.d/modes/sml.el b/.emacs.local.d/modes/sml.el
new file mode 100644
index 0000000..247d9b2
--- /dev/null
+++ b/.emacs.local.d/modes/sml.el
@@ -0,0 +1,7 @@
+;; Debian packages: elpa-smart-mode-line elpa-smart-mode-line-powerline-theme
+
+(use-package smart-mode-line
+ :ensure t
+ :config
+ (setq sml/theme 'respectful)
+ (sml/setup))
diff --git a/.emacs.local.d/modes/sublimity.el b/.emacs.local.d/modes/sublimity.el
new file mode 100644
index 0000000..a702a22
--- /dev/null
+++ b/.emacs.local.d/modes/sublimity.el
@@ -0,0 +1,8 @@
+;; Elpa packages: sublimity
+
+(require 'sublimity)
+(require 'sublimity-scroll)
+;(require 'sublimity-map)
+;(require 'sublimity-attractive)
+
+(sublimity-mode 1)
diff --git a/.emacs.local.d/modes/themes.el b/.emacs.local.d/modes/themes.el
new file mode 100644
index 0000000..c6e9bc2
--- /dev/null
+++ b/.emacs.local.d/modes/themes.el
@@ -0,0 +1,6 @@
+;; Debian packages: elpa-clues-theme elpa-monokai-theme elpa-smart-mode-line-powerline-theme elpa-solarized-theme elpa-zenburn-theme
+;; Elpa packages: atom-one-dark
+
+(load-theme 'clues t)
+
+
diff --git a/.emacs.local.d/modes/whitespace.el b/.emacs.local.d/modes/whitespace.el
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/.emacs.local.d/modes/whitespace.el
@@ -0,0 +1 @@
+
diff --git a/.emacs.local.d/modes/writeroom.el b/.emacs.local.d/modes/writeroom.el
new file mode 100644
index 0000000..ba700a1
--- /dev/null
+++ b/.emacs.local.d/modes/writeroom.el
@@ -0,0 +1,27 @@
+(use-package writeroom-mode
+ :defer t
+ :config
+ (setq writeroom-width 140
+ writeroom-mode-line nil
+ writeroom-global-effects '(writeroom-set-bottom-divider-width
+ writeroom-set-internal-border-width
+ (lambda (arg)
+ (let ((langs '("python"
+ "emacs-lisp"
+ "common-lisp"
+ "js"
+ "ruby")))
+ (cond
+ ((= arg 1)
+ (progn
+ (setq org-src-block-faces
+ (mapcar (lambda (lang) (list lang '(:family "Source Code Pro" :height 0.8))) langs))
+ (normal-mode)
+ (variable-pitch-mode)))
+ ((= arg -1)
+ (progn
+ (setq org-src-block-faces
+ (mapcar (lambda (lang) (list lang '(:family "Source Code Pro" :height 1.0))) langs))
+ (normal-mode)
+ (variable-pitch-mode)
+(variable-pitch-mode)))))))))
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e13418a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+# Installing Emacs dependencies
+
+Install the following dependencies before attempting to run my emacs
+configuration. It should work for Debian 10 or later:
+
+ sudo apt-get install $(grep -ri 'Debian packages:' .emacs .emacs.local.d/ | awk -F: '{print $3}' | tr '\n' ' ')
+
+If Debian packages aren't available, we use Elpa packages. At some
+point I should create Debian packages for these.
+
+ emacs-pkg-install $(grep -ri 'Elpa packages:' .emacs .emacs.local.d/ | awk -F: '{print $3}' | tr '\n' ' ')
+
+The first time you open emacs after installing these packages, a bunch
+of warnings will be printed. You can dismiss them; they'll only appear
+on the first run.
nihil fit ex nihilo