Skip to content
This repository has been archived by the owner on Dec 1, 2020. It is now read-only.

Latest commit

 

History

History
630 lines (531 loc) · 20.2 KB

emacs-init.org

File metadata and controls

630 lines (531 loc) · 20.2 KB

Emacs Configuration

;;; ------------------------------------------
;;; Do not edit this file. It was tangled from
;;; an org file.
;;; ------------------------------------------

Emacs Configuration in an Org file

I’ve declared Emacs config bankruptcy for the new year, and it couldn’t have come at a better time. All at once there are a bunch of new tools for Emacs configuration: use-package (and req-package, used here) and bind-key simplify a lot of the annoying bits. Dependency management was hard before Emacs 24, but with these new tools we can vastly simplify the process. We even get a simpler keybinding mechanism in the bargain.

Package Management with Pallet

Pallet is a wonderful little tool built on Cask, a dependency management tool for Emacs packages. Pallet adds automatic updating of the Caskfile when packages are installed and deleted.

Installing Cask

Just run this command in your terminal of choice:

$ curl -fsSkL https://raw.github.com/cask/cask/master/go | python

then add ~/.cask/bin to your PATH so that you can use cask.

Creating a Caskfile

For now, we just need a minimal Cask to get Pallet set up. Mine looks like this:

(source gnu)
(source melpa)

(depends-on "pallet")
(depends-on "req-package")
(depends-on "moe-theme")
(depends-on "evil")

Then run the following command in your .emacs.d directory to set up Pallet:

cask install

Initialize Cask

Finally, we add the following lines to our init file:

