"                               -----------------
"                                  Tim's vimrc
"                               -----------------
"
" This Vim configuration file is the result of many years of using Vim on a
" number of different platforms for all kinds of editing tasks. Primarily C/C++
" and Haskell development on Linux and OS X, though. Feel free to re-use any
" scripts or tweaks found here. I try to include the URL for snippets I found
" somewhere else. It is compatible with Vim version 7.0 - 7.4 on Windows, OS X
" and Linux. Some features only work on UNIX platforms, some key mappings differ
" on the Mac because of the way it reserves and interprets the OPT/CTRL/Fxx
" keys, for terminal Vim the ALT/CMD and certain CTRL key mappings don't work.
" If the colors are messed up in the terminal version, see the notes in the
" color scheme section. It's tricky to have a single setup that works on all
" platforms and Vim versions, but I tried my best. Note that at the end of the
" file there are a couple of handy Makefile templates and configuration files
" for UNIX platforms.
"
" tim@blitzcode.net / www.blitzcode.net / http://www.blitzcode.net/files/vimrc

" ------------------------------------------------------------------------------
" Index
" ------------------------------------------------------------------------------

" List of New or Changed Key Mappings
" General Configuration & Utilities
" Script Helper Functions
" Buffers
" Windows
" Completion
" Tabs
" Status Line
" Syntax / Highlighting / Color Scheme
" Searching
" QuickFix / Make
" CTags
" Help
" vimrc Related
" C/C++ Support
" Haskell Support
" Rust Support
" Git Support
" HTML Support
" Asynchronous Command Execution
" GUI Specific
" QuickName Buffer Switcher
" C/C++ Makefile Template
" Haskell Makefile Template
" Rust Makefile Template
" Shell Configuration

" ------------------------------------------------------------------------------
" List of New or Changed Key Mappings
" ------------------------------------------------------------------------------

" TAB            | Open / close QuickName fast buffer switcher
" F1             | Open documentation for word under the cursor
" F2             | Open tag under cursor in preview window
" F3             | Recursive grep search (asks for root / pattern / filespec)
" F4             | Grep search through all buffers
" F5             | Alias for ,xr
" F7             | Alias for ,xm
" ,tags          | Generate tags file (prompts for root path)
" ALT/CMD+D      | Delete buffer, keep window
" ,bd            | Like above, but can be mapped in a terminal
" CTRL+S         | Save buffer (also works in insert mode)
" CTRL/CMD+LEFT  | Go backward in the QuickFix list
" CTRL/CMD+RIGHT | Go forward in the QuickFix list
" CTRL+J         | Re-wrap paragraph
" SHIFT+T        | Toggle tab size
" ,,             | Turn off highlighting (:nohl)
" -              | Mapped to $ (end of the line, handy on a kb without END key)
" Y              | Yank to EOL, making Y behave more like the other capitals
" BACKSPACE      | Mapped to CTRL+^ (edit alternate file)
" ENTER          | Bring up command prompt ':'
" SPACE          | Mapped to CTRL+W, quick window opening / closing / switching
" SPACE [1-9]    | Move to window by number (leftmost field in the status line)
" .              | Puts cursor back to its starting position after repeating
" HOME / 0       | First does ^, then toggles between ^ and 0
" ,s             | Switch between C/C++ source and header files
" ,cd            | Switch Vim's current directory to that of the current buffer
" ,update        | Replace local ~/.vimrc file with copy from the website
" ,url           | Open URL from the current line in a browser
" `              | In insert mode the backtick is mapped to CTRL+P
" \`             | Prefix with a backslash to enter an actual backtick
" {{             | Insert 3 line C/C++ curly brace construct (insert mode only)
" UP/DOWN/ENTER  | Navigate insert mode completion menu
" < / >          | Keep selection when shifting in visual mode
" ,g[sld]        | Git status, log and diff displayed in a new vertical split
" ,xx            | Asynchronous shell command (all these are UNIX-only)
" ,xr            | Repeat last ,xx
" ,xm            | Asynchronous make
" ,xc            | Cancel asynchronous command / make
" ,fs            | Enter / leave fullscreen mode (MacVim GUI only)
" :Man           | Vim manpage reader
" :Hlint         | Like ,xm but running 'hlint' instead of the default makeprg
" + / _          | Increase / decrease window size
" ,p             | Paste toggle

" ------------------------------------------------------------------------------
" General Configuration & Utilities
" ------------------------------------------------------------------------------

" Don't inherit any vi craziness
set nocompatible

" Version check
if v:version < 700
    echo "Wrong editor version for vimrc, need 7.0 or newer!"
    finish
endif

" Clear all autocommands
autocmd!

" Don't scatter .swp files everywhere, no constant warnings when opening files
" from a crashed session etc.
set noswapfile

" Indentation
set shiftwidth=4

" I don't like text files reconfiguring my editor, could also have security
" implications
set nomodeline

" Allow backspace to delete indents, line breaks and past the start of the
" current insert. This makes the key work like in every other editor
set backspace=indent,eol,start

" Don't insert two spaces after the end of a sentence when using join
set nojoinspaces

" Automatically break text after 90 characters
set textwidth=90

" Re-wrap paragraph to make all lines fit in texwidth characters
noremap <C-j> {gq}

" Map - to $, causing it to jump to the end of the line. This is handy on a
" keyboard without an END key and is shorter than SHIFT+4. Also right next to
" 0, which goes to the beginning of a line
noremap - $

" Yank to EOL, making Y behave more like the other capitals (C and D).
" Taken from http://vimbits.com/bits/11
noremap Y y$

" Treat text lines, wrapped to several screen lines as separate lines when
" navigating with the arrow keys
noremap <Up> gk
noremap <Down> gj

