The Setup

Table of Contents

Introduction

This file takes a page out of the book of Hardcore Freestyle Emacs, in which a single org-file can be tangled to create all the necessary dotfiles required for my everyday computer usage.

This file was last exported: 2015-12-12 14:45

How to use these files

My dotfiles setup uses a slightly different setup than most peoples'.

Instead of a repo full of configuration files that people can put into place, my setup consists of a number of org-mode files that contain source code in them. Using Emacs they are then "tangled" to produce specific files. For example, the zsh.org file can be tangled to produce the .zshenv, .zshrc and other ZSH-related configuration files.

So how would you use this?

I include the bin/tangle script, which invokes Emacs in such a manner to produce the files a particular org file would produce. Generally if you want to actually use these, you wouldn't have to do that because I provide a Makefile that will perform the tangling for you. So generally you could do:

git clone https://github.com/dakrone/dakrone-dotfiles.git ~/dotfiles
cd ~/dotfiles
./bootstrap-packages.sh # see caveats below
make

# And then, if you wanted to symlink the files to be "installed"
make install # or `make force-install` to overwrite files

When an update is posted, I usually do:

cd ~/dotfiles
make

And since the installation consists of symlinks, there is no install needed. Makefile will only regenerate the config files for files that have changes.

Caveat

The only caveat to this is the bootstrap-packages.sh file, because this installs the files necessary for tangling, therefore it needs to be runable directly from the repo. It can be regenerated however, from bootstrap.org.

Bootstrapping machines

Host machine configuration script

I spend probably 90% of my time on various Linux machines, usually either a Fedora or Ubuntu machine, and the other 9.99% on Mac OSX. The remaining 0.01% of my time is spent in Windows, so it isn't covered much here.

I need to make sure a number of packages are installed and set up correctly on each machine, so the following should pass:

#!/bin/bash

#####################################################
## Warning, this file was automatically generated! ##
## Change bootstrap.org if you need to update it.  ##
#####################################################

# Bash strict mode
set -euo pipefail

function setup_osx() {
    echo "[-] Setting up OSX"
    echo "[-] Done setting up OSX"
}

function setup_linux() {
    # returns a string like "Fedora" or "Ubuntu"
    DISTRO=`lsb_release -i | cut -d: -f 2 | tr -d '[:space:]'`

    case $DISTRO in
        Fedora)
            setup_fedora
            setup_linux_generic
            ;;
        Ubuntu)
            setup_ubuntu
            setup_linux_generic
            ;;
        Debian)
            # We can *try* to set up Debian like Ubuntu, but it's not tested
            setup_ubuntu
            setup_linux_generic
            ;;
        *)
            echo "Sorry, I haven't implemented anything for this OS ($DISTRO) yet"
            exit 1
    esac
}

# Common things for all Linux distributions
function setup_linux_generic() {
    echo "[-] Setting up generic Linux things"

    echo -n "[!] Don't forget to update the max_file_descriptors, currently: "
    ulimit -n

    echo "[-] Done setting up generic Linux"
}

function setup_fedora() {
    PACKAGES="emacs git tmux zsh htop keychain the_silver_searcher python-pip cmake"

    echo "[-] Setting up Fedora"

    echo "Enabling sshd..."
    sudo systemctl enable sshd.service
    sudo systemctl start sshd.service

    # Install the minimal necessary software I need
    echo "Installing software..."
    sudo dnf group install -y "Development Tools"
    sudo dnf group install -y "C Development Tools and Libraries"
    sudo dnf install -y $PACKAGES

    echo "[-] Done setting up Fedora"
}

function setup_ubuntu() {
    PACKAGES="git tmux zsh htop keychain silversearcher-ag"

    echo "[-] Setting up Ubuntu"

    echo "Installing software..."
    sudo apt-get update
    sudo apt-get install -y --force-yes build-essential
    sudo apt-get install -y --force-yes $PACKAGES

    echo "[-] Done setting up Ubuntu"
}

function setup_generic() {
    echo "[-] Setting up OS-agnostic things..."

    mkdir -p ~/.zsh
    if [ ! -d ~/.zsh/zsh-completions ]; then
        echo "Installing zsh completions"
        cd ~/.zsh && git clone https://github.com/zsh-users/zsh-completions.git
    fi

    if [ ! -d ~/.zsh/zsh-syntax-highlighting ]; then
        echo "Installing zsh syntax highlighting"
        cd ~/.zsh && git clone https://github.com/zsh-users/zsh-syntax-highlighting.git
    fi

    echo "[-] Done Setting up OS-agnostic things"
}

function main() {
    echo "[+] Starting setup"

    OS=`uname`

    case $OS in
        Darwin)
            setup_osx
            ;;
        Linux)
            setup_linux
            ;;
        *)
            echo "Operating system ($OS) not supported!"
            exit 1
    esac

    setup_generic

    echo "[+] Finished setup"
}

### Start of actual script

main

This should then be manually run by running:

$ sh ~/bootstrap-packages.sh

Note that you'll need password-less (or caching) sudo access for this.

ZSH Configuration

Introduction

~/.zshenv

The ~/.zshenv file is read for all shells, regardless of login state or not, and it's a pretty good place to put PATH stuff and other env variables that don't cause a lot of interaction.

[[ -o interactive ]] && echo "+++Reading .zshenv"

MANPATH=/opt/local/man:/usr/local/man:$MANPATH
WORDCHARS='*?_[]~=&;!#$%^(){}'
# default is: *?_-.[]~=/&;!#$%^(){}<>
# other: "*?_-.[]~=&;!#$%^(){}<>\\"
WORDCHARS=${WORDCHARS:s,/,,}
LEDGER_FILE=$HOME/ledger.dat; export LEDGER_FILE

export EDITOR=nano # to be overwritten later
export PAGER=less

# Update path with local ~/bin and cabal's bin dir
export PATH=~/bin:~/.cabal/bin:/usr/local/bin:/usr/local/sbin:$PATH

# Node/npm
export PATH=$PATH:~/node_modules/.bin

# Cask
export PATH=$PATH:~/.cask/bin

# rbenv
export PATH=~/.rbenv/bin:$PATH

# Virtualenvwrapper environment home
export WORKON_HOME=~/.venvs
if [ -d "$WORKON_HOME" ]; then
    mkdir -p "$WORKON_HOME"
fi

# Read sdkman config for all shells
if [ -f ~/.sdkman/bin/sdkman-init.sh ]; then
    source ~/.sdkman/bin/sdkman-init.sh
fi

# history
HISTFILE=$HOME/.zsh-history
HISTSIZE=10000
SAVEHIST=5000

I read OS and machine-specific things in .zshenv, usually because they're pretty small and just set up paths.

## Sourcing OS-specific things
OS=$(uname -s); export OS
if [[ -f ~/.zsh.d/zsh.${OS} ]]; then
    if [[ ! -z $ZSHDEBUG ]]; then
        echo +++ ~/.zsh.d/zsh.${OS}
    fi
    source ~/.zsh.d/zsh.${OS}
fi

## Sourcing machine-specific things
export HOSTPREFIX=`hostname | cut -d. -f1 | sed 's/.*/\L&/'`
if [[ -f ~/.zsh.d/zsh.${HOSTPREFIX} ]]; then
    if [[ ! -z $ZSHDEBUG ]]; then
        echo +++ ~/.zsh.d/zsh.${HOSTPREFIX}
    fi
    source ~/.zsh.d/zsh.${HOSTPREFIX}
fi

Vagrant seems to want to use libvirt instead of VirtualBox on my machines, so I want to force it to use virtualbox.

export VAGRANT_DEFAULT_PROVIDER=virtualbox

Finally, if this a dumb term (running M-x shell in Emacs, unset some things and make it really dumb.

## With Emacs 23, I've found this needs to go in ~root/.zshrc too to
## help with Tramp hangs.
[[ $TERM == "dumb" ]] && unsetopt zle && PS1='$ '
[[ ! $TERM == "dumb" ]] && TERM=xterm-256color

~/.zprofile

I don't currently use ~/.zprofile, but I should…

~/.zshrc

# Handle dumb terms
[[ $TERM == "dumb" ]] && unsetopt zle && PS1='$ ' && return

echo -n "+++Reading .zshrc"
[[ -o interactive ]] && echo -n " (for interactive use)"
echo .

# Used for reporting how load loading takes
zmodload zsh/datetime
start=$EPOCHREALTIME

# for $PATH see ~/.zshenv

# report things that take more than 5 seconds
export REPORTTIME=5

# 10 second poll time for autossh
export AUTOSSH_POLL=10

# don't show load in prompt by default
export SHOW_LOAD=false

# start with a pre-title of nothing
export PRETITLE=""

# "persistent history"
# just write important commands you always need to ~/.important_commands
if [[ -r ~/.important_commands ]] ; then
    fc -R ~/.important_commands
fi

# support colors in less
export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;31m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m'
export LESS_TERMCAP_so=$'\E[01;44;33m'
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;32m'

# zsh completion
if [ -d ~/.zsh/zsh-completions ] ; then
    fpath=(~/.zsh/zsh-completions/src $fpath)
fi

autoload -U compinit zrecompile

zsh_cache=${HOME}/.zsh-cache
if [ $UID -eq 0 ]; then
    compinit
else
    compinit -d $zsh_cache/zcomp-$HOST

    for f in ~/.zshrc $zsh_cache/zcomp-$HOST; do
        zrecompile -p $f && rm -f $f.zwc.old
    done
fi

zstyle ':completion:::::' completer _complete _approximate
zstyle ':completion:*' use-cache on
zstyle ':completion:*' cache-path ~/.zsh-cache
zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
zstyle ':completion:*' hosts $ssh_hosts
zstyle ':completion:*:my-accounts' users-hosts $my_accounts
zstyle ':completion:*:other-accounts' users-hosts $other_accounts
zstyle -e ':completion:*:approximate:*' max-errors 'reply=( $(( ($#PREFIX + $#SUFFIX) / 3 )) )'
zstyle ':completion:*:descriptions' format "- %d -"
zstyle ':completion:*:corrections' format "- %d - (errors %e})"
zstyle ':completion:*:default' list-prompt '%S%M matches%s'
zstyle ':completion:*' group-name ''
zstyle ':completion:*:manuals' separate-sections true
zstyle ':completion:*:manuals.(^1*)' insert-sections true
zstyle ':completion:*' verbose yes
zstyle ':completion:*' file-list list=20 insert=10


### OPTIONS ###
setopt multios               # allow pipes to be split/duplicated
# ^^ try this: cat foo.clj > >(fgrep java | wc -l) > >(fgrep copy | wc -l)
setopt auto_cd
setopt extended_glob
setopt append_history
setopt extended_history
setopt share_history
setopt histignorealldups
setopt nohup
setopt longlistjobs
setopt notify
# I use dvorak, so correct spelling mistakes that a dvorak user would make
setopt dvorak

autoload -U url-quote-magic
zle -N self-insert url-quote-magic

# Source z.sh if available
if [ -s ~/bin/z.sh ] ; then
    source ~/bin/z.sh ;
fi

# Use zsh syntax highlighting if available
if [ -s ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh ] ; then
    source ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
fi

# Source ~/.zsh.d/*
setopt EXTENDED_GLOB
for zshrc in ~/.zsh.d/[0-9][0-9]*[^~] ; do
    if [[ ! -z $ZSHDEBUG ]]; then
        echo +++ $(basename $zshrc)
    fi
    source $zshrc
done
unsetopt EXTENDED_GLOB

end=$EPOCHREALTIME

printf "+++Loaded files in %0.4f seconds\n" $(($end-$start))

Other ZSH configuration

I try to segment out different things I need into separate zsh files, all inside of .zsh.d and loaded by order. This includes

  • aliases
  • functions
  • OS-specific things
  • host specific things

First, aliases, for which I have many!

Aliases
# colorful ls for whichever platform
if ls -F --color=auto >&/dev/null; then
    alias ls="ls --color=auto -F"
else
    alias ls="ls -GF"
fi
# various ls helpers
alias l.='ls -d .*'
alias ll='ls -lh'
alias l='ls -lh'
alias la='ls -alh'
alias lr='ls -lR'
# colorize greps
alias grep='grep --color=auto'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
# make less a little more sane
alias less='less -RX'
# various port forwarding and hole-punching
alias scsetup='sudo socat -d -d TCP4-listen:6666,fork OPENSSL:typoet.com:443,cert=host.pem,verify=0'
alias scsetup2='sudo socat -d -d TCP4-listen:7777,fork OPENSSL:blackex:443,cert=host.pem,verify=0'
# reverse proxy & keepopen
alias prox='ssh -nNT -R 4444:localhost:22 writequit.org'
alias autoprox='autossh -M 22000 -nNT -R 4444:localhost:22 writequit.org'
alias awq='autossh -M 22001 writequit.org'
# open elinks quickly
alias el='TERM=xterm-color elinks'
# datetime aliases
alias dt='gdate "+%Y-%m-%dT%H:%M:%S.%3N%zZ"'
# Elasticsearch's basic_date_time
alias bdt='gdate "+%Y%m%dT%H%M%S.%3N%z"'
alias epoch='date +%s'
# jump start to magit
alias magit='emacs -f magit-status'
# simple-extract
alias se="tar zxvf"
alias ga="git annex"
# download manager
alias aria2c='aria2c -c -x5 -s10 -m0'
# sync org files
alias org2ivalice='rsync -azP --delete ~/org/ ivalice-local:~/org'
alias ivalice2org='rsync -azP --delete ivalice-local:~/org/ ~/org'
alias xanadu2org='rsync -azP --delete xanadu:~/org/ ~/org'
alias org2xanadu='rsync -azP --delete ~/org/ xanadu:~/org'
# start a master tmux
alias tmaster='tmux -2 -u -S /tmp/mastermux -f .tmux.master.conf'
Functions

Next, some functions, when shell aliases just won't do!

# functions
function history-all { history -E 1 }

# function to fix ssh agent
function fix-agent() {
    disable -a ls
    export SSH_AUTH_SOCK=`ls -t1 $(find /tmp/ -uid $UID -path \\*ssh\\* -type s 2> /dev/null) | head -1`
    enable -a ls
}

## TODO make these scripts instead of functions

# Check if a URL is up
function chk-url() {
    curl -sL -w "%{http_code} %{url_effective}\\n" "$1" -o /dev/null
}

# Tunnel ES from somewhere to here locally on port 9400
function es-tunnel() {
    autossh -M0 $1 -L 9400:localhost:9200 -CNf
}

# Tunnel logstash/kibana locally
function kibana-tunnel() {
    autossh -M0 $1 -L 9292:localhost:9292 -CNf
}

# Delete a branch locally and on my (dakrone) fork
function del-branch() {
    git branch -D $1
    git push dakrone :$1
}

# look up a process quickly
function pg {
    # doing it again afterwards for the coloration
    ps aux | grep -F -i $1 | grep -F -v grep | grep -F -i $1
}

# cd back up to the highest level git repo dir
# thanks Dan!
function cds () {
    ORIGINAL_PWD=`pwd`
    while [ ! -d ".git" -a `pwd` != "/" ]
    do
        cd ..
    done
    if [ ! -d ".git" ]
    then
        cd $ORIGINAL_PWD
    fi
}
Keybindings

Keybindings for the shell, in this case, mostly Emacs-compatible, but with some disabled to prevent dumb things. (Mostly me being dumb)

bindkey -e
bindkey "^?"    backward-delete-char
bindkey "^H"    backward-delete-char
bindkey "^[[3~" backward-delete-char
bindkey "^[[1~" beginning-of-line
bindkey "^[[4~" end-of-line

bindkey '^r' history-incremental-search-backward
bindkey "^[[5~" up-line-or-history
bindkey "^[[6~" down-line-or-history
bindkey "^A" beginning-of-line
bindkey "^E" end-of-line
bindkey "^W" backward-delete-word
bindkey "^k" kill-line
bindkey ' ' magic-space    # also do history expansion on space
bindkey '^I' complete-word # complete on tab, leave expansion to _expand
bindkey -r '^j' #unbind ctrl-j, I hit it all the time accidentaly
bindkey -r '^[x' # remove M-x for emacs-things
SSH

I'm using Keychain to manage SSH agent inheritance, so it just needs to be eval-ed when nodes start up. It loads the key in ~/.ssh/id_rsa.

eval $(keychain --eval --agents ssh -Q id_rsa)
Git

Next, I need to set up some colors and formatting that ZSH will use for VCS info

autoload colors
colors

git_branch() {
    git branch --no-color 2>/dev/null | grep '^*' | colrm 1 2
    # $pipestatus[1] for the git exit code
}

autoload -Uz vcs_info

if [[ ! $TERM = "dumb" ]]; then
    zstyle ":vcs_info:*" check-for-changes true
    zstyle ":vcs_info:*" stagedstr "%F{green}*"
    zstyle ":vcs_info:*" unstagedstr "%F{yellow}*"
    zstyle ":vcs_info:(sv[nk]|bzr):*" branchformat "%b%F{1}:%F{yellow}%r%{$reset_color%}"
    zstyle ":vcs_info:*" enable git svn bzr hg
    precmd () {
        if [[ -z $(git ls-files --other --exclude-standard 2> /dev/null) ]] {
               zstyle ":vcs_info:*" formats "%b%c%u%{$reset_color%}"
           } else {
               zstyle ":vcs_info:*" formats "%b%c%u%F{red}*%{$reset_color%}"
           }
           vcs_info
    }
else
    zstyle ":vcs_info:*" check-for-changes true
    zstyle ":vcs_info:*" stagedstr "*"
    zstyle ":vcs_info:*" unstagedstr "*"
    zstyle ":vcs_info:(sv[nk]|bzr):*" branchformat "%b:%r"
    zstyle ":vcs_info:*" enable git svn bzr hg
    precmd () {
        if [[ -z $(git ls-files --other --exclude-standard 2> /dev/null) ]] {
               zstyle ":vcs_info:*" formats "%b%c%u"
           } else {
               zstyle ":vcs_info:*" formats "%b%c%u*"
           }
           vcs_info
    }
fi
HTTP helpers

Very small, but since I do so much HTTP testing for Elasticsearch on the command line, they end up saving a lot of time.

# HTTP verbs
alias get='curl -s -XGET'
alias post='curl -s -XPOST'
alias put='curl -s -XPUT'
alias delete='curl -s -XDELETE'
Gtags

For Java development in Emacs, I rely heavily on GNU Global, which I usually install by hand since most package managers have outdated versions. So I set some various things for the config here

if [ -f ~/.globalrc ]; then
    export GTAGSCONF=$HOME/.globalrc
elif [ -f /usr/local/share/gtags/gtags.conf ] ; then
    export GTAGSCONF=/usr/local/share/gtags/gtags.conf
fi

export GTAGSLABEL=ctags
Ruby (rbenv)

I need to set up the rbenv wrapper so I can have sane ruby building. If it exists, anyway.

if [ -f ~/.rbenv/bin/rbenv ]; then
    eval "$(rbenv init -)"
fi
Python (virtualenvwrapper)

So virtualenvwrapper is a handy thing for managing virtualenv sessions, but it needs to be sourced if available.

Use pip install virtualenvwrapper to install it

if whence -cp virtualenvwrapper.sh > /dev/null 2>&1; then
    source `whence -cp virtualenvwrapper.sh`
fi
Opam (ocaml)

I'm checking this out…

if [ -f ~/.opam/opam-init/init.zsh ]; then
  . ~/.opam/opam-init/init.zsh > /dev/null 2> /dev/null || true
fi
Prompt

I would I have a medium-level prompt in terms of ridiculousness. It's two lines, displays git information, and has decent colors, so it's not too bad. I've never been a fan of ZSH frameworks though, so mine is hand-written and mostly combined from various places around the internet.

I used to have a nethack pet (the dog) in it too, that would randomly wander around, but yeah, it didn't last.

When used, it looks something like this (with more colors):

~/src/elasticsearch (git) ac32f3d3 * master [origin/master +1/-2] (1 stashed)
»

And it's not too unreadable…

autoload -U add-zsh-hook
autoload -U colors && colors
autoload -Uz vcs_info
setopt prompt_subst

local gray="%{$fg_bold[black]%}"
local green="%{$fg_bold[green]%}"
local blue="%{$fg[blue]%}"
local red="%{$fg[red]%}"
local yellow="%{$fg[yellow]%}"

zstyle ':vcs_info:*' enable git svn cvs hg
zstyle ':vcs_info:git*:*' get-revision true
zstyle ':vcs_info:git*:*' check-for-changes true

# hash changes branch misc
zstyle ':vcs_info:git*' formats "(%s) %8.8i ${green}%c${red}%u${gray} %b%m"
zstyle ':vcs_info:git*' actionformats "(%s|${yellow}%a${gray}) %8.8i ${green}%c${red}%u${gray} %b%m"
zstyle ':vcs_info:git*+set-message:*' hooks git-st git-stash

# Show remote ref name and number of commits ahead-of or behind
function +vi-git-st() {
    local ahead behind remote
    local -a gitstatus

    # Are we on a remote-tracking branch?
    remote=${$(git rev-parse --verify ${hook_com[branch]}@{upstream} \
                   --symbolic-full-name 2>/dev/null)/refs\/remotes\/}

    if [[ -n ${remote} ]] ; then
        ahead=$(git rev-list ${hook_com[branch]}@{upstream}..HEAD 2>/dev/null | wc -l | tr -d " ")
        (( $ahead )) && gitstatus+=( "${green}+${ahead}${gray}" )

        behind=$(git rev-list HEAD..${hook_com[branch]}@{upstream} 2>/dev/null | wc -l | tr -d " ")
        (( $behind )) && gitstatus+=( "${red}-${behind}${gray}" )

        if [[ -n ${gitstatus} ]] ; then
            hook_com[branch]="${hook_com[branch]} [${remote} ${(j:/:)gitstatus}]"
        else
            hook_com[branch]="${hook_com[branch]} [${remote}]"
        fi
    fi
}

# Show count of stashed changes
function +vi-git-stash() {
    local -a stashes
    if [[ -s ${hook_com[base]}/.git/refs/stash ]] ; then
        stashes=$(git stash list 2>/dev/null | wc -l | tr -d " ")
        hook_com[misc]+=" (${stashes} stashed)"
    fi
}

function colorSetup {
    # A script to make using 256 colors in zsh less painful.
    # P.C. Shyamshankar <sykora@lucentbeing.com>
    # Copied from http://github.com/sykora/etc/blob/master/zsh/functions/spectrum/

    typeset -Ag FX FG BG

    FX=(
        reset     "%{%}"
        bold      "%{%}" no-bold      "%{%}"
        italic    "%{%}" no-italic    "%{%}"
        underline "%{%}" no-underline "%{%}"
        blink     "%{%}" no-blink     "%{%}"
        reverse   "%{%}" no-reverse   "%{%}"
    )

    for color in {000..255}; do
        FG[$color]="%{[38;5;${color}m%}"
        BG[$color]="%{[48;5;${color}m%}"
    done

    # Show all 256 colors with color number
    function spectrum_ls() {
        for code in {000..255}; do
            print -P -- "$code: %F{$code}Test%f"
        done
    }

    # Show all 256 colors where the background is set to specific color
    function spectrum_bls() {
        for code in {000..255}; do
            ((cc = code + 1))
            print -P -- "$BG[$code]$code: Test %{$reset_color%}"
        done
    }
}

# Initialize colors for setprompt2
colorSetup

# old-prompt
PROMPT='$FG[032]%~ $FG[237]${vcs_info_msg_0_}
$FG[105]%(?..${red}%?$FG[105] )%(!.#.»)%{$reset_color%} '

add-zsh-hook precmd vcs_info

Here's a commented out (but much less extravagant) version of a prompt that I keep around, just in case.

# Simple prompt setup
# if not_in_cloud; then
#     # PROMPT='%n@%m %w %* %! %? %B%3~%b(${vcs_info_msg_0_})%# '; export PROMPT
#     PROMPT='%n@%m %? %B%3~%b(${vcs_info_msg_0_})%# '; export PROMPT
# else
#     PROMPT='%n@%m %? %~%# '; export PROMPT
# fi
Dumb terminal setup

Just a couple of left overs for very dumb terminals (running shells inside of things, mostly). It tangles to 99-dumb.zsh to ensure it's loaded last.

# Things for dumb terminals
if [[ "$EMACSx" == "tx" || "$TERM" == "dumb" ]]; then
    unsetopt zle
    #unfunction precmd
    export DISABLE_AUTO_TITLE=true
    export ZSH_HIGHLIGHT_MAXLENGTH=0
else
    alias ag="ag --pager='less -FRX'"
fi
Machine/OS-specific shell configuration

Finally, I have some either OS-specific or host-specific configurations, which are loaded by ~/.zshenv by looking for

~/.zsh.d/zsh.$OS

Where $OS is something like 'Darwin' or 'Linux'

And then also loading

~/.zsh.d/zsh.$HOSTPREFIX

Which $HOSTPREFIX is the output of

hostname | cut -d. -f1 | sed 's/.*/\L&/'

which essentially calls hostname, takes only the first part and lowercases it.

  • Darwin (OSX)

    On OSX, I mostly just have to do a lot of nonsense to get Emacs stuff to work correctly.

    export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
    
    export EMACS_HOME="/Applications/Emacs.app/Contents/MacOS"
    export ERC_HOME="/Applications/ERC.app/Contents/MacOS"
    export GNUS_HOME="/Applications/Gnus.app/Contents/MacOS"
    
    if [ -s /usr/local/bin/emacs ]; then
        alias emacs='TERM=xterm-256color emacs'
        alias hb_emacs='/usr/local/bin/emacs'
    fi
    
    #function ec() { TERM=xterm-256color PATH=$EMACS_HOME/bin:$PATH emacsclient -t $@ }
    alias e="TERM=xterm-256color PATH=$EMACS_HOME/bin:$PATH $EMACS_HOME/Emacs -nw"
    alias ec="emacsclient"
    
    #function el() { ps ax|grep Emacs }
    function ekill() { emacsclient -e '(kill-emacs)' }
    
    alias emacs="TERM=xterm-256color PATH=$EMACS_HOME/bin:$PATH $EMACS_HOME/Emacs -nw"
    alias gemacs="TERM=xterm-256color PATH=$EMACS_HOME/bin:$PATH $EMACS_HOME/Emacs 2>&1 > /dev/null &"
    alias erc="TERM=xterm-256color PATH=$ERC_HOME/bin:$PATH $ERC_HOME/Emacs 2>&1 > /dev/null &"
    alias gnus="TERM=xterm-256color PATH=$GNUS_HOME/bin:$PATH $GNUS_HOME/Emacs 2>&1 > /dev/null &"
    
    # for connection to a running emacs
    export EDITOR="emacsclient"
    export ALTERNATIVE_EDITOR="TERM=xterm-256color PATH=$EMACS_HOME/bin:$PATH $EMACS_HOME/Emacs -nw"
    
    # Use MacVim's vim for terminal sessions, since it has everything compiled in.
    alias vim='/Applications/MacVim.app/Contents/MacOS/Vim'
    
    # Remove ctrl+y from the keybinds for delayed suspend
    stty dsusp undef
    
    # awesome
    alias gps="ps -c -r -ax -o command,pid,pcpu,time | sed 's/\(PID *\)%/\1 %/' | head -n 11 && echo && ps -c -m -ax -o command,pid,pmem,rss=RSIZE | sed 's/\(.\{23\}\)/\1 /' | head -n 9"
    
    alias tmux='tmux -2 -f .tmux.osx.conf'
    
    # A function to mimic Linux's strace, whichout running the program as root
    function strace {
        sudo dtruss -f sudo -u `whoami` $*
    }
    
  • Linux

    Linux has less customization, mostly differing aliases.

    # make emacs have 256 colors
    alias -g emacs='TERM=xterm-256color /usr/local/bin/emacs -nw'
    
    alias -g ec="/home/hinmanm/bin/emacsclient"
    
    function ekill() { emacsclient -e '(kill-emacs)' }
    
    export EDITOR="emacs -nw"
    
    alias tmux='tmux -2'
    
    # awesome
    alias gps='ps -eo cmd,fname,pid,pcpu,time --sort=-pcpu | head -n 11 && echo && ps -eo cmd,fname,pid,pmem,rss --sort=-rss | head -n 9'
    
  • Thulcandra (main laptop)

    There's probably more I need to put here, but for now it's empty.

    # Thulcandra had better be a linux machine...
    if [[ $OS == "Linux" ]]; then
       export JAVA_HOME=/opt/jdk1.8.0_60
       export PATH=$JAVA_HOME/bin:$PATH
    fi
    
  • Xanadu (old laptop)

    There's probably more I need to put here, but for now it's empty.

    # Xanadu had better be a linux machine...
    if [[ $OS == "Linux" ]]; then
       export JAVA_HOME=/opt/jdk1.8.0_51
       export PATH=$JAVA_HOME/bin:$PATH
    fi
    
  • Ivalice (main desktop)

    I am using a specific JDK on this machine, so I set it here.

    # Ivalice had better be a linux machine...
    if [[ $OS == "Linux" ]]; then
       export JAVA_HOME=/opt/jdk1.8.0_45
       export PATH=$JAVA_HOME/bin:$PATH
    fi
    

Git Configuration

Introduction

First, let's start with who I am

[user]
  name = Lee Hinman
  email = lee@writequit.org
  signingkey = 3ACECAE0

I globally ignore anything in ~/.gitignore, which allows me to ignore things like TAGS files

[core]
  excludesfile = /home/hinmanm/.gitignore

Turn on color and rerere everywhere. Additionally I default pushing to track the branch if it isn't already.

[color]
  ui = auto

[rerere]
  enabled = 1

[push]
  default = tracking

There's a separate diffing algorithm called "patience" that I've been trying out, to see if maybe it helps with conflicts and reading patches, so configure that here.

For Clojure code in particular, I set up a variable that tells git what the function name is, so for something like:

(defn myfunc [x]
  (+ 1 thing))

If you change the (+ 1 thing) line, git can use the ^\\(.* regex to display the name of the function in the diff.

[diff]
  algorithm = patience

[diff "clojure"]
  xfuncname = "^\\(.*"

Set up the various aliases that I end up using. Out of these I probably use co, rips, pr, and sync the most.

[alias]
  co = checkout
  cob = checkout -b
  cot = checkout -t
  st = status -sb
  stat = status -sb
  status = status -sb
  unpushed = log --branches --not --remotes --simplify-by-decoration --decorate --oneline
  lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative
  l = log --pretty="format:'%C(yellow)%h %C(red)%G? %C(blue)%aN%C(reset)  %s'" --show-signature
  lol = log --graph --decorate --pretty=oneline --abbrev-commit
  lola = log --graph --decorate --pretty=oneline --abbrev-commit --all
  rips = rebase -i -p -s subtree
  headless = !sh -c 'git symbolic-ref HEAD refs/heads/$0 && rm .git/index && git clean -fdx'
  vtags = !sh -c 'git tag -l | xargs git tag -v'
  ver = "!git log --pretty=format:'%ai %h' -1 | perl -pe 's,(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d) (\\d\\d):(\\d\\d):(\\d\\d) [^ ]+ ([a-z0-9]+),\\1\\2\\3+\\7,'"
  pr = "!f() { git fetch origin pull/$1/head:pr/$1; }; f"
  sync = annex sync

There are a couple of tools that use Github config settings, which I store in a separate file, but make available through the config here.

[github]
  user = dakrone
  token = !cat ~/.github.token
  oauth-token = !cat ~/.github.oauth-token

[ghi]
  token = !cat ~/.github.token

Finally, for the git-annex tool that I use quite a bit, I configure git-annex to automatically generate metadata for files added to a repo. Quite handy for storing pictures and not having to manually tag each one.

[annex]
  genmetadata = true

Global gitignore

Basically a list of files or directories I always want to ignore in git repositories. Since it's read from ~/.gitconfig, I don't have to configure every individual repository to ignore these files separately.

.tern-port
TAGS
GTAGS
GPATH
GRTAGS
GSYM*
.DS_Store
.dir-locals.el
LUCENE-*.patch
.lein-repl*
pom.xml.asc
core/data
.projectile

Tmux Configuration

Introduction

I couldn't live without tmux, so much of my work is done on remote machines where I need to be able to disconnect running work and re-attach later.

To go even more insane, I have an interesting setup with I nest tmux inside of itself to act like terminal tabs, because, well, it's better than terminal tabs. In order to do this, I do some fancy work with multiple configuration files, so it works out like this:

On Linux, the tmux command reads ~/.tmux.conf. On OSX, the tmux command is aliased to read ~/.tmux.osx.conf, which, after setting a couple of OSX-specific settings, sources ~/.tmux.conf.

When I am running a

So, starting with the most specific

.tmux.osx.conf

# OSX tmux config that uses the wrapper from 
# https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard

set-option -g default-command "reattach-to-user-namespace -l zsh"

source-file ~/.tmux.conf

bind-key > run-shell "tmux saveb -| pbcopy"

.tmux.conf

I set the bind-key to C-z (control-z) here and not in ~/.tmux.shared.conf because I use a different bind-key for the master tmux, so I only want it in certain cases.

source-file ~/.tmux.shared.conf

# Set the prefix to ^z
#unbind-key C-b
set-option -g prefix C-z
bind-key C-z send-prefix

# keybindings to make resizing easier
bind -r C-h resize-pane -L
bind -r C-j resize-pane -D
bind -r C-k resize-pane -U
bind -r C-l resize-pane -R

# make it so that I can hold down prefix key for these
bind-key C-d detach
bind-key C-n next-window
bind-key C-p previous-window

# number windows from 0
set -g base-index 0

.tmux.master.conf

First, a nice alias to help us. I start it on a separate socket so it doesn't really interfere with anything.

# start a master tmux
alias tmaster='tmux -2 -u -S /tmp/mastermux -f .tmux.master.conf'

And then, the master-specific configuration. This config only gets run if tmux is invoked using the tmaster alias.

The bind-key in this case gets changed to M-C-z (control-alt-z) instead of my regular C-z bind-key, which allows nesting to work.

# master client conf

source-file ~/.tmux.shared.conf

# change bind key to M-C-z
set-option -g prefix M-C-z

# prefix again goes to last window
bind-key M-C-z last-window

# reload
bind r source-file ~/.tmux.master

# keybindings to make resizing easier
bind -r M-C-h resize-pane -L
bind -r M-C-j resize-pane -D
bind -r M-C-k resize-pane -U
bind -r M-C-l resize-pane -R

# make it so that I can hold down prefix key for these
bind-key M-C-d detach
bind-key M-C-n next-window
bind-key M-C-p previous-window

# window navigation
#bind-key -n M-C-h prev
#bind-key -n M-C-l next
bind-key -n M-C-n select-pane -t :.-
bind-key -n M-C-p select-pane -t :.+

# number windows from 1
set -g base-index 1

# Alt-# window nav
bind-key -n M-1 select-window -t 1
bind-key -n M-2 select-window -t 2
bind-key -n M-3 select-window -t 3
bind-key -n M-4 select-window -t 4
bind-key -n M-5 select-window -t 5
bind-key -n M-6 select-window -t 6
bind-key -n M-7 select-window -t 7
bind-key -n M-8 select-window -t 8

bind-key -n s-1 select-window -t 1
bind-key -n s-2 select-window -t 2
bind-key -n s-3 select-window -t 3
bind-key -n s-4 select-window -t 4
bind-key -n s-5 select-window -t 5
bind-key -n s-6 select-window -t 6
bind-key -n s-7 select-window -t 7
bind-key -n s-8 select-window -t 8

## Custom status bar, via https://github.com/myusuf3/dotfiles
## Powerline symbols: ⮂ ⮃ ⮀ ⮁ ⭤
## If you do not have a patched font (see: https://github.com/Lokaltog/vim-powerline/tree/develop/fontpatcher)
## comment out the lines below to get a "regular" statusbar without special symbols
set-option -g status-bg colour234
set-option -g message-fg colour16
set-option -g message-bg colour221
set-option -g status-left-length 40
set-option -g status-right-length 40
set-option -g status-interval 5
set-option -g pane-border-fg colour245
set-option -g pane-active-border-fg colour39
set-option -g status-justify left

set-option -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour238,nobold]#[fg=colour15,bg=colour238,bold] #(up) #[fg=colour238,bg=colour234,nobold]'

set-option -g status-right '#[fg=colour245]%R %d %b #[fg=colour254,bg=colour234,nobold]#[fg=colour16,bg=colour254,bold] #h '

set-option -g window-status-format "#[fg=white,bg=colour234] #I #W "
set-option -g window-status-current-format "#[fg=colour234,bg=colour39]#[fg=colour16,bg=colour39,noreverse,bold] #I #W #[fg=colour39,bg=colour234,nobold]"

set-option -g default-terminal "screen-256color"

.tmux.shared.conf

Finally, all the tmux configuration that gets shared between all tmux instances, regardless or where or how they're invoked.

TODO: document all of this.

# Emacs mode keys
setw -g mode-keys emacs

# reload
bind r source-file ~/.tmux.conf \; display-message "Config reloaded..."
bind R source-file ~/.tmux.conf \; display-message "Config reloaded..."

# make it easy to grab a pane and put it into the current window
bind-key @ command-prompt -p "create pane from:"  "join-pane -s ':%%'"

# and to break the current pane into a new window thing
bind-key B break-pane

# easily toggle synchronization (mnemonic: e is for echo)
bind e setw synchronize-panes on
bind E setw synchronize-panes off

# " windowlist -b
unbind-key '"'
bind-key '"' choose-window

# don't wait after escape
set -s escape-time 0

# UTF-8 everywhere
set-option -g status-utf8 on

# monitor activity
setw -g monitor-activity on
set -g visual-activity off
bind m setw monitor-activity off
bind M setw monitor-activity on

############

# screen ^C c
unbind-key ^C
bind-key ^C new-window
unbind-key C-M-c
bind-key C-M-c new-window
unbind-key c
bind-key c new-window

# detach ^D d
unbind-key ^D
bind-key ^D detach

# displays *
unbind-key *
bind-key * list-clients

# next ^@ ^N sp n
unbind-key ^@
bind-key ^@ next-window
unbind-key ^N
bind-key ^N next-window
unbind-key " "
bind-key " " next-window
unbind-key n
bind-key n next-window

# title A
unbind-key A
bind-key A command-prompt "rename-window %%"

# prev ^H ^P p ^?
unbind-key ^H
bind-key ^H previous-window
unbind-key ^P
bind-key ^P previous-window
unbind-key p
bind-key p previous-window
# unbind-key BSpace
# bind-key BSpace previous-window

# windows ^W w
unbind-key ^W
bind-key ^W list-windows
unbind-key w
bind-key w list-windows

# redisplay ^L l
unbind-key ^L
bind-key ^L refresh-client
unbind-key l
bind-key l refresh-client

# " windowlist -b
unbind-key '"'
bind-key '"' choose-window

# Copy mode
bind-key ^[ copy-mode
bind-key Escape copy-mode

# Paste mode
bind-key ] choose-buffer
bind-key ^] choose-buffer
# bind-key ] paste-buffer
# bind-key ^] paste-buffer
set-window-option -g mode-keys emacs
# Make mouse useful in copy mode
#set-window-option -g mode-mouse on

# drew paste
bind-key P run-shell 'tmux saveb -| curl -s -XPOST -H "Content-type: text/plain" --data-binary @- http://p.draines.com/'
# x clipboard
bind-key > run-shell "tmux saveb -| xclip -selection clipboard -i"

# More straight forward key bindings for splitting
#unbind-key %
bind-key | split-window -h
bind-key h split-window -h
#unbind-key '"'
bind-key - split-window -v
bind-key v split-window -v

# History
set-option -g history-limit 15000

# Notifying if other windows has activities
set-window-option -g monitor-activity off
set-option -g visual-activity off

# Highlighting the active window in status bar
#set-window-option -g window-status-current-bg cyan
set-window-option -g window-status-current-fg cyan

# Clock
set-window-option -g clock-mode-colour green
set-window-option -g clock-mode-style 24

# don't clobber ssh agent
set-option -g update-environment "DISPLAY WINDOWID GPG_AGENT_INFO"

# term
set-option -g default-terminal "screen-256color"

## Custom status bar, via https://github.com/myusuf3/dotfiles
## Powerline symbols: ⮂ ⮃ ⮀ ⮁ ⭤
## If you do not have a patched font (see: https://github.com/Lokaltog/vim-powerline/tree/develop/fontpatcher)
## comment out the lines below to get a "regular" statusbar without special symbols
set-option -g status-bg colour234
set-option -g message-fg colour16
set-option -g message-bg colour221
set-option -g status-left-length 40
set-option -g status-right-length 40
set-option -g status-interval 5
set-option -g pane-border-fg colour245
set-option -g pane-active-border-fg colour39
set-option -g status-justify left

set-option -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour238,nobold]#[fg=colour15,bg=colour238,bold] #(up) #[fg=colour238,bg=colour234,nobold]'