(require 'cask "~/.cask/cask.el")
(cask-initialize)

Dependency Management with req-package

req-package is a wrapper on top of use-package, a package dependency management tool. The documentation for use-package is immensely helpful for figuring out how to describe package dependencies and settings. req-package adds the :require keyword which allows us to define dependencies between related packages.

Initialize req-package

With the preceding process complete, we just need to add the following line to our init file to begin using req-package:

(require 'req-package)
(setq custom-file "~/.emacs.d/custom.el")
(load custom-file)

Keybindings with bind-key

There are a number of ways to bind keys in Emacs, but I find bind-key, bundled with use-package, easier to work with and, more importantly, easier to read. bind-key takes a key sequence, a command, and an optional keymap. bind-key* overrides any minor mode which sets the keybinding. unbind-key takes a key sequence and a keymap and removes that binding. Invoking describe-personal-keybindings prints a summary of your keybindings through bind-key and any overrides or conflicts. This is really the killer convenience of using bind-key.

Evil

I was a diehard Vim user for a long time, and I still prefer the Vim way of interacting with text. Evil is a (mostly feature-complete) Vim emulation mode for Emacs. It has sane defaults and I rarely find weirdness or lacking features.

evil

Here we have our first package with dependencies. Historically, I’ve had little annoyances when evil loads before evil-leader, so we’ll make sure it gets loaded first. By default, evil uses its own cursor which shows up black regardless of face settings. Setting evil-default-cursor lets us use the cursor we’re used to. I also redefine a couple of ex commands for convenience.

(req-package evil
  :require (helm-config undo-tree surround ace-jump-mode)
  :ensure evil
  :init
  (progn
    (setq evil-default-cursor t)
    (evil-mode 1)
    (setq evil-motion-state-modes
          (append evil-emacs-state-modes evil-motion-state-modes))
    (setq evil-emacs-state-modes '(magit-mode dired-mode)))
  :config
  (progn
    (evil-ex-define-cmd "e[dit]" 'helm-find-files)
    (evil-ex-define-cmd "b[uffer]" 'helm-buffers-list)
    (bind-key "[escape]" 'keyboard-escape-quit evil-normal-state-map)
    (bind-key "[escape]" 'keyboard-escape-quit evil-visual-state-map)
    (bind-key "<escape>" 'keyboard-escape-quit)
    (bind-key "\"" 'ace-jump-mode evil-normal-state-map)
    (evil-define-key 'normal
                     tern-mode-keymap "gd" 'tern-find-definition)))

evil-leader

<leader> is a really useful shorthand in Vim, and evil-leader brings the same facility to Evil. For really common commands, leader bindings can save those precious keystrokes.

(req-package evil-leader
             :require evil
             :ensure evil-leader
             :init
             (progn
               (evil-leader/set-leader "<SPC>")
               (global-evil-leader-mode 1)
               (evil-leader/set-key
                 "l" 'org-insert-link
                 "o" 'other-window
                 "d" 'delete-window
                 "D" 'delete-other-windows
                 "h" 'split-window-below
                 "v" 'split-window-right
                 "k" 'kill-buffer
                 "K" 'kill-buffer-and-window
                 "f" 'dired
                 "gs" 'magit-status)))

evil-numbers

One of the little Vim conveniences not found in vanilla Evil is incrementing and decrementing numbers with C-a and C-x, respectively, likely because these are already important bindings in Emacs. However, by limiting the effect to normal mode, we can use evil-numbers to bring this functionality back without stomping all over existing bindings.

(req-package evil-numbers
             :require evil
             :config
             (progn
               (bind-key "C-a" 'evil-numbers/inc-at-pt evil-normal-state-map)
               (bind-key "C-x" 'evil-numbers/dec-at-pt evil-normal-state-map)))

evil-surround

Working with delimiter pairs is common enough that Tim Pope wrote a plugin to ease working with them in Vim, called vim-surround. evil-surround emulates this functionality in evil. It’s quite extensible, but the defaults seem to cover all my needs. Check out the README for some examples.

(req-package surround
             :init (global-surround-mode 1))

undo-tree

Standard Emacs undo is kind of confusing. undo-tree replaces this with a simpler tree structure. It also allows us to visualize the tree directly.

(req-package undo-tree
             :diminish ""
             :init
             (progn
               (setq undo-tree-auto-save-history t)
               (global-undo-tree-mode)))    

ace-jump-mode

(req-package ace-jump-mode)

Helm

Coming from Vim, I was very used to the fuzzy matching of CtrlP. Ido is a popular choice when one wishes to add this functionality to Emacs since it is built in and there is a fairly significant ecosystem built around it. I used Ido for a while, but after experimenting with Helm, I’ve decided to migrate. Think of Helm as CtrlP for every minibuffer. From describing functions and variables to interacting with org, Helm covers just about anything.

helm

Helm offers a command called helm-mini that opens a helm buffer populated with recent files and currently open buffers. I want Helm everywhere, so instead we’ll activate helm-mode and work from there.

(req-package helm-config
  :require popwin
  :diminish (helm-mode . "")
  :init
  (progn
    (setq helm-ff-auto-update-initial-value)
    (setq popwin:special-display-config
          (append helm-popwin
                  popwin:special-display-config))
    (bind-key* "M-x" 'helm-M-x)
    (bind-key* "C-x C-f" 'helm-find-files))
  :config
  (helm-mode 1))

popwin

The one annoying thing about helm is that the window it opens to show results is kinda huge. I use popwin to limit the height of most of the Helm buffers.

(req-package popwin
  :init
  (progn
    (popwin-mode 1)
    (setq helm-popwin
          '(("*Helm Find Files*" :height 10)
            ("^\*helm.+\*$" :regexp t :height 10)))))

Org

(req-package org
  :config
  (progn
    (add-hook 'org-mode-hook
              '(lambda ()
                 (setq mode-name "")))
    (bind-key* "C-c c" 'org-capture)
    (bind-key* "C-c l" 'org-store-link)
    (bind-key* "C-c a" 'org-agenda)
    (bind-key* "C-c b" 'org-iswitch)))

UI

I’m pretty picky about how I want my editor to look, so there’s a fair bit of configuration that goes here.

Theme

I’ve switched entirely to dark themes to make working with Structured Haskell Mode easier, and I like the colors of moe-theme. It’s bright and has good default faces for most modes. It also has dark and light versions, which is convenient.

I also advise load-theme to fully unload the previous theme before loading a new one.

(defadvice load-theme 
  (before theme-dont-propagate activate)
  (mapc #'disable-theme custom-enabled-themes))

(req-package moe-theme)

(req-package moe-theme-switcher
  :require moe-theme)

Modeline

Powerline is very popular in Vim (and with Evil users), but I much prefer smart-mode-line. It’s compatible with just about anything you can imagine, and it’s easy to set up.

smart-mode-line

(req-package smart-mode-line
             :require nyan-mode
             :init (sml/setup))    

nyan-mode

nyan-mode is a goofy way to display one’s location in a file.

(req-package nyan-mode
         :init
         (progn
           (nyan-mode)
           (setq nyan-wavy-trail t))
         :config (nyan-start-animation))    

powerline

(req-package powerline)

Faces

(req-package faces
             :config
             (progn
               (set-face-attribute 'default nil :family "DejaVu Sans Mono")
               (set-face-attribute 'default nil :height 120)))   

Cleanup

Who wants all that toolbars and scrollbars noise?

(req-package scroll-bar
             :config
             (scroll-bar-mode -1))

(req-package tool-bar
             :config
             (tool-bar-mode -1))

(req-package menu-bar
             :config
             (menu-bar-mode -1))   

I also use diminish to clean up the modeline.

(req-package diminish)

(req-package server
             :diminish (server-buffer-clients . ""))

IDE

A few conveniences that I like to have in all my prog-mode buffers.

Flycheck

Flycheck has helped me write more programs than I’m totally comfortable admitting.

(req-package flycheck
  :diminish (global-flycheck-mode . "")
  :config
  (add-hook 'after-init-hook 'global-flycheck-mode))

(req-package helm-flycheck
  :require flycheck
  :commands helm-flycheck
  :config
  (bind-key "C-c ! h"
            'helm-flycheck
            flycheck-mode-map))

Magit

The only git wrapper that matters.

(req-package magit
  :diminish magit-auto-revert-mode)

Line Numbers

(req-package linum
  :config
  (add-hook 'prog-mode-hook
            '(lambda () (linum-mode 1))))   

Relative Line Numbers

I was a little spoiled by this feature in Vim, and not having it just doesn’t sit well with me.

(req-package linum-relative
  :init (setq linum-relative-current-symbol ""))

Delimiters

I like my delimiters matched and visually distinct. I used paredit for a long time, but I’m currently experimenting with smartparens. As for the visual element, I quite like rainbow-delimiters.

(req-package smartparens-config
  :ensure smartparens
  :diminish (smartparens-mode . "()")
  :init (smartparens-global-mode t))

(req-package rainbow-delimiters
  :config
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode))

Colors

I’ve had to work with colors in a fair bit of code, so having them displayed in buffer is convenient.

(req-package rainbow-mode
  :diminish (rainbow-mode . "")
  :config (add-hook 'prog-mode-hook 'rainbow-mode))

There’s also an interesting mode for uniquely coloring identifiers in code so that they are easy to scan for. It’s still a bit iffy, but it’s fun to try.

(req-package color-identifiers-mode
  :diminish (color-identifiers-mode . "")
  :init
  (setq color-identifiers:num-colors 50)
  :config
  (progn
    (add-hook 'emacs-lisp-mode-hook 'color-identifiers-mode)
    (add-hook 'ruby-mode-hook 'color-identifiers-mode)))

Completion

(req-package auto-complete-config
  :ensure auto-complete
  :init
  (progn
    (ac-config-default)
    (setq ac-auto-start 3))
  :config
  (progn
    (require 'ac-math)
    (require 'auto-complete-auctex)))

Tags

(req-package ggtags)

Grepping

Except really I’m =ag=ging.

(req-package helm-ag
  :require evil-leader)

Languages

Haskell

(req-package haskell-mode
  :require (flycheck flycheck-haskell)
  :commands haskell-mode
  :init
  (add-to-list 'auto-mode-alist '("\\.l?hs$" . haskell-mode))
  :config
  (progn
    (req-package inf-haskell)
    (req-package hs-lint)
    (bind-key "C-x C-d" nil haskell-mode-map)
    (bind-key "C-c C-z" 'haskell-interactive-switch haskell-mode-map)
    (bind-key "C-c C-l" 'haskell-process-load-file haskell-mode-map)
    (bind-key "C-c C-b" 'haskell-interactive-switch haskell-mode-map)
    (bind-key "C-c C-t" 'haskell-process-do-type haskell-mode-map)
    (bind-key "C-c C-i" 'haskell-process-do-info haskell-mode-map)
    (bind-key "C-c M-." nil haskell-mode-map)
    (bind-key "C-c C-d" nil haskell-mode-map)
    (defun my-haskell-hook ()
      (setq mode-name " λ ")
      (turn-on-haskell-doc)
      (diminish 'haskell-doc-mode "")
      (capitalized-words-mode)
      (diminish 'capitalized-words-mode "")
      (turn-on-eldoc-mode)
      (diminish 'eldoc-mode "")
      (turn-on-haskell-decl-scan)
      (setq evil-auto-indent nil))
    (setq haskell-font-lock-symbols 'unicode)
    (setq haskell-literate-default 'tex)
    (setq haskell-stylish-on-save t)
    (setq haskell-tags-on-save t)
    (add-hook 'haskell-mode-hook 'my-haskell-hook)))

(req-package flycheck-haskell
  :config (add-hook 'flycheck-mode-hook #'flycheck-haskell-setup))

Structured Haskell Mode

(req-package shm
             :require haskell-mode
             :commands structured-haskell-mode
             :init (add-hook 'haskell-mode-hook
                             'structured-haskell-mode))

ghc-mod

(req-package ghc
  :init (add-hook 'haskell-mode-hook (lambda () (ghc-init))))

Emacs Lisp

(req-package lisp-mode
  :init
  (add-hook 'emacs-lisp-mode-hook
            (lambda ()
              (setq mode-name " ξ ")))) 

LaTeX

All you need is AUCTeX, end of story.

(req-package tex-site
  :require auto-complete-config
  :ensure auctex)

(req-package ac-math
  :require auto-complete-config)

(req-package auto-complete-auctex
  :require auto-complete-config)

R

(req-package ess-site
  :ensure ess)

Idris

(req-package idris-mode)

PHP/Drupal

drupal-mode has php-mode as a dependency, so we could conceivably get away with just including the former here, but just in case we want a bit more control or we decide that drupal-mode isn’t worth it, we’ll make separate req-package blocks.

(req-package php-mode
  :init (setq php-template-compatibility nil))

(req-package web-mode)

(req-package drupal-mode
  :require (php-mode ggtags))

Javascript

(req-package tern
  :require tern-auto-complete
  :init
  (progn
    (add-hook 'js-mode-hook
              (lambda ()
                (tern-mode t))))
  :config
  (progn
    (tern-ac-setup)))

(req-package tern-auto-complete)

Annoyances

Fixing a couple of gripes I have with Emacs.

Exec path

(req-package exec-path-from-shell
  :init
  (when (memq window-system '(mac ns))
    (exec-path-from-shell-initialize)))

Backups and Autosave Files

These things end up everywhere, so let’s stick them all in a temporary directory.

(req-package files
  :init
  (progn
    (setq backup-directory-alist
          `((".*" . ,temporary-file-directory)))
    (setq auto-save-file-name-transforms
          `((".*" ,temporary-file-directory t)))))

Questions

Keep it short.

(defalias 'yes-or-no-p 'y-or-n-p)

Customizations

cus-edit+ is a really handy way to keep your customizations up to date, especially if you set your custom-file.

(req-package cus-edit+
  :init (customize-toggle-outside-change-updates))

Fulfill Requirements

At long last we need only call the following function to send req-package on its merry way.

(req-package-finish)