Bash, Vim and Tmux Configuration

Introduction

One of the key components to a happy development workflow is to ensure your tools work hard for you, are efficient and also fun to use (although sadly that latter point is sorely underappreciated as a deciding factor into what tools you choose).

This is where configuring your own setup to suit your individual needs becomes essential.

Now although you and I may differ as to what we like, there will inevitably be some common tools and solutions that we'll use and build upon. So what I present here today are are my tools. My configuration and enhancements of those core tools and what I feel to be 'good enough' to suit my needs.

Feel free to take whatever you like and leave the rest.

So what will I be covering? Good question, the running order of things will be:

I'll be going over my terminal/shell configurations, as well as my current Vim and tmux setup. So let's get started...

Note: it shouldn't matter too much, but I'm running Mac OS X

Bash

The reason for using Bash over another shell, such as Zsh (which I used to use), is primarily its ubiquitousness. I find that whenever I'm writing shell scripts I'll end up using a feature that isn't supported in Bash.

Obviously this is fine if you're writing shell scripts for your own machine, but I sometimes write scripts for other environments (e.g. continuous integration systems) and they will mostly be running Bash only.

The reasons other people prefer shells like Zsh or Fish (or whatever) is that you get additional features much more easily. But as you'll see shortly, I've configured my Bash environment to provide some of these features, and OK maybe sometimes it's not as slick as other shells; but this is the first - of possibly many - situations where I'm happy with the setup and feel it's "good enough" to suit my needs, but you might disagree (c'est la vie).

I've decided to break this section down into sub parts to make easier to digest:

Install terminal theme

Now although not strictly anything to do with the shell, I thought it was worth mentioning that I have a few different Terminal themes that I use and choose from. You can find a few of those themes here:

github.com/integralist/dotfiles

Update: I'm now using my own custom theme integralist

To install them, download the .terminal file you want and double click on it.

Install a newer version of Bash

I like to install a newer version of Bash via Homebrew.

For example, execute the following command to see your current Bash version:

/bin/bash --version

When you do, you'll likely find Mac OS X comes with a much older version of Bash than you'd expect.

For me I see version 3.2.57.

So first things first, I want to install a newer version of Bash:

brew install bash

Next add the location of this new Bash shell to our system's list of known shells:

echo /usr/local/bin/bash | sudo tee -a /etc/shells

Finally, let's tell our system to permanently change to the new Bash shell:

chsh -s /usr/local/bin/bash

OK, at this point if you open a new terminal you'll be using the new Bash shell you've downloaded:

bash --version

You'll notice I didn't specify the full path, I just used bash and the terminal was able to locate the new installation. Doing this for me tells me the new version of Bash is 4.3.42.

Configure ~/.bash_profile

In order for the terminal to pick up my actual user configuration for Bash I needed to modify the file ~/.bash_profile. The specific change I add to this file is as follows:

#!/usr/bin/env bash

if [ -f "$HOME/.bashrc" ]; then
  source ~/.bashrc
  cd . || exit
fi

Note: I use /usr/bin/env to identify my new brew installed version of Bash

What this code will do is check if there is a ~/.bashrc file, and if so 'source' it into the currently running shell.

Note: cd . isn't necessary, I just like using it
e.g. each new shell will reset the location back to $HOME

One other thing I add to ~/.bash_profile is the following snippet of code:

if [ -f "$(brew --prefix)/etc/bash_completion" ]; then
  source "$(brew --prefix)/etc/bash_completion"
fi

