" aramis' vimrc

""
"" Basics
""

" Encoding, UTF-8, other backend stuff

if !has('nvim')
  let &termencoding = &encoding " Keyboard and terminal both  use utf-8
endif
if !has('nvim')
  set encoding=utf-8            " Use utf-8 internally (Nvim always does this)
endif
setglobal fileencoding=utf-8    " Set default file encoding
scriptencoding utf-8            " This file uses some utf-8 symbols
set fileencodings=ucs-bom,utf-8 " Sets how (N)vim detects the current encoding
set fileformat=unix             " Unix file format

" Load matchit.vim if it hasn't already been loaded
if !exists('g:loaded_matchit') && findfile('plugin/matchit.vim', &runtimepath) ==# ''
  runtime! macros/matchit.vim
endif

" Install pathogen if it hasn't been installed yet
if empty(glob('~/.vim/autoload/pathogen.vim'))
  silent !curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
endif

" Load plugins with pathogen
execute pathogen#infect()

" Allow syntax colouring, filetype detection, and built-in plugins
filetype plugin indent on

" Enable syntax colouring
if !exists('g:syntax_on')
  syntax enable
endif

" Create autocmd group used by all my autocmds (cleared when sourcing vimrc)
augroup vimrc
  autocmd!
augroup END


""
"" Plug options
""

" Easy align

" Allow alignment of % character
let g:easy_align_delimiters = { '%': {
      \ 'pattern': '%\+', 'delimiter_align': 'l', 'ignore_groups': ['!Comment']
      \ }, }

" Start interactive EasyAlign in visual mode (e.g. vipga)
xmap ga <Plug>(EasyAlign)

" Start interactive EasyAlign for a motion/text object (e.g. gaip)
nmap ga <Plug>(EasyAlign)

" Goyo

let g:goyo_width = 68 " Leave a few extra chars more than textwidth

function! s:goyo_enter() " On goyo enter:
  set noshowcmd          " Don't show last command
  set scrolloff=999      " Centre current line
  Limelight              " Enable paragraph focus mode
endfunction

function! s:goyo_leave() " On goyo exit:
  set showcmd            " Show last command
  set scrolloff=1        " Show 1 line of context when scrolling
  Limelight!             " Disable paragraph focus mode
endfunction

" Activate respective function on goyo enter and leave
autocmd! vimrc User GoyoEnter nested call <sid>goyo_enter()
autocmd! vimrc User GoyoLeave nested call <sid>goyo_leave()

" Limelight

let g:limelight_default_coefficient = 0.6 " Set dimmed paragraph colour
let g:limelight_priority            = -1  " Don't overrule hlsearch

" Cool

let g:CoolTotalMatches = 1 " Show number of matches in command line

" MUComplete

" Complete dictionary words first before looking for similar words in markdown
" files.
let g:mucomplete#chains = {
      \ 'vim':      [ 'path', 'omni', 'cmd', 'c-p' ],
      \ 'markdown': [ 'dict', 'c-p' ],
      \ 'default':  [ 'path', 'c-p', 'omni' ],
      \ }

" Smooth-scroll

" We use a variable for this so that the duration of animations can be
" controlled more esaily
let g:scrolltime = 20

" Map the animated scrolls to their respective keys
noremap <silent> <c-u> :call smooth_scroll#up(&scroll,     g:scrolltime,   2)<CR>
noremap <silent> <c-d> :call smooth_scroll#down(&scroll,   g:scrolltime,   2)<CR>
noremap <silent> <c-b> :call smooth_scroll#up(&scroll*2,   2*g:scrolltime, 4)<CR>
noremap <silent> <c-f> :call smooth_scroll#down(&scroll*2, 2*g:scrolltime, 4)<CR>


""
"" Options
""

" Editing

set backspace=2                       " Backspace over everything
set clipboard^=unnamed                " Use macOS clipboard when available
set dictionary+=/usr/share/dict/words " Larger dictionary
set formatoptions=1cjqr               " Wrap and join comments intelligently
set mouse=nvc                         " Use mouse in all modes except insert
set nojoinspaces                      " Insert only one space after punctuation
set nostartofline                     " Keep cursor on same column
set nrformats+=alpha                  " Allow incrementing single alphabetics
set nrformats-=octal                  " Do not consider octal numbers
set sessionoptions-=options           " Don't keep manually-set options
set spelllang=en_gb                   " Use British English
set tags=./tags;,tags                 " Where to search for tags
set textwidth=80                      " 80 chars/line
set virtualedit=block                 " Better visual block mode

" User interface and interactions

set belloff=all           " Disable all bells
set completeopt+=noselect " Required for MUComplete
set completeopt+=preview  " Display extra info about the match
set completeopt=menuone   " Show completions when there is a match
set conceallevel=0        " Never conceal
set cursorline            " Make the current line more prominent
set display=lastline      " Show as much of partly-displayed lines as possible
set guioptions=           " Hide scrollbars in MacVim
set laststatus=2          " Always display statusline
set lazyredraw            " Don't redraw during macros
set list                  " Show invisibles
set listchars=trail:•     " Display a bullet point on trailing spaces
set pumheight=7           " Show 7 completion items before showing a scrollbar
set shortmess=acIT        " Abbreviate error messages
set showcmd               " Tells you if you press a non-alphabetic key
set splitbelow            " Create new splits to the bottom
set splitright            " Create new splits to the right
set tabpagemax=50         " Maximum number of tab pages

" Show colourcolumn on characters that go over textwidth
autocmd vimrc winEnter,BufEnter * call clearmatches()
      \ | call matchadd('ColorColumn', '\%81v', 100)

" Make redrawing smoother
if !has('nvim')
  set ttyfast
endif

" Time out mapping after 100ms
if !has('nvim') && &ttimeoutlen == -1
  set ttimeout
  set ttimeoutlen=100
endif

" Dynamic cursor shape that does not blink
if has('nvim')
  set guicursor=n-v-c-ci:block,i-ve:ver25,r-cr:hor20,o:hor50,n:blinkon0
else
  let &t_SI.="\e[5 q"
  let &t_SR.="\e[4 q"
  let &t_EI.="\e[1 q"
endif

" Smart window title

" See README.md

function! ProxyIconTitle(prefix,suffix) abort
  " This is the format used by Terminal.app to specify what file is being
  " edited
  let filenameurl = 'file://' . hostname() . expand('%:p')

  " This is how the command used to specify the window title is structured in
  " both Vim and Nvim, so we specify it here
  let args = a:prefix . filenameurl . a:suffix

  if has('nvim')
    " We use Nvim's chansend to send the escape codes because it is more direct
    " than printf
    let cmd = 'call chansend(2, "' . args . '")'
    execute cmd
  else
    " Use printf to call escape sequence because chansend() is unique to Nvim
    let cmd = 'silent !printf "' . args . '"'
    execute cmd
    execute 'redraw!'
    execute 'redraw!'
  endif
endfunction

" Make the gui's window title match with the others
if has('gui_running')
  set title
  set titlestring=%t\ —\ gvim
else
  " Only run proxy icon code if we are in Terminal.app (no other terminal
  " emulators have implemented this feature)
  if $TERM_PROGRAM ==# 'Apple_Terminal'

    " Tmux requires different escape codes, so we check if we are in tmux and
    " set which escape codes are called accordingly.
    if $TERM ==# 'screen-256color'
      " Refresh window title on BufEnter (when switching files) and on
      " FocusGained (when switching tmux panes)
      autocmd vimrc BufEnter,FocusGained *
            \ call ProxyIconTitle('\ePtmux;\e\e]6;','\e\\')
    else
      autocmd vimrc BufEnter,FocusGained *
            \ call ProxyIconTitle('\e]6;','')
    endif
  else
    " If setting a proxy icon is unavailable …
    if $TERM ==# 'screen-256color'
      " Set tmux window name to Vim filename (not entire path to file because
      " that is often too long for a tab name) in conjunction with the name of
      " the program being run ((N)vim).
      autocmd vimrc BufReadPost,FileReadPost,BufNewFile *
            \ call system("tmux rename-window
            \ $(echo " . expand("%:t") . " — vim")
    else
      " Same format as tmux, but for system window title
      set title
      if has('nvim')
        set titlestring=%t\ —\ nvim
      else
        set titlestring=%t\ —\ vim
      endif
    endif
  endif
endif

" Buffers and files

set autoread     " Read a file again if it has been modified outside of Vim
set hidden       " A buffer becomes hidden when abandoned
set path=$PWD/** " Recursively search directories

" Viminfo

set viminfo+=! " Save uppercase variables into viminfo (default behavoiur)

" Undo

" Only use persistent undo if Vim has it compiled in
if has('persistent_undo')
  set undodir=~/.vim/undo " Save undo history here
  set undofile            " Keep undo history between sessions

  " Automatically create directory for undo if it does not exist
  if !isdirectory(expand('~').'/.vim/undo')
    !mkdir -p $HOME/.vim/undo
  endif
endif

" Undo points
inoremap ! !<C-g>u
inoremap , ,<C-g>u
inoremap . .<C-g>u
inoremap : :<C-g>u
inoremap ; ;<C-g>u
inoremap ? ?<C-g>u

" Backup

set backup                  " Make a backup of every file you open in Vim
set backupdir=~/.vim/backup " Save backups here

" Automatically create directory for backups if it does not exist
if !isdirectory(expand('~').'/.vim/backup')
  !mkdir -p $HOME/.vim/backup
endif

" Swapfiles

set swapfile                " Create swapfiles
set directory=~/.vim/swap// " Save swap files here

" Automatically create directory for swapfiles if it does not exist
if !isdirectory(expand('~').'/.vim/swap')
  !mkdir -p $HOME/.vim/swap
endif

" Searching, substitute, global, etc

set gdefault   " Replace all instances on a line by default (use /g to disable)
set hlsearch   " Highlight search matches
set ignorecase " Ignore
set incsearch  " Jump to search results as you type
set smartcase  " Smarter capitalisation when searching

" Do not jump to the next match automatically when searching for the current
" word
nnoremap * *N
nnoremap # #N
nnoremap g* g*N
nnoremap g# g#N

" Live preview of substitute command
if has('nvim')
  set inccommand=split
endif

" Use ripgrep if available, then fall back to the silver searcher for grepping
if executable('rg')
  set grepprg=rg\ --vimgrep
  set grepformat^=%f:%l:%c:%m
elseif executable('ag')
  set grepprg=ag\ --vimgrep
  set grepformat^=%f:%l:%c:%m
endif

" Smarter grep command
command! -nargs=+ -complete=file_in_path -bar Grep
      \ silent! grep! <q-args> | redraw!

" Wrapping and scrolling

if exists('+breakindent')
  let &showbreak = '››› ' " Mark which lines have been wrapped with three arrows
  set breakindent         " Display indents before wrapped lines
  set breakindentopt=sbr  " Display  showbreak  before indent
endif
if has('linebreak')
  set linebreak           " Don't break words
endif
set scrolloff=1           " Show one line of context around the cursor
set sidescroll=1          " Scroll horizontally when wrapping is disabled
set sidescrolloff=5       " Show five columns of context around the cursor
set wrap                  " Soft wrap text

" Indentation

set autoindent   " Copy the previous line's indent to the current one
set expandtab    " Insert spaces when tab is pressed
set shiftround   " Always set indentation to a multiple of 2
set shiftwidth=2 " 2 spaces for indentation
set smarttab     " Indent and dedent in insert mode using <Tab>
let &softtabstop = &shiftwidth

" Command-line

set history=1000     " Save a lot of command-line history
set wildcharm=<C-z>  " Use <C-z> when tab completing (for incsearch tab mapping)
set wildignorecase   " Ignore case when completing files and directories
set wildmenu         " Better command-line completion
set wildmode=longest " Complete the longest common string
set wildmode+=full   " … then each full match

""
"" Statusline
""

" The file

set statusline=\     " Add space to start us off
set statusline+=%f\  " Filename relative to PWD
set statusline+=%y\  " Filetype

" Flags

set statusline+=%m   " Modified flag
set statusline+=%r   " Readonly flag
set statusline+=%=   " Separator to jump to the other side

" Position

set statusline+=%l   " Current line
set statusline+=\/   " Solidus
set statusline+=%-6L " Total lines in file (plus 6 padding on the right)
set statusline+=%3c  " Current column (padded for less than 4 chars)
set statusline+=\    " Finish off with a space


""
"" 'Leader' mappings
""

" Comma

" Juggling with buffers
" Comma, how you want to view the buffer, then b
" 'b' for buffer
nnoremap ,b :ls<CR>:buffer<Space>
nnoremap ,sb :ls<CR>:sbuffer<Space>
nnoremap ,vb :ls<CR>:vert sbuffer<Space>
nnoremap ,tb :tabnew<CR>:ls<CR>:buffer<Space>
nnoremap ,, :bnext<CR>

" Juggling with files
" Same for opening new files, just replace b with f
" 'f' for find
nnoremap ,f :find<Space>
nnoremap ,e :edit<Space>
nnoremap ,sf :sfind<Space>
nnoremap ,vf :vert sfind<Space>
nnoremap ,tf :tabfind<Space>

" Project-wide search
" 'g' for grep
nnoremap ,g :Grep<Space>

" Close stuff
" 'c' for close
nnoremap ,c :close<CR>
" 'tc' for tabclose
nnoremap ,tc :tabclose<CR>

" Space

" LaTeX + pandoc compilation mappings
" 'c' for compile
" 'a' for article
" 'c' for clean
" 'p' for presentation
" 'r' for report
nnoremap <Space>ca :call aramis#functions#pandocconvertarticle()<CR>
nnoremap <Space>cc :call aramis#functions#pandocclean()<CR>
nnoremap <Space>cp :call aramis#functions#pandocconvertpres()<CR>
nnoremap <Space>cr :call aramis#functions#pandocconvertreport()<CR>

" Auto-indent the whole file
" 'i' for indent
nnoremap <Space>i mzgg=G`zzz

" Sort the current paragraph alphabetically
" 's' for sort
nnoremap <Space>s mzvip:sort<CR>`z

" Enter distractioon-free mode using goyo
" 'w' for writing
nnoremap <Space>g :Goyo<CR>

" Toggle paragraph focus mode using limelight
" 'l' for limelight
nnoremap <Space>l :Limelight!!<CR>

" Redraw the screen
" 'r' for redraw
nnoremap <Space>r :redraw!<CR>

" Run the linter/compiler
" 'm' for make
nnoremap <Space>m :update<CR>:make<CR>

" Quick 'n' easy find-and-replace
nnoremap <Space><Space> :'{,'}s/\<<C-r>=expand("<cword>")<CR>\>/
nnoremap <Space>%       :%s/\<<C-r>=expand("<cword>")<CR>\>/


""
"" Misc mappings
""

" Jump through incsearch matches with <Tab>
cnoremap <expr> <Tab>   getcmdtype()
      \ == "/" \|\| getcmdtype()
      \ == "?" ? "<CR>/<C-r>/" : "<C-z>"

cnoremap <expr> <S-Tab> getcmdtype()
      \ == "/" \|\| getcmdtype()
      \ == "?" ? "<CR>?<C-r>/" : "<S-Tab>"

" Autoexpansion
inoremap (<CR> (<CR>)<Esc>O
inoremap {<CR> {<CR>}<Esc>O
inoremap {; {<CR>};<Esc>O
inoremap {, {<CR>},<Esc>O
inoremap [<CR> [<CR>]<Esc>O
inoremap [; [<CR>];<Esc>O
inoremap [, [<CR>],<Esc>O

" Mapping to search in the current screen
nnoremap <silent> \ :set scrolloff=0<CR>VHoL<Esc>:set scrolloff=1<CR>``/\%V

" Code                 | Purpose
" ---                  | ---
" :set scrolloff=0<CR> | Disable scrolloff so that we can select everything
" VHol<Esc>            | Select everything on screen, then exit visual mode
" :set scrolloff=1<CR> | Enable scrolloff again
" ``                   | Jump back to the last position
" /\%V                 | Start a search in the last visual selection

" Move around splits more easily
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l

" Move by screen lines, except when a count is given. This is for the relative
" line numbers used for quick jumps around.
nnoremap <expr> j v:count ? 'j' : 'gj'
nnoremap <expr> k v:count ? 'k' : 'gk'

" Use arrow keys to resize splits
nnoremap <left>  :vertical resize +2<cr>
nnoremap <right> :vertical resize -2<cr>
nnoremap <up>    :resize   -2<cr>
nnoremap <down>  :resize   +2<cr>

" Make 0 behave like ^ (i.e. not acting upon whitespace at the beginning of
" a line)
nnoremap 0 ^
xnoremap 0 ^
onoremap 0 ^

" Make Y behave like other capitals (why does everyone use this exact comment
" for this option? Because we all steal our vimrcs from other people? Or am
" I being cynical?).
nnoremap Y y$


""
"" Autocmds
""

" Do not show whitespace in insert mode
autocmd vimrc InsertEnter * set nolist
autocmd vimrc InsertLeave * set list

" Write name of current markdown file to ~/.currentfile for automatic LaTeX
" compilation
autocmd vimrc BufReadPost,BufWritePost,FileReadPost,BufNewFile *.md
      \ call system('echo ' . expand('%:p') . ' > $HOME/.currentfile')

" Show highlight group when pressing F10
nnoremap <F10> :echo "hi<"
      \ . synIDattr(synID(line("."),col("."),1),"name") . '> trans<'
      \ . synIDattr(synID(line("."),col("."),0),"name") . "> lo<"
      \ . synIDattr(synIDtrans(synID(line("."),col("."),1)),"name") . ">"<CR>

" Set makeprg to the appropriate linter for a given filetype
autocmd vimrc FileType ruby setlocal makeprg=ruby\ -c\ %
autocmd vimrc FileType sh   setlocal makeprg=shellcheck\ --format=gcc\ %


""
"" Colourscheme
""

" Custom Vim interface

" As colourcolumn has been hijacked for highlighting lines that go over
" textwidth, there is no need for its colouring to be subtle. It is because of
" this that we link colourcolumn to ErrorMsg -- to make it more prominent.
autocmd vimrc ColorScheme * highlight! link ColorColumn ErrorMsg

" Better syntax highlighting

" These are just some custom linkings to make Vim highlight Vimscript better
autocmd vimrc ColorScheme * highlight! link vimAutoCmdSfxList Type
autocmd vimrc ColorScheme * highlight! link vimIsCommand      Statement

" Set colourscheme
colorscheme apprentice