" vim: set sw=2 ts=2 sts=2 et tw=80 fmr={{{,}}} fdl=0 fdm=marker:
"
" The Main Vim Config File
"
" Contains plugin-independent, have-anywhere settings and mappings.
"
" Loads plugins from `~/dotfiles/plugins.vim`.
" Loads additional settings from `~/dotfiles/vim/settings/*.vim`.
"

" General Behaviours {{{
set nocompatible               " Use vim's full features (not vi-compatible)
set backspace=indent,eol,start " Backspace for dummies - deletes most things
set hidden                     " Change buffers without unsaved-warnings
set modeline                   " Support vim modelines (e.g. start of this file)
set mouse=a                    " Mouse support
set splitbelow                 " Normal splits open below current
set splitright                 " Vertical splits open right of current
set virtualedit=block          " Move cursor freely in visual block mode
if has('autocmd')
  " Clear existing autocommands to avoid side effects
  autocmd!

  " Detect a file's type and use plugin/indent configs if available
  filetype plugin indent on

  " Restore cursor position in a file (:help last-position-jump)
  autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$")
                     \|   execute "normal! g`\""
                     \| endif

  " Keep cursor at start of buffer for: Git commits
  autocmd FileType gitcommit call cursor(1, 1)
endif
" }}}

" Persistent Undo {{{
if has('persistent_undo')
  set undofile          " Keep undo history even when files are closed
  set undolevels=1000   " Max changes that can be undone
  set undoreload=10000  " Max lines to save for undo on a buffer reload
endif
" }}}

" Folders {{{

" Store vim working files in `~/.vim[something]` folders (e.g. `~/.vimswap`)
let folders = {}
if &swapfile   | let folders['swap']   = 'directory' | endif
if &backup     | let folders['backup'] = 'backupdir' | endif
if has('persistent_undo')
  if &undofile | let folders['undo']   = 'undodir'   | endif
endif

for [folder, setting] in items(folders)
  let path = expand('~/.vim'.folder.'/')
  " Create the folders
  if exists('*mkdir') && !isdirectory(path)
    call mkdir(path, 'p')
  endif
  " Use the folders
  if isdirectory(path)
    execute 'set' setting.'='.path
  else
    echo 'Warning! No folder:' path
  endif
endfor
" }}}

" Tabs and Whitespace {{{
set expandtab      " Convert tabs to spaces
set shiftround     " Indent to nearest tabstops
set shiftwidth=2   " Number of spaces for (auto)indent.
set tabstop=2      " Number of spaces to a tab
if has('autocmd')
  " Highlight trailing whitespace
  highlight ExtraWhitespace ctermbg=red guibg=red
  autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
  autocmd InsertLeave * match ExtraWhitespace /\s\+$/

  " Remove trailing whitespace on save
  autocmd BufWritePre * call RemoveTrailingWhitespace()
  function! RemoveTrailingWhitespace()
    let _s=@/ | let l = line(".") | let c = col(".")  " Save search and cursor
    %s/\s\+$//e                                       " Remove whitespace
    let @/=_s | call cursor(l, c)                     " Restore settings
  endfunction
endif
" }}}

" Search {{{
set hlsearch    " Highlight search terms
set ignorecase  " Case-insensitive search
set incsearch   " Find-as-you-type search
set smartcase   " Case-sensitive when uppercase present
" }}}

" Visuals {{{
set colorcolumn=+1                      " Highlight the textwidth limit
set cursorline                          " Highlight current line
set list                                " Display unprintables (like tabs)
set listchars=tab:>\ ,extends:#,nbsp:#  " Customise unprintables
set scrolloff=5                         " Lines to keep above/below cursor
set shortmess+=filmnrxoOtT              " Abbreviation of messages
set showmatch                           " Show matching brackets/parenthesis
set matchpairs+=<:>                     " Match <> as a pair, not just ()[]{}
set sidescroll=1                        " Side-ways scroll speed
set sidescrolloff=10                    " Columns to keep left/right of cursor
set wildmenu                            " Ex command tab-complete shows options
if has('syntax')
  syntax enable                         " Syntax highlighting
endif
set number                              " Show line numbers
if v:version >= 703
  set relativenumber                    " Show numbers relative to current line
endif
" }}}

" Folding {{{
if has('folding')
  set foldenable
  set foldmethod=indent  " Fold lines based on indentation levels
  set foldlevel=10       " Files start mostly unfolded, start 10 indents deep