set-option -g status-right '#[fg=colour245]%R %d %b #[fg=colour254,bg=colour234,nobold]#[fg=colour16,bg=colour254,bold] #h '

set-option -g window-status-format "#[fg=white,bg=colour234] #I #W "
set-option -g window-status-current-format "#[fg=colour234,bg=colour39]#[fg=colour16,bg=colour39,noreverse,bold] #I #W #[fg=colour39,bg=colour234,nobold]"

set-option -g default-terminal "screen-256color"

Emacs Configuration

Introduction

This is my Emacs setup. There are many like it, but this one is mine.

A note about keyboard shortcuts

There are a lot (like, a lot) of keyboard shortcuts in this configuration, and they aren't all collected in one place. So I thought I should collect the most common of them into a single place so anyone looking at this can get an idea of what I use the most. I borrowed the idea from a different configuration I saw, when I thought it was useful for seeing how people bound things.

This doesn't include stuff that I haven't really rebound, like C-x C-f, which just calls find-file, though there may be a few sprinkled in there. This list is in no way exhaustive.

Shortcut What it does Usage frequency
C-c a SPC open my custom agenda view Every time I start emacs
C-x m open eshell Every time I start emacs
M-1 open first eshell Every time I start emacs
M-2 open second eshell Every time I start emacs
M-3 open third eshell Every time I start emacs
M-4 open fourth eshell Every time I start emacs
C-x C-r helm-mini Very
C-x b helm-mini Very
C-x f helm-projectile Very
C-p s s helm-ag-project (search entire project for some text) Very
C-c b switch between org-mode buffers Very
M-g M-g open Magit Very
C-x C-i open helm's semantic imenu Very
C-c n cleanup buffer (reindent, clean whitespace) Often
C-x d open my remote (TRAMP) eshell buffer Often
C-x C-j open dired folder for current file Often
C-M-n scroll half a page down Often
C-M-p scroll half a page up Often
M-i open helm-swoop Often
<F11> prompt for recent headlines to clock into Often
<F12> clock out of current task Often
M-g . helm-grep, with C-u prefix, rgrep Sometimes
C-c p p switch projectile projecs Sometimes
C-h b see the current bindings (helm-descbinds) Sometimes
C-h a look through all emacs vars, functions, faces (helm-apropos) Sometimes
C-h t see the current time in different locations (helm-world-time) Sometimes
C-c c open capture template for capturing org agenda things Sometimes
C-x M-b open a list of my file/buffer bookmarks (helm-bookmarks) Sometimes
C-c d look up something in the Elasticsearch documentation Sometimes
C-c u search for the last URL and browse to it with system browser Sometimes
C-x 4 t transpose the current buffers Rarely
C-h e pop up the *Messages* buffer Rarely
C-x RET open shell (zsh) Rarely

Pre-cursors

This is stuff that absolutely must be set up, because I want it available if everything else fails entirely.

Turn on debugging, it will be turned off at the end. In case something happens during loading that breaks something, it's nice to have a debug information.

(setq debug-on-error t)
(setq debug-on-quit t)
;; Dvorak nicety, regardless of loading settings
(define-key key-translation-map "\C-t" "\C-x")

;; Keep track of loading time
(defconst emacs-start-time (current-time))

;; Load a development version of CEDET instead of the built-in Emacs one
(when (file-exists-p "~/src/elisp/cedet/cedet-devel-load.el")
  (load-file "~/src/elisp/cedet/cedet-devel-load.el")
  ;; Use the full Java 1.5 grammar to parse Java files
  (autoload 'wisent-java-default-setup "semantic/wisent/java"
    "Hook run to setup Semantic in `java-mode'." nil nil))