" This puts the cursor back to its starting position when repeating a change
" with the useful dot command. Taken from http://vim.wikia.com/wiki/VimTip1142
nnoremap . .`[

" Pressing HOME or 0 will now place the cursor at the first non-blank
" character (like ^). If it's already there it'll be placed at the first
" character (like 0). Taken from http://vim.wikia.com/wiki/Smart_home
function! SmartHome()
    let s:col = col(".")
    normal! ^
    if s:col == col(".")
        normal! 0
    endif
endfunc
noremap <silent> <Home> :call SmartHome()<CR>
noremap <silent> 0 :call SmartHome()<CR>

" Enter ,cd to switch Vim's current directory to where the current buffer is.
" From http://vim.wikia.com/wiki/Set_working_directory_to_the_current_file
nmap ,cd :cd %:p:h<CR>:echo getcwd()<CR>

" Give priority to UNIX newlines even on other systems where the default is
" different
set fileformats=unix,dos,mac

" Already the default for both Mac versions, hopefully a sane choice on other
" platforms as well
set encoding=utf-8

" Configure display for unprintable characters. Disabled by default, show with
" :set list
set listchars=tab:▸\ ,eol:¬,trail:·

" Enable filetype plugins (required i.e. for omnicppcomplete)
filetype plugin on

" Enable loading of indent files
filetype indent on

" CTRL+S saves (also mapped for insert mode)
map <C-s> :w<CR>
imap <C-s> <C-o>:w<CR>

" Don't let the cursor on the window border, always show one more line
set scrolloff=1

" Allow free positioning of the cursor in visual block mode
set virtualedit=block

" Retain selection after shift operations. I generally just shift right and then
" use dot to repeat, but this seems like the more sensible behavior anyway.
" Taken from http://vimbits.com/bits/20
vnoremap < <gv
vnoremap > >gv

" The default of 20 entries for the history table is somewhat low
set history=500

" No blinking or beeping. We need the autocommand because on Windows it seems
" the t_vb variable is reset after the vimrc is parsed
set visualbell
set vb t_vb=
autocmd GUIEnter * set vb t_vb=

" File browser (:Explore settings)
let g:netrw_list_hide='^\.' " Hide files that start with a dot
let g:netrw_liststyle=1     " Long listing (ll-style) as default

" Enable mouse support even for the terminal version. This doesn't work with all
" terminals, but i.e. iTerm 2 on OS X is fine
"
" TODO: Disabled for now, I dislike how it overrides the normal terminal
"       system clipboard functionality
" set mouse=a

" Open URL in the current line in a browser
nmap <silent> ,url :call OpenDocument(matchstr(getline("."), "http[^ )]*"))<CR>

" Paste toggle. See 'paste' in the Vim documentation for why this is useful
map ,p :set invpaste<CR>:echo &paste ? "Paste Enabled" : "Paste Disabled"<CR>

" Line numbers
set numberwidth=4
set number

" Less redrawing and flickering, speeds up various things, requires a manual
" 'redraw' in a number of bindings and scripts
set lazyredraw

" Reduce delay for recognizing a single Esc key press for terminal Vim. 100ms
" wait time to recognize longer sequences should still be large enough to never
" cause any issues
set ttimeoutlen=100

" Don't replace partial lines at the end of the window with '@' characters
set display+=lastline

" Automatically reload changes for unmodified buffers, helpful when switching
" branches
set autoread

" ------------------------------------------------------------------------------
" Script Helper Functions
" ------------------------------------------------------------------------------

" Error message
function! EchoError(err)
    echohl ErrorMsg | echo a:err | echohl None
endfunction

" Open a new read-only scratch buffer in a vertical split and display the output
" of the passed command in it. The buffer is automatically discarded as soon as
" the user leaves it
function! CommandIntoScratchBuffer(cmd)
    " Close existing window if we're already in a scratch buffer. Without doing
    " this Vim (even 7.4) exhibits a range of weird behaviors, including
    " crashing, when doing a scratch buffer command while already viewing the
    " output of one
    if &buftype == "nofile"
        wincmd c
    endif
    wincmd s
    enew
    setlocal buftype=nofile
    execute "r!" a:cmd
    normal gg
    setlocal nomodifiable
    setlocal nospell
    autocmd BufLeave <buffer> execute "bd"
endfunction

" Open a document or URL with the associated program
" TODO: Consider using escape() or shellescape()
function! OpenDocument(document)
    if has("win32") || has("win64")
        " It's often recommended to use the build-in 'start' command of the cmd
        " shell, but that didn't seem to work on my Windows 7 machine. Invoking
        " Explorer opens the document correctly
        call system("explorer \"" . a:document . "\"")
    elseif has("unix")
        " OS X or Linux? Note that we can't rely on has("mac") here as this
        " isn't set for the console Vim shipping with OS X
        let s:uname = system("uname")
        if s:uname == "Darwin\n"
            " Use 'open' command on OS X
            call system("open '" . a:document . "'")
        else
            " Assume we're on Linux, use 'xdg-open'
            call system("xdg-open '" . a:document . "'")
        endif
    endif
endfunc

" Return the path of the current buffer. Can't be script local as we will call
" this from the status bar to display the buffer's path
function! CurBufPath()
    return fnamemodify(bufname("%"), ":p:h")
endfunction

" Prompt the user to enter a path, using the location of the current buffer as a
" default. Returns the path or an empty string if the user aborts or enters an
" invalid path
function! s:QueryPath(prompt)
    let s:path_sep = (has("win32") || has("win64")) ? "\\" : "/"
    let s:initial_path = CurBufPath() . s:path_sep
    let s:directory = input(a:prompt, s:initial_path, "dir")
    if s:directory == ""
        return
    endif
    if !isdirectory(s:directory)
        call EchoError("Not a directory!")
        return
    endif
    return s:directory
endfunction

" ------------------------------------------------------------------------------
" Buffers
" ------------------------------------------------------------------------------

" Allow buffers with changes to be hidden
set hidden

" Map BS to CTRL+^ (edit alternate file). I like to have a frequently used
" function like this on a single key
noremap <BS> :e #<CR>

" Delete current buffer with Mac style CMD+D (ALT+D everywhere else). Switches
" to another buffer first to prevent the window from being closed. Note that
" this fails for other windows were the buffer might also be visible. Also
" activates the last buffer instead of just the next one
function! DeleteBufferSafe()
    let s:prev_buf = bufnr("#")
    execute "bn"
    " Don't proceed if there's only one buffer left
    if !bufloaded(bufnr("#"))
        return
    endif
    execute "bd #"
    " Don't switch to the last / alternate buffer if it's already deleted
    if bufloaded(s:prev_buf)
        execute "b " s:prev_buf
    endif
endfunction
if has("mac")
    map <D-d> :call DeleteBufferSafe()<CR>
else
    " We use the ALT key on Linux/Windows as it is in the same position as
    " the command key on the Mac
    map <A-d> :call DeleteBufferSafe()<CR>
endif
map ,bd :call DeleteBufferSafe()<CR>

" Don't change the view when returning to a buffer
" http://vim.wikia.com/wiki/Avoid_scrolling_when_switch_buffers
"
" TODO: Disabled for now as there are some glitches when closing splits and when
"       mouse scrolling in an inactive window
" autocmd BufLeave * let b:winview = winsaveview()
" autocmd BufEnter * if (exists('b:winview')) | call winrestview(b:winview) | en

" ------------------------------------------------------------------------------
" Windows
" ------------------------------------------------------------------------------

" Map the SPACE key to CTRL+W, allowing window operations to be executed
" quickly
noremap <SPACE> <C-w>

" SPACE followed by 1-9 directly jumps to the window by number (leftmost field
" in the status line)
noremap <silent> <SPACE>1 :exe 1 . "wincmd w"<CR>
noremap <silent> <SPACE>2 :exe 2 . "wincmd w"<CR>
noremap <silent> <SPACE>3 :exe 3 . "wincmd w"<CR>
noremap <silent> <SPACE>4 :exe 4 . "wincmd w"<CR>
noremap <silent> <SPACE>5 :exe 5 . "wincmd w"<CR>
noremap <silent> <SPACE>6 :exe 6 . "wincmd w"<CR>
noremap <silent> <SPACE>7 :exe 7 . "wincmd w"<CR>
noremap <silent> <SPACE>8 :exe 8 . "wincmd w"<CR>
noremap <silent> <SPACE>9 :exe 9 . "wincmd w"<CR>

" Draw vertical window split bars using vertical pipe Unicode character
set fillchars+=vert:┃

" Resize windows with _ / +
noremap + <C-w>+
noremap _ <C-w>-

" ------------------------------------------------------------------------------
" Completion
" ------------------------------------------------------------------------------

" Map the backtick key to CTRL+P completion in insert mode. Allow insert of a
" backtick by typing backslash first, important for languages like Clojure and
" Haskell that use it
inoremap ` <C-p>
inoremap \` `

" Allow usage of arrow keys, ENTER and ESC to navigate, confirm and abort
" completions in insert mode when the popup menu is visible. Make sure we treat
" wrapped lines the same as with the mapping for normal mode Up/Down defined
" just above. Taken from http://vim.wikia.com/wiki/Improve_completion_popup_menu
inoremap <expr> <CR>   pumvisible() ? "\<C-y>" : "\<CR>"
inoremap <expr> <Down> pumvisible() ? "\<C-n>" : "\<C-o>gj"
inoremap <expr> <Up>   pumvisible() ? "\<C-p>" : "\<C-o>gk"
" inoremap <expr> <ESC>  pumvisible() ? "\<C-e>" : "\<ESC>"

" Activate enhanced command line completion, ignore files we probably don't want
" to edit
set wildmenu
set wildignore+=.DS_Store,desktop.ini,Thumbs.db,.git,.hg,.svn,*.a,*.o,*.swp,*.hi

" ------------------------------------------------------------------------------
" Tabs
" ------------------------------------------------------------------------------

set smarttab
set tabstop=8
set softtabstop=4
set expandtab

" Makefiles need to use hard tabs
autocmd FileType Makefile setlocal noexpandtab

" Press SHIFT+T to toggle tab size
function! ToggleTabStop()
    if &tabstop == 4
        set tabstop=8
        echo "tab size 8"
    else
        set tabstop=4
        echo "tab size 4"
    endif
endfunc
map <S-t> :call ToggleTabStop()<CR>

" ------------------------------------------------------------------------------
" Status Line
" ------------------------------------------------------------------------------

" Always show the status line
set laststatus=2

" We already do that ourselves in the status line
set noshowmode

" This is similar to what 'set ruler' would get us, except it displays the
" window number by followed the buffer name and path separately and adds some
" fancy separators between the fields. We also show relevant modes and highlight
" the modified status of a buffer a bit more clearly
set statusline=%!MyStatusLine()
function! MyStatusLine()
    " This function gets called in the context of the current window and buffer,
    " while the functions we call in the status line string will get called from
    " the window and buffer which the status line that's being drawn belongs to.
    " If we pass the current window number from here to one of those functions,
    " it can check if it is returning text for the status line of the current
    " window or not. This is useful, for instance, for the mode display as
    " otherwise we might leave false mode notifications in the status line of
    " another window if it happens to get redrawn while we're in i.e. insert
    " mode (the 'matchparen' plugin causes this)
    return
        \ "%#SLSep#▬%*" .
        \ "%#SLWinNr#\ %{winnr()}\ " .
        \ "%#Error#%{SLInsertReplace(" . winnr() .")}" .
        \ "%#Todo#%{SLVisual(" . winnr() .")}" .
        \ "%#SLSep#◄%*" .
        \ "\ %t\ %#Error#%{SLModified()}%*" .
        \ "%#SLSep#▬%*" .
        \ "\ %<%{CurBufPath()}\ " .
        \ "%#SLSep#▬%*" .
        \ "\ %Y%W%R%=\ " .
        \ "%#SLSep#▬%*" .
        \ "\ %-13.(%l,%c%V%)\ " . 
        \ "%#SLSep#▬%*" .
        \ "\ %P\ " .
        \ "%#SLSep#▬%*"
endfunction

function! SLModified()
    return &modified ? " + " : ""
endfunction

" This could flicker sometimes because we briefly go into normal mode due to
" remapping <Up> / <Down> to <C-O>g k / j so we can move by screen columns. We
" fix this with 'set lazyredraw' earlier
function! SLInsertReplace(curwin)
    if a:curwin != winnr()
        return ""
    endif
    if mode() ==# "i"
        return " INSERT "
    elseif mode() ==# "R"
        return " REPLACE "
    endif
    return ""
endfunction

function! SLVisual(curwin)
    if a:curwin != winnr()
        return ""
    endif
    if mode() ==# "v"
        return " VISUAL "
    elseif mode() ==# "V"
        return " VISUAL LINE "
    elseif mode() ==# ""
        return " VISUAL BLOCK "
    endif
    return ""
endfunction

" TODO: Show build status in status line of QuickFix window

" ------------------------------------------------------------------------------
" Syntax / Highlighting / Color Scheme
" ------------------------------------------------------------------------------