endif
" }}}}

" Text Wrapping {{{
set textwidth=80               " Try to keep text within 80 chars
set nowrap                     " Start without text wrapping
set whichwrap=b,s,h,l,<,>,[,]  " Backspace and cursor keys wrap too
set linebreak                  " Do not break lines mid-word, only on spaces
" }}}

" Join (J) Behaviour {{{
set nojoinspaces        " Prevents inserting two spaces after punctuation
if v:version > 703 || v:version == 703 && has("patch541")
  set formatoptions+=j  " Remove comment chars when joining comment lines
endif
" }}}

" Status Line {{{
set laststatus=2  " always show the status line
set showcmd       " display command keypresses on the last line
" }}}

" Mappings {{{

" <Space> is a very convenient leader key
let mapleader = ' '

" Reload vimrc settings
map <leader>s :source $MYVIMRC<CR>:echo "vimrc reloaded..."<CR>

  " Buffers {{{

  " Save changes to a buffer
  map <silent> <Leader>w :up<CR>

  " Close a buffer
  map <silent> <Leader>q :q<CR>

  " List (:ls) all buffers, <CR> to close, or [number]<CR> to go to [number]
  map <Leader>l :buffers<CR>:buffer<Space>

  " Go to buffer [number] with [number]<CR>
  nmap <silent> <expr> <CR> (v:count > 0) ? ':<C-U>b'.v:count.'<CR>' : '<CR>'
  " }}}

  " Movement {{{

  " Jump to start and end of line with shift
  map H ^
  map L $

  " Move by screen lines, not vim lines, when wrapped (g-movements)
  for key in ['j', 'k', '0', '^', '$']
    for mode in ['n', 'o', 'x']
      execute mode.'map <silent> <expr>' key '&wrap ? "g'.key.'" : "'.key.'"'
    endfor
  endfor

  " Move between splits easily
  map <C-j> <C-w>j
  map <C-k> <C-w>k
  map <C-h> <C-w>h
  map <C-l> <C-w>l
  " }}}

  " Visuals {{{

  " Equalise split sizes
  map <Leader>= <C-w>=

  " Half-screen horizontal scrolling instead of full-screen
  map zl zL
  map zh zH

  " Backspace to hide search highlight temporarily
  nmap <silent> <backspace> :nohlsearch<CR>

  if v:version >= 703
    " Toggle both types of line numbers together
    map <Leader>n :set number! relativenumber! number?<CR>
  endif
  " }}}

  " Folding {{{
  if has('folding')

    " Toggle the fold of the current line with double-space
    nmap <silent> <expr> <Leader><Space> foldlevel('.') ? 'za' : '<Space>'

    " Set fold level 0-9 with zf[number]
    for lvl in range(10)
      execute 'nmap <silent> zf'.lvl.' :set foldlevel='.lvl.'<CR>'
    endfor
  endif
  " }}}

  " Editing {{{

  " Yank to end of line. Consistent with `C` and `D`
  map Y y$

  " Convenient escape - mash `jk`
  imap kj <Esc>
  imap jk <Esc>

  " Increment numbers with `<C-c>`. For when `<C-a>` is used by tmux
  map <C-c> <C-a>

  " Convert tabs to spaces
  map <silent> <Leader><Tab> :retab<CR>

  " Toggle paste mode - no autoindenting of pasted material
  nmap <silent> <F2> :set paste! paste?<CR>
  set pastetoggle=<F2>

  " Search for version control conflict markers
  map <Leader>c /\v^[<\|=>]{7}( .*\|$)<CR>
  " }}}
" }}}

" Commands {{{
if has('user_commands')

  " View unsaved changes in a file (:help DiffOrig)
  command! DiffOrig vert new | set buftype=nofile | read ++edit # | 0d_
        \| diffthis | wincmd p | diffthis

  " Insert a date/time on the next line
  command! Date :put = strftime('%d/%m/%Y %I:%M%p')
endif
" }}}

" Load Plugins {{{
if filereadable(expand("~/dotfiles/vim/plugins.vim"))
  source ~/dotfiles/vim/plugins.vim
endif
" }}}

" Load Extra Settings {{{
for filePath in split(globpath('~/dotfiles/vim/settings', '*.vim'), '\n')
  execute 'source' filePath
endfor
" }}}
