"
" Vim configuration
" Author:   Alisue <lambdalisue@hashnote.net>
" URI:      http://hashnote.net/
" Platform: Linux, Mac OS X
"

" skip if the live Vim is vim-tiny or vim-small
if !1 | finish | endif

" reset settings for reloading vimrc
augroup MyAutoCmd
  autocmd!
augroup END

" disable unnecessary default plugins
let g:loaded_gzip              = 1
let g:loaded_tar               = 1
let g:loaded_tarPlugin         = 1
let g:loaded_zip               = 1
let g:loaded_zipPlugin         = 1
let g:loaded_rrhelper          = 1
let g:loaded_2html_plugin      = 1
let g:loaded_vimball           = 1
let g:loaded_vimballPlugin     = 1
let g:loaded_getscript         = 1
let g:loaded_getscriptPlugin   = 1
let g:loaded_netrw             = 1
let g:loaded_netrwPlugin       = 1
let g:loaded_netrwSettings     = 1
let g:loaded_netrwFileHandlers = 1

if !has('gui_running')
  " disable matchparen in non GUI Vim
  let g:loaded_matchparen = 1
endif

" environmental variables {{{
if has('nvim')
  set shell=/bin/zsh
else
  set shell=/bin/bash
endif

" Add several required path to $PATH
call rook#add_path([
      \ '/usr/local/texlive/2013/bin/x86_64-linux',
      \ '/usr/local/texlive/2013/bin/x86_64-darwin',
      \ '~/.pyenv/bin',
      \ '~/.plenv/bin',
      \ '~/.rbenv/bin',
      \ '~/.ndenv/bin',
      \ '~/.pyenv/shims',
      \ '~/.plenv/shims',
      \ '~/.rbenv/shims',
      \ '~/.ndenv/shims',
      \ '~/.anyenv/envs/pyenv/bin',
      \ '~/.anyenv/envs/plenv/bin',
      \ '~/.anyenv/envs/rbenv/bin',
      \ '~/.anyenv/envs/ndenv/bin',
      \ '~/.anyenv/envs/pyenv/shims',
      \ '~/.anyenv/envs/plenv/shims',
      \ '~/.anyenv/envs/rbenv/shims',
      \ '~/.anyenv/envs/ndenv/shims',
      \ '~/.cabal/bin',
      \ '~/.vim/bundle/vim-themis/bin',
      \])