What this does is load an auto completion script (similar to what you'll find in other shells) so you can type part of a comand, then press <Tab> and have the shell autocomplete the rest of the command for you (as well as other types of autocompletion).

The script itself is provided by Homebrew. Hence you'll see we tell the script to look inside the Homebrew source directory (e.g. $(brew --prefix)), which typically will be something like /usr/local.

Configure ~/.bashrc

The ~/.bashrc file is the main container of our configuration. It'll determine things like the appearance of the shell's prompt, keybindings for moving the cursor around, how we handle the shell's history and many other things.

I'm going to just leave this whole file here and let the comments speak for themselves, but I encourage you to view the current version in my dotfiles repo as it'll be up to date and include any fixes/tweaks...

Note: Google is your friend for any Bash voodoo you're unsure on

#!/usr/bin/env bash

# https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh
source ~/.git-prompt.sh

# https://github.com/jarun/googler/blob/master/auto-completion/bash/googler-completion.bash
source ~/googler-completion.bash

# https://git.zx2c4.com/password-store/plain/src/completion/pass.bash-completion
source /usr/local/etc/bash_completion.d/password-store

# tells Readline to perform filename completion in a case-insensitive fashion
bind "set completion-ignore-case on"

# filename matching during completion will treat hyphens and underscores as equivalent
bind "set completion-map-case on"

# will get Readline to display all possible matches for an ambiguous pattern at the first <Tab> press instead of at the second
bind "set show-all-if-ambiguous on"

# no bell sound on error
bind "set bell-style none"

# enable emacs like bindings (<C-a> and <C-e> for start and end of line shortcuts)
set -o emacs

# append to the history file, don't overwrite it
shopt -s histappend

# save multi-line commands as one command
shopt -s cmdhist

# no need to type cd (works for .. but not -, although alias -- -='cd -' fixes it)
shopt -s autocd 2>/dev/null

# autocorrect minor spelling errors
shopt -s dirspell 2>/dev/null
shopt -s cdspell 2>/dev/null

# check windows size if windows is resized
shopt -s checkwinsize 2>/dev/null

# use extra globing features. See man bash, search extglob.
shopt -s extglob 2>/dev/null

# include .files when globbing.
shopt -s dotglob 2>/dev/null

# case insensitive globbing
shopt -s nocaseglob 2>/dev/null

# can be useful to utilise the gnu style error message format
shopt -s gnu_errfmt 2>/dev/null

# ensure SIGHUP is sent to all jobs when an interactive login shell exits
shopt -s huponexit 2>/dev/null

# specify other paths to look inside of when autocompleting
CDPATH=".:~/code"

# custom environment variables
export DROPBOX="$HOME/Dropbox"
export GITHUB_USER="integralist"

# application configuration
export GOOGLER_COLORS=bjdxxy # https://github.com/jarun/googler/
export LSCOLORS="dxfxcxdxbxegedabagacad" # http://geoff.greer.fm/lscolors/
export GREP_OPTIONS="--color=auto"
export GREP_COLOR="1;32"
export MANPAGER="less -X" # Don't clear the screen after quitting a manual page
export GOPATH=$HOME/code/go
export PATH=$HOME/code/go/bin:$HOME/dotvim/voom:/usr/local/sbin:$PATH
export EDITOR="vim"
export HOMEBREW_NO_ANALYTICS=1
export SSH_PUBLIC_KEY="$HOME/.ssh/github_rsa.pub"

# git specific configurations
export GIT_PS1_SHOWCOLORHINTS=true
export GIT_PS1_SHOWDIRTYSTATE=true     # * for unstaged changes (+ staged but uncommitted changes)
export GIT_PS1_SHOWSTASHSTATE=true     # $ for stashed changes
export GIT_PS1_SHOWUNTRACKEDFILES=true # % for untracked files
export GIT_PS1_SHOWUPSTREAM="auto"     # > for local commits on HEAD not pushed to upstream
                                       # < for commits on upstream not merged with HEAD
                                       # = HEAD points to same commit as upstream

# history configuration
history -a # record each line as it gets issued
export HISTSIZE=500000
export HISTFILESIZE=100000
export HISTCONTROL="erasedups:ignoreboth" # avoid duplicate entries
export HISTIGNORE="&:[ ]*:exit:ls:bg:fg:history" # don't record some commands
export HISTTIMEFORMAT='%F %T ' # useful timestamp format

# force colours
export force_color_prompt=yes

# use colour prompt
export color_prompt=yes

# \e indicates escape sequence (sometimes you'll see \033)
# the m indicates you've provided a colour sequence
#
# 30: Black
# 31: Red
# 32: Green
# 33: Yellow
# 34: Blue
# 35: Purple
# 36: Cyan
# 37: White
#
# a semicolon allows additional attributes:
#
# 0: Normal text
# 1: Bold or light, depending on terminal
# 4: Underline text
#
# there are also background colours (put before additional attributes with ; separator):
#
# 40: Black background
# 41: Red background
# 42: Green background
# 43: Yellow background
# 44: Blue background
# 45: Purple background
# 46: Cyan background
# 47: White background

function prompt_right() {
  echo -e ""
}

function prompt_left() {
  num_jobs=$(jobs | wc -l)

  if [ "$num_jobs" -eq 0 ]; then
    num_jobs=""
  else
    num_jobs=" (\j)"
  fi

  echo -e "\e[33m\]\u. \[\e[37m\]\w\[\e[00m\]$num_jobs\e[31m\]$(__git_ps1)\e[00m\] \e[0;37m(\A)\e[0m"
}

function prompt() {
  compensate=11
  unset PS1
  PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+compensate))" "$(prompt_right)" "$(prompt_left)")
}

