summaryrefslogtreecommitdiff
path: root/init.el
diff options
context:
space:
mode:
authorKyle Meyer <kyle@kyleam.com>2016-01-10 23:43:14 -0500
committerKyle Meyer <kyle@kyleam.com>2016-01-12 22:15:25 -0500
commit8d97d1f2063f19c0c679e54fc082691a495c9303 (patch)
tree77cc0ce12ecceb5739b5d0e35a5bc2eab09a9adc /init.el
parent2d395ef1ccedd51e3c11b1eb8ff552f03bae4797 (diff)
downloademacs.d-8d97d1f2063f19c0c679e54fc082691a495c9303.tar.gz
Rewrite configuration with use-package
Diffstat (limited to 'init.el')
-rw-r--r--init.el1854
1 files changed, 1818 insertions, 36 deletions
diff --git a/init.el b/init.el
index 315dca9..5fb4bdb 100644
--- a/init.el
+++ b/init.el
@@ -22,9 +22,6 @@
;; (package-initialize)
-(defvar km/init-lisp-dir (expand-file-name "lisp/" user-emacs-directory))
-(add-to-list 'load-path km/init-lisp-dir)
-
(require 'package)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
(package-initialize)
@@ -34,52 +31,1837 @@
(require 'pallet)
(pallet-mode 1)
-(require 'init-appearance)
-
(require 'cl-lib)
(require 'dash)
(require 's)
-(require 'init-util)
-(require 'init-general)
-(require 'init-abbrev)
-(require 'init-diminish)
+(require 'bind-key)
+(require 'use-package)
+(require 'use-package-chords)
+(key-chord-mode 1)
+
+(defvar km/init-lisp-dir (expand-file-name "lisp/" user-emacs-directory))
+(add-to-list 'load-path km/init-lisp-dir)
+
+(require 'km-util)
+(require 'km-emacs-autoloads)
+
+
+;;; Appearance
+
+(setq inhibit-splash-screen t
+ initial-scratch-message nil)
+
+(blink-cursor-mode -1)
+(menu-bar-mode -1)
+(scroll-bar-mode -1)
+(tool-bar-mode -1)
+
+(column-number-mode)
+(line-number-mode)
+(size-indication-mode)
+
+(require 'km-theme)
+
+
+;;; Custom prefix maps
+
+(pcase-dolist (`(,key ,map) '(("C-c c" km/compile-map)
+ ("C-c e" km/editing-map)
+ ("C-c f" km/file-map)
+ ("C-c g" km/git-map)
+ ("C-c k" km/kill-map)
+ ("C-x m" km/mail-map)
+ ("C-c o" km/global-org-map)
+ ("C-c z" km/external-map)))
+ (define-prefix-command map)
+ (global-set-key (kbd key) map))
+
+
+;;; Org
+
+(use-package org
+ :load-path "~/src/emacs/org-mode/lisp/"
+ :mode ("\\.org.txt\\'" . org-mode)
+ :init
+ (require 'org-loaddefs)
+ (add-to-list 'load-path "~/src/emacs/org-mode/contrib/lisp/" t)
+ (add-to-list 'Info-directory-list "~/src/emacs/org-mode/doc/")
+ (bind-keys :map km/global-org-map
+ ("b" . org-iswitchb)
+ ("l" . org-store-link)
+ ("o" . org-open-at-point)
+ ("s" . org-save-all-org-buffers)
+ ("w" . org-refile-goto-last-stored))
+ (define-prefix-command 'km/org-prefix-map)
+ :config
+ (setq org-log-done t
+ org-log-into-drawer t
+ org-clock-into-drawer t
+ org-todo-keywords '((sequence "TODO(t)" "STARTED(s)" "WAITING(w@)"
+ "|" "DONE(d)" "NA(n@)")))
+ (setq org-catch-invisible-edits 'error
+ org-special-ctrl-k t
+ org-insert-heading-respect-content t
+ org-M-RET-may-split-line nil
+ org-adapt-indentation nil
+ org-blank-before-new-entry '((heading . t) (plain-list-item . auto)))
+ (setq org-use-speed-commands t
+ org-use-extra-keys t
+ org-fast-tag-selection-single-key 'expert)
+ (setq org-outline-path-complete-in-steps nil
+ org-goto-interface 'outline-path-completionp
+ org-goto-max-level 3)
+
+ (setq org-default-notes-file "~/notes/agenda/tasks.org")
+ (setq org-agenda-text-search-extra-files
+ (file-expand-wildcards "~/notes/extra/*.org"))
+
+ (setq org-reverse-note-order t)
+ (setq org-refile-targets '((nil :maxlevel . 2))
+ org-refile-cache nil)
+
+ (setq org-link-search-must-match-exact-headline nil)
+
+ (setq org-confirm-babel-evaluate nil
+ org-src-fontify-natively t)
+
+ (org-babel-do-load-languages
+ 'org-babel-load-languages
+ '((shell . t)
+ (python . t)
+ (R . t)
+ (emacs-lisp . t)
+ (latex . t)))
+
+ (defadvice org-open-file (after km/org-open-add-to-recentf activate)
+ (recentf-add-file path))
+
+ (put 'org-goto-max-level 'safe-local-variable #'integerp)
+
+ (add-to-list 'org-latex-packages-alist '("" "amsmath" t))
+
+ (setq org-structure-template-alist
+ (append '(("p" "#+property: ")
+ ("o" "#+options: ")
+ ("d" "#+date: ")
+ ("t" "#+title: ")
+ ("S" "#+setupfile: ?")
+ ("n" "#+name: ")
+ ("w" "#+begin_note\n ?\n#+end_note")
+ ("C" "#+caption: ")
+ ("b" "#+label: ")
+ ("r" "#+attr_latex: ")
+ ("R" "#+attr_html: "))
+ (mapcar (lambda (i) (list (car i) (downcase (cadr i))))
+ org-structure-template-alist)))
+
+ (add-hook 'next-error-hook (lambda ()
+ (when (eq major-mode 'org-mode)
+ (org-show-context))))
+
+ (bind-keys :map org-mode-map
+ ("C-c l" . org-goto)
+ ("C-c m" . km/org-prefix-map)
+ ;; Don't let `org-cycle-agenda-files' binding override
+ ;; custom `backward-kill-word' binding
+ ;; (`org-cycle-agenda-files' is still bound to C-,).
+ ("C-'" . nil)
+ ("C-c C-x d" . org-metadown)
+ ("C-c C-x n" . org-next-item)
+ ("C-c C-x s" . org-set-property)
+ ("C-c C-x p" . org-previous-item)
+ ("C-c C-x w" . org-insert-drawer)))
+
+(use-package ox-latex
+ :defer t
+ :config
+ (add-to-list 'org-latex-classes
+ '("short"
+ "\\documentclass{short}"
+ ("\\section{%s}" . "\\section*{%s}")
+ ("\\subsection{%s}" . "\\subsection*{%s}")
+ ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
+ ("\\paragraph{%s}" . "\\paragraph*{%s}")
+ ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))
+
+(use-package org-table
+ :defer t
+ :diminish (orgtbl-mode . "Ot"))
+
+(use-package org-capture
+ :defer t
+ :init (define-key km/global-org-map "c" #'org-capture)
+ :config
+ (setq org-capture-templates
+ '(("t" "task" entry (file+headline "~/notes/tasks.org" "Inbox")
+ "* TODO %?%i" :prepend t)
+ ("d" "date" entry (file+headline "~/notes/calendar.org" "Inbox")
+ "* %?%i" :prepend t)
+ ("b" "bookmark" entry (file+headline "~/notes/bookmarks.org" "Inbox")
+ "* %?%i" :prepend t)
+ ("v" "Visit" checkitem (file+headline "~/notes/tasks.org" "Visit")
+ "- [ ] %?%i\n" :prepend t)
+ ("r" "Revisit" checkitem (file+headline "~/notes/tasks.org" "Revisit")
+ "- [ ] %?%i\n" :prepend t)
+ ;; Link counterparts
+ ("T" "task link" entry (file+headline "~/notes/tasks.org" "Inbox")
+ "* TODO %?%i\n\n%a" :prepend t)
+ ("D" "date link" entry (file+headline "~/notes/calendar.org" "Inbox")
+ "* %?%i\n\n%a" :prepend t)
+ ("B" "bookmark link" entry
+ (file+headline "~/notes/bookmarks.org" "Inbox")
+ "* %?%i\n\n%a" :prepend t)
+ ;; Clipboard
+ ("x" "task clipboard" entry (file+headline "~/notes/tasks.org" "Inbox")
+ "* TODO %?%i\n\n%x" :prepend t)
+ ("X" "bookmark clipboard" entry
+ (file+headline "~/notes/bookmarks.org" "Inbox")
+ "* %?%i\n\n%x" :prepend t))))
+
+(use-package org-agenda
+ :defer t
+ :init (define-key km/global-org-map "a" #'org-agenda)
+ :config
+ (setq org-agenda-restore-windows-after-quit t
+ org-agenda-window-setup 'only-window
+ org-agenda-sticky t)
+ (setq org-agenda-dim-blocked-tasks nil
+ org-agenda-show-all-dates t
+ org-agenda-skip-deadline-if-done t
+ org-agenda-skip-scheduled-if-done t
+ org-agenda-start-on-weekday nil
+ org-agenda-use-time-grid nil)
+ (setq org-agenda-sorting-strategy
+ '((agenda time-up deadline-up scheduled-up priority-down category-keep)
+ (todo priority-down category-keep)
+ (tags priority-down category-keep)
+ (search category-keep)))
+ (setq org-agenda-custom-commands
+ '(("d" todo "DONE" nil)
+ ("u" "Unschedule TODO entries" alltodo ""
+ ((org-agenda-skip-function
+ (lambda nil
+ (org-agenda-skip-entry-if 'scheduled 'deadline
+ 'regexp "\n]+>")))
+ (org-agenda-overriding-header "Unscheduled TODO entries: ")))
+ ("p" "Past timestamps" tags "TIMESTAMP<=\"<now>\"")))
+
+ ;; Bind `org-agenda-follow-mode' to same key as
+ ;; `next-error-follow-minor-mode'.
+ (define-key org-agenda-mode-map (kbd "C-c C-f") #'org-agenda-follow-mode)
+ ;; Free up 'j' for `km/org-agenda-avy-goto-subword-1'.
+ (define-key org-agenda-mode-map (kbd "C-j") #'org-agenda-goto-date))
+
+(use-package org-contacts
+ :defer t
+ :after org
+ :init
+ (setq org-contacts-files '("~/notes/contacts.org"))
+ (after 'org-capture
+ (add-to-list 'org-capture-templates
+ '("a" "email address" entry
+ (file+headline "~/notes/contacts.org" "Inbox")
+ "
+** %(org-contacts-template-name)
+:PROPERTIES:
+:EMAIL: %(org-contacts-template-email)
+:END:"))))
+
+(use-package km-org
+ :defer t
+ :after org-agenda
+ :init
+ (bind-keys :map km/global-org-map
+ ("j" . km/org-goto-agenda-heading)
+ ("m" . km/org-open-default-notes-file-inbox)
+ ("n" . km/org-agenda-add-or-remove-file))
+ (bind-keys :map km/org-prefix-map
+ ("c" . km/org-clone-and-shift-by-repeater)
+ ("d" . km/org-link-dired-jump)
+ ("D" . km/org-delete-checked-items)
+ ("i" . km/org-refile-list-item)
+ ("l" . km/org-remove-title-leader)
+ ("n" . km/org-normalize-spaces)
+ ("s" . km/org-sort-parent)
+ ("w" . km/org-refile-to-other-org-buffer))
+ (bind-keys :map km/file-map
+ ("a" . km/org-open-annex-file)
+ ("o" . km/org-open-file)
+ ("p" . km/org-open-file-at-point)
+ ("r" . km/org-open-recent-file))
+
+ (define-key ctl-x-4-map "o" #'km/org-switch-to-buffer-other-window)
+
+ (after 'org
+ (define-key org-mode-map (kbd "C-c C-x B")
+ #'km/org-tree-to-indirect-buffer-current-window)
+ (define-key org-mode-map [remap org-tree-to-indirect-buffer]
+ #'km/org-tree-to-indirect-buffer)
+ (define-key org-mode-map [remap org-refile] #'km/org-refile-dwim)
+
+ (add-to-list 'org-speed-commands-user '("o" . km/org-open-at-point-stay))
+ (add-to-list 'org-speed-commands-user '("w" . km/org-refile-dwim))
+
+ (setq org-refile-target-verify-function #'km/org-refile-verify-target)
+ (add-hook 'org-after-refile-insert-hook #'km/org-maybe-sort-parent)
+
+ (org-add-link-type "pmid" #'km/org-pmid-open))
+ (after 'org-agenda
+ (setq km/org-agenda-file-directory "~/notes/agenda/"
+ org-agenda-files (list km/org-agenda-file-directory))
+ (add-hook 'org-agenda-mode-hook #'km/org-agenda-cd-and-read-dir-locals)
+ (add-hook 'org-agenda-finalize-hook #'km/org-agenda-store-current-span))
+ (after 'ox-md
+ (advice-add 'org-md-paragraph :filter-return #'km/org-md--fill-string))
+ :config
+ (add-to-list 'safe-local-variable-values
+ (cons 'org-refile-targets km/org-agenda-refile-targets)))
+
+(use-package poporg
+ :defer t
+ :init
+ (define-key km/global-org-map "p" #'poporg-dwim)
+ :config
+ (define-key poporg-mode-map (kbd "C-c C-c") #'poporg-edit-exit))
+
+(use-package org-link-edit
+ :load-path "~/src/emacs/org-link-edit/"
+ :defer t
+ :init (require 'org-link-edit-autoloads))
+
+(use-package bog
+ :load-path "~/src/emacs/bog/"
+ :defer t
+ :init
+ (require 'bog-autoloads)
+ (after 'org
+ (add-hook 'org-mode-hook #'bog-mode))
+ (setq bog-keymap-prefix (kbd "C-c b"))
+ (global-set-key bog-keymap-prefix bog-command-map)
+ :config
+ (setq bog-subdirectory-group 2
+ bog-combined-bib-ignore-not-found t
+ bog-use-citekey-cache t))
+
+
+;;; Editing, navigation, and search
+
+(setq set-mark-command-repeat-pop t)
+(setq recenter-positions '(top middle bottom))
+
+(setq-default indicate-empty-lines t
+ indent-tabs-mode nil)
+
+(electric-indent-mode -1)
+(electric-pair-mode)
+(show-paren-mode)
+
+(put 'upcase-region 'disabled nil)
+(put 'downcase-region 'disabled nil)
+
+(put 'set-goal-column 'disabled nil)
+
+(bind-keys :map km/editing-map
+ ("C-i" . indent-relative)
+ ("l" . toggle-truncate-lines))
+
+(bind-keys :map occur-mode-map
+ ("n" . next-line)
+ ("p" . previous-line))
+(bind-keys :map search-map
+ ("s" . query-replace)
+ ("S" . replace-string)
+ ("r" . query-replace-regexp)
+ ("R" . replace-regexp))
+
+;; Avoid shift key for `backward-paragraph' and `forward-paragraph'.
+(global-set-key (kbd "M-}") nil)
+(global-set-key (kbd "M-]") #'forward-paragraph)
+(global-set-key (kbd "M-{") nil)
+(global-set-key (kbd "M-[") #'backward-paragraph)
+
+(global-set-key (kbd "C-z") #'zap-to-char)
+(use-package misc
+ :bind ("M-z" . zap-up-to-char))
+
+(global-set-key (kbd "C-'") #'backward-kill-word)
+
+(put 'fill-paragraph-function 'safe-local-variable
+ (lambda (v) (equal v (lambda (_) t))))
+
+(key-chord-define-global "qp" #'fill-paragraph)
+
+(use-package align
+ :bind ("C-x \\" . align-regexp))
+
+(use-package expand-region
+ :bind ("C-." . er/expand-region))
+
+(use-package iedit
+ :defer t
+ :init (define-key km/editing-map "i" #'iedit-mode)
+ :config
+ (setq iedit-toggle-key-default nil))
+
+(use-package easy-kill
+ :defer t
+ :init
+ (global-set-key [remap kill-ring-save] #'easy-kill))
+
+(use-package whitespace
+ :defer 5
+ :diminish global-whitespace-mode
+ :config
+ (setq whitespace-style '(face trailing indentation))
+ (global-whitespace-mode))
+
+(use-package km-editing
+ :defer t
+ :chords ("jx" . km/toggle-line-or-region-comment)
+ :init
+ (define-key search-map "o" #'km/occur)
+ (define-key narrow-map "c" #'km/narrow-to-comment-heading)
+
+ (bind-keys :map km/editing-map
+ ("f" . km/fill-surrounding-indented)
+ ("u" . km/unfill-paragraph))
+ :config
+ (bind-keys :map km/kill-map
+ ("." . km/kill-sentence-at-point)
+ ("j" . km/join-next-line-with-space)
+ ("l" . km/kill-line-at-point)
+ ("p" . km/kill-paragraph-at-point)
+ ("s" . km/kill-string-at-point)
+ ("w" . km/kill-word-at-point)))
+
+(use-package km-outline
+ :defer t)
+
+(put 'narrow-to-page 'disabled nil)
+(put 'narrow-to-region 'disabled nil)
+
+(use-package narrow-indirect
+ :defer t
+ :init
+ (bind-keys :map ctl-x-4-map
+ ("nd" . ni-narrow-to-defun-indirect-other-window)
+ ("nn" . ni-narrow-to-region-indirect-other-window)
+ ("np" . ni-narrow-to-page-indirect-other-window)))
+
+(use-package avy
+ :defer t
+ :chords ("jf" . avy-goto-subword-1)
+ :init
+ (define-key isearch-mode-map (kbd "C-'") #'avy-isearch))
+
+(use-package ace-link
+ :defer t
+ :init
+ (after 'org
+ (define-key org-mode-map (kbd "C-c m o") #'ace-link-org))
+ (after 'info
+ (define-key Info-mode-map "o" #'ace-link-info))
+ (after 'compile
+ (define-key compilation-mode-map "o" #'ace-link-compilation))
+ (after 'help-mode
+ (define-key help-mode-map "o" #'ace-link-help))
+ (after 'woman
+ (define-key woman-mode-map "o" #'ace-link-woman))
+ (after 'eww
+ (define-key eww-link-keymap "o" #'ace-link-eww)
+ (define-key eww-mode-map "o" #'ace-link-eww)))
+
+(use-package km-ace-link
+ :defer t
+ :init
+ (after 'dired
+ ;; This overrides the binding for `dired-find-file-other-window'.
+ (define-key dired-mode-map "o" #'km/ace-link-dired)
+ (define-key dired-mode-map "r" #'dired-find-file-other-window))
+ (after 'notmuch
+ (define-key notmuch-hello-mode-map "o" #'km/ace-link-widget))
+ (after 'gnus-sum
+ (define-key gnus-summary-mode-map "o" #'km/ace-link-widget))
+ (after 'gnus-art
+ (define-key gnus-article-mode-map "o" #'km/ace-link-widget)))
+
+(use-package km-avy
+ :defer t
+ :init
+ (after 'gnus-sum
+ (define-key gnus-summary-mode-map "j"
+ #'km/gnus-avy-goto-subword-and-select))
+ (after 'grep
+ (define-key grep-mode-map "j" #'km/grep-avy-goto-subword-1))
+ (after 'org-agenda
+ (define-key org-agenda-mode-map "j" #'km/org-agenda-avy-goto-subword-1))
+ (after 'magit
+ (define-key magit-refs-mode-map "j" #'km/magit-avy-goto-subword-1))
+ (after 'magit-log
+ (define-key magit-log-mode-map "j" #'km/magit-avy-goto-subword-1)
+ (define-key magit-cherry-mode-map "j" #'km/magit-avy-goto-subword-1))
+ (after 'replace
+ (define-key occur-mode-map "j" #'km/occur-avy-goto-subword-1)))
+
+
+;;; Buffers and files
+
+(setq require-final-newline t)
+
+;; Disable `suspend-frame' binding.
+(global-set-key (kbd "C-x C-z") nil)
+
+(use-package uniquify
+ :config
+ (setq uniquify-buffer-name-style 'forward))
+
+(use-package autorevert
+ :config
+ (setq auto-revert-use-notify nil)
+ (global-auto-revert-mode))
+
+(use-package ibuffer
+ :bind ("C-x C-b" . ibuffer)
+ :init
+ (setq ibuffer-expert t
+ ibuffer-restore-window-config-on-quit t
+ ibuffer-show-empty-filter-groups nil))
+
+(use-package km-buffer-cleanup
+ :config
+ (add-hook 'before-save-hook #'km/cleanup-buffer)
+ (define-key km/editing-map "t" #'km/toggle-prevent-cleanup))
+
+(use-package km-buffers
+ :defer t
+ :chords ("js" . km/save-buffers)
+ :bind ("C-x k" . km/kill-buffer))
+
+(use-package ffap
+ :defer t
+ :config
+ (setq ffap-machine-p-known 'reject))
+
+(use-package tramp
+ :defer t
+ :config
+ (setq tramp-default-method "sshx"))
+
+(use-package recentf
+ :config
+ (setq recentf-max-menu-items 15
+ recentf-max-saved-items 200
+ recentf-save-file "~/.emacs.d/cache/recentf")
+ (recentf-mode))
+
+(use-package nlines
+ :load-path "~/src/emacs/nlines/"
+ :defer t
+ :init
+ (require 'nlines-autoloads)
+ (define-key km/file-map "l" #'nlines-run-command))
+
+(use-package view
+ :diminish (view-mode . "Vw")
+ :defer t
+ :chords ("hq" . view-mode)
+ :init
+ (define-key ctl-x-4-map "v" #'view-file-other-window)
+ :config
+ (bind-keys :map view-mode-map
+ ("l" . recenter-top-bottom)
+ ("f" . forward-word)
+ ("b" . backward-word)
+ ("]" . forward-paragraph)
+ ("[" . backward-paragraph)
+ ("j" . avy-goto-subword-1)))
+
+(use-package km-files
+ :bind (("C-c s" . km/scratch-find-file)
+ ("C-x C-w" . km/write-file))
+ :init
+ (bind-keys :map km/file-map
+ ("j" . km/dired-jump-file-at-point)
+ ("n" . km/rename-current-buffer-file)
+ ("R" . km/find-file-as-root))
+ (bind-keys :map ctl-x-4-map
+ ("r" . km/recentf-find-file-other-window)
+ ("s" . km/scratch-find-file-other-window)))
+
+
+;;; Frames and windows
+
+(use-package winner
+ :defer 10
+ :chords ("lq" . winner-undo)
+ :config
+ (winner-mode))
+
+(use-package km-framewin
+ :defer t
+ :init
+ (define-key ctl-x-4-map "c"
+ #'km/clone-indirect-buffer-other-window-and-widen))
+
+(use-package ace-window
+ :commands km/ace-window
+ :chords ("jw" . km/ace-window)
+ :init
+ (defun km/ace-window (arg)
+ "Run `ace-window', swapping single and double C-u's."
+ (interactive "p")
+ (cl-case arg
+ (4 (setq arg 16))
+ (16 (setq arg 4)))
+ (ace-window arg))
+ :config
+ (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)
+ aw-scope 'frame))
+
+
+;;; Projectile
+
+(use-package projectile
+ :defer t
+ :diminish projectile-mode
+ :chords ("jq" . projectile-commander)
+ :init
+ (define-prefix-command 'km/projectile-ctl-x-4-map)
+ (define-key ctl-x-4-map "p" 'km/projectile-ctl-x-4-map)
+ (bind-keys :map km/projectile-ctl-x-4-map
+ ("C-o" . projectile-display-buffer)
+ ("b" . projectile-switch-to-buffer-other-window)
+ ("d" . projectile-find-dir-other-window)
+ ("f" . projectile-find-file-other-window)
+ ("t" . projectile-find-implementation-or-test-other-window))
+ :config
+ (setq projectile-find-dir-includes-top-level t
+ projectile-completion-system 'helm
+ projectile-use-git-grep t)
+
+ (defun km/projectile-ignore-directory-p (name)
+ (or (file-remote-p name)
+ (string-prefix-p "/tmp/" name)))
+ (setq projectile-ignored-project-function #'km/projectile-ignore-directory-p)
+
+ (projectile-register-project-type 'snakemake
+ '("Snakefile") "snakemake -p" "")
+
+ (bind-keys :map projectile-command-map
+ ("g" . projectile-vc)
+ ("i" . projectile-ibuffer)
+ ("I" . projectile-invalidate-cache)
+ ("l" . projectile-project-buffers-other-buffer)
+ ("q" . projectile-replace)
+ ("s" . projectile-grep))
+
+ ;; I'm redefining a lot of bindings, so unset pre-defined methods
+ ;; and define everyting here.
+ (setq projectile-commander-methods nil)
+
+ (def-projectile-commander-method ?c
+ "Run project compilation command."
+ (call-interactively 'projectile-compile-project))
+ (def-projectile-commander-method ?D
+ "Find a project directory in other window."
+ (call-interactively 'projectile-find-dir-other-window))
+ (def-projectile-commander-method ?F
+ "Find project file in other window."
+ (call-interactively 'projectile-find-file-other-window))
+ (def-projectile-commander-method ?g
+ "Open project root in vc-dir or magit."
+ (projectile-vc))
+ (def-projectile-commander-method ?i
+ "Open an IBuffer window showing all buffers in the current project."
+ (call-interactively 'projectile-ibuffer))
+ (def-projectile-commander-method ?o
+ "Display a project buffer in other window."
+ (call-interactively 'projectile-display-buffer))
+ (def-projectile-commander-method ?O
+ "Run multi-occur on project buffers."
+ (projectile-multi-occur))
+ (def-projectile-commander-method ?r
+ "Find recently visited file in project."
+ (projectile-recentf))
+ (def-projectile-commander-method ?s
+ "Run grep on project."
+ (call-interactively #'projectile-grep))
+
+ (projectile-global-mode))
+
+(use-package helm-projectile
+ :after projectile
+ :config
+ (bind-keys :map projectile-command-map
+ ("b" . helm-projectile-switch-to-buffer)
+ ("d" . helm-projectile-find-dir)
+ ("f" . helm-projectile-find-file)
+ ("F" . helm-projectile-find-file-in-known-projects)
+ ("p" . helm-projectile-switch-project)
+ ("r" . helm-projectile-recentf))
+ (def-projectile-commander-method ?b
+ "Find project buffer."
+ (call-interactively 'helm-projectile-switch-to-buffer))
+ (def-projectile-commander-method ?d
+ "Find directory in project."
+ (helm-projectile-find-dir))
+ (def-projectile-commander-method ?f
+ "Open project file."
+ (helm-projectile-find-file))
+ (def-projectile-commander-method ?p
+ "Switch project."
+ (helm-projectile-switch-project)))
+
+(use-package km-projectile
+ :defer t
+ :chords ("gp" . km/projectile-switch-project)
+ :init
+ (define-key km/projectile-ctl-x-4-map "v"
+ #'km/projectile-view-file-other-window)
+
+ (after 'projectile
+ (bind-keys :map projectile-command-map
+ ("4 v" . km/projectile-view-file-other-window)
+ ("." . km/projectile-copy-project-filename-as-kill)
+ ("e" . km/projectile-restore-thing)
+ ("k" . km/projectile-kill-buffers)
+ ("v" . km/projectile-view-file)
+ ("w" . km/projectile-save-thing))
+
+ (def-projectile-commander-method ?e
+ "Restore saved thing."
+ (km/projectile-restore-thing))
+ (def-projectile-commander-method ?k
+ "Kill all project buffers."
+ (call-interactively #'km/projectile-kill-buffers))
+ (def-projectile-commander-method ?v
+ "View project file."
+ (km/projectile-view-file))
+ (def-projectile-commander-method ?V
+ "View project file in other window."
+ (km/projectile-view-file-other-window))
+ (def-projectile-commander-method ?w
+ "Save thing."
+ (call-interactively #'km/projectile-save-thing))))
+
+
+;;; Version control
+
+(use-package vc
+ :defer t
+ :config
+ (setq vc-follow-symlinks t))
+
+(use-package vc-git
+ :commands vc-git-grep)
+
+(use-package smerge-mode
+ :defer t
+ :config
+ (setq smerge-diff-switches '("-d" "-b" "-u")))
+
+(use-package git-annex
+ :defer t
+ :after dired
+ :config
+ (setq git-annex-commit nil))
+
+(use-package magit
+ :load-path "~/src/emacs/magit/lisp/"
+ :defer t
+ :chords ("jg" . magit-status)
+ :init
+ (load "magit-autoloads.el")
+ (bind-keys :map km/git-map
+ ("d" . magit-dispatch-popup)
+ ("l" . magit-log-buffer-file)
+ ("s" . magit-stage-file))
+ (define-prefix-command 'km/magit-map)
+ (define-prefix-command 'km/magit-wip-map)
+ (define-key km/git-map "w" 'km/magit-wip-map)
+ :config
+ (setq magit-revert-buffers 'silent
+ magit-revert-buffers-only-for-tracked-files nil
+ magit-push-always-verify nil
+ magit-delete-by-moving-to-trash nil
+ magit-diff-auto-show-delay 0.1
+ magit-revision-show-gravatars nil
+ magit-log-section-arguments nil
+ magit-log-show-margin nil)
+ (setq magit-uniquify-buffer-names nil
+ magit-buffer-name-format "*%M%v: %t*")
+ (setq magit-no-confirm '(stage-all-changes unstage-all-changes reverse))
+
+ (setq magit-popup-show-help-echo nil
+ magit-popup-show-common-commands nil
+ magit-popup-use-prefix-argument 'default)
+ (setq magit-branch-arguments
+ (delete "--track" magit-branch-arguments))
+ (setq magit-patch-popup
+ (plist-put magit-patch-popup :use-prefix 'popup))
+
+ (remove-hook 'magit-refs-sections-hook #'magit-insert-tags)
+
+ (add-hook 'magit-post-display-buffer-hook
+ (lambda ()
+ (when (eq major-mode 'magit-status-mode)
+ (delete-other-windows))))
+
+ (define-key ctl-x-4-map "g" #'magit-find-file-other-window)
+ (define-key km/file-map "g" #'magit-find-file)
+
+ (bind-keys :map magit-mode-map
+ ("o" . magit-push-popup)
+ ;; Remove `magit-add-change-log-entry-other-window',
+ ;; which overrides my binding for
+ ;; `km/zsh-ansi-term-other-window'.
+ ("P" . magit-submodule-popup)
+ ("C-x 4 a" . nil))
+
+ (define-key magit-file-section-map (kbd "C-j")
+ #'magit-diff-visit-file-worktree)
+ (define-key magit-hunk-section-map (kbd "C-j")
+ #'magit-diff-visit-file-worktree)
+
+ (define-key magit-process-mode-map (kbd "C-c C-k") #'magit-process-kill)
+
+ (define-key magit-mode-map "." 'km/magit-map)
+ (bind-keys :map km/magit-map
+ ("l" . magit-toggle-buffer-lock))
+
+ (bind-keys :map km/magit-wip-map
+ ("a" . magit-wip-after-apply-mode)
+ ("b" . magit-wip-before-change-mode)
+ ("c" . magit-wip-commit)
+ ("f" . magit-wip-commit-buffer-file)
+ ("l" . magit-wip-log-current)
+ ("o" . magit-wip-log)
+ ("s" . magit-wip-after-save-mode)
+ ("S" . magit-wip-after-save-local-mode))
+
+ (define-key magit-popup-mode-map (kbd "SPC <t>")
+ #'magit-invoke-popup-switch)
+ (define-key magit-popup-mode-map (kbd "SPC SPC <t>")
+ #'magit-invoke-popup-option)
+
+ (magit-define-popup-action 'magit-diff-popup
+ ?e "Edit options" #'magit-diff-refresh-popup)
+
+ (magit-change-popup-key 'magit-stash-popup :action
+ ?Z ?s)
+
+ (magit-define-popup-switch 'magit-log-popup
+ ?p "First parent" "--first-parent")
+ (magit-define-popup-switch 'magit-log-popup
+ ?n "No merges" "--no-merges")
+ (magit-define-popup-switch 'magit-log-popup
+ ?t "Date order" "--date-order")
+
+ (magit-define-popup-action 'magit-log-popup
+ ?e "Edit options" 'magit-log-refresh-popup)
+ (magit-define-popup-action 'magit-log-popup
+ ?w "Log current WIP" 'magit-wip-log-current)
+ (magit-define-popup-action 'magit-log-popup
+ ?W "Log other WIP" 'magit-wip-log)
+
+ (magit-change-popup-key 'magit-branch-popup :action
+ ?c ?o)
+ (magit-change-popup-key 'magit-branch-popup :action
+ ?n ?C)
+ (magit-change-popup-key 'magit-branch-popup :action
+ ?m ?R)
+ (magit-change-popup-key 'magit-branch-popup :action
+ ?s ?v)
+
+ (defadvice magit-merge-editmsg (around km/magit-merge-editmsg-no-ff activate)
+ "Set '--no-ff' flag when running `magit-merge-editmsg'."
+ (let ((args '("--no-ff")))
+ ad-do-it)))
+
+(use-package km-magit
+ :defer t
+ :after magit
+ :init
+ (bind-keys :map km/git-map
+ ("." . km/magit-show-commit-at-point)
+ ("c" . km/magit-copy-commit-summary)
+ ("e" . km/magit-commit-extend-with-file)
+ ("f" . km/magit-reset-file)
+ ("i" . km/magit-insert-staged-file)
+ ("n" . km/magit-shorten-hash-at-point)
+ ("p" . km/magit-pin-file)
+ ("r" . km/magit-find-recently-changed-file)
+ ("u" . km/magit-auto-commit)
+ ("v" . km/magit-revfile-reset))
+ :config
+ (bind-keys :map magit-mode-map
+ ("Q" . km/magit-mode-bury-all-windows)
+ ("C-w" . km/magit-copy-as-kill))
+
+ (define-key magit-file-section-map (kbd "C-o")
+ #'km/magit-diff-visit-file-other-window)
+ (define-key magit-hunk-section-map (kbd "C-o")
+ #'km/magit-diff-visit-file-other-window)
+
+ (define-key magit-log-select-mode-map "."
+ #'km/magit-log-select-guess-fixup-commit)
+
+ (define-key magit-refs-mode-map (kbd "C-c C-t") #'km/magit-refs-toggle-tags)
+
+ (define-key magit-file-section-map [remap magit-visit-thing]
+ #'km/magit-diff-visit-file)
+ (define-key magit-hunk-section-map [remap magit-visit-thing]
+ #'km/magit-diff-visit-file)
+
+ (define-key magit-refs-mode-map (kbd "C-c C-f")
+ #'km/magit-refs-filter-recent)
+
+ (bind-keys :map km/magit-map
+ ("c" . km/magit-find-commit-file)
+ ("g" . km/git-map)
+ ("f" . km/magit-flip-revs))
+
+ (define-key km/magit-wip-map "w" #'km/magit-commit-wip-with-file)
+
+ (magit-define-popup-action 'magit-commit-popup
+ ?u "Auto commit" #'km/magit-auto-commit)
+
+ (magit-define-popup-action 'magit-push-popup
+ ?a "Push all" #'km/magit-push-all)
+ (magit-define-popup-action 'magit-push-popup
+ ?h "Push HEAD" #'km/magit-push-head)
+
+ (magit-define-popup-action 'magit-merge-popup
+ ?u "Merge upstream" #'km/magit-ff-merge-upstream)
+
+ (magit-define-popup-action 'magit-branch-popup
+ ?c "Create & checkout from current"
+ #'km/magit-branch-and-checkout-from-current)
+
+ (magit-define-popup-action 'magit-branch-popup
+ ?K "Delete previous branch" #'km/magit-delete-previous-branch)
+ (magit-define-popup-action 'magit-branch-popup
+ ?m "Checkout master" #'km/magit-checkout-master)
+ (magit-define-popup-action 'magit-branch-popup
+ ?n "Checkout recent ref" #'km/magit-checkout-recent-ref)
+ (magit-define-popup-action 'magit-branch-popup
+ ?N "Track recent ref" #'km/magit-checkout-track-recent-ref)
+ (magit-define-popup-action 'magit-branch-popup
+ ?l "Checkout previous" #'km/magit-checkout-previous-branch)
+ (magit-define-popup-action 'magit-branch-popup
+ ?r "Rename branch" #'km/magit-branch-rename)
+ (magit-define-popup-action 'magit-branch-popup
+ ?s "Backup current branch" #'km/magit-backup-branch)
+ (magit-define-popup-action 'magit-branch-popup
+ ?t "Local tracking" #'km/magit-checkout-local-tracking))
+
+(use-package magit-annex
+ :load-path "~/src/emacs/magit-annex/"
+ :defer t
+ :init (require 'magit-annex-autoloads)
+ :config
+ (setq magit-annex-unused-open-function #'org-open-file)
+ (setq magit-annex-all-action-arguments
+ (delete "--auto" magit-annex-all-action-arguments)))
+
+(use-package git-commit
+ :load-path "~/src/emacs/magit/lisp/"
+ :defer t
+ :config
+ (setq git-commit-finish-query-functions nil)
+
+ (add-hook 'git-commit-setup-hook
+ (lambda ()
+ (add-hook 'with-editor-pre-finish-hook
+ #'git-commit-save-message nil t)))
+ (add-hook 'git-commit-setup-hook #'git-commit-turn-on-flyspell))
+
+(use-package orgit
+ :load-path "~/src/emacs/orgit/"
+ :defer t
+ :init (require 'orgit-autoloads))
+
+
+;;; Command interfaces
+
+(use-package setkey
+ :bind ("C-c v" . setkey-call))
+
+(use-package god-mode
+ :bind (("C-c d" . god-local-mode)
+ ("C-x C-1" . delete-other-windows)
+ ("C-x C-2" . split-window-below)
+ ("C-x C-3" . split-window-right)
+ ("C-x C-0" . delete-window))
+ :config
+ (add-hook 'view-mode-hook
+ (lambda ()
+ (if view-mode (god-local-mode-pause) (god-local-mode-resume))))
+ (add-hook 'org-capture-mode-hook
+ (lambda () (god-local-mode -1)))
+
+ (add-hook 'god-mode-enabled-hook
+ (lambda ()
+ (when view-mode
+ (view-mode -1))
+ (when (derived-mode-p 'emacs-lisp-mode)
+ (lispy-mode -1))))
+ (add-hook 'god-mode-disabled-hook
+ (lambda ()
+ (when (derived-mode-p 'emacs-lisp-mode)
+ (lispy-mode 1))))
+ (bind-keys :map god-local-mode-map
+ ("." . repeat)
+ ("i" . god-local-mode)))
+
+(use-package km-god
+ :defer t
+ :after god-mode
+ :config
+ (add-to-list 'god-exempt-predicates #'km/god-gnus-p)
+ (add-hook 'god-mode-enabled-hook #'km/god-update-cursor)
+ (add-hook 'god-mode-disabled-hook #'km/god-update-cursor))
+
+(use-package km-hydra
+ :bind (("C-c n" . km/hydra-outline-mode)
+ ("C-c w" . hydra-window-map/body))
+ :commands (hydra-file-search-map/body
+ hydra-kmacro/body
+ hydra-multiple-cursors/body
+ hydra-org-link-edit/body
+ hydra-smerge/body)
+ :init
+ (bind-keys :map km/editing-map
+ ("k" . hydra-kmacro/body)
+ ("o" . hydra-multiple-cursors/body))
+ (define-key km/git-map "m" #'hydra-smerge/body)
+ (define-key km/file-map "s" #'hydra-file-search-map/body)
+ (after 'org
+ (define-key km/org-prefix-map "." #'hydra-org-link-edit/body)))
+
+
+;;; Helm
+
+(use-package helm
+ :defer t
+ :config
+ (setq helm-move-to-line-cycle-in-source t))
+
+(use-package helm-config
+ :defer t
+ :config
+ (global-set-key (kbd "C-x c") nil)
+ (customize-set-value 'helm-command-prefix-key "C-c h"))
+
+(use-package helm-buffers
+ :defer t
+ :chords ("jt" . helm-mini))
+
+(use-package helm-files
+ :defer t
+ :chords ("jc" . helm-find-files)
+ :config
+ (setq helm-ff-newfile-prompt-p nil
+ helm-ff-file-name-history-use-recentf t
+ helm-ff-skip-boring-files t))
+
+(use-package km-helm
+ :defer t
+ :after helm
+ :init
+ (after 'helm-files
+ (bind-keys :map helm-find-files-map
+ ("C-c x" . km/helm-ff-org-open-file)
+ ("C-c C-o" . km/helm-display-file)))
+ (after 'helm-locate
+ (bind-keys :map helm-generic-files-map
+ ("C-c x" . km/helm-ff-org-open-file)
+ ("C-c C-o" . km/helm-display-file)))
+ (after 'helm-buffers
+ (define-key helm-buffer-map (kbd "C-c C-o") #'km/helm-display-buffer)))
+
+(use-package helm-mode
+ :diminish helm-mode
+ :after helm
+ :config
+ (helm-mode 1))
+
+(use-package helm-command
+ :defer t
+ :chords ("kx" . helm-M-x))
+
+(use-package helm-swoop
+ :defer t
+ :init (define-key search-map "k" #'helm-swoop))
+
+(use-package helm-apropos
+ :bind ("C-h a" . helm-apropos))
+
+(use-package helm-imenu
+ :bind ("C-c l" . helm-imenu))
+
+(use-package helm-man
+ :defer t
+ :init (define-key km/external-map "m" #'helm-man-woman))
+
+
+;;; Expansion
+
+(use-package abbrev
+ :commands abbrev-mode
+ :diminish (abbrev-mode . "Ab")
+ :init
+ (add-hook 'text-mode-hook #'abbrev-mode)
+ (add-hook 'prog-mode-hook #'abbrev-mode)
+ :config
+ (define-abbrev-table 'typo-abbrev-table nil)
+ (abbrev-table-put global-abbrev-table :parents
+ (cons typo-abbrev-table
+ (abbrev-table-get global-abbrev-table :parents))))
+
+(use-package km-abbrev
+ :defer t
+ :init
+ (after 'abbrev
+ (bind-keys :map abbrev-map
+ ("c" . km/abbrev-add-case-global)
+ ("iu" . km/abbrev-inverse-add-uppercase-global))))
+
+(use-package hippie-exp
+ :bind ("M-/" . hippie-expand)
+ :config
+ (setq hippie-expand-try-functions-list '(try-complete-file-name-partially
+ try-complete-file-name
+ try-expand-all-abbrevs
+ try-expand-dabbrev
+ try-expand-dabbrev-all-buffers
+ try-expand-dabbrev-from-kill
+ try-complete-lisp-symbol-partially
+ try-complete-lisp-symbol)))
+
+(use-package yasnippet
+ :diminish yas-minor-mode
+ :commands km/yas-dummy
+ :init
+ (global-set-key (kbd "C-c i") #'km/yas-dummy)
+ :config
+ (defun km/yas-dummy ()
+ (interactive))
+ (global-set-key (kbd "C-c i") nil)
+ (setq yas-fallback-behavior nil)
+
+ (defun km/yas-with-comment (str)
+ (concat comment-start
+ (unless (s-ends-with? " " comment-start) " ")
+ str comment-end))
+
+ (defvar km/personal-snippets
+ (file-name-as-directory (expand-file-name "psnippets"
+ user-emacs-directory)))
+ (when (file-exists-p km/personal-snippets)
+ (add-to-list 'yas-snippet-dirs km/personal-snippets))
+
+ (bind-keys :map yas-minor-mode-map
+ ("C-c i" . yas-expand)
+ ;; Remove commands with 'C-c &' prefix, which conflicts
+ ;; with `org-mark-ring-goto' binding'
+ ("C-c &" . nil)
+ ("<tab>" . nil)
+ ("TAB" . nil))
+ (yas-global-mode))
+
+
+;;; Dired
+
+(use-package dired
+ :defer t
+ :init
+ (define-prefix-command 'km/dired-prefix-map)
+ :config
+ (require 'dired-x)
+ ;; .git is present as part of `dired-omit-extensions', but this
+ ;; seems to only be taken into account if a non-exension part
+ ;; exists.
+ (setq dired-omit-files
+ (concat dired-omit-files
+ "\\|^\\.git$\\|^\\.gitignore$"
+ "\\|^__pycache__$\\|^\\.snakemake$"))
+
+ (setq dired-omit-extensions
+ (append dired-omit-extensions
+ (list ".aux" ".fdb_latexmk" ".fls"
+ ".log" ".nav" ".out" ".snm")))
+
+ (setq-default dired-omit-mode t)
+
+ (setq dired-dwim-target t
+ dired-listing-switches "-alht")
+
+ (setq dired-guess-shell-alist-user '(("\\.pdf\\'" "zathura")))
+
+ (setq dired-recursive-copies t
+ dired-recursive-deletes t)
+
+ (put 'dired-find-alternate-file 'disabled nil)
+
+ (add-hook 'dired-mode-hook #'dired-hide-details-mode)
+
+ (define-key dired-mode-map "c" #'dired-do-copy)
+
+ (define-key dired-mode-map (kbd "C-c m") 'km/dired-prefix-map))
+
+(use-package km-dired
+ :bind ("C-x C-d" . km/dired-switch-to-buffer)
+ :init
+ (define-prefix-command 'km/dired-copy-filename-map)
+ (after 'projectile
+ (define-key km/dired-copy-filename-map "p"
+ #'km/dired-copy-project-filename-as-kill))
+ (define-key km/dired-copy-filename-map "o"
+ #'km/dired-copy-relative-filename-as-kill)
+ (define-key km/dired-copy-filename-map "w"
+ #'dired-copy-filename-as-kill)
+
+ (after 'dired
+ ;; This overrides the default binding for `dired-copy-filename-as-kill'.
+ (define-key dired-mode-map "w" #'km/dired-copy-filename-map)
+ (bind-keys :map dired-mode-map
+ ("w" . km/dired-copy-filename-map)
+ ("C" . km/dired-copy-and-edit)
+ ("V" . km/dired-view-file-other-window)))
+
+ (define-key ctl-x-4-map "D" #'km/dired-switch-to-buffer-other-window)
+
+ (after 'org
+ ;; This overrides `dired-find-file', which is also bound to "f".
+ (define-key dired-mode-map "e" #'km/org-open-dired-marked-files)))
+
+(use-package dired-narrow
+ :defer t
+ :init
+ (after 'dired
+ (define-prefix-command 'km/dired-narrow-prefix-map)
+ (bind-keys :map km/dired-narrow-prefix-map
+ ("f" . dired-narrow-fuzzy)
+ ("n" . dired-narrow)
+ ("r" . dired-narrow-regexp))
+ (define-key km/dired-prefix-map "n" 'km/dired-narrow-prefix-map)
+ (define-key dired-mode-map "/" #'dired-narrow)))
+
+(use-package dired-subtree
+ :defer t
+ :init
+ (after 'dired
+ (define-prefix-command 'km/dired-subtree-prefix-map)
+ (bind-keys :map km/dired-subtree-prefix-map
+ ("@" . dired-subtree-mark-subtree)
+ ("." . dired-subtree-unmark-subtree)
+ ("<" . dired-subtree-beginning)
+ (">" . dired-subtree-end)
+ ("g" . dired-subtree-revert)
+ ("d" . dired-subtree-down)
+ ("i" . dired-subtree-insert)
+ ("n" . dired-subtree-next-sibling)
+ ("p" . dired-subtree-previous-sibling)
+ ("r" . dired-subtree-remove)
+ ("s" . dired-subtree-narrow)
+ ("u" . dired-subtree-up))
+ (define-key km/dired-prefix-map "s" 'km/dired-subtree-prefix-map)))
+
+
+;;; Compilation and shells
+
+(setq shell-file-name "/bin/bash")
+(setq shell-command-switch "-c")
+
+(bind-keys :map km/external-map
+ ("r" . shell-command-on-region)
+ ("s" . shell-command)
+ ("S" . shell))
+
+(use-package grep
+ :defer t
+ :config
+ (defun km/grep-hide-header ()
+ (narrow-to-region
+ (save-excursion (goto-char (point-min))
+ (line-beginning-position 5))
+ (point-max)))
+
+ (add-hook 'grep-setup-hook #'km/grep-hide-header)
+ (add-hook 'grep-mode-hook #'toggle-truncate-lines))
+
+(use-package compile
+ :defer t
+ :init
+ (bind-keys :map km/compile-map
+ ("c" . compile)
+ ("g" . recompile))
+ :config
+ (defadvice compile (around prevent-duplicate-compilation-windows activate)
+ "Pop to compilation buffer only if it isn't visible.
+This is useful for using multiple frames (e.g., with a two
+monitor setup)."
+ (if (get-buffer-window (km/compilation-name-by-directory)
+ 'visible)
+ (save-window-excursion ad-do-it)
+ ad-do-it))
+
+ (defadvice recompile (around prevent-window-on-compilation activate)
+ "Prevent recompiling from spawning new windows."
+ (save-window-excursion ad-do-it)))
+
+(use-package km-compile
+ :after compile
+ :chords ("hv" . km/compilation-recompile)
+ :init
+ (bind-keys :map km/compile-map
+ ("h" . km/compile-in-home-dir)
+ ("o" . km/compilation-display-buffer))
+ :config
+ (setq compilation-buffer-name-function #'km/compilation-name-by-directory))
+
+(use-package km-shell
+ :chords ("kz" . km/zsh-toggle-ansi-term-home)
+ :init
+ (define-key ctl-x-4-map "a" #'km/zsh-ansi-term-other-window)
+ (bind-keys :map km/external-map
+ ("a" . km/zsh-ansi-term)
+ ("t" . km/open-external-terminal)))
+
+
+;;; Other external programs
+
+(use-package browse-url
+ :defer t
+ :init
+ (define-key km/external-map "b" #'browse-url)
+ :config
+ (setq browse-url-browser-function 'browse-url-generic
+ browse-url-generic-program "firefox"))
+
+(use-package webjump
+ :defer t
+ :config
+ (setq webjump-sites
+ '(("Arch User Repository" .
+ [simple-query "https://aur.archlinux.org"
+ "https://aur.archlinux.org/packages/?K=" ""])
+ ("DuckDuckGo" .
+ [simple-query "https://duckduckgo.com"
+ "https://duckduckgo.com/?q=" ""])
+ ("Emacs Wiki" .
+ [simple-query "www.emacswiki.org"
+ "www.emacswiki.org/cgi-bin/wiki/" ""])
+ ("GitHub" . "https://github.com")
+ ("GitHub search" .
+ [simple-query "https://github.com"
+ "https://github.com/search?q=" ""])
+ ("Google" .
+ [simple-query "www.google.com"
+ "www.google.com/search?q=" ""])
+ ("Google Scholar" .
+ [simple-query "http://scholar.google.com"
+ "http://scholar.google.com/scholar?&q=" ""])
+ ("Wikipedia" .
+ [simple-query "wikipedia.org"
+ "wikipedia.org/wiki/" ""]))))
+
+(use-package km-webjump
+ :after webjump
+ :defer t
+ :init
+ (define-key km/external-map "j" #'km/webjump))
+
+(use-package select
+ :defer t
+ :config
+ (setq x-select-enable-clipboard t
+ x-select-enable-primary t))
+
+(use-package man
+ :defer t
+ :config
+ (setq Man-notify-method 'aggressive))
+
+(use-package mailcap
+ :config
+ (mailcap-parse-mailcaps)
+ (pcase-dolist (`(_ . ,info)
+ (cdr (assoc-string "application" mailcap-mime-data)))
+ ;; Instead of deleting doc-view-mode entry, just make its test
+ ;; always fail.
+ (when (eq (cdr (assq 'viewer info)) 'doc-view-mode)
+ (setf (cdr (assq 'test info)) (lambda (&rest _) nil)))))
+
+(use-package diff
+ :defer t
+ :config
+ (setq diff-command "/bin/diff"
+ diff-switches "-u"))
+
+(use-package ediff
+ :defer t
+ :init
+ (define-key km/external-map "e" #'ediff)
+ :config
+ (setq ediff-window-setup-function #'ediff-setup-windows-plain))
+
+(use-package km-diff
+ :defer t
+ :init
+ (bind-keys :map km/external-map
+ ("o" . km/ediff-with-other-window)
+ ("d" . km/diff))
+ :config
+ (after 'diff-mode-map
+ (define-key diff-mode-map (kbd "C-c C-g") #'km/revert-buffer-and-view)))
+
+
+;;; Text modes
+
+(add-hook 'text-mode-hook #'turn-on-auto-fill)
+
+(use-package footnote
+ :defer t
+ :config
+ (setq footnote-section-tag ""))
+
+(use-package ispell
+ :defer t
+ :init
+ (define-key km/external-map "i" #'ispell-buffer)
+ (define-key km/editing-map "w" #'ispell-word)
+ :config
+ (setq ispell-program-name "aspell"))
+
+(use-package flyspell
+ :defer t
+ :diminish (flyspell-mode . "Fy")
+ :config
+ (setq flyspell-auto-correct-binding (kbd "C-c e ;"))
+ (define-key flyspell-mode-map (kbd "C-.") nil))
+
+(use-package tex-site
+ :mode ("\\.[tT]e[xX]\\'" . TeX-latex-mode)
+ :config
+ (setq font-latex-fontify-sectioning 'color)
+ (setq TeX-electric-math '("$" . "$"))
+
+ (after 'latex
+ (put 'LaTeX-narrow-to-environment 'disabled nil)
+ (add-hook 'LaTeX-mode-hook #'turn-on-reftex)
+ (add-hook 'LaTeX-mode-hook #'flyspell-mode)))
+
+(use-package km-tex
+ :defer t
+ :after latex
+ :config
+ (add-hook 'LaTeX-mode-hook
+ (lambda ()
+ (setq imenu-create-index-function
+ #'km/latex-imenu-create-index-function))))
+
+(use-package reftex
+ :diminish (reftex-mode . "Rf")
+ :defer t
+ :config
+ (setq reftex-default-bibliography '("refs.bib")))
+
+(use-package bibtex
+ :defer t
+ :config
+ ;; Make cite key have form <last author last name><year><first word>.
+ (setq bibtex-autokey-titlewords 1
+ bibtex-autokey-titleword-ignore '("A" "An" "On" "The" "[0-9].*")
+ bibtex-autokey-titleword-length nil
+ bibtex-autokey-titlewords-stretch 0
+ bibtex-autokey-year-length 4
+ bibtex-autokey-year-title-separator "")
+ (setq bibtex-align-at-equal-sign t) ; Used by `bibtex-fill-entry'.
+ (setq bibtex-entry-format
+ (append '(realign whitespace last-comma delimiters sort-fields)
+ bibtex-entry-format)))
+
+(use-package km-bib
+ :after bibtex
+ :config
+ (dolist (h '(km/bibtex-delete-article-fields
+ km/bibtex-downcase-author-and
+ km/bibtex-downcase-entry
+ km/bibtex-downcase-keys
+ km/bibtex-pages-use-double-hyphen
+ km/bibtex-use-title-case
+ km/bibtex-remove-doi-leader
+ km/bibtex-remove-entry-space
+ km/bibtex-set-coding-system
+ km/bibtex-single-space-author-list))
+ (add-hook 'bibtex-clean-entry-hook h)))
+
+
+;;; Language modes
+
+(add-to-list 'auto-mode-alist '("\\.zsh\\'" . shell-script-mode))
+(add-to-list 'auto-mode-alist '("\\.*rc\\'" . conf-unix-mode))
+
+(add-hook 'after-save-hook
+ #'executable-make-buffer-file-executable-if-script-p)
+
+(global-set-key (kbd "C-c x") #'eval-expression)
+
+(use-package haskell-mode
+ :defer t
+ :config
+ (setq haskell-process-show-debug-tips nil)
+ (add-hook 'haskell-mode-hook #'turn-on-haskell-indentation)
+ (add-hook 'haskell-mode-hook #'interactive-haskell-mode)
+ (add-hook 'haskell-mode-hook #'turn-on-haskell-doc)
+ (bind-keys :map haskell-mode-map
+ ("C-x C-d" . nil)
+ ("C-c C-z" . haskell-interactive-switch)
+ ("C-c C-l" . haskell-process-load-file)
+ ("C-c C-b" . haskell-interactive-switch)
+ ("C-c C-t" . haskell-process-do-type)
+ ("C-c C-i" . haskell-process-do-info)
+ ("C-c M-." . nil)
+ ("C-c C-d" . nil)))
+
+(use-package elisp-mode
+ :defer t
+ :config
+ (defun km/elisp-outline-level ()
+ (and (looking-at (concat "^" outline-regexp))
+ (- (match-end 0) (match-beginning 0) 3)))
+
+ (defun km/elisp-set-outline-vars ()
+ (setq outline-regexp ";;;;* ")
+ (setq outline-level #'km/elisp-outline-level))
+
+ (add-hook 'emacs-lisp-mode-hook #'km/elisp-set-outline-vars))
+
+(use-package find-function
+ :bind (("C-h ;" . find-function)
+ ("C-h 4 ;" . find-function-other-window)))
+
+(use-package paredit
+ :defer t
+ :diminish (paredit-mode . "Pe"))
+
+(use-package lispy
+ :defer t
+ :init
+ (add-hook 'emacs-lisp-mode-hook #'lispy-mode)
+ :config
+ (setq lispy-no-permanent-semantic t)
+ (add-hook 'lispy-mode-hook #'km/elisp-set-outline-vars)
+ ;; This is ugly, but I haven't found another way to stop
+ ;; `imenu-create-index-function' from being set to
+ ;; `semantic-create-imenu-index'. Trying to set it in
+ ;; `emacs-lisp-mode-hook' or `lispy-mode-hook' doesn't work.
+ (defalias 'semantic-create-imenu-index 'imenu-default-create-index-function))
+
+(use-package python
+ :defer t
+ :init
+ (add-to-list 'interpreter-mode-alist '("python2" . python-mode))
+ (add-to-list 'interpreter-mode-alist '("python3" . python-mode))
+ (define-prefix-command 'km/python-prefix-map)
+ :config
+ (setq python-fill-docstring-style 'pep-257-nn
+ python-indent-guess-indent-offset nil)
+ (setq python-shell-interpreter "ipython"
+ python-shell-prompt-detect-enabled nil)
+
+ (defun km/python-outline-level ()
+ (and (looking-at (concat "^" outline-regexp))
+ (- (match-end 0) (match-beginning 0) 3)))
+
+ (defun km/python-set-local-vars ()
+ (setq outline-regexp "####* ")
+ (setq outline-level #'km/python-outline-level)
+ ;; Stop semantic from taking over imenu.
+ (setq imenu-create-index-function #'python-imenu-create-index)
+ (set (make-local-variable 'compile-command) "py.test"))
+ (add-hook 'python-mode-hook #'km/python-set-local-vars)
+
+ (bind-keys :map python-mode-map
+ ("C-c C-b" . python-shell-send-buffer)
+ ("C-c C-f" . python-shell-send-defun)
+ ("C-M-x" . python-eldoc-at-point)))
+
+(use-package pydoc
+ :load-path "~/src/emacs/pydoc/"
+ :defer t
+ :config
+ (setq pydoc-make-method-buttons nil)
+ ;; Don't shadow my `ace-link' binding.
+ (define-key pydoc-mode-map "o" #'ace-link-help))
+
+(use-package km-python
+ :after python
+ :bind ("C-h y" . km/pydoc)
+ :init
+ (bind-keys :map km/python-prefix-map
+ ("c" . km/python-copy-last-shell-line-as-comment)
+ ("t" . km/find-python-test-file-other-window))
+ :config
+ (bind-keys :map python-mode-map
+ ("C-c C-." . km/python-shell-send-buffer-up-to-line)
+ ("C-c C-c" . km/python-shell-send-function-or-paragraph-and-step)
+ ("C-c C-d" . km/python-shell-send-set-string)
+ ("C-c m" . km/python-prefix-map))
+
+ (add-hook 'python-mode-hook
+ (lambda ()
+ (add-hook
+ 'post-self-insert-hook
+ #'km/python-indent-post-self-insert-function 'append 'local)))
+
+ (add-hook 'pydoc-after-finish-hook #'km/pydoc-store-name)
+ (when (file-exists-p km/pydoc-names-file)
+ (km/pydoc-read-names-file km/pydoc-names-file)))
+
+(use-package snakemake-mode
+ :load-path "~/src/emacs/snakemake-mode/"
+ :defer t
+ :init
+ (require 'snakemake-mode-autoloads)
+ (autoload 'snakemake-compile-command "snakemake-mode")
+ (setq snakemake-compile-command-options '("-p"))
+ :config
+ (defun km/snakemake-set-local-vars ()
+ (set (make-local-variable 'compile-command)
+ (snakemake-compile-command))
+ (set (make-local-variable 'imenu-create-index-function)
+ #'snakemake-imenu-create-index))
+
+ ;; Although `compile-command' and `imenu-create-index-function' are
+ ;; set when snakemake-mode is derived from Python mode, I need to
+ ;; define them again here because I have a Python mode hook
+ ;; overrides the Python versions.
+ (add-hook 'snakemake-mode-hook #'km/snakemake-set-local-vars))
+
+(use-package km-snakemake
+ :defer t
+ :init
+ (bind-keys :map km/compile-map
+ ("b" . km/snakemake-compile-project-rule)
+ ("p" . km/snakemake-compile-project-file))
+ (after 'dired
+ (define-key dired-mode-map "b" #'km/snakemake-compile-project-file)))
+
+(use-package ess-site
+ :mode ("\\.[rR]\\'" . R-mode)
+ :config
+ (setq ess-smart-S-assign-key ";")
+ (ess-toggle-S-assign nil)
+ (ess-toggle-S-assign nil)
+ (setq ess-use-ido nil)
+
+ (define-abbrev-table 'ess-mode-abbrev-table
+ '(("true" "TRUE")
+ ("false" "FALSE"))
+ :system t)
+ (dolist (hook '(ess-mode-hook inferior-ess-mode-hook))
+ (add-hook hook (lambda ()
+ (setq local-abbrev-table ess-mode-abbrev-table)))
+ (add-hook hook #'abbrev-mode)))
+
+(use-package km-ess
+ :defer t
+ :config
+ (bind-keys :map ess-mode-map
+ ("C-c C-." . km/ess-eval-buffer-up-to-line)
+ ("|" . km/ess-insert-dplyr-pipe))
+ (after 'ess-inf
+ (define-key inferior-ess-mode-map "|" #'km/ess-insert-dplyr-pipe)))
+
+
+;;; Mail
+
+(use-package notmuch
+ :defer t
+ :init (define-key km/mail-map "n" #'notmuch)
+ :config
+ (setq notmuch-fcc-dirs nil
+ notmuch-search-oldest-first nil)
+ (add-to-list 'notmuch-saved-searches
+ '(:name "today" :query "date:today.." :key "."))
+ (define-prefix-command 'km/notmuch-show-prefix-map)
+ (define-key notmuch-show-mode-map (kbd "C-c m") 'km/notmuch-show-prefix-map)
+
+ (define-key km/notmuch-show-prefix-map "i"
+ #'km/notmuch-show-copy-message-id-as-kill))
+
+(use-package message
+ :defer t
+ :config
+ (setq message-send-mail-function 'message-send-mail-with-sendmail
+ message-sendmail-envelope-from 'header
+ message-kill-buffer-on-exit t)
+ (add-hook 'message-mode-hook #'flyspell-mode))
+
+(use-package mml
+ :defer t
+ :diminish (mml-mode . "ML"))
+
+(use-package mm-decode
+ :defer t
+ :config
+ (setq mm-discouraged-alternatives '("text/html" "text/richtext")))
+
+(use-package gnus
+ :defer t
+ :init
+ (bind-keys :map km/mail-map
+ ("g" . gnus)
+ ("p" . gnus-plugged)
+ ("u" . gnus-unplugged))
+ :config
+ (setq gnus-home-directory "~/.gnus.d/"
+ gnus-directory gnus-home-directory
+ gnus-article-save-directory (expand-file-name "saved/" gnus-directory)
+ gnus-kill-files-directory (expand-file-name "scores/" gnus-directory))
+ (setq gnus-startup-file (expand-file-name "newsrc" gnus-home-directory)
+ gnus-init-file (expand-file-name "gnus" gnus-home-directory)
+ gnus-save-newsrc-file nil
+ gnus-read-newsrc-file nil
+ gnus-inhibit-startup-message t)
+ (setq gnus-summary-line-format "%U%R %&user-date;%-20= %-15,15f %B %S \n")
+ (setq gnus-gcc-mark-as-read t
+ gnus-visible-headers '("^From" "^Subject" "^Date" "^To" "^Cc" "^User-Agent")
+ gnus-confirm-mail-reply-to-news t)
+ (setq gnus-agent-go-online t
+ gnus-agent-synchronize-flags t)
+ (setq gnus-inhibit-images t)
+ (setq gnus-interactive-exit nil)
+
+ (setq gnus-topic-display-empty-topics nil
+ gnus-group-line-format "%M\%S\%p\%P\%5y:%B%(%G%)\n"
+ gnus-group-list-inactive-groups nil)
+
+ (setq gnus-group-use-permanent-levels t)
+
+ (setq gnus-auto-select-next 'quietly)
+
+ (setq gnus-thread-hide-subtree t
+ gnus-thread-sort-functions '(gnus-thread-sort-by-most-recent-number))
+ (define-key gnus-group-mode-map "e" #'gnus-group-select-group))
+
+(use-package gnus-sum
+ :defer t
+ :init
+ (define-prefix-command 'km/gnus-summary-prefix-map)
+ :config
+ (setq gnus-sum-thread-tree-indent " "
+ gnus-sum-thread-tree-root "."
+ gnus-sum-thread-tree-false-root "o "
+ gnus-sum-thread-tree-single-indent ""
+ gnus-sum-thread-tree-leaf-with-other "+-> "
+ gnus-sum-thread-tree-vertical "| "
+ gnus-sum-thread-tree-single-leaf "`-> ")
+ (bind-keys :map gnus-summary-mode-map
+ ("e" . gnus-summary-scroll-up)
+ ("v" . org-capture)
+ (";" . gnus-summary-universal-argument))
+ (define-key gnus-summary-mode-map (kbd "C-c m")
+ 'km/gnus-summary-prefix-map))
+
+(use-package gnus-art
+ :defer t
+ :init
+ (define-prefix-command 'km/gnus-article-prefix-map)
+ :config
+ (define-key gnus-article-mode-map "v" #'org-capture)
+ (define-key gnus-article-mode-map (kbd "C-c m")
+ 'km/gnus-article-prefix-map))
+
+(use-package shr
+ :defer t
+ :config
+ (bind-keys :map shr-map
+ ;; Allow `km/ace-link-widget' binding to work even when
+ ;; on shr widget.
+ ("o" . nil)
+ ("O" . shr-save-contents)
+ ("v" . nil)))
+
+(use-package sendmail
+ :defer t
+ :config
+ (setq sendmail-program "/usr/bin/msmtp"))
+
+(use-package km-mail
+ :defer t
+ :after gnus
+ :config
+ (add-hook 'message-send-hook #'km/message-confirm-sender)
+ (add-hook 'kill-emacs-hook #'km/gnus-grace-exit-before-kill-emacs)
+
+ (setq gnus-group-sort-function '(gnus-group-sort-by-alphabet
+ km/gnus-group-sort-by-topic
+ gnus-group-sort-by-level))
-(require 'init-org)
-(require 'init-helm)
+ (after 'gnus-sum
+ (bind-keys :map gnus-summary-mode-map
+ ("c" . km/gnus-summary-catchup)
+ ("l" . km/gnus-copy-message-link)
+ ("o" . km/ace-link-widget)))
+ (after 'gnus-art
+ (bind-keys :map gnus-article-mode-map
+ ("C-c l" . km/gnus-follow-last-message-link)
+ ("e" . km/shr-browse-url-and-goto-next)
+ ("o" . km/ace-link-widget)))
-(require 'init-files)
-(require 'init-buffers)
-(require 'init-framewin)
-(require 'init-ace)
-(require 'init-view)
+ (bind-keys :map km/gnus-summary-prefix-map
+ ("p" . km/gnus-open-github-patch)
+ ("l" . km/gnus-copy-gmane-link-as-kill))
+ (bind-keys :map km/gnus-article-prefix-map
+ ("p" . km/gnus-open-github-patch)
+ ("l" . km/gnus-copy-gmane-link-as-kill)))
-(require 'init-editing)
-(require 'init-outline)
-(require 'init-god)
+
+;;; Miscellaneous configuration
-(require 'init-elisp)
-(require 'init-haskell)
-(require 'init-python)
-(require 'init-ess)
+(setq echo-keystrokes 0.1
+ use-dialog-box nil
+ visible-bell t
+ enable-recursive-minibuffers t)
-(require 'init-tex)
-(require 'init-bib)
-(require 'init-bog)
+(setq bookmark-save-flag nil)
-(require 'init-dired)
-(require 'init-git)
-(require 'init-projectile)
-(require 'init-snakemake)
+;; This is intentionally not loaded.
+(setq custom-file "~/.emacs.d/.custom.el")
-(require 'init-external)
+(setq default-input-method "TeX")
-(require 'init-yas)
+(defalias 'yes-or-no-p 'y-or-n-p)
-(when (file-exists-p (expand-file-name "init-untracked.el" km/init-lisp-dir))
- (require 'init-untracked))
+(when (file-exists-p (expand-file-name "km-untracked.el" km/init-lisp-dir))
+ (require 'km-untracked))
-(require 'init-mail)
-(require 'init-server)
+(use-package server
+ :config
+ (setq server-use-tcp t)
+ (unless (server-running-p)
+ (server-start))
+ (let ((server (daemonp)))
+ (cond
+ ((string= server "default")
+ ;; Remove all mail map bindings except notmuch.
+ (global-set-key (kbd "C-x m") nil)
+ (global-set-key (kbd "C-x m n") #'notmuch)
+ (after 'km-python
+ (add-hook 'kill-emacs-hook #'km/pydoc-save-names-file))
+ (setq save-abbrevs 'silently
+ bookmark-save-flag 1))
+ ((string= server "mail")
+ (setq mode-line-misc-info
+ (cons (propertize " [Mail] " 'face 'font-lock-doc-face)
+ mode-line-misc-info))
+ (key-chord-define-global "jg" 'km/mail-map)
+ (setq recentf-save-file "~/.emacs.d/cache/recentf-mail")
+ (setq save-abbrevs nil)))))
;;; init.el ends here