Skip to content

Latest commit

 

History

History
2223 lines (1731 loc) · 56.4 KB

init.org

File metadata and controls

2223 lines (1731 loc) · 56.4 KB

Header bits

Before anything else, we need to bootstrap up a working implementation of use-package so that we can install all the other packages that we desire

(setq package-archives
 (quote
  (("gnu" . "http://elpa.gnu.org/packages/")
   ("org" . "http://orgmode.org/elpa/")
   ; ("marmalade" . "http://marmalade-repo.org/packages/")
   ("melpa" . "http://melpa.milkbox.net/packages/"))))
(package-initialize)
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))
(eval-when-compile
  (require 'use-package))
(customize-set-variable 'use-package-always-ensure t)

Load the string package. We need this for a lot of the other code.

(use-package s)

Common libraries

evil

(use-package evil
  :demand t
  :init (setq evil-want-integration nil)
  :hook (git-commit-mode . evil-insert-state)
  :custom
  (evil-insert-state-modes
   '(comint-mode erc-mode eshell-mode geiser-repl-mode gud-mode
   inferior-apl-mode inferior-caml-mode inferior-emacs-lisp-mode
   inferior-j-mode inferior-python-mode inferior-scheme-mode
   inferior-sml-mode internal-ange-ftp-mode prolog-inferior-mode
   reb-mode shell-mode slime-repl-mode term-mode wdired-mode))
  :config
  (evil-mode)
  (add-hook 'git-commit-mode-hook #'evil-insert-state)
  (evil-define-operator evil-narrow-op (begin end type)
    "Evil indirect *narrow* operator."
    (interactive "<R>")
    (cond ((buffer-narrowed-p) (widen))
	  (t (narrow-to-region begin end))))

  (define-key evil-motion-state-map (kbd "g n") #'evil-narrow-op))

general

The general package is a set of convenience scripts for defining key bindings

(use-package general
  :custom
  (general-default-prefix "SPC")
  (general-default-non-normal-prefix "M-SPC")
  (general-default-states '(normal insert emacs))
  :config
  (general-simulate-key "C-c C-c"
   :docstring "Run whatever the native mode's C-c C-c command would be"
   :name general-simulate-C-c_C-c-in-emacs-state)

  (general-define-key
   :keymaps 'override
   "cc" #'general-simulate-C-c_C-c-in-emacs-state)

  (general-define-key
   :keymaps 'with-editor-mode-map
   "ck" 'with-editor-cancel)

  (general-define-key
   :infix "x"
   :keymaps 'override
   "s" 'save-buffer
   "B" 'ibuffer
   "k" 'kill-this-buffer
   "e" 'eval-last-sexp))

(defun avy-jump-link ()
   (interactive)
   (avy--generic-jump "https://" nil 'pre))

(general-define-key
 :prefix 'nil
 :infix 'nil
 "M-o" 'avy-jump-link)

evil collection

A set of evil bindings for multiple modes. This should make life less frustrating

(use-package evil-collection
  :after evil
  :custom
  (evil-collection-setup-minibuffer t)
  :config
  (evil-collection-init))

which-key

(use-package which-key
  :diminish which-key-mode
  :custom
  (which-key-show-operator-state-maps t)
  :config
  (which-key-mode))

evil-magit

(use-package evil-magit
  :after (evil magit))

evil org mode

(use-package evil-org
  :diminish evil-org-mode
  :after (org evil)
  :hook ((org-mode . evil-org-mode)
	 (evil-org-mode . evil-org-set-key-theme)))

evil-indent-plus

Evil indent plus does a great job at handling Python and Haskell source code

(use-package evil-indent-plus
  :after evil
  :config
  (evil-indent-plus-default-bindings))

evil quickscope

evil quickscope highlight unique characters in the words around the cursor to identify the best options for using the f/t/F/T keys for navigation. If there is no best single character, it uses a blue highlight to indicate that a 2f/2F will still find the correct word.

(use-package evil-quickscope
  :after evil
  :config
  (global-evil-quickscope-mode 1))

Evil Goggles

Evil google should make learning evil slightly easier, as it shows the exact regions chosen.

(use-package evil-goggles
  :after evil
  :config
  (evil-goggles-mode))

evil text object python

This package allows for using evil operations on the structure of python statements, instead of just looking at things on a line by line basis. Due to Python’s whitespace sensitive setup, this might be necessary.

(use-package evil-text-object-python
  :after evil
  :hook (python-mode . evil-text-object-python-add-bindings))

evil matichit

(use-package evil-matchit
  :after evil
  :config
  (global-evil-matchit-mode 1))

evil-escape

(use-package evil-escape
  :after evil
  :diminish evil-escape-mode
  :custom
  (evil-escape-unordered-key-sequence t)
  (evil-escape-key-sequence "jk")
  :config
  (evil-escape-mode))

evil easymotion

easymotion helps with the fact that I don’t instantly know how many lines or characters I’m looking at 90% of the time when using evil.

(use-package evil-easymotion
  :after evil
  :config
  (evilem-default-keybindings "RET"))

evil commentary

Evil commentary should hopefully give me the commenting options that evil-nerd-commenter sould never get working right

(use-package evil-commentary
  :after evil
  :config
  (evil-commentary-mode))

evil numbers

This should allow for easier number manipulation in evil mode

(use-package evil-numbers
  :after evil
  :general
  (:prefix 'nil :infix "g"
  "+" 'evil-numbers/inc-at-pt
  "-" 'evil-numbers/dec-at-pt))

Machine recognition

Since different computers have different file structures and capabilities, Emacs needs to customise itself for the specific computer that it is running on. To this end, the functions below identify computers and operating systems. This should simplify much of the code.

(defun insert-system-name ()
  "Get current system's name."
  (interactive)
  (insert (format "%s" (system-name))))

(defun insert-system-type ()
  "Get the current system OS."
  (interactive)
  (insert (format "%s" system-type)))

(defun system-is-darwin ()
  "Are we on a Mac?"
  (string-equal system-type "darwin"))

(defun system-is-windows ()
  "Are we on (*shudder*) Windows?"
  (string-equal system-type "windows-nt"))

(defun system-is-linux ()
  "Are we on Linux?"
  (string-equal system-type "gnu/linux"))

(defun system-is-arch ()
  "Are we on the Arch Virtualbox?"
  (or
   (s-starts-with? "NDLT969a" (system-name))
   (s-starts-with? "NDW1748" (system-name))))

(defun system-is-sheffield ()
  "Are we on the old Sheffield workstation?"
  (s-ends-with? "shef.ac.uk" (system-name)))

(defun system-is-macbook ()
  "Are we on my Sheffield Macbook?"
  (or
   (s-starts-with? "adams-mbp" (system-name))
   (s-starts-with? "Adams-MBP" (system-name))
   (s-starts-with? "Adams-MacBook" (system-name))))

Handle system paths

(let
    ((mypaths
      (cond
       ((system-is-sheffield)
	(list
	 "$NPM_PACKAGES/bin"
	 "/home/adam/.local/bin"
	 "/home/adam/bin"
	 "/usr/local/texlive/2015/bin/x86_64-linux"
	 "/usr/local/MATLAB/MATLAB_Compiler_Runtime/v82/runtime/glnxa64"
	 "/usr/local/MATLAB/MATLAB_Compiler_Runtime/v82/bin/glnxa64"
	 "/usr/local/MATLAB/MATLAB_Compiler_Runtime/v82/sys/os/glnxa64"
	 "/usr/local/MATLAB/MATLAB_Compiler_Runtime/v82/sys/java/jre/glnxa64/jre/lib/amd64/native_threads"
	 "/usr/local/MATLAB/MATLAB_Compiler_Runtime/v82/sys/java/jre/glnxa64/jre/lib/amd64/server"
	 "/usr/local/MATLAB/MATLAB_Compiler_Runtime/v82/sys/java/jre/glnxa64/jre/lib/amd64"
	 "/home/adam/.cabal/bin"
	 "/home/adam/.npm-packages/bin/"
	 "/usr/local/bin"
	 "/home/adam/Science/LINUX64"
	 "/opt/maple18/bin"
	 "/usr/local/cuda-7.5/bin"
	 "/usr/bin"
	 "/bin"
	 (getenv "PATH")))
	 ((system-is-macbook)
	  (list
	   "/Users/adam/Library/Python/2.7/bin/"
	   "/Users/adam/.local/bin/"
	   "/opt/local/bin"
	   "/opt/local/sbin"
	   "/usr/local/bin"
	   "/usr/bin"
	   "/bin"
	   "/usr/sbin"
	   "/sbin"
	   "/opt/X11/bin"
	   "/Library/Frameworks/Mono.framework/Versions/Current/Commands"))
	 ((system-is-arch)
	  (append
	   (split-string (getenv "PATH") ":")
	   (list "~/bin")))
       ('t (split-string (getenv "PATH") ":")))))
  (if
      (not (system-is-windows))
      (progn
	(setenv "PATH" (mapconcat 'identity mypaths ":"))
	(setq exec-path (append mypaths (list "." exec-directory))))))

(setq w32-apps-modifier 'super)

Prettify

Next, let’s get rid of the window chrome. It’s just so ugly.

(tool-bar-mode -1)
(scroll-bar-mode -1)
(menu-bar-mode -1)

Similarly, get rid of the awful startup screen.

(setq inhibit-startup-screen t)

Let’s set the default font and size

(set-fontset-font "fontset-default" nil
		  (font-spec :size 12 :name "DejaVu Sans"))

(set-fontset-font "fontset-default" nil
		  (font-spec :size 20 :name "DejaVu Sans"))

Make everything pretty!

(global-prettify-symbols-mode t)

Unsorted

Use diminish to stop minor modes from taking over the entire taskbar.

(use-package diminish
  :config
  (diminish 'auto-revert-mode "")
  (diminish 'auto-fill-mode "")
  (diminish 'visual-line-mode "")
  (diminish 'flyspell-mode "")
  (diminish 'undo-tree-mode "")
  (diminish 'auto-fill-function ""))

Always use spaces instead of tabs to avoid complaints from bored people on the internet.

'(indent-tabs-mode nil)

Use the TeX input method to get those glorious unicode characters.

(setq default-input-method "TeX")
(toggle-input-method)

Emacs gives us line numbers by default, but not column numbers. I think that that’s a legacy decision left over from the terminal days? Either way, I disagree with it, so we’ll put the column numbers in.

(setq column-number-mode t)

Tell emacs to treat all themes as safe. This is, honestly, a gapping security hole, but I only install themes from trusted sources and I’m not auditing them as it currently is. Plus, this gets the terrible custom-safe-themes variable out of customize

(setq custom-safe-themes t)

Give a default e-mail address.

(setq user-mail-address "[email protected]")

I don’t like emacs backup files. They’re coarse and rough and irritating, and the get everywhere. I’m going to confine them to a single directory.

(setq backup-by-copying t)
(setq backup-directory-alist (quote (("." . "~/.saves"))))
(setq delete-old-versions t)
(setq kept-new-versions 6)
(setq vc-make-backup-files t)
(setq version-control t)

Themes

Load a theme based on my base16 configurations

(load-file "~/Code/dotfiles/base16/emacs")

Apps

Dired

Dired is a wonderful way of handling directories.

(use-package dired
  :commands dired
  :custom
  (dired-dwim-target t)
  (dired-listing-switches "-alh"))

Get dired to intergate with imenu, since that just makes sense.

(use-package dired-imenu)

Direct Quick Sort offers more sorting options than just name and time

(use-package dired-quick-sort
  :config
  (dired-quick-sort-setup))

Dired-collapse gets rid of annoying chains of single file directories

(use-package dired-collapse)

eshell

fish completion

When eshell can’t find a completion, let fish take a shot at it

(use-package fish-completion
  :after eshell
  :config
  (global-fish-completion-mode))

Images

Load images as images, instead of as bye arrays

(setq auto-image-file-mode t)

Always revert images files without asking.

(setq revert-without-query '(".png"))

magit

(use-package magit
  :commands (magit)
  :custom
  (diff-switches "-u")
  (magit-commit-arguments (quote ("--gpg-sign=0D2B93AB0C87BAF1")))
  (magit-bury-buffer-function 'magit-mode-quit-window)
  :init
  (if
      (system-is-macbook)
      (setq magit-git-executable "/usr/bin/git")))

magithub

This package let’s me interface with github through magit. Anything to stay out of the browser.

(use-package magithub
  :after magit
  :config (magithub-feature-autoinject t))

ledger-mode

(use-package ledger-mode
  :mode "\\.ledger\\'")

Code

Universal

Which-function mode helps me when I’m stuck in some giant routine and lose track of where I am in the program. There’s the function, right there on the modeline.

(which-function-mode 't)
(set-face-foreground 'which-func (face-foreground font-lock-variable-name-face))

Skeletor

Handles the generation of project skeletons

(use-package skeletor
  :custom
  (skeletor-project-directory "~/Code"))

C♯

Set the C♯ compiler for linux

(setq csharp-make-tool "mcs")

emacs-lisp

Let’s try and make elisp symbols pretty!

(add-hook 'emacs-lisp-mode-hook
	  (lambda ()
	    (push '("<=" . ?≤) prettify-symbols-alist)
	    (push '("**2" . ) prettify-symbols-alist)))

haskell-mode

(use-package haskell-mode
  :mode "\\.hs\\'"
  :custom
  (haskell-tags-on-save t)
  :config

  (add-hook
   'haskell-mode-hook
   (lambda ()
     (push '("\\" . ) prettify-symbols-alist)
     (push '(">>=" . ?↣) prettify-symbols-alist)
     (push '("->" . ?→) prettify-symbols-alist)
     (push '("<-" . ?←) prettify-symbols-alist)
     (push '("=>" . ?⇒) prettify-symbols-alist)
     (push '("not" . ) prettify-symbols-alist)
     (push '("==" . ?≟) prettify-symbols-alist)
     (push '("/=" . ?≠) prettify-symbols-alist)
     (push '("<=" . ?≤) prettify-symbols-alist)
     (push '(">=" . ?≥) prettify-symbols-alist)
     (push '("=" . ?≡) prettify-symbols-alist)
     (push '("pi" . ) prettify-symbols-alist)
     (push '(">>" . ?≫) prettify-symbols-alist)
     (push '("<<" . ?≪) prettify-symbols-alist)
     (push '("++" . ?⧺) prettify-symbols-alist)
     (push '("*" . ?⋅) prettify-symbols-alist)
     (push '(" . " . ?∘) prettify-symbols-alist)
     (push '("<*>" . ?⊛) prettify-symbols-alist)
     (push '("<+>" . ?⊕) prettify-symbols-alist)
     (push '("::" . ?⁝) prettify-symbols-alist))))

I’ve added command line completion for cabal and stack, since I’m too lazy to type out my executable names on my own.

(defconst pcmpl-cabal-commands
  '("update" "install" "help" "info" "list" "fetch" "user" "get" "init" "configure" "build"
  "clean" "run" "repl" "test" "bench" "check" "sdist" "upload" "report" "freeze" "gen"
  "haddock" "hscolour" "copy" "register" "sandbox" "exec" "repl"))

(defun pcmpl-cabal-get-execs ()
  (with-temp-buffer
    (message "Loading")
    (insert (shell-command-to-string "cat *.cabal"))
    (goto-char (point-min))
    (let ((ref-list))
      (while (re-search-forward "^executable +\\(.+\\) *$" nil t)
	(message "Insert")
	(add-to-list 'ref-list (match-string 1)))
      ref-list)))

(defun pcomplete/cabal ()
  "Completion for `cabal'"
  (pcomplete-here* pcmpl-cabal-commands)

  (cond
   ((pcomplete-match (regexp-opt '("run")) 1)
    (pcomplete-here* (pcmpl-cabal-get-execs)))))

(defconst pcmpl-stack-commands
  '( "build" "install" "uninstall" "test" "bench" "haddock" "new" "templates" "init" "solver"
  "setup" "path" "unpack" "update" "upgrade" "upload" "sdist" "dot" "exec" "ghc" "ghci"
  "repl" "runghc" "runhaskell" "eval" "clean" "list" "query" "ide" "docker" "config" "image" "hpc")
  "List of Stack Commands")

(defun pcomplete/stack ()
  "Completion for `stack'"
  (pcomplete-here* pcmpl-stack-commands)

  (cond
   ((pcomplete-match (regexp-opt '("exec")) 1)
    (pcomplete-here* (pcmpl-cabal-get-execs)))))

intero

(use-package intero
  :hook haskell-mode)

Python

Let’s make our python prettier, too!

(add-hook 'python-mode-hook
	  (lambda ()
	    (push '("<=" . ?≤) prettify-symbols-alist)
	    (push '(">=" . ?≥) prettify-symbols-alist)
	    (push '("!=" . ?≠) prettify-symbols-alist)
	    (push '("np.pi" . ) prettify-symbols-alist)
	    (push '("np.sum" . ) prettify-symbols-alist)
	    (push '("np.sqrt" . ?√) prettify-symbols-alist)
	    (push '("sqrt" . ?√) prettify-symbols-alist)
	    (push '("sum" . ) prettify-symbols-alist)
	    (push '("alpha" . ) prettify-symbols-alist)
	    (push '("sigma" . ) prettify-symbols-alist)
	    (push '("lambda" . ) prettify-symbols-alist)
	    (push '("**2" . ) prettify-symbols-alist)))

(defun switch-to-python (&rest r)
  (interactive)
  (message "Switching! %S" r)
  (switch-to-buffer-other-window "*Python*"))

(advice-add 'run-python :after #'switch-to-python)

Add support to python mode for finding errors

Add mypy for doing type checking

(use-package flycheck-mypy)

rainbow-delimiters

(use-package rainbow-delimiters
	     :hook (prog-mode . rainbow-delimiters-mode))

Systemd

I need to be able to edit systemd service files.

(use-package systemd)

nix

Add nix-mode for editting nix files

(use-package nix-mode)

Communication Tools

We need spell checking in generic Mail mode.

(add-hook 'mail-mode-hook 'flyspell-mode)

Also, there are some generic message mode settings that I need to review again so that I can remember exactly how they work. FIXME

(setq message-send-mail-function 'message-send-mail-with-sendmail)
(setq message-sendmail-envelope-from 'header)
(setq message-sendmail-extra-arguments '("--read-envelope-from"))
(setq message-sendmail-f-is-evil t)

eww

We will use eww as our default browser, with the option to escape to firefox if things get bad.

(setq browse-url-browser-function 'eww-browse-url)

I customise the eww bindings to make them more VimFx

jabber

(use-package jabber
  :commands (jabber-connect jabber-connect-all)
  :custom
   (jabber-chat-buffer-show-avatar nil)
   (jabber-vcard-avatars-retrieve nil)
  :config
   (setq jabber-account-list
    (let
	((passwd (funcall (plist-get (car (auth-source-search :max 1 :host "talk.google.com")) :secret))))
      `(("[email protected]"
	 (:port . 5223)
	 (:password . ,passwd)
	 (:network-server . "talk.google.com")
	 (:connection-type . ssl)))))
  (progn
   (defun x-urgency-hint (frame arg &optional source)
     (let* ((wm-hints (append (x-window-property
			       "WM_HINTS" frame "WM_HINTS" source nil t) nil))
	    (flags (car wm-hints)))
       (setcar wm-hints
	       (if arg
		   (logior flags #x100)
		 (logand flags (lognot #x100))))
       (x-change-window-property "WM_HINTS" wm-hints frame "WM_HINTS" 32 t)))
   (defun jabber-notify-taffy ()
     (if (equal "0" jabber-activity-count-string) t
       (progn
	 ;; (notifications-notify
	 ;;  :title jabber-activity-make-string
	 ;;  :body jabber-activity-count-string)
	 (x-urgency-hint (selected-frame) t))))
   (add-hook 'jabber-chat-mode-hook 'flyspell-mode)
   (add-hook 'jabber-activity-update-hook 'jabber-notify-taffy)))

twittering-mode

(use-package twittering-mode
	     :bind (("C-c t" . twit))
	     :hook (twittering-edit-mode . company-mode)
	     :custom
	     (twittering-use-master-password t)
	     (twittering-timer-interval 30))

sx

(use-package sx)

gnus

(defun gnus-keys () (local-set-key ["S-delete"] 'gnus-summary-delete-article))

(use-package gnus
  :custom
  (gnus-select-method '(nntp "news.gwene.org"))
  (send-mail-function (quote smtpmail-send-it))
  (sendmail-program "msmtp")
  (message-send-mail-function (quote message-send-mail-with-sendmail))
  (message-sendmail-envelope-from (quote header))
  (message-sendmail-extra-arguments (quote ("--read-envelope-from")))
  (message-sendmail-f-is-evil t)
  (gnus-secondary-select-methods
   (quote
    ((nnmaildir "Professional" (directory "~/Maildir/Professional"))
     (nnmaildir "Work" (directory "~/Maildir/Work"))
     (nnmaildir "Personal" (directory "~/Maildir/Personal")))))
  :hook (gnus-summary-mode-hook . gnus-keys))

notmuch

notmuch is a wonderful little utility for managing my mail

(use-package notmuch
  ;; :bind
  ;; (:map notmuch-search-mode-map
  ;;	("a" . my-notmuch-archive))
  :commands notmuch
  :init
  (defun my-notmuch-archive (&optional arg)
    (interactive "p")
    (kmacro-exec-ring-item (quote ([45 117 110 114 101 97 100 32 45 105 110 98 111 120 return] 0 "%d")) arg))
  :custom
  (notmuch-archive-tags (quote ("-inbox" "-unread")))
  (notmuch-fcc-dirs
   (quote
    (("[email protected]" . "Personal/[Gmail].Sent Mail")
     ("[email protected]" . "Work/Sent -inbox -unread +sent"))))
  (notmuch-hello-thousands-separator ",")
  (notmuch-saved-searches
   (quote
    ((:name "inbox" :query "tag:inbox" :key "i")
     (:name "unread" :query "tag:unread" :key "u")
     (:name "flagged" :query "tag:flagged" :key "f")
     (:name "sent" :query "tag:sent" :key "t")
     (:name "drafts" :query "tag:draft" :key "d")
     (:name "all mail" :query "*" :key "a")
     (:name "Today's mail" :query "date:0d..")
     (:name "promotional" :query "to:promotional tag:inbox")
     (:name "SasView" :query "Sas from:[email protected]"))))
  :custom-face
  (notmuch-search-unread-face ((t (:foreground "#859900")))))

elfeed

(use-package elfeed
  :bind (("C-c c" . org-capture))
  :commands (elfeed)
  :custom
  (elfeed-feeds
   '(("http://www.xkcd.org/atom.xml" comic)
     ("http://phdcomics.com/gradfeed.php" comic)
     ("http://www.merriam-webster.com/wotd/feed/rss2" education)
     ("http://sachachua.com/blog/feed/" sw emacs)
     ("https://planet.haskell.org/rss20.xml" sw haskell)
     ("https://wordsmith.org/awad/rss1.xml" education)
     ("http://emacsninja.com/feed.atom" sw emacs)
     ("http://emacshorrors.com/feed.atom" sw emacs)
     ("https://blogs.msdn.microsoft.com/oldnewthing/feed" sw tech)
     ("http://endlessparentheses.com/atom.xml" sw emacs)
     ("http://pragmaticemacs.com/feed/" sw emacs)
     ("https://www.reddit.com/r/emacs/.rss" sw emacs)
     ("https://www.reddit.com/r/haskell/.rss" sw haskell)
     ("https://www.reddit.com/r/julia/.rss" sw julia)
     ("https://hnrss.org/newest?points=300" sw tech)
     ("https://yager.io/feed/" sw haskell)
     "http://us10.campaign-archive1.com/feed?u=49a6a2e17b12be2c5c4dcb232&id=ffbbbbd930")))

Slack

(use-package slack
  :commands (slack-start)
  :custom
  (slack-buffer-emojify t) ;; if you want to enable emoji, default nil
  (slack-prefer-current-team t)
  :general
  (:keymaps 'slack-info-mode-map :infix ","
	    "u" 'slack-room-update-messages)
  (:keymaps 'slack-edit-message-mode-map :infix ","
	    "k" 'slack-message-cancel-edit
	    "s" 'slack-message-send-from-buffer
	    "2" 'slack-message-embed-mention
	    "3" 'slack-message-embed-channel)
  (:keymaps 'slack-mode-map :infix ","
	    "c" 'slack-buffer-kill
	    "j" 'slack-buffer-goto-next-message
	    "k" 'slack-buffer-goto-prev-message
	    "ra" 'slack-message-add-reaction
	    "rr" 'slack-message-remove-reaction
	    "rs" 'slack-message-show-reaction-users
	    "pl" 'slack-room-pins-list
	    "pa" 'slack-message-pins-add
	    "pr" 'slack-message-pins-remove
	    "mm" 'slack-message-write-another-buffer
	    "me" 'slack-message-edit
	    "md" 'slack-message-delete
	    "u" 'slack-room-update-messages
	    "2" 'slack-message-embed-mention
	    "3" 'slack-message-embed-channel)
  :config
  (slack-register-team
   :name "SasView"
   :client-id "165525662918.164903213860"
   :client-secret (funcall (plist-get (car (auth-source-search :max 1 :host "sasview.slack.com")) :secret))
   :token (funcall (plist-get (car (auth-source-search :max 1 :host "token.sasview.slack.com")) :secret))
   :subscribed-channels '(general random build github trac jenkins)))

Tramp

Tramp is emacs’ builtin system for handling remote files

(use-package tramp
  :config
  (setq my-tramp-ssh-completions
	'((tramp-parse-sconfig "~/.ssh/config")
	  (tramp-parse-sknownhosts "~/.ssh/known_hosts")))

  (mapc
   (lambda (method)
     (tramp-set-completion-function method my-tramp-ssh-completions))
   '("fcp" "rsync" "scp" "scpc" "scpx" "sftp" "ssh" "sshx")))

EUDC

EUDC is the LDAP client for emacs. It should allow me to query the directory of STFC.

(use-package eudc
  :commands
  (eudc-get-email eudc-get-phone eudc-query-form)
  :custom
  (eudc-server-hotlist (quote (("126.0.0.1:1389" . ldap))))
  :config
  (setq ldap-host-parameters-alist
	`(("127.0.0.1:1389"
	   base "ou=people"
	   binddn "CLRC\\auv61894"
	   passwd ,(funcall (plist-get (car (auth-source-search :max 1 :host "127.0.0.1" :port 1389)) :secret))
	   auth simple))))

excorporate

Excorporate pulls calendar data from an exchange server. I’ve then written way too much code to allow this to interface with the org-mode agenda, allowing me to insert my outlook agenda directly into org.

(use-package excorporate
  :commands excorporate
  :custom
  (excorporate-configuration "[email protected]"))

excorporate org

This is my little code to put my Exchange calendar into my org-agenda. It’s probably horribly broken. Additionally, it depends on latch.el, which isn’t available as a package and had to be installed manually.

At some point, I need to turn this into a proper package.

(add-to-list 'load-path "/home/adam/.emacs.d/scripts")

(require 'latch)

(defun excorporate-first-meeting (&optional mark)
  (if exco--connections
      (let
	  ((meeting (car-safe (adam-get-meetings date))))
	(if meeting
	    (format
	     "%s %s"

	     (if (plist-get meeting 'all-day)
		""
	       (adam-relative-date-format
		(plist-get meeting 'start)
		(plist-get meeting 'stop)
		date))
	     (plist-get meeting 'subject))))))

(defun excorporate-second-meeting (&optional mark)
  (if exco--connections
      (let
	  ((meeting (car-safe (cdr-safe (adam-get-meetings date)))))
	(if meeting
	    (format
	     "%s %s"

	     (if (plist-get meeting 'all-day)
		""
	       (adam-relative-date-format
		(plist-get meeting 'start)
		(plist-get meeting 'stop)
		date))
	     (plist-get meeting 'subject))))))

(defun adam-relative-date-format (begin end local)
     (pcase-let
	 ((`(,month ,day ,year) local)
	  (`(,es ,em ,eh ,eD ,eM ,eY) begin)
	  (`(,bs ,bm ,bh ,bD ,bM ,bY) end))
       (cond
	((and (= day eD) (= month eM) (= year eY)
	      (= day bD) (= month bM) (= year bY))
	 (format "%2d:%02d--%2d:%02d" bh bm eh em))
	((and (= day eD) (= month eM) (= year eY))
	 (format "%2d:%02d" eh em))
	((and (= day bD) (= month bM) (= year bY))
	 (format "%2d:%02d" bh bm))
	 "")))

(defun adam-parse-calendar-item (item)
  (setq result '(all-day ()))
  (dolist (key item result)
    (if (listp key)
	(cond
	 ((eq 'Subject (car key))
	  (setq result
		(plist-put result 'subject (cdr key))))
	 ((eq 'End (car key))
	  (setq result
		(plist-put result 'stop
			   (decode-time (date-to-time (cdr key))))))
	 ((eq 'IsAllDayEvent (car key))
	  (setq result
		(plist-put result 'all-day (cdr key))))
	 ((eq 'Start (car key))
	  (setq result
		(plist-put result 'start
			   (decode-time (date-to-time (cdr key))))))))))

(defun adam-get-meetings (date)
  (lexical-let
      ((promise (make-promise))
       (month (car date))
       (day (cadr date))
       (year (caddr date)))
    (exco-get-meetings-for-day
     "[email protected]"
     month day year
     (lambda (ident resp) (deliver promise resp)))
     (-filter
      (lambda (x)
	(pcase-let
	    ((`(,second ,minute ,hour ,date)
	      (plist-get x 'stop)))
	  (not
	   (and (eq date day) (eq hour 0) (eq minute 0)))))
      (mapcar #'adam-parse-calendar-item
	      (cdar (last (car (last (cdr (cadaar (retrieve promise)))))))))))

org

(use-package org
  :ensure org-plus-contrib
  :bind (("C-c l" . org-store-link)
	 ("C-c a" . org-agenda)
	 ("C-c b" . org-iswitchb))
  :hook
  ((org-mode-hook . auto-fill-mode)
   (org-mode-hook . flyspell-mode))
  :general
  (:keymaps 'org-mode-map :infix "c"
	    "'" 'org-edit-special
	    "vt" 'org-babel-tangle
	    "d" 'org-deadline
	    "s" 'org-schedule
	    "e" 'org-export-dispatch)
  :custom
  (org-agenda-files
   (quote
    ("~/org/sync.org"
     "~/org/appointments.org"
     "~/org/personal-notes.org")))
  (calendar-latitude 53.3836)
  (calendar-longitude 1.4669)
  (org-agenda-window-setup 'current-window)
  (org-agenda-start-on-weekday nil)
  (org-return-follows-link t)
  (org-imenu-depth 4)
  (org-agenda-start-on-weekday nil)
  (org-babel-load-languages (quote ((emacs-lisp . t) (python . t))))
  (org-confirm-babel-evaluate nil)
  (org-src-fontify-natively t)
  (org-agenda-include-diary nil)
  (org-src-preserve-indentation t)
  (org-table-convert-region-max-lines 99999)
  (org-agenda-day-face-function (quote jd:org-agenda-day-face-holidays-function))
  (org-file-apps
   (quote
    ((auto-mode . emacs)
     ("\\.mm\\'" . default)
     ("\\.x?html?\\'" . default)
     ("\\.pdf\\'" . system))))
  (org-capture-templates
   (quote
    (("m" "Unsorted Mail Tasks" entry
      (file+headline "~/org/appointments.org" "Unsorted Mail")
      "** TODO%?\n    SCHEDULED:%T\n\n    %a")
     ("v" "Vocab" entry
      (file+headline "~/org/appointments.org" "Vocab")
      "** TODO %a\n    SCHEDULED:%T%?\n\n    %a"))))
  (org-latex-listings (quote minted))
  (org-latex-packages-alist (quote (("" "minted" nil))))
  (org-latex-pdf-process
   (quote
    ("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f" "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f" "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f")))
  (holiday-other-holidays
   (quote
    (
     (holiday-float 5 1 -1 "Spring Bank Holiday")
     (holiday-float 5 1 1 "May Day Bank Holiday")
     (holiday-float 8 1 -1 "Late Summer Bank Holiday")
     )))
  (org-agenda-custom-commands
   '(("c" . "My Custom Agendas")
     ("cu" "Unscheduled TODO"
      ((todo ""
	     ((org-agenda-overriding-header "\nUnscheduled TODO")
	      (org-agenda-skip-function '(org-agenda-skip-entry-if 'timestamp)))))
      nil
      nil)))
  :custom-face
  (org-table ((t (:inherit 'fixed-pitch))))
  (org-block ((t (:inherit 'fixed-pitch))))
  (org-block-begin-line ((t (:inherit 'fixed-pitch))))
  (org-block-end-line ((t (:inherit 'fixed-pitch))))
  (org-verbatim ((t (:inherit 'fixed-pitch))))
  :config

  (add-hook 'org-mode-hook
	    (lambda ()
	      (face-remap-add-relative 'default :inherit 'variable-pitch)))

  (defun adam-org-sunrise ()
    (concat
     (nth 1 (split-string (diary-sunrise-sunset)))
     " Sunrise for "
     (string-remove-prefix "(" (nth 9 (split-string (diary-sunrise-sunset))))))
  (defun adam-org-sunset ()
    (concat
     (nth 4 (split-string (diary-sunrise-sunset)))
     " Sunset"))


  (defface org-agenda-date-beam
    `((t  :foreground ,(face-attribute 'font-lock-keyword-face :foreground)
	  :inherit org-agenda-date))
    "Face used for agenda entries on days when the ISIS beam is on"
    :group 'org-faces)

  (defface org-agenda-date-beam-weekend
    `((t  :foreground ,(face-attribute 'font-lock-keyword-face :foreground)
	  :inherit org-agenda-date-weekend))
    "Face used for agenda entries on days when the ISIS beam is on"
    :group 'org-faces)

  (defun my-org-agenda-day-face-holidays-function (date)
    "Compute DATE face for holidays."
    (unless (org-agenda-todayp date)
      (letrec
	  ((day-of-week (calendar-day-of-week date))
	   (weekend (or (= day-of-week 0)
			(= day-of-week 6)))
	   (files (org-agenda-files nil 'ifmode))
	   (entries (-flatten
		     (-map
		      (lambda (file) (org-agenda-get-day-entries file date))
		      files)))
	   (categories (-flatten (-map (lambda (entry)
					 (with-temp-buffer
					   (insert entry)
					   (org-get-category (point-min))))
				       entries))))
	(cond
	 ((and (-contains? categories "BeamOn")
	       (or weekend
		   (-contains? categories "Holidays")
		   (-contains? categories "Vacation")))
	  'org-agenda-date-beam-weekend)
	 ((-contains? categories "BeamOn")
	  'org-agenda-date-beam)
	 ((or weekend
	      (-contains? categories "Holidays")
	      (-contains? categories "Vacation"))
	  'org-agenda-date-weekend)
	 (t 'org-agenda-date)))))

  (setq
   org-agenda-day-face-function
   (function
    my-org-agenda-day-face-holidays-function))
					; (require 'org-notify)

  (bind-key "RET" 'org-agenda-goto org-agenda-mode-map)
  (bind-key [tab] 'org-agenda-switch-to org-agenda-mode-map)
  (require 'org-agenda))

Calculate Local Contacting

The code below calculates uses the org-calendar to calculate the expected local contacting payment.

(defun get-timestamps (tags)
  (-map
   (lambda (x) (cdr (assoc "TIMESTAMP" x)))
   (-filter (lambda (x) (assoc "TIMESTAMP" x))
	    (org-map-entries
	     (lambda ()
	       (org-entry-properties))
	     tags
	     'agenda))))

(defun timestamp-to-dates (stamp)
  (-map
   #'calendar-gregorian-from-absolute
   (apply
    #'number-sequence
    (-map
     #'org-time-string-to-absolute
     (split-string
      stamp
      "--")))))

(defun local-contacting (dates)
  (apply
   '+
   (-map
    (lambda (x)
      (pcase x
	(`(,month ,day, year)
	 (pcase (org-day-of-week day month year)
	   (6 40.40)
	   (0 40.40)
	   (_ 20.20)
	   ))))
   dates)))

(defun calculate-local-contacting ()
  "Calculate expected local contacting fees."
  (interactive)
  (print
   (apply
    '+
    (-map
     (lambda (x)
       (local-contacting
	(timestamp-to-dates x)))
     (get-timestamps "+LocalContact+TODO=\"TODO\"")))))

htmlize

Org-mode uses the htmlize library to highlight the code in the exported documentation. As long as I’ve installed the library, I should never need to think about it again.

(use-package htmlize)

org-notmuch

We need to load the contrib package to get notmuch links into org

(require 'org-notmuch)

org-edna

This package allow much finer control over the triggers and blocking in our org-mode files. The manual can be found at http://www.nongnu.org/org-edna-el/

(use-package org-edna
  :config
  (org-edna-load))

Prose

LaTeX

I like for each sentence in a LaTeX document to be its own line. That way, when I’m editing, only the relevant sections get marked in the version control, instead of the entire paragraph. This code tries to alleviate the problem. I’m not sure how well it work.

(defadvice LaTeX-fill-region-as-paragraph (around LaTeX-sentence-filling)
  "Start each sentence on a new line."
  (let ((from (ad-get-arg 0))
	(to-marker (set-marker (make-marker) (ad-get-arg 1)))
	tmp-end)
    (while (< from (marker-position to-marker))
      (forward-sentence)
      ;; might have gone beyond to-marker --- use whichever is smaller:
      (ad-set-arg 1 (setq tmp-end (min (point) (marker-position to-marker))))
      ad-do-it
      (ad-set-arg 0 (setq from (point)))
      (unless (or
	       (bolp)
	       (looking-at "\\s *$"))
	(LaTeX-newline)))
    (set-marker to-marker nil)))

(ad-activate 'LaTeX-fill-region-as-paragraph)

auctex

Auctex is a nice TeX environment for emacs. I used it constantly in working on my thesis

(use-package auctex
  :custom
  (TeX-PDF-mode t)
  (TeX-view-program-list (quote (("Okular" "okular --unique %o#src:%n%b"))))
  (TeX-view-program-selection
   (quote
    (((output-dvi style-pstricks)
      "dvips and gv")
     (output-dvi "Okular")
     (output-pdf "Evince")
     (output-html "xdg-open"))))
  :hook
  (LaTeX-mode-hook . visual-line-mode)
  (LaTeX-mode-hook . auto-fill-mode)
  (LaTeX-mode-hook . flyspell-mode)
  (LaTeX-mode-hook . LaTeX-math-mode)
  :mode ("\\.tex\\'" . TeX-latex-mode))

Text Mode

(add-hook 'text-mode-hook 'flyspell-mode)
(add-hook 'text-mode-hook 'visual-line-mode)

There didn’t used to be a built in word count function. I believe that there is now, so I may not need this any longer.

(defun count-words (&optional begin end)
  "count words between BEGIN and END (region); if no region defined, count words in buffer"
  (interactive "r")
  (let ((b (if mark-active begin (point-min)))
      (e (if mark-active end (point-max))))
    (message "Word count: %s" (how-many "\\w+" b e))))


langtool

(use-package langtool
  :custom
  (langtool-language-tool-jar "~/bin/LanguageTool-3.5/languagetool-commandline.jar"))

writegood-mode

(use-package writegood-mode
  :diminish writegood-mode
  :hook (text-mode latex-mode org-mode))

Toys

encourage-mode

(use-package encourage-mode
  :diminish encourage-mode
  :init (encourage-mode))

Tidal

(if
    (file-exists-p "~/Code/tidal")
    (progn
      (add-to-list 'load-path "~/Code/tidal/" )
      (require 'tidal)))

emojify

(use-package emojify
  :hook ((text-mode jabber-console-mode) . emojify-mode)
  :custom
  (emojify-display-style 'unicode)) ; :-)

Utilities

ace-window

(use-package ace-window
  :bind (("M-z" . ace-window))
  :custom
  (aw-keys '(?f ?j ?d ?k ?s ?l ?a ?g ?h ?r ?u ?e ?i ?w ?o ?n ?c ?m ?v )))

alert

A basic emacs customication system. Slack uses this to handle system messages and other parts of emacs could probably benefit from it. I really need to tweak the customisation.

(use-package alert
  :commands (alert)
  :custom
  (alert-default-style 'libnotify))

all-the-icons

Use the all-the-icons package to get icon fonts.

(use-package all-the-icons)

Automatically display file icons in dired.

(use-package all-the-icons-dired
  :hook (dired-mode . all-the-icons-dired-mode))

Display icons when switching buffers

(use-package all-the-icons-ivy
  :config
  (all-the-icons-ivy-setup))

avy

I’ve been trying to get into avy, with moderate success.

(use-package avy
  :bind
  (("M-d" . avy-goto-char-timer)))

company

(use-package company
  :hook (prog-mode . company-mode)
  :bind (("M-/" . company-complete))
  :custom
  (company-dabbrev-code-modes
   (quote
    (prog-mode batch-file-mode csharp-mode css-mode erlang-mode haskell-mode
    jde-mode lua-mode python-mode purescript-mode)))
  :diminish company-mode)

company-emoji

This should allow me to more easily type emoji. Because that’s what my life has been missing.

(use-package company-emoji
  :config
  (add-to-list 'company-backends 'company-emoji))

company-math

Let’s use company-math mode so that we don’t have to keep using the TeX input method

(use-package company-math
  :config
  (add-to-list 'company-backends 'company-math-symbols-unicode))

😄

company-qml

(use-package company-qml
  :config
  (add-to-list 'company-backends 'company-qml))

company-auctex

(use-package company-auctex
  :after (company latex))

imenu-anywhere

This package allows me to do the imenu jump to any buffer with the same major mode. This should be a big boon when working on multi-file projects (and not require greping my way around all of the time)

(use-package imenu-anywhere
  :general (:keymaps 'override "i" 'ivy-imenu-anywhere))

eyebrowse

(use-package eyebrowse
  :disabled t
  :general
  (:infix "w"
   "j" 'eyebrowse-create-window-config
   "j" 'eyebrowse-next-window-config
   "k" 'eyebrowse-prev-window-config
   "r" 'eyebrowse-rename-window-config
   "/" 'eyebrowse-switch-to-window-config
   "x" 'eyebrowse-close-window-config
   "0" 'eyebrowse-switch-to-window-config-0
   "1" 'eyebrowse-switch-to-window-config-1
   "2" 'eyebrowse-switch-to-window-config-2
   "3" 'eyebrowse-switch-to-window-config-3
   "4" 'eyebrowse-switch-to-window-config-4
   "5" 'eyebrowse-switch-to-window-config-5
   "6" 'eyebrowse-switch-to-window-config-6
   "7" 'eyebrowse-switch-to-window-config-7
   "8" 'eyebrowse-switch-to-window-config-8
   "9" 'eyebrowse-switch-to-window-config-9)
  :config
  (eyebrowse-mode))

flycheck

(use-package flycheck
  :diminish flycheck-mode
  :hook ((prog-mode haskell-mode) . flycheck-mode)
  :config
  (flycheck-define-checker
   proselint
   "A linter for plain prose"
   :command ("proselint" source)
   :standard-input f
   :error-patterns
   ((warning line-start (file-name) ":" line ":" column ": " (message) line-end))
   :modes (markdown-mode text-mode org-mode))
  (add-to-list 'flycheck-checkers 'proselint)
  (flycheck-add-next-checker 'python-flake8 'python-pylint))

hydra

Hydra is a useful little utility for making custom keyboard DSLs.

(use-package hydra
  :config

  (defhydra hydra-flycheck ()
    ("X" (progn
	   (let ((current-prefix-arg 4))
	   (call-interactively 'flycheck-disable-checker))) "enable" :color blue)
    ("x" flycheck-disable-checker "disable")
    ("v" flycheck-verify-setup "verify")
    ("c" flycheck-select-checker "checkerer")
    ("e" flycheck-display-error-at-point "explain" :color blue)
    ("j" flycheck-next-error "next")
    ("k" flycheck-previous-error "previous"))
  (general-define-key
   :keymaps '(flycheck-mode-map)
   "f" 'hydra-flycheck/body)

  (defhydra hydra-flyspell ()
    ("j" flyspell-goto-next-error "next")
    ("l" flyspell-correct-previous-word-generic "fix")
    ("I" ispell-pdict-save "insert")
    ("a" flyspell-auto-correct-word "auto"))
  (general-define-key
   :keymaps '(flyspell-mode-map)
   "f" 'hydra-flyspell/body)

  (defhydra hydra-apropos (:color blue)
    "Apropos"
    ("a" apropos "apropos")
    ("c" apropos-command "cmd")
    ("d" apropos-documentation "doc")
    ("e" apropos-value "val")
    ("l" apropos-library "lib")
    ("o" apropos-user-option "option")
    ("u" apropos-user-option "option")
    ("v" apropos-variable "var")
    ("i" info-apropos "info")
    ("t" tags-apropos "tags")
    ("z" hydra-customize-apropos/body "customize"))
  (defhydra hydra-customize-apropos (:color blue)
    "Apropos (customize)"
    ("a" customize-apropos "apropos")
    ("f" customize-apropos-faces "faces")
    ("g" customize-apropos-groups "groups")
    ("o" customize-apropos-options "options"))
  (general-define-key
   "h" 'hydra-apropos/body)

  (defhydra hydra-windows (:hint nil)
   "Manage Windows"
   ("z" ace-window "ace" :column "Movement")
   ("j" windmove-down "" :column "Movement")
   ("k" windmove-up "" :column "Movement")
   ("h" windmove-left "" :column "Movement")
   ("l" windmove-right "" :column "Movement")
   ("J" shrink-window "X↓" :column "Resize")
   ("K" enlarge-window "X↑" :column "Resize")
   ("H" shrink-window-horizontally "X←" :column "Resize")
   ("L" enlarge-window-horizontally "X→" :column "Resize")
   ("=" balance-windows "equalise" :column "Resize")
   ("-" split-window-below "vertical" :column "Split")
   ("|" split-window-right "horizontal" :column "Split")
   ("x" delete-window "close" :column "Split")
   ("d" purpose-toggle-window-purpose-dedicated "purpose" :column "Dedicate")
   ("D" purpose-toggle-window-buffer-dedicated "buffer" :column "Dedicate")
   ("q" nil "quit" :color blue :column nil))

  (general-define-key
   " W" 'hydra-windows/body))

hydra-ivy

Add hydra bindings to ivy

(use-package ivy-hydra)

Key Bindings

Kill this buffer

I hate when emacs asks me which buffer to kill, because it’s my current buffer 99% of the time. Just change the key binding and be done with it.

(bind-key "C-x k" 'kill-this-buffer)

Refresh Key

Refreshing buffers is a constant chore that really should have it’s own hotkey. Why not steal F5 from the browser?

(global-set-key
 (kbd "<f5>")
 (lambda (&optional force-reverting)
   "Interactive call to revert-buffer. Ignoring the auto-save
file and not requesting for confirmation. When the current buffer
is modified, the command refuses to revert it, unless you specify
the optional argument: force-reverting to true."
   (interactive "P")
   ;;(message "force-reverting value is %s" force-reverting)
   (if (or force-reverting (not (buffer-modified-p)))
	(revert-buffer :ignore-auto :noconfirm)
     (error "The buffer has been modified"))))

keyfreq

(use-package keyfreq
  :config
  (keyfreq-mode 1)
  (keyfreq-autosave-mode 1))

ivy

(use-package ivy
  :general (:keymaps 'override :infix "x" "b" 'ivy-switch-buffer)
  :diminish ivy-mode)

counsel

(use-package counsel
  :after ivy
  :demand t
  :bind   (("C-s" . swiper)
	   ("C-c C-r" . ivy-resume)
	   ("<f6>" . ivy-resume)
	   ("C-x b" . ivy-switch-buffer)
	   ("M-x" . counsel-M-x)
	   ("M-y" . counsel-yank-pop)
	   ("C-x C-f" . counsel-find-file)
	   ("<f1> f" . counsel-describe-function)
	   ("<f1> v" . counsel-describe-variable)
	   ("<f1> l" . counsel-load-library)
	   ("<f2> i" . counsel-info-lookup-symbol)
	   ("C-x 8 RET" . counsel-unicode-char)
	   ("<f2> u" . counsel-unicode-char))
  :general
  (:keymaps 'org-mode-map
	    "i" 'counsel-org-goto
	    "cq" 'counsel-org-tag)
  (:infix "x" "f" 'counsel-find-file
	  "8 RET" 'counsel-unicode-char)
  ("/" 'swiper "?" 'swiper-all)
  :diminish counsel-mode
  :custom
  (ivy-use-virtual-buffers t)
  (counsel-find-file-at-point t)
  (counsel-mode t)
  :config
  (ivy-mode 1))

counsel-dash

Dash is an offline documentation framework. The open source version is Zeal. It’s useful for getting programming documentation without needing to load up a google search. It’s especially useful when there’s no internet access or the scipy website is down yet again.

FIXME: The current version of counsel-dash relies on helm-dash, which subsequently relies on Helm. I may be able to get rid of the helm dependency in the future if this changes. I need to check on this from time to time and see if anything has improved.

(defun python-set-docsets ()
  (setq-local counsel-dash-docsets
	      '("SciPy" "NumPy" "Matplotlib" "Python_2" "Python_3" "Qt_5")))

(defun elisp-set-docsets ()
  (setq-local counsel-dash-docsets
	      '("Emacs_Lisp")))

(defun haskell-set-docsets ()
  (setq-local counsel-dash-docsets
	      '("Haskell")))

(defun html-set-docsets ()
  (setq-local counsel-dash-docsets
	      '("HTML" "CSS")))

(use-package counsel-dash
  :hook
  ((python-mode . python-set-docsets)
   (elisp-mode . elisp-set-docsets)
   (haskell-mode . haskell-set-docsets)
   (html-mode . html-set-docsets))
  :general
  (:keymaps 'override :infix "z"
  "d" 'counsel-dash)
  :custom
  (counsel-dash-browser-func 'eww)
  (counsel-dash-docsets-path "~/.local/share/Zeal/Zeal/docsets"))

flyspell-correct-ivy

(use-package flyspell-correct-ivy
  :config
  (require 'flyspell-correct-ivy))

link-hint

(use-package link-hint
  :bind
  ("M-o" . link-hint-open-link))

ace-link

(use-package ace-link
  :general
  (:prefix 'nil :infil 'nil :keymaps 'org-mode-map
  "M-o" 'ace-link-org)
  :config
  (ace-link-setup-default))

persp

Persp mode gives named window groups

(use-package persp-mode
  :general
  (:infix "w"
	  :keymaps 'override
	  "n" 'persp-next
	  "p" 'persp-prev
	  "s" 'persp-frame-switch
	  "S" 'persp-window-switch
	  "r" 'persp-rename
	  "c" 'persp-copy
	  "C" 'persp-kill
	  "a" 'persp-add-buffer
	  "b" 'persp-switch-to-buffer
	  "t" 'persp-temporarily-display-buffer
	  "i" 'persp-import-buffers
	  "I" 'persp-import-win-conf
	  "k" 'persp-remove-buffer
	  "K" 'persp-kill-buffer
	  "w" 'persp-save-state-to-file
	  "W" 'persp-save-to-file-by-names
	  "l" 'persp-load-state-from-file
	  "L" 'persp-load-from-file-by-names)
  :config
  (persp-mode 1))

projectile

(use-package projectile
  :demand t
  :general
  (:infix "p"
   :keymaps 'override
   "xe" 'projectile-run-eshell
   "xs" 'projectile-run-shell
   "xt" 'projectile-run-term
   "d" 'projectile-find-dir
   "D" 'projectile-dired
   "P" 'projectile-test-project
   "s" 'projectile-save-project-buffers
   "B" 'projectile-ibuffer
   "k" 'projectile-kill-buffers
   "c" 'projectile-compile-project
   "v" 'projectile-vc
   "t" 'projectile-find-tag
   "T" 'projectile-regenerate-tags
   "R" 'projectile-replace-regexp
   "E" 'projectile-edit-dir-locals
   "r" 'projectile-run-project)
  :custom
  (projectile-keymap-prefix (kbd "C-c C-p"))
  (projectile-mode-line
   '(:eval
     (if
	 (file-remote-p default-directory)
	 ""
       (format " {%s}" (projectile-project-name)))))
  (projectile-completion-system 'ivy)
  :config
  (projectile-global-mode))

counsel-projectile

(use-package counsel-projectile
  :after (projectile counsel)
  :general
  (:infix "p"
   :keymaps 'override
   "f" 'counsel-projectile
   "b" 'counsel-projectile-switch-to-buffer
   "p" 'counsel-projectile-switch-project
   "g" 'counsel-projectile-rg))

recentf

Recentf keeps track of recently edited files.

(require 'recentf)
(recentf-mode)

space-line

(use-package spaceline
  :demand t
  :custom
  (spaceline-highlight-face-func 'spaceline-highlight-face-evil-state))

(use-package spaceline-all-the-icons
  :demand t
  :after (spaceline all-the-icons)
  :config
  (spaceline-all-the-icons-theme)
  (spaceline-all-the-icons--setup-git-ahead)
  (spaceline-toggle-all-the-icons-buffer-size-off)
  (spaceline-toggle-all-the-icons-time-off)
  (spaceline-toggle-all-the-icons-region-info-off)
  (spaceline-toggle-all-the-icons-git-ahead-on)
  (spaceline-toggle-all-the-icons-projectile-on))

yasnippets

Yasnippets provide programmable skeletons for filling out boilerplate

(use-package yasnippet
  :custom
  (yas-indent-line 'fixed)
  :general
  (:keymaps 'yas-minor-mode-map
   "yv" 'yas-visit-snippet-file
   "yn" 'yas-new-snippet
   "ys" 'yas-insert-snippet)
  :config
  (yas-global-mode))

yasnippets-mpa

(defun mpa-parse-param (param)
  (pcase-let
      ((`(,name . ,value) (split-string param "=")))
    (cond
    ((string-equal name "OutputWorkspace")
    "self.declareProperty(\n            WorkspaceProperty(name=\"OutputWorkspace\",\n                defaultValue=\"\",\n                direction=Direction.Output))")
     ((eq value '())
      (format "self.declareProperty(\"%s\", defaultValue=0)" name))
     ((string-match "[0-9]+" (car value))
      (format "self.declareProperty(\"%s\", defaultValue=%s)"
	      name
	      (string-to-number (car value))))
     ((string-equal (car value) "file")
      (format "self.declareProperty(\n            FileProperty(name=\"%s\",\n                defaultValue=\"\",\n                action=FileAction.%s))" name (cadr value)))
     ((string-equal (car value) "wksp")
      (format "self.declareProperty(\n            WorkspaceProperty(name=\"%s\",\n                defaultValue=\"\",\n                direction=Direction.%s))" name (cadr value)))
     (t (format "self.declareProperty(\"%s\", defaultValue=\"%s\")" name (car value))))))

(defun mpa-get-param (param)
  (pcase-let
      ((`(,name . ,value) (split-string param "=")))
    (cond
     ((string-equal name "OutputWorkspace") "")
     ('t (format "%s = self.getProperty(\"%s\").value" name name)))))


(defun mpf-parse-param (param)
  (pcase-let
      ((`(,name . ,value) (split-string param "=")))
    (cond
     ((eq value '())
      (format "self.declareProperty(\"%s\", defaultValue=0.0)" name))
     ((string-match "[0-9]+" (car value))
      (format "self.declareProperty(\"%s\", defaultValue=%s)"
	      name
	      (string-to-number (car value))))
     (t (format "self.declareProperty(\"%s\", defaultValue=%s)" name (car value))))))

(defun mpf-get-param (param)
  (pcase-let
      ((`(,name . ,value) (split-string param "=")))
    (format "%s = self.getParameter(\"%s\")" name name)))

whitespace-cleanup-mode

(use-package whitespace-cleanup-mode
  :diminish whitespace-cleanup-mode
  :config
  (global-whitespace-cleanup-mode))

window-purpose

(use-package window-purpose
  :after (ivy)
  :general
  (:infix "xr"
  "p" 'ivy-purpose-switch-buffer-with-some-purpose
  "P" 'ivy-purpose-switch-buffer-with-purpose)
  (:infix ","
	  "d" 'purpose-toggle-window-buffer-dedicated
	  "D" 'purpose-toggle-window-purpose-dedicated
	  "1" 'purpose-delete-non-dedicated-windows
	  "b" 'purpose-switch-buffer-with-purpose
	  "s" 'purpose-save-window-layout
	  "l" 'purpose-load-window-layout)
  :config
  (purpose-mode)
  (purpose-x-kill-setup)
  (purpose-x-magit-single-on)
  (add-to-list 'purpose-user-mode-purposes '(haskell-cabal-mode . edit))
  (add-to-list 'purpose-user-mode-purposes '(eshell-mode . terminal))
  (add-to-list 'purpose-user-mode-purposes '(jabber-chat-mode . chat))
  (add-to-list 'purpose-user-mode-purposes '(slack-mode . chat))
  (add-to-list 'purpose-user-mode-purposes '(notmuch-hello-mode . chat))
  (add-to-list 'purpose-user-mode-purposes '(notmuch-message-mode . chat))
  (add-to-list 'purpose-user-mode-purposes '(notmuch-search-mode . chat))
  (add-to-list 'purpose-user-mode-purposes '(notmuch-show-mode . chat))
  (add-to-list 'purpose-user-mode-purposes '(org-mode . edit))
  (add-to-list 'purpose-user-mode-purposes '(ein:notebook-multilang-mode . edit))
  (add-to-list 'purpose-user-mode-purposes '(systemd-mode . edit))
  (add-to-list 'purpose-user-mode-purposes '(help-mode . help))
  (add-to-list 'purpose-user-mode-purposes '(Info-mode . help))
  (add-to-list 'purpose-user-mode-purposes '(Custom-mode . custom))
  (purpose-compile-user-configuration))

Winner

Winner mode allows me to undo and redo changes to the window layout within emacs. Very useful when I make a mistake. It’s also handy for focusing on a single window, then returning to my previous, more complex layout with a single C-c ←

(winner-mode)