" We expect a 256 color terminal and either degrade gracefully or horribly if
" this is not the case. Make sure your terminal supports it (i.e. Terminal.app
" before 10.7 doesn't) and that any terminal multiplexers (tmux, screen, etc.)
" are properly configured. For instance, screen on Ubuntu needs this added to
" the .screenrc to work in 256 color mode:
"
" attrcolor b ".I"
" termcapinfo xterm 'Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm'
" defbce on
"
" Older screen versions on OS X just might not support 256 colors. tmux needs to
" be invoked with this:
"
" TERM=xterm-256color
"
" In emergencies, a quick ':colorscheme desert' should provide some relief...

" Syntax highlighting
syntax on
highlight clear
set t_Co=256
set background=dark
if has("gui_macvim")
    let g:macvim_skip_colorscheme=1
endif

" This is a rewritten/modified version of the stock desert color scheme. Unlike
" the original, this will also look almost identical to the GUI in a terminal
" with 256 color mode. Many highlights with a color background have a better
" contrasting foreground as well

" Standard highlights
highlight Normal       guifg=White guibg=grey16
                     \ ctermbg=234 ctermfg=231
highlight Cursor       guibg=khaki guifg=slategrey
highlight VertSplit    guifg=#d3cfb5 guibg=bg gui=none
                     \ cterm=none ctermbg=bg ctermfg=187
highlight Folded       guibg=grey30 guifg=gold
                     \ ctermbg=239 ctermfg=220
highlight FoldColumn   guibg=grey30 guifg=tan
                     \ ctermbg=239 ctermfg=180
highlight IncSearch    guifg=slategrey guibg=khaki
                     \ cterm=reverse ctermbg=66 ctermfg=222
highlight ModeMsg      guifg=goldenrod
                     \ ctermbg=bg ctermfg=178
highlight MoreMsg      guifg=SeaGreen
                     \ ctermbg=bg ctermfg=29
highlight NonText      guifg=LightBlue guibg=grey25
                     \ ctermbg=236 ctermfg=152
highlight Question     guifg=springgreen
                     \ ctermbg=bg ctermfg=48
highlight Search       guibg=DarkOrange guifg=Black
                     \ ctermbg=208 ctermfg=16
highlight SpecialKey   guifg=yellowgreen
                     \ ctermbg=bg ctermfg=113
highlight StatusLineNC guibg=#d3cfb5 guifg=grey50 gui=none
                     \ cterm=none ctermbg=187 ctermfg=244
highlight StatusLine   guibg=#d3cfb5 guifg=Black gui=none
                     \ cterm=none ctermbg=187 ctermfg=16
highlight Title	       guifg=indianred
                     \ ctermbg=bg ctermfg=167
highlight Visual       gui=none guifg=khaki guibg=olivedrab
                     \ ctermbg=64 ctermfg=222
highlight WarningMsg   guifg=salmon
                     \ ctermbg=bg ctermfg=209
highlight Comment      guifg=SkyBlue
                     \ ctermbg=bg ctermfg=74
highlight Constant     guifg=#ffa0a0
                     \ ctermbg=bg ctermfg=217
highlight Identifier   guifg=palegreen
                     \ ctermbg=bg ctermfg=120
highlight Statement    guifg=khaki
                     \ ctermbg=bg ctermfg=222
highlight PreProc      guifg=indianred
                     \ ctermbg=bg ctermfg=167
highlight Type	       guifg=darkkhaki
                     \ ctermbg=bg ctermfg=143
highlight Special      guifg=navajowhite
                     \ ctermbg=bg ctermfg=223
highlight Ignore       guifg=grey40
                     \ ctermbg=bg ctermfg=241
highlight Todo	       guifg=Black guibg=yellow2
                     \ ctermbg=226 ctermfg=16
highlight Pmenu        guibg=DarkRed
                     \ ctermbg=88 ctermfg=fg
highlight PmenuSel     guifg=Black guibg=DarkOrange
                     \ ctermbg=208 ctermfg=16
highlight LineNr       guifg=#6f6f6f
                     \ ctermfg=DarkGrey ctermbg=bg ctermfg=242
highlight CursorLine   guibg=gray22
                     \ ctermbg=235
highlight CursorLineNr guibg=gray23 guifg=gray60
                     \ ctermbg=237 ctermfg=244 " Only works since 7.4
highlight SignColumn   guifg=white guibg=bg
                     \ ctermfg=white ctermbg=bg
highlight Directory    ctermbg=bg ctermfg=51
highlight MatchParen   ctermbg=30 ctermfg=fg
highlight SpellBad     cterm=underline ctermfg=red ctermbg=bg
highlight SpellCap     cterm=underline ctermfg=red ctermbg=bg

" Custom highlights
highlight customURL         gui=underline guisp=Purple
                          \ cterm=underline ctermbg=bg ctermfg=129
highlight SignGenericMarker guifg=black guibg=white  
                          \ ctermfg=black ctermbg=white 
highlight SignErrorMarker   guifg=black guibg=red    
                          \ ctermfg=black ctermbg=red   
highlight SignWarningMarker guifg=black guibg=yellow 
                          \ ctermfg=black ctermbg=yellow
highlight cCustomFunc       guifg=PaleGreen
                          \ ctermfg=LightGreen
highlight scrnSuccess       guifg=Black guibg=Green  
                          \ ctermfg=Black ctermbg=Green 
highlight scrnWarnings      guifg=Black guibg=Yellow 
                          \ ctermfg=Black ctermbg=Yellow
highlight scrnErrors        guifg=White guibg=Red    
                          \ ctermfg=White ctermbg=Red   
highlight SLSep             guibg=bg guifg=#d3cfb5 
                          \ ctermbg=bg ctermfg=187
highlight SLWinNr           guibg=#d3cfb5 guifg=Black 
                          \ ctermbg=187 ctermfg=16

" Show the stack of syntax highlighting classes affecting whatever is under the
" cursor. This is useful for debugging custom syntax statements. Taken from
" https://github.com/sjl/dotfiles/blob/master/vim/.vimrc
function! ShowSyntaxStack()
    echo join(map(synstack(line('.'), col('.')),
        \ 'synIDattr(v:val, "name")'), " > ")
endfunc

" Highlight merge conflicts delimiters
match ErrorMsg '^\(<\|=\|>\)\{7\}\([^=].\+\)\?$'

" Highlight URLs by underlining them. Taken from
" http://tobym.posterous.com/url-regex-in-vim-for-syntax-highlighting
match customURL /https\?:\/\/\(\w\+\(:\w\+\)\?@\)\?\([A-Za-z][-_0-9A-Za-z]*\.\)
                \\{1,}\(\w\{2,}\.\?\)\{1,}\(:[0-9]\{1,5}\)\?\S*/

" ------------------------------------------------------------------------------
" Searching
" ------------------------------------------------------------------------------

" Text search options
set incsearch  " Incremental search
set ignorecase " Ignore case
set smartcase  " ...unless uppercase letters are used
if !&hlsearch  " Highlight the searched text
    " Don't set this again as it'll turn highlighting back on every time the
    " vimrc is reloaded. This can get quite annoying when testing changes
    set hlsearch
endif

" Turn off highlighting after a search
noremap <silent> ,, :nohl<CR>

" Setup search path (search upwards and three directory levels downwards)
set path=.,./**3,.;

" Recursive search using Vim's build-in grep feature. Prompts will appear asking
" the user for the root of the search (defaulting to the buffer's directory),
" the pattern to search for and the types of files to search (defaulting to
" typical source code extensions). The search can be cancelled with CTRL+C. After
" the search has completed the QuickFix window will be displayed showing all the
" matches
"
" TODO: Cancelling the search will change the current directory
func! GrepSearch()
    let s:directory = s:QueryPath("Search root dir: ")
    if (s:directory == "")
        return
    endif
    let s:search_pattern = input("Search pattern: ")
    if (s:search_pattern == "")
        return
    endif
    let s:search_files = input("Search files: ", "**/*.cpp **/*.[cCh] **/*.hs")
    if (s:search_files == "")
        return
    endif
    let s:old_cwd = getcwd()
    execute "cd" s:directory
    execute "vimgrep /" . s:search_pattern . "/j " . s:search_files
    execute "cd" s:old_cwd
    execute "cwindow"
endfunc
map <F3> :call GrepSearch()<CR>

" Build a list of all listed buffers. Taken from
" http://stackoverflow.com/questions/271364/
function! BuildBufferList()
    let all = range(0, bufnr('$'))
    let res = []
    for b in all
        if buflisted(b)
            call add(res, fnameescape(bufname(b)))
        endif
    endfor
    if (exists("*uniq"))
        return uniq(sort(res)) " Filter out duplicates
    else
        " uniq() was just added in some 7.4 patch, we might not have it
        return res
    endif
endfunc

" Do a grep search on all buffers, display QuickFix window at the end
func! SearchAllBuffers()
    let search_pat = input("Search pattern: ")
    if (search_pat == "")
        return
    endif
    " Clear QuickFix list
    call setqflist([])
    execute "vimgrep /" . search_pat . "/j " . join(BuildBufferList(), ' ')
    " The simpler and often suggested alternative to this
    " 
    " execute "silent bufdo try | vimgrepadd! " . pat . " % | catch | endtry"
    "
    " can cause issues because the bufdo command disables the Syntax autocommand
    " event, causing syntax highlighting to be disabled on buffers first loaded
    " by it
    execute "cwindow"