function rubo() {
  docker run \
    --cpu-shares 1024 \
    --rm=true \
    --volume "$(pwd):/app" \
    bbcnews/rubocop-config --format simple --fail-level F | grep '^F:\|=='
}

function toggle_hidden() {
  setting=$(defaults read com.apple.finder AppleShowAllFiles)

  if [ "$setting" == "NO" ]; then
    echo "Enabling hidden files"
    defaults write com.apple.finder AppleShowAllFiles YES
  else
    echo "Disabling hidden files"
    defaults write com.apple.finder AppleShowAllFiles NO
  fi

  killall Finder
}

function gms() {
  git merge --squash "$1"
}

function dash {
  local docs=$1
  local query=$2
  open "dash://$docs:$query"
}

function gc {
  if [ -z "$1" ]; then
    printf "\n\tUse: gc some-existing-branch-name\n"
  else
    git checkout "$1"
  fi
}

function gcb {
  if [ -z "$1" ]; then
    printf "\n\tUse: gcb some-new-branch-name (branch will be created)\n"
  else
    git checkout -b "$1"
  fi
}

# We use _ to indicate an unused variable
# Otherwise shellcheck will kick up a stink
# shellcheck disable=SC2034
read -r -d '' git_icons <<- EOF
* unstaged changes
+ staged but uncommitted changes
$ stashed changes
% untracked files
> local commits on HEAD not pushed to upstream
< commits on upstream not merged with HEAD
= HEAD points to same commit as upstream
EOF

# use `type <alias>` to see what is assigned to an alias/fn/builtin/keyword
alias c="clear"
alias dotfiles="ls -a | grep '^\.' | grep --invert-match '\.DS_Store\|\.$'"
alias getcommit="git log -1 | cut -d ' ' -f 2 | head -n 1 | pbcopy"
alias sshkey="ssh-keygen -t rsa -b 4096 -C 'mark.mcdx@gmail.com'"
alias irc="irssi"
alias ls="ls -GpF"
alias ll="ls -laGpFHh"
alias r="source ~/.bashrc"
alias cm="git checkout master"
alias c-="git checkout -"
alias gb="git branch"
alias gbd="git branch -D"
alias gcp="git cherry-pick -"
alias gpr="git pull --rebase origin master"
alias wat='echo "$git_icons"'
alias wut='echo "$git_icons"'
alias gitupstream="echo git branch -u origin/\<branch\>"
alias sshconfig='nvim -c "norm 12ggVjjjgc" -c "wq" ~/.ssh/config && cat ~/.ssh/config | awk "/switch/ {for(i=0; i<=3; i++) {getline; print}}"'
alias copy="tr -d '\n' | pbcopy" # e.g. echo $DEV_CERT_PATH | copy
alias be="bundle exec"
alias sshvm="ssh rig.dev"
alias drm='docker rm $(docker ps -a -q)'
alias drmi='docker rmi $(docker images -q)'
alias dns="scutil --dns | grep 'nameserver\[[0-9]*\]'"
alias nvimupdate="brew reinstall --HEAD neovim"
alias pipall="pip freeze --local | grep -v '^\-e' | cut -d = -f 1  | xargs -n1 pip install -U"
alias uid='echo $(uuidgen)'
alias datesec='date +%s'

eval "$(rbenv init -)"
eval "$(pyenv init -)"

# lazyload nvm
# all props goes to http://broken-by.me/lazy-load-nvm/
# grabbed from reddit @ https://www.reddit.com/r/node/comments/4tg5jg/lazy_load_nvm_for_faster_shell_start/