;; Load the development version of Org instead of the built-in one
(when (file-exists-p "~/src/elisp/org-mode/lisp/org.el")
  (add-to-list 'load-path "~/src/elisp/org-mode/lisp")
  (add-to-list 'load-path "~/src/elisp/org-mode/contrib/lisp")
  (require 'org))

;; initalize all ELPA packages
(require 'package)
(package-initialize)

(setq package-archives '(("melpa" . "http://melpa.org/packages/")
                         ("melpa-stable" . "http://stable.melpa.org/packages/")
                         ("gnu" . "http://elpa.gnu.org/packages/")))

(when (boundp 'package-pinned-packages)
  (setq package-pinned-packages
        '((cider                             . "melpa-stable")
          (ac-cider                          . "melpa-stable")
          (clojure-mode                      . "melpa-stable")
          (clojure-mode-extra-font-locking   . "melpa-stable")
          (company-cider                     . "melpa-stable")
          (malabar-mode                      . "melpa-stable"))))

(let ((elapsed (float-time (time-subtract (current-time)
                                          emacs-start-time))))
  (message "Loaded packages in %.3fs" elapsed))

;; keep customize settings in their own file
(setq custom-file "~/.emacs.d/custom.el")
(when (file-exists-p custom-file)
  (load custom-file))

(require 'cl-lib)

Packages that need to be installed

This is code that installs packages before any config is loaded, since a lot of things add hooks that don't work if packages aren't installed.

(defvar my/install-packages
  '(
    ;; package management
    use-package

    ;; themeing
    rainbow-mode leuven-theme dakrone-theme color-identifiers-mode
    nyan-mode color-theme-sanityinc-tomorrow apropospriate-theme
    material-theme smart-mode-line beacon aurora-theme moe-theme
    spaceline solarized-theme

    ;; misc
    diminish gist async sx exec-path-from-shell bbdb symon scpaste

    ;; es-mode is run from a git checkout

    ;; IRC/ERC and social stuff
    alert twittering-mode rcirc-color rcirc-alertify
    ercn erc-hl-nicks

    ;; for auto-complete
    fuzzy popup company auto-complete

    ;; editing utilities
    expand-region smex windresize ag undo-tree iedit ido-ubiquitous
    ido-vertical-mode yasnippet smart-tab anzu smartparens flx-ido projectile
    smooth-scrolling multiple-cursors ggtags bookmark+ golden-ratio wc-mode
    eyebrowse vlf hydra shrink-whitespace quick-preview pdf-tools smartscan
    indent-guide fill-column-indicator

    ;; external process things
    prodigy

    ;; logs
    log4j-mode logstash-conf

    ;; infrastructure stuff
    restclient

    ;; highlighting
    idle-highlight-mode

    ;; LaTeX
    auctex

    ;; org-mode
    org htmlize gnuplot-mode gnuplot org-alert org-present org-bullets

    ;; buffer utils
    popwin dired+

    ;; haskell
    haskell-mode ghc ghci-completion

    ;; config
    ssh-config-mode

    ;; flycheck
    flycheck flycheck-tip flycheck-haskell flycheck-pos-tip

    ;; clojure
    clojure-mode clojure-mode-extra-font-locking cider paredit paren-face
    ac-cider

    ;; python
    hy-mode virtualenvwrapper

    ;; ruby
    ruby-mode ruby-test-mode inf-ruby puppet-mode rbenv

    ;; rust
    rust-mode

    ;; go
    go-mode

    ;; java
    malabar-mode groovy-mode javap-mode emacs-eclim java-imports

    ;; javascript
    tern json-mode js2-mode

    ;; emacs-lisp
    elisp-slime-nav paredit

    ;; markup language
    markdown-mode markdown-mode+ yaml-mode zencoding-mode adoc-mode

    ;; helm
    helm helm-descbinds helm-ag helm-projectile helm-swoop
    helm-gtags helm-ls-git helm-flycheck helm-flyspell helm-flx
    helm-c-yasnippet

    ;; git
    magit git-gutter git-timemachine magit-gh-pulls with-editor

    ;; eshell
    eshell-prompt-extras

    ;; eww
    eww-lnum
    ))

(defvar packages-refreshed? nil)

(dolist (pack my/install-packages)
  (unless (package-installed-p pack)
    (unless packages-refreshed?
      (package-refresh-contents)
      (setq packages-refreshed? t))
    (unwind-protect
        (condition-case ex
            (package-install pack)
          ('error (message "Failed to install package [%s], caught exception: [%s]"
                           pack ex)))
      (message "Installed %s" pack))))

;; Load use-package, used for loading packages everywhere else
(require 'use-package)
;; Set to t to debug package loading or nil to disable
(setq use-package-verbose t)

Setting up $PATH and other vars

This allows a GUI emacs to inherit $PATH and other things from the shell when run. I use it for the path on OSX and JAVA_HOME everywhere else.

(use-package exec-path-from-shell
  :defer t
  :init
  (progn
    (setq exec-path-from-shell-variables '("JAVA_HOME"
                                           "PATH"
                                           "WORKON_HOME"
                                           "MANPATH"))
    (exec-path-from-shell-initialize)))

Basics and settings used everywhere

Mostly settings that don't fit in elsewhere, so they end up here. However, this does include settings that aren't part of packages and need to configure Emacs' built-in packages.

General settings

First, let's determine whether I'm going to be using a dark theme, or a light theme. I set a var to either 'light or 'dark depending on whatever I'm in the mood for. This is used later on for the modeline theme, as well as the general theme for things.

;;(defvar my/background 'light)
(defvar my/background 'dark)

Now some personal information about me:

(setq user-full-name "Lee Hinman"
      user-mail-address "leehinman@fastmail.com")

Always, always, prefer UTF-8, anything else is insanity

(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(setq default-buffer-file-coding-system 'utf-8)

Turn on syntax highlighting for all buffers:

(global-font-lock-mode t)

Raise the maximum number of logs in the *Messages* buffer:

(setq message-log-max 16384)

We don't really need to garbage collect as frequently as Emacs would like to by default, so set the threshold up higher:

(setq gc-cons-threshold (* 100 1024 1024)) ;; 100 mb
;; Allow font-lock-mode to do background parsing
(setq jit-lock-stealth-time 1
      ;; jit-lock-stealth-load 200
      jit-lock-chunk-size 1000
      jit-lock-defer-time 0.05)

line-number-mode displays the current line number in the mode line, however it stops doing that in buffers when encountering at least one overly long line and displays two question marks instead. This is pretty unhelpful, the only workaround I've been able to find was to increase line-number-display-width to a substantially higher value.

(setq line-number-display-limit-width 10000)

Make gnutls a bit safer

(setq gnutls-min-prime-bits 4096)

Echo commands I haven't finished quicker than the default of 1 second:

(setq echo-keystrokes 0.4)

When I select a region and start typing, just delete the region automatically

(delete-selection-mode 1)

Don't warn me about large files unless they're at least 25mb:

(setq large-file-warning-threshold (* 25 1024 1024))

If you change buffer, or focus, disable the current buffer's mark:

(transient-mark-mode 1)

Don't indicate empty lines or the end of a buffer with visual marks (the lines are cleaned up automatically anyway)

(setq-default indicate-empty-lines nil)
(setq-default indicate-buffer-boundaries nil)

Turn off all kinds of modes, I don't need the menu bar, or the tool bar:

(when (functionp 'menu-bar-mode)
  (menu-bar-mode -1))
(when (functionp 'set-scroll-bar-mode)
  (set-scroll-bar-mode 'nil))
(when (functionp 'mouse-wheel-mode)
  (mouse-wheel-mode -1))
(when (functionp 'tooltip-mode)
  (tooltip-mode -1))
(when (functionp 'tool-bar-mode)
  (tool-bar-mode -1))
(when (functionp 'blink-cursor-mode)
  (blink-cursor-mode -1))

Don't beep. Just don't. Also, don't show the startup message, I know Emacs is starting.

(setq ring-bell-function (lambda ()))
(setq inhibit-startup-screen t
      initial-major-mode 'fundamental-mode)

Why would you not want to know lines/columns in your mode-line?

(line-number-mode 1)
(column-number-mode 1)

Ignore case when using completion for file names:

(setq read-file-name-completion-ignore-case t)

Nobody likes to have to type "yes" to questions, so change it to just hitting the y key to confirm:

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

Confirm before killing emacs, but only on graphical sessions

(when (window-system)
  (setq confirm-kill-emacs 'yes-or-no-p))

It's much easier to move around lines based on how they are displayed, rather than the actual line. This helps a ton with long log file lines that may be wrapped:

(setq line-move-visual t)

Hide the mouse while typing:

(setq make-pointer-invisible t)

Set up the fill-column to 80 characters and set tab width to 2

(setq-default fill-column 80)
(setq-default default-tab-width 2)
(setq-default indent-tabs-mode nil)

Fix some weird color escape sequences

(setq system-uses-terminfo nil)

Resolve symlinks:

(setq-default find-file-visit-truename t)

Require a newline at the end of files:

(setq require-final-newline t)

Uniquify buffers, using angle brackets, so you get foo and foo<2>:

(use-package uniquify
  :config
  (setq uniquify-buffer-name-style 'post-forward-angle-brackets))

Search (and search/replace) using regex by default, since that's usually what I want to do:

(global-set-key (kbd "C-s") 'isearch-forward-regexp)
(global-set-key (kbd "C-r") 'isearch-backward-regexp)
(global-set-key (kbd "M-%") 'query-replace-regexp)

Single space still ends a sentence:

(setq sentence-end-double-space nil)

Split windows a bit better (don't split horizontally, I have a widescreen :P)

(setq split-height-threshold nil)
(setq split-width-threshold 180)

Make sure auto automatically rescan for imenu changes:

(set-default 'imenu-auto-rescan t)

Seed the random number generator:

(random t)

Switch to unified diffs by default:

(setq diff-switches "-u")

Turn on auto-fill mode in text buffers:

(add-hook 'text-mode-hook 'turn-on-auto-fill)

(use-package diminish
  :init
  (progn
    (diminish 'auto-fill-function "")))

Set the internal calculator not to go to scientific form quite so quickly:

(setq calc-display-sci-low -5)

Bury the *scratch* buffer, never kill it:

(defadvice kill-buffer (around kill-buffer-around-advice activate)
  (let ((buffer-to-kill (ad-get-arg 0)))
    (if (equal buffer-to-kill "*scratch*")
        (bury-buffer)
      ad-do-it)))

These are some settings for version control stuff.

Automatically revert file if it's changed on disk:

(global-auto-revert-mode 1)
;; be quiet about reverting files
(setq auto-revert-verbose nil)

Start a server if not running, but a only for gui-only:

;; Lame, server has bad autoloads :(
(require 'server nil t)
(use-package server
  :if window-system
  :init
  (when (not (server-running-p server-name))
    (server-start)))

GUI-specific thing:

(when (window-system)
  (setenv "EMACS_GUI" "t"))

Prettify all the symbols, if available (an Emacs 24.4 feature):

(when (boundp 'global-prettify-symbols-mode)
  (add-hook 'emacs-lisp-mode-hook
            (lambda ()
              (push '("lambda" . ?λ) prettify-symbols-alist)))
  (add-hook 'clojure-mode-hook
            (lambda ()
              (push '("fn" . ?ƒ) prettify-symbols-alist)))
  (global-prettify-symbols-mode +1))

Display the time and load on the modeline

(setq
 ;; don't display info about mail
 display-time-mail-function (lambda () nil)
 ;; update every 15 seconds instead of 60 seconds
 display-time-interval 15)
(display-time-mode 1)

Quit as fast as possible with kill -USR1 <pid>

(defun my/quit-emacs-unconditionally ()
  (interactive)
  (my-quit-emacs '(4)))

(define-key special-event-map (kbd "<sigusr1>") #'my/quit-emacs-unconditionally)

Emacs (foolishly) defaults to adding the --insecure flag. It also supports the (incredibly broken) SSL version 3. What are you thinking Emacs!?!

Here I set it back to a sane value:

(setq tls-program
      ;; Defaults:
      ;; '("gnutls-cli --insecure -p %p %h"
      ;;   "gnutls-cli --insecure -p %p %h --protocols ssl3"
      ;;   "openssl s_client -connect %h:%p -no_ssl2 -ign_eof")
      '("gnutls-cli -p %p %h"
        "openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof"))

Before we load any helm things, need to load helm-flx so it uses flx instead of helm's fuzzy matching.

(use-package helm-flx
  :init (helm-flx-mode +1))
OS-specific settings

These are settings that are applied depending on what OS I'm currently running on. On gnu/linux systems, I bind C-M-w to the yank-to-x-clipboard method, which uses xsel to yank text. On OSX, I use the pbpaste and pbcopy methods to interact with the system clipboard.

For OSX, use brew install coreutils to get gls which has better support for dired buffers.

(when (eq system-type 'gnu/linux)

  ;; Don't use GTK tooltips, use emacs ones
  (setq x-gtk-use-system-tooltips nil)

  (defun my/max-fullscreen ()
    (interactive)
    (toggle-frame-maximized))

  ;; fullscreen
  (add-hook 'after-init-hook #'my/max-fullscreen)

  (setq dired-listing-switches "-lFaGh1v --group-directories-first")
  (defun yank-to-x-clipboard ()
    (interactive)
    (if (region-active-p)
        (progn
          (shell-command-on-region (region-beginning) (region-end) "xsel -i -b")
          (message "Yanked region to clipboard!")
          (deactivate-mark))
      (message "No region active; can't yank to clipboard!")))

  (global-set-key (kbd "C-M-w") 'yank-to-x-clipboard)
  ;; suspend-frame isn't working on Linux?
  (global-unset-key (kbd "C-z"))
  (global-unset-key (kbd "C-x C-z")))

(when (eq system-type 'darwin)
  (setq ns-use-native-fullscreen nil)
  ;; brew install coreutils
  (if (executable-find "gls")
      (progn
        (setq insert-directory-program "gls")
        (setq dired-listing-switches "-lFaGh1v --group-directories-first"))
    (setq dired-listing-switches "-ahlF"))
  (defun copy-from-osx ()
    "Handle copy/paste intelligently on osx."
    (let ((pbpaste (purecopy "/usr/bin/pbpaste")))
      (if (and (eq system-type 'darwin)
               (file-exists-p pbpaste))
          (let ((tramp-mode nil)
                (default-directory "~"))
            (shell-command-to-string pbpaste)))))

  (defun paste-to-osx (text &optional push)
    (let ((process-connection-type nil))
      (let ((proc (start-process "pbcopy" "*Messages*" "/usr/bin/pbcopy")))
        (process-send-string proc text)
        (process-send-eof proc))))
  (setq interprogram-cut-function 'paste-to-osx
        interprogram-paste-function 'copy-from-osx)

  (defun move-file-to-trash (file)
    "Use `trash' to move FILE to the system trash.
When using Homebrew, install it using \"brew install trash\"."
    (call-process (executable-find "trash")
                  nil 0 nil
                  file))

  ;; Trackpad scrolling
  (global-set-key [wheel-up] 'previous-line)
  (global-set-key [wheel-down] 'next-line))

Sometimes I use the OSX emacs-mac port: https://github.com/railwaycat/emacs-mac-port , which has a whole other set of issues, so this is special handling of it…

(when (eq window-system 'mac)

  (defun my/max-fullscreen ()
    (interactive)
    (set-frame-parameter nil 'fullscreen 'fullboth))

  ;; fullscreen
  (add-hook 'after-init-hook #'my/max-fullscreen)
  ;; use alt as hyper
  (setq mac-option-modifier 'meta)
  ;; use command as meta
  (setq mac-command-modifier 'hyper))
  • Windows

    Hahahahaha, you must be joking.

Clipboard settings

Change the clipboard settings to better integrate into Linux:

(setq x-select-enable-clipboard t)
;; Treat clipboard input as UTF-8 string first; compound text next, etc.
(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))

Save whatever's in the current (system) clipboard before replacing it with the Emacs' text.

(setq save-interprogram-paste-before-kill t)
Temporary file settings

Settings for what to do with temporary files.

;; savehist
(setq savehist-additional-variables
      ;; also save my search entries
      '(search-ring regexp-search-ring)
      savehist-file "~/.emacs.d/savehist")
(savehist-mode t)
(setq-default save-place t)

;; delete-auto-save-files
(setq delete-auto-save-files t)
(setq backup-directory-alist
      '(("." . "~/.emacs_backups")))

;; delete old backups silently
(setq delete-old-versions t)
Shell settings

Things for running shells inside of emacs

First, Emacs doesn't handle less well, so use cat instead for the shell pager:

(setenv "PAGER" "cat")
(custom-set-variables
 '(comint-scroll-to-bottom-on-input t)  ; always insert at the bottom
 '(comint-scroll-to-bottom-on-output nil) ; always add output at the bottom
 '(comint-scroll-show-maximum-output t) ; scroll to show max possible output
 ;; '(comint-completion-autolist t)     ; show completion list when ambiguous
 '(comint-input-ignoredups t)           ; no duplicates in command history
 '(comint-completion-addsuffix t)       ; insert space/slash after file completion
 '(comint-prompt-read-only nil)         ; if this is t, it breaks shell-command
 '(comint-get-old-input (lambda () "")) ; what to run when i press enter on a
                                        ; line above the current prompt
 )

(defun my/shell-kill-buffer-sentinel (process event)
  (when (memq (process-status process) '(exit signal))
    (kill-buffer)))

(defun my/kill-process-buffer-on-exit ()
  (set-process-sentinel (get-buffer-process (current-buffer))
                        #'my/shell-kill-buffer-sentinel))

(dolist (hook '(ielm-mode-hook term-exec-hook comint-exec-hook))
  (add-hook hook 'my/kill-process-buffer-on-exit))

(defun set-scroll-conservatively ()
  "Add to shell-mode-hook to prevent jump-scrolling on newlines in shell buffers."
  (set (make-local-variable 'scroll-conservatively) 10))

(defadvice comint-previous-matching-input
    (around suppress-history-item-messages activate)
  "Suppress the annoying 'History item : NNN' messages from shell history isearch.
If this isn't enough, try the same thing with
comint-replace-by-expanded-history-before-point."
  (let ((old-message (symbol-function 'message)))
    (unwind-protect
        (progn (fset 'message 'ignore) ad-do-it)
      (fset 'message old-message))))

(add-hook 'shell-mode-hook 'set-scroll-conservatively)
;; truncate buffers continuously
(add-hook 'comint-output-filter-functions 'comint-truncate-buffer)
;; interpret and use ansi color codes in shell output windows
(add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on)
Eshell settings

Eshell is great for one-off shell things, but I use ZSH too much for it to be a full replacement. Regardless, it needs some tweaks in order to be fully useful.

First, a function to be called when eshell-mode is entered

(defun my/setup-eshell ()
  (interactive)
  ;; turn off semantic-mode in eshell buffers
  (semantic-mode -1)
  ;; turn off hl-line-mode
  (hl-line-mode -1)
  (setq-local show-trailing-whitespace nil)
  (define-key eshell-mode-map (kbd "M-l")
    'helm-eshell-history)
  (when (fboundp smartscan-mode)
    (smartscan-mode -1)))

Also, after eshell has loaded its options, let's load some other niceties like completion, prompt and term settings:

(use-package eshell
  :commands (eshell eshell-command)
  :config
  (defalias 'emacs 'find-file)
  (defalias 'sec 'sudoec)
  (setenv "PAGER" "cat")
  (use-package esh-opt
    :config
    (use-package em-cmpl)
    (use-package em-prompt)
    (use-package em-term)

    (setq eshell-cmpl-cycle-completions nil
          ;; auto truncate after 12k lines
          eshell-buffer-maximum-lines 12000
          ;; history size
          eshell-history-size 500
          ;; buffer shorthand -> echo foo > #'buffer
          eshell-buffer-shorthand t
          ;; my prompt is easy enough to see
          eshell-highlight-prompt nil
          ;; treat 'echo' like shell echo
          eshell-plain-echo-behavior t)

    ;; Visual commands
    (setq eshell-visual-commands '("vi" "screen" "top" "less" "more" "lynx"
                                   "ncftp" "pine" "tin" "trn" "elm" "vim"
                                   "nmtui" "alsamixer" "htop" "el" "elinks"
                                   ))
    (setq eshell-visual-subcommands '(("git" "log" "diff" "show")))

    (defun my/truncate-eshell-buffers ()
      "Truncates all eshell buffers"
      (interactive)
      (save-current-buffer
        (dolist (buffer (buffer-list t))
          (set-buffer buffer)
          (when (eq major-mode 'eshell-mode)
            (eshell-truncate-buffer)))))

    ;; After being idle for 5 seconds, truncate all the eshell-buffers if
    ;; needed. If this needs to be canceled, you can run `(cancel-timer
    ;; my/eshell-truncate-timer)'
    (setq my/eshell-truncate-timer
          (run-with-idle-timer 5 t #'my/truncate-eshell-buffers))

    (when (not (functionp 'eshell/rgrep))
      (defun eshell/rgrep (&rest args)
        "Use Emacs grep facility instead of calling external grep."
        (eshell-grep "rgrep" args t)))

    (defun eshell/cds ()
      "Change directory to the project's root."
      (eshell/cd (locate-dominating-file default-directory ".git")))

    (defun eshell/l (&rest args) "Same as `ls -lh'"
           (apply #'eshell/ls "-lh" args))
    (defun eshell/ll (&rest args) "Same as `ls -lh'"
           (apply #'eshell/ls "-lh" args))
    (defun eshell/la (&rest args) "Same as `ls -alh'"
           (apply #'eshell/ls "-alh" args))

    (defun eshell/ec (pattern)
      (if (stringp pattern)
          (find-file pattern)
        (mapc #'find-file (mapcar #'expand-file-name pattern))))

    (defun eshell/clear ()
      "Clear the eshell buffer"
      (interactive)
      (let ((eshell-buffer-maximum-lines 0))
        (eshell-truncate-buffer))))

  (add-hook 'eshell-mode-hook #'my/setup-eshell)

  ;; See eshell-prompt-function below
  (setq eshell-prompt-regexp "^[^#$\n]* [#$] ")

  ;; So the history vars are defined
  (require 'em-hist)
  (if (boundp 'eshell-save-history-on-exit)
      ;; Don't ask, just save
      (setq eshell-save-history-on-exit t))

  ;; See: https://github.com/kaihaosw/eshell-prompt-extras
  (use-package eshell-prompt-extras
    :init
    (progn
      (setq eshell-highlight-prompt nil
            epe-git-dirty-char " Ϟ"
            ;; epe-git-dirty-char "*"
            eshell-prompt-function 'epe-theme-dakrone)))

  (defun eshell/magit ()
    "Function to open magit-status for the current directory"
    (interactive)
    (magit-status default-directory)
    nil))

I use a dedicated buffer for connection to my desktop, with a binding of C-x d, if the buffer doesn't exist it is created.

(defun my/create-or-switch-to-delta-buffer ()
  "Switch to the *eshell delta* buffer, or create it"
  (interactive)
  (if (get-buffer "*eshell-delta*")
      (switch-to-buffer "*eshell-delta*")
    (let ((eshell-buffer-name "*eshell-delta*"))
      (eshell))))

(global-set-key (kbd "C-x d") 'my/create-or-switch-to-delta-buffer)

(defun my/create-or-switch-to-eshell-1 ()
  "Switch to the *eshell* buffer, or create it"
  (interactive)
  (if (get-buffer "*eshell*")
      (switch-to-buffer "*eshell*")
    (let ((eshell-buffer-name "*eshell*"))
      (eshell))))

(defun my/create-or-switch-to-eshell-2 ()
  "Switch to the *eshell*<2> buffer, or create it"
  (interactive)
  (if (get-buffer "*eshell*<2>")
      (switch-to-buffer "*eshell*<2>")
    (let ((eshell-buffer-name "*eshell*<2>"))
      (eshell))))

(defun my/create-or-switch-to-eshell-3 ()
  "Switch to the *eshell*<3> buffer, or create it"
  (interactive)
  (if (get-buffer "*eshell*<3>")
      (switch-to-buffer "*eshell*<3>")
    (let ((eshell-buffer-name "*eshell*<3>"))
      (eshell))))

(defun my/create-or-switch-to-eshell-4 ()
  "Switch to the *eshell*<4> buffer, or create it"
  (interactive)
  (if (get-buffer "*eshell*<4>")
      (switch-to-buffer "*eshell*<4>")
    (let ((eshell-buffer-name "*eshell*<4>"))
      (eshell))))

(global-set-key (kbd "H-1") 'my/create-or-switch-to-eshell-1)
(global-set-key (kbd "H-2") 'my/create-or-switch-to-eshell-2)
(global-set-key (kbd "H-3") 'my/create-or-switch-to-eshell-3)
(global-set-key (kbd "H-4") 'my/create-or-switch-to-eshell-4)
(global-set-key (kbd "s-1") 'my/create-or-switch-to-eshell-1)
(global-set-key (kbd "s-2") 'my/create-or-switch-to-eshell-2)
(global-set-key (kbd "s-3") 'my/create-or-switch-to-eshell-3)
(global-set-key (kbd "s-4") 'my/create-or-switch-to-eshell-4)
(global-set-key (kbd "M-1") 'my/create-or-switch-to-eshell-1)
(global-set-key (kbd "M-2") 'my/create-or-switch-to-eshell-2)
(global-set-key (kbd "M-3") 'my/create-or-switch-to-eshell-3)
(global-set-key (kbd "M-4") 'my/create-or-switch-to-eshell-4)
  • eshell aliases

    Like zsh, I use a lot of aliases in eshell, so I need to set those up here:

    alias aria2c aria2c -c -x5 -s10 -m0 $*
    alias bdt gdate "+%Y%m%dT%H%M%S.%3N%z"
    alias delete curl -s -XDELETE $*
    alias dt gdate "+%Y-%m-%dT%H:%M:%S.%3N%zZ"
    alias epoch date +%s
    alias ga git annex $*
    alias get curl -s -XGET $*
    alias ivalice2org rsync -azP --delete ivalice-local:~/org/ ~/org
    alias org2ivalice rsync -azP --delete ~/org/ ivalice-local:~/org
    alias org2xanadu rsync -azP --delete ~/org/ xanadu:~/org
    alias post curl -s -XPOST $*
    alias put curl -s -XPUT $*
    alias se tar zxvf $*
    alias xanadu2org rsync -azP --delete xanadu:~/org/ ~/org
    
Tramp settings

I have really been getting into TRAMP lately, I use it with eshell all the time, and dired tramp buffers are great for file management.

(use-package tramp
  :defer 5
  :config
  (setq )
  ;; Turn of auto-save for tramp files
  (defun tramp-set-auto-save ()
    (auto-save-mode -1))
  (with-eval-after-load 'tramp-cache
    (setq tramp-persistency-file-name "~/.emacs.d/etc/tramp"))
  (setq tramp-default-method "ssh"
        tramp-default-user-alist '(("\\`su\\(do\\)?\\'" nil "root"))
        tramp-adb-program "adb"
        ;; use the settings in ~/.ssh/config instead of Tramp's
        tramp-use-ssh-controlmaster-options nil
        backup-enable-predicate
        (lambda (name)
          (and (normal-backup-enable-predicate name)
               (not (let ((method (file-remote-p name 'method)))
                      (when (stringp method)
                        (member method '("su" "sudo"))))))))

  (use-package tramp-sh
    :config
    (add-to-list 'tramp-remote-path "/usr/local/sbin")
    (add-to-list 'tramp-remote-path "/opt/java/current/bin")
    (add-to-list 'tramp-remote-path "~/bin")))
Spell check and flyspell settings

I use Hunspell and Aspell checking spelling, ignoring words under 3 characters and running very quickly. My personal word dictionary is at ~/.flydict.

First, set up some Hunspell things if applicable, falling back to Aspell if Hunspell isn't available:

;; Standard location of personal dictionary
(setq ispell-personal-dictionary "~/.flydict")

;; Mostly taken from
;; http://blog.binchen.org/posts/what-s-the-best-spell-check-set-up-in-emacs.html
(when (executable-find "aspell")
  (setq ispell-program-name (executable-find "aspell"))
  (setq ispell-extra-args
        (list "--sug-mode=fast" ;; ultra|fast|normal|bad-spellers
              "--lang=en_US"
              "--ignore=4")))

;; hunspell
(when (executable-find "hunspell")
  (setq ispell-program-name (executable-find "hunspell"))
  (setq ispell-extra-args '("-d en_US")))

(add-to-list 'ispell-skip-region-alist '("[^\000-\377]+"))
(add-to-list 'ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:"))
(add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_SRC" . "#\\+END_SRC"))
(add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_EXAMPLE" . "#\\+END_EXAMPLE"))

In most non-programming modes, M-. can be used to spellcheck the word (otherwise it would jump to the definition)

(defun my/enable-flyspell-prog-mode ()
  (interactive)
  (flyspell-prog-mode))

(use-package flyspell
  :defer t
  :diminish ""
  :init (add-hook 'prog-mode-hook #'my/enable-flyspell-prog-mode)
  :config
  (use-package helm-flyspell
    :init
    (define-key flyspell-mode-map (kbd "M-S") #'helm-flyspell-correct)))
View-mode and doc-view

Read-only viewing of files. Keybindings for paging through stuff in a less/vim manner.

Make sure you install mupdf for the best quality PDFs on Linux and OSX. (brew install mupdf on osx)

(use-package view
  :defer 15
  :bind
  (("C-M-n" . View-scroll-half-page-forward)
   ("C-M-p" . View-scroll-half-page-backward))
  :config
  (progn
    (defun View-goto-line-last (&optional line)
      "goto last line"
      (interactive "P")
      (goto-line (line-number-at-pos (point-max))))

    ;; less like
    (define-key view-mode-map (kbd "N") 'View-search-last-regexp-backward)
    (define-key view-mode-map (kbd "?") 'View-search-regexp-backward?)
    (define-key view-mode-map (kbd "g") 'View-goto-line)
    (define-key view-mode-map (kbd "G") 'View-goto-line-last)
    ;; vi/w3m like
    (define-key view-mode-map (kbd "h") 'backward-char)
    (define-key view-mode-map (kbd "j") 'next-line)
    (define-key view-mode-map (kbd "k") 'previous-line)
    (define-key view-mode-map (kbd "l") 'forward-char)))

(use-package doc-view
  :config
  (define-key doc-view-mode-map (kbd "j")
    #'doc-view-next-line-or-next-page)
  (define-key doc-view-mode-map (kbd "k")
    #'doc-view-previous-line-or-previous-page)
  ;; use 'q' to kill the buffer, not just hide it
  (define-key doc-view-mode-map (kbd "q")
    #'kill-this-buffer))

I also use the 'pdf-tools' pacakge

(use-package pdf-tools)
Dired

Dired is sweet, I require dired-x also so I can hit C-x C-j and go directly to a dired buffer.

Setting ls-lisp-dirs-first means directories are always at the top. Always copy and delete recursively. Also enable hl-line-mode in dired, since it's easier to see the cursor then.

And then some other things to setup when dired runs. C-x C-q to edit writable-dired mode is aawwweeeesssoooommee, it makes renames super easy.

(defun my/dired-mode-hook ()
  (toggle-truncate-lines 1))

(use-package dired
  :bind ("C-x C-j" . dired-jump)
  :config
  (use-package dired-x
    :init (setq-default dired-omit-files-p t)
    :config
    (add-to-list 'dired-omit-extensions ".DS_Store"))
  (customize-set-variable 'diredp-hide-details-initially-flag nil)
  (use-package dired+)
  (use-package dired-aux
    :init (use-package dired-async))
  (put 'dired-find-alternate-file 'disabled nil)
  (setq ls-lisp-dirs-first t
        dired-recursive-copies 'always
        dired-recursive-deletes 'always
        dired-dwim-target t
        ;; -F marks links with @
        dired-ls-F-marks-symlinks t
        delete-by-moving-to-trash t
        ;; Auto refresh dired
        global-auto-revert-non-file-buffers t
        wdired-allow-to-change-permissions t)
  (define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file)
  (define-key dired-mode-map (kbd "C-M-u") 'dired-up-directory)
  (define-key dired-mode-map (kbd "M-o") #'my/dired-open)
  (define-key dired-mode-map (kbd "C-x C-q") 'wdired-change-to-wdired-mode)
  (bind-key "l" #'dired-up-directory dired-mode-map)
  (bind-key "M-!" #'async-shell-command dired-mode-map)
  (add-hook 'dired-mode-hook #'hl-line-mode)
  (add-hook 'dired-mode-hook #'my/dired-mode-hook))
emacsclient

Let's make sure to start up a server!

Disabled, I actually start up a background emacs --daemon for this

saveplace

Navigates back to where you were editing a file next time you open it

(use-package saveplace
  :defer t
  :init
  (setq-default save-place t)
  (setq save-place-file (expand-file-name ".places" user-emacs-directory)))
recentf

Set up keeping track of recent files, up to 2000 of them.

If emacs has been idle for 10 minutes, clean up the recent files. Also save the list of recent files every 5 minutes.

This also only enables recentf-mode if idle, so that emacs starts up faster.

(use-package recentf
  :defer 10
  :commands (recentf-mode
             recentf-add-file
             recentf-apply-filename-handlers)
  :init
  (setq recentf-max-saved-items 300
        recentf-exclude '("/auto-install/" ".recentf" "/repos/" "/elpa/"
                          "\\.mime-example" "\\.ido.last" "COMMIT_EDITMSG"
                          ".gz"
                          "~$" "/tmp/" "/ssh:" "/sudo:" "/scp:")
        recentf-auto-cleanup 600)
  (when (not noninteractive) (recentf-mode 1))

  (defun recentf-save-list ()
    "Save the recent list.
Load the list from the file specified by `recentf-save-file',
merge the changes of your current session, and save it back to
the file."
    (interactive)
    (let ((instance-list (cl-copy-list recentf-list)))
      (recentf-load-list)
      (recentf-merge-with-default-list instance-list)
      (recentf-write-list-to-file)))

  (defun recentf-merge-with-default-list (other-list)
    "Add all items from `other-list' to `recentf-list'."
    (dolist (oitem other-list)
      ;; add-to-list already checks for equal'ity
      (add-to-list 'recentf-list oitem)))

  (defun recentf-write-list-to-file ()
    "Write the recent files list to file.
Uses `recentf-list' as the list and `recentf-save-file' as the
file to write to."
    (condition-case error
        (with-temp-buffer
          (erase-buffer)
          (set-buffer-file-coding-system recentf-save-file-coding-system)
          (insert (format recentf-save-file-header (current-time-string)))
          (recentf-dump-variable 'recentf-list recentf-max-saved-items)
          (recentf-dump-variable 'recentf-filter-changer-current)
          (insert "\n \n;;; Local Variables:\n"
                  (format ";;; coding: %s\n" recentf-save-file-coding-system)
                  ";;; End:\n")
          (write-file (expand-file-name recentf-save-file))
          (when recentf-save-file-modes
            (set-file-modes recentf-save-file recentf-save-file-modes))
          nil)
      (error
       (warn "recentf mode: %s" (error-message-string error)))))
  (recentf-mode 1))
whitespace-mode

Here are the things that whitespace-mode should highlight

(setq whitespace-style '(tabs newline space-mark
                         tab-mark newline-mark
                         face lines-tail))

Display pretty things for newlines and tabs (nothing for spaces)

(setq whitespace-display-mappings
      ;; all numbers are Unicode codepoint in decimal. e.g. (insert-char 182 1)
      ;; 32 SPACE, 183 MIDDLE DOT
      '((space-mark nil)
        ;; 10 LINE FEED
        ;;(newline-mark 10 [172 10])
        (newline-mark nil)
        ;; 9 TAB, MIDDLE DOT
        (tab-mark 9 [183 9] [92 9])))

Always turn on whitespace mode in programming buffers

;; turn on whitespace mode globally in prog-mode buffers
;; (add-hook 'prog-mode-hook #'whitespace-mode)
(add-hook 'whitespace-mode-hook (lambda () (diminish 'whitespace-mode)))

Indicate trailing empty lines in the GUI:

(setq-default show-trailing-whitespace t)

Programming language-specific configuration

Configuration options for language-specific packages live here. I generally only have configuration for languages I use, but the "order of usage" usually goes clojure & shell > elisp > python > ruby > java > everything else.

CEDET (semantic-mode)

Basic semantic-mode things

First, use a development version of cedet if applicable, I download the latest snapshot from http://www.randomsample.de/cedet-snapshots/ and extract it in ~/src/elisp. Don't forget to run make in it!

And then things to set up semantic mode

(defun my/setup-semantic-mode ()
  (interactive)
  (use-package semantic
    :init
    (require 'semantic/ia)
    (require 'semantic/wisent)
    (semantic-mode t)))

(add-hook 'c-mode-hook #'my/setup-semantic-mode)
(add-hook 'java-mode-hook #'my/setup-semantic-mode)
General prog-mode hooks

Remove some back-ends from vc-mode, no need to check all these things, I use magit for everything anyway:

(setq vc-handled-backends '())

In programming modes, make sure things like FIXME and TODO are highlighted so they stand out:

(defun my/add-watchwords ()
  "Highlight FIXME, TODO, and NOCOMMIT in code TODO"
  (font-lock-add-keywords
   nil '(("\\<\\(FIXME:?\\|TODO:?\\|NOCOMMIT:?\\)\\>"
          1 '((:foreground "#d7a3ad") (:weight bold)) t))))

(add-hook 'prog-mode-hook #'my/add-watchwords)

Also highlight the line in prog-mode:

(add-hook 'prog-mode-hook #'hl-line-mode)

I need to hide the lighter for subword mode:

(use-package subword
  :diminish subword-mode)
Clojure (also cider)

Things for Clojure development, which I do a lot of.

(defun my/clojure-things-hook ()
  "Set up clojure-y things"
  (eldoc-mode 1)
  (subword-mode t)
  (paredit-mode 1)
  (global-set-key (kbd "C-c t") 'clojure-jump-between-tests-and-code))

(use-package clojure-mode
  :init
  (add-hook #'clojure-mode-hook #'my/clojure-things-hook))

Let's define a couple of helper functions for setting up the cider and ac-nrepl packages:

(defun my/setup-cider ()
  (interactive)
  (setq cider-history-file "~/.nrepl-history"
        cider-hide-special-buffers t
        cider-repl-history-size 10000
        cider-prefer-local-resources t
        cider-popup-stacktraces-in-repl t)
  (paredit-mode 1)
  (eldoc-mode 1))

And then finally use them if cider and ac-nrepl packages are available:

(use-package cider
  :defer 30
  :init
  (add-hook #'cider-mode-hook #'my/setup-cider)
  (add-hook #'cider-repl-mode-hook #'my/setup-cider)
  (add-hook #'cider-mode-hook #'my/clojure-things-hook)
  (add-hook #'cider-repl-mode-hook #'my/clojure-things-hook)
  (use-package ac-cider
    :init
    (add-hook #'cider-mode-hook #'ac-flyspell-workaround)
    (add-hook #'cider-mode-hook #'ac-cider-setup)
    (add-hook #'cider-repl-mode-hook #'ac-cider-setup)))
Shell

Not much really here, just making .zsh also be a shell script.

(add-to-list 'auto-mode-alist '("\\.zsh$" . shell-script-mode))
Elisp

This contains the configuration for elisp programming

First, turn on eldoc everywhere it's useful:

(defun my/turn-on-paredit-and-eldoc ()
  (interactive)
  (paredit-mode 1)
  (eldoc-mode 1))

(add-hook 'emacs-lisp-mode-hook #'my/turn-on-paredit-and-eldoc)
(add-hook 'ielm-mode-hook #'my/turn-on-paredit-and-eldoc)

And some various eldoc settings:

(use-package eldoc
  :diminish eldoc-mode
  :config
  (setq eldoc-idle-delay 0.3)
  (set-face-attribute 'eldoc-highlight-function-argument nil
                      :underline t :foreground "green"
                      :weight 'bold))

Change the faces for elisp regex grouping:

(set-face-foreground 'font-lock-regexp-grouping-backslash "#ff1493")
(set-face-foreground 'font-lock-regexp-grouping-construct "#ff8c00")

Define some niceties for popping up an ielm buffer:

(defun ielm-other-window ()
  "Run ielm on other window"
  (interactive)
  (switch-to-buffer-other-window
   (get-buffer-create "*ielm*"))
  (call-interactively 'ielm))

(define-key emacs-lisp-mode-map (kbd "C-c C-z") 'ielm-other-window)
(define-key lisp-interaction-mode-map (kbd "C-c C-z") 'ielm-other-window)

Turn on elisp-slime-nav if available, so M-. works to jump to function definitions:

(use-package elisp-slime-nav
  :diminish elisp-slime-nav-mode
  :init (add-hook 'emacs-lisp-mode-hook #'elisp-slime-nav-mode))

Borrowed from Steve Purcell's config. This pretty-prints the results.

(bind-key "M-:" 'pp-eval-expression)

(defun sanityinc/eval-last-sexp-or-region (prefix)
 "Eval region from BEG to END if active, otherwise the last sexp."
 (interactive "P")
 (if (and (mark) (use-region-p))
 (eval-region (min (point) (mark)) (max (point) (mark)))
 (pp-eval-last-sexp prefix)))

(bind-key "C-x C-e" 'sanityinc/eval-last-sexp-or-region emacs-lisp-mode-map)

(define-key lisp-mode-shared-map (kbd "RET") 'reindent-then-newline-and-indent)
Python

Some various python settings, including loading jedi if needed to set up keys, the custom hook only loads jedi when editing python files:

(use-package python
  :defer t
  :config
  (define-key python-mode-map (kbd "C-c C-z") 'run-python)
  (define-key python-mode-map (kbd "<backtab>") 'python-back-indent))
  • virtualenv

    I'm using the virtualenvwrapper package for managing these

    (use-package virtualenvwrapper
      :defer t
      :init
      (progn
        (venv-initialize-interactive-shells)
        (venv-initialize-eshell)
        (setq venv-location (or (getenv "WORKON_HOME")
                                "~/.venvs"))))
    
Java

Java uses eclim and/or malabar to make life at least a little bit livable.

intellij-java-style is a copy of our Intellij indentation rules for Elasticsearch, which are a little weird in some cases, but needed in order to work with the ES codebase.

;; via http://emacs.stackexchange.com/questions/17327/how-to-have-c-offset-style-correctly-detect-a-java-constructor-and-change-indent
(defun my/point-in-defun-declaration-p ()
  (let ((bod (save-excursion (c-beginning-of-defun)
                             (point))))
    (<= bod
        (point)
        (save-excursion (goto-char bod)
                        (re-search-forward "{")
                        (point)))))

(defun my/is-string-concatenation-p ()
  "Returns true if the previous line is a string concatenation"
  (save-excursion
    (let ((start (point)))
      (forward-line -1)
      (if (re-search-forward " \\\+$" start t) t nil))))

(defun my/inside-java-lambda-p ()
  "Returns true if point is the first statement inside of a lambda"
  (save-excursion
    (c-beginning-of-statement-1)
    (let ((start (point)))
      (forward-line -1)
      (if (search-forward " -> {" start t) t nil))))

(defun my/trailing-paren-p ()
  "Returns true if point is a training paren and semicolon"
  (save-excursion
    (end-of-line)
    (let ((endpoint (point)))
      (beginning-of-line)
      (if (re-search-forward "[ ]*);$" endpoint t) t nil))))

(defun my/prev-line-call-with-no-args-p ()
  "Return true if the previous line is a function call with no arguments"
  (save-excursion
    (let ((start (point)))
      (forward-line -1)
      (if (re-search-forward ".($" start t) t nil))))

(defun my/arglist-cont-nonempty-indentation (arg)
  (if (my/inside-java-lambda-p)
      '+
    (if (my/is-string-concatenation-p)
        16 ;; TODO don't hard-code
      (unless (my/point-in-defun-declaration-p) '++))))

(defun my/statement-block-intro (arg)
  (if (and (c-at-statement-start-p) (my/inside-java-lambda-p)) 0 '+))

(defun my/block-close (arg)
  (if (my/inside-java-lambda-p) '- 0))

(defun my/arglist-close (arg) (if (my/trailing-paren-p) 0 '--))

(defun my/arglist-intro (arg)
  (if (my/prev-line-call-with-no-args-p) '++ 0))

(defconst intellij-java-style
  '((c-basic-offset . 4)
    (c-comment-only-line-offset . (0 . 0))
    ;; the following preserves Javadoc starter lines
    (c-offsets-alist
     .
     ((inline-open . 0)
      (topmost-intro-cont    . +)
      (statement-block-intro . my/statement-block-intro)
      (block-close           . my/block-close)
      (knr-argdecl-intro     . +)
      (substatement-open     . +)
      (substatement-label    . +)
      (case-label            . +)
      (label                 . +)
      (statement-case-open   . +)
      (statement-cont        . +)
      (arglist-intro         . my/arglist-intro)
      (arglist-cont-nonempty . (my/arglist-cont-nonempty-indentation c-lineup-arglist))
      (arglist-close         . my/arglist-close)
      (inexpr-class          . 0)
      (access-label          . 0)
      (inher-intro           . ++)
      (inher-cont            . ++)
      (brace-list-intro      . +)
      (func-decl-cont        . ++))))
  "Elasticsearch's Intellij Java Programming Style")

(c-add-style "intellij" intellij-java-style)
(customize-set-variable 'c-default-style
                        '((java-mode . "intellij")
                          (awk-mode . "awk")
                          (other . "gnu")))

(defun setup-java ()
  (interactive)
  (define-key java-mode-map (kbd "M-,") 'pop-tag-mark)
  (define-key java-mode-map (kbd "C-c M-i") 'java-imports-add-import)
  (c-set-style "intellij" t)
  (subword-mode 1)
  (toggle-truncate-lines 1)
  ;; Generic java stuff things
  (setq-local fci-rule-column 99)
  (setq-local fill-column 140)
  ;; remove the stupid company-eclim backend
  (when (boundp 'company-backends)
    (delete 'company-eclim company-backends)))

(add-hook 'java-mode-hook #'setup-java)

;; Make emacs' compile recognize gradle output
(add-to-list 'compilation-error-regexp-alist
             '("^.*:compileJava\(.*\):\([0-9]+\)\(.\)" 1 2 3))
  • java-imports

    I also have a custom package, java-imports, which I use to quickly add imports for things.

    (use-package java-imports
      :config
      ;; Elasticsearch's import style
      (setq java-imports-find-block-function 'java-imports-find-place-sorted-block))
    
  • eclim

    Eclim is decent for emacs-java integration, but isn't quite there for showing errors or things like that. Unfortunately I still have to jump back into Intellij all the time for things.

    (use-package emacs-eclim
      :disabled t
      :init
      (progn
        ;; only show errors
        (setq-default eclim--problems-filter "e")
        (global-eclim-mode))
      :config
      (progn
        (use-package company-emacs-eclim
          :init (company-emacs-eclim-setup))))
    
Ruby

Using rbenv, set it up correctly when idle

(use-package rbenv
  :defer 25
  :init
  ;; I don't really care about the active ruby in the modeline
  (setq rbenv-show-active-ruby-in-modeline nil)
  (global-rbenv-mode t))
Haskell

Use GHC for haskell mode, and turn on auto-complete and some doc/indent modes:

(use-package haskell-mode
  :defer t
  :init
  (progn
    (add-hook 'haskell-mode-hook #'haskell-indentation-mode)
    (add-hook 'haskell-mode-hook #'turn-on-haskell-doc-mode)
    (add-hook 'haskell-mode-hook #'subword-mode)))
Javascript

I want indentation of 2 for json/js.

(setq-default js-indent-level 2)

Bleh javascript. js2-mode is better than nothing.

(use-package js2-mode
  :mode "\\.js\\'"
  :config
  (js2-imenu-extras-setup)
  (setq-default js-auto-indent-flag nil))

There's tern also, but I leave it turned off by default

(use-package tern)
Elasticsearch (es-mode)

(es-mode) stuff, loaded from disk so I can develop on it quickly.

(if (file-exists-p "~/src/elisp/es-mode")
    (progn
      (add-to-list 'load-path "~/src/elisp/es-mode")
      (use-package es-mode
        :init (use-package ob-elasticsearch)
        ;; Don't warn me about delete statements
        :config (setq es-warn-on-delete-query nil)))
  (progn
    (use-package es-mode
      :ensure t
      :init (use-package ob-elasticsearch)
      ;; Don't warn me about delete statements
      :config (setq es-warn-on-delete-query nil))))

theme

Misc theme settings

color theme

For light-colored backgrounds, I used leuven-theme. For dark-colored backgrounds (most of the time), I use my own custom theme, called dakrone-theme. Sometimes I use moe-theme for a dark background also, although the magenta text annoys me somewhat. Lately I've also been checking out tangotango, which seems nice.

(setq ns-use-srgb-colorspace t)

(defun dakrone-dark ()
  (interactive)
  ;; (use-package color-theme-sanityinc-tomorrow
  ;;   :init (color-theme-sanityinc-tomorrow-night))
  (use-package material-theme
    :disabled t
    :init (load-theme 'material t))
  (use-package apropospriate-theme
    :disabled t
    :init (load-theme 'apropospriate-dark t))
  (use-package color-theme-sanityinc-tomorrow
    :init (load-theme 'sanityinc-tomorrow-night t))
  (use-package solarized-theme
    :disabled t
    :init (load-theme 'solarized-dark t)))

(defun dakrone-light ()
  (interactive)
  (use-package leuven-theme
    :disabled t
    :init (load-theme 'leuven t) ;;:config (set-background-color "#f0f0f0")
    )
  (use-package solarized-theme
    :init (load-theme 'solarized-light t))
  (use-package material-theme
    :disabled t
    :init (load-theme 'material-light t)))

(if (eq my/background 'dark)
    (dakrone-dark)
  (dakrone-light))
fonts

I've been using Fantasque Sans Mono lately, it looks pretty nice to me. On Linux I've been using Bitstream Vera Sans Mono. I also use Anonymous Pro and Inconsolata a lot.

Config for OSX:

(defun my/setup-osx-fonts ()
  (interactive)
  (when (eq system-type 'darwin)
    (set-fontset-font "fontset-default" 'symbol "Monaco")
    ;;(set-default-font "Fantasque Sans Mono")
    ;;(set-default-font "Monaco")
    ;;(set-default-font "Anonymous Pro")
    ;;(set-default-font "Inconsolata")
    (set-default-font "Bitstream Vera Sans Mono")
    ;;(set-default-font "Menlo")
    ;;(set-default-font "Source Code Pro")
    ;;(set-default-font "Mensch")
    (set-face-attribute 'default nil :height 120)
    (set-face-attribute 'fixed-pitch nil :height 120)

    ;; Anti-aliasing
    (setq mac-allow-anti-aliasing t)))

(when (eq system-type 'darwin)
  (add-hook 'after-init-hook #'my/setup-osx-fonts))

Config for Linux/X11 systems:

(defun my/setup-x11-fonts ()
  (interactive)
  (when (eq window-system 'x)
    ;; Font family
    (set-frame-font "DejaVu Sans Mono")
    ;; (set-frame-font "Ubuntu Mono")
    ;; (set-frame-font "Hack")
    ;; (set-frame-font "Fantasque Sans Mono")
    ;; (set-frame-font "Anonymous Pro")
    ;; (set-frame-font "Inconsolata")
    (set-face-attribute 'default nil :height 105)))

(when (eq window-system 'x)
  (add-hook 'after-init-hook #'my/setup-x11-fonts))
modeline (mode-line)
(use-package smart-mode-line
  :init
  (progn
    (setq sml/theme my/background)
    (sml/setup))
  :config
  (setq sml/shorten-directory t
        sml/shorten-modes t)
  (add-to-list 'sml/replacer-regexp-list '("^~/es/x-plugins/" ":X:"))
  (add-to-list 'sml/replacer-regexp-list '("^~/es/elasticsearch/" ":ES:") t))
fringe

So, fringe is nice actually, I set it to the same color as the background

(defun my/set-fringe-background ()
  "Set the fringe background to the same color as the regular background."
  (interactive)
  (setq my/fringe-background-color
        (face-background 'default))
  (custom-set-faces
   `(fringe ((t (:background ,my/fringe-background-color))))))

(add-hook 'after-init-hook #'my/set-fringe-background)

;; Indicate where a buffer stars and stops
(setq-default indicate-buffer-boundaries 'right)

org-mode

I use org-mode a ton, so it get's its own page here.

A great lot of this was taken from http://doc.norang.ca/org-mode.html, to which I owe almost all of the agenda configuration. The capture stuff and regular org settings are mine.

First, the hook that gets run every time org-mode is started, to turn on certain modes

(defun my/org-mode-hook ()
  (interactive)
  (turn-on-auto-fill)
  (turn-on-flyspell)
  (when (fboundp 'yas-minor-mode)
    (yas-minor-mode 1))
  (when (fboundp 'my/enable-abbrev-mode)
    (my/enable-abbrev-mode))

  ;; fix some bindings that org-mode overwrites
  (define-key org-mode-map [C-tab] 'other-window)
  (define-key org-mode-map [C-S-tab]
    (lambda ()
      (interactive)
      (other-window -1)))
  (define-key org-mode-map (kbd "C-'")
    'eyebrowse-next-window-config)
  (define-key org-mode-map (kbd "C-c C-x C-f") 'org-refile)
  (when (boundp 'org-agenda-mode-map)
    (define-key org-agenda-mode-map (kbd "C-c C-x C-f") 'org-agenda-refile)))

And now the huge org-mode configuration

(use-package org
  :bind (("C-c l" . org-store-link)
         ("C-c a" . org-agenda)
         ("C-c b" . org-iswitchb)
         ("C-c c" . org-capture)
         ("C-c M-p" . org-babel-previous-src-block)
         ("C-c M-n" . org-babel-next-src-block)
         ("C-c S" . org-babel-previous-src-block)
         ("C-c s" . org-babel-next-src-block))
  :defer 30
  :config
  (progn
    (use-package org-install)
    (use-package ob-core)
    ;; org-export
    (use-package ox)
    ;; Enable archiving things
    (use-package org-archive)
    (add-hook 'org-mode-hook #'hl-line-mode)
    (add-hook 'org-mode-hook #'my/org-mode-hook)
    ;; enabled export backends
    (custom-set-variables '(org-export-backends '(ascii html latex md rss)))
    (setq org-directory (file-truename "~/org")
          ;; follow links by pressing ENTER on them
          org-return-follows-link t
          ;; allow changing between todo stats directly by hotkey
          org-use-fast-todo-selection t
          ;; syntax highlight code in source blocks
          org-src-fontify-natively t
          ;; for the leuven theme, fontify the whole heading line
          org-fontify-whole-heading-line t
          ;; force UTF-8
          org-export-coding-system 'utf-8
          ;; don't use ido completion (I use helm)
          org-completion-use-ido nil
          ;; start up org files with indentation (same as #+STARTUP: indent)
          org-startup-indented t
          ;; don't indent source code
          org-edit-src-content-indentation 0
          ;; don't adapt indentation
          org-adapt-indentation nil
          ;; preserve the indentation inside of source blocks
          org-src-preserve-indentation t
          ;; Imenu should use 3 depth instead of 2
          org-imenu-depth 3
          ;; put state change log messages into a drawer
          org-log-into-drawer t
          ;; special begin/end of line to skip tags and stars
          org-special-ctrl-a/e t
          ;; special keys for killing a headline
          org-special-ctrl-k t
          ;; don't adjust subtrees that I copy
          org-yank-adjusted-subtrees nil
          ;; try to be smart when editing hidden things
          org-catch-invisible-edits 'smart
          ;; blank lines are removed when exiting the code edit buffer
          org-src-strip-leading-and-trailing-blank-lines t
          ;; how org-src windows are set up when hitting C-c '
          org-src-window-setup 'current-window
          ;; Overwrite the current window with the agenda
          org-agenda-window-setup 'current-window
          ;; Use 100 chars for the agenda width
          org-agenda-tags-column -100
          ;; Use full outline paths for refile targets - we file directly with IDO
          org-refile-use-outline-path t
          ;; Targets complete directly with IDO
          org-outline-path-complete-in-steps nil
          ;; Allow refile to create parent tasks with confirmation
          org-refile-allow-creating-parent-nodes 'confirm
          ;; never leave empty lines in collapsed view
          org-cycle-separator-lines 0
          ;; Use cider as the clojure backend
          org-babel-clojure-backend 'cider
          ;; don't run stuff automatically on export
          org-export-babel-evaluate nil
          ;; export tables as CSV instead of tab-delineated
          org-table-export-default-format "orgtbl-to-csv"
          ;; start up showing images
          org-startup-with-inline-images t
          ;; always enable noweb, results as code and exporting both
          org-babel-default-header-args
          (cons '(:noweb . "yes")
                (assq-delete-all :noweb org-babel-default-header-args))
          org-babel-default-header-args
          (cons '(:exports . "both")
                (assq-delete-all :exports org-babel-default-header-args))
          ;; I don't want to be prompted on every code block evaluation
          org-confirm-babel-evaluate nil
          ;; Mark entries as done when archiving
          org-archive-mark-done t
          ;; Where to put headlines when archiving them
          org-archive-location "%s_archive::* Archived Tasks"
          ;; Sorting order for tasks on the agenda
          org-agenda-sorting-strategy
          '((agenda habit-down
                    time-up
                    priority-down
                    user-defined-up
                    effort-up
                    category-keep)
            (todo priority-down category-up effort-up)
            (tags priority-down category-up effort-up)
            (search priority-down category-up))
          ;; Enable display of the time grid so we can see the marker for the
          ;; current time
          org-agenda-time-grid
          '((daily today remove-match)
            #("----------------" 0 16 (org-heading t))
            (0900 1100 1300 1500 1700))
          ;; keep the agenda filter until manually removed
          org-agenda-persistent-filter t
          ;; show all occurrences of repeating tasks
          org-agenda-repeating-timestamp-show-all t
          ;; always start the agenda on today
          org-agenda-start-on-weekday nil
          ;; Use sticky agenda's so they persist
          org-agenda-sticky t
          ;; show 4 agenda days
          org-agenda-span 4
          ;; Do not dim blocked tasks
          org-agenda-dim-blocked-tasks nil
          ;; Compact the block agenda view
          org-agenda-compact-blocks t
          ;; Show all agenda dates - even if they are empty
          org-agenda-show-all-dates t
          ;; Agenda org-mode files
          org-agenda-files `(,(file-truename "~/org/refile.org")
                             ,(file-truename "~/org/todo.org")
                             ,(file-truename "~/org/microsoft.org")
                             ,(file-truename "~/org/bibliography.org")
                             ,(file-truename "~/org/notes.org")
                             ,(file-truename "~/org/es-team.org")
                             ,(file-truename "~/org/journal.org")))

    ;; Org todo keywords
    (setq org-todo-keywords
          '((sequence "TODO(t)" "|" "DONE(d)")
            (sequence "TODO(t)"
                      "SOMEDAY(s)"
                      "INPROGRESS(i)"
                      "HOLD(h)"
                      "WAITING(w@/!)"
                      "NEEDSREVIEW(n@/!)"
                      "|" "DONE(d)")
            (sequence "TODO(t)" "INPROGRESS(i)" "|" "CANCELLED(c@/!)")))
    ;; Org faces
    (setq org-todo-keyword-faces
          '(("TODO" :foreground "red" :weight bold)
            ("INPROGRESS" :foreground "deep sky blue" :weight bold)
            ("SOMEDAY" :foreground "purple" :weight bold)
            ("NEEDSREVIEW" :foreground "#edd400" :weight bold)
            ("DONE" :foreground "forest green" :weight bold)
            ("WAITING" :foreground "orange" :weight bold)
            ("HOLD" :foreground "magenta" :weight bold)
            ("CANCELLED" :foreground "forest green" :weight bold)))
    ;; add or remove tags on state change
    (setq org-todo-state-tags-triggers
          '(("CANCELLED" ("CANCELLED" . t))
            ("WAITING" ("WAITING" . t))
            ("HOLD" ("WAITING") ("HOLD" . t))
            (done ("WAITING") ("HOLD"))
            ("TODO" ("WAITING") ("CANCELLED") ("HOLD"))
            ("INPROGRESS" ("WAITING") ("CANCELLED") ("HOLD"))
            ("DONE" ("WAITING") ("CANCELLED") ("HOLD"))))
    ;; refile targets all level 1 and 2 headers in current file and agenda files
    (setq org-refile-targets '((nil :maxlevel . 2)
                               (org-agenda-files :maxlevel . 2)))
    ;; quick access to common tags
    (setq org-tag-alist
          '(("oss" . ?o)
            ("home" . ?h)
            ("work" . ?w)
            ("xplugins" . ?x)
            ("book" . ?b)
            ("support" . ?s)
            ("docs" . ?d)
            ("export" . ?e)
            ("noexport" . ?n)
            ("recurring" . ?r)))
    ;; capture templates
    (setq org-capture-templates
          '(("t" "Todo" entry (file "~/org/refile.org")
             "* TODO %?\n%U\n")
            ("n" "Notes" entry (file+headline "~/org/notes.org" "Notes")
             "* %? :NOTE:\n%U\n")
            ("e" "Emacs note" entry
             (file+headline "~/org/notes.org" "Emacs Links")
             "* %? :NOTE:\n%U\n")
            ("j" "Journal" entry (file+datetree "~/org/journal.org")
             "* %?\n%U\n")
            ("b" "Book/Bibliography" entry
             (file+headline "~/org/bibliography.org" "Refile")
             "* %?%^{TITLE}p%^{AUTHOR}p%^{TYPE}p")))
    ;; Custom agenda command definitions
    (setq org-agenda-custom-commands
          '(("N" "Notes" tags "NOTE"
             ((org-agenda-overriding-header "Notes")
              (org-tags-match-list-sublevels t)))
            (" " "Agenda"
             ((agenda "" nil)
              ;; All items with the "REFILE" tag, everything in refile.org
              ;; automatically gets that applied
              (tags "REFILE"
                    ((org-agenda-overriding-header "Tasks to Refile")
                     (org-tags-match-list-sublevels nil)))
              ;; All "INPROGRESS" todo items
              (todo "INPROGRESS"
                    ((org-agenda-overriding-header "Current work")))
              ;; All headings with the "support" tag
              (tags "support/!"
                    ((org-agenda-overriding-header "Support cases")))
              ;; All "NEESREVIEW" todo items
              (todo "NEEDSREVIEW"
                    ((org-agenda-overriding-header "Waiting on reviews")))
              ;; All "WAITING" items without a "support" tag
              (tags "WAITING-support"
                    ((org-agenda-overriding-header "Waiting for something")))
              ;; All TODO items
              (todo "TODO"
                    ((org-agenda-overriding-header "Task list")
                     (org-agenda-sorting-strategy
                      '(time-up priority-down category-keep))))
              ;; Everything on hold
              (todo "HOLD"
                    ((org-agenda-overriding-header "On-hold")))
              ;; All headings with the "recurring" tag
              (tags "recurring/!"
                    ((org-agenda-overriding-header "Recurring"))))
             nil)))

    ;; Exclude DONE state tasks from refile targets
    (defun my/verify-refile-target ()
      "Exclude todo keywords with a done state from refile targets"
      (not (member (nth 2 (org-heading-components)) org-done-keywords)))
    (setq org-refile-target-verify-function 'my/verify-refile-target)

    ;; org-mode bindings
    (define-key org-mode-map (kbd "C-M-<return>") 'org-insert-todo-heading)
    (define-key org-mode-map (kbd "C-c t") 'org-todo)
    (define-key org-mode-map (kbd "M-G") 'org-plot/gnuplot)
    (define-key org-mode-map (kbd "RET") 'org-return-indent)
    ;; swap C-RET and M-RET
    (define-key org-mode-map (kbd "C-<return>") 'org-insert-heading)
    (define-key org-mode-map (kbd "M-<return>")
      'org-insert-heading-after-current)

    (local-unset-key (kbd "M-S-<return>"))

    ;; org-babel stuff
    (require 'ob-clojure)
    (org-babel-do-load-languages
     'org-babel-load-languages
     '((emacs-lisp . t)
       (elasticsearch . t)
       (clojure . t)
       (dot . t)
       (sh . t)
       (js . t)
       (haskell . t)
       (ruby . t)
       (python . t)
       (gnuplot . t)
       (plantuml . t)
       (latex . t)))

    ;; this is where Fedora installs it, YMMV
    (setq org-plantuml-jar-path "/usr/share/java/plantuml.jar")

    ;; Use org.css from the :wq website for export document stylesheets
    (setq org-html-head-extra
          "<link rel=\"stylesheet\" href=\"https://dakrone.github.io/org.css\" type=\"text/css\" />"
          org-html-head-include-default-style nil)

    ;; ensure this variable is defined
    (unless (boundp 'org-babel-default-header-args:sh)
      (setq org-babel-default-header-args:sh '()))

    ;; add a default shebang header argument shell scripts
    (add-to-list 'org-babel-default-header-args:sh
                 '(:shebang . "#!/usr/bin/env bash"))

    ;; add a default shebang header argument for python
    (add-to-list 'org-babel-default-header-args:python
                 '(:shebang . "#!/usr/bin/env python"))

    ;; Clojure-specific org-babel stuff
    (defvar org-babel-default-header-args:clojure
      '((:results . "silent")))

    (defun org-babel-execute:clojure (body params)
      "Execute a block of Clojure code with Babel."
      (let ((result-plist
             (nrepl-send-string-sync
              (org-babel-expand-body:clojure body params) nrepl-buffer-ns))
            (result-type  (cdr (assoc :result-type params))))
        (org-babel-script-escape
         (cond ((eq result-type 'value) (plist-get result-plist :value))
               ((eq result-type 'output) (plist-get result-plist :value))
               (t (message "Unknown :results type!"))))))

    ;; Function declarations
    (defun my/skip-non-archivable-tasks ()
      "Skip trees that are not available for archiving"
      (save-restriction
        (widen)
        ;; Consider only tasks with done todo headings as archivable candidates
        (let ((next-headline (save-excursion
                               (or (outline-next-heading) (point-max))))
              (subtree-end (save-excursion (org-end-of-subtree t))))
          (if (member (org-get-todo-state) org-todo-keywords-1)
              (if (member (org-get-todo-state) org-done-keywords)
                  (let* ((daynr (string-to-int
                                 (format-time-string "%d" (current-time))))
                         (a-month-ago (* 60 60 24 (+ daynr 1)))
                         (this-month
                          (format-time-string "%Y-%m-" (current-time)))
                         (subtree-is-current
                          (save-excursion
                            (forward-line 1)
                            (and (< (point) subtree-end)
                                 (re-search-forward this-month
                                                    subtree-end t)))))
                    (if subtree-is-current
                        subtree-end     ; Has a date in this month, skip it
                      nil))             ; available to archive
                (or subtree-end (point-max)))
            next-headline))))

    (defun my/save-all-agenda-buffers ()
      "Function used to save all agenda buffers that are
   currently open, based on `org-agenda-files'."
      (interactive)
      (save-current-buffer
        (dolist (buffer (buffer-list t))
          (set-buffer buffer)
          (when (member (buffer-file-name)
                        (mapcar 'expand-file-name (org-agenda-files t)))
            (save-buffer)))))

    ;; save all the agenda files after each capture
    (add-hook 'org-capture-after-finalize-hook 'my/save-all-agenda-buffers)

    (use-package org-id
      :config
      (progn
        (setq org-id-link-to-org-use-id t)

        (defun my/org-custom-id-get (&optional pom create prefix)
          "Get the CUSTOM_ID property of the entry at point-or-marker POM.
   If POM is nil, refer to the entry at point. If the entry does
   not have an CUSTOM_ID, the function returns nil. However, when
   CREATE is non nil, create a CUSTOM_ID if none is present
   already. PREFIX will be passed through to `org-id-new'. In any
   case, the CUSTOM_ID of the entry is returned."
          (interactive)
          (org-with-point-at pom
            (let ((id (org-entry-get nil "CUSTOM_ID")))
              (cond
               ((and id (stringp id) (string-match "\\S-" id))
                id)
               (create
                (setq id (org-id-new prefix))
                (org-entry-put pom "CUSTOM_ID" id)
                (org-id-add-location id (buffer-file-name (buffer-base-buffer)))
                id)))))

        (defun my/org-add-ids-to-headlines-in-file ()
          "Add CUSTOM_ID properties to all headlines in the
   current file which do not already have one."
          (interactive)
          (org-map-entries (lambda () (my/org-custom-id-get (point) 'create))))

        ;; automatically add ids to captured headlines
        (add-hook 'org-capture-prepare-finalize-hook
                  (lambda () (my/org-custom-id-get (point) 'create)))))

    (defun my/org-inline-css-hook (exporter)
      "Insert custom inline css to automatically set the
   background of code to whatever theme I'm using's background"
      (when (eq exporter 'html)
        (let* ((my-pre-bg (face-background 'default))
               (my-pre-fg (face-foreground 'default)))
          ;;(setq org-html-head-include-default-style nil)
          (setq
           org-html-head-extra
           (concat
            org-html-head-extra
            (format
             "<style type=\"text/css\">\n pre.src {background-color: %s; color: %s;}</style>\n"
             my-pre-bg my-pre-fg))))))

    (add-hook 'org-export-before-processing-hook #'my/org-inline-css-hook)))

org-clock

Now, my org-mode clocking configuration:

First, a function to use for clocking in

(defun my/org-clock-in ()
  (interactive)
  (org-clock-in '(4)))

(global-set-key (kbd "<f11>") #'my/org-clock-in)
(global-set-key (kbd "<f12>") 'org-clock-out)
(use-package org
  :bind (("C-c C-x C-i" . my/org-clock-in)
         ("C-c C-x C-o" . org-clock-out))
  :config
  (progn
    ;; Insinuate it everywhere
    (org-clock-persistence-insinuate)
    ;; Show lot of clocking history so it's easy to pick items off the C-F11 list
    (setq org-clock-history-length 23
          ;; Resume clocking task on clock-in if the clock is open
          org-clock-in-resume t
          ;; Separate drawers for clocking and logs
          org-drawers '("PROPERTIES" "CLOCK" "LOGBOOK" "RESULTS" "HIDDEN")
          ;; Save clock data and state changes and notes in the LOGBOOK drawer
          org-clock-into-drawer t
          ;; Sometimes I change tasks I'm clocking quickly -
          ;; this removes clocked tasks with 0:00 duration
          org-clock-out-remove-zero-time-clocks t
          ;; Clock out when moving task to a done state
          org-clock-out-when-done t
          ;; Save the running clock and all clock history when exiting Emacs, load it on startup
          org-clock-persist t
          ;; Prompt to resume an active clock
          org-clock-persist-query-resume t
          ;; Enable auto clock resolution for finding open clocks
          org-clock-auto-clock-resolution #'when-no-clock-is-running
          ;; Include current clocking task in clock reports
          org-clock-report-include-clocking-task t
          ;; don't use pretty things for the clocktable
          org-pretty-entities nil
          ;; some default parameters for the clock report
          org-agenda-clockreport-parameter-plist
          '(:maxlevel 10 :fileskip0 t :score agenda :block thismonth :compact t :narrow 60))))

org-publishing

Publishing org-mode files to my hosting provider:

(use-package org
  :config
  (require 'ox-rss)
  (require 'ox-icalendar)
  (setq org-publish-project-alist
        `(;; Main website at http://writequit.org
          ("writequit-org"
           :base-directory ,(file-truename "~/org/writequit/")
           :base-extension "org"
           :publishing-directory "/ssh:writequit.org:~/www/"
           :publishing-function org-html-publish-to-html
           :with-toc nil
           :html-preamble t
           :html-head-extra
           "<link rel=\"alternate\" type=\"application/rss+xml\"
                href=\"http://writequit.org/posts.xml\"
                title=\"RSS feed for writequit.org\">")
n          ("writequit-rss"
           :base-directory ,(file-truename  "~/org/writequit")
           :base-extension "org"
           :publishing-directory "/ssh:writequit.org:~/www/"
           :publishing-function org-rss-publish-to-rss
           :html-link-home "http://writequit.org/"
           :exclude ".*"
           :include ("posts.org")
           :html-link-use-abs-url t)
          ("writequit-images"
           :base-directory ,(file-truename  "~/org/writequit/images")
           :base-extension "png\\|jpg\\|gif"
           :publishing-directory "/ssh:writequit.org:~/www/images"
           :publishing-function org-publish-attachment)
          ("writequit-files"
           :base-directory ,(file-truename  "~/org/writequit/files")
           :base-extension "*"
           :publishing-directory "/ssh:writequit.org:~/www/files/"
           :publishing-function org-publish-attachment)
          ("writequit" :components ("writequit-org"
                                    "writequit-images"
                                    "writequit-files"
                                    "writequit-rss"))

          ;; Denver emacs site
          ("denver-emacs"
           :base-directory ,(file-truename "~/org/denver-emacs-meetup/")
           :base-extension "org\\|html"
           :publishing-directory
           "/ssh:writequit.org:~/www/denver-emacs"
           :publishing-function org-html-publish-to-html
           :with-toc nil
           :html-preamble t)

          ;; Org-mode files for ~/.emacs.d/settings.org
          ("dotfiles"
           :base-directory ,(file-truename "~/.emacs.d/../")
           :base-extension "org\\|html"
           :publishing-directory
           "/ssh:writequit.org:~/www/org/"
           :publishing-function org-html-publish-to-html
           :with-toc t
           :html-preamble t)

          ;; Org-mode files for ~/org files
          ("org-org"
           :base-directory ,(file-truename "~/org/")
           :base-extension "org\\|html"
           :publishing-directory
           "/ssh:writequit.org:~/www/org/"
           :publishing-function org-html-publish-to-html
           :with-toc t
           :html-preamble t)
          ("org-images"
           :base-directory ,(file-truename "~/org/images")
           :base-extension "png\\|jpg"
           :publishing-directory
           "/ssh:writequit.org:~/www/org/images"
           :publishing-function org-publish-attachment)
          ("org" :components ("org-org" "org-images"))

          ;; Org-mode for the ~/org/es files
          ("org-es-org"
           :base-directory ,(file-truename "~/org/es/")
           :base-extension "org\\|html"
           :publishing-directory
           "/ssh:writequit.org:~/www/org/es"
           :publishing-function org-html-publish-to-html
           :with-toc t
           :html-preamble t)
          ("org-es-files"
           :base-directory ,(file-truename "~/org/es/")
           :base-extension "css\\|pdf\\|sh\\|es\\|zsh\\|py\\|org"
           :publishing-directory
           "/ssh:writequit.org:~/www/org/es"
           :publishing-function org-publish-attachment)
          ("org-es-images"
           :base-directory ,(file-truename "~/org/es/images")
           :base-extension "png\\|jpg"
           :publishing-directory
           "/ssh:writequit.org:~/www/org/es/images"
           :publishing-function org-publish-attachment)
          ("org-es"
           :components ("org-es-org" "org-es-files" "org-es-images"))

          ;; Org-mode for the ~/org/es/design files
          ("org-es-design-org"
           :base-directory ,(file-truename "~/org/es/design")
           :base-extension "org\\|html"
           :publishing-directory
           "/ssh:writequit.org:~/www/org/es/design"
           :publishing-function org-html-publish-to-html
           :with-toc t
           :html-preamble t)
          ("org-es-design-files"
           :base-directory ,(file-truename "~/org/es/design")
           :base-extension "css\\|pdf\\|sh\\|es\\|zsh\\|py\\|org"
           :publishing-directory
           "/ssh:writequit.org:~/www/org/es/design"
           :publishing-function org-publish-attachment)
          ("org-es-designs-images"
           :base-directory ,(file-truename "~/org/es/design/images")
           :base-extension "png\\|jpg"
           :publishing-directory
           "/ssh:writequit.org:~/www/org/es/design/images"
           :publishing-function org-publish-attachment)
          ("org-es-design"
           :components ("org-es-design-org"
                        "org-es-design-files"
                        "org-es-design-images")))))

Then, when I'm editing ~/org/es/feature-foo.org, I can hit C-c C-e P f and export the file to show up in http://p.writequit.org/org

org-alert

Notifications for upcoming org statuses!

(use-package org-alert
  :disabled t
  :init (org-alert-enable))

org-present

A simple presentation mode for org-mode

(use-package org-present
  :defer 20
  :init
  (add-hook 'org-present-mode-hook
            (lambda ()
              (org-present-big)
              (org-display-inline-images)
              (org-present-hide-cursor)
              (org-present-read-only)))
  (add-hook 'org-present-mode-quit-hook
            (lambda ()
              (org-present-small)
              (org-remove-inline-images)
              (org-present-show-cursor)
              (org-present-read-write))))

org-bullets

(use-package org-bullets
  :init
  (add-hook 'org-mode-hook #'org-bullets-mode))

alert (notifications)

Yep. I need to actually make this work for OSX, for Linux it's no problem though.

(use-package alert
  :config
  (when (eq system-type 'darwin)
    (setq alert-default-style 'notifier))
  (when (eq system-type 'gnu/linux)
    (setq alert-default-style 'notifications))

  (defun finish ()
    (interactive)
    (alert (concat "Finished shell command in " (buffer-name))
           :severity 'high
           :category 'eshell
           :title (buffer-name)
           :persistent t)))

To use this, I just need to do (alert "this is a message").

ERC

IRC in Emacs

(defun start-erc ()
  (interactive)
  (load-file "~/.ercpass")
  ;; Don't need flycheck for IRC
  (when (fboundp 'global-flycheck-mode)
    (global-flycheck-mode -1))
  (use-package erc
    :init
    (setq erc-nick "dakrone"
          erc-keywords '("clj-http")
          erc-pals '("hiredman"
                     "technomancy"
                     "leathekd"
                     "joegallo"
                     "danlarkin"
                     "yazirian"
                     "pjstadig"
                     "scgilardi"
                     "drewr")
          erc-hide-list '("JOIN" "PART" "QUIT"))
    (defun my/disable-font-lock ()
      (font-lock-mode -1))
    ;; ERC is crazy, for some reason it doesn't like font-lock...
    (add-hook 'erc-mode-hook #'my/disable-font-lock))
  (use-package erc-hl-nicks)
  (use-package ercn
    :init
    (setq ercn-notify-rules
          '((current-nick . all)
            (keyword . all)
            (pal . ("#emacs" "#elasticsearch"))
            (query-buffer . all)))

    (use-package s)
    (defun do-notify (nickname message)
      (interactive)
      (alert (concat nickname ": "
                     (s-trim (s-collapse-whitespace message)))
             :title (buffer-name)))
    (add-hook 'ercn-notify-hook 'do-notify))
  (let ((tls-program
         '("gnutls-cli --priority secure256 --x509certfile ~/host.pem -p %p %h"
           "openssl s_client -connect %h:%p -no_ssl2 -ign_eof -cert ~/host.pem")))
    (erc-tls :server "irc.writequit.org"
             :port 31425
             :nick "dakrone"
             :password freenode-znc-pass)))

rcirc

rcirc is a lighter-weight alternative to ERC that is also included in Emacs. Doesn't have as many options, but seems to work.

(defun start-rcirc ()
  (interactive)
  (load-file "~/.ercpass")
  ;; Don't need flycheck for IRC
  (when (fboundp 'global-flycheck-mode)
    (global-flycheck-mode -1))
  (use-package rcirc
    :init
    (progn
      (setq rcirc-server-alist
            `(("irc.writequit.org"
               :nick "dakrone"
               :port 31425
               :password ,freenode-znc-pass
               :full-name "Lee"
               :encryption tls
               :channels
               ("#emacs")))
            rcirc-omit-responses '("315" "345" "JOIN" "PART"
                                   "QUIT" "NICK" "AWAY")))
    :config
    (progn
      (use-package rcirc-color)
      (use-package rcirc-alertify
        :init (rcirc-alertify-enable))
      ;; Scroll to the bottom
      (add-hook 'rcirc-mode-hook
                (lambda ()
                  (set (make-local-variable 'scroll-conservatively)
                       8192)))
      (add-hook 'rcirc-mode-hook 'rcirc-omit-mode)

      ;; Allows using /reconnect
      (defun-rcirc-command reconnect (arg)
        "Reconnect the server process."
        (interactive "i")
        (unless process
          (error "There's no process for this target"))
        (let* ((server (car (process-contact process)))
               (port (process-contact process :service))
               (nick (rcirc-nick process))
               channels query-buffers)
          (dolist (buf (buffer-list))
            (with-current-buffer buf
              (when (eq process (rcirc-buffer-process))
                (remove-hook 'change-major-mode-hook
                             'rcirc-change-major-mode-hook)
                (if (rcirc-channel-p rcirc-target)
                    (setq channels (cons rcirc-target channels))
                  (setq query-buffers (cons buf query-buffers))))))
          (delete-process process)
          (rcirc-connect server port nick
                         rcirc-default-user-name
                         rcirc-default-full-name
                         channels)))))

  (defun my/enable-rcirc-track ()
    (interactive)
    (rcirc-track-minor-mode 1))

  (let ((tls-program
         '("gnutls-cli --priority secure256 --x509certfile ~/host.pem -p %p %h"
           "openssl s_client -connect %h:%p -no_ssl2 -ign_eof -cert ~/host.pem")))
    (rcirc nil)
    (add-hook 'rcirc-mode-hook #'my/enable-rcirc-track)))

email (mu4e) Configuration

I usually install mu from source. I unpack it to ~/src/mu-0.9.13 (or whatever version) so I can reference the mu4e elisp files. Then run the following to install mu:

autoreconf -i
./configure --prefix=/usr/local
make
sudo make install

Keep in mind this configuration is a lot more complex than it needs to be, but that's because I manage 3 different email accounts from a single mu4e session, and they have account-specific mail directories so a lot of functions are needed to return the correct path depending on the account the email is from.

(defun mail ()
  (interactive)
  ;; All kinds of trailing whitespace in mu4e
  (setq-default show-trailing-whitespace nil)
  (add-to-list 'load-path "~/src/mu-0.9.13/mu4e")
  ;; Don't need flycheck for Email
  (when (fboundp 'global-flycheck-mode)
    (global-flycheck-mode -1))
  (use-package nyan-mode
    :init (nyan-mode 1)) ;; nyan-mode for mail!
  (use-package mu4e
    :config
    (progn
      (add-hook 'mu4e-compose-mode-hook 'turn-on-flyspell)
      ;; gpg stuff
      (use-package epa-file
        :init (epa-file-enable))

      ;; Various mu4e settings
      (setq mu4e-mu-binary (executable-find "mu")
            ;;mu4e-sent-messages-behavior 'delete
            mu4e-user-mail-address-list
            '("matthew.hinman@gmail.com"
              "lee@writequit.org"
              "leehinman@fastmail.com"
              "lee@elastic.co")
            ;; save attachments to the Downloads folder
            mu4e-attachment-dir "~/Downloads"
            ;; attempt to show images
            mu4e-view-show-images t
            mu4e-view-image-max-width 800
            ;; start in non-queuing mode
            smtpmail-queue-mail nil
            smtpmail-queue-dir "~/.mail/queue/"
            mml2015-use 'epg
            pgg-default-user-id "3acecae0"
            epg-gpg-program (executable-find "gpg")
            message-kill-buffer-on-exit t ;; kill sent msg buffers
            ;; use msmtp
            message-send-mail-function 'message-send-mail-with-sendmail
            sendmail-program (executable-find "msmtp")
            ;; Look at the from header to determine the account from which
            ;; to send. Might not be needed b/c of mlh-msmtp
            mail-specify-envelope-from t
            mail-envelope-from 'header
            message-sendmail-envelope-from 'header
            ;; emacs email defaults
            user-full-name "Lee Hinman"
            user-mail-address "leehinman@fastmail.com"
            mail-host-address "fastmail.com"
            ;; no signature
            mu4e-compose-signature ";; Lee"
            ;; mu4e defaults
            mu4e-maildir       "~/.mail"
            ;; don't use unicode
            mu4e-use-fancy-chars nil
            ;; check for new messages every 90 seconds (3 min)
            mu4e-update-interval 90)

      ;; the default is html2text, and elinks does a slightly better option
      (when (executable-find "elinks")
        (setq mu4e-html2text-command (concat (executable-find "elinks") " -dump")))

      (add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)
      (use-package gnus-dired
        :config
        (progn
          ;; make the `gnus-dired-mail-buffers' function also work on
          ;; message-mode derived modes, such as mu4e-compose-mode
          (defun gnus-dired-mail-buffers ()
            "Return a list of active message buffers."
            (let (buffers)
              (save-current-buffer
                (dolist (buffer (buffer-list t))
                  (set-buffer buffer)
                  (when (and (derived-mode-p 'message-mode)
                             (null message-sent-message-via))
                    (push (buffer-name buffer) buffers))))
              (nreverse buffers)))

          (setq gnus-dired-mail-mode 'mu4e-user-agent)))

      ;; Vars used below
      (defvar mlh-mu4e-new-mail nil
        "Boolean to represent if there is new mail.")

      (defvar mlh-mu4e-url-location-list '()
        "Stores the location of each link in a mu4e view buffer")

      ;; This is also defined in init.el, but b/c ESK runs all files in the
      ;; user-dir before init.el it must also be defined here
      (defvar message-filter-regexp-list '()
        "regexps to filter matched msgs from the echo area when message is called")

      ;; Multi-account support
      (defun mlh-mu4e-current-account (&optional msg ignore-message-at-point)
        "Figure out what the current account is based on the message being
composed, the message under the point, or (optionally) the message
passed in. Also supports ignoring the msg at the point."
        (let ((cur-msg (or msg
                           mu4e-compose-parent-message
                           (and (not ignore-message-at-point)
                                (mu4e-message-at-point t)))))
          (when cur-msg
            (let ((maildir (mu4e-msg-field cur-msg :maildir)))
              (string-match "/\\(.*?\\)/" maildir)
              (match-string 1 maildir)))))

      (defun is-gmail-account? (acct)
        (if (or (equal "elastic" acct) (equal "gmail" acct))
            t nil))

      ;; my elisp is bad and I should feel bad
      (defun mlh-folder-for (acct g-folder-name other-folder-name)
        (if (or (equal "elastic" acct) (equal "gmail" acct))
            (format "/%s/[Gmail].%s" acct g-folder-name)
          (format "/%s/INBOX.%s" acct other-folder-name)))

      ;; Support for multiple accounts
      (setq mu4e-sent-folder   (lambda (msg)
                                 (mlh-folder-for (mlh-mu4e-current-account msg)
                                                 "Sent Mail" "Sent"))
            mu4e-drafts-folder (lambda (msg)
                                 (mlh-folder-for (mlh-mu4e-current-account msg)
                                                 "Drafts" "Drafts"))
            mu4e-trash-folder  (lambda (msg)
                                 (mlh-folder-for (mlh-mu4e-current-account msg)
                                                 "Trash" "Trash"))
            mu4e-refile-folder (lambda (msg)
                                 (mlh-folder-for (mlh-mu4e-current-account msg)
                                                 "All Mail" "Archive"))
            ;; The following list represents the account followed by key /
            ;; value pairs of vars to set when the account is chosen
            mlh-mu4e-account-alist
            '(("gmail"
               (user-mail-address   "matthew.hinman@gmail.com")
               (msmtp-account       "gmail")
               (mu4e-sent-messages-behavior delete))
              ("elastic"
               (user-mail-address   "lee@elastic.co")
               (msmtp-account       "elastic")
               (mu4e-sent-messages-behavior delete))
              ("fastmail"
               (user-mail-address   "leehinman@fastmail.com")
               (msmtp-account       "fastmail")
               (mu4e-sent-messages-behavior sent))
              )
            ;; These are used when mu4e checks for new messages
            mu4e-my-email-addresses
            (mapcar (lambda (acct) (cadr (assoc 'user-mail-address (cdr acct))))
                    mlh-mu4e-account-alist))

      (defun mlh-mu4e-choose-account ()
        "Prompt the user for an account to use"
        (completing-read (format "Compose with account: (%s) "
                                 (mapconcat #'(lambda (var) (car var))
                                            mlh-mu4e-account-alist "/"))
                         (mapcar #'(lambda (var) (car var))
                                 mlh-mu4e-account-alist)
                         nil t nil nil (caar mlh-mu4e-account-alist)))

      (defun mlh-mu4e-set-compose-account ()
        "Set various vars when composing a message. The vars to set are
  defined in `mlh-mu4e-account-alist'."
        (let* ((account (or (mlh-mu4e-current-account nil t)
                            (mlh-mu4e-choose-account)))
               (account-vars (cdr (assoc account mlh-mu4e-account-alist))))
          (when account-vars
            (mapc #'(lambda (var)
                      (set (car var) (cadr var)))
                  account-vars))))
      (add-hook 'mu4e-compose-pre-hook 'mlh-mu4e-set-compose-account)

      ;; Send mail through msmtp (setq stuff is below)
      (defun mlh-msmtp ()
        "Add some arguments to the msmtp call in order to route the message
  through the right account."
        (if (message-mail-p)
            (save-excursion
              (let* ((from (save-restriction (message-narrow-to-headers)
                                             (message-fetch-field "from"))))
                (setq message-sendmail-extra-arguments (list "-a" msmtp-account))))))
      (add-hook 'message-send-mail-hook 'mlh-msmtp)

      ;; Notification stuff
      (setq global-mode-string
            (if (string-match-p "mlh-mu4e-new-mail"
                                (prin1-to-string global-mode-string))
                global-mode-string
              (cons
               ;;         '(mlh-mu4e-new-mail "✉" "")
               '(mlh-mu4e-new-mail "Mail" "")
               global-mode-string)))

      (defun mlh-mu4e-unread-mail-query ()
        "The query to look for unread messages in all account INBOXes.
  More generally, change this code to affect not only when the
  envelope icon appears in the modeline, but also what shows up in
  mu4e under the Unread bookmark"
        (mapconcat
         (lambda (acct)
           (let ((name (car acct)))
             (format "%s"
                     (mapconcat (lambda (fmt)
                                  (format fmt name))
                                '("flag:unread AND maildir:/%s/INBOX")
                                " "))))
         mlh-mu4e-account-alist
         " OR "))

      (defun mlh-mu4e-new-mail-p ()
        "Predicate for if there is new mail or not"
        (not (eq 0 (string-to-number
                    (replace-regexp-in-string
                     "[ \t\n\r]" "" (shell-command-to-string
                                     (concat "mu find "
                                             (mlh-mu4e-unread-mail-query)
                                             " | wc -l")))))))

      (defun mlh-mu4e-notify ()
        "Function called to update the new-mail flag used in the mode-line"
        ;; This delay is to give emacs and mu a chance to have changed the
        ;; status of the mail in the index
        (run-with-idle-timer
         1 nil (lambda () (setq mlh-mu4e-new-mail (mlh-mu4e-new-mail-p)))))

      ;; I put a lot of effort (probably too much) into getting the
      ;; 'new mail' icon to go away by showing or hiding it:
      ;; - periodically (this runs even when mu4e isn't running)
      (setq mlh-mu4e-notify-timer (run-with-timer 0 500 'mlh-mu4e-notify))
      ;; - when the index is updated (this runs when mu4e is running)
      (add-hook 'mu4e-index-updated-hook 'mlh-mu4e-notify)
      ;; - after mail is processed (try to make the icon go away)
      (defadvice mu4e-mark-execute-all
          (after mu4e-mark-execute-all-notify activate) 'mlh-mu4e-notify)
      ;; - when a message is opened (try to make the icon go away)
      (add-hook 'mu4e-view-mode-hook 'mlh-mu4e-notify)
      ;; wrap lines
      (add-hook 'mu4e-view-mode-hook 'visual-line-mode)

      (defun mlh-mu4e-quit-and-notify ()
        "Bury the buffer and check for new messages. Mainly this is intended
  to clear out the envelope icon when done reading mail."
        (interactive)
        (bury-buffer)
        (mlh-mu4e-notify))

      ;; Make 'quit' just bury the buffer
      (define-key mu4e-headers-mode-map "q" 'mlh-mu4e-quit-and-notify)
      (define-key mu4e-main-mode-map "q" 'mlh-mu4e-quit-and-notify)

      ;; View mode stuff
      ;; Make it possible to tab between links
      (defun mlh-mu4e-populate-url-locations (&optional force)
        "Scans the view buffer for the links that mu4e has identified and
  notes their locations"
        (when (or (null mlh-mu4e-url-location-list) force)
          (make-local-variable 'mlh-mu4e-url-location-list)
          (let ((pt (next-single-property-change (point-min) 'face)))
            (while pt
              (when (equal (get-text-property pt 'face) 'mu4e-view-link-face)
                (add-to-list 'mlh-mu4e-url-location-list pt t))
              (setq pt (next-single-property-change pt 'face)))))
        mlh-mu4e-url-location-list)

      (defun mlh-mu4e-move-to-link (pt)
        (if pt
            (goto-char pt)
          (error "No link found.")))

      (defun mlh-mu4e-forward-url ()
        "Move the point to the beginning of the next link in the buffer"
        (interactive)
        (let* ((pt-list (mlh-mu4e-populate-url-locations)))
          (mlh-mu4e-move-to-link
           (or (some (lambda (pt) (when (> pt (point)) pt)) pt-list)
               (some (lambda (pt) (when (> pt (point-min)) pt)) pt-list)))))

      (defun mlh-mu4e-backward-url ()
        "Move the point to the beginning of the previous link in the buffer"
        (interactive)
        (let* ((pt-list (reverse (mlh-mu4e-populate-url-locations))))
          (mlh-mu4e-move-to-link
           (or (some (lambda (pt) (when (< pt (point)) pt)) pt-list)
               (some (lambda (pt) (when (< pt (point-max)) pt)) pt-list)))))

      (define-key mu4e-view-mode-map (kbd "TAB") 'mlh-mu4e-forward-url)
      (define-key mu4e-view-mode-map (kbd "<backtab>") 'mlh-mu4e-backward-url)

      ;; Misc
      ;; The bookmarks for the main screen
      (setq mu4e-bookmarks
            `((,(mlh-mu4e-unread-mail-query) "New messages"         ?b)
              ("maildir:/elastic/build"      "Build failures"       ?B)
              ("date:today..now"             "Today's messages"     ?t)
              ("date:7d..now"                "Last 7 days"          ?W)
              ("maildir:/fastmail/INBOX"     "Fastmail"             ?f)
              ("maildir:/elastic/INBOX"      "Elastic"              ?s)
              ("maildir:/gmail/INBOX"        "Gmail"                ?g)
              ("maildir:/elastic/INBOX OR maildir:/gmail/INBOX OR maildir:/fastmail/INBOX"
               "All Mail" ?a)
              ("maildir:/elastic/INBOX AND subject:Production AND from:support@elastic.co"
               "Production support" ?p)
              ("maildir:/elastic/INBOX AND subject:Development AND from:support@elastic.co"
               "Development support" ?d)))

      ;; start mu4e
      (mu4e~start)
      ;; check for unread messages
      (mlh-mu4e-notify)

      (add-to-list 'mu4e-view-actions
                   '("ViewInBrowser" . mu4e-action-view-in-browser) t)

      (define-key mu4e-view-mode-map (kbd "j") 'next-line)
      (define-key mu4e-view-mode-map (kbd "k") 'previous-line)

      (define-key mu4e-headers-mode-map (kbd "J") 'mu4e~headers-jump-to-maildir)
      (define-key mu4e-headers-mode-map (kbd "j") 'next-line)
      (define-key mu4e-headers-mode-map (kbd "k") 'previous-line)))

  (global-set-key (kbd "C-c m") 'mu4e))

elfeed (RSS reader)

(use-package elfeed
  :init
  (defun start-elfeed ()
    (interactive)
    (load-file "~/.emacs.d/elfeed-urls.el")
    (setq url-queue-timeout 30)
    (elfeed)))

ediff

Ediff is fantastic for looking through diffs

(use-package ediff
  :config
  (progn
    (setq
     ;; Always split nicely for wide screens
     ediff-split-window-function 'split-window-horizontally)))

fill-column-indicator

I have to hack something around this to make it fixed for org->html exports

(use-package fill-column-indicator
  :init
  (add-hook 'prog-mode-hook #'fci-mode)

  :config
  ;; fix for org -> html export
  (defun fci-mode-override-advice (&rest args))
  (use-package org)
  (advice-add 'org-html-fontify-code :around
              (lambda (fun &rest args)
                (advice-add 'fci-mode :override #'fci-mode-override-advice)
                (let ((result  (apply fun args)))
                  (advice-remove 'fci-mode #'fci-mode-override-advice)
                  result))))

smooth-scrolling

Smooth scrolling means when you hit C-n to go to the next line at the bottom of the page, instead of doing a page-down, it shifts down by a single line. The margin means that much space is kept between the cursor and the bottom of the buffer.

(use-package smooth-scrolling
  :defer t
  :config
  (setq smooth-scroll-margin 3
        scroll-margin 3
        scroll-conservatively 101
        scroll-preserve-screen-position t
        auto-window-vscroll nil))

paredit

Paredit for all the lisps.

(use-package paredit
  :commands paredit-mode
  :diminish "()"
  :config
  (bind-key "M-)" #'paredit-forward-slurp-sexp paredit-mode-map)
  (bind-key "C-(" #'paredit-forward-barf-sexp paredit-mode-map)
  (bind-key "C-)" #'paredit-forward-slurp-sexp paredit-mode-map)
  (bind-key ")" #'paredit-close-parenthesis paredit-mode-map)
  (bind-key "M-\"" #'my/other-window-backwards paredit-mode-map))

electric modes (pair/indent/layout)

Emacs finally has better support for automatically doing things like indentation and pairing parentheses. So, let's enable (some) of that

First, stuff for automatically inserting pairs of characters:

(electric-pair-mode 1)
(setq electric-pair-preserve-balance t
      electric-pair-delete-adjacent-pairs t
      electric-pair-open-newline-between-pairs nil)
(show-paren-mode 1)

Now, how about some auto-indentation:

(electric-indent-mode 1)

;; Ignore electric indentation for python and yaml
(defun electric-indent-ignore-mode (char)
  "Ignore electric indentation for python-mode"
  (if (or (equal major-mode 'python-mode)
          (equal major-mode 'yaml-mode))
      'no-indent
    nil))
(add-hook 'electric-indent-functions 'electric-indent-ignore-mode)

Finally, perhaps we want some automatic layout:

(electric-layout-mode 1)

smartparens

I'm on the fence about this, I did some CPU profiling and this was taking up a looooot of CPU, so I have disabled it and I'm using electric-pair-mode now.

So, paredit is great, however, it doesn't work for non-lisp modes. Smartparens works pretty well, so I use it everywhere paredit-mode doesn't work.

(use-package smartparens
  :disabled t
  :defer 5
  :diminish smartparens-mode
  :bind (("M-9" . sp-backward-sexp)
         ("M-0" . sp-forward-sexp))
  :init
  (show-smartparens-global-mode t)
  (add-hook 'prog-mode-hook #'turn-on-smartparens-mode)

  :config
  (add-to-list 'sp-sexp-suffix '(json-mode regex ""))
  (add-to-list 'sp-sexp-suffix '(es-mode regex ""))

  (use-package smartparens-config)

  ;; Remove the M-<backspace> binding that smartparens adds
  (let ((disabled '("M-<backspace>")))
    (setq sp-smartparens-bindings
          (cl-remove-if (lambda (key-command)
                          (member (car key-command) disabled))
                        sp-smartparens-bindings)))

  (define-key sp-keymap (kbd "C-(") 'sp-forward-barf-sexp)
  (define-key sp-keymap (kbd "C-)") 'sp-forward-slurp-sexp)
  (define-key sp-keymap (kbd "M-(") 'sp-forward-barf-sexp)
  (define-key sp-keymap (kbd "M-)") 'sp-forward-slurp-sexp)
  (define-key sp-keymap (kbd "C-M-f") 'sp-forward-sexp)
  (define-key sp-keymap (kbd "C-M-b") 'sp-backward-sexp)
  (define-key sp-keymap (kbd "C-M-f") 'sp-forward-sexp)
  (define-key sp-keymap (kbd "C-M-b") 'sp-backward-sexp)
  (define-key sp-keymap (kbd "C-M-d") 'sp-down-sexp)
  (define-key sp-keymap (kbd "C-M-a") 'sp-backward-down-sexp)
  (define-key sp-keymap (kbd "C-S-a") 'sp-beginning-of-sexp)
  (define-key sp-keymap (kbd "C-S-d") 'sp-end-of-sexp)
  (define-key sp-keymap (kbd "C-M-e") 'sp-up-sexp)
  (define-key emacs-lisp-mode-map (kbd ")") 'sp-up-sexp)
  (define-key sp-keymap (kbd "C-M-u") 'sp-backward-up-sexp)
  (define-key sp-keymap (kbd "C-M-t") 'sp-transpose-sexp)
  ;; (define-key sp-keymap (kbd "C-M-n") 'sp-next-sexp)
  ;; (define-key sp-keymap (kbd "C-M-p") 'sp-previous-sexp)
  (define-key sp-keymap (kbd "C-M-k") 'sp-kill-sexp)
  (define-key sp-keymap (kbd "C-M-w") 'sp-copy-sexp)
  (define-key sp-keymap (kbd "M-D") 'sp-splice-sexp)
  (define-key sp-keymap (kbd "C-]") 'sp-select-next-thing-exchange)
  (define-key sp-keymap (kbd "C-<left_bracket>") 'sp-select-previous-thing)
  (define-key sp-keymap (kbd "C-M-]") 'sp-select-next-thing)
  (define-key sp-keymap (kbd "M-F") 'sp-forward-symbol)
  (define-key sp-keymap (kbd "M-B") 'sp-backward-symbol)
  (define-key sp-keymap (kbd "H-t") 'sp-prefix-tag-object)
  (define-key sp-keymap (kbd "H-p") 'sp-prefix-pair-object)
  (define-key sp-keymap (kbd "H-s c") 'sp-convolute-sexp)
  (define-key sp-keymap (kbd "H-s a") 'sp-absorb-sexp)
  (define-key sp-keymap (kbd "H-s e") 'sp-emit-sexp)
  (define-key sp-keymap (kbd "H-s p") 'sp-add-to-previous-sexp)
  (define-key sp-keymap (kbd "H-s n") 'sp-add-to-next-sexp)
  (define-key sp-keymap (kbd "H-s j") 'sp-join-sexp)
  (define-key sp-keymap (kbd "H-s s") 'sp-split-sexp)

  (sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil)
  ;; Remove '' pairing in elisp because quoting is used a ton
  (sp-local-pair 'emacs-lisp-mode "'" nil :actions nil)

  (sp-with-modes '(html-mode sgml-mode)
    (sp-local-pair "<" ">"))

  (sp-with-modes sp--lisp-modes
    (sp-local-pair "(" nil :bind "C-(")))

golden-ratio

Automagically resizes the windows to be the golden ratio (1.618), nice when using a big font size and I need more eshell space

(use-package golden-ratio
  :diminish golden-ratio-mode
  :defer t
  :config
  (defun my/helm-alive-p ()
    (if (boundp 'helm-alive-p)
        (symbol-value 'helm-alive-p)))
  (add-to-list 'golden-ratio-exclude-modes #'messages-buffer-mode)
  (add-to-list 'golden-ratio-exclude-modes #'fundamental-mode)
  ;; Inhibit helm
  (add-to-list 'golden-ratio-inhibit-functions #'my/helm-alive-p))

flycheck

Pretty minimally configured, but awesome tool for most dynamic languages.

(use-package flycheck
  :defer 5
  :bind (("M-g M-n" . flycheck-next-error)
         ("M-g M-p" . flycheck-previous-error)
         ("M-g M-=" . flycheck-list-errors))
  :init (global-flycheck-mode)
  :diminish flycheck-mode
  :config
  (progn
    (setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc json-jsonlint json-python-json))
    (use-package flycheck-pos-tip
      :init (flycheck-pos-tip-mode))
    (use-package helm-flycheck
      :init (define-key flycheck-mode-map (kbd "C-c ! h") 'helm-flycheck))
    (use-package flycheck-haskell
      :init (add-hook 'flycheck-mode-hook #'flycheck-haskell-setup))))

ggtags

See: https://github.com/leoliu/ggtags

If on OSX, you'll need to:

brew install ctags
wget -c http://tamacom.com/global/global-6.3.1.tar.gz
tar zxvf global-6.3.1.tar.gz
cd global-6.3.1
./configure --prefix=/usr/local --with-exuberant-ctags=/usr/local/bin/ctags
make install

I also add this to my shell configuration:

export GTAGSCONF=/usr/local/share/gtags/gtags.conf
export GTAGSLABEL=ctags

In order to get this to work also

(defun my/setup-helm-gtags ()
  (interactive)
  ;; this variables must be set before load helm-gtags
  ;; you can change to any prefix key of your choice
  (setq helm-gtags-prefix-key "\C-cg")
  (setq helm-gtags-ignore-case t
        helm-gtags-auto-update t
        helm-gtags-use-input-at-cursor t
        helm-gtags-pulse-at-cursor t
        helm-gtags-suggested-key-mapping t)
  (use-package helm-gtags
    :init (helm-gtags-mode t)
    :diminish "")
  ;; key bindings
  (define-key helm-gtags-mode-map (kbd "M-s") 'helm-gtags-select)
  (define-key helm-gtags-mode-map (kbd "M-.") 'helm-gtags-dwim)
  (define-key helm-gtags-mode-map (kbd "M-,") 'helm-gtags-pop-stack)
  (define-key helm-gtags-mode-map (kbd "C-c <") 'helm-gtags-previous-history)
  (define-key helm-gtags-mode-map (kbd "C-c >") 'helm-gtags-next-history))

(defun my/setup-ggtags ()
  (interactive)
  (ggtags-mode 1)
  ;; turn on eldoc with ggtags
  (setq-local eldoc-documentation-function #'ggtags-eldoc-function)
  ;; add ggtags to the hippie completion
  (setq-local hippie-expand-try-functions-list
              (cons 'ggtags-try-complete-tag
                    hippie-expand-try-functions-list))
  ;; use helm for completion
  (setq ggtags-completing-read-function nil))

(use-package ggtags
  :defer t
  :init
  (progn
    (add-hook 'c-mode-common-hook
              (lambda ()
                (when (derived-mode-p 'c-mode 'c++-mode 'java-mode 'asm-mode)
                  (my/setup-semantic-mode)
                  (my/setup-helm-gtags) ;; helm-gtags
                  ;;(my/setup-ggtags) ;; regular gtags
                  )))))

expand-region

Great for selecting the inside of Elasticsearch queries

(use-package expand-region
  :defer t
  :bind (("C-c e" . er/expand-region)
         ("C-M-@" . er/contract-region)))

quick-preview

Provides a quick preview of the thing at point

(use-package quick-preview
  :init
  (progn
    (global-set-key (kbd "C-c q") 'quick-preview-at-point)
    (define-key dired-mode-map (kbd "Q") 'quick-preview-at-point)))

with-editor

Sets up the with-editor package so things that invoke $EDITOR will use the current emacs if I'm already inside of emacs

(use-package with-editor
  :init
  (progn
    (add-hook 'shell-mode-hook  'with-editor-export-editor)
    (add-hook 'eshell-mode-hook 'with-editor-export-editor)))

magit

I use M-g M-g everywhere to go directly to Magit.

(use-package magit
  :bind (("M-g M-g" . magit-status)
         ("C-x g" . magit-status))
  :init (add-hook 'magit-mode-hook 'hl-line-mode)
  :config
  (setenv "GIT_PAGER" "")
  (if (file-exists-p  "/usr/local/bin/emacsclient")
      (setq magit-emacsclient-executable "/usr/local/bin/emacsclient")
    (setq magit-emacsclient-executable (executable-find "emacsclient")))
  (defun my/magit-browse ()
    "Browse to the project's github URL, if available"
    (interactive)
    (let ((url (with-temp-buffer
                 (unless (zerop (call-process-shell-command
                                 "git remote -v" nil t))
                   (error "Failed: 'git remote -v'"))
                 (goto-char (point-min))
                 (when (re-search-forward
                        "github\\.com[:/]\\(.+?\\)\\.git" nil t)
                   (format "https://github.com/%s" (match-string 1))))))
      (unless url
        (error "Can't find repository URL"))
      (browse-url url)))

  (define-key magit-mode-map (kbd "C-c C-b") #'my/magit-browse)
  ;; Magit has its own binding, so re-bind them
  (bind-key "M-1" #'my/create-or-switch-to-eshell-1 magit-mode-map)
  (bind-key "M-2" #'my/create-or-switch-to-eshell-2 magit-mode-map)
  (bind-key "M-3" #'my/create-or-switch-to-eshell-3 magit-mode-map)
  (bind-key "M-4" #'my/create-or-switch-to-eshell-4 magit-mode-map))

magit-gh-pulls

Pull requests! Directly from the Magit buffer! Hooray!

(use-package magit-gh-pulls
  :init (add-hook 'magit-mode-hook #'turn-on-magit-gh-pulls))

projectile

Per-project navigation

(use-package projectile
  :defer 5
  :commands projectile-global-mode
  :diminish projectile-mode
  :config
  (bind-key "C-c p b" #'projectile-switch-to-buffer #'projectile-command-map)
  (bind-key "C-c p K" #'projectile-kill-buffers #'projectile-command-map)

  ;; global ignores
  (add-to-list 'projectile-globally-ignored-files ".tern-port")
  (add-to-list 'projectile-globally-ignored-files "GTAGS")
  (add-to-list 'projectile-globally-ignored-files "GPATH")
  (add-to-list 'projectile-globally-ignored-files "GRTAGS")
  (add-to-list 'projectile-globally-ignored-files "GSYMS")
  (add-to-list 'projectile-globally-ignored-files ".DS_Store")
  ;; always ignore .class files
  (add-to-list 'projectile-globally-ignored-file-suffixes ".class")
  (use-package helm-projectile
    :init
    (use-package grep) ;; required for helm-ag to work properly
    (setq projectile-completion-system 'helm)
    ;; no fuzziness for projectile-helm
    (setq helm-projectile-fuzzy-match nil)
    (helm-projectile-on))
  (projectile-global-mode))

prodigy

I basically use this to start up ES when I need to test something really quickly

I have been trying out esvm for this lately also, check out my ESVM configuration elsewhere in my dotfiles

So I configure prodigy like so:

(use-package prodigy
  :defer t
  :bind ("C-x P" . prodigy)
  :config
  (progn
    (prodigy-define-service
      :name "ES 2.x, 3 nodes"
      :cwd "~/ies/"
      :command "esvm"
      :args '("3node")
      :tags '(work test es)
      :port 9200)

    (prodigy-define-service
      :name "ES 2.x, 2 nodes"
      :cwd "~/ies/"
      :command "esvm"
      :args '("2node")
      :tags '(work test es)
      :port 9200)

    (prodigy-define-service
      :name "ES master branch"
      :cwd "~/ies/"
      :command "esvm"
      :args '("master")
      :tags '(work test es)
      :port 9200)

    (prodigy-define-service
      :name "Kibana 4.2.0"
      :cwd "~/ies/kibana-4.2.0-linux-x64"
      :command "~/ies/kibana-4.2.0-linux-x64/bin/kibana"
      :tags '(work kibana)
      :port 5601)

    (prodigy-define-service
      :name "Kibana 4.3.0"
      :cwd "~/ies/kibana-4.3.0-linux-x64"
      :command "~/ies/kibana-4.3.0-linux-x64/bin/kibana"
      :tags '(work kibana)
      :port 5601)

    (prodigy-define-service
      :name "Elasticsearch 1.7.3"
      :cwd "~/ies/elasticsearch-1.7.3"
      :command "~/ies/elasticsearch-1.7.3/bin/elasticsearch"
      :tags '(work test es)
      :port 9200)
    (prodigy-define-service
      :name "Elasticsearch 2.0.0"
      :cwd "~/ies/elasticsearch-2.0.0"
      :command "~/ies/elasticsearch-2.0.0/bin/elasticsearch"
      :tags '(work test es)
      :port 9200)
    (prodigy-define-service
      :name "Elasticsearch 2.1.0"
      :cwd "~/ies/elasticsearch-2.1.0"
      :command "~/ies/elasticsearch-2.1.0/bin/elasticsearch"
      :tags '(work test es)
      :port 9200)))

git-gutter

Only enabled in a few modes, but quite useful, as well as the C-x n and C-x p bindings.

(use-package git-gutter
  :defer t
  :bind (("C-x =" . git-gutter:popup-hunk)
         ("C-c P" . git-gutter:previous-hunk)
         ("C-c N" . git-gutter:next-hunk)
         ("C-x p" . git-gutter:previous-hunk)
         ("C-x n" . git-gutter:next-hunk)
         ("C-c G" . git-gutter:popup-hunk))
  :diminish ""
  :init
  (add-hook 'prog-mode-hook 'git-gutter-mode)
  (add-hook 'org-mode-hook 'git-gutter-mode))

anzu

Anzu shows the number of search hits in the modeline, which is handy.

It can also be used for a "refactor-like" thing similar to query-replace.

(use-package anzu
  :defer t
  :bind ("M-%" . anzu-query-replace-regexp)
  :config
  (progn
    (use-package thingatpt)
    (setq anzu-mode-lighter "")
    (set-face-attribute 'anzu-mode-line nil :foreground "yellow")))

(add-hook 'prog-mode-hook #'anzu-mode)
(add-hook 'org-mode-hook #'anzu-mode)

Also, add a thing for yanking the entire symbol into the query while searching:

(defun isearch-yank-symbol ()
  (interactive)
  (isearch-yank-internal (lambda () (forward-symbol 1) (point))))

(define-key isearch-mode-map (kbd "C-M-w") 'isearch-yank-symbol)

helm-swoop

Best way to search in a buffer ever

(use-package helm-swoop
  :bind (("M-i" . helm-swoop)
         ("M-I" . helm-swoop-back-to-last-point)
         ("C-c M-i" . helm-multi-swoop))
  :config
  ;; When doing isearch, hand the word over to helm-swoop
  (define-key isearch-mode-map (kbd "M-i") 'helm-swoop-from-isearch)
  ;; From helm-swoop to helm-multi-swoop-all
  (define-key helm-swoop-map (kbd "M-i") 'helm-multi-swoop-all-from-helm-swoop)
  ;; Save buffer when helm-multi-swoop-edit complete
  (setq helm-multi-swoop-edit-save t
        ;; If this value is t, split window inside the current window
        helm-swoop-split-with-multiple-windows t
        ;; Split direcion. 'split-window-vertically or 'split-window-horizontally
        helm-swoop-split-direction 'split-window-vertically
        ;; If nil, you can slightly boost invoke speed in exchange for text color
        helm-swoop-speed-or-color nil))

helm-descbinds

(use-package helm-descbinds
  :bind ("C-h b" . helm-descbinds)
  :init (fset 'describe-bindings 'helm-descbinds)
  :config (require 'helm-config))

helm

There are many helm things. I use it a lot.

A lot of things are taken from taken from https://tuhdo.github.io/helm-intro.html

(use-package helm-config
  :demand t
  :diminish helm-mode
  :bind
  (("C-M-z" . helm-resume)
   ("C-x C-f" . helm-find-files)
   ("C-x C-r" . helm-mini)
   ("C-x M-o" . helm-occur)
   ("M-y" . helm-show-kill-ring)
   ("C-h a" . helm-apropos)
   ("C-h m" . helm-man-woman)
   ("C-h SPC" . helm-all-mark-rings)
   ("M-g >" . helm-ag-this-file)
   ("M-g ," . helm-ag-pop-stack)
   ("M-g ." . helm-do-grep)
   ("C-x C-i" . helm-semantic-or-imenu)
   ("M-x" . helm-M-x)
   ("C-x C-b" . helm-buffers-list)
   ("C-x C-r" . helm-mini)
   ("C-x b" . helm-mini)
   ("C-h t" . helm-world-time))
  :config
  (use-package helm-files
    :config (setq helm-ff-file-compressed-list '("gz" "bz2" "zip" "tgz" "xz")))
  (use-package helm-commands)
  (use-package helm-buffers)
  (use-package helm-mode
    :diminish helm-mode
    :init (helm-mode 1))
  (use-package helm-ls-git
    :bind ("C-x C-d" . helm-browse-project))
  (use-package helm-grep
    :config
    (progn
      (define-key helm-grep-mode-map (kbd "<return>")  'helm-grep-mode-jump-other-window)
      (define-key helm-grep-mode-map (kbd "n")  'helm-grep-mode-jump-other-window-forward)
      (define-key helm-grep-mode-map (kbd "p")  'helm-grep-mode-jump-other-window-backward)))
  (use-package helm-man)
  (use-package helm-misc)
  (use-package helm-aliases)
  (use-package helm-elisp)
  (use-package helm-imenu)
  (use-package helm-semantic)
  (use-package helm-ring)
  (use-package helm-bookmark
    :bind ("C-x M-b" . helm-bookmarks))
  (use-package helm-projectile
    :bind (("C-x f" . helm-projectile)
           ("C-c p f" . helm-projectile-find-file)
           ("C-c p s" . helm-projectile-switch-project)))

  (global-set-key (kbd "C-c h") 'helm-command-prefix)
  (global-unset-key (kbd "C-x c"))

  ;; Via: https://www.reddit.com/r/emacs/comments/3asbyn/new_and_very_useful_helm_feature_enter_search/
  (setq helm-echo-input-in-header-line t)
  (defun helm-hide-minibuffer-maybe ()
    (when (with-helm-buffer helm-echo-input-in-header-line)
      (let ((ov (make-overlay (point-min) (point-max) nil nil t)))
        (overlay-put ov 'window (selected-window))
        (overlay-put ov 'face (let ((bg-color (face-background 'default nil)))
                                `(:background ,bg-color :foreground ,bg-color)))
        (setq-local cursor-type nil))))
  (add-hook 'helm-minibuffer-set-up-hook 'helm-hide-minibuffer-maybe)

  (setq ;; truncate long lines in helm completion
        helm-truncate-lines t
        ;; may be overridden if 'ggrep' is in path (see below)
        helm-grep-default-command
        "grep -a -d skip %e -n%cH -e %p %f"
        helm-grep-default-recurse-command
        "grep -a -d recurse %e -n%cH -e %p %f"
        ;; do not display invisible candidates
        helm-quick-update t
        ;; be idle for this many seconds, before updating in delayed sources.
        ;; helm-idle-delay 0.01
        ;; helm-input-idle-delay 0.01
        ;; wider buffer name in helm-buffers-list
        helm-buffer-max-length 25 ;; default is 20
        ;; open helm buffer in another window
        helm-split-window-default-side 'other
        ;; open helm buffer inside current window, don't occupy whole other window
        helm-split-window-in-side-p t
        ;; limit the number of displayed canidates
        ;; helm-candidate-number-limit 200
        ;; don't use recentf stuff in helm-ff, I use C-x C-r for this
        helm-ff-file-name-history-use-recentf nil
        ;; move to end or beginning of source when reaching top or bottom
        ;; of source
        helm-move-to-line-cycle-in-source t
        ;; don't display the header line
        helm-display-header-line nil
        ;; fuzzy matching
        helm-recentf-fuzzy-match t
        helm-locate-fuzzy-match nil ;; locate fuzzy is worthless
        helm-M-x-fuzzy-match t
        helm-buffers-fuzzy-matching t
        helm-semantic-fuzzy-match t
        helm-apropos-fuzzy-match t
        helm-imenu-fuzzy-match t
        helm-lisp-fuzzy-completion t
        helm-completion-in-region-fuzzy-match t
        ;; Here are the things helm-mini shows, I add `helm-source-bookmarks'
        ;; here to the regular default list
        helm-mini-default-sources '(helm-source-buffers-list
                                    helm-source-recentf
                                    helm-source-bookmarks
                                    helm-source-buffer-not-found))

  ;; List of times to show in helm-world-time
  (setq display-time-world-list '(("PST8PDT" "Los Altos")
                                  ("America/Denver" "Denver")
                                  ("EST5EDT" "Boston")
                                  ("UTC" "UTC")
                                  ("Europe/London" "London")
                                  ("Europe/Amsterdam" "Amsterdam")
                                  ("Asia/Bangkok" "Bangkok")
                                  ("Asia/Tokyo" "Tokyo")
                                  ("Australia/Sydney" "Sydney")))

  (define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action) ; rebind tab to do persistent action
  (define-key helm-map (kbd "C-i") 'helm-execute-persistent-action) ; make TAB works in terminal
  (define-key helm-map (kbd "C-z")  'helm-select-action) ; list actions using C-z

  (define-key helm-map (kbd "C-p")   'helm-previous-line)
  (define-key helm-map (kbd "C-n")   'helm-next-line)
  (define-key helm-map (kbd "C-M-n") 'helm-next-source)
  (define-key helm-map (kbd "C-M-p") 'helm-previous-source)
  (define-key helm-map (kbd "M-N")   'helm-next-source)
  (define-key helm-map (kbd "M-P")   'helm-previous-source)
  ;; The normal binding is C-c h M-g s which is insane
  (global-set-key (kbd "C-c h g")    'helm-do-grep)
  (global-set-key (kbd "C-c h a")    'helm-do-ag)

  (when (executable-find "curl")
    (setq helm-google-suggest-use-curl-p t))

  ;; ggrep is gnu grep on OSX
  (when (executable-find "ggrep")
    (setq helm-grep-default-command
          "ggrep -a -d skip %e -n%cH -e %p %f"
          helm-grep-default-recurse-command
          "ggrep -a -d recurse %e -n%cH -e %p %f"))

  ;; helm-mini instead of recentf
  (define-key 'help-command (kbd "C-f") 'helm-apropos)
  (define-key 'help-command (kbd "r") 'helm-info-emacs)

  (defvar helm-httpstatus-source
    '((name . "HTTP STATUS")
      (candidates . (("100 Continue") ("101 Switching Protocols")
                     ("102 Processing") ("200 OK")
                     ("201 Created") ("202 Accepted")
                     ("203 Non-Authoritative Information") ("204 No Content")
                     ("205 Reset Content") ("206 Partial Content")
                     ("207 Multi-Status") ("208 Already Reported")
                     ("300 Multiple Choices") ("301 Moved Permanently")
                     ("302 Found") ("303 See Other")
                     ("304 Not Modified") ("305 Use Proxy")
                     ("307 Temporary Redirect") ("400 Bad Request")
                     ("401 Unauthorized") ("402 Payment Required")
                     ("403 Forbidden") ("404 Not Found")
                     ("405 Method Not Allowed") ("406 Not Acceptable")
                     ("407 Proxy Authentication Required") ("408 Request Timeout")
                     ("409 Conflict") ("410 Gone")
                     ("411 Length Required") ("412 Precondition Failed")
                     ("413 Request Entity Too Large")
                     ("414 Request-URI Too Large")
                     ("415 Unsupported Media Type")
                     ("416 Request Range Not Satisfiable")
                     ("417 Expectation Failed") ("418 I'm a teapot")
                     ("422 Unprocessable Entity") ("423 Locked")
                     ("424 Failed Dependency") ("425 No code")
                     ("426 Upgrade Required") ("428 Precondition Required")
                     ("429 Too Many Requests")
                     ("431 Request Header Fields Too Large")
                     ("449 Retry with") ("500 Internal Server Error")
                     ("501 Not Implemented") ("502 Bad Gateway")
                     ("503 Service Unavailable") ("504 Gateway Timeout")
                     ("505 HTTP Version Not Supported")
                     ("506 Variant Also Negotiates")
                     ("507 Insufficient Storage") ("509 Bandwidth Limit Exceeded")
                     ("510 Not Extended")
                     ("511 Network Authentication Required")))
      (action . message)))

  (defvar helm-clj-http-source
    '((name . "clj-http options")
      (candidates
       .
       ((":accept - keyword for content type to accept")
        (":as - output coercion: :json, :json-string-keys, :clojure, :stream, :auto or string")
        (":basic-auth - string or vector of basic auth creds")
        (":body - body of request")
        (":body-encoding - encoding type for body string")
        (":client-params - apache http client params")
        (":coerce - when to coerce response body: :always, :unexceptional, :exceptional")
        (":conn-timeout - timeout for connection")
        (":connection-manager - connection pooling manager")
        (":content-type - content-type for request")
        (":cookie-store - CookieStore object to store/retrieve cookies")
        (":cookies - map of cookie name to cookie map")
        (":debug - boolean to print info to stdout")
        (":debug-body - boolean to print body debug info to stdout")
        (":decode-body-headers - automatically decode body headers")
        (":decompress-body - whether to decompress body automatically")
        (":digest-auth - vector of digest authentication")
        (":follow-redirects - boolean whether to follow HTTP redirects")
        (":form-params - map of form parameters to send")
        (":headers - map of headers")
        (":ignore-unknown-host? - whether to ignore inability to resolve host")
        (":insecure? - boolean whether to accept invalid SSL certs")
        (":json-opts - map of json options to be used for form params")
        (":keystore - file path to SSL keystore")
        (":keystore-pass - password for keystore")
        (":keystore-type - type of SSL keystore")
        (":length - manually specified length of body")
        (":max-redirects - maximum number of redirects to follow")
        (":multipart - vector of multipart options")
        (":oauth-token - oauth token")
        (":proxy-host - hostname of proxy server")
        (":proxy-ignore-hosts - set of hosts to ignore for proxy")
        (":proxy-post - port for proxy server")
        (":query-params - map of query parameters")
        (":raw-headers - boolean whether to return raw headers with response")
        (":response-interceptor - function called for each redirect")
        (":retry-handler - function to handle HTTP retries on IOException")
        (":save-request? - boolean to return original request with response")
        (":socket-timeout - timeout for establishing socket")
        (":throw-entire-message? - whether to throw the entire response on errors")
        (":throw-exceptions - boolean whether to throw exceptions on 5xx & 4xx")
        (":trust-store - file path to trust store")
        (":trust-store-pass - password for trust store")
        (":trust-store-type - type of trust store")))
      (action . message)))

  (defun helm-httpstatus ()
    (interactive)
    (helm-other-buffer '(helm-httpstatus-source) "*helm httpstatus*"))

  (defun helm-clj-http ()
    (interactive)
    (helm-other-buffer '(helm-clj-http-source) "*helm clj-http flags*"))

  (global-set-key (kbd "C-c M-C-h") 'helm-httpstatus)
  (global-set-key (kbd "C-c M-h") 'helm-clj-http))

helm-c-yasnippet

This allows me to get a listing of all the snippets for the mode and look through them with helm

(use-package helm-c-yasnippet
  :bind
  (("M-=" . helm-yas-complete)))

hydra

I'm not really a huge fan of hydra, mostly because I don't need popups for every little thing under the sun. I'm only using it for my toggle map right now, so I can remember what all I can toggle.

(use-package hydra
  :init
  (progn
    (defhydra hydra-toggle-map nil
      "
^Toggle^
^^^^^^^^--------------------
_d_: debug-on-error
_D_: debug-on-quit
_f_: auto-fill-mode
_l_: toggle-truncate-lines
_h_: hl-line-mode
_r_: read-only-mode
_v_: viewing-mode
_n_: narrow-or-widen-dwim
_g_: golden-ratio-mode
_q_: quit
"
      ("d" toggle-debug-on-error :exit t)
      ("D" toggle-debug-on-quit :exit t)
      ("g" golden-ratio-mode :exit t)
      ("f" auto-fill-mode :exit t)
      ("l" toggle-truncate-lines :exit t)
      ("r" read-only-mode :exit t)
      ("h" hl-line-mode :exit t)
      ("v" my/turn-on-viewing-mode :exit t)
      ("n" my/narrow-or-widen-dwim :exit t)
      ("q" nil :exit t))

    (global-set-key (kbd "C-x t") 'hydra-toggle-map/body)

    ;; Jump between errors in a buffer:
    (defhydra hydra-next-error (global-map "C-x")
      "next-error"
      ("`" next-error "next")
      ("j" next-error "next" :bind nil)
      ("n" next-error "next" :bind nil)
      ("k" previous-error "previous" :bind nil)
      ("p" previous-error "previous" :bind nil)
      ("l" flycheck-list-errors "list-errors" :exit t))))

yasnippet

Snippets for coding, loading while idle so startup is faster.

(use-package yasnippet
  :defer t
  :diminish yas-minor-mode
  :commands (yas-expand yas-minor-mode)
  :functions (yas-guess-snippet-directories yas-table-name)
  :defines (yas-guessed-modes)
  :mode ("/\\.emacs\\.d/snippets/" . snippet-mode)
  :config
  (yas-load-directory "~/.emacs.d/snippets/")
  (yas-global-mode 1)
  (bind-key "C-i" #'yas-next-field-or-maybe-expand yas-keymap))

markdown-mode

Various markdown-related settings. Not much here.

(use-package markdown-mode
  :load-path "site-lisp/markdown-mode"
  :init (add-hook 'markdown-mode-hook #'whitespace-mode)
  :mode (("\\`README\\.md\\'" . gfm-mode)
         ("github\\.com.*\\.txt\\'" . gfm-mode)
         ("\\.md\\'"          . markdown-mode)
         ("\\.markdown\\'"    . markdown-mode)))

log4j-mode

Need hl-line-mode for this

(use-package log4j-mode
  :init (add-hook #'log4j-mode-hook #'my/turn-on-viewing-mode))

bookmark+

Extended bookmarks, which I've started used for dired buffers and so on

(use-package bookmark+
  :defer 10
  :config
  (progn
    (setq bookmark-version-control t
          ;; auto-save bookmarks
          bookmark-save-flag 1)))

auto completion (company)

Standard auto-completion configuration with company-mode

(use-package company
  :defer t
  :diminish company-mode
  :bind ("C-." . company-complete)
  :init (add-hook #'prog-mode-hook #'company-mode)
  :config
  (progn
    (setq company-idle-delay 0.4
          ;; min prefix of 3 chars
          company-minimum-prefix-length 3
          company-selection-wrap-around t
          company-show-numbers t
          company-dabbrev-downcase nil
          company-transformers '(company-sort-by-occurrence))
    (bind-keys :map company-active-map
               ("C-n" . company-select-next)
               ("C-p" . company-select-previous)
               ("C-d" . company-show-doc-buffer)
               ("<tab>" . company-complete))))

smart-tab

Used smart-tab to complete everywhere except for ERC, shell and mu4e.

(use-package smart-tab
  :defer t
  :diminish ""
  :init (global-smart-tab-mode 1)
  :config
  (progn
    (add-to-list 'smart-tab-disabled-major-modes 'mu4e-compose-mode)
    (add-to-list 'smart-tab-disabled-major-modes 'erc-mode)
    (add-to-list 'smart-tab-disabled-major-modes 'shell-mode)))

shrink-whitespace

Usually M-SPC is bound to just-one-space, but shrink-whitespace is actually a better alternative because it can shrink space between lines.

Thanks to http://pragmaticemacs.com/emacs/delete-blank-lines-and-shrink-whitespace/ for the link to this package.

(use-package shrink-whitespace
  :bind (("M-SPC" . shrink-whitespace)
         ("M-S-SPC" . shrink-whitespace)))

undo-tree

Undo-tree allows me to have sane undo defaults, as well as being able to visualize it in ascii art if needed.

(use-package undo-tree
  :init (global-undo-tree-mode t)
  :defer t
  :diminish ""
  :config
  (progn
    (define-key undo-tree-map (kbd "C-x u") 'undo-tree-visualize)
    (define-key undo-tree-map (kbd "C-/") 'undo-tree-undo)))

popwin

Popwin handles little popup windows at the bottom of the screen, which is very helpful for documentation buffers and so on.

(use-package popwin
  :commands popwin-mode
  :init (popwin-mode 1)
  :config
  (progn
    (defvar popwin:special-display-config-backup popwin:special-display-config)
    (setq display-buffer-function 'popwin:display-buffer)

    ;; basic
    (push '("*Help*" :stick t :noselect t) popwin:special-display-config)
    (push '("*Pp Eval Output*" :stick t) popwin:special-display-config)

    ;; compilation
    (push '(compilation-mode :stick t :width 0.4) popwin:special-display-config)

    ;; magit
    (push '("*magit-process*" :stick t) popwin:special-display-config)

    ;; quickrun
    (push '("*quickrun*" :stick t) popwin:special-display-config)

    ;; dictionaly
    (push '("*dict*" :stick t) popwin:special-display-config)
    (push '("*sdic*" :stick t) popwin:special-display-config)

    ;; popwin for slime
    (push '(slime-repl-mode :stick t) popwin:special-display-config)

    ;; man
    (push '(Man-mode :stick t :height 20) popwin:special-display-config)

    ;; Elisp
    (push '("*ielm*" :stick t) popwin:special-display-config)
    (push '("*eshell pop*" :stick t) popwin:special-display-config)

    ;; pry
    (push '(inf-ruby-mode :stick t :height 20) popwin:special-display-config)

    ;; python
    (push '("*Python*"   :stick t) popwin:special-display-config)
    (push '("*Python Help*" :stick t :height 20) popwin:special-display-config)
    (push '("*jedi:doc*" :stick t :noselect t) popwin:special-display-config)

    ;; Haskell
    (push '("*haskell*" :stick t) popwin:special-display-config)
    (push '("*GHC Info*") popwin:special-display-config)

    ;; sgit
    (push '("*sgit*" :position right :width 0.5 :stick t)
          popwin:special-display-config)

    ;; git-gutter
    (push '("*git-gutter:diff*" :width 0.5 :stick t)
          popwin:special-display-config)

    ;; es-results-mode
    (push '(es-result-mode :stick t :width 0.5)
          popwin:special-display-config)

    ;; direx
    (push '(direx:direx-mode :position left :width 40 :dedicated t)
          popwin:special-display-config)

    (push '("*Occur*" :stick t) popwin:special-display-config)

    ;; prodigy
    (push '("*prodigy*" :stick t) popwin:special-display-config)

    ;; malabar-mode
    (push '("*Malabar Compilation*" :stick t :height 30)
          popwin:special-display-config)

    ;; org-mode
    (push '("*Org tags*" :stick t :height 30)
          popwin:special-display-config)

    ;; Completions
    (push '("*Completions*" :stick t :noselect t) popwin:special-display-config)

    ;; ggtags
    (push '("*ggtags-global*" :stick t :noselect t :height 30) popwin:special-display-config)

    ;; async shell commands
    (push '("*Async Shell Command*" :stick t) popwin:special-display-config)

    (defun my/popup-downloads ()
      "Pop up the downloads buffer (4th eshell buffer for me"
      (interactive)
      (when (not (get-buffer "*eshell downloads*"))
        (let ((eshell-buffer-name "*eshell downloads*"))
          (eshell)))
      (popwin:popup-buffer "*eshell downloads*"))

    ;; eshell 4 is always my "download stuff" buffer
    (global-set-key (kbd "C-x M-d") #'my/popup-downloads)
    (global-set-key (kbd "C-h e") 'popwin:messages)))

paren-face

Paren-face adds a face for parentheses, which is used by themes to darken the parens.

(use-package paren-face
  :init (global-paren-face-mode))

ido-mode

First, turn on ido-mode everywhere, and if flx-ido is installed, enable it everywhere as well.

Ido gives really nice completion while flx-ido makes everything more flexible instead of rigid completions

(use-package ido
  :config
  (use-package ido-ubiquitous)
  (use-package flx-ido
    :init (flx-ido-mode 1)
    :config (setq ido-use-faces nil))
  (use-package ido-vertical-mode
    :init (ido-vertical-mode t))
  (setq ido-use-virtual-buffers nil
        ;; this setting causes weird TRAMP connections, don't set it!
        ;;ido-enable-tramp-completion nil
        ido-enable-flex-matching t
        ido-auto-merge-work-directories-length nil
        ido-create-new-buffer 'always
        ido-use-filename-at-point 'guess
        ido-max-prospects 10))

multiple-cursors

Mulitple cursors is like rectangular selection/insertion but on steroids

(use-package multiple-cursors
  :bind (("C-S-c C-S-c" . mc/edit-lines)
         ("C->" . mc/mark-next-like-this)
         ("C-<" . mc/mark-previous-like-this)
         ("C-c C-<" . mc/mark-all-like-this)))

twittering-mode

Load up twittering mode, but defer it since I'm probably not loading emacs to immediately use Twitter :P

(use-package twittering-mode
  :config
  (setq twittering-icon-mode t
        twittering-use-master-password t
        twittering-timer-interval 900
        ;; Start up with home and "emacs" search
        twittering-initial-timeline-spec-string
         '("(:home+@)"
           "(:search/emacs/)")))

smex

Smex is IDO, but for M-x. This is disabled because I'm using helm for M-x now.

(use-package smex
  :disabled t
  :bind (("M-x" . smex)
         ("M-X" . smex-major-mode-commands)))

iedit

Iedit is interactive edit, where if you are on a word and you enter iedit-mode, you're basically editing every instance of that word/variable in the buffer.

(use-package iedit
  :bind ("C-;" . iedit-mode))

beacon

Beacon flashes the cursor whenever you adjust position

(use-package beacon
  :disabled t
  :diminish beacon-mode
  :init (beacon-mode 1))

eyebrowse

Eyebrowse is a great package for workspaces in Emacs.

(use-package eyebrowse
  :diminish eyebrowse-mode
  :init
  (progn
    (defun my/create-eyebrowse-setup ()
      (interactive)
      "Create a default window config, if none is present"
      (when (not (eyebrowse--window-config-present-p 2))
        ;; there's probably a better way to do this, creating two workspaces
        (eyebrowse-switch-to-window-config-2)
        (eyebrowse-switch-to-window-config-1)))
    (setq eyebrowse-wrap-around t
          eyebrowse-new-workspace t)
    (eyebrowse-mode 1)
    (global-set-key (kbd "C-'") 'eyebrowse-next-window-config)
    (add-hook 'after-init-hook #'my/create-eyebrowse-setup)))

smartscan

Uses M-n and M-p to jump between the same variable in multiple places

(use-package smartscan
  :init (add-hook #'prog-mode-hook #'smartscan-mode)
  :config
  (bind-key "M-'" #'other-window smartscan-map))

hideshow

Kind of like Vim's folding, but manually done right now.

I'm in the process of trying out Origami as a replacement, since it's a bit easier on the brain and has nicer functions (supposedly), so this is disabled for now.

(use-package hideshow
  :bind (("C-c TAB" . hs-toggle-hiding)
         ("C-\\" . hs-toggle-hiding)
         ("M-\\" . hs-hide-all)
         ("M-+" . hs-show-all))
  :init (add-hook #'prog-mode-hook #'hs-minor-mode)
  :diminish hs-minor-mode
  :config
  (defvar hs-special-modes-alist
    (mapcar 'purecopy
            '((c-mode "{" "}" "/[*/]" nil nil)
              (c++-mode "{" "}" "/[*/]" nil nil)
              (bibtex-mode ("@\\S(*\\(\\s(\\)" 1))
              (java-mode "{" "}" "/[*/]" nil nil)
              (js-mode "{" "}" "/[*/]" nil)
              (javascript-mode  "{" "}" "/[*/]" nil)))))

symon

Symon is a neat little system monitor that shows info in the modeline when Emacs is inactive.

(use-package symon
  :if window-system
  :disabled t
  :init
  (setq symon-refresh-rate 2
        symon-delay 5)
  (symon-mode 1)
  :config
  (setq symon-sparkline-type 'bounded))

abbrev

I use abbrev-mode to automatically correct misspellings I usually make. In this case, I misspell elasticsearch all the time, and I type it all the time, so it's helpful to auto-correct.

(use-package abbrev
  :disabled t
  :diminish abbrev-mode
  :init (add-hook 'prog-mode-hook #'abbrev-mode)
  :defer 30
  :config
  (progn
    (define-key ctl-x-map "\M-a" 'my/ispell-word-then-abbrev)

    (defun my/ispell-word-then-abbrev (p)
      "Call `ispell-word'. Then create an abbrev for the correction made.
With prefix P, create local abbrev. Otherwise it will be global."
      (interactive "P")
      (let ((bef (downcase (or (thing-at-point 'word) ""))) aft)
        (call-interactively 'ispell-word)
        (setq aft (downcase (or (thing-at-point 'word) "")))
        (unless (string= aft bef)
          (message "\"%s\" now expands to \"%s\" %sally"
                   bef aft (if p "loc" "glob"))
          (define-abbrev
            (if p local-abbrev-table global-abbrev-table)
            bef aft))))

    (setq save-abbrevs t)
    (setq-default abbrev-mode t)))

From https://github.com/purcell/emacs.d/blob/master/lisp/init-auto-complete.el - Exclude very large buffers from dabbrev

(defun sanityinc/dabbrev-friend-buffer (other-buffer)
 (< (buffer-size other-buffer) (* 1 1024 1024)))
(setq dabbrev-friend-buffer-function 'sanityinc/dabbrev-friend-buffer)

vlf (view large files)

VLF lets me handle things like 2gb files gracefully.

(use-package vlf-setup)

scpaste

Export an HTML version of the buffer and scp it somewhere

(defvar fci-enabled? nil)
(defvar fc-enabled? nil)
(defun my/surround-scpaste (fun &rest args)
  (if fci-mode
      (progn
        (setq fci-enabled? t)
        (fci-mode -1))
    (setq fci-enabled? nil))
  (if flycheck-mode
      (progn
        (setq fc-enabled? t)
        (flycheck-mode -1))
    (setq fc-enabled? nil))
  (let ((result  (apply fun args)))
    (when fci-enabled?
      (fci-mode 1))
    (when fc-enabled?
      (flycheck-mode 1))
    result))

(require 'scpaste)
(setq scpaste-http-destination "http://writequit.org/paste"
      scpaste-scp-destination "writequit.org:www/paste"
      scpaste-user-name "dakrone"
      scpaste-user-address "http://writequit.org/")
;; Disable fill-column-indicator while scpasting
(advice-add 'scpaste :around #'my/surround-scpaste)

eww

Ewwwwww…

Wait, no, I mean the Emacs web browser built in to 24.4

(use-package eww
  :defer t
  :config
  (define-key eww-mode-map "o" 'eww)
  (define-key eww-mode-map "O" 'eww-browse-with-external-browser)
  (use-package eww-lnum
    :config
    (bind-key "f" #'eww-lnum-follow eww-mode-map)
    (bind-key "F" #'eww-lnum-universal eww-mode-map)))

idle-highlight-mode

Highlight idle things, but only in certain modes

(use-package idle-highlight-mode
  :init
  (add-hook 'java-mode-hook #'idle-highlight-mode)
  (add-hook 'emacs-lisp-mode-hook #'idle-highlight-mode)
  (add-hook 'clojure-lisp-mode-hook #'idle-highlight-mode))

Key Bindings

These are miscellaneous bindings used all over the place that don't really fit in anywhere else.

(global-set-key (kbd "C-x +") 'balance-windows-area)

(global-set-key (kbd "C-x C-l") 'toggle-truncate-lines)

(defun my/turn-on-viewing-mode ()
  "Turn on the viewing mode, to make looking through logs easier"
  (interactive)
  (view-mode 1)
  (hl-line-mode 1))

;; join on killing lines
(defun kill-and-join-forward (&optional arg)
  "If at end of line, join with following; otherwise kill line.
Deletes whitespace at join."
  (interactive "P")
  (if (and (eolp) (not (bolp)))
      (delete-indentation t)
    (kill-line arg)))

(global-set-key (kbd "C-k") 'kill-and-join-forward)

;; join line to next line
(global-set-key (kbd "M-j")
                (lambda ()
                  (interactive)
                  (join-line -1)))

;; Completion that uses many different methods to find options.
(global-set-key (kbd "M-/") 'hippie-expand)

;; Font size
(define-key global-map (kbd "C-+") 'text-scale-increase)
(define-key global-map (kbd "C--") 'text-scale-decrease)

;; Use regex searches by default.
(global-set-key (kbd "C-s") 'isearch-forward-regexp)
(global-set-key (kbd "\C-r") 'isearch-backward-regexp)

(global-set-key (kbd "C-c y") 'bury-buffer)
(global-set-key (kbd "C-c r") 'revert-buffer)

;; Start eshell or switch to it if it's active.
(global-set-key (kbd "C-x m") 'eshell)

;; Start a regular shell if you prefer that.
(global-set-key (kbd "C-x C-m") 'shell)

;; If you want to be able to M-x without meta (phones, etc)
(global-set-key (kbd "C-c C-x") 'execute-extended-command)

;; Activate occur easily inside isearch
(define-key isearch-mode-map (kbd "C-o")
  (lambda () (interactive)
    (let ((case-fold-search isearch-case-fold-search))
      (occur (if isearch-regexp isearch-string (regexp-quote isearch-string))))))

;; ==== Window switching ====
(defun my/other-window-backwards ()
  (interactive)
  (other-window -1))

(global-set-key (kbd "M-'") 'other-window)
(global-set-key (kbd "M-\"") 'my/other-window-backwards)
(global-set-key (kbd "H-'") 'other-window)
(global-set-key [C-tab] 'other-window)
(global-set-key [C-S-tab] 'my/other-window-backwards)

Utility methods

Various methods I call interactively for things.

Fixing SSH agent settings

This is for correctly finding the SSH agent:

(defun find-agent ()
  (first (split-string
          (shell-command-to-string
           (concat
            "ls -t1 "
            "$(find /tmp/ -uid $UID -path \\*ssh\\* -type s 2> /dev/null)"
            "|"
            "head -1")))))

(defun fix-agent ()
  (interactive)
  (let ((agent (find-agent)))
    (setenv "SSH_AUTH_SOCK" agent)
    (message agent)))
Format JSON more beautifully

I work with a ton of JSON, so I need to be able to format it nicely. Fortunately this is really easy with Python:

(defun beautify-json ()
  (interactive)
  (let ((b (if mark-active (min (point) (mark)) (point-min)))
        (e (if mark-active (max (point) (mark)) (point-max))))
    (shell-command-on-region b e
                             "python -mjson.tool" (current-buffer) t)))
Recompile startup elisp files

Byte-compile startup stuff.

(defun byte-recompile-init-files ()
  "Recompile all of the startup files"
  (interactive)
  (byte-recompile-directory "~/.emacs.d" 0))
Adding directories to $PATH

A utility method to add things to the $PATH, if needed.

(defun add-to-path (path-element)
  "Add the specified path element to the Emacs PATH"
  (interactive "DEnter directory to be added to path: ")
  (if (file-directory-p path-element)
      (setenv "PATH"
              (concat (expand-file-name path-element)
                      path-separator (getenv "PATH")))))
Misc methods for cleaning up a buffer
(defun untabify-buffer ()
  (interactive)
  (untabify (point-min) (point-max)))

(defun indent-buffer ()
  (interactive)
  (indent-region (point-min) (point-max)))

(defvar bad-cleanup-modes '(python-mode yaml-mode)
  "List of modes where `cleanup-buffer' should not be used")

(defun cleanup-buffer ()
  "Perform a bunch of operations on the whitespace content of a
buffer. If the buffer is one of the `bad-cleanup-modes' then no
re-indenting and un-tabification is done."
  (interactive)
  (unless (member major-mode bad-cleanup-modes)
    (progn
      (indent-buffer)
      (untabify-buffer)))
  (delete-trailing-whitespace))

;; Perform general cleanup.
(global-set-key (kbd "C-c n") #'cleanup-buffer)
Browsing URLs

Search backwards, prompting to open any URL found. This is fantastic for ERC buffers. I bind this to C-c u because I use it a lot.

(defun browse-last-url-in-brower ()
  (interactive)
  (save-excursion
    (let ((ffap-url-regexp
           (concat
            "\\("
            "news\\(post\\)?:\\|mailto:\\|file:"
            "\\|"
            "\\(ftp\\|https?\\|telnet\\|gopher\\|www\\|wais\\)://"
            "\\).")))
      (ffap-next t t))))

(global-set-key (kbd "C-c u") 'browse-last-url-in-brower)
Numbering rectangles

Let's say you have a list like:

First Item
Second Item
Third Item
Fourth Item

And you want to number it to look like:

1. First Item
2. Second Item
3. Third Item
4. Fourth Item

This function allows you to hit C-x r N and specify the pattern and starting offset to number lines in rectangular-selection mode:

(defun number-rectangle (start end format-string from)
  "Delete (don't save) text in the region-rectangle, then number it."
  (interactive
   (list (region-beginning) (region-end)
         (read-string "Number rectangle: "
                      (if (looking-back "^ *") "%d. " "%d"))
         (read-number "From: " 1)))
  (save-excursion
    (goto-char start)
    (setq start (point-marker))
    (goto-char end)
    (setq end (point-marker))
    (delete-rectangle start end)
    (goto-char start)
    (loop with column = (current-column)
          while (and (<= (point) end) (not (eobp)))
          for i from from   do
          (move-to-column column t)
          (insert (format format-string i))
          (forward-line 1)))
  (goto-char start))

(global-set-key (kbd "C-x r N") 'number-rectangle)
Insert look of disapproval

Used more often than you'd think…

(defun lod ()
  "Well. This is disappointing."
  (interactive)
  (insert "ಠ_ಠ"))

(global-set-key (kbd "C-c M-d") #'lod)
Narrow-widen DWIM (do what I mean)
(defun my/narrow-or-widen-dwim (p)
  "If the buffer is narrowed, it widens. Otherwise, it narrows intelligently.
Intelligently means: region, org-src-block, org-subtree, or defun,
whichever applies first.
Narrowing to org-src-block actually calls `org-edit-src-code'.

With prefix P, don't widen, just narrow even if buffer is already
narrowed."
  (interactive "P")
  (declare (interactive-only))
  (cond ((and (buffer-narrowed-p) (not p)) (widen))
        ((region-active-p)
         (narrow-to-region (region-beginning) (region-end)))
        ((derived-mode-p 'org-mode)
         ;; `org-edit-src-code' is not a real narrowing command.
         ;; Remove this first conditional if you don't want it.
         (cond ((org-in-src-block-p)
                (org-edit-src-code)
                (delete-other-windows))
               ((org-at-block-p)
                (org-narrow-to-block))
               (t (org-narrow-to-subtree))))
        (t (narrow-to-defun))))
Move by block of text

See: http://ergoemacs.org/emacs/emacs_move_by_paragraph.html

(defun xah-forward-block (&optional φn)
  "Move cursor forward to the beginning of next text block.
A text block is separated by blank lines. In most major modes,
this is similar to `forward-paragraph', but this command's
behavior is the same regardless of syntax table."
  (interactive "p")
  (search-forward-regexp "\n[\t\n ]*\n+" nil "NOERROR" φn))

(defun xah-backward-block (&optional φn)
  "Move cursor backward to previous text block.
See: `xah-forward-block'"
  (interactive "p")
  (dotimes (ξn φn) (if (search-backward-regexp "\n[\t\n ]*\n+" nil "NOERROR")
                       (progn
                         (skip-chars-backward "\n\t "))
                     (progn (goto-char (point-min))))))

(global-set-key (kbd "<C-up>") 'xah-backward-block)
(global-set-key (kbd "<C-down>") 'xah-forward-block)
Replace smart quotes

I hate smart quotes

(defcustom smart-to-ascii '(("\x201C" . "\"")
                            ("\x201D" . "\"")
                            ("\x2018" . "'")
                            ("\x2019" . "'")
                            ;; en-dash
                            ("\x2013" . "-")
                            ;; em-dash
                            ("\x2014" . "-"))
  "Map of smart quotes to their replacements"
  :type '(repeat (cons (string :tag "Smart Character  ")
                       (string :tag "Ascii Replacement"))))

(defun my/smart-to-ascii (beg end)
  "Replace smart quotes and dashes with their ASCII equivalents"
  (interactive "r")
  (format-replace-strings smart-to-ascii
                          nil beg end))
Change number of replicas for an index

So, I'd like something to prompt me for an index name and the number of replicas to change to, because I do this quite a bit…

(defun change-replicas (index replicas)
  (interactive (list (read-string "Index (_all for all): ")
                     (read-string "Number of replicas: ")))
  (async-shell-command
   (concat
    "curl -s -XPUT 'localhost:9200/"
    index
    "/_settings' -d'{\"index\": {\"number_of_replicas\": "
    replicas
    "}}'")))
Sudo-edit a file (useful in eshell)
(defun sudoec (file)
  (interactive)
  (find-file (concat "/sudo::" (expand-file-name file))))
Transpose buffers

If you split buffers and have A on the left and B on the right, this will put B on the left and A on the right.

(defun transpose-buffers (arg)
  "Transpose the buffers shown in two windows."
  (interactive "p")
  (let ((selector (if (>= arg 0) 'next-window 'previous-window)))
    (while (/= arg 0)
      (let ((this-win (window-buffer))
            (next-win (window-buffer (funcall selector))))
        (set-window-buffer (selected-window) next-win)
        (set-window-buffer (funcall selector) this-win)
        (select-window (funcall selector)))
      (setq arg (if (plusp arg) (1- arg) (1+ arg))))))

(global-set-key (kbd "C-x 4 t") 'transpose-buffers)

Finalizers

Turn off debugging, now that initialization has ended

(setq debug-on-error nil)
(setq debug-on-quit nil)

;; Message how long it took to load everything (minus packages)
(let ((elapsed (float-time (time-subtract (current-time)
                                          emacs-start-time))))
  (message "Loading settings...done (%.3fs)" elapsed))
(put 'narrow-to-region 'disabled nil)

ESVM configuration

Introduction

I've been using esvm for managing starting up multiple ES nodes when I need to test something using the rest API. Here's my configuration for it that tangles and installs into ~/.esvmrc

Here is the branch with the latest release I use:

2.x
{
  "clusters": {
    "2.x": {
      "branch": "2.x",
      "nodes": 1
    },
    "2node": {
      "branch": "2.x",
      "nodes": 2
    },
    "3node": {
      "branch": "2.x",
      "nodes": 3
    },
    "master": {
      "branch": "master",
      "nodes": 1
    }
  },
  "defaults": {
    "config": {
      "cluster.name": "es-lee",
      "node.add_id_to_custom_path": false,
      "node.enable_custom_paths": true,
      "path.repo": "/tmp",
      "path.shared_data": "/tmp",
      "script.indexed": "on",
      "script.inline": "on"
    },
    "plugins": []
  }
}

Author: Lee Hinman

Created: 2015-12-12 Sat 14:46

Validate