endfunc
map <F4> :call SearchAllBuffers()<CR>

" ------------------------------------------------------------------------------
" QuickFix / Make
" ------------------------------------------------------------------------------

" Determine which make program to use for the current directory
function! GetMakeProgram()
    " User set a custom make program?
    if &makeprg != "make" && &makeprg != ""
        return &makeprg
    endif
    " Make and Makefile present?
    if executable("make") == 1 && 
           \ (filereadable("Makefile") || filereadable("makefile"))
        return "make -j" " Parallel make
    endif
    " Stack and stack.yaml present?
    if executable("stack") == 1 && filereadable("stack.yaml")
        return "stack build"
    endif
    " Cabal and .cabal present?
    if executable("cabal") == 1 && filereadable(glob("*.cabal"))
        return "cabal build"
    endif
    " Cargo and Cargo.toml present?
    if executable("cargo") == 1 && filereadable("Cargo.toml")
        return "cargo build"
    endif
    call EchoError("No custom 'makeprg' set and neither of the supported " .
        \ "'make', 'cabal', 'stack' or 'cargo' build systems can be found")
    return ""
endfunction

" Disable spell checking
autocmd Filetype qf setlocal nospell

" No need for line numbers
autocmd Filetype qf setlocal nonumber

" The filetype script for the QuickFix window unfortunately sets a custom status
" line, overwriting our nice one. Set it back here
autocmd Filetype qf setlocal statusline<

" function! QFStatusLineFix()
"     let win_lst = range(1, winnr('$'))
"     let cnt_active = 0
"     for nr in win_lst
"         if getwinvar(nr, "&statusline") == g:my_statusline
"             let cnt_active = cnt_active + 1
"         endif
"     endfor
"     if cnt_active == 0
"         let &l:statusline=g:my_statusline
"     else
"         let &l:statusline=g:my_statusline_nc
"     endif
" endfunction
" autocmd Filetype qf call QFStatusLineFix()

" Use CTRL+LEFT/RIGHT to cycle through the QuickFix list. Use CMD on the Mac
" because of the conflict with the hotkeys for Spaces. Useful for browsing
" through compile errors or grep results
function! PrevError()
    try | cp | catch | cc | endtry   
endfunction
function! NextError()
    try | cn | catch | cc | endtry   
endfunction
if has("mac")
    map <silent> <D-Left>  :call PrevError()<CR>
    map <silent> <D-Right> :call NextError()<CR>
else
    map <silent> <C-Left>  :call PrevError()<CR>
    map <silent> <C-Right> :call NextError()<CR>
endif

" Map ENTER to bringing up the command prompt. This is very handy as the ENTER
" key in normal mode does nothing useful by default and is also the key to
" confirm a command. Prevent the ENTER key from being remapped for the QuickFix
" window (we want the normal selection there)
"
" TODO: Couldn't this be done using an <expr> mapping?
function! QuickFixAwareEnter()
    if &buftype != "quickfix"
        " Enter command mode
        call feedkeys(":")
    else
        " QuickFix window - pass on ENTER key so we can select entries
        call feedkeys("\<CR>", 'n')
    endif
endfunc
noremap <silent> <CR> :call QuickFixAwareEnter()<CR>
" Fix for visual mode, as the above doesn't work when trying to run a command on
" a visual selection
vnoremap <CR> :

" ------------------------------------------------------------------------------
" CTags
" ------------------------------------------------------------------------------

" Tag file search path. The ; at the end of the tags path will search for the
" tag file in all directories above the file location. Very useful as this will
" always find the right tag file, even when working with multiple projects and
" sandboxes
set tags=./tags;

" Additional master tags file in the home directory. This is a good place to
" generate tags for i.e. the system includes ('ctags -R /usr/include' on Linux
" or 'ctags --languages=C,C++ -R /usr/include/ /System/Library/Frameworks/
" /Developer/SDKs/' on Mac). Note that Vim expects Exuberant Ctags, not the
" older out of date versions installed by default on many systems
set tags+=~/tags

" Helper for quickly generating a tags file. When editing a file in some project
" hierarchy without tags one usually wants to cd to a directory a few levels
" above the file, execute a ctags -R and switch back to the old directory. This
" script does just that, removing most of the typing work. Since Vim here is
" configured to automatically search all parent directories it'll find the newly
" generated tag file for sure. Just hit ',tags', specify the project root and
" hit enter
func! GenerateTags()
    let s:directory = s:QueryPath("Tags root dir: ")
    if (s:directory == "")
        return
    endif
    let s:old_cwd = getcwd()
    execute "cd" s:directory
    execute "!ctags -R"
    execute "cd" s:old_cwd
endfunc
map ,tags :call GenerateTags()<CR>

" Show tag under cursor in preview window. This is an example from the Vim
" manual, see :h CursorHold-example
function! PreviewWord()
    if &previewwindow                " Don't do this in the preview window
        return
    endif
    let w = expand("<cword>")        " Get the word under cursor
    if w =~ '\a'                     " If the word contains a letter
        " Delete any existing highlight before showing another tag
        silent! wincmd P             " Jump to preview window
        if &previewwindow            " If we really get there...
            match none               " Delete existing highlight
            wincmd p                 " Back to old window
        endif
        " Try displaying a matching tag for the word under the cursor
        try
           exe "ptag " . w
        catch
           return
        endtry
        silent! wincmd P             " Jump to preview window
        if &previewwindow            " If we really get there...
            if has("folding")
                silent! .foldopen    " Don't want a closed fold
            endif
            call search("$", "b")    " To end of previous line
            let w = substitute(w, '\\', '\\\\', "")
            call search('\<\V' . w . '\>') " Position cursor on match
            " Add a match highlight to the word at this position
            hi previewWord term=bold ctermbg=DarkRed guibg=DarkRed
            exe 'match previewWord "\%'
                \ . line(".") . 'l\%' . col(".") . 'c\k*"'
            wincmd p                 " Back to old window
        endif
    endif
endfunc
map <F2> :call PreviewWord()<CR>

" ------------------------------------------------------------------------------
" Help
" ------------------------------------------------------------------------------

" Bring up documentation the word under the cursor
function! FileTypeAwareHelp()
    if &filetype == "vim" || &filetype == "help"
        " Help or Vim script file, open build-in help
        execute "help " . expand("<cword>")
    elseif &filetype == "haskell"
        " Use the Hoogle Haskell API search engine on the word under the cursor.
        " Alternative, we could also query the compiler with something like 'ghc
        " -v0 --interactive File.hs :info someType'. This is all rather shoddy,
        " switch to the Haskell Mode plugin if we ever need a more sophisticated
        " documentation / compiler integration
        if executable("hoogle")
            " Use local Hoogle, if available
            let cword = expand("<cWORD>")
            call CommandIntoScratchBuffer
                \ ("hoogle --info " . cword . " && echo && hoogle -n5 " . cword)
        else
            " No local Hoogle, use browser
            call OpenDocument("http://www.haskell.org/hoogle/?hoogle=" .
                \ expand("<cWORD>"))
        endif
    else
        " Use manpage integration (no use to try this on non-UNIX platforms)
        if has("unix")
            execute "Man " . expand("<cword>")
        endif
    endif
endfunc
map <F1> :call FileTypeAwareHelp()<CR>

" Vim manpage reader 
runtime! ftplugin/man.vim
autocmd FileType man setlocal nospell

" ------------------------------------------------------------------------------
" vimrc Related
" ------------------------------------------------------------------------------

" Automatically reload changes after saving a vimrc
autocmd BufWritePost {.vimrc,.gvimrc,_vimrc,_gvimrc} exe "so" . bufname("%")

" Replace the local ~/.vimrc file with the current copy from the website. Only
" available on UNIX and needs either curl or wget installed (very common)
function! UpdateVimRC()
    let url = "http://www.blitzcode.net/files/vimrc"
    " Use 'curl -s url | diff - ~/.vimrc' to check for differences
    if has("unix")
        if executable("curl")
            execute "!curl -o ~/.vimrc" url
            return
        endif
        if executable("wget")
            execute "!wget -O ~/.vimrc" url
            return
        endif
    endif
    call EchoError("Only available on UNIX systems with curl / wget installed!")
endfunc
" Need to source here in the mapping as we can't reload a running function
map ,update :call UpdateVimRC()<CR>:source ~/.vimrc<CR>

" ------------------------------------------------------------------------------
" C/C++ Support
" ------------------------------------------------------------------------------

" Assume gcc as compiler
autocmd FileType c,cpp compiler gcc