lazynvm() {
  unset -f nvm node npm
  export NVM_DIR=~/.nvm
  [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"  # This loads nvm
}

nvm() {
  lazynvm
  nvm "$@"
}

node() {
  lazynvm
  node "$@"
}

npm() {
  lazynvm
  npm "$@"
}

# Setup File Search AutoComplete (Ctrl-f, type to filter, arrow to look inside folders)
[[ -s "$HOME/.qfc/bin/qfc.sh" ]] && source "$HOME/.qfc/bin/qfc.sh"

# https://raw.githubusercontent.com/rcaloras/bash-preexec/master/bash-preexec.sh
source ~/.bash-preexec.sh

# preexec executes just BEFORE a command is executed
# preexec() { echo "just typed $1"; }

# precmd executes just AFTER a command is executed, but before the prompt is shown
precmd() { prompt; }

All things considered, this covers a lot of ground for me. There's just one other piece I like to utilise...

Configure ~/.inputrc

In the ~/.inputrc file I have two lines of configuration only:

TAB: menu-complete
"\e[Z": "\e-1\C-i"

In short, what this gives you is the ability to press <C-n> and <C-p> to tab back and forth through any ambigious autocompletion suggestions that you may be presented with.

Other dotfiles?

My $HOME directory is littered with miscellaneous dotfiles such as:

...and many many more (inc dot folders such as .git, .ssh and .irssi etc)

I'm not going to bother to document them all here though.

Vim

I've written a book about Vim (Pro Vim). I've also written about it on my own site quite a few times so I won't waste time explaining it to any one.

So I'll assume you're already a Vim user and are just interested in my set-up.

One key aspect of using Vim are plugins and so any serious Vim user will need a way to manage their plugins. In my book I cover the in's and out's of Pathogen and since then I moved back to Vundle as I wanted an abstraction to help me better manage my growing list of plugins.

I've since now moved over to Voom which is a simple Bash script that has a dependency on Pathogen (itself ~300 lines of VimL code). This is, to me at least, a vast improvement on the ~1k lines of VimL for Vundle and I get a simple voom command I can use to install/delete/update my plugins via the use of a plugin manifest file.

Voom utilises a manifest file (~/.vim/plugins) to determine what plugins are installed. Below is the current contents of that file (the configuration for the plugins themselves can be found within the ~/.vimrc file, which I'll show to you shortly):

chriskempson/vim-tomorrow-theme
endel/vim-github-colorscheme
ekalinin/Dockerfile.vim
ervandew/supertab
fatih/vim-go
godlygeek/tabular
guns/vim-clojure-highlight
guns/vim-clojure-static
guns/vim-sexp
kana/vim-textobj-user
kien/ctrlp.vim
kien/rainbow_parentheses.vim
m-kat/aws-vim
mileszs/ack.vim
nelstrom/vim-textobj-rubyblock
othree/html5.vim
plasticboy/vim-markdown
scrooloose/syntastic
sheerun/vim-polyglot
Shougo/unite.vim
Shougo/vimfiler.vim
tpope/vim-commentary
tpope/vim-endwise
tpope/vim-fireplace
tpope/vim-leiningen
tpope/vim-repeat
tpope/vim-sexp-mappings-for-regular-people
tpope/vim-surround
vim-scripts/Gist.vim
vim-scripts/camelcasemotion

Note: voom edit will open the file for you

Right. Let's now look at getting Vim setup. Below is a break-down of 'from nothing, to something':

mkdir -p ~/.vim/{autoload,bundle}
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
curl -LSso ~/.vim/plugins https://raw.githubusercontent.com/Integralist/dotfiles/master/voom/plugins
curl -LSso /usr/local/bin/voom https://raw.githubusercontent.com/airblade/voom/master/voom
chmod 744 /usr/local/bin/voom
alias voom='VIM_DIR=~/.vim voom'
curl -LSso ~/.vimrc https://raw.githubusercontent.com/Integralist/dotfiles/master/.vimrc
ln -s ~/.vim ~/.config/nvim
ln -s ~/.vimrc ~/.config/nvim/init.vim
voom

The above snippet of code creates a .vim directory and then downloads Pathogen into it. We symlink the directory to ~/.config/nvim so when we start up NeoVim it'll actually pick up the ~/.vim directory. This means both Vim and NeoVim will work from the same set of files.

You'll notice that we clone down my .vimrc from GitHub to ~/.vimrc and then symlink it to ~/.config/nvim/init.vim so again when we start up NeoVim it'll work just fine. On top of that we've installed Voom (our plugin manager) into a place where my $PATH has access to it, and then executed the voom command to start installing the plugins defined in the ~/.vim/plugins file.

Note that in order for our plugins to work with Pathogen, we need the following snippet within our ~/.vimrc:

execute pathogen#infect()
syntax on
filetype plugin indent on

This simply sets Pathogen running whenever Vim starts up.

The configuration for all my plugins are placed directly inside my ~/.vimrc file (shown below) along with all my other Vim settings. Again, I won't bother to detail every single item in my ~/.vimrc; hopefully the comments will suffice. Otherwise Google anything you're unsure about:

Note: again see my dotfiles repo for latest version

" Use the system clipboard
set clipboard+=unnamed

" Switch syntax highlighting on
syntax on

" Don't worry about trying to support old school Vi features
set nocompatible

" Disable Mouse (this is something that only recently affected me within NeoVim)
" Seemed using the mouse to select some text would make NeoVim jump into VISUAL mode?
set mouse=
" No backup files
set nobackup

" No write backup
set nowritebackup

" No swap file
set noswapfile

" Command history
set history=100

" Always show cursor
set ruler

" Show incomplete commands
set showcmd

" Incremental searching (search as you type)
set incsearch

" Highlight search matches
set hlsearch

" Ignore case in search
set smartcase

" Make sure any searches /searchPhrase doesn't need the \c escape character
set ignorecase

" A buffer is marked as ‘hidden’ if it has unsaved changes, and it is not currently loaded in a window
" If you try and quit Vim while there are hidden buffers, you will raise an error:
" E162: No write since last change for buffer “a.txt”
set hidden

" Turn word wrap off
set nowrap

" Allow backspace to delete end of line, indent and start of line characters
set backspace=indent,eol,start

" Convert tabs to spaces
set expandtab

" Set tab size in spaces (this is for manual indenting)
set tabstop=2

" The number of spaces inserted for a tab (used for auto indenting)
set shiftwidth=2

" Turn on line numbers
set number

" Highlight tailing whitespace
set list listchars=tab:\ \ ,trail:·

" Get rid of the delay when pressing O (for example)
" http://stackoverflow.com/questions/2158516/vim-delay-before-o-opens-a-new-line
set timeout timeoutlen=1000 ttimeoutlen=100

" Always show status bar
set laststatus=2

" Set the status line to something useful
set statusline=%f\ %m\ %=L:%l/%L\ C:%c\ (%p%%)

" UTF encoding
set encoding=utf-8

" Autoload files that have changed outside of vim
set autoread

" Better splits (new windows appear below and to the right)
set splitbelow
set splitright

" Highlight the current line
set cursorline

" Ensure Vim doesn't beep at you every time you make a mistype
set visualbell

" Visual autocomplete for command menu (e.g. :e ~/path/to/file)
set wildmenu

" Redraw only when we need to (i.e. don't redraw when executing a macro)
set lazyredraw

" Highlight a matching [{()}] when cursor is placed on start/end character
set showmatch

" <C-x><C-k> for word autocomplete
set dictionary=/usr/share/dict/words

" Use Ag for :grep command (would use Sift but it doesn't work well)
set grepprg=ag\ --nogroup\ --nocolor

" Set built-in file system explorer to use layout similar to the NERDTree plugin
" P opens file in previously focused window
" o opens file in new horizontal split window
" v opens file in new vertical split window
" t opens file in new tab split window
let g:netrw_liststyle=3

execute pathogen#infect()
filetype plugin indent on

let g:default_theme="gruvbox"

set background=dark
execute 'colorscheme ' . g:default_theme

" http://pep8.readthedocs.io/en/latest/intro.html#error-codes
" https://github.com/PyCQA/pep8-naming
let g:neomake_python_flake8_args = neomake#makers#ft#python#flake8()['args'] + ['--ignore', 'N802']

" http://pylint-messages.wikidot.com/all-codes
" http://pylint-messages.wikidot.com/all-messages
let g:neomake_python_pylint_args = neomake#makers#ft#python#pylint()['args'] + ['-d', 'missing-docstring,invalid-name']

" Enable both default Python linters
let g:neomake_python_enabled_makers = ['flake8', 'pylint']

" https://github.com/koalaman/shellcheck/wiki/SC1091
let g:neomake_sh_shellcheck_args = neomake#makers#ft#sh#shellcheck()['args'] + ['-e', 'SC1090,SC1091']
let g:neomake_bash_enabled_makers = ['shellcheck']

let g:neomake_c_enabled_makers = ['clang']

let g:neomake_js_enabled_makers = ['eslint']
let g:neomake_js_eslint_args = ['--config', '~/eslint.config.js']

" General Neomake configuration
let g:neomake_open_list=2
let g:neomake_list_height=5
let g:neomake_verbose=3

" Run Neomake whenever we enter or write a buffer
autocmd BufWritePost,BufWinEnter * silent Neomake

" The following configuration is useful if you don't like
" the icons (which are provided by default) for highlighting errors/warnings
"
" let g:neomake_warning_sign = {
"   \ 'text': 'W',
"   \ 'texthl': 'WarningMsg',
"   \ }
" let g:neomake_error_sign = {
"   \ 'text': 'E',
"   \ 'texthl': 'ErrorMsg',
"   \ }

" vim-go
let g:go_fmt_command = "goimports"
let g:go_metalinter_autosave = 1
let g:go_metalinter_autosave_enabled = ['vet', 'golint']
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']

" tabular
map <Leader>e :Tabularize /=<CR>
map <Leader>c :Tabularize /:<CR>
map <Leader>es :Tabularize /=\zs<CR>
map <Leader>cs :Tabularize /:\zs<CR>

" ctrlp
map <leader>t <C-p>
map <leader>y :CtrlPBuffer<CR>
let g:ctrlp_show_hidden=1
let g:ctrlp_working_path_mode=0
let g:ctrlp_max_height=30
let g:ctrlp_arg_map = 1 " Override <C-o> to provide options for how to open files
set wildignore+=*/.git/*,*/.hg/*,*/.svn/*.,*/.DS_Store " Files matched are ignored when expanding wildcards
let g:ctrlp_user_command = 'ag %s -l --nocolor -g ""' " Use Ag for searching instead of VimScript (might not work with ctrlp_show_hidden and ctrlp_custom_ignore)
let g:ctrlp_custom_ignore = '\v[\/]((node_modules)|\.(git|svn|grunt|sass-cache))$' " Directories to ignore when fuzzy finding

" ack
let g:ackprg = 'ag --nogroup --nocolor --column'

" vim-textobj-rubyblock
runtime macros/matchit.vim

" vim-commentary
xmap <leader><leader><leader> <Plug>Commentary
nmap <leader><leader><leader> <Plug>Commentary
omap <leader><leader><leader> <Plug>Commentary
nmap <leader><leader><leader> <Plug>CommentaryLine

" gist
let g:github_user = $GITHUB_USER
let g:gist_detect_filetype = 1
let g:gist_open_browser_after_post = 1

" camelcase
map <silent> w <Plug>CamelCaseMotion_w
map <silent> b <Plug>CamelCaseMotion_b
map <silent> e <Plug>CamelCaseMotion_e
sunmap w
sunmap b
sunmap e

" nofrils
let g:nofrils_strbackgrounds=1 " enable highlighting of strings and mispellings

" NeoVim shortcut for quick terminal exit
:silent! tnoremap <Esc> <C-\><C-n>

" Allow substitutions to dynamically be represented in the buffer
" https://asciinema.org/a/92207
:silent! set inccommand=nosplit

fun! StripTrailingWhitespace()
  " Don't strip on these filetypes
  if &ft =~ 'markdown'
    return
  endif
  %s/\s\+$//e
endfun
autocmd BufWritePre * call StripTrailingWhitespace()

autocmd FileType gitcommit setlocal spell textwidth=72
autocmd FileType markdown setlocal wrap linebreak nolist textwidth=0 wrapmargin=0 " http://vim.wikia.com/wiki/Word_wrap_without_line_breaks
autocmd FileType sh,cucumber,ruby,yaml,zsh,vim setlocal shiftwidth=2 tabstop=2 expandtab
autocmd FileType php,python setlocal shiftwidth=4 tabstop=4 expandtab

" See `:h fo-table` for details of formatoptions `t` to force wrapping of text
autocmd FileType python,ruby,go,sh,javascript setlocal textwidth=79 formatoptions+=t

" Set different colorscheme for Bash and VimL scripts
autocmd BufEnter *.sh,*.vimrc,*.txt colorscheme github
autocmd BufLeave *.sh,*.vimrc,*.txt execute 'set background=dark' | execute 'colorscheme ' . g:default_theme

" Specify syntax highlighting for specific files
autocmd Bufread,BufNewFile *.spv set filetype=php
autocmd Bufread,BufNewFile *.md set filetype=markdown " Vim interprets .md as 'modula2' otherwise, see :set filetype?

" Run Goyo plugin on Markdown files for when I'm writing blog posts
autocmd Bufread,BufEnter *.md,*.txt execute 'normal zR' | execute 'Goyo'
autocmd BufLeave *.md,*.txt execute 'Goyo!'

" Automatically reload vimrc when it's saved
" autocmd BufWritePost .vimrc so ~/.vimrc

" Rainbow parenthesis always on!
autocmd VimEnter * if exists(':RainbowParenthesesToggle') | exe ":RainbowParenthesesToggleAll" | endif

" Change colourscheme when diffing
fun! SetDiffColours()
  highlight DiffAdd    cterm=bold ctermfg=white ctermbg=DarkGreen
  highlight DiffDelete cterm=bold ctermfg=white ctermbg=DarkGrey
  highlight DiffChange cterm=bold ctermfg=white ctermbg=DarkBlue
  highlight DiffText   cterm=bold ctermfg=white ctermbg=DarkRed
endfun
autocmd FilterWritePre * call SetDiffColours()

" Map § key to :nohlsearch (or :noh for short)
map § :nohlsearch<CR>

Tmux

As far as tmux is concerned, my ~/.tmux.conf is relatively short/concise compared to my other configuration files and to other config files I've seen shared online.

I no longer bother to style the tmux panel, I prefer minimalism nowadays. This means my tmux settings generally revolve around key binding abstractions (although some minor tweaks here and there).

So one last time, just to repeat myself, I don't go into detail as to what these configurations mean; if the comments don't give you the answer you need, then a cursory Google should reveal all you need:

# Remap prefix
unbind C-b
set -g prefix C-Space

# Quick key for moving back to the previous window
bind-key L last-window

# Vim style bindings for pane movement
bind-key -r h select-pane -L
bind-key -r j select-pane -D
bind-key -r k select-pane -U
bind-key -r l select-pane -R

# Fix issue with tmux repeating -r the arrow directions (meaning when you switch panes you can accidentally jump back to the other pane again)
bind-key Up    select-pane -U
bind-key Down  select-pane -D
bind-key Left  select-pane -L
bind-key Right select-pane -R

# Make resizing panes easier
bind-key < resize-pane -L 5
bind-key > resize-pane -R 5
bind-key + resize-pane -U 5
bind-key - resize-pane -D 5
bind-key = select-layout even-vertical
bind-key | select-layout even-horizontal

# Reload tmux config
bind-key r source-file ~/.tmux.conf

# Ensure terminal starts with its own colour scheme (helps Vim/Neovim themes to not break)
set -g default-terminal "screen-256color"

# Enable UTF8 support
set-window-option -g utf8 on

# Use Vi style key bindings to move around copy mode
setw -g mode-keys vi

# Make sure messages (using display-message) are displayed for long enough to read
set-option -g display-time 2000

# Remove delay when pressing esc in Vim
set -sg escape-time 0

# Fix issue with copying from within a tmux session and wanting to paste outside of it
set -g default-command "reattach-to-user-namespace -l '/bin/zsh'"

# Setup `v` and `y` operators to start and yank selections (like Vim)
bind-key -t vi-copy 'v' begin-selection
bind-key -t vi-copy 'y' copy-pipe "reattach-to-user-namespace pbcopy" # pbcopy is only available for Mac (not Linux)

# Prevent tmux from renaming the tab when processes change
set-option -g allow-rename off

# Set base index value to one (as apposed to zero)
set -g base-index 1

# Increase scrollback lines
set -g history-limit 30000

# Renumber windows automatically after removing a window
# Prevents my OCD and means I don't have to manually run {move|swap}-window
set-option -g renumber-windows on

Links