let $MYVIMRUNTIME = expand('~/.vim')
let $MYVIMRC  = resolve(rook#normpath('vimrc'))
let $MYGVIMRC = resolve(rook#normpath('gvimrc'))

let $PYENV_ROOT =
    \ isdirectory(expand('~/.anyenv/envs/pyenv'))
    \   ? expand('~/.anyenv/envs/pyenv')
    \   : isdirectory(expand('~/.pyenv'))
    \     ? expand('~/.pyenv')
    \     : ''
" }}}

" fundemental config {{{

" history ':' commands and search patterns as much as possible (max: 10000)
if &history != 10000
  set history=10000
endif

" define <Leader> and <LocalLeader>
let g:mapleader = ';'
let g:maplocalleader = ','

" remove any existing keymap for leader and localleader
nnoremap ; <Nop>
xnoremap ; <Nop>
nnoremap , <Nop>
xnoremap , <Nop>

if !has('gui_running')
  if !has('nvim') " neovim does not have the following features
    set ttyfast         " enable fast terminal connection
    set ttyscroll=3     " the maximum number of lines for scrolling.
                        " use small value here to prefer redraw the screen
                        " instead of scrolling
  endif
  set t_Co=256        " use 256 colors
  " to enable cursor keys in terminal
  set notimeout ttimeout ttimeoutlen=100
else
  set timeout timeoutlen=1000 ttimeoutlen=100
endif

" }}}

" ancient features {{{
set noswapfile
set nobackup
set nowritebackup
set noerrorbells
set novisualbell
set t_vb=
" }}}

" language and encoding {{{

" prefer English interface
language message C

" prefer English help
set helplang=en,ja

" set default language for spell check
" cjk - ignore spell check on Asian characters (China, Japan, Korea)
set nospell
set spelllang=en_us,cjk
let &spellfile = expand('~/Copy/Apps/Vim/system/spellfile.utf-8.add')

" prefer UTF-8
if has('multi_byte')
  if has('vim_starting') || !has('nvim')
    set encoding=utf-8
    set termencoding=utf-8
    set ambiwidth=double
  endif
  scriptencoding utf-8
  set fileencodings=ucs-bom,utf-8,euc-jp,iso-2022-jp,cp932,utf-16,utf-16le
  set fileformats=unix,dos,mac
endif

" }}}

" search {{{
set smartcase       " override the ignorecase if the search pattern contains
                    " upper case characters
set incsearch       " use incremental search
set nowrapscan      " do not wrap scan
set hlsearch        " highlight found terms

" K to search the help with the cursor word
set keywordprg=:help

" automatically escape / or ?
cnoremap <expr> / getcmdtype() == '/' ? '\/' : '/'
cnoremap <expr> ? getcmdtype() == '?' ? '\?' : '?'

" remove highlight with pressing ESC twice
nnoremap <silent> <Esc><Esc> :<C-u>nohlsearch<CR>
" }}}

" edit {{{
set nomodeline    " do not use modeline
set smarttab      " insert blanks according to shiftwidth
set expandtab     " use spaces instead of TAB
set tabstop=8     " the number of spaces that a TAB counts for
set softtabstop=4 " the number of spaces that a TAB counts for
set shiftwidth=4  " the number of spaces of an indent
set shiftround    " round indent to multiple of shiftwidth with > and <

set autoindent    " copy indent from current line when starting a new line
set copyindent    " copy the structure of the existing lines indent when
                  " autoindenting a new line

" every wrapped line will continue visually indented
if has('&breakindent')
  set breakindent 
endif

set hidden              " hide the buffer instead of close
set switchbuf=useopen   " use an existing buffer instaed of creating a new one
set autoread            " automatically read the changed contents

" store cursor, folds, slash, unix on view
set viewoptions=cursor,folds,slash,unix
" change default view directory
let &viewdir = rook#normpath('.view')
if !isdirectory(&viewdir)
  call mkdir(&viewdir, 'p', 0700)
endif

" use clipboard register
if has('unnamedplus')
  set clipboard=unnamedplus,unnamed
else
  set clipboard=unnamed
endif

" keep undo history on undofile
if has('persistent_undo')
  let &undodir = rook#normpath('.undo')
  if !isdirectory(&undodir)
    call mkdir(&undodir, 'p', 0700)
  endif
  set undofile
endif

" allow backspacing over everything in insert mode
set backspace=indent,eol,start

" }}}

" cursor {{{
set showmatch             " highlight a partner of cursor character
set matchtime=1           " highlight a partner ASAP
set matchpairs& mps+=<:>  " add < and > pair
set updatetime=1000       " increase speed of CursorHold autocommand

set virtualedit=all       " allow virtual editing in all modes
set nostartofline         " let C-D, C-U,... to keep same column

" move cursor previous/next line when the cursor is first/last character in the
" line and user move the cursor left/right
set whichwrap=b,s,<,>,~,[,]

" do not display cursorline/column (it makes Vim really slow)
set nocursorline
set nocursorcolumn
" }}}

" folding {{{
set foldenable
set foldmethod=marker
set foldlevelstart=99
set foldnestmax=3         " maximum fold nesting level
set foldcolumn=3          " show fold guide
" }}}

" interface {{{
set number                " show line numbers
set list                  " show invisible characters
set laststatus=2          " always shows statusline
set scrolloff=4           " minimum number of screen lines to keep in scroll
set sidescrolloff=4       " minimum number of screen columns to keep in scroll
set cmdheight=2           " the number of screen lines to use for command line
set showcmd               " show command on statusline
set nolazyredraw          " do not redraw while command execution
set nosplitbelow          " split to top
set splitright            " vsplit to right
set report=0              " report any changes
set display=lastline      " display last line in a window AMAP
set showtabline=2         " display tabline always

set wrap                  " wrap long text
set textwidth=0           " do not automatically break
set breakat=\ \ ;:,!?     " characters which might cause a line break

" do not display greeting message
set shortmess=aTI

" use rich completion system in command line
set wildmenu
set wildmode=list:longest,full
set wildoptions=tagfile

" completion settings
set completeopt=menu,longest
set complete=.            " don't complete from other buffer
set pumheight=20          " height of popup menu
set showfulltag           " show both the tag name and the search pattern
if has('patch-7.4.314')
  " don't give ins-completion-menu messages
  set shortmess+=c
endif

" Conceal
set conceallevel=2
set concealcursor=c
set colorcolumn=80

if has('multi_byte') && $LANG !=# 'C'
  set listchars=tab:»-,trail:-,extends:»,precedes:«,nbsp:%,eol:$
else
  set listchars=tab:>-,trail:-,extends:>,precedes:<,nbsp:%,eol:$
endif
" }}}

" key mapping {{{

" disable middle-click paste
map <MiddleMouse>   <Nop>
map <2-MiddleMouse> <Nop>
map <3-MiddleMouse> <Nop>
map <4-MiddleMouse> <Nop>
imap <MiddleMouse>   <Nop>
imap <2-MiddleMouse> <Nop>
imap <3-MiddleMouse> <Nop>
imap <4-MiddleMouse> <Nop>

" remap j and k to act as expected when used on long, wrapped, lines
nnoremap j gj
nnoremap k gk
nnoremap gj j
nnoremap gk k

" Emacs like binding in Insert mode
inoremap <C-a> <C-o>^
inoremap <C-e> <C-o>$
inoremap <C-f> <C-o>w
inoremap <C-b> <C-o>b
inoremap <C-d> <C-o>x

" Ctrl-h/j/k/l to move around in Insert mode
inoremap <C-h> <C-o>h
inoremap <C-j> <C-o>j
inoremap <C-k> <C-o>k
inoremap <C-l> <C-o>l

" Y to yank the end of line
nnoremap Y y$

" <C-p> to paste from 0 register
nnoremap <C-p> "0p

" vv to select the line, like yy, dd
nnoremap vv 0v$

" simple window resize navigation
nnoremap <S-Left>  <C-w><<CR>
nnoremap <S-Right> <C-w>><CR>
nnoremap <S-Up>    <C-w>-<CR>
nnoremap <S-Down>  <C-w>+<CR>

" tag navigation
nnoremap [t  :<C-u>tprevious<CR>
nnoremap ]t  :<C-u>tnext<CR>
nnoremap g[t :<C-u>tfirst<CR>
nnoremap g]t :<C-u>tlast<CR>

" quickfix navigation
nnoremap [q  :<C-u>cprevious<CR>
nnoremap ]q  :<C-u>cnext<CR>
nnoremap g[q :<C-u>cfirst<CR>
nnoremap g]q :<C-u>clast<CR>
nnoremap [l  :<C-u>lprevious<CR>
nnoremap ]l  :<C-u>lnext<CR>
nnoremap g[l :<C-u>lfirst<CR>
nnoremap g]l :<C-u>llast<CR>

" file navigation
nnoremap {f  :<C-u>previous<CR>
nnoremap }f  :<C-u>next<CR>
nnoremap g{f :<C-u>first<CR>
nnoremap g}f :<C-u>last<CR>

" tab operation (make similar mapping with window operation)
" ref.
" <C-w>n    Create a new window and start editing an empty file in it.
" <C-w>q    Quit the current window. (:quit)
" <C-w>c    Close the current window. (:close)
" <C-w>o    Only
" <C-w>w    Move to below/right
" <C-w>W    Move to top/left
" <C-w>p    Move to previous
function! s:tab_quit() abort
  if tabpagenr('$') == 1
    quit
  else
    tabclose
  endif
endfunction

nnoremap <silent> <Plug>(my-tab-new) :<C-u>tabnew<CR>
nnoremap <silent> <Plug>(my-tab-quit) :<C-u>call <SID>tab_quit()<CR>
nnoremap <silent> <Plug>(my-tab-close) :<C-u>tabclose<CR>
nnoremap <silent> <Plug>(my-tab-only) :<C-u>tabonly<CR>
nnoremap <silent> <Plug>(my-tab-next) :<C-u>tabnext<CR>
nnoremap <silent> <Plug>(my-tab-prev) :<C-u>tabprevious<CR>
nnoremap <silent> <Plug>(my-tab-move-0) :<C-u>0tabmove<CR>
nnoremap <silent> <Plug>(my-tab-move-L) :<C-u>-tabmove<CR>
nnoremap <silent> <Plug>(my-tab-move-R) :<C-u>+tabmove<CR>
nnoremap <silent> <Plug>(my-tab-move-$) :<C-u>$tabmove<CR>
nnoremap <silent> <Plug>(my-tab-0) :<C-u>tabfirst<CR>
nnoremap <silent> <Plug>(my-tab-1) :<C-u>tabnext1<CR>
nnoremap <silent> <Plug>(my-tab-2) :<C-u>tabnext2<CR>
nnoremap <silent> <Plug>(my-tab-3) :<C-u>tabnext3<CR>
nnoremap <silent> <Plug>(my-tab-4) :<C-u>tabnext4<CR>
nnoremap <silent> <Plug>(my-tab-5) :<C-u>tabnext5<CR>
nnoremap <silent> <Plug>(my-tab-6) :<C-u>tabnext6<CR>
nnoremap <silent> <Plug>(my-tab-7) :<C-u>tabnext7<CR>
nnoremap <silent> <Plug>(my-tab-8) :<C-u>tabnext8<CR>
nnoremap <silent> <Plug>(my-tab-9) :<C-u>tabnext9<CR>
nnoremap <silent> <Plug>(my-tab-$) :<C-u>tablast<CR>

nmap <C-t>n     <Plug>(my-tab-new)
nmap <C-t><C-n> <Plug>(my-tab-new)
nmap <C-t>q     <Plug>(my-tab-quit)
nmap <C-t><C-q> <Plug>(my-tab-quit)
nmap <C-t>c     <Plug>(my-tab-close)
nmap <C-t>o     <Plug>(my-tab-only)
nmap <C-t><C-o> <Plug>(my-tab-only)

nmap <C-t>t     <Plug>(my-tab-next)
nmap <C-t><C-t> <Plug>(my-tab-next)
nmap <C-t>l     <Plug>(my-tab-next)
nmap <C-t><C-l> <Plug>(my-tab-next)
nmap <C-t>T     <Plug>(my-tab-prev)
nmap <C-t>h     <Plug>(my-tab-prev)
nmap <C-t><C-h> <Plug>(my-tab-prev)
nmap <C-t>j     <Plug>(my-tab-$)
nmap <C-t><C-j> <Plug>(my-tab-$)
nmap <C-t>k     <Plug>(my-tab-0)
nmap <C-t><C-k> <Plug>(my-tab-0)

nmap <C-t>L     <Plug>(my-tab-move-R)
nmap <C-t>H     <Plug>(my-tab-move-L)
nmap <C-t>J     <Plug>(my-tab-move-$)
nmap <C-t>K     <Plug>(my-tab-move-0)

nmap <C-t>0 <Plug>(my-tab-0)
nmap <C-t>1 <Plug>(my-tab-1)
nmap <C-t>2 <Plug>(my-tab-2)
nmap <C-t>3 <Plug>(my-tab-3)
nmap <C-t>4 <Plug>(my-tab-4)
nmap <C-t>5 <Plug>(my-tab-5)
nmap <C-t>6 <Plug>(my-tab-6)
nmap <C-t>7 <Plug>(my-tab-7)
nmap <C-t>8 <Plug>(my-tab-8)
nmap <C-t>9 <Plug>(my-tab-9)
nmap <C-t>$ <Plug>(my-tab-$)

" switch options
nnoremap <Plug>(my-switch) <Nop>
nmap <Leader>s <Plug>(my-switch)
nnoremap <silent> <Plug>(my-switch)s :<C-u>setl spell! spell?<CR>
nnoremap <silent> <Plug>(my-switch)l :<C-u>setl list! list?<CR>
nnoremap <silent> <Plug>(my-switch)t :<C-u>setl expandtab! expandtab?<CR>
nnoremap <silent> <Plug>(my-switch)w :<C-u>setl wrap! wrap?<CR>
nnoremap <silent> <Plug>(my-switch)p :<C-u>setl paste! paste?<CR>
nnoremap <silent> <Plug>(my-switch)b :<C-u>setl scrollbind! scrollbind?<CR>
nnoremap <silent> <Plug>(my-switch)y :call <SID>toggle_syntax()<CR>
function! s:toggle_syntax() abort
  if exists('g:syntax_on')
    syntax off
    redraw
    echo 'syntax off'
  else
    syntax on
    redraw
    echo 'syntax on'
  endif
endfunction
" }}}

" macros {{{

" save the file as root with 'sudo"
cabbr w!! w !sudo tee > /dev/null %

" save and restore the cursor position and folding level
function! s:is_view_available() abort " {{{
  if !&buflisted || &buftype !=# ''
    return 0
  elseif !filewritable(expand('%:p'))
    return 0
  endif
  return 1
endfunction " }}}
function! s:mkview() abort " {{{
  if s:is_view_available()
    silent! mkview
  endif
endfunction " }}}
function! s:loadview() abort " {{{
  if s:is_view_available()
    silent! loadview
  endif
endfunction " }}}
"autocmd MyAutoCmd BufWinLeave ?* call s:mkview()
"autocmd MyAutoCmd BufReadPost ?* call s:loadview()

" reload vimrc with <Leader><Leader>r {{{
if has('vim_starting')
  function s:reload_vimrc() abort
    execute printf('source %s', $MYVIMRC)
    if has('gui_running')
      execute printf('source %s', $MYGVIMRC)
    endif
    redraw
    echo printf('.vimrc/.gvimrc has reloaded (%s).', strftime('%c'))
  endfunction
endif
nmap <silent> <Plug>(my-reload-vimrc) :<C-u>call <SID>reload_vimrc()<CR>
nmap <Leader><Leader>r <Plug>(my-reload-vimrc)
" }}}

" source/reload current vimscript file " {{{
function! s:source_current_vimscript() abort
  let abspath = resolve(expand('%:p'))
  if &filetype !=# 'vim'
    redraw
    echohl WarningMsg
    echo printf(
          \ 'The filetype of the current buffer is "%s" but it must be "vim" for source.',
          \ &filetype,
          \)
    echohl None
    return
  elseif abspath ==# $MYVIMRC || abspath ==# $MYGVIMRC
    redraw
    echohl WarningMsg
    echo 'The .vimrc/.gvimrc cannot be reloaded by this function. Use <Plug>(my-reload-vimrc) instead.'
    echohl None
    return
  endif
  execute printf('source %s', expand('%'))
  redraw
  echo printf('"%s" has sourced (%s).', expand('%:t'), strftime('%c'))
endfunction
nmap <silent> <Plug>(my-source) :<C-u>call <SID>source_current_vimscript()<CR>
nmap <LocalLeader><LocalLeader>s <Plug>(my-source)
" }}}

" automatically create missing directories {{{
autocmd MyAutoCmd BufWritePre * call s:makedirs(expand('<afile>:p:h'), v:cmdbang)
function! s:makedirs(dir, force) abort
  if a:dir =~# '^.\{-}://'
      " Non local file, ignore
      return
  endif
  if !isdirectory(a:dir)
    if a:force || input(printf('"%s" does not exist. Create? [y/N]', a:dir)) =~? '^y\%[es]$'
      call mkdir(a:dir, 'p')
    endif
  endif
endfunction
" }}}

" automatically change working directory on vim enter {{{
function! s:workon(dir, bang) abort
  let dir = (a:dir ==# '' ? expand('%') : a:dir)
  " convert filename to directory if required
  if filereadable(dir)
    let dir = fnamemodify(expand(dir),':p:h')
  else
    let dir = fnamemodify(dir, ':p')
  endif
  " change directory to specified directory
  if isdirectory(dir)
    silent execute 'cd ' . fnameescape(dir)
    if a:bang ==# ''
      redraw | echo 'Working on: '.dir
      if v:version > 703 || (v:version == 703 && has('patch438'))
        doautocmd <nomodeline> MyAutoCmd User my-workon-post
      else
        doautocmd MyAutoCmd User my-workon-post
      endif
    endif
  endif
endfunction
autocmd MyAutoCmd VimEnter ?* call s:workon(expand('<afile>'), 1)
command! -nargs=? -complete=dir -bang Workon call s:workon('<args>', '<bang>')
" }}}

" toggle quickfix window {{{
function! s:toggle_qf() abort
  let nwin = winnr('$')
  cclose
  if nwin == winnr('$')
    cwindow
  endif
endfunction
nnoremap <silent> <Leader>q :call <SID>toggle_qf()<CR>
" }}}

" highlight zenkaku spaces {{{
autocmd MyAutoCmd VimEnter,WinEnter * match MyZenkakuSpace / /
highlight MyZenkakuSpace
      \ cterm=reverse ctermfg=DarkMagenta
      \ gui=reverse guifg=DarkMagenta
" }}}

" Open junk file."{{{
let s:junk_dir = rook#normpath('.junk')
if !isdirectory(s:junk_dir)
  call mkdir(s:junk_dir, 'p')
endif
function! s:open_junk_file() abort
  let filename = strftime('%Y-%m-%d-%H-%M-%S.')
  let filename = input(
        \ 'Name of the junk file: ',
        \ filename,
        \)
  let path = s:junk_dir . '/' . filename
  redraw!
  if !empty(path)
    execute printf('edit %s', fnameescape(path))
  else
    echo 'The operation has canceled by user.'
  endif
endfunction
command! -nargs=0 JunkFile call s:open_junk_file()
" }}}

function! s:screen_cast() abort
  set columns=80
  set lines=30
endfunction
command! -nargs=0 ScreenCast call s:screen_cast()

function! s:clear_messages() abort
  for i in range(201)
    echomsg ''
  endfor
endfunction
command! -nargs=0 MessageClear call s:clear_messages()

function! s:timeit(q_args) abort
  let q_args = a:q_args
  let q_args = substitute(q_args, '^[\t :]\+', '', '')
  let q_args = substitute(q_args, '\s\+$', '', '')
  let args = substitute(q_args, '^[ :]*!', '', '')
  let start = reltime()
  try
    if q_args !=# '' && q_args[0] ==# '!'
      echo system(args)
    else
      execute q_args
    endif
  finally
    echomsg printf('Timeit: %s s [%s]', reltimestr(reltime(start)), a:q_args)
  endtry
endfunction
command! -nargs=+ -bang -complete=command Timeit call s:timeit(<q-args>)
" }}}

if !has('gui_running')
  let g:hybrid_use_Xresources = 1
endif
colorscheme hybrid

call rook#source(rook#normpath('rc/plugin.vim'))
call rook#source(expand('~/.vimrc.local'))

" vim:set et ts=2 sts=2 sw=2 tw=0 fdm=marker: