""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Basic configuration
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

" Remove ALL autocommands for the current group.
:autocmd!

" Use Vim settings, rather than Vi settings (much better!). This must be first, because it changes other options as a side effect.
set nocompatible

" Start pathogen
runtime bundle/vim-pathogen/autoload/pathogen.vim
execute pathogen#infect()

" Force English UI
language messages en_US.utf-8

" In many terminal emulators the mouse works just fine, thus enable it.
set mouse=a

" Special handling for urxvt
if &term =~# 'rxvt-unicode'
  " Mouse does not work in urxvt and more than 223 columns.
  set ttymouse=urxvt
  set t_kh=[7~
  set t_@7=[8~

  " Fix ctrl-arrow keycode mapping
  set <C-Left>=Od
  set <C-Right>=Oc
endif

" Fix <ESC> timeout.
set timeoutlen=1000
set ttimeoutlen=100

" Sets how many lines of history VIM has to remember
set history=500

" Enable filetype plugins
filetype plugin on
filetype indent on

" Reread files when they are changed from the outside (and if there are no changes in the buffer)
set autoread

" Show n lines above and below of current line
set scrolloff=5
set sidescrolloff=7
set sidescroll=1

" Commandline completion and extra line with suggestions
set wildmenu
set wildmode=longest:full,full

" Always show current position and line numbers
set ruler
set number

" Show content of last line if it does not fit
set display+=lastline

" A buffer becomes hidden when it is abandoned
set hidden

" Configure backspace so it acts as it should act
set backspace=eol,start,indent
set whichwrap=b,s,<,>,[,],h,l

" Display incomplete commands
set showcmd
set showmode

" Ignore case when searching, but be smart (if uppercase search, search uppercase). Highlight search and search already while typing.
set ignorecase
set smartcase
set hlsearch
set incsearch

" Don't redraw while executing macros (good performance config)
set lazyredraw

" No annoying sound on errors
set noerrorbells
set novisualbell
set t_vb=

" Colors
syntax enable
set background=dark

let s:colorscheme='default'
let s:lightlinecolorscheme='default'
if $TERM_TRUECOLOR ==? 'true'
  set termguicolors
  let s:colorscheme='gruvbox'
endif
if has('gui_running')
  let s:colorscheme='gruvbox'
endif

if s:colorscheme ==# 'gruvbox'
  let g:gruvbox_contrast_dark='hard'
  let g:gruvbox_sign_column='bg0'
  let g:gruvbox_invert_selection=0
  if &termguicolors || has('gui_running')
    colorscheme gruvbox
    highlight Search term=reverse cterm=none ctermfg=234 ctermbg=214 gui=none guifg=#1d2021 guibg=#fabd2f
    highlight ExtraWhitespace ctermbg=167 guibg=#fb4934
    if !get(g:, 'gruvbox_invert_selection', 1)
      highlight Visual term=reverse cterm=reverse ctermfg=223 ctermbg=234 gui=reverse guifg=#ebdbb2 guibg=#1d2021
    endif
  else
    let g:gruvbox_termcolors=16
    colorscheme gruvbox
    highlight Normal ctermbg=none
    highlight Search term=reverse cterm=none ctermfg=0 ctermbg=11 gui=none guifg=#1d2021 guibg=#fabd2f
    highlight ExtraWhitespace ctermbg=9 guibg=#fb4934
    if !get(g:, 'gruvbox_invert_selection', 1)
      highlight Visual term=reverse cterm=reverse ctermfg=15 ctermbg=0 gui=reverse guifg=#ebdbb2 guibg=#1d2021
    endif
  endif
  let s:lightlinecolorscheme='gruvboxlike'
else
  colorscheme default
  set background=dark
  highlight SpecialKey  ctermfg=240
  highlight ColorColumn ctermbg=236
  highlight DiffAdd     cterm=reverse ctermfg=10 ctermbg=0 gui=reverse guifg=#b8bb26 guibg=#282828
  highlight DiffChange  cterm=reverse ctermfg=8  ctermbg=0 gui=reverse guifg=#928374 guibg=#282828
  highlight DiffDelete  cterm=reverse ctermfg=9  ctermbg=0 gui=reverse guifg=#fb4934 guibg=#282828
  highlight DiffText    cterm=reverse ctermfg=11 ctermbg=0 gui=reverse guifg=#83a598 guibg=#282828
endif

" Enable line length warning if textwidth is set
set colorcolumn=+1
autocmd OptionSet textwidth set colorcolumn=+1

" Set extra options when running in GUI mode
if has('gui_running')
  set guioptions-=T
  set guioptions+=e
  set guioptions-=l
  set guioptions-=L
  set guioptions-=r
  set guioptions-=b
  set guioptions-=m
  set guitablabel=%M\ %t
  imap <C-Space> <C-P>
  set guicursor=a:blinkon0
endif

" Default file format
set encoding=utf8
set fileformats=unix,dos,mac

" Turn backup off
set nobackup
set nowritebackup
set directory=~/.vim/swap//,.

" Enable persistent undo
if has('persistent_undo')
  set undodir=~/.vim/undo/
  set undofile
endif

" Use spaces instead of tabs
set expandtab
set smarttab
set shiftwidth=2
set tabstop=8
set softtabstop=4

set autoindent
set smartindent

" Wrap long lines
set wrap
set linebreak
set breakindent
let &showbreak = '↪ '

" Don't show the splash screen when starting
set shortmess+=I

" Block selection after line ending
set virtualedit=block

" Select where to search for open buffers
set switchbuf=useopen,usetab
set showtabline=1

" Don't search in non-opened files for completion
set complete-=i

" Return to last edit position when opening files (except commit messages)
autocmd BufReadPost * call s:RestoreCursorPosition()
function! s:RestoreCursorPosition()
  if &filetype =~? '\vsvn|commit'
    call cursor(1,1)
    return
  endif

  if line("'\"") > 0 && line("'\"") <= line("$")
    exe "normal! g`\""
    normal! zz
  endif
endfunction

" Set default filetype
autocmd BufRead,BufNewFile * if &ft == '' | setlocal ft=text | endif

" Set extra filetype detection
autocmd BufRead,BufNewFile *.less   setlocal filetype=css
autocmd BufRead,BufNewFile *.cls    setlocal filetype=tex
autocmd BufRead,BufNewFile *.ino    setlocal filetype=cpp
autocmd BufRead,BufNewFile *.launch setlocal filetype=xml
let g:tex_flavor='latex'

" Delete comment character when joining commented lines
set formatoptions+=j

" Close vim if last window is NERDTree
autocmd BufEnter * if (winnr("$") == 1 && exists("b:NERDTree") && b:NERDTree.isTabTree()) | q | endif

" Fix annoyances in the QuickFix window
autocmd FileType qf setlocal nonumber nolist scrolloff=0

" Heurindent config
let g:heurindent_ratio_threshold = 0.4

" NERDTree config
let g:NERDTreeMinimalUI=1

" tcomment config
let g:tcomment_mapleader_uncomment_anyway = ''
let g:tcomment_mapleader_comment_anyway = ''
let g:tcomment_textobject_inlinecomment = ''
call tcomment#type#Define('c', tcomment#GetLineC('// %s'))

" gitgutter config
let g:gitgutter_map_keys = 0
let g:gitgutter_enabled = 1
autocmd VimEnter,FilterWritePre * if &diff | GitGutterDisable | endif

" fugitive config
let g:fugitive_git_executable = 'LANG=en_US.UTF-8 LANGUAGE=en_US git'

" better-whitespace config
let g:better_whitespace_operator = ''
let g:show_spaces_that_precede_tabs = 1

" lightline config
set laststatus=2
set noshowmode

let g:lightline = {
      \   'active': {
      \     'left':  [['mode'], ['truncate', 'filename'], ['fugitive']],
      \     'right': [['percentwin'], ['lineinfo'], ['paste', 'fileencoding', 'fileformat', 'indentation']]
      \   },
      \   'inactive': {
      \     'left':  [['filename']],
      \     'right': [['percentwin']]
      \   },
      \   'tab': {
      \     'active':   ['tabinfo'],
      \     'inactive': ['tabinfo']
      \   },
      \   'component': {
      \     'paste':    '%{&paste?"paste":""}',
      \     'truncate': '%<',
      \   },
      \   'component_function': {
      \     'fugitive':     'LightlineFugitive',
      \     'filename':     'LightlineFilename',
      \     'mode':         'LightlineMode',
      \     'fileformat':   'LightlineFileformat',
      \     'fileencoding': 'LightlineFileencoding',
      \     'indentation':  'LightlineIndentation',
      \   },
      \   'tab_component_function': {
      \     'tabinfo': 'LightlineTabinfo',
      \   },
      \   'tabline': { 'right': [] },
      \   'colorscheme': s:lightlinecolorscheme,
      \   'separator':    { 'left': '', 'right': '' },
      \   'subseparator': { 'left': '', 'right': '' },
      \   'tabline_separator':    { 'left': '', 'right': '' },
      \   'tabline_subseparator': { 'left': '', 'right': '' },
      \   'mode_map': { 'c': 'NORMAL' },
      \   'enable': { 'statusline': 1, 'tabline': 1 },
      \ }

" vim-swap config
let g:swap#rules = deepcopy(g:swap#default_rules)
let g:swap#rules += [
      \   {
      \     'description': 'Attributes in XML tags',
      \     'filetype':    ['xml'],
      \     'surrounds':   ['<?\?\h\w*\_s*', '\_s*[/?]\?>'],
      \     'delimiter':   ['\_s\+'],
      \     'immutable':   ['\%(^\_s\|\n\)\s*', '\s\+$'],
      \     'quotes':      [['"', '"'], ["'", "'"]]
      \   },
      \ ]

" ack.vim config
if executable('rg')
  let g:ackprg = 'rg --vimgrep --smart-case'
else
  let g:ackprg = 'grep -nRHis --color=never'
endif
let g:ackhighlight = 1

" undotree config
let g:undotree_ShortIndicators = 1
let g:undotree_HelpLine = 0


""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Mappings
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

" Set <leader> key
let mapleader = ","
let g:mapleader = ","

" Make ZQ safer by requiring a confirmation
noremap <silent> ZQ :confirm quit<cr>

" Movement in wrapped lines
noremap j gj
noremap k gk
noremap gj j
noremap gk k

" Smart way to move between windows
noremap <C-j> <C-W>j
noremap <C-k> <C-W>k
noremap <C-h> <C-W>h
noremap <C-l> <C-W>l

" Close the current buffer without closing window
noremap <leader>bd :Bclose<cr>

" Show list of open buffers
noremap <leader>bl :buffers<cr>

" Close all buffers
map <silent> <leader>bc :if confirm('Close all buffers?', "&Yes\n&No", 1)==1 <Bar> :%bd! <Bar> endif<CR>

" Useful mappings for managing tabs
noremap <silent> <leader>tn :tabnew<cr>
noremap <silent> <leader>to :tabonly<cr>
noremap <silent> <leader>tc :tabclose<cr>
noremap          <leader>tm :tabmove<space>
noremap <silent> <leader>tl :tabs<cr>

" Open a new tab with the current buffer's path
map <leader>te :tabedit <c-r>=expand("%:p:h")<cr>/

" Directly jump to tag
nnoremap <leader>1 1gt
nnoremap <leader>2 2gt
nnoremap <leader>3 3gt
nnoremap <leader>4 4gt
nnoremap <leader>5 5gt
nnoremap <leader>6 6gt
nnoremap <leader>7 7gt
nnoremap <leader>8 8gt
nnoremap <leader>9 9gt

" Switch between last and current tab
autocmd TabLeave * let g:lasttab = tabpagenr()
nnoremap <silent> <leader>^ :execute "tabn ".g:lasttab<CR>

" Switch CWD to the directory of the open buffer
noremap <leader>cd :cd %:p:h<cr>:pwd<cr>

" Remove search highlight
noremap <silent> <leader>h :nohlsearch<cr>

" Disable some annoying mappings
noremap Q <nop>
noremap K <nop>

" CTRL-U and CTRL-W in insert mode deletes a lot. Add an undo point.
inoremap <C-U> <C-G>u<C-U>
inoremap <C-W> <C-G>u<C-W>

" Remap word-completion from <C-P> to <C-space> (see also GUI section)
imap <Nul> <C-P>

" Toggle NERDtree
noremap <silent> <leader>f :NERDTreeToggle<CR>
noremap <silent> <leader>F :NERDTreeFind<CR>

" Visual mode pressing * or # searches for the current selection
vnoremap <silent> * :<C-U>
  \let old_reg=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
  \gvy/<C-R><C-R>=substitute(
  \escape(@", '/\.*$^~['), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  \gV:call setreg('"', old_reg, old_regtype)<CR>
vnoremap <silent> # :<C-U>
  \let old_reg=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
  \gvy?<C-R><C-R>=substitute(
  \escape(@", '?\.*$^~['), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  \gV:call setreg('"', old_reg, old_regtype)<CR>

" Start grep
noremap <leader>/ :Ack<space>

" Quickfix list (open, next, previous)
noremap <silent> <leader>cc :botright cope<cr>
noremap <silent> <leader>n :cn<cr>
noremap <silent> <leader>N :cp<cr>

" Toggle paste mode on and off
noremap <silent> <leader>sp :setlocal paste!<CR>

" Toggle colorcolumn
noremap <silent> <leader>sc :<C-U>call ToggleColorColumn(v:count)<CR>

" Toggle gitgutter
noremap <silent> <leader>sg :GitGutterToggle<CR>

" Show tab characters
set listchars=tab:¦·,extends:›,precedes:‹,nbsp:␣
set list
noremap <silent> <leader>sl :setlocal nolist!<CR>

" Toggle whitespace highlighting
noremap <silent> <leader>sw :ToggleWhitespace<CR>

" Re-detect filetype
nnoremap <silent> <leader>d :filetype detect<CR>

" Convenient command to see the difference between the current buffer and the
" file it was loaded from, thus the changes you made.
if !exists(":DiffOrig")
  command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis
        \ | wincmd p | diffthis
endif

" Limelight configuration
let g:limelight_conceal_ctermfg = 244
noremap <silent> <leader>l :Limelight!!<CR>

" Show undo tree
noremap <silent> <leader>u :UndotreeToggle<CR>

" EasyMotion
map <Space> <Plug>(easymotion-prefix)

" Fast saving
nnoremap <leader>w :w<cr>

" Show file overview (using ctags)
noremap <silent> <leader>o :TagbarToggle<CR>

" tex-autoclose
autocmd FileType tex inoremap <buffer> <silent> <C-e> <C-\><C-o>:call TexCloseCurrentEnv()<cr>

" Load or start a session
noremap <leader>O :ObsessionManager<space>

" gitgutter text-objects
omap ih <Plug>GitGutterTextObjectInnerPending
omap ah <Plug>GitGutterTextObjectOuterPending
xmap ih <Plug>GitGutterTextObjectInnerVisual
xmap ah <Plug>GitGutterTextObjectOuterVisual

" fugitive git shortcuts
nnoremap <silent> <leader>gs :Gstatus<CR>
nnoremap <silent> <leader>gc :Gcommit<CR>
nnoremap <silent> <leader>gl :Glog<CR>
nnoremap          <leader>ge :Gedit<space>
nnoremap          <leader>gr :Gread<space>
nnoremap <silent> <leader>gw :Gwrite<CR>
nnoremap <silent> <leader>gd :Gdiff<CR>
nnoremap <silent> <leader>gb :Gblame<CR>

" textobj for numbers
call textobj#user#plugin('number', {
      \   '-': {
      \     'pattern': '\v(0[xXbBoOdD][[:xdigit:]]+)|(-?[0-9]+([.,][0-9]+)?([eE]-?[0-9]+)?)',
      \     'select': ['an', 'in'],
      \   },
      \ })

" textobj for parameters/swappables
omap i, <Plug>(swap-textobject-i)
xmap i, <Plug>(swap-textobject-i)
omap a, <Plug>(swap-textobject-a)
xmap a, <Plug>(swap-textobject-a)


""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"    Helper functions
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

" Don't close window, when deleting a buffer
command! Bclose call <SID>BufcloseCloseIt()
function! <SID>BufcloseCloseIt()
  let l:currentBufNum = bufnr("%")
  let l:alternateBufNum = bufnr("#")

  if buflisted(l:alternateBufNum)
    buffer #
  else
    bnext
  endif

  if bufnr("%") == l:currentBufNum
    new
  endif

  if buflisted(l:currentBufNum)
    execute("bdelete! ".l:currentBufNum)
  endif
endfunction

" Complete LaTeX environment (e.g. \begin{foo.)
function! TexCloseCurrentEnv()
  let line = getline('.')
  let linestart = strpart( line, 0, col('.'))

  let env = matchstr( linestart, '\v%(\\begin\{)@<=[a-zA-Z0-9*]+$')
  if env != ''
    exec "normal! a\<C-G>u}\<cr>\\end{" . env . "}\<esc>k"
    startinsert!
  else
    " Not a begin tag. Resume insert mode as if nothing had happened
    if col('.') < strlen(substitute(line, ".", "x", "g"))
      normal! l
      startinsert
    else
      startinsert!
    endif
  endif
endfunction

" Toggle colorcolumn
let g:colorcolumn_default_width = 81
function! ToggleColorColumn(width)
  if a:width != 0
    let &l:colorcolumn = a:width
    return
  endif

  if &l:textwidth == 0
    if &l:colorcolumn == '' || &l:colorcolumn == '+1'
      let &l:colorcolumn = g:colorcolumn_default_width
    else
      let &l:colorcolumn = ''
    endif
  else
    if &l:colorcolumn == ''
      let &l:colorcolumn = '+1'
    else
      let &l:colorcolumn = ''
    endif
  endif
endfunction


""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"    Lightline functions (yes, this is too much code for a statusline…)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

function! LightlineIsSpecial()
  return &ft =~? '^help\|undotree\|tagbar\|nerdtree\|qf\|fugitiveblame$'
endfunction

function! LightlineModified()
  return &modified ? '[+]' : &modifiable ? '' : '[-]'
endfunction

function! LightlineReadonly()
  return &modifiable && &readonly ? '[RO]' : ''
endfunction

function! LightlineFilename()
  if &ft ==? 'nerdtree' && exists('b:NERDTree')
    return b:NERDTree.root.path.str()
  endif

  if &ft ==? 'help' && !&modifiable | return expand('%:t') | endif

  if LightlineIsSpecial() | return '' | endif

  let fname = expand('%:~:.')
  if strwidth(fname) > 60 | let fname = pathshorten(fname) | endif
  let readonly = LightlineReadonly()
  let modified = LightlineModified()
  return  ('' != readonly ? readonly . ' ' : '') .
        \ ('' != fname ? fname : '[No Name]') .
        \ ('' != modified ? ' ' . modified : '')
endfunction

function! LightlineFugitive()
  if LightlineIsSpecial() || winwidth(0) < 80 | return '' | endif
  return fugitive#head(7)
endfunction

function! LightlineMode()
  if !&modifiable
    return  &ft ==? 'nerdtree'      ? 'NERD' :
          \ &ft ==? 'undotree'      ? 'UNDO' :
          \ &ft ==? 'tagbar'        ? 'TAGS' :
          \ &ft ==? 'fugitiveblame' ? 'BLAME' :
          \ &ft ==? 'qf'            ? 'QUICKFIX' :
          \ &ft ==? 'help'          ? 'HELP' :
          \ lightline#mode()
  endif

  return  lightline#mode()
endfunction

function! LightlineFileformat()
  return LightlineIsSpecial() || winwidth(0) < 80 || &ff ==? 'unix' ? '' : &ff
endfunction

function! LightlineFileencoding()
  return LightlineIsSpecial() || winwidth(0) < 80 || &fenc ==? 'utf-8' ? '' : &fenc
endfunction

function! LightlineIndentation()
  if LightlineIsSpecial() || winwidth(0) < 60 | return '' | endif

  let sw = &shiftwidth ? &shiftwidth : &tabstop
  if &expandtab
    return 'sw:'.sw
  elseif &tabstop == sw
    return 'ts:'.&tabstop
  else
    return 'sw:'.sw.' ts:'.&tabstop
  endif
endfunction

function! LightlineTabinfo(n)
  let buflist = tabpagebuflist(a:n)
  let winnr = tabpagewinnr(a:n)
  " let wincount = tabpagewinnr(a:n, '$')
  let fname = expand('#'.buflist[winnr - 1].':t')
  let modified = gettabwinvar(a:n, winnr, '&modified') ? '*' : ''
  return a:n . ' ' . (empty(fname) ? '[No Name]' : fname) . modified
        " \ . (wincount > 1 ? ' (' . wincount . ')' : '')
endfunction