" Indentation
set cindent
set cinoptions+=(1s " Only one shift for a continuation line after an open (
set cinoptions+=g0  " Place public: etc. on the same indent as the {

" Switch between source and header files
function! SwitchSourceHeader()
    let s:ext  = expand("%:e")
    let s:base = expand("%:t:r")
    let s:cmd  = "find " . s:base
    if (s:ext == "cpp" || s:ext == "c" || s:ext == "C" || s:ext == "cxx")
        if findfile(s:base . ".h"  ) != "" | exe s:cmd . ".h"   | return | en
        if findfile(s:base . ".hpp") != "" | exe s:cmd . ".hpp" | return | en
        if findfile(s:base . ".hxx") != "" | exe s:cmd . ".hxx" | return | en
    else
        if findfile(s:base . ".cpp") != "" | exe s:cmd . ".cpp" | return | en
        if findfile(s:base . ".c"  ) != "" | exe s:cmd . ".c"   | return | en
        if findfile(s:base . ".C")   != "" | exe s:cmd . ".C"   | return | en
        if findfile(s:base . ".cxx") != "" | exe s:cmd . ".cxx" | return | en
    endif
endfunc
autocmd Filetype c,cpp nmap <buffer> ,s :call SwitchSourceHeader()<CR>

" Insert common C/C++ curly brace construct like this:
"
" {
"     <Cursor>
" }
"
" When {{ is typed. Also works in the middle of the line
au Filetype c,cpp,cs,cuda,java,javascript
    \ ino <buffer> {{ <C-o>o{<CR>}<ESC><Up>o

" Automatically insert C/C++ include guards for new header files. Taken
" from http://vim.wikia.com/wiki/Automatic_insertion_of_C/C%2B%2B_header_gates
function! InsertIncludeGuards()
    let guard_name = substitute(toupper(expand("%:t")), "\\.", "_", "g")
    normal! o
    execute "normal! i#ifndef " . guard_name
    execute "normal! o#define " . guard_name
    normal! 3o
    execute "normal! Go#endif // " . guard_name
    normal! o
    normal! 3k
endfunc
autocmd BufNewFile *.{h,hpp,hxx} call InsertIncludeGuards()

" Vim's C++ syntax highlighting is in a poor state. The cpp.vim hasn't seen
" changes in a decade and it actually breaks on some new C++11 code. Neither
" does it highlight any of the new keywords, nor did it ever do basic stuff like
" defining a syntax group for functions. I'm not an expert on regular
" expressions or Vim's syntax highlighting, but I'll try to fix as much as I can
" in here
function! EnhancedCppSyntax()
    " TODO: Because we call this on every BufEnter we need to reset the syntax
    "       to avoid slowing Vim to a crawl with repeated keyword / match
    "       additions. This is really quite messy, find a better way
    syntax clear
    runtime! syntax/cpp.vim

    " Highlight namespaces/scopes and function declarations/calls
    " http://stackoverflow.com/questions/736701/
    syntax match cCustomParen "(" contains=cParen contains=cCppParen
    syntax match cCustomFunc  "\w\+\s*(" contains=cCustomParen
    syntax match cCustomScope "::"
    syntax match cCustomClass "\w\+\s*::" contains=cCustomScope
    highlight link cCustomClass Type

    " New C++11 keywords
    syntax keyword cpp11Constant     nullptr
    syntax keyword cpp11Type         constexpr override final noexcept
    syntax keyword cpp11StorageClass decltype
    highlight link cpp11Type         Type
    highlight link cpp11StorageClass StorageClass
    highlight link cpp11Constant     Constant

    " http://vim.wikia.com/wiki/Highlighting_of_method_names_in_the_definition
    " syntax match cppFuncDef "::\~\?\zs\h\w*\ze([^)]*\()\s*\(const\)\?\)\?$"
    " highlight def link cppFuncDef Special
    " highlight cppFuncDef gui=underline
endfunc
" TODO: Can't get this to work with the Syntax or FileType events
autocmd BufEnter *.{c,cpp,C,cc,cxx,h,hpp,hxx} call EnhancedCppSyntax()

" ------------------------------------------------------------------------------
" Haskell Support
" ------------------------------------------------------------------------------

" Configure Vim to understand Haskell includes / import statements and to make
" it parse error / warning output from GHC.
"
" TODO: Partially taken from http://projects.haskell.org/haskellmode-vim/ and
"       rather simplistic, maybe just switch to use everything from the actual
"       plugin?
function! SetupHaskell()
    " Includes
    setlocal include=^import\\s*\\(qualified\\)\\?\\s*
    setlocal includeexpr=substitute(v:fname,'\\.','/','g').'.'
    setlocal suffixesadd=hs,lhs,hsc
    " Indentation. Smart and C indentation do more harm than good here, stick
    " with taking the indent from the previous line
    setlocal autoindent
    setlocal nosmartindent
    setlocal nocindent
    " Disable some completion option
    set complete-=t
    set complete-=i
    " GHC errors / warnings. Fixed version from haskellmode, no longer
    " interprets multi-line warnings as errors
    setlocal errorformat=
        \%W%f:%l:%c:\ Warning:\ %m,
        \%W%f:%l:%c:\ Warning:,
        \%E%f:%l:%c:\ %m,
        \%E%f:%l:%c:,
        \%+C\ \ %#, " TODO: Adding %m gives us the full error message in the
                    "       error string, but then the entire multi-line
                    "       message is all joined into a single line in the
                    "       quickfix window
        \%-Z\ %#,
endfunc
autocmd FileType haskell call SetupHaskell()

" Configure syntax highlighting for Haskell files. Consider using the improved
" syntax file from
" https://github.com/urso/dotrc/blob/master/vim/syntax/haskell.vim
let hs_highlight_boolean=1
let hs_highlight_types=1
let hs_highlight_more_types=1
let hs_highlight_debug=1

" Vim's included Haskell syntax files are pretty bad. Maybe using one of the
" many alternative ones available would be a good idea, but for now this fixes
" the worst
function! EnhancedHaskellSyntax()
    " TODO: Because we call this on every BufEnter we need to reset the syntax
    "       to avoid slowing Vim to a crawl with repeated keyword / match
    "       additions. This is really quite messy, find a better way
    syntax clear
    runtime! syntax/haskell.vim

    " Highlight TODO in comments like most other languages
    syntax match hsTodo "TODO" containedin=hsLineComment,hsBlockComment
    highlight link hsTodo Todo

    " A bit more context
    syntax sync minlines=300

    " Redefine the comment elements to have spell checking. By default Haskell
    " files are either spell checked entirely or not at all
    syntax spell notoplevel
    syntax match hsLineComment "---*\([^-!#$%&\*\+./<=>\?@\\^|~].*\)\?$"
        \ contains=@Spell
    syntax region hsBlockComment start="{-" end="-}" contains=@Spell
    syntax region hsPragma start="{-#" end="#-}"
endfunc
" TODO: Can't get this to work with the Syntax or FileType events
autocmd BufEnter *.{hs,hi,lhs,hsc} call EnhancedHaskellSyntax()

" Setting up a readable prompt for GHCi (careful, overwrites the config file!)
" echo ":set prompt \"\n▸▸▸ \"" > ~/.ghc/ghci.conf

" Input files for Haskell's hsc2hs would otherwise be recognized as the
" 'Hamster' file format
autocmd BufNewFile,BufRead *.hsc set filetype=haskell

" Disable spell checking for Cabal files
autocmd Filetype cabal setlocal nospell

" Run current file through hlint, use the asynchronous Screen*() functions
function! ScreenMakeHlint()
    if !executable("hlint")
        call EchoError("Can't find 'hlint' executable")
        return
    endif
    let old_makeprg = &makeprg
    try
        let &makeprg = "hlint --utf8 " . expand("%")
        call ScreenMake()
    finally
        let &makeprg = old_makeprg
    endtry
endfunction
command! Hlint call ScreenMakeHlint()

" ------------------------------------------------------------------------------
" Rust Support
" ------------------------------------------------------------------------------

" A couple of tweaks intended to be together with
" https://github.com/wting/rust.vim

function! SetupRust()
    " For some reason, the official Rust support for Vim does not seem to set
    " the compiler to rustc. Invoking the compiler script sets the make program
    " to rustc directly, though. Set back to 'make' here, the asynchronous make
    " feature will detect a Cargo project automatically anyway
    compiler rustc
    set makeprg=make
endfunc
autocmd FileType rust call SetupRust()

" Insert common curly brace construct like this:
"
" ... {
"     <Cursor>
" }
"
" When {{ is typed. Also works in the middle of the line
au Filetype rust
    \ ino <buffer> {{ <C-o>A {<CR>}<ESC><Up>o

" See Rust + Haskell Makefile template at the end of this file

" ------------------------------------------------------------------------------
" Git Support
" ------------------------------------------------------------------------------

" Basic Git Setup
"
" git config --global alias.lg
"     "log --date=relative --graph --format=\"%C(yellow)%h%Creset %C(green)%ad
"     %Creset %aN %C(bold cyan)%d%Creset %C(white)%s%Creset\" --all"
" git config --global user.name "Tim C. Schroeder"
" git config --global user.email tim@blitzcode.net
" git config --global color.ui true

" Various git related shortcuts
function! GitStatus()
    call CommandIntoScratchBuffer("git status && echo && " .
        \ "git log --oneline --decorate --graph --all -n10")
    setlocal ft=gitcommit
    setlocal nowrap
    " Some highlighting for the log part
    syntax match gitcommitBranch "HEAD"
    syntax match Constant '^*\ \S*\ '
endfunction
noremap ,gs :call GitStatus()<CR>
function! GitLog()
    call CommandIntoScratchBuffer("git log --oneline --decorate --graph --all")
    setlocal ft=gitrebase
    setlocal nowrap
endfunction
noremap ,gl :call GitLog()<CR>
function! GitDiff()
    call CommandIntoScratchBuffer("git diff")
    setlocal ft=git
endfunction
noremap ,gd :call GitDiff()<CR>

" ------------------------------------------------------------------------------
" HTML Support
" ------------------------------------------------------------------------------

function! SetupHTML()
    setlocal autoindent
    setlocal nosmartindent
    setlocal nocindent 
endfunc
autocmd FileType html call SetupHTML()
autocmd BufNewFile,BufRead *.ssi set filetype=html " Server Side Includes (SSI)

" ------------------------------------------------------------------------------
" Asynchronous Command Execution
" ------------------------------------------------------------------------------

" Small library of functions allowing asynchronous command execution from Vim
" using the GNU 'screen' utility, including make builds with QuickFix
" integration

function! ScreenCheck()
    " Make sure everything is setup correctly to use our asynchronous functions
    if has("unix") == 0
        call EchoError("Asynchronous functionality only available on UNIX")
        return 0
    endif
    if has("clientserver") == 0
        call EchoError("Need Vim compiled with +clientserver")
        return 0
    endif
    if v:servername == ""
        if has("mac")
            " See https://code.google.com/p/macvim/issues/detail?id=431
            call EchoError("Command server not running " .
                \ "(terminal MacVim does not support the server role)")
        else
            call EchoError("Command server not running " .
                \ "(terminal Vim needs to be started with '--servername name')")
        endif
        return 0
    endif
    if executable("screen") != 1
        call EchoError("Can't find 'screen' executable")
        return 0
    endif
    return 1
endfunction

function! ScreenIsRunning()
    " See if we already got a screen session running for this editor instance
    call system("screen -q -ls " . v:servername)
    " This should return 11 for a single instance matching, but on OS X it seems
    " to return 10. Works fine on Ubuntu, even though it is the exact same
    " version of screen, strange
    if v:shell_error == 10 || v:shell_error == 11
        return 1
    else
        return 0
    endif
endfunction

function! ScreenEnsureRunning()
    " Start a screen session, if necessary. We use v:servername as the name of
    " the session, as it is a unique identifier among all Vim instances
    if ScreenIsRunning() == 0
        " Not there, create session. The '-s -$SHELL' part is to make sure we
        " get a login shell with all settings, helpful for when we have an
        " actual terminal open and attached to look at the output of commands
        " launched from within Vim, everything will look and work as expected.
        " Also specify '-U' to have UTF8 working
        call system("screen -U -s -$SHELL -dm -S " . v:servername)
        if v:shell_error != 0
            call EchoError("Failed to create screen session (" .
                \ v:shell_error . ")")
            return 0
        endif
        if ScreenIsRunning() == 0
            call EchoError("Can't find screen session we just created")
            return 0
        endif
    endif
    return 1
endfunction

function! ScreenCleanUp()
    " Destroy editor's screen instance at exit
    if ScreenIsRunning() == 1
        call system("screen -S " . v:servername . " -X quit")
    endif
endfunction
if has("unix") == 1 && executable("screen") == 1
    autocmd VimLeave * call ScreenCleanUp()
endif 

function! ScreenSendCommand(cmd)
    " Send a command to this editor's screen session. The '-p 0' is needed
    " because otherwise sending commands to a screen session which we never
    " attached to will not work
    call system("screen -p 0 -S " . v:servername . " -X stuff " .
        \ shellescape(a:cmd . ""))
    if v:shell_error != 0
        call EchoError("Failed to send command to screen session")
        return 0
    endif
    return 1
endfunction

if !exists("g:last_cmd")
    let g:last_cmd = "./out"
endif
function! ScreenCommand(cmdarg)
    " Run passed command or query user for a command to be run asynchronously in
    " a screen session
    if ScreenCheck() == 0
        return
    endif
    if ScreenEnsureRunning() == 0
        return
    endif
    let cmd = a:cmdarg
    if cmd == ""
        let cmd = input("Async Command: ", g:last_cmd, "shellcmd")
        if cmd == ""
            return
        endif
    endif
    let g:last_cmd = cmd
    let callback = ScreenCreateCallback("ScreenCommandDone($?)")
    if ScreenSendCommand("cd " . getcwd() . " ; " . cmd . " ; " . callback) == 0
        return
    endif
    redraw | echo "Command started... (attach with 'screen -r " .
        \ v:servername . "' to monitor, ',xc' to cancel)"
endfunction
nmap <silent> ,xx  :call ScreenCommand("")<CR>
nmap <silent> ,xr  :call ScreenCommand(g:last_cmd)<CR>
nmap <silent> <F5> :call ScreenCommand(g:last_cmd)<CR>

function! ScreenCommandDone(retval)
    " Callback on command completion
    if a:retval == 0
        echohl scrnSuccess | echo " Command Successful " | echohl None
    else
        echohl scrnErrors |
            \ echo " Command Failed (" . a:retval . ") " | echohl None
    endif
endfunction

function! ScreenCancel()
    " Send CTRL+C to the screen session, do nothing if there's no session
    if ScreenIsRunning() == 0
        return
    endif
    call system("screen -p 0 -S " . v:servername . " -X stuff ''")
    if v:shell_error != 0
        call EchoError("Failed to send command to screen session")
    endif
endfunction
nmap <silent> ,xc :call ScreenCancel()<CR>

function! ScreenGetVimExecutable()
    " Little trick to get the name of our Vim executable. We need this as the
    " 'vim' in the path might be the system Vim compiled with minimal features,
    " while our Vim is MacVim etc. somewhere not in the path
    let s:vimexe = system("ps -o comm= -p $PPID")
    " Remove trailing newline before returning
    return shellescape(substitute(s:vimexe, "\n", "", ""))
endfunction

function! ScreenCreateCallback(callback)
    " Make a command which calls the supplied function on completion
    let s:vimexe = ScreenGetVimExecutable()
    return s:vimexe . " --servername " . v:servername .
        \ " --remote-expr \"" . a:callback . "\""
endfunction

function! ScreenMake()
    " Save files, run make asynchronously, load the results into the quickfix
    " list when done, report result and set signs
    if ScreenCheck() == 0
        return
    endif
    if ScreenEnsureRunning() == 0
        return
    endif
    try
        execute ":wa"
    catch
        call EchoError("Couldn't save all open files, aborting Make")
        return
    endtry
    let s:makeprg = GetMakeProgram()
    if s:makeprg == ""
        return
    endif
    " Change directory to sync up with Vim and execute make, saving the results
    " into a temporary file while echoing them to the screen, call us back once
    " the build is done
    let s:tmpbuild = tempname()
    let s:callback =
        \ ScreenCreateCallback("ScreenMakeDone('" . s:tmpbuild . "')")
    if ScreenSendCommand("cd " . getcwd() . " ; " . s:makeprg . " 2>&1| tee "
        \ . s:tmpbuild . " ; " . s:callback) == 0
        return
    endif
    redraw | echo "Build started... (executing '" . s:makeprg .
        \ "', attach with 'screen -r " .
        \ v:servername . "' to monitor, ',xc' to cancel)"
endfunction
nmap <silent> ,xm :call ScreenMake()<CR>
nmap <silent> <F7> :call ScreenMake()<CR>

function! ScreenMakeDone(tmpbuild)
    " Callback from screen when a make build has completed
    " Load results into quickfix window / list
    execute ":cgetfile " . a:tmpbuild
    " Determine and report build success status
    let qf_list = getqflist()
    let num_err = 0
    let num_warn = 0
    for error in qf_list
        if error.valid
            if error.type =~ 'w'
                let num_warn = num_warn + 1
            elseif error.type =~ 'e'
                let num_err = num_err + 1
            endif
        endif
    endfor
    if num_err != 0
        echohl scrnErrors | echo " Build Failed: " .
            \ num_err . " Error(s), " . num_warn . " Warning(s) "| echohl None
    elseif num_warn != 0
        echohl scrnWarnings | echo " Build Succeeded: " .
            \ num_warn . " Warning(s) " | echohl None
    else
        echohl scrnSuccess | echo " Build Succeeded " | echohl None
    endif
    " Add signs, if supported
    if has("signs")
        call AddQuickFixSigns()
    endif
    " GUI doesn't automatically update for remote commands
    execute ":redraw"
endfunction

" Script function to highlight errors, warnings or grep locations with markers
" in the sign column. Currently only used by the asynchronous make code
if has("signs")
    sign define generic texthl=SignGenericMarker text=▸M
    sign define error   texthl=SignErrorMarker   text=▸E
    sign define warning texthl=SignWarningMarker text=▸W

    function! AddQuickFixSigns()
        sign unplace *

        let qf_list = getqflist()
        let sign_cnt = 0

        for error in qf_list
            if error.valid
                if error.bufnr >= 0
                    let sign_cnt = sign_cnt + 1

                    if error.type =~ 'w'
                        let sign_name = 'warning'
                    elseif error.type =~ 'e'
                        let sign_name = 'error'
                    else
                        let sign_name = 'generic'
                    endif

                    exec 'sign place ' . sign_cnt
                        \ . ' line='   . error.lnum
                        \ . ' name='   . sign_name
                        \ . ' buffer=' . error.bufnr
                endif
            endif
        endfor
    endfunction

    " autocmd QuickFixCmdPost make call AddQuickFixSigns()
endif

" ------------------------------------------------------------------------------
" GUI Specific
" ------------------------------------------------------------------------------

if has("gui_running")

" Font
if has("gui_gtk2")
    " Default terminal font on Ubuntu
    set guifont=Monospace\ 8
    set linespace=-1
    " Alternatively, we can also install the usual 6x13 MiscFixed by following
    " the instructions from here:
    "
    " http://
    " talby.rcs.manchester.ac.uk/~rcs/_bits/gnome-terminal_and_misc-fixed-6x13
    "
    " and then use:
    "
    " set guifont=MiscFixed\ 10
    " set linespace=0
    "
    " Unfortunately, specifying multiple fonts or catching an exception when a
    " font can't be found doesn't seem to work on this version / platform, so we
    " can only specify a safe default here
elseif has("x11")
    " Normal xterm font
    set guifont=-Misc-Fixed-Medium-R-Normal--12-120-75-75-C-70-ISO8859-1
elseif has("gui_win32")
    " First try 6x13 Linux terminal font from http://www.ank.com.ar/fonts/, then
    " Consolas (ships with >=Vista, free download otherwise), then settle for
    " the standard Lucida Console font
    set guifont=6x13-ISO8859-1:h10,Consolas:h9,Lucida_Console:h9
    " No extra line spacing, 0 is already the non-Win32 default
    set linespace=0
elseif has("mac")
    " Try 6x13 Linux terminal font from
    " http://monkey.org/~marius/beautiful-fixed-width-fonts-for-osx.html
    try
        set guifont=6x13:h13
        set linespace=-1
    catch
        " Settle for Monaco (standard OS X terminal font)
        set guifont=Monaco:h10
        set linespace=-2
    endtry
    " Turn off font anti-aliasing for MacVim
    if has("gui_macvim")
        set noantialias
    endif
endif

" Highlight the screen line of the cursor with a subtly brighter shade of grey.
" Only do this for the current window. Disable for the QuickFix window as it
" interferes with the current error marker. Not done for the terminal version
" because of slowdown
set cursorline
autocmd WinLeave,BufLeave * set nocursorline
autocmd WinEnter,BufEnter * if &buftype != "quickfix" | set cursorline | endif

" Don't need the toolbar
set guioptions-=T

" Sets some basic mouse / GUI behavior. Make sure we're in xterm mode, even on
" Windows
behave xterm

" Don't use xterm style selection but move the cursor and pop-up a context menu
" for the right mouse button. Useful to correct spelling errors with the mouse
set mousemodel=popup_setpos

" Enable spell checking. Terminals don't have squiggly lines, so GUI only
set spell

" Make sure :mksession saves the size and position of the gVim root window
" itself
set sessionoptions+=resize
set sessionoptions+=winpos

" Reasonable default window size
if !exists("g:set_win_size")
    " Only do this once, don't want to resize the window when testing changes
    set lines=60
    set columns=90
endif
let g:set_win_size=1

" Add a new menu called 'Custom'. This is a place for useful commands that are
" hard to type or remember, but not used frequently enough to justify a key
" binding
"
" TODO: I hardly ever use this menu...
silent! aunmenu Custom
amenu &Custom.Sort\ Paragraph
    \ !}sort<CR>
amenu &Custom.Remove\ Trailing\ Blanks
    \ :%s=  *$==<CR>
amenu &Custom.Remove\ Blank\ Lines
    \ :g/^\s*$/d<CR>
amenu &Custom.List\ All\ Lines\ Containing\ Word\ Under\ Cursor
    \ :g/<C-r><C-w>/#<CR>
amenu &Custom.Copy\ Buffer\ to\ Clipboard
    \ gg"*yG
amenu &Custom.Convert\ Buffer\ To\ HTML
    \ :TOhtml<CR>
amenu &Custom.Highlight\ Long\ Lines.On
    \ :exec 'match Todo /\%>' . &textwidth . 'v.\+/'<CR>
amenu &Custom.Highlight\ Long\ Lines.Off
    \ :exec 'match none /\%>' . &textwidth . 'v.\+/'<CR>
    " TODO: This breaks URL highlighting

" MacVim automatically maps some of the Mac specific key combinations like
" CMD/OPT+ARROW which we want to map ourselves. Fortunately we can disable this
" as follows. Note that for this to work the variable has to be set in the
" vimrc, gvimrc is already to late
if has("gui_macvim")
    let macvim_skip_cmd_opt_movement=1
endif

" MacVim only shortcut to toggle fullscreen mode, also hide/show scrollbars
if has("gui_macvim")
    function! ToggleFS()
        if &fullscreen
            set nofullscreen
            set guioptions+=L
            set guioptions+=r
        else
            set guioptions-=L
            set guioptions-=r
            set fullscreen
        endif
    endfunction
    noremap ,fs :call ToggleFS()<CR>
endif

endif " has("gui_running")

" ------------------------------------------------------------------------------
" QuickName Buffer Switcher
" ------------------------------------------------------------------------------

" Taken from http://www.vim.org/scripts/script.php?script_id=2317, v1.1
"
" I put this here as I like to keep my configuration limited to a single file,
" makes syncing and deployment so much easier. A few modification from the
" original:
"
" - Change ls! to plain ls (no unlisted buffers)
" - Default hotkey is now TAB
" - Disabled check for 'already loaded?', makes debugging easier
" - Added a 'echo<cr>' to clear the last cmap from the screen
" - Broke up lines longer than 80 characters
" - Use five rows instead of four
" - Added a 'redraw' so everything works with lazyredraw enabled

if !exists("g:qname_hotkey") || g:qname_hotkey == ""
    let g:qname_hotkey = "<TAB>"
endif
exe "nmap" g:qname_hotkey ":cal QNameInit(1)<cr>:~:echo<cr>"
let s:qname_hotkey = eval('"\'.g:qname_hotkey.'"')

if exists("g:qname_loaded") && g:qname_loaded
    " (Check disabled - reloading useful for debugging)
    " finish
endif
let g:qname_loaded = 1

function! QNameRun()
    exe "redraw"
    cal s:colPrinter.print()
    echo "\rMatch" len(s:n)."/".len(s:ls) "names:" s:inp
    call inputsave()
    let _key = getchar()
    if !type(_key)
        let _key = nr2char(_key)
    endif

    if _key == "\<BS>"
        let s:inp = s:inp[:-2]
    elseif strlen(_key) == 1 && char2nr(_key) > 31
        let s:inp = s:inp._key
    endif
    if _key == "\<ESC>" || _key == "\<CR>"
        let _sel = s:colPrinter.sel
        if _key == "\<CR>" && _sel < len(s:n) && _sel >= 0
            call s:swb(matchstr(s:s[_sel], '<\zs\d\+\ze>'),"")
        endif
        cal QNameInit(0)
    elseif _key == "\<Up>"
        cal s:colPrinter.vert(-1)
    elseif _key == "\<Down>"
        cal s:colPrinter.vert(1)
    elseif _key == "\<Left>"
        cal s:colPrinter.horz(-1)
    elseif _key == "\<Right>"
        cal s:colPrinter.horz(1)
    elseif _key == s:qname_hotkey
        cal QNameInit(0)
    else
        cal s:build()
    endif
    redraws
    call inputrestore()
endfunc

function! QNameInit(start)
    if a:start
        cmap ~ cal QNameRun()<CR>:~
        let s:pro = "Prompt: "
        let s:cmdh = &cmdheight
        if a:start != -1
            let s:inp = ""
        endif
        call s:baselist()
        call s:build()
        exe "set cmdheight=".(s:colPrinter.trow+1)
    else
        cmap ~ exe "cunmap \x7E"<cr>
        exe "set cmdheight=".s:cmdh
    endif
endfunc

function! s:build()
    let s:s = []
    let s:n = []
    let s:blen = 0
    let _cmp = tolower(tr(s:inp, '\', '/'))
    for _line in s:ls
        let _name = matchstr(_line, '^.\{-}\ze \+<')
        if s:fmatch(tolower(_name), _cmp)
            cal add(s:s, _line)
            cal add(s:n, _name)
        endif
    endfor
    if len(s:n) > s:colPrinter.trow
        cal s:colPrinter.put(s:n)
    else
        cal s:colPrinter.put(s:s)
    endif
endfunc

function! s:swb(bno,mod)
    if bufwinnr(a:bno) == -1
        exe "hid b".a:mod a:bno
    else
        exe bufwinnr(a:bno) . "winc w"
    endif
endfunc

function! s:fmatch(src,pat)
    let _si = strlen(a:src)-1
    let _pi = strlen(a:pat)-1
    while _si>=0 && _pi>=0
        if a:src[_si] == a:pat[_pi]
            let _pi -= 1
        endif
        let _si -= 1
    endwhile
    return _pi < 0
endfunc

function! s:baselist()
    let s:ls = []
    redir @y | silent ls | redir END
    for _line in split(@y,"\n")
        if _line[3]!='u' || _line[6]!='-'
            let _bno = matchstr(_line, '^ *\zs\d*')+0
            let _fname = substitute(expand("#"._bno.":p"), '\', '/', 'g')
            if _fname == ""
                let _fname = "\xA0".matchstr(_line, '"\zs[^"]*')
            endif
            let _name = fnamemodify(_fname,":t")
            cal add(s:ls, _name." <"._bno."> ".fnamemodify(_fname,":h"))
        endif
    endfor
    let _align = max(map(copy(s:ls),'stridx(v:val,">")'))
    call map(s:ls, 'substitute(v:val, " <",
        \ repeat(" ",_align-stridx(v:val,">"))." <", "")')
    cal sort(s:ls, 1)
endfunc

let s:colPrinter = {"trow": 5}
function! s:colPrinter.put(its) dict
    let _cols = []
    let _trow = self.trow

    let _its = copy(a:its)
    let _len = len(_its)
    let _i = 0
    while _i < _len
        if _i+_trow <= _len
            cal add(_cols, remove(_its,0,_trow-1))
        else
            cal add(_cols, _its)
        endif
        let _i += _trow
    endwhile

    let _cpos = [0]
    let _cw = []
    let _t = 0
    for _li in _cols
        let _w = max(map(copy(_li),'strlen(v:val)'))+4
        let _t += _w
        cal add(_cpos,_t)
        cal add(_cw,_w)
    endfor

    let _rows = []
    for _i in range(_trow)
        let _row = []
        for _j in range(len(_cols))
            if _j*_trow+_i < _len
                cal add(_row,_cols[_j][_i])
            endif
        endfor
        cal add(_rows, _row)
    endfor

    let self.cols = _cols
    let self.cw = _cw
    let self.rows = _rows
    let self.cpos = _cpos
    let self.len = _len
    let self.lcol = 0
    let self.sel = 0
endfunc

function! s:colPrinter.horz(mv) dict
    let _t = self.sel + a:mv*self.trow
    if _t >= 0 && _t < self.len
        let self.sel = _t
    endif
endfunc

function! s:colPrinter.vert(mv) dict
    let _t = self.sel + a:mv
    let _len = self.len
    if _t < 0 && _len > 0
        let self.sel = _len-1
    elseif _t >= _len
        let self.sel = 0
    else
        let self.sel = _t
    endif
endfunc

function! s:colPrinter.print() dict
    let _len = self.len
    let _trow = self.trow
    if !_len
        echo "  No Match" repeat("\n",_trow)
        return
    endif
    let _sel = self.sel
    let _t = _sel/_trow
    let _cpos = self.cpos
    let _lcol = self.lcol
    let _tcol = &columns
    if _cpos[_lcol]+_tcol < _cpos[_t+1]
        let _rcol = _t
        let _pos = _cpos[_t+1]-_tcol-2
        while _cpos[_lcol] < _pos
            let _lcol += 1
        endwhile
        let _lcol -= _lcol > _t
    else
        if _t < _lcol
            let _lcol = _t
        endif
        let _rcol = len(_cpos)-1
        let _pos = _cpos[_lcol]+_tcol+2
        while _cpos[_rcol] > _pos
            let _rcol -= 1
        endwhile
        let _rcol -= _rcol > _lcol
    endif
    let _cw = self.cw
    let _pos = _cpos[_lcol]+_tcol
    let self.lcol = _lcol
    for _i in range(_trow)
        let _row = self.rows[_i]
        for _j in range(_lcol,_rcol)
            if _j*_trow+_i < _len
                let _txt = "  " . _row[_j]
                let _txt .= repeat(" ", _cw[_j] - strlen(_txt))
                let _txt = _txt[:_pos-_cpos[_j]-2]
                if _j*_trow + _i == _sel
                    echoh Search|echon _txt|echoh None
                else
                    echon _txt
                endif
            endif
        endfor
        echon "\n"
    endfor
endfunc

finish " Tell Vim to stop parsing now, Make and Config files start below

" ------------------------------------------------------------------------------
" C/C++ Makefile Template
" ------------------------------------------------------------------------------

# Main makefile for a C++ project using GLFW (http://www.glfw.org/)
#
# Targets:
#
#  all           Compile project (default)
#  libs          Build libraries
#  clean         Clean project build
#  clean-libs    Clean libraries

# Default target depending on main output
.PHONY : all
all : out 

# Compiler and linker flags
CXXFLAGS = -g -O3 -mtune=native -Wall -Wextra -Wno-unused-parameter \
	   -I ./glfw/include/
#          -fno-omit-frame-pointer
#          -fno-inline
#          -DNDEBUG
LDFLAGS  = ./glfw/lib/cocoa/libglfw.a \
	   -framework Cocoa           \
	   -framework OpenGL

# Sources and objects. We assume all .cpp files in the directory are meant to
# be compiled
SRC = $(wildcard *.cpp)
OBJ = $(SRC:.cpp=.o)

# Rule for producing the main output
LD_OBJ = $(OBJ) $(patsubst %, %/out, $(LIBSUBDIRS))
out : $(LD_OBJ)
	$(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) $(LD_OBJ)

# Only include this if we actually build the main output, don't want to
# generate dependencies for the clean targets etc.
ifeq ($(MAKECMDGOALS), )
 
# Automatically generate dependencies with the compiler for each source file
%.d: %.cpp
	@set -e; rm -f $@; \
	 $(CXX) -MM $(CXXFLAGS) $< > $@.$$$$; \
	 sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
	 $(RM) $@.$$$$

# Include the generated dependencies for object and dependency files. Be silent
# during the first compile where the .d files have not been generated yet and
# everything is recompiled
-include $(SRC:.cpp=.d)

endif

# Compile GLFW library
.PHONY : glfw
glfw:
	cd ./glfw/lib/cocoa/ && $(MAKE) -f Makefile.cocoa

# Compile libraries
.PHONY: libs
libs: glfw

# Clean libraries
.PHONY : clean-libs
clean-libs:
	cd ./glfw && $(MAKE) cocoa-clean

# Clean by deleting final output, dependency and object files
.PHONY : clean
clean:
	$(RM) out $(OBJ) $(OBJ:.o=.d)

" ------------------------------------------------------------------------------
" Haskell Makefile Template
" ------------------------------------------------------------------------------

# Main Makefile for a Haskell project which also compiles and links C++ code

WARN_FLAGS = -Wall
# LLVM_FLAGS = -fllvm -optlo-O3
GHC_FLAGS = $(PROF_FLAGS) $(WARN_FLAGS) $(LLVM_FLAGS) \
            -rtsopts -H512m -O2 -threaded

# By default, we build everything
.PHONY : all
all: out

# GHC compiler flags
WARN_FLAGS = -Wall
# LLVM_FLAGS = -fllvm -optlo-O3
GHC_FLAGS = $(PROF_FLAGS) $(WARN_FLAGS) $(LLVM_FLAGS) \
            -rtsopts -H512m -O2 -threaded

# Profile build
# Run with '+RTS -p -s' or '+RTS -xc'
.PHONY: profile
profile: PROF_FLAGS = -prof -fprof-auto -caf-all -osuf p_o -hisuf p_hi
profile: out

# Get some build options out of 'ghc --info'. This is mostly so we can pass
# '-m32' and '-arch i386' to clang/gcc/configure scripts in case we're
# running with a 32 bit version of GHC
GHC_CFLAGS  := $(shell ghc --info | ghc -e "fmap read getContents >>= \
               putStrLn . Data.Maybe.fromJust . lookup \
               \"C compiler flags\"")
GHC_LDFLAGS := $(shell ghc --info | ghc -e "fmap read getContents >>= \
               putStrLn . unwords . read . Data.Maybe.fromJust . lookup \
               \"Ld Linker flags\"")

# We assume all .cpp files in the directory are meant to be compiled
CXXSRC = $(wildcard *.cpp)
CXXOBJ = $(CXXSRC:.cpp=.o)

# All C++ linking related flags and files
CXX_LDFLAGS = -lstdc++ $(CXXOBJ)

HSSRC = $(wildcard *.hs)

# Build the full program
out: $(HSSRC) $(CXXOBJ)
	ghc $(GHC_FLAGS) -o $@ -lstdc++ --make Main $(CXX_LDFLAGS)

.PHONY : clean
clean:
	$(RM) out out.prof
	$(RM) $(HSSRC:.hs=.hi)   $(HSSRC:.hs=.o)
	$(RM) $(HSSRC:.hs=.p_hi) $(HSSRC:.hs=.p_o)
	$(RM) $(CXXOBJ) $(CXXOBJ:.o=.d)

# C++ compilation rules beyond this point

# Clang for the C++ sources
CXX = clang++
CXXFLAGS = -std=c++11 -g -O3 -mtune=native -Wall -Wextra -Wno-unused-parameter \
           $(GHC_CFLAGS)

# Only include this if we actually build the main program, don't want to
# generate dependencies for the clean targets etc.
ifeq ($(MAKECMDGOALS), )

# Automatically generate dependencies with the compiler for each C++ source file
%.d: %.cpp
	@set -e; rm -f $@; \
	 $(CXX) -MM $(CXXFLAGS) $< > $@.$$$$; \
	 sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
	 $(RM) $@.$$$$

# Include the generated dependencies for object and dependency files. Be silent
# during the first compile where the .d files have not been generated yet and
# everything is recompiled
-include $(CXXSRC:.cpp=.d)

endif

" ------------------------------------------------------------------------------
" Rust + Haskell Makefile Template
" ------------------------------------------------------------------------------

# Build project by invoking Haskell's Stack and Rust's Cargo build tools

ifeq (, $(shell which stack))
$(error "No Haskell 'stack' tool found, install from http://haskellstack.org/")
endif

ifeq (, $(shell which cargo))
$(error "No Rust 'cargo' tool found, install from https://www.rust-lang.org/")
endif

.PHONY: all
all:
	cargo build --release
	stack build

.PHONY: clean
clean:
	cargo clean
	stack clean
	$(RM) -rf .stack-work

" ------------------------------------------------------------------------------
" Shell Configuration
" ------------------------------------------------------------------------------

" Bash
export PS1="\[$(tput rev)\][\w]\[$(tput sgr0)\] "
alias ll='ls -FAlGh'

" tcsh
alias ll ls -FAlGh
set prompt="%{\033[7m[%}%/]%{\033[0m%} "
