diff --git a/.vim/.netrwhist b/.vim/.netrwhist
new file mode 100644
index 0000000..1804996
--- /dev/null
+++ b/.vim/.netrwhist
@@ -0,0 +1,12 @@
+let g:netrw_dirhistmax =10
+let g:netrw_dirhistcnt =3
+let g:netrw_dirhist_3='/home/sdk'
+let g:netrw_dirhist_2='/home/sdk/.vim'
+let g:netrw_dirhist_1='sftp://sdk@gopher.codevoid.de/../www/htdocs/gopher/'
+let g:netrw_dirhist_0='/home/sdk/code/bcbackup'
+let g:netrw_dirhist_9='/home/sdk/code'
+let g:netrw_dirhist_8='/home/sdk/code/bar'
+let g:netrw_dirhist_7='/home/sdk/code/drist'
+let g:netrw_dirhist_6='/home/sdk/code'
+let g:netrw_dirhist_5='/home/sdk/.FontForge'
+let g:netrw_dirhist_4='/home/sdk/.audacity-data'
diff --git a/.vim/autoload/pathogen.vim b/.vim/autoload/pathogen.vim
new file mode 100644
index 0000000..3582fbf
--- /dev/null
+++ b/.vim/autoload/pathogen.vim
@@ -0,0 +1,264 @@
+" pathogen.vim - path option manipulation
+" Maintainer: Tim Pope
+" Version: 2.4
+
+" Install in ~/.vim/autoload (or ~\vimfiles\autoload).
+"
+" For management of individually installed plugins in ~/.vim/bundle (or
+" ~\vimfiles\bundle), adding `execute pathogen#infect()` to the top of your
+" .vimrc is the only other setup necessary.
+"
+" The API is documented inline below.
+
+if exists("g:loaded_pathogen") || &cp
+ finish
+endif
+let g:loaded_pathogen = 1
+
+" Point of entry for basic default usage. Give a relative path to invoke
+" pathogen#interpose() or an absolute path to invoke pathogen#surround().
+" Curly braces are expanded with pathogen#expand(): "bundle/{}" finds all
+" subdirectories inside "bundle" inside all directories in the runtime path.
+" If no arguments are given, defaults "bundle/{}", and also "pack/{}/start/{}"
+" on versions of Vim without native package support.
+function! pathogen#infect(...) abort
+ if a:0
+ let paths = filter(reverse(copy(a:000)), 'type(v:val) == type("")')
+ else
+ let paths = ['bundle/{}', 'pack/{}/start/{}']
+ endif
+ if has('packages')
+ call filter(paths, 'v:val !~# "^pack/[^/]*/start/[^/]*$"')
+ endif
+ let static = '^\%([$~\\/]\|\w:[\\/]\)[^{}*]*$'
+ for path in filter(copy(paths), 'v:val =~# static')
+ call pathogen#surround(path)
+ endfor
+ for path in filter(copy(paths), 'v:val !~# static')
+ if path =~# '^\%([$~\\/]\|\w:[\\/]\)'
+ call pathogen#surround(path)
+ else
+ call pathogen#interpose(path)
+ endif
+ endfor
+ call pathogen#cycle_filetype()
+ if pathogen#is_disabled($MYVIMRC)
+ return 'finish'
+ endif
+ return ''
+endfunction
+
+" Split a path into a list.
+function! pathogen#split(path) abort
+ if type(a:path) == type([]) | return a:path | endif
+ if empty(a:path) | return [] | endif
+ let split = split(a:path,'\\\@]','\\&','')
+ endif
+endfunction
+
+" Like findfile(), but hardcoded to use the runtimepath.
+function! pathogen#runtime_findfile(file,count) abort
+ let rtp = pathogen#join(1,pathogen#split(&rtp))
+ let file = findfile(a:file,rtp,a:count)
+ if file ==# ''
+ return ''
+ else
+ return fnamemodify(file,':p')
+ endif
+endfunction
+
+" vim:set et sw=2 foldmethod=expr foldexpr=getline(v\:lnum)=~'^\"\ Section\:'?'>1'\:getline(v\:lnum)=~#'^fu'?'a1'\:getline(v\:lnum)=~#'^endf'?'s1'\:'=':
diff --git a/.vim/bundle/vim-diffchar/README.md b/.vim/bundle/vim-diffchar/README.md
new file mode 100644
index 0000000..4f0f2a0
--- /dev/null
+++ b/.vim/bundle/vim-diffchar/README.md
@@ -0,0 +1,120 @@
+# diffchar.vim
+*Highlight the exact differences, based on characters and words*
+```
+ ____ _ ____ ____ _____ _ _ _____ ____
+| | | || || || || | | || _ || _ |
+| _ || || __|| __|| || | | || | | || | ||
+| | | || || |__ | |__ | __|| |_| || |_| || |_||_
+| |_| || || __|| __|| | | || || __ |
+| || || | | | | |__ | _ || _ || | | |
+|____| |_||_| |_| |_____||_| |_||_| |_||_| |_|
+```
+
+#### Introduction
+
+This plugin has been developed in order to make diff mode more useful. Vim
+highlights all the text in between the first and last different characters on
+a changed line. But this plugin will find the exact differences between them,
+character by character - so called *DiffChar*.
+
+For example, in diff mode:
+![example1](example1.png)
+
+This plugin will exactly show the changed and added units:
+![example2](example2.png)
+
+This plugin will synchronously show/reset the highlights of the exact
+differences as soon as the diff mode begins/ends. And the exact differences
+will be kept updated while editing.
+
+This plugin shows the differences based on a `g:DiffUnit`. Its default is
+'Word1' and it handles a `\w\+` word and a `\W` character as a difference unit.
+There are other types of word provided and you can also set 'Char' to compare
+character by character.
+
+In diff mode, the corresponding `hl-DiffChange` lines are compared between two
+windows. You can set a number of matching colors to a `g:DiffColors` to make
+it easy to find the corresponding units between two windows. As a default, all
+the changed units are highlighted with `hl-DiffText`. In addition,
+`hl-DiffAdd` is always used for the added units and both the previous and next
+character of the deleted units are shown in bold/underline.
+
+While showing the exact differences, when the cursor is moved on a difference
+unit, you can see its corresponding unit highlighted with `hl-Cursor`,
+`hl-TermCursor`, or similar one in another window, based on a
+`g:DiffPairVisible`. If you change its default, the corresponding unit is
+echoed in the command line or displayed in a popup/floating window just below
+the cursor position or at the mouse position.
+
+You can use `]b` or `]e` to jump cursor to start or end position of the next
+difference unit, and `[b` or `[e` to the start or end position of the previous
+unit. Those keymaps are configurable in your vimrc and so on.
+
+Like line-based `:diffget`/`:diffput` and `do`/`dp` vim commands, you can use
+`g` and `p` commands in normal mode to get and put each
+difference unit, where the cursor is on, between 2 buffers and undo its
+difference.
+
+When the diff mode begins, this plugin locally checks the `hl-DiffChange`
+lines in the limited range of the current visible and its upper/lower lines of
+a window. And each time a cursor is moved on to a different range upon
+scrolling or searching, the new `hl-DiffChange` lines will be incrementally
+checked in that range. Which means, independently of the file size, the number
+of lines to be checked and then the time consumed are always constant.
+
+This plugin works on each tab page individually. You can use a tab page
+variable (t:), instead of a global one (g:), to specify different options on
+each tab page. Note that this plugin can not handle more than two diff mode
+windows in a tab page. If it would happen, to prevent any trouble, all the
+highlighted units are to be reset in the tab page.
+
+To find the exact differences, this plugin uses "An O(NP) Sequence Comparison
+Algorithm" developed by S.Wu, et al., which always finds an optimum sequence.
+But it takes time to check a long and dissimilar line. To improve the
+performance, if there are so many diff units included in a line or it has
+taken much time in a diff session, this plugin tries to use the external diff
+command together if available.
+
+#### Options
+
+* `g:DiffUnit`, `t:DiffUnit`:
+ A type of difference unit
+ * 'Char' : any single character
+ * 'Word1' : `\w\+` word and any `\W` single character (default)
+ * 'Word2' : non-space and space words
+ * 'Word3' : `\<` or `\>` character class boundaries
+ * 'CSV(,)' : separated by characters such as ',', ';', and '\t'
+
+* `g:DiffColors`, `t:DiffColors`:
+ Matching colors for changed units
+ * 0 : `hl-DiffText` (default)
+ * 1 : `hl-DiffText` + up to 3 other highlights
+ * 2 : `hl-DiffText` + up to 7 other highlights
+ * 3 : `hl-DiffText` + up to 15 other highlights
+
+* `g:DiffPairVisible`, `t:DiffPairVisible`:
+ Visibility of corresponding diff units
+ * 0 : disable
+ * 1 : highlight with `hl-Cursor` (default)
+ * 2 : highlight with `hl-Cursor` + echo in the command line
+ * 3 : highlight with `hl-Cursor` + popup/floating window at cursor position
+ * 4 : highlight with `hl-Cursor` + popup/floating window at mouse position
+
+#### Keymaps
+
+* `JumpDiffCharPrevStart` (default: `[b`)
+ * Jump cursor to the start position of the previous difference unit
+* `JumpDiffCharNextStart` (default: `]b`)
+ * Jump cursor to the start position of the next difference unit
+* `JumpDiffCharPrevEnd` (default: `[e`)
+ * Jump cursor to the end position of the previous difference unit
+* `JumpDiffCharNextEnd` (default: `]e`)
+ * Jump cursor to the end position of the next difference unit
+* `GetDiffCharPair` (default: `g`)
+ * Get a corresponding difference unit from another buffer to undo difference
+* `PutDiffCharPair` (default: `p`)
+ * Put a corresponding difference unit to another buffer to undo difference
+
+#### Demo
+
+![demo](demo.gif)
diff --git a/.vim/bundle/vim-diffchar/autoload/diffchar.vim b/.vim/bundle/vim-diffchar/autoload/diffchar.vim
new file mode 100644
index 0000000..ce1e411
--- /dev/null
+++ b/.vim/bundle/vim-diffchar/autoload/diffchar.vim
@@ -0,0 +1,1658 @@
+" diffchar.vim: Highlight the exact differences, based on characters and words
+"
+" ____ _ ____ ____ _____ _ _ _____ ____
+" | | | || || || || | | || _ || _ |
+" | _ || || __|| __|| || | | || | | || | ||
+" | | | || || |__ | |__ | __|| |_| || |_| || |_||_
+" | |_| || || __|| __|| | | || || __ |
+" | || || | | | | |__ | _ || _ || | | |
+" |____| |_||_| |_| |_____||_| |_||_| |_||_| |_|
+"
+" Last Change: 2021/12/07
+" Version: 8.91
+" Author: Rick Howe (Takumi Ohtani)
+" Copyright: (c) 2014-2021 by Rick Howe
+
+let s:save_cpo = &cpoptions
+set cpo&vim
+
+" Vim feature, function, event and patch number which this plugin depends on
+" patch-8.0.736: OptionSet event triggered with diff option
+" patch-8.0.794: count() fixed to accept a string
+" patch-8.0.914: nocombine attribute available
+" patch-8.0.1038: strikethrough attribute available
+" patch-8.0.1160: gettabvar() fixed not to return empty
+" patch-8.0.1290: changenr() fixed to return correct value
+" patch-8.1.414: v:option fixed in OptionSet diff autocmd
+" patch-8.1.1084: window ID argument available in all match functions
+" patch-8.1.1832: win_execute() fixed to work in other tabpage
+let s:VF = {
+ \'DiffUpdated': exists('##DiffUpdated'),
+ \'WinScrolled': exists('##WinScrolled'),
+ \'WinClosed': exists('##WinClosed'),
+ \'GUIColors': has('gui_running') ||
+ \has('termguicolors') && &termguicolors,
+ \'DiffExecutable': executable('diff'),
+ \'PopupWindow': has('popupwin'),
+ \'FloatingWindow': exists('*nvim_create_buf'),
+ \'GetMousePos': exists('*getmousepos'),
+ \'WinExecute': exists('*win_execute') &&
+ \(has('patch-8.1.1832') || has('nvim-0.5.0')),
+ \'DiffOptionSet': has('patch-8.0.736'),
+ \'CountString': has('patch-8.0.794'),
+ \'StrikeAttr': has('patch-8.0.1038') &&
+ \(has('gui_running') || !empty(&t_Ts) && !empty(&t_Te)),
+ \'GettabvarFixed': has('patch-8.0.1160'),
+ \'ChangenrFixed': has('patch-8.0.1290'),
+ \'VOptionFixed': has('patch-8.1.414') || has('nvim-0.3.2'),
+ \'WinIDinMatch': has('patch-8.1.1084') || has('nvim-0.5.0'),
+ \'NvimDiffHLID': has('nvim') && !has('nvim-0.4.0')}
+
+function! s:SetDiffCharHL() abort
+ " check vim original Diff highlights and set attributes for changes
+ let s:DiffHL = {}
+ for [hs, hl] in [['A', 'DiffAdd'], ['C', 'DiffChange'],
+ \['D', 'DiffDelete'], ['T', 'DiffText']]
+ let dh = {}
+ let hn = has('nvim') ? '' :
+ \matchstr(split(&highlight, ','), '^' . hs . '\C:')
+ let dh.id = hlID(empty(hn) ? hl : hn[2:])
+ let dh.it = synIDtrans(dh.id) " in case of linked
+ let dh.nm = synIDattr(dh.it, 'name')
+ " dh: 0 = original, 1 = for single color, 2 = for multi color
+ let dh.0 = {}
+ for hm in ['term', 'cterm', 'gui']
+ for hc in ['fg', 'bg', 'sp']
+ let dh.0[hm . hc] = synIDattr(dh.it, hc, hm)
+ endfor
+ let dh.0[hm] = join(filter(['bold', 'underline', 'undercurl',
+ \'strikethrough', 'reverse', 'inverse', 'italic', 'standout'],
+ \'!empty(synIDattr(dh.it, v:val, hm))'), ',')
+ endfor
+ call filter(dh.0, '!empty(v:val)')
+ let dh.1 = (hs == 'C' || hs == 'T') ?
+ \filter(copy(dh.0), 'v:key =~ "bg$"') : dh.0
+ let dh.2 = (hs == 'T') ? {} : dh.1
+ " diff_hlID() incorrectly returns (hlID() - 1) until nvim 0.4.0
+ if s:VF.NvimDiffHLID | let dh.id -= 1 | endif
+ let s:DiffHL[hs] = dh
+ endfor
+ " set DiffChar specific highlights
+ let s:DCharHL = {'A': 'DiffAdd', 'D': 'DiffDelete', 'n': 'LineNr'}
+ if has('nvim')
+ let s:DCharHL.c = 'TermCursor'
+ else
+ let s:DCharHL.c = 'Cursor'
+ if !s:VF.GUIColors
+ let id = 1
+ while 1
+ let nm = synIDattr(id, 'name')
+ if empty(nm) | break | endif
+ if id == synIDtrans(id) && !empty(synIDattr(id, 'reverse')) &&
+ \empty(filter(['fg', 'bg', 'sp', 'bold', 'underline',
+ \'undercurl', 'strikethrough', 'italic', 'standout'],
+ \'!empty(synIDattr(id, v:val))'))
+ let s:DCharHL.c = nm
+ break
+ endif
+ let id += 1
+ endwhile
+ endif
+ endif
+ for [fs, ts, th, ta] in [['C', 'C', 'dcDiffChange', ''],
+ \['T', 'T', 'dcDiffText', ''],
+ \['C', 'E', 'dcDiffErase', 'bold,underline']] +
+ \(s:VF.StrikeAttr ? [['D', 'D', 'dcDiffDelete', 'strikethrough']] : [])
+ let fa = copy(s:DiffHL[fs].0)
+ if !empty(ta)
+ for hm in ['term', 'cterm', 'gui']
+ let fa[hm] = has_key(fa, hm) ? fa[hm] . ',' . ta : ta
+ endfor
+ endif
+ call execute(['highlight clear ' . th,
+ \'highlight ' . th . ' ' .
+ \join(map(items(fa), 'join(v:val, "=")'))])
+ let s:DCharHL[ts] = th
+ endfor
+ " change diff highlights according to current DChar
+ call s:ToggleDiffHL(exists('t:DChar'))
+endfunction
+
+function! s:InitializeDiffChar() abort
+ " select current and next diff mode windows whose buffer is different
+ " do no initiate if more than 2 diff mode windows exist in a tab page and
+ " if a selected buffer already DChar highlighted in other tab pages
+ let cw = win_getid()
+ let cb = winbufnr(cw)
+ let nw = filter(map(range(winnr() + 1, winnr('$')) +
+ \range(1, winnr() - 1), 'win_getid(v:val)'),
+ \'getwinvar(v:val, "&diff") && winbufnr(v:val) != cb')
+ let nb = map(copy(nw), 'winbufnr(v:val)')
+ if !getwinvar(cw, '&diff') || empty(nw) || min(nb) != max(nb)
+ return -1
+ endif
+ for tn in filter(range(1, tabpagenr('$')), 'v:val != tabpagenr()')
+ let dc = s:Gettabvar(tn, 'DChar')
+ if !empty(dc)
+ for bn in values(dc.bnr)
+ if index([cb, nb[0]], bn) != -1
+ call s:EchoWarning('Both or either selected buffer already
+ \ highlighted in tab page ' . tn . '!')
+ return -1
+ endif
+ endfor
+ endif
+ endfor
+ " set diffchar highlights
+ call s:SetDiffCharHL()
+ " define a DiffChar dictionary on this tab page
+ let t:DChar = {}
+ " windowID and bufnr
+ let t:DChar.wid = {'1': cw, '2': nw[0]}
+ let t:DChar.bnr = {'1': cb, '2': nb[0]}
+ " diff mode synchronization flag
+ let t:DChar.dsy = get(g:, 'DiffModeSync', 1)
+ " a multiple of the current visible page to locally detect diff lines
+ let t:DChar.dfp = get(g:, 'DiffFocalPages', 3)
+ " top/bottom/last lines, cursor line/column, changenr on each window
+ let t:DChar.lcc = s:GetLineColCnr(1)
+ " a list of diff focus lines
+ let t:DChar.dfl = s:FocusDiffLines(1, 1)
+ " a type of diff pair visible
+ let pv = get(t:, 'DiffPairVisible', g:DiffPairVisible)
+ if (pv == 3 || pv == 4) && !(s:VF.PopupWindow || s:VF.FloatingWindow) ||
+ \pv == 4 && !s:VF.GetMousePos
+ let pv = 1
+ endif
+ let t:DChar.dpv = {'pv': pv}
+ if 0 < pv
+ let t:DChar.dpv.ch = {}
+ if pv == 3 || pv == 4
+ let t:DChar.dpv.pw = s:VF.PopupWindow ? 0 :
+ \s:VF.FloatingWindow ? {'fb': -1, 'fw': -1} : -1
+ endif
+ endif
+ " a list of highlight IDs per line
+ let t:DChar.mid = {'1': {}, '2': {}}
+ " a list of added/deleted/changed columns per line
+ let t:DChar.hlc = {'1': {}, '2': {}}
+ " checksum per line
+ let t:DChar.cks = {'1': {}, '2': {}}
+ " ignorecase and ignorespace flags
+ let do = split(&diffopt, ',')
+ let t:DChar.igc = (index(do, 'icase') != -1)
+ let t:DChar.igs = (index(do, 'iwhiteall') != -1) ? 1 :
+ \(index(do, 'iwhite') != -1) ? 2 :
+ \(index(do, 'iwhiteeol') != -1) ? 3 : 0
+ " a pattern to split difference units
+ let du = get(t:, 'DiffUnit', g:DiffUnit)
+ if du == 'Char' " any single character
+ let t:DChar.upa = '\zs'
+ elseif du == 'Word2' " non-space and space words
+ let t:DChar.upa = '\%(\s\+\|\S\+\)\zs'
+ elseif du == 'Word3' " \< or \> boundaries
+ let t:DChar.upa = '\<\|\>'
+ elseif du =~ '^CSV(.\+)$' " split characters
+ let s = escape(du[4 : -2], '^-]')
+ let t:DChar.upa = '\%([^'. s . ']\+\|[' . s . ']\)\zs'
+ elseif du =~ '^SRE(.\+)$' " split regular expression
+ let t:DChar.upa = du[4 : -2]
+ else
+ " \w\+ word and any \W character
+ let t:DChar.upa = '\%(\w\+\|\W\)\zs'
+ if du != 'Word1'
+ call s:EchoWarning('Not a valid difference unit type.
+ \ Use "Word1" instead.')
+ endif
+ endif
+ " a list of difference matching colors
+ let t:DChar.hgp = [s:DCharHL.T]
+ let dc = get(t:, 'DiffColors', g:DiffColors)
+ if 1 <= dc && dc <= 4
+ " select all available hl which has bg and has not attribute
+ let [fd, bd] = map(['fg#', 'bg#'], 'synIDattr(hlID("Normal"), v:val)')
+ let id = 1
+ while empty(fd) || empty(bd)
+ let nm = synIDattr(id, 'name')
+ if empty(nm) | break | endif
+ if id == synIDtrans(id)
+ if empty(fd) && synIDattr(id, 'bg') == 'fg'
+ let fd = synIDattr(id, 'bg#')
+ endif
+ if empty(bd) && synIDattr(id, 'fg') == 'bg'
+ let bd = synIDattr(id, 'fg#')
+ endif
+ endif
+ let id += 1
+ endwhile
+ let xb = map(values(s:DCharHL), 'synIDattr(hlID(v:val), "bg#")')
+ let hl = {}
+ let id = 1
+ while 1
+ let nm = synIDattr(id, 'name')
+ if empty(nm) | break | endif
+ if id == synIDtrans(id)
+ let [fg, bg, rv] = map(['fg#', 'bg#', 'reverse'],
+ \'synIDattr(id, v:val)')
+ if empty(fg) | let fg = fd | endif
+ if !empty(rv) | let bg = !empty(fg) ? fg : fd | endif
+ if !empty(bg) && bg != fg && bg != bd &&
+ \index(xb, bg) == -1 && empty(filter(map(['bold',
+ \'underline', 'undercurl', 'strikethrough', 'italic',
+ \'standout'],
+ \'synIDattr(id, v:val)'), '!empty(v:val)'))
+ let hl[bg] = nm
+ endif
+ endif
+ let id += 1
+ endwhile
+ let t:DChar.hgp += values(hl)[: ((dc == 1) ? 2 : (dc == 2) ? 6 :
+ \(dc == 3) ? 14 : -1)]
+ elseif dc == 100
+ let hl = {}
+ let id = 1
+ while 1
+ let nm = synIDattr(id, 'name')
+ if empty(nm) | break | endif
+ if index(values(s:DCharHL), nm) == -1 && id == synIDtrans(id) &&
+ \!empty(filter(['fg', 'bg', 'sp', 'bold', 'underline',
+ \'undercurl', 'strikethrough', 'reverse', 'inverse',
+ \'italic', 'standout'],
+ '!empty(synIDattr(id, v:val))'))
+ let hl[reltimestr(reltime())[-2 :] . id] = nm
+ endif
+ let id += 1
+ endwhile
+ let t:DChar.hgp += values(hl)
+ elseif -3 <= dc && dc <= -1
+ let t:DChar.hgp += ['SpecialKey', 'Search', 'CursorLineNr',
+ \'Visual', 'WarningMsg', 'StatusLineNC', 'MoreMsg',
+ \'ErrorMsg', 'LineNr', 'Conceal', 'NonText',
+ \'ColorColumn', 'ModeMsg', 'PmenuSel', 'Title']
+ \[: ((dc == -1) ? 2 : (dc == -2) ? 6 : -1)]
+ endif
+endfunction
+
+function! diffchar#ToggleDiffChar(lines) abort
+ if exists('t:DChar')
+ for k in [1, 2, 0]
+ if k == 0 | return | endif
+ if t:DChar.wid[k] == win_getid() | break | endif
+ endfor
+ for hl in keys(t:DChar.hlc[k])
+ if index(a:lines, eval(hl)) != -1
+ call diffchar#ResetDiffChar(a:lines)
+ return
+ endif
+ endfor
+ endif
+ call diffchar#ShowDiffChar(a:lines)
+endfunction
+
+function! diffchar#ShowDiffChar(...) abort
+ let init = !exists('t:DChar')
+ if init && s:InitializeDiffChar() == -1 | return | endif
+ for ak in [1, 2, 0]
+ if ak == 0 | return | endif
+ if t:DChar.wid[ak] == win_getid() | break | endif
+ endfor
+ let dl = []
+ for n in filter(map(copy(t:DChar.dfl[ak]),
+ \'(a:0 && index(a:1, v:val) == -1) ? -1 : v:key'), 'v:val != -1')
+ let [l1, l2] = [t:DChar.dfl[1][n], t:DChar.dfl[2][n]]
+ if !has_key(t:DChar.hlc[1], l1) && !has_key(t:DChar.hlc[2], l2)
+ let dl += [[{l1: getbufline(t:DChar.bnr[1], l1)[0]},
+ \{l2: getbufline(t:DChar.bnr[2], l2)[0]}]]
+ endif
+ endfor
+ if !init && empty(dl) | return | endif
+ let save_igc = &ignorecase | let &ignorecase = t:DChar.igc
+ let uu = []
+ for n in range(len(dl))
+ let [d1, d2] = dl[n]
+ for k in [1, 2]
+ let t = values(d{k})[0]
+ if t:DChar.igs
+ let u{k} = split(substitute(t, '\s\+$', '', ''), t:DChar.upa)
+ if t:DChar.igs == 1 " iwhiteall
+ call filter(u{k}, 'v:val !~ "^\\s\\+$"')
+ elseif t:DChar.igs == 2 " iwhite
+ let s = len(u{k}) - 1
+ while 0 < s
+ if u{k}[s - 1] . u{k}[s] =~ '^\s\+$'
+ let u{k}[s - 1] .= u{k}[s]
+ unlet u{k}[s]
+ endif
+ let s -= 1
+ endwhile
+ endif
+ else
+ let u{k} = split(t, t:DChar.upa)
+ endif
+ endfor
+ if u1 == u2 | let dl[n] = []
+ else | let uu += [[u1, u2]]
+ endif
+ endfor
+ call filter(dl, '!empty(v:val)')
+ let es = []
+ if s:VF.DiffExecutable
+ let [mu, mt, st] = [get(g:, 'DiffIntMaxUnits', 2000),
+ \get(g:, 'DiffIntMaxTime', 1.0), reltime()]
+ endif
+ for n in range(len(uu))
+ let [u1, u2] = uu[n]
+ if s:VF.DiffExecutable &&
+ \(mu < len(u1) + len(u2) || mt < reltimefloat(reltime(st)))
+ " the next line includes 2000+ units OR already spent 1.0+s
+ let es += s:ExecDiffCommand(uu[n :])
+ break
+ endif
+ if t:DChar.igs == 2 " iwhite
+ for k in [1, 2]
+ let u{k} = map(copy(u{k}),
+ \'(v:val =~ "^\\s\\+$") ? " " : v:val')
+ endfor
+ endif
+ let es += [s:TraceDiffChar(u1, u2)]
+ endfor
+ let lc = {'1': {}, '2': {}}
+ for n in range(len(dl))
+ let [d1, d2] = dl[n]
+ let [c1, c2] = s:GetDiffUnitPos(es[n], uu[n])
+ for k in [1, 2]
+ let [l, t] = items(d{k})[0]
+ if t:DChar.igs == 1 " iwhiteall
+ if t =~ '\s\+'
+ let ap = filter(range(1, len(t)), 't[v:val - 1] !~ "\\s"')
+ call map(c{k}, '[v:val[0],
+ \[ap[v:val[1][0] - 1], ap[v:val[1][1] - 1]]]')
+ endif
+ endif
+ let lc[k][l] = c{k}
+ let t:DChar.cks[k][l] = s:ChecksumStr(t)
+ endfor
+ endfor
+ let &ignorecase = save_igc
+ call s:HighlightDiffChar(ak, lc)
+ if !t:DChar.dsy && index(values(t:DChar.hlc), {}) != -1
+ unlet t:DChar
+ else
+ if init " set event when DChar HL is newly defined
+ call s:ToggleDiffCharEvent(1)
+ call s:ToggleDiffHL(1)
+ call s:ToggleDiffCharPair(1)
+ endif
+ if 0 < t:DChar.dpv.pv | call s:ShowDiffCharPair(ak) | endif
+ endif
+endfunction
+
+function! s:ExecDiffCommand(uu) abort
+ " prepare 2 input files for diff
+ for [k, u] in [[1, 0], [2, 1]]
+ " insert '|:' before each line and
+ " add '=:' at the beginning of each unit
+ let u{k} = [''] " a dummy to avoid 1st null unit error
+ for n in range(len(a:uu))
+ let u{k} += ['|' . n . ':'] +
+ \map(copy(a:uu[n][u]), '"=" . n . ":" . v:val')
+ endfor
+ let f{k} = tempname()
+ call writefile(u{k}, f{k})
+ endfor
+ " call diff in unified format and assign edit symbols [=+-] to each unit
+ let dc = ['diff', '-a', '--binary', ((t:DChar.igc == 1) ? '-i' : ''),
+ \((t:DChar.igs == 1) ? '-w' : (t:DChar.igs == 2) ? '-b' :
+ \(t:DChar.igs == 3) ? '-Z' : ''),
+ \'-d', '-U', len(u1) + len(u2), f1, f2]
+ let save_stmp = &shelltemp
+ let &shelltemp = 0
+ let dt = systemlist(join(dc))
+ let &shelltemp = save_stmp
+ for k in [1, 2] | call delete(f{k}) | endfor
+ return split(join(map(filter(dt, 'v:val =~ "^[ +-][|=]"'),
+ \'(v:val[0] != " ") ? v:val[0] : v:val[1]'), ''), '|')
+endfunction
+
+function! s:GetDiffUnitPos(es, uu) abort
+ let [u1, u2] = a:uu
+ if empty(u1)
+ return [[['d', [0, 0]]], [['a', [1, len(join(u2, ''))]]]]
+ elseif empty(u2)
+ return [[['a', [1, len(join(u1, ''))]]], [['d', [0, 0]]]]
+ endif
+ let [c1, c2] = [[], []]
+ let [l1, l2, p1, p2] = [1, 1, 0, 0]
+ for ed in split(a:es, '[+-]\+\zs', 1)[: -2]
+ let [qe, q1, q2] = [s:CountChar(ed, '='), s:CountChar(ed, '-'),
+ \s:CountChar(ed, '+')]
+ for k in [1, 2]
+ if 0 < qe
+ let [l{k}, p{k}] +=
+ \[len(join(u{k}[p{k} : p{k} + qe - 1], '')), qe]
+ endif
+ if 0 < q{k}
+ let l = l{k}
+ let [l{k}, p{k}] +=
+ \[len(join(u{k}[p{k} : p{k} + q{k} - 1], '')), q{k}]
+ let h{k} = [l, l{k} - 1]
+ else
+ let h{k} = [
+ \l{k} - ((0 < p{k}) ? len(strcharpart(u{k}[p{k} - 1],
+ \strchars(u{k}[p{k} - 1]) - 1, 1)) : 0),
+ \l{k} + ((p{k} < len(u{k})) ?
+ \len(strcharpart(u{k}[p{k}], 0, 1)) - 1 : -1)]
+ endif
+ endfor
+ let [r1, r2] = (q1 == 0) ? ['d', 'a'] :
+ \(q2 == 0) ? ['a', 'd'] : ['c', 'c']
+ let [c1, c2] += [[[r1, h1]], [[r2, h2]]]
+ endfor
+ return [c1, c2]
+endfunction
+
+function! s:TraceDiffChar(u1, u2) abort
+ " An O(NP) Sequence Comparison Algorithm
+ let [n1, n2] = [len(a:u1), len(a:u2)]
+ if a:u1 == a:u2 | return repeat('=', n1)
+ elseif n1 == 0 | return repeat('+', n2)
+ elseif n2 == 0 | return repeat('-', n1)
+ endif
+ " reverse to be N >= M
+ let [N, M, u1, u2, e1, e2] = (n1 >= n2) ?
+ \[n1, n2, a:u1, a:u2, '+', '-'] : [n2, n1, a:u2, a:u1, '-', '+']
+ let D = N - M
+ let fp = repeat([-1], M + N + 1)
+ let etree = [] " [next edit, previous p, previous k]
+ let p = -1
+ while fp[D] != N
+ let p += 1
+ let epk = repeat([[]], p * 2 + D + 1)
+ for k in range(-p, D - 1, 1) + range(D + p, D, -1)
+ let [y, epk[k]] = (fp[k - 1] < fp[k + 1]) ?
+ \[fp[k + 1], [e1, (k < D) ? p - 1 : p, k + 1]] :
+ \[fp[k - 1] + 1, [e2, (k > D) ? p - 1 : p, k - 1]]
+ let x = y - k
+ while x < M && y < N && u2[x] == u1[y]
+ let epk[k][0] .= '='
+ let [x, y] += [1, 1]
+ endwhile
+ let fp[k] = y
+ endfor
+ let etree += [epk]
+ endwhile
+ " create a shortest edit script (SES) from last p and k
+ let ses = ''
+ while 1
+ let ses = etree[p][k][0] . ses
+ if p == 0 && k == 0 | return ses[1 :] | endif
+ let [p, k] = etree[p][k][1 : 2]
+ endwhile
+endfunction
+
+function! diffchar#ResetDiffChar(...) abort
+ if !exists('t:DChar') | return | endif
+ let last = (a:0 && type(a:1) == type(0)) ? a:1 : 0
+ for k in [1, 2, 0]
+ if k == 0 | return | endif
+ if t:DChar.wid[k] == win_getid() | break | endif
+ endfor
+ let dl = {'1': [], '2': []}
+ for n in filter(map(copy(t:DChar.dfl[k]),
+ \'(a:0 && type(a:1) == type([]) &&
+ \index(a:1, v:val) == -1) ? -1 : v:key'), 'v:val != -1')
+ let [l1, l2] = [t:DChar.dfl[1][n], t:DChar.dfl[2][n]]
+ if has_key(t:DChar.hlc[1], l1) || has_key(t:DChar.hlc[2], l2)
+ let [dl[1], dl[2]] += [[l1], [l2]]
+ unlet t:DChar.cks[1][l1] | unlet t:DChar.cks[2][l2]
+ endif
+ endfor
+ if !last && empty(dl[k])| return | endif
+ call s:ClearDiffChar(k, dl)
+ if 0 < t:DChar.dpv.pv | call s:ClearDiffCharPair(k) | endif
+ if last || !t:DChar.dsy && index(values(t:DChar.hlc), {}) != -1
+ call s:ToggleDiffHL(0)
+ call s:ToggleDiffCharPair(0)
+ unlet t:DChar
+ call s:ToggleDiffCharEvent(0)
+ endif
+endfunction
+
+function! s:ToggleDiffCharEvent(on) abort
+ call execute(g:DiffCharInitEvent)
+ let tv = filter(map(range(1, tabpagenr('$')),
+ \'s:Gettabvar(v:val, "DChar")'), '!empty(v:val)')
+ if empty(tv) | return | endif
+ let ac = []
+ for td in tv
+ for k in [1, 2]
+ let bl = ''
+ if td.dsy
+ let ac += [['TextChanged,InsertLeave', bl,
+ \'s:UpdateDiffChar(' . k . ', 0)']]
+ if s:VF.DiffUpdated
+ let ac += [['DiffUpdated', bl,
+ \'s:UpdateDiffChar(' . k . ', 1)']]
+ endif
+ let ac += [[s:VF.WinClosed ? 'WinClosed' : 'BufWinLeave', bl,
+ \'s:WinClosedDiffChar()']]
+ if td.dfp != 0
+ let ac += [[
+ \s:VF.WinScrolled ? 'WinScrolled' : 'CursorMoved', bl,
+ \'s:ScrollDiffChar(' . k . ')']]
+ endif
+ endif
+ if 0 < td.dpv.pv
+ let ac += [['CursorMoved', bl,
+ \'s:ShowDiffCharPair(' . k . ')']]
+ endif
+ endfor
+ endfor
+ let ac += [['TabEnter', '*', 's:AdjustGlobalOption()']]
+ let ac += [['ColorScheme', '*', 's:SetDiffCharHL()']]
+ let ac += [[s:VF.WinClosed ? 'BufWinEnter' : 'BufWinEnter,WinEnter', '*',
+ \'s:RepairDiffChar()']]
+ if !s:VF.DiffUpdated
+ if s:VF.DiffOptionSet
+ let ac += [['OptionSet', 'diffopt', 's:FollowDiffOption()']]
+ elseif !empty(filter(tv, 'v:val.dsy'))
+ let s:save_ch = 's:ResetDiffModeSync()'
+ let ac += [['CursorHold', '*', s:save_ch]]
+ if !a:on | unlet s:save_ch | endif
+ call s:ChangeUTOpt(a:on)
+ endif
+ endif
+ call execute(map(ac, '"autocmd diffchar " . v:val[0] . " " . v:val[1] .
+ \" call " . v:val[2]'))
+endfunction
+
+function! s:FocusDiffLines(key, init) abort
+ " a:init : initialize dfl (do not use previous dfl)
+ let dfl = {}
+ let ks = (a:key == 1) ? [2, 1] : [1, 2]
+ " check all diff lines
+ if t:DChar.dfp == 0
+ if a:init
+ for k in ks
+ call s:WinGotoID(t:DChar.wid[k])
+ call s:WinExecute('let dfl[k] =
+ \s:GetDiffLines(1, t:DChar.lcc[k].ll)')
+ endfor
+ return dfl
+ else
+ return t:DChar.dfl
+ endif
+ endif
+ " check visible diff lines and merge with previous dfl
+ " 1. get dfl in current visible lines and return if no new in both wins
+ for k in ks
+ call s:WinGotoID(t:DChar.wid[k])
+ call s:WinExecute('let dfl[k] =
+ \s:GetDiffLines(t:DChar.lcc[k].tl, t:DChar.lcc[k].bl)')
+ endfor
+ if !a:init
+ let nd = 0
+ for k in ks
+ if !empty(dfl[k])
+ let [ti, bi] = [index(t:DChar.dfl[k], dfl[k][0]),
+ \index(t:DChar.dfl[k], dfl[k][-1])]
+ let nd += (ti == -1) || (bi == -1) ||
+ \(t:DChar.dfl[k][ti : bi] != dfl[k])
+ endif
+ endfor
+ if nd == 0 | return t:DChar.dfl | endif
+ let lh = {}
+ for k in ks
+ let lh[k] = empty(dfl[k]) ? {'l': 0, 'h': 0} :
+ \{'l': len(filter(copy(t:DChar.dfl[k]), 'v:val < dfl[k][0]')),
+ \'h': len(filter(copy(t:DChar.dfl[k]), 'dfl[k][-1] < v:val'))}
+ endfor
+ endif
+ " 2. get dfl in upper/lower lines and return if not found in both wins
+ for k in ks
+ let [fl, tl, bl, ll] =
+ \[1, t:DChar.lcc[k].tl, t:DChar.lcc[k].bl, t:DChar.lcc[k].ll]
+ if !a:init && 0 < t:DChar.dfp
+ if 0 < lh[k].l | let fl = t:DChar.dfl[k][lh[k].l - 1] + 1 | endif
+ if 0 < lh[k].h | let ll = t:DChar.dfl[k][-lh[k].h] - 1 | endif
+ endif
+ let [tz, bz] = [tl, bl]
+ let rc = min([(tl - fl) + (ll - bl),
+ \(abs(t:DChar.dfp) - 1) * (bl - tl + 1)])
+ if 0 < rc
+ let hc = (rc + 1) / 2
+ let [tr, br] = [tl - fl, ll - bl]
+ let tb = [hc <= tr, rc - hc <= br]
+ let [tc, bc] = (tb == [1, 1]) ? [hc, rc - hc] : (tb == [0, 1]) ?
+ \[tr, rc - tr] : (tb == [1, 0]) ? [rc - br, br] : [tr, br]
+ let [tz, bz] += [-tc, bc]
+ endif
+ call s:WinGotoID(t:DChar.wid[k])
+ call s:WinExecute('let dfl[k] = s:GetDiffLines(tz, tl - 1) + dfl[k] +
+ \s:GetDiffLines(bl + 1, bz)')
+ endfor
+ if empty(dfl[1]) && empty(dfl[2]) | return dfl | endif
+ if index(values(dfl), []) != -1
+ " 3. if no dfl found in either, set a reference line and search dfl
+ let [ek, fk] = empty(dfl[1]) ? [1, 2] : [2, 1]
+ let rl = {}
+ let [rl[ek], rl[fk]] = [{'l': 1, 'h': t:DChar.lcc[ek].ll},
+ \{'l': 1, 'h': t:DChar.lcc[fk].ll}]
+ if !a:init
+ if 0 < lh[fk].l
+ let [rl[ek].l, rl[fk].l] = [t:DChar.dfl[ek][lh[fk].l - 1],
+ \t:DChar.dfl[fk][lh[fk].l - 1]]
+ endif
+ if 0 < lh[fk].h
+ let [rl[ek].h, rl[fk].h] = [t:DChar.dfl[ek][-lh[fk].h],
+ \t:DChar.dfl[fk][-lh[fk].h]]
+ endif
+ endif
+ let sd = dfl[fk][0] - rl[fk].l < rl[fk].h - dfl[fk][-1]
+ call s:WinGotoID(t:DChar.wid[fk])
+ call s:WinExecute('let dc = len(sd ?
+ \s:GetDiffLines(rl[fk].l, dfl[fk][0] - 1) :
+ \s:GetDiffLines(dfl[fk][-1] + 1, rl[fk].h))')
+ let fc = len(dfl[fk])
+ call s:WinGotoID(t:DChar.wid[ek])
+ call s:WinExecute('let dfl[ek] =
+ \s:SearchDiffLines(sd, sd ? rl[ek].l : rl[ek].h, dc + fc)')
+ let dfl[ek] = sd ? dfl[ek][-fc :] : dfl[ek][: fc - 1]
+ call s:WinGotoID(t:DChar.wid[ks[1]])
+ else
+ " 4. set reference lines using the closest line of previous dfl
+ let rl = [[1, 1], [t:DChar.lcc[1].ll, t:DChar.lcc[2].ll]]
+ if !a:init && index(values(t:DChar.dfl), []) == -1
+ let rx = []
+ for k in ks
+ let rx += ((0 < lh[k].l) ? [lh[k].l - 1] : [0]) +
+ \((0 < lh[k].h) ? [-lh[k].h] : [-1])
+ endfor
+ let rl += map(rx,
+ \'[t:DChar.dfl[1][v:val], t:DChar.dfl[2][v:val]]')
+ endif
+ " 5. select a reference line which is closest to new dfl
+ let ds = map(copy(rl), 'abs(v:val[0] - (dfl[1][0] + dfl[1][-1]) / 2) +
+ \abs(v:val[1] - (dfl[2][0] + dfl[2][-1]) / 2)')
+ let ci = index(ds, min(ds))
+ let cl = {'1': rl[ci][0], '2': rl[ci][1]}
+ " 6. get # of dfl (+/-) between reference and top/bottom lines
+ let tb = {}
+ for k in ks
+ let [tl, bl, xl] = [dfl[k][0], dfl[k][-1], cl[k]]
+ let td = (tl <= xl) ? [tl, xl, 1] : [xl, tl, -1]
+ let bd = (bl <= xl) ? [bl, xl, 1] : [xl, bl, -1]
+ call s:WinGotoID(t:DChar.wid[k])
+ call s:WinExecute('let tb[k] =
+ \[(len(s:GetDiffLines(td[0], td[1])) - 1) * td[2],
+ \(len(s:GetDiffLines(bd[0], bd[1])) - 1) * bd[2]]')
+ endfor
+ " 7. search and adjust dfl above/below the top/bottom in each window
+ let dx = {'1': [0, 0, len(dfl[2])], '2': [0, 0, len(dfl[1])]}
+ let [tx, bx] = [tb[1][0] - tb[2][0], tb[1][1] - tb[2][1]]
+ let k = (tx < 0) ? 1 : (tx > 0) ? 2 : 0
+ if k != 0 | let dx[k][0] = abs(tx) | endif
+ let k = (bx < 0) ? 2 : (bx > 0) ? 1 : 0
+ if k != 0 | let dx[k][1] = abs(bx) | endif
+ for k in ks
+ call s:WinGotoID(t:DChar.wid[k])
+ call s:WinExecute('let [td, bd] =
+ \[s:SearchDiffLines(0, dfl[k][0] - 1, dx[k][0]),
+ \s:SearchDiffLines(1, dfl[k][-1] + 1, dx[k][1])]')
+ let [tx, bx] =
+ \[min([dx[k][2], len(td)]), min([dx[k][2], len(bd)])]
+ let dfl[k] = td[: tx - 1] + dfl[k] + bd[-bx :]
+ endfor
+ endif
+ " 8. merge with previous dfl when 0 < dfp
+ if !a:init && index(values(t:DChar.dfl), []) == -1 && 0 < t:DChar.dfp
+ for k in ks
+ call filter(dfl[k], 'index(t:DChar.dfl[k], v:val) == -1')
+ let dfl[k] = empty(dfl[k]) ? t:DChar.dfl[k] :
+ \(t:DChar.dfl[k][-1] < dfl[k][0]) ? t:DChar.dfl[k] + dfl[k] :
+ \(dfl[k][-1] < t:DChar.dfl[k][0]) ? dfl[k] + t:DChar.dfl[k] :
+ \filter(copy(t:DChar.dfl[k]), 'v:val < dfl[k][0]') + dfl[k] +
+ \filter(copy(t:DChar.dfl[k]), 'dfl[k][-1] < v:val')
+ endfor
+ endif
+ return dfl
+endfunction
+
+function! s:SearchDiffLines(sd, sl, sc) abort
+ " a:sd = direction (1:down, 0:up), a:sl = start line, a:sc = count
+ let dl = []
+ if 0 < a:sc
+ let sl = a:sl
+ if a:sd
+ while sl <= line('$')
+ let fl = foldclosedend(sl)
+ if fl != -1 | let sl = fl + 1 | endif
+ let dl += s:GetDiffLines(sl, min([sl + a:sc - 1, line('$')]))
+ if a:sc <= len(dl) | let dl = dl[: a:sc - 1] | break | endif
+ let sl += a:sc
+ endwhile
+ else
+ while 1 <= sl
+ let fl = foldclosed(sl)
+ if fl != -1 | let sl = fl - 1 | endif
+ let dl = s:GetDiffLines(max([sl - a:sc + 1, 1]), sl) + dl
+ if a:sc <= len(dl) | let dl = dl[-a:sc :] | break | endif
+ let sl -= a:sc
+ endwhile
+ endif
+ endif
+ return dl
+endfunction
+
+function! s:GetDiffLines(sl, el) abort
+ return (a:sl > a:el) ? [] :
+ \filter(filter(range(a:sl, a:el), 'foldlevel(v:val) == 0'),
+ \'index([s:DiffHL.C.id, s:DiffHL.T.id], diff_hlID(v:val, 1)) != -1')
+endfunction
+
+function! s:GetLineColCnr(key) abort
+ let lcc = {}
+ for k in (a:key == 1) ? [2, 1] : [1, 2]
+ call s:WinGotoID(t:DChar.wid[k])
+ call s:WinExecute('let lcc[k] =
+ \{"tl": line("w0"), "bl": line("w$"), "ll": line("$"),
+ \"cl": line("."), "cc": col("."), "cn": s:Changenr(), "ig": 0}')
+ call s:WinExecute('let [tl, bl] =
+ \[foldclosedend(lcc[k].tl), foldclosed(lcc[k].bl)]')
+ if tl != -1 | let lcc[k].tl = tl | endif
+ if bl != -1 | let lcc[k].bl = bl | endif
+ endfor
+ return lcc
+endfunction
+
+function! s:ScrollDiffChar(key) abort
+ if !exists('t:DChar') || t:DChar.wid[a:key] != win_getid()
+ return
+ endif
+ let lcc = s:GetLineColCnr(a:key)
+ let scl = 0
+ for k in [1, 2]
+ " check if a scroll happens in either window with no change on both
+ let scl += (t:DChar.lcc[k].cn != lcc[k].cn) ? -1 :
+ \([t:DChar.lcc[k].tl, t:DChar.lcc[k].bl] !=
+ \[lcc[k].tl, lcc[k].bl]) ? 1 : 0
+ let [t:DChar.lcc[k].tl, t:DChar.lcc[k].bl] = [lcc[k].tl, lcc[k].bl]
+ endfor
+ if 0 < scl
+ let dfl = s:FocusDiffLines(a:key, 0)
+ if t:DChar.dfl != dfl
+ " reset/show DChar lines on dfl changes
+ if t:DChar.dfp < 0
+ let ddl = filter(copy(t:DChar.dfl[a:key]),
+ \'index(dfl[a:key], v:val) == -1')
+ if !empty(ddl) | call diffchar#ResetDiffChar(ddl) | endif
+ endif
+ let adl = filter(copy(dfl[a:key]),
+ \'index(t:DChar.dfl[a:key], v:val) == -1')
+ let t:DChar.dfl = dfl
+ if !empty(adl) | call diffchar#ShowDiffChar(adl) | endif
+ endif
+ endif
+endfunction
+
+function! s:HighlightDiffChar(key, lec) abort
+ let hn = len(t:DChar.hgp)
+ for k in (a:key == 1) ? [2, 1] : [1, 2]
+ if !s:VF.WinIDinMatch | call s:WinGotoID(t:DChar.wid[k]) | endif
+ for [l, ec] in items(a:lec[k])
+ if has_key(t:DChar.mid[k], l) | continue | endif
+ let t:DChar.hlc[k][l] = ec
+ " collect all the column positions per highlight group
+ let hc = {}
+ let cn = 0
+ for [e, c] in ec
+ if e == 'c'
+ let h = t:DChar.hgp[cn % hn]
+ let cn += 1
+ elseif e == 'a'
+ let h = s:DCharHL.A
+ elseif e == 'd'
+ if c == [0, 0] | continue | endif
+ let h = s:DCharHL.E
+ endif
+ if !has_key(hc, h) | let hc[h] = [] | endif
+ let hc[h] += [[l, c[0], c[1] - c[0] + 1]]
+ endfor
+ let t:DChar.mid[k][l] = [s:Matchaddpos(s:DCharHL.C, [[l]], -5, -1,
+ \{'window': t:DChar.wid[k]})]
+ for [h, c] in items(hc)
+ let t:DChar.mid[k][l] += map(range(0, len(c) - 1, 8),
+ \'s:Matchaddpos(h, c[v:val : v:val + 7], -3, -1,
+ \{"window": t:DChar.wid[k]})')
+ endfor
+ endfor
+ endfor
+endfunction
+
+function! s:ClearDiffChar(key, lines) abort
+ for k in (a:key == 1) ? [2, 1] : [1, 2]
+ if win_id2win(t:DChar.wid[k]) != 0
+ if !s:VF.WinIDinMatch | call s:WinGotoID(t:DChar.wid[k]) | endif
+ for l in a:lines[k]
+ silent! call map(t:DChar.mid[k][l],
+ \'s:Matchdelete(v:val, t:DChar.wid[k])')
+ endfor
+ endif
+ for l in a:lines[k]
+ unlet t:DChar.mid[k][l]
+ unlet t:DChar.hlc[k][l]
+ endfor
+ endfor
+endfunction
+
+function! s:ShiftDiffChar(key, lines, shift) abort
+ let [lid, hlc, cks] = [[], {}, {}]
+ for l in filter(copy(a:lines), 'has_key(t:DChar.mid[a:key], v:val)')
+ let lid += [[l, t:DChar.mid[a:key][l]]]
+ let hlc[l + a:shift] = t:DChar.hlc[a:key][l]
+ let cks[l + a:shift] = t:DChar.cks[a:key][l]
+ unlet t:DChar.mid[a:key][l]
+ unlet t:DChar.hlc[a:key][l]
+ unlet t:DChar.cks[a:key][l]
+ endfor
+ call extend(t:DChar.mid[a:key],
+ \s:ShiftMatchaddLines(t:DChar.wid[a:key], lid, a:shift))
+ call extend(t:DChar.hlc[a:key], hlc)
+ call extend(t:DChar.cks[a:key], cks)
+endfunction
+
+function! s:ShiftMatchaddLines(wid, lid, shift) abort
+ let lid = {}
+ let gm = s:Getmatches(a:wid)
+ for [l, id] in a:lid
+ let mx = filter(copy(gm), 'index(id, v:val.id) != -1')
+ call map(copy(mx), 's:Matchdelete(v:val.id, a:wid)')
+ let lid[l + a:shift] = map(reverse(mx), 's:Matchaddpos(v:val.group,
+ \map(filter(items(v:val), "v:val[0] =~ ''^pos\\d\\+$''"),
+ \"[v:val[1][0] + a:shift] + v:val[1][1 :]"), v:val.priority, -1,
+ \{"window": a:wid})')
+ endfor
+ return lid
+endfunction
+
+function! s:UpdateDiffChar(key, event) abort
+ " a:event : 0 = TextChanged/InsertLeave, 1 = DiffUpdated
+ if mode(1) != 'n' || !exists('t:DChar') ||
+ \!empty(filter(values(t:DChar.wid), '!getwinvar(v:val, "&diff")'))
+ return
+ endif
+ if !s:VF.DiffUpdated | call s:RedrawDiffChar(a:key, 1) | return | endif
+ " try to redraw updated DChar lines at the last DiffUpdated which comes
+ " just after TextChanged or InsertLeave if text changed
+ if a:event == 1
+ if t:DChar.lcc[a:key].ig == 0
+ if t:DChar.lcc[a:key].cn == s:Changenr()
+ call s:RedrawDiffChar(a:key, 0)
+ else
+ " in case of e:, DiffUpdated happens 3 times,
+ " first, all dfl not diff highlighed but no line diff folded,
+ " then wait for the next two events
+ if &foldmethod == 'diff' && !empty(t:DChar.dfl[a:key]) &&
+ \empty(filter(copy(t:DChar.dfl[a:key]),
+ \'diff_hlID(v:val, 1) != 0')) &&
+ \empty(filter(range(t:DChar.dfl[a:key][0],
+ \t:DChar.dfl[a:key][-1]), '0 < foldlevel(v:val)'))
+ let t:DChar.lcc[a:key].ig += 1
+ endif
+ endif
+ elseif t:DChar.lcc[a:key].ig == 1 " wait for another one
+ let t:DChar.lcc[a:key].ig += 1
+ elseif t:DChar.lcc[a:key].ig == 2 " the last one came then redraw
+ let t:DChar.lcc[a:key].ig = 0
+ call s:RedrawDiffChar(a:key, 1)
+ endif
+ else
+ if &diffopt =~ 'internal' && empty(&diffexpr)
+ if t:DChar.lcc[a:key].cn != s:Changenr()
+ let t:DChar.lcc[a:key].ig = 2 " wait for the next/last one
+ endif
+ else
+ call s:RedrawDiffChar(a:key, 1) " DiffUpdated not happen next
+ endif
+ endif
+endfunction
+
+function! s:RedrawDiffChar(key, txtcg) abort
+ " a:txtcg : 0 = for text unchanged, 1 = for text changed
+ let ll = t:DChar.lcc[a:key].ll
+ let t:DChar.lcc = s:GetLineColCnr(a:key)
+ let cfl = s:FocusDiffLines(a:key, 1)
+ if a:txtcg
+ " compare between previous and current DChar and diff lines
+ " using checksum and find ones to be deleted, added, and shifted
+ let lnd = t:DChar.lcc[a:key].ll - ll
+ let bk = (a:key == 1) ? 2 : 1
+ let [pfa, pfb] = [t:DChar.dfl[a:key], t:DChar.dfl[bk]]
+ let [cfa, cfb] = [cfl[a:key], cfl[bk]]
+ let m = min([len(pfa), len(cfa)])
+ if pfa == cfa
+ let ddl = []
+ for s in range(m)
+ if pfb[s] != cfb[s] || get(t:DChar.cks[a:key], pfa[s]) !=
+ \s:ChecksumStr(getline(cfa[s]))
+ let ddl += [pfa[s]]
+ endif
+ endfor
+ let adl = ddl
+ let sdl = []
+ else
+ let s = 0
+ while s < m && pfa[s] == cfa[s] && pfb[s] == cfb[s] &&
+ \get(t:DChar.cks[a:key], pfa[s]) ==
+ \s:ChecksumStr(getline(cfa[s]))
+ let s += 1
+ endwhile
+ let e = -1
+ let m -= s
+ while e >= -m && pfa[e] + lnd == cfa[e] && pfb[e] == cfb[e] &&
+ \get(t:DChar.cks[a:key], pfa[e]) ==
+ \s:ChecksumStr(getline(cfa[e]))
+ let e -= 1
+ endwhile
+ let ddl = pfa[s : e]
+ let adl = cfa[s : e]
+ let sdl = (lnd != 0 && e < -1) ? pfa[e + 1 :] : []
+ endif
+ " redraw updated DChar lines
+ if 0 < t:DChar.dpv.pv | call s:ClearDiffCharPair(a:key) | endif
+ if !empty(ddl) | call diffchar#ResetDiffChar(ddl) | endif
+ let t:DChar.dfl = cfl
+ if !empty(sdl) | call s:ShiftDiffChar(a:key, sdl, lnd) | endif
+ if !empty(adl) | call diffchar#ShowDiffChar(adl) | endif
+ else
+ " reset dfl and redraw all DChar lines on text unchanged
+ " (diffupdate and diffopt changes)
+ let do = split(&diffopt, ',')
+ let igc = (index(do, 'icase') != -1)
+ let igs = (index(do, 'iwhiteall') != -1) ? 1 :
+ \(index(do, 'iwhite') != -1) ? 2 :
+ \(index(do, 'iwhiteeol') != -1) ? 3 : 0
+ if [t:DChar.dfl, t:DChar.igc, t:DChar.igs] != [cfl, igc, igs]
+ call diffchar#ResetDiffChar()
+ let [t:DChar.dfl, t:DChar.igc, t:DChar.igs] = [cfl, igc, igs]
+ call diffchar#ShowDiffChar()
+ endif
+ endif
+endfunction
+
+function! diffchar#JumpDiffChar(dir, pos) abort
+ " a:dir : 0 = backward, 1 = forward / a:pos : 0 = start, 1 = end
+ if !exists('t:DChar') | return | endif
+ for k in [1, 2, 0]
+ if k == 0 | return | endif
+ if t:DChar.wid[k] == win_getid() | break | endif
+ endfor
+ let [ln, co] = [line('.'), col('.')]
+ if co == col('$') " empty line
+ if !a:dir | let co = 0 | endif
+ else
+ if a:pos
+ let co += len(strcharpart(getline(ln)[co - 1 :], 0, 1)) - 1
+ endif
+ endif
+ if has_key(t:DChar.hlc[k], ln) &&
+ \(a:dir ? co < t:DChar.hlc[k][ln][-1][1][a:pos] :
+ \co > t:DChar.hlc[k][ln][0][1][a:pos])
+ " found in current line
+ let hc = filter(map(copy(t:DChar.hlc[k][ln]), 'v:val[1][a:pos]'),
+ \a:dir ? 'co < v:val' : 'co > v:val')
+ let co = hc[a:dir ? 0 : -1]
+ else
+ if t:DChar.dfp == 0
+ " try to find the next highlighted line
+ let hl = filter(map(keys(t:DChar.hlc[k]), 'eval(v:val)'),
+ \a:dir ? 'ln < v:val' : 'ln > v:val')
+ if empty(hl) | return | endif
+ let ln = a:dir ? min(hl) : max(hl)
+ else
+ " try to find the next diff line and then apply hlc or scroll
+ let cp = [line('.'), col('.')]
+ while 1
+ let dl = s:SearchDiffLines(a:dir, a:dir ? ln + 1 : ln - 1, 1)
+ if empty(dl) | noautocmd call cursor(cp) | return | endif
+ let ln = dl[0]
+ if has_key(t:DChar.hlc[k], ln) | break | endif
+ noautocmd call cursor(ln, 0)
+ call s:ScrollDiffChar(k)
+ if has_key(t:DChar.hlc[k], ln) | break | endif
+ endwhile
+ endif
+ let co = t:DChar.hlc[k][ln][a:dir ? 0 : -1][1][a:pos]
+ endif
+ " set a dummy cursor position to adjust the start/end
+ if 0 < t:DChar.dpv.pv
+ call s:ClearDiffCharPair(k)
+ if [a:dir, a:pos] == [1, 0] " forward/start : rightmost
+ let [t:DChar.lcc[k].cl, t:DChar.lcc[k].cc] = [ln, col('$')]
+ elseif [a:dir, a:pos] == [0, 1] " backward/end : leftmost
+ let [t:DChar.lcc[k].cl, t:DChar.lcc[k].cc] = [ln, 0]
+ endif
+ endif
+ call cursor(ln, co)
+endfunction
+
+function! s:ShowDiffCharPair(key) abort
+ if mode(1) != 'n' || !exists('t:DChar') ||
+ \t:DChar.wid[a:key] != win_getid()
+ return
+ endif
+ let [pl, pc, pn] = [t:DChar.lcc[a:key].cl, t:DChar.lcc[a:key].cc,
+ \t:DChar.lcc[a:key].cn]
+ let [cl, cc] = [line('.'), col('.')]
+ if cc == col('$') | let cc = 0 | endif
+ let [t:DChar.lcc[a:key].cl, t:DChar.lcc[a:key].cc] = [cl, cc]
+ if pn != s:Changenr() | return | endif " do nothing on TextChanged
+ if !empty(t:DChar.dpv.ch)
+ " pair highlight exists
+ let [hl, hi] = t:DChar.dpv.ch.lc
+ let hc = t:DChar.hlc[a:key][hl][hi][1]
+ " inside the highlight, do nothing
+ if cl == hl && hc[0] <= cc && cc <= hc[1] | return | endif
+ call s:ClearDiffCharPair(a:key) " outside, clear it
+ endif
+ if has_key(t:DChar.hlc[a:key], cl)
+ let hu = filter(map(copy(t:DChar.hlc[a:key][cl]),
+ \'[v:key, v:val[1]]'), 'v:val[1][0] <= cc && cc <= v:val[1][1]')
+ if !empty(hu)
+ " for 2 contineous 'd', check if cursor moved forward or backward
+ let ix = (len(hu) == 1) ? 0 : (cl == pl) ? cc < pc : cl < pl
+ call s:HighlightDiffCharPair(a:key, cl, hu[ix][0])
+ endif
+ endif
+endfunction
+
+function! s:HighlightDiffCharPair(key, line, col) abort
+ let [ak, bk] = (a:key == 1) ? [1, 2] : [2, 1]
+ let [al, bl] = [a:line, t:DChar.dfl[bk][index(t:DChar.dfl[ak], a:line)]]
+ " set a pair cursor position (line, colnum) and match id
+ let t:DChar.dpv.ch.lc = [al, a:col]
+ let t:DChar.dpv.ch.bk = bk
+ " show a cursor-like highlight at the corresponding position
+ let bc = t:DChar.hlc[bk][bl][a:col][1]
+ if bc != [0, 0]
+ let [pos, len] = [bc[0], bc[1] - bc[0] + 1]
+ if !s:VF.WinIDinMatch | call s:WinGotoID(t:DChar.wid[bk]) | endif
+ let t:DChar.dpv.ch.id = s:Matchaddpos(s:DCharHL.c, [[bl, pos, len]],
+ \-1, -1, {'window': t:DChar.wid[bk]})
+ if !s:VF.WinIDinMatch | call s:WinGotoID(t:DChar.wid[ak]) | endif
+ else
+ let t:DChar.dpv.ch.id = -1 " no cursor hl on empty line
+ endif
+ call execute(['augroup diffchar2', 'autocmd!',
+ \'autocmd WinLeave call s:ClearDiffCharPair(' . ak . ')', 'augroup END'])
+ if t:DChar.dpv.pv < 2 | return | endif
+ " show the corresponding unit in echo or popup-window
+ let at = getbufline(t:DChar.bnr[ak], al)[0]
+ let bt = getbufline(t:DChar.bnr[bk], bl)[0]
+ let [ae, ac] = t:DChar.hlc[ak][al][a:col]
+ if ae == 'c'
+ let hl = t:DChar.hgp[(count(map(t:DChar.hlc[ak][al][: a:col],
+ \'v:val[0]'), 'c') - 1) % len(t:DChar.hgp)]
+ let [tb, tx, te] = ['', bt[bc[0] - 1 : bc[1] - 1], '']
+ elseif ae == 'd'
+ let hl = s:DCharHL.A
+ let [tb, tx, te] = [(1 < bc[0]) ? '<' : '',
+ \bt[bc[0] - 1 : bc[1] - 1], (bc[1] < len(bt)) ? '>' : '']
+ elseif ae == 'a'
+ let hl = s:DCharHL.D
+ let [tb, tx, te] = [(1 < ac[0]) ? '>' : '',
+ \repeat((t:DChar.dpv.pv == 2 && s:VF.StrikeAttr) ? ' ' :
+ \(&fillchars =~ 'diff') ? matchstr(&fillchars, 'diff:\zs.') : '-',
+ \strwidth(at[ac[0] - 1 : ac[1] - 1])),
+ \(ac[1] < len(at)) ? '<' : '']
+ endif
+ if t:DChar.dpv.pv == 2
+ call execute(['echon tb', 'echohl ' . hl, 'echon tx', 'echohl None',
+ \'echon te'], '')
+ elseif t:DChar.dpv.pv == 3 || t:DChar.dpv.pv == 4
+ if t:DChar.dpv.pv == 4 | let mp = getmousepos() | endif
+ if s:VF.PopupWindow
+ call popup_move(t:DChar.dpv.pw, (t:DChar.dpv.pv == 3) ?
+ \{'line': 'cursor+1', 'col': 'cursor'} :
+ \{'line': mp.screenrow, 'col': mp.screencol})
+ call popup_settext(t:DChar.dpv.pw, tb . tx . te)
+ call popup_show(t:DChar.dpv.pw)
+ elseif s:VF.FloatingWindow
+ call nvim_win_set_config(t:DChar.dpv.pw.fw,
+ \extend((t:DChar.dpv.pv == 3) ?
+ \{'relative': 'cursor', 'row': 1, 'col': 0} :
+ \{'relative': 'editor', 'row': mp.screenrow,
+ \'col': mp.screencol},
+ \{'width': strdisplaywidth(tb . tx . te)}))
+ call setbufline(t:DChar.dpv.pw.fb, 1, tb . tx . te)
+ call setwinvar(t:DChar.dpv.pw.fw, '&winblend', 0)
+ endif
+ endif
+endfunction
+
+function! s:ClearDiffCharPair(key) abort
+ if !empty(t:DChar.dpv.ch)
+ let [bk, id] = [t:DChar.dpv.ch.bk, t:DChar.dpv.ch.id]
+ if id != -1 && win_id2win(t:DChar.wid[bk]) != 0
+ if !s:VF.WinIDinMatch | call s:WinGotoID(t:DChar.wid[bk]) | endif
+ silent! call s:Matchdelete(id, t:DChar.wid[bk])
+ if !s:VF.WinIDinMatch
+ call s:WinGotoID(t:DChar.wid[a:key])
+ endif
+ endif
+ call execute(['augroup diffchar2', 'autocmd!', 'augroup END',
+ \'augroup! diffchar2'])
+ let t:DChar.dpv.ch = {}
+ endif
+ if t:DChar.dpv.pv == 2 | call execute('echo', '')
+ elseif t:DChar.dpv.pv == 3 || t:DChar.dpv.pv == 4
+ if s:VF.PopupWindow | call popup_hide(t:DChar.dpv.pw)
+ elseif s:VF.FloatingWindow
+ call nvim_win_set_config(t:DChar.dpv.pw.fw,
+ \{'relative': 'editor', 'row': 0, 'col': 0, 'width': 1})
+ call setbufline(t:DChar.dpv.pw.fb, 1, '')
+ call setwinvar(t:DChar.dpv.pw.fw, '&winblend', 100)
+ endif
+ endif
+endfunction
+
+function! s:ToggleDiffCharPair(on) abort
+ if t:DChar.dpv.pv == 3 || t:DChar.dpv.pv == 4
+ if s:VF.PopupWindow
+ let t:DChar.dpv.pw = a:on ?
+ \popup_create('', {'hidden': 1, 'scrollbar': 0, 'wrap': 0,
+ \'highlight': s:DCharHL.c}) :
+ \popup_close(t:DChar.dpv.pw)
+ elseif s:VF.FloatingWindow
+ if a:on
+ let t:DChar.dpv.pw.fb = nvim_create_buf(0, 1)
+ let t:DChar.dpv.pw.fw = nvim_open_win(t:DChar.dpv.pw.fb, 0,
+ \{'relative': 'editor', 'row': 0, 'col': 0, 'height': 1,
+ \'width': 1, 'focusable': 0, 'style': 'minimal'})
+ call setbufline(t:DChar.dpv.pw.fb, 1, '')
+ call setwinvar(t:DChar.dpv.pw.fw, '&winblend', 100)
+ call setwinvar(t:DChar.dpv.pw.fw, '&winhighlight',
+ \'Normal:' . s:DCharHL.c)
+ else
+ call nvim_win_close(t:DChar.dpv.pw.fw, 1)
+ let t:DChar.dpv.pw = {'fb': -1, 'fw': -1}
+ endif
+ endif
+ endif
+endfunction
+
+function! diffchar#CopyDiffCharPair(dir) abort
+ " a:dir : 0 = get, 1 = put
+ if !exists('t:DChar') | return | endif
+ for ak in [1, 2, 0]
+ if ak == 0 | return | endif
+ if t:DChar.wid[ak] == win_getid() | break | endif
+ endfor
+ let bk = (ak == 1) ? 2 : 1
+ let un = -1
+ if 0 < t:DChar.dpv.pv
+ if !empty(t:DChar.dpv.ch) | let [al, un] = t:DChar.dpv.ch.lc | endif
+ else
+ let [al, co] = [line('.'), col('.')]
+ if co == col('$') | let co = 0 | endif
+ if has_key(t:DChar.hlc[ak], al)
+ let hc = filter(map(copy(t:DChar.hlc[ak][al]),
+ \'[v:key, v:val[1]]'),
+ \'v:val[1][0] <= co && co <= v:val[1][1]')
+ if !empty(hc) | let un = hc[0][0] | endif
+ endif
+ endif
+ if un == -1
+ call s:EchoWarning('Cursor is not on a difference unit!')
+ return
+ endif
+ let bl = t:DChar.dfl[bk][index(t:DChar.dfl[ak], al)]
+ let [ae, ac] = t:DChar.hlc[ak][al][un]
+ let [be, bc] = t:DChar.hlc[bk][bl][un]
+ let at = getbufline(t:DChar.bnr[ak], al)[0]
+ let bt = getbufline(t:DChar.bnr[bk], bl)[0]
+ let [x, y] = a:dir ? ['b', 'a'] : ['a', 'b'] " put : get
+ let s1 = (1 < {x}c[0]) ? {x}t[: {x}c[0] - 2] : ''
+ let s2 = ({x}e != 'a') ? {y}t[{y}c[0] - 1 : {y}c[1] - 1] : ''
+ if {x}e == 'd' && {x}c != [0, 0]
+ let ds = split({x}t[{x}c[0] - 1 : {x}c[1] - 1], '\zs')
+ let s2 = ((1 < {y}c[0]) ? ds[0] : '') . s2 .
+ \(({y}c[1] < len({y}t)) ? ds[-1] : '')
+ endif
+ let s3 = ({x}c[1] < len({x}t)) ? {x}t[{x}c[1] :] : ''
+ let ss = s1 . s2 . s3
+ if a:dir " put
+ call s:WinGotoID(t:DChar.wid[bk])
+ call s:WinExecute('noautocmd call setline(bl, ss)')
+ call s:WinExecute('call s:RedrawDiffChar(bk, 1)')
+ call s:WinGotoID(t:DChar.wid[ak])
+ else " get
+ call setline(al, ss)
+ endif
+endfunction
+
+function! diffchar#EchoDiffChar(lines, short) abort
+ if !exists('t:DChar') | return | endif
+ for ak in [1, 2, 0]
+ if ak == 0 | return | endif
+ if t:DChar.wid[ak] == win_getid() | break | endif
+ endfor
+ let bk = (ak == 1) ? 2 : 1
+ let nw = max([&numberwidth - 1, len(string(line('$')))])
+ let ec = []
+ for al in a:lines
+ let gt = []
+ if &number || &relativenumber
+ let gt += [[s:DCharHL.n, printf('%'. nw . 'd ',
+ \(&relativenumber ? abs(al - line('.')) : al))]]
+ endif
+ let at = getbufline(t:DChar.bnr[ak], al)[0]
+ if !has_key(t:DChar.hlc[ak], al)
+ if a:short | continue | endif
+ let gt += [['', empty(at) ? "\n" : at]]
+ else
+ let bl = t:DChar.dfl[bk][index(t:DChar.dfl[ak], al)]
+ let bt = getbufline(t:DChar.bnr[bk], bl)[0]
+ let hl = repeat('C', len(at))
+ let tx = at
+ for an in range(len(t:DChar.hlc[ak][al]) - 1, 0, -1)
+ let [ae, ac] = t:DChar.hlc[ak][al][an]
+ " enclose highlight and text in '[+' and '+]'
+ " if strike not available
+ if ae == 'c' || ae == 'a'
+ let it = at[ac[0] - 1 : ac[1] - 1]
+ if !s:VF.StrikeAttr | let it = '[+' . it . '+]' | endif
+ let ih = repeat((ae == 'a') ? 'A' : 'T', len(it))
+ let hl = ((1 < ac[0]) ? hl[: ac[0] - 2] : '') . ih .
+ \hl[ac[1] :]
+ let tx = ((1 < ac[0]) ? tx[: ac[0] - 2] : '') . it .
+ \tx[ac[1] :]
+ endif
+ " enclose corresponding changed/deleted units in '[-' and '-]'
+ " if strike not available,
+ " and insert them to highlight and text
+ if ae == 'c' || ae == 'd'
+ let bc = t:DChar.hlc[bk][bl][an][1]
+ let it = bt[bc[0] - 1 : bc[1] - 1]
+ if !s:VF.StrikeAttr | let it = '[-' . it . '-]' | endif
+ let ih = repeat('D', len(it))
+ if ae == 'c'
+ let hl = ((1 < ac[0]) ? hl[: ac[0] - 2] : '') . ih .
+ \hl[ac[0] - 1 :]
+ let tx = ((1 < ac[0]) ? tx[: ac[0] - 2] : '') . it .
+ \tx[ac[0] - 1 :]
+ else
+ if ac[0] == 1 && bc[0] == 1
+ let hl = ih . hl
+ let tx = it . tx
+ else
+ let ix = ac[0] +
+ \len(strcharpart(at[ac[0] - 1 :], 0, 1)) - 2
+ let hl = hl[: ix] . ih . hl[ix + 1 :]
+ let tx = tx[: ix] . it . tx[ix + 1 :]
+ endif
+ endif
+ endif
+ endfor
+ let sm = a:short && &columns <= strdisplaywidth(tx)
+ let ix = 0
+ let tn = 0
+ for h in split(hl, '\%(\(.\)\1*\)\zs')
+ if h[0] == 'T'
+ let g = t:DChar.hgp[tn % len(t:DChar.hgp)]
+ let tn += 1
+ else
+ let g = s:DCharHL[h[0]]
+ endif
+ let t = tx[ix : ix + len(h) - 1]
+ if sm && h[0] == 'C'
+ let s = split(t, '\zs')
+ if ix == 0 && 1 < len(s) &&
+ \3 < strdisplaywidth(join(s[: -2], ''))
+ let t = '...' . s[-1]
+ elseif ix + len(h) == len(tx) && 1 < len(s) &&
+ \3 < strdisplaywidth(join(s[1 :], ''))
+ let t = s[0] . '...'
+ elseif 2 < len(s) &&
+ \3 < strdisplaywidth(join(s[1 : -2], ''))
+ let t = s[0] . '...' . s[-1]
+ endif
+ endif
+ let gt += [[g, t]]
+ let ix += len(h)
+ endfor
+ endif
+ let ec += ['echo ""']
+ for [g, t] in gt
+ let ec += ['echohl ' . g, 'echon "' . escape(t, '"') . '"']
+ endfor
+ let ec += ['echohl None']
+ endfor
+ call execute(ec, '')
+endfunction
+
+function! diffchar#DiffCharExpr() abort
+ let [f1, f2] = [readfile(v:fname_in), readfile(v:fname_new)]
+ call writefile(([f1, f2] == [['line1'], ['line2']]) ? ['1c1'] :
+ \(s:VF.DiffExecutable && len(f1) + len(f2) > 100) ?
+ \s:ExtDiffExpr(v:fname_in, v:fname_new) :
+ \s:IntDiffExpr(f1, f2), v:fname_out)
+endfunction
+
+function! s:IntDiffExpr(f1, f2) abort
+ let [f1, f2] = [a:f1, a:f2]
+ let do = split(&diffopt, ',')
+ let save_igc = &ignorecase
+ let &ignorecase = (index(do, 'icase') != -1)
+ if index(do, 'iwhiteall') != -1
+ for k in [1, 2]
+ call map(f{k}, 'substitute(v:val, "\\s\\+", "", "g")')
+ endfor
+ elseif index(do, 'iwhite') != -1
+ for k in [1, 2]
+ call map(f{k}, 'substitute(v:val, "\\s\\+", " ", "g")')
+ call map(f{k}, 'substitute(v:val, "\\s\\+$", "", "")')
+ endfor
+ elseif index(do, 'iwhiteeol') != -1
+ for k in [1, 2]
+ call map(f{k}, 'substitute(v:val, "\\s\\+$", "", "")')
+ endfor
+ endif
+ let dfcmd = []
+ let [l1, l2] = [1, 1]
+ for ed in split(s:TraceDiffChar(f1, f2), '[+-]\+\zs', 1)[: -2]
+ let [qe, q1, q2] = [s:CountChar(ed, '='), s:CountChar(ed, '-'),
+ \s:CountChar(ed, '+')]
+ let [l1, l2] += [qe, qe]
+ let dfcmd += [((1 < q1) ? l1 . ',' : '') . (l1 + q1 - 1) .
+ \((q1 == 0) ? 'a' : (q2 == 0) ? 'd' : 'c') .
+ \((1 < q2) ? l2 . ',' : '') . (l2 + q2 - 1)]
+ let [l1, l2] += [q1, q2]
+ endfor
+ let &ignorecase = save_igc
+ return dfcmd
+endfunction
+
+function! s:ExtDiffExpr(f1, f2) abort
+ let do = split(&diffopt, ',')
+ let dc = ['diff', '-a', '--binary',
+ \((index(do, 'icase') != -1) ? '-i' : ''),
+ \((index(do, 'iwhiteall') != -1) ? '-w' :
+ \(index(do, 'iwhite') != -1) ? '-b' :
+ \(index(do, 'iwhiteeol') != -1) ? '-Z' : ''), a:f1, a:f2]
+ let save_stmp = &shelltemp
+ let &shelltemp = 0
+ let dt = systemlist(join(dc))
+ let &shelltemp = save_stmp
+ return filter(dt, 'v:val[0] =~ "\\d"')
+endfunction
+
+if s:VF.DiffOptionSet
+ function! diffchar#ToggleDiffModeSync(event) abort
+ " a:event : 0 = OptionSet diff, 1 = VimEnter
+ if !get(g:, 'DiffModeSync', 1) | return | endif
+ if s:VF.VOptionFixed
+ if a:event || v:option_old != v:option_new
+ call s:SwitchDiffChar(a:event || v:option_new)
+ endif
+ else
+ call s:SwitchDiffChar(a:event || &diff)
+ endif
+ endfunction
+else
+ function! diffchar#SetDiffModeSync() abort
+ " DiffModeSync is triggered ON by FilterWritePost
+ if !get(g:, 'DiffModeSync', 1) | return | endif
+ if !exists('s:dmbuf')
+ " as a diff session, when FilterWritePos comes, current buf and
+ " other 1 or more buf should be diff mode
+ let s:dmbuf = map(filter(gettabinfo(tabpagenr())[0].windows,
+ \'getwinvar(v:val, "&diff")'), 'winbufnr(v:val)')
+ if index(s:dmbuf, bufnr('%')) == -1 ||
+ \min(s:dmbuf) == max(s:dmbuf)
+ " not a diff session, then clear
+ unlet s:dmbuf
+ return
+ endif
+ " wait for the contineous 1 or more FilterWitePost (diff) or
+ " 1 ShellFilterPost (non diff)
+ call execute('autocmd! diffchar ShellFilterPost *
+ \ call s:ClearDiffModeSync()')
+ " prepare to complete sync just in case for accidents
+ let s:id = timer_start(0, function('s:CompleteDiffModeSync'))
+ endif
+ " check if all the FilterWritePost has come
+ if empty(filter(s:dmbuf, 'v:val != bufnr("%")'))
+ call s:CompleteDiffModeSync(0)
+ endif
+ endfunction
+
+ function! s:CompleteDiffModeSync(id) abort
+ if exists('s:id')
+ if a:id == 0 | call timer_stop(s:id) | endif
+ unlet s:id
+ else
+ if exists('s:save_ch') && !empty(s:save_ch)
+ call execute('autocmd! diffchar CursorHold * call ' .
+ \s:save_ch)
+ call s:ChangeUTOpt(1)
+ else
+ call execute('autocmd! diffchar CursorHold *')
+ call s:ChangeUTOpt(0)
+ endif
+ silent call feedkeys("g\", 'n')
+ endif
+ call s:ClearDiffModeSync()
+ call timer_start(0, function('s:SwitchDiffChar'))
+ endfunction
+
+ function! s:ClearDiffModeSync() abort
+ unlet s:dmbuf
+ call execute('autocmd! diffchar ShellFilterPost *')
+ endfunction
+
+ function! s:ResetDiffModeSync() abort
+ " DiffModeSync is triggered OFF by CursorHold
+ if exists('t:DChar') && t:DChar.dsy &&
+ \!empty(filter(values(t:DChar.wid), '!getwinvar(v:val, "&diff")'))
+ " if either or both of DChar win is now non-diff mode,
+ " reset it and show with current diff mode wins
+ call s:SwitchDiffChar(0)
+ endif
+ endfunction
+endif
+
+function! s:SwitchDiffChar(on) abort
+ let cw = win_getid()
+ let aw = cw
+ if exists('t:DChar') &&
+ \(a:on ? index(values(t:DChar.bnr), winbufnr(cw)) == -1 :
+ \index(values(t:DChar.wid), cw) != -1)
+ " diff mode ON on non-DChar buf || OFF on DChar win, try reset
+ for k in [1, 2]
+ if getwinvar(t:DChar.wid[k], '&diff')
+ let aw = t:DChar.wid[k]
+ call s:WinGotoID(aw)
+ call s:WinExecute('call diffchar#ResetDiffChar(1)')
+ call s:WinGotoID(cw)
+ break
+ endif
+ endfor
+ endif
+ if !exists('t:DChar') && get(g:, 'DiffModeSync', 1)
+ let aw = win_id2win(aw)
+ let dw = filter(map(range(aw, winnr('$')) + range(1, aw - 1),
+ \'win_getid(v:val)'), 'getwinvar(v:val, "&diff")')
+ if 1 < len(dw)
+ " 2 or more diff mode wins exists, try show
+ call s:WinGotoID(dw[0])
+ call s:WinExecute('call diffchar#ShowDiffChar()')
+ call s:WinGotoID(cw)
+ endif
+ endif
+endfunction
+
+function! s:WinClosedDiffChar() abort
+ " reset and show (if possible) DChar on WinClosed or BufWinLeave
+ for tn in range(1, tabpagenr('$'))
+ let dc = s:Gettabvar(tn, 'DChar')
+ if !empty(dc)
+ for k in [1, 2]
+ if s:VF.WinClosed ? dc.wid[k] == eval(expand('')) :
+ \dc.bnr[k] == eval(expand(''))
+ let cw = win_getid()
+ call s:WinGotoID(dc.wid[k])
+ call s:WinExecute('call diffchar#ResetDiffChar(1)')
+ if dc.dsy
+ let dw = filter(gettabinfo(tn)[0].windows,
+ \'v:val != dc.wid[k] &&
+ \winbufnr(v:val) == dc.bnr[k] &&
+ \getwinvar(v:val, "&diff")')
+ if !empty(dw)
+ call s:WinGotoID(dw[0])
+ call s:WinExecute('call diffchar#ShowDiffChar()')
+ endif
+ endif
+ call s:WinGotoID(cw)
+ return
+ endif
+ endfor
+ endif
+ endfor
+endfunction
+
+function! s:RepairDiffChar() abort
+ " repair DChar whose win was accidentally closed on BufWinEnter/WinEnter
+ if exists('t:DChar')
+ let dc = t:DChar
+ let dw = filter(copy(dc.wid), 'win_id2win(v:val) != 0 &&
+ \winbufnr(v:val) == dc.bnr[v:key] && getwinvar(v:val, "&diff")')
+ if len(dw) == 1
+ let cw = win_getid()
+ call s:WinGotoID(values(dw)[0])
+ call s:WinExecute('call diffchar#ResetDiffChar(1)')
+ if dc.dsy
+ call s:WinExecute('call diffchar#ShowDiffChar()')
+ endif
+ call s:WinGotoID(cw)
+ endif
+ endif
+endfunction
+
+function! s:ChecksumStr(str) abort
+ return eval('0x' . sha256(a:str)[-4 :])
+endfunction
+
+function! s:EchoWarning(msg) abort
+ call execute(['echohl WarningMsg', 'echo a:msg', 'echohl None'], '')
+endfunction
+
+if s:VF.CountString
+ let s:CountChar = function('count')
+else
+ function! s:CountChar(str, chr) abort
+ return len(a:str) - len(substitute(a:str, a:chr, '', 'g'))
+ endfunction
+endif
+
+if s:VF.GettabvarFixed
+ let s:Gettabvar = function('gettabvar')
+else
+ function! s:Gettabvar(tp, var) abort
+ call gettabvar(a:tp, a:var) " call twice as a workaround
+ return gettabvar(a:tp, a:var)
+ endfunction
+endif
+
+if s:VF.ChangenrFixed
+ let s:Changenr = function('changenr')
+else
+ function! s:Changenr() abort
+ let ute = undotree().entries
+ for n in range(len(ute))
+ if has_key(ute[n], 'curhead')
+ " if curhead exists, undotree().seq_cur should be this but not
+ " then changenr() returns a wrong number
+ return (0 < n) ? ute[n - 1].seq : 0
+ endif
+ endfor
+ return changenr()
+ endfunction
+endif
+
+if s:VF.WinExecute
+ function! s:WinGotoID(wid) abort
+ let s:WinExecute = function('win_execute', [a:wid])
+ endfunction
+else
+ function! s:WinGotoID(wid) abort
+ noautocmd call win_gotoid(a:wid)
+ endfunction
+ let s:WinExecute = function('execute')
+endif
+
+if s:VF.WinIDinMatch
+ let s:Matchaddpos = function('matchaddpos')
+ let s:Matchdelete = function('matchdelete')
+ let s:Getmatches = function('getmatches')
+else
+ function! s:Matchaddpos(grp, pos, pri, ...) abort
+ return matchaddpos(a:grp, a:pos, a:pri)
+ endfunction
+
+ function! s:Matchdelete(id, ...) abort
+ return matchdelete(a:id)
+ endfunction
+
+ function! s:Getmatches(...) abort
+ return getmatches()
+ endfunction
+endif
+
+function! s:AdjustGlobalOption() abort
+ if !s:VF.DiffUpdated && !s:VF.DiffOptionSet
+ call s:ChangeUTOpt(exists('t:DChar') && t:DChar.dsy)
+ endif
+ call s:ToggleDiffHL(exists('t:DChar'))
+endfunction
+
+if !s:VF.DiffUpdated
+ if s:VF.DiffOptionSet
+ function! s:FollowDiffOption() abort
+ if v:option_old != v:option_new
+ let cw = win_getid()
+ for dc in filter(map(range(1, tabpagenr('$')),
+ \'s:Gettabvar(v:val, "DChar")'), '!empty(v:val)')
+ call s:WinGotoID(dc.wid[1])
+ call s:WinExecute('call s:RedrawDiffChar(1, 0)')
+ endfor
+ call s:WinGotoID(cw)
+ endif
+ endfunction
+ else
+ function! s:ChangeUTOpt(on) abort
+ if a:on && !exists('s:save_ut')
+ let s:save_ut = &updatetime
+ let &updatetime = 500
+ elseif !a:on && exists('s:save_ut')
+ let &updatetime = s:save_ut
+ unlet s:save_ut
+ endif
+ endfunction
+ endif
+endif
+
+function! s:ToggleDiffHL(on) abort
+ " dh: 0 = original, 1 = for single color, 2 = for multi color
+ for dh in values(s:DiffHL)
+ call execute(['highlight clear ' . dh.nm, 'highlight ' . dh.nm . ' ' .
+ \join(map(items(dh[!a:on ? 0 : (len(t:DChar.hgp) == 1) ? 1 : 2]),
+ \'join(v:val, "=")'))])
+ endfor
+endfunction
+
+let &cpoptions = s:save_cpo
+unlet s:save_cpo
+
+" vim: ts=4 sw=4
diff --git a/.vim/bundle/vim-diffchar/doc/diffchar.txt b/.vim/bundle/vim-diffchar/doc/diffchar.txt
new file mode 100644
index 0000000..132b67c
--- /dev/null
+++ b/.vim/bundle/vim-diffchar/doc/diffchar.txt
@@ -0,0 +1,216 @@
+*diffchar.txt* Highlight the exact differences, based on characters and words
+>
+ ____ _ ____ ____ _____ _ _ _____ ____
+ | | | || || || || | | || _ || _ |
+ | _ || || __|| __|| || | | || | | || | ||
+ | | | || || |__ | |__ | __|| |_| || |_| || |_||_
+ | |_| || || __|| __|| | | || || __ |
+ | || || | | | | |__ | _ || _ || | | |
+ |____| |_||_| |_| |_____||_| |_||_| |_||_| |_|
+<
+Last Change: 2021/12/07
+Version: 8.91
+Author: Rick Howe (Takumi Ohtani)
+Copyright: (c) 2014-2021 by Rick Howe
+
+-----------------------------------------------------------------------------
+INTRODUCTION *diffchar*
+
+This plugin has been developed in order to make diff mode more useful. Vim
+highlights all the text in between the first and last different characters on
+a changed line. But this plugin will find the exact differences between them,
+character by character - so called DiffChar.
+
+For example, in diff mode: ([|hl-DiffText|], <|hl-DiffAdd|>)
+>
+ (window A) The [quick brown fox jumps over the lazy] dog.
+ (window B) The [lazy fox jumps over the quick brown] dog.
+<
+this plugin will exactly show the changed and added units:
+>
+ (window A) The [quick] fox jumps over the [lazy] dog.
+ (window B) The [lazy] fox jumps over the [quick] dog.
+<
+This plugin will synchronously show/reset the highlights of the exact
+differences as soon as the diff mode begins/ends. And the exact differences
+will be kept updated while editing.
+
+This plugin shows the differences based on a |g:DiffUnit|. Its default is
+'Word1' and it handles a \w\+ word and a \W character as a difference unit.
+There are other types of word provided and you can also set 'Char' to compare
+character by character.
+
+In diff mode, the corresponding |hl-DiffChange| lines are compared between two
+windows. You can set a number of matching colors to a |g:DiffColors| to make
+it easy to find the corresponding units between two windows. As a default, all
+the changed units are highlighted with |hl-DiffText|. In addition,
+|hl-DiffAdd| is always used for the added units and both the previous and next
+character of the deleted units are shown in bold/underline.
+
+While showing the exact differences, when the cursor is moved on a difference
+unit, you can see its corresponding unit highlighted with |hl-Cursor|,
+|hl-TermCursor|, or similar one in another window, based on a
+|g:DiffPairVisible|. If you change its default, the corresponding unit is
+echoed in the command line or displayed in a popup/floating window just below
+the cursor position or at the mouse position.
+
+You can use `]b` or `]e` to jump cursor to start or end position of the next
+difference unit, and `[b` or `[e` to the start or end position of the previous
+unit. Those keymaps are configurable in your vimrc and so on.
+
+Like line-based `:diffget`/`:diffput` and `do`/`dp` vim commands, you can use
+`g` and `p` commands in normal mode to get and put each
+difference unit, where the cursor is on, between 2 buffers and undo its
+difference.
+
+When the diff mode begins, this plugin locally checks the |hl-DiffChange|
+lines in the limited range of the current visible and its upper/lower lines of
+a window. And each time a cursor is moved on to a different range upon
+scrolling or searching, the new |hl-DiffChange| lines will be incrementally
+checked in that range. Which means, independently of the file size, the number
+of lines to be checked and then the time consumed are always constant.
+
+This plugin works on each tab page individually. You can use a tab page
+variable (t:), instead of a global one (g:), to specify different options on
+each tab page. Note that this plugin can not handle more than two diff mode
+windows in a tab page. If it would happen, to prevent any trouble, all the
+highlighted units are to be reset in the tab page.
+
+To find the exact differences, this plugin uses "An O(NP) Sequence Comparison
+Algorithm" developed by S.Wu, et al., which always finds an optimum sequence.
+But it takes time to check a long and dissimilar line. To improve the
+performance, if there are so many diff units included in a line or it has
+taken much time in a diff session, this plugin tries to use the external diff
+command together if available.
+
+-----------------------------------------------------------------------------
+OPTIONS *diffchar-options*
+
+|g:DiffUnit|, |t:DiffUnit|
+ A type of difference unit
+ 'Char' : any single character
+ 'Word1' : \w\+ word and any \W single character (default)
+ 'Word2' : non-space and space words
+ 'Word3' : \< or \> character class boundaries
+ 'CSV(,)' : separated by characters such as ',', ';', and '\t'
+
+|g:DiffColors|, |t:DiffColors|
+ Matching colors for changed units
+ 0 : |hl-DiffText| (default)
+ 1 : |hl-DiffText| + up to 3 other highlights
+ 2 : |hl-DiffText| + up to 7 other highlights
+ 3 : |hl-DiffText| + up to 15 other highlights
+
+|g:DiffPairVisible|, |t:DiffPairVisible|
+ Visibility of corresponding diff units
+ 0 : disable
+ 1 : highlight with |hl-Cursor| (default)
+ 2 : highlight with |hl-Cursor| + echo in the command line
+ 3 : highlight with |hl-Cursor| + popup/floating window at cursor position
+ 4 : highlight with |hl-Cursor| + popup/floating window at mouse position
+
+-----------------------------------------------------------------------------
+KEYMAPS *diffchar-keymaps*
+
+JumpDiffCharPrevStart (default: `[b`)
+ Jump cursor to the start position of the previous difference unit
+
+JumpDiffCharNextStart (default: `]b`)
+ Jump cursor to the start position of the next difference unit
+
+JumpDiffCharPrevEnd (default: `[e`)
+ Jump cursor to the end position of the previous difference unit
+
+JumpDiffCharNextEnd (default: `]e`)
+ Jump cursor to the end position of the next difference unit
+
+GetDiffCharPair (default: `g`)
+ Get a corresponding difference unit from another buffer to undo difference
+
+PutDiffCharPair (default: `p`)
+ Put a corresponding difference unit to another buffer to undo difference
+
+-----------------------------------------------------------------------------
+CHANGE HISTORY *diffchar-history*
+
+Update : 8.91
+* Updated to check a new WinClosed event (patch-8.2.3591) to appropriately
+ reset or repair the highlighted DiffChar units when a window is closed.
+
+Update : 8.9
+* Fixed not to overrule syntax highlighting.
+* Fixed to successfully sync with diff mode even without patch-8.1.414.
+* Changed the highlighting groups used in |g:DiffColors| option.
+
+Update : 8.8
+* Changed the highlighting groups used in |g:DiffColors| option, to completely
+ highlight a changed diff unit and to make each unit more visible.
+* Changed to use |hl-Cursor|, |hl-TermCursor|, or similar one, as appropriate,
+ to highlight a corresponding diff unit, for |g:DiffPairVisible| option.
+* Fixed to use proper |hl-Diff| highlighting groups even if they are linked.
+* Optimized how to draw each unit and then improved performance.
+
+Update : 8.7
+* Enhanced |g:DiffPairVisible| option to show a corresponding diff unit as
+ well in a floating window on nvim, if its value is 3, and show a popup (not
+ a balloon) window at the mouse position, if its value is 4.
+* Improved performance, use the external diff command together if available,
+ if there are so many diff units included in a line or it has taken much time
+ in a diff session.
+* Removed |g:DiffMaxLines| option, and locally checks the limited number of
+ the |hl-DiffChange| lines and incrementally checks them upon scrolling or
+ searching.
+* Removed |g:DiffModeSync| option, and always synchronize with the diff mode.
+* `:SDChar`, `:RDChar`, `:TDChar`, and `:EDChar` commands are still available
+ but deprecated.
+
+Update : 8.6
+* Enhanced |g:DiffPairVisible| option to show a corresponding diff unit as a
+ popup-window just below the cursor position (available on patch-8.1.1391).
+ And changed its default as 1 (diff unit highlighting only).
+* Fixed not to stop monitoring the changes of text and 'diffopt' option,
+ even if there is no |hl-DiffChange| line, when |g:DiffModeSync| option is
+ enabled.
+
+Update : 8.5
+* Enhanced to show a balloon on GUI and display a corresponding diff unit,
+ where the mouse is pointing, if |g:DiffPairVisible| option is enabled
+ (patch-8.1.647 is required to correctly display multibyte characters).
+* Fixed to correctly handle |hl-DiffChange| lines while editing.
+
+Update : 8.4
+* Extended |g:DiffMaxLines| option to allow a negative value as multiples of
+ the window height and changed its default as -3.
+* Fixed to reset all highlighted DiffChar units when more than two windows
+ become diff mode in a tab page.
+* Deleted |g:DiffSplitTime| option.
+
+Update : 8.3
+* Fixed not to detect more |hl-DiffChange| lines than |g:DiffMaxLines| option.
+
+Update : 8.2
+* Fixed to correctly update the highlighted DiffChar units while editing when
+ a new internal diff is not specified in 'diffopt' option (patch-8.1.360).
+
+Update : 8.1
+* Fixed to properly detect |hl-DiffChange| lines even when all visible lines
+ of current window are in a closed fold if a |g:DiffMaxLines| option is
+ enabled.
+
+Update : 8.0
+* Introduced a |g:DiffMaxLines| option to dynamically detect a limited number
+ of |hl-DiffChange| lines, when the diff mode begins and whenever a cursor is
+ moved onto an undetected line. It enables to always take a minimum constant
+ time, independently of the file size.
+* Enhanced to check a new DiffUpdated event (patch-8.1.397) to follow diff
+ updates and some changes of 'diffopt' option.
+* Enhanced to support new iwhiteall and iwhiteeol of 'diffopt' option
+ (patch-8.1.360).
+* Removed |g:DiffUpdate| option and merged it into |g:DiffModeSync|.
+* Removed keymap for and , which toggle to show/reset the highlights.
+* Changed to work in diff mode, not in non-diff mode.
+* Removed a support for vim version 7.x.
+* Changed not to set 'diffexpr' option when a new internal diff is specified
+ in 'diffopt' (patch-8.1.360).
+
+ vim:tw=78:ts=8:ft=help:norl:
diff --git a/.vim/bundle/vim-diffchar/doc/tags b/.vim/bundle/vim-diffchar/doc/tags
new file mode 100644
index 0000000..f61b1ae
--- /dev/null
+++ b/.vim/bundle/vim-diffchar/doc/tags
@@ -0,0 +1,5 @@
+diffchar diffchar.txt /*diffchar*
+diffchar-history diffchar.txt /*diffchar-history*
+diffchar-keymaps diffchar.txt /*diffchar-keymaps*
+diffchar-options diffchar.txt /*diffchar-options*
+diffchar.txt diffchar.txt /*diffchar.txt*
diff --git a/.vim/bundle/vim-diffchar/plugin/diffchar.vim b/.vim/bundle/vim-diffchar/plugin/diffchar.vim
new file mode 100644
index 0000000..9cb8f74
--- /dev/null
+++ b/.vim/bundle/vim-diffchar/plugin/diffchar.vim
@@ -0,0 +1,118 @@
+" diffchar.vim: Highlight the exact differences, based on characters and words
+"
+" ____ _ ____ ____ _____ _ _ _____ ____
+" | | | || || || || | | || _ || _ |
+" | _ || || __|| __|| || | | || | | || | ||
+" | | | || || |__ | |__ | __|| |_| || |_| || |_||_
+" | |_| || || __|| __|| | | || || __ |
+" | || || | | | | |__ | _ || _ || | | |
+" |____| |_||_| |_| |_____||_| |_||_| |_||_| |_|
+"
+" Last Change: 2021/12/07
+" Version: 8.91
+" Author: Rick Howe (Takumi Ohtani)
+" Copyright: (c) 2014-2021 by Rick Howe
+
+if exists('g:loaded_diffchar') || !has('diff') || v:version < 800
+ finish
+endif
+let g:loaded_diffchar = 8.91
+
+let s:save_cpo = &cpoptions
+set cpo&vim
+
+" Commands
+command! -range -bar SDChar
+ \ call diffchar#ShowDiffChar(range(, ))
+command! -range -bar RDChar
+ \ call diffchar#ResetDiffChar(range(, ))
+command! -range -bar TDChar
+ \ call diffchar#ToggleDiffChar(range(, ))
+command! -range -bang -bar EDChar
+ \ call diffchar#EchoDiffChar(range(, ), 1)
+
+" Configurable Keymaps
+for [key, plg, cmd] in [
+ \['[b', 'JumpDiffCharPrevStart',
+ \':call diffchar#JumpDiffChar(0, 0)'],
+ \[']b', 'JumpDiffCharNextStart',
+ \':call diffchar#JumpDiffChar(1, 0)'],
+ \['[e', 'JumpDiffCharPrevEnd',
+ \':call diffchar#JumpDiffChar(0, 1)'],
+ \[']e', 'JumpDiffCharNextEnd',
+ \':call diffchar#JumpDiffChar(1, 1)'],
+ \['g', 'GetDiffCharPair',
+ \':call diffchar#CopyDiffCharPair(0)'],
+ \['p', 'PutDiffCharPair',
+ \':call diffchar#CopyDiffCharPair(1)']]
+ if !hasmapto(plg, 'n') && empty(maparg(key, 'n'))
+ if get(g:, 'DiffCharDoMapping', 1)
+ execute 'nmap ' . key . ' ' . plg
+ endif
+ endif
+ execute 'nnoremap ' plg . ' ' . cmd . ''
+endfor
+
+" a type of difference unit
+if !exists('g:DiffUnit')
+ let g:DiffUnit = 'Word1' " \w\+ word and any \W single character
+ " let g:DiffUnit = 'Word2' " non-space and space words
+ " let g:DiffUnit = 'Word3' " \< or \> character class boundaries
+ " let g:DiffUnit = 'Char' " any single character
+ " let g:DiffUnit = 'CSV(,)' " split characters
+endif
+
+" matching colors for changed units
+if !exists('g:DiffColors')
+ let g:DiffColors = 0 " always 1 color
+ " let g:DiffColors = 1 " up to 4 colors in fixed order
+ " let g:DiffColors = 2 " up to 8 colors in fixed order
+ " let g:DiffColors = 3 " up to 16 colors in fixed order
+ " let g:DiffColors = 4 " all available colors in fixed order
+ " let g:DiffColors = 100 " all colors in dynamic random order
+endif
+
+" a visibility of corresponding diff units
+if !exists('g:DiffPairVisible')
+ let g:DiffPairVisible = 1 " highlight
+ " let g:DiffPairVisible = 2 " highlight + echo
+ " let g:DiffPairVisible = 3 " highlight + popup/floating at cursor pos
+ " let g:DiffPairVisible = 4 " highlight + popup/floating at mouse pos
+ " let g:DiffPairVisible = 0 " disable
+endif
+
+" Set this plugin's DiffCharExpr() to the diffexpr option if empty
+" and when internal diff is not used
+if !exists('g:DiffExpr')
+ let g:DiffExpr = 1 " enable
+ " let g:DiffExpr = 0 " disable
+endif
+if g:DiffExpr && empty(&diffexpr) && &diffopt !~ 'internal'
+ let &diffexpr = 'diffchar#DiffCharExpr()'
+endif
+
+" an event group of this plugin
+if has('patch-8.0.736') " OptionSet triggered with diff option
+ let g:DiffCharInitEvent = ['augroup diffchar', 'autocmd!',
+ \'autocmd OptionSet diff call diffchar#ToggleDiffModeSync(0)',
+ \'augroup END']
+ call execute(g:DiffCharInitEvent)
+ if has('patch-8.1.1113') || has('nvim-0.4.0')
+ call execute('autocmd diffchar VimEnter * ++once
+ \ if &diff | call diffchar#ToggleDiffModeSync(1) | endif')
+ else
+ call execute('autocmd diffchar VimEnter *
+ \ if &diff | call diffchar#ToggleDiffModeSync(1) | endif |
+ \ autocmd! diffchar VimEnter')
+ endif
+else
+ let g:DiffCharInitEvent = ['augroup diffchar', 'autocmd!',
+ \'autocmd FilterWritePost * call diffchar#SetDiffModeSync()',
+ \'augroup END']
+ call execute(g:DiffCharInitEvent)
+endif
+
+let &cpoptions = s:save_cpo
+unlet s:save_cpo
+
+" vim: ts=4 sw=4
diff --git a/.vim/bundle/vim-easy-align/autoload/easy_align.vim b/.vim/bundle/vim-easy-align/autoload/easy_align.vim
new file mode 100644
index 0000000..795ea31
--- /dev/null
+++ b/.vim/bundle/vim-easy-align/autoload/easy_align.vim
@@ -0,0 +1,1148 @@
+" Copyright (c) 2014 Junegunn Choi
+"
+" MIT License
+"
+" Permission is hereby granted, free of charge, to any person obtaining
+" a copy of this software and associated documentation files (the
+" "Software"), to deal in the Software without restriction, including
+" without limitation the rights to use, copy, modify, merge, publish,
+" distribute, sublicense, and/or sell copies of the Software, and to
+" permit persons to whom the Software is furnished to do so, subject to
+" the following conditions:
+"
+" The above copyright notice and this permission notice shall be
+" included in all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if exists("g:loaded_easy_align")
+ finish
+endif
+let g:loaded_easy_align = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+let s:easy_align_delimiters_default = {
+\ ' ': { 'pattern': ' ', 'left_margin': 0, 'right_margin': 0, 'stick_to_left': 0 },
+\ '=': { 'pattern': '===\|<=>\|\(&&\|||\|<<\|>>\)=\|=\~[#?]\?\|=>\|[:+/*!%^=><&|.?-]\?=[#?]\?',
+\ 'left_margin': 1, 'right_margin': 1, 'stick_to_left': 0 },
+\ ':': { 'pattern': ':', 'left_margin': 0, 'right_margin': 1, 'stick_to_left': 1 },
+\ ',': { 'pattern': ',', 'left_margin': 0, 'right_margin': 1, 'stick_to_left': 1 },
+\ '|': { 'pattern': '|', 'left_margin': 1, 'right_margin': 1, 'stick_to_left': 0 },
+\ '.': { 'pattern': '\.', 'left_margin': 0, 'right_margin': 0, 'stick_to_left': 0 },
+\ '#': { 'pattern': '#\+', 'delimiter_align': 'l', 'ignore_groups': ['!Comment'] },
+\ '"': { 'pattern': '"\+', 'delimiter_align': 'l', 'ignore_groups': ['!Comment'] },
+\ '&': { 'pattern': '\\\@ winlen ? '..' : ''
+
+ echon "\r"
+ let yet = 0
+ for [hl, msg] in a:tokens
+ if empty(msg) | continue | endif
+ execute "echohl ". hl
+ let yet += len(msg)
+ if yet > winlen - len(ellipsis)
+ echon msg[ 0 : (winlen - len(ellipsis) - yet - 1) ] . ellipsis
+ break
+ else
+ echon msg
+ endif
+ endfor
+ finally
+ echohl None
+ let [&ruler, &showcmd] = xy
+ endtry
+endfunction
+
+function! s:echon(l, n, r, d, o, warn)
+ let tokens = [
+ \ ['Function', s:live ? ':LiveEasyAlign' : ':EasyAlign'],
+ \ ['ModeMsg', get(s:mode_labels, a:l, a:l)],
+ \ ['None', ' ']]
+
+ if a:r == -1 | call add(tokens, ['Comment', '(']) | endif
+ call add(tokens, [a:n =~ '*' ? 'Repeat' : 'Number', a:n])
+ call extend(tokens, a:r == 1 ?
+ \ [['Delimiter', '/'], ['String', a:d], ['Delimiter', '/']] :
+ \ [['Identifier', a:d == ' ' ? '\ ' : (a:d == '\' ? '\\' : a:d)]])
+ if a:r == -1 | call extend(tokens, [['Normal', '_'], ['Comment', ')']]) | endif
+ call add(tokens, ['Statement', empty(a:o) ? '' : ' '.string(a:o)])
+ if !empty(a:warn)
+ call add(tokens, ['WarningMsg', ' ('.a:warn.')'])
+ endif
+
+ call s:echon_(tokens)
+ return join(map(tokens, 'v:val[1]'), '')
+endfunction
+
+function! s:exit(msg)
+ call s:echon_([['ErrorMsg', a:msg]])
+ throw 'exit'
+endfunction
+
+function! s:ltrim(str)
+ return substitute(a:str, '^\s\+', '', '')
+endfunction
+
+function! s:rtrim(str)
+ return substitute(a:str, '\s\+$', '', '')
+endfunction
+
+function! s:trim(str)
+ return substitute(a:str, '^\s*\(.\{-}\)\s*$', '\1', '')
+endfunction
+
+function! s:fuzzy_lu(key)
+ if has_key(s:known_options, a:key)
+ return a:key
+ endif
+ let key = tolower(a:key)
+
+ " stl -> ^s.*_t.*_l.*
+ let regexp1 = '^' .key[0]. '.*' .substitute(key[1 : -1], '\(.\)', '_\1.*', 'g')
+ let matches = filter(keys(s:known_options), 'v:val =~ regexp1')
+ if len(matches) == 1
+ return matches[0]
+ endif
+
+ " stl -> ^s.*t.*l.*
+ let regexp2 = '^' . substitute(substitute(key, '-', '_', 'g'), '\(.\)', '\1.*', 'g')
+ let matches = filter(keys(s:known_options), 'v:val =~ regexp2')
+
+ if empty(matches)
+ call s:exit("Unknown option key: ". a:key)
+ elseif len(matches) == 1
+ return matches[0]
+ else
+ " Avoid ambiguity introduced by deprecated margin_left and margin_right
+ if sort(matches) == ['margin_left', 'margin_right', 'mode_sequence']
+ return 'mode_sequence'
+ endif
+ if sort(matches) == ['ignore_groups', 'ignores']
+ return 'ignore_groups'
+ endif
+ call s:exit("Ambiguous option key: ". a:key ." (" .join(matches, ', '). ")")
+ endif
+endfunction
+
+function! s:shift(modes, cycle)
+ let item = remove(a:modes, 0)
+ if a:cycle || empty(a:modes)
+ call add(a:modes, item)
+ endif
+ return item
+endfunction
+
+function! s:normalize_options(opts)
+ let ret = {}
+ for k in keys(a:opts)
+ let v = a:opts[k]
+ let k = s:fuzzy_lu(k)
+ " Backward-compatibility
+ if k == 'margin_left' | let k = 'left_margin' | endif
+ if k == 'margin_right' | let k = 'right_margin' | endif
+ if k == 'mode_sequence' | let k = 'align' | endif
+ let ret[k] = v
+ unlet v
+ endfor
+ return s:validate_options(ret)
+endfunction
+
+function! s:compact_options(opts)
+ let ret = {}
+ for k in keys(a:opts)
+ let ret[s:shorthand[k]] = a:opts[k]
+ endfor
+ return ret
+endfunction
+
+function! s:validate_options(opts)
+ for k in keys(a:opts)
+ let v = a:opts[k]
+ if index(s:known_options[k], type(v)) == -1
+ call s:exit("Invalid type for option: ". k)
+ endif
+ unlet v
+ endfor
+ return a:opts
+endfunction
+
+function! s:split_line(line, nth, modes, cycle, fc, lc, pattern, stick_to_left, ignore_unmatched, ignore_groups)
+ let mode = ''
+
+ let string = a:lc ?
+ \ strpart(getline(a:line), a:fc - 1, a:lc - a:fc + 1) :
+ \ strpart(getline(a:line), a:fc - 1)
+ let idx = 0
+ let nomagic = match(a:pattern, '\\v') > match(a:pattern, '\C\\[mMV]')
+ let pattern = '^.\{-}\s*\zs\('.a:pattern.(nomagic ? ')' : '\)')
+ let tokens = []
+ let delims = []
+
+ " Phase 1: split
+ let ignorable = 0
+ let token = ''
+ let phantom = 0
+ while 1
+ let matchidx = match(string, pattern, idx)
+ " No match
+ if matchidx < 0 | break | endif
+ let matchend = matchend(string, pattern, idx)
+ let spaces = matchstr(string, '\s'.(a:stick_to_left ? '*' : '\{-}'), matchend + (matchidx == matchend))
+
+ " Match, but empty
+ if len(spaces) + matchend - idx == 0
+ let char = strpart(string, idx, 1)
+ if empty(char) | break | endif
+ let [match, part, delim] = [char, char, '']
+ " Match
+ else
+ let match = strpart(string, idx, matchend - idx + len(spaces))
+ let part = strpart(string, idx, matchidx - idx)
+ let delim = strpart(string, matchidx, matchend - matchidx)
+ endif
+
+ let ignorable = s:highlighted_as(a:line, idx + len(part) + a:fc, a:ignore_groups)
+ if ignorable
+ let token .= match
+ else
+ let [pmode, mode] = [mode, s:shift(a:modes, a:cycle)]
+ call add(tokens, token . match)
+ call add(delims, delim)
+ let token = ''
+ endif
+
+ let idx += len(match)
+
+ " If the string is non-empty and ends with the delimiter,
+ " append an empty token to the list
+ if idx == len(string)
+ let phantom = 1
+ break
+ endif
+ endwhile
+
+ let leftover = token . strpart(string, idx)
+ if !empty(leftover)
+ let ignorable = s:highlighted_as(a:line, len(string) + a:fc - 1, a:ignore_groups)
+ call add(tokens, leftover)
+ call add(delims, '')
+ elseif phantom
+ call add(tokens, '')
+ call add(delims, '')
+ endif
+ let [pmode, mode] = [mode, s:shift(a:modes, a:cycle)]
+
+ " Preserve indentation - merge first two tokens
+ if len(tokens) > 1 && empty(s:rtrim(tokens[0]))
+ let tokens[1] = tokens[0] . tokens[1]
+ call remove(tokens, 0)
+ call remove(delims, 0)
+ let mode = pmode
+ endif
+
+ " Skip comment line
+ if ignorable && len(tokens) == 1 && a:ignore_unmatched
+ let tokens = []
+ let delims = []
+ " Append an empty item to enable right/center alignment of the last token
+ " - if the last token is not ignorable or ignorable but not the only token
+ elseif a:ignore_unmatched != 1 &&
+ \ (mode ==? 'r' || mode ==? 'c') &&
+ \ (!ignorable || len(tokens) > 1) &&
+ \ a:nth >= 0 " includes -0
+ call add(tokens, '')
+ call add(delims, '')
+ endif
+
+ return [tokens, delims]
+endfunction
+
+function! s:do_align(todo, modes, all_tokens, all_delims, fl, ll, fc, lc, nth, recur, dict)
+ let mode = a:modes[0]
+ let lines = {}
+ let min_indent = -1
+ let max = { 'pivot_len2': 0, 'token_len': 0, 'just_len': 0, 'delim_len': 0,
+ \ 'indent': 0, 'tokens': 0, 'strip_len': 0 }
+ let d = a:dict
+ let [f, fx] = s:parse_filter(d.filter)
+
+ " Phase 1
+ for line in range(a:fl, a:ll)
+ let snip = a:lc > 0 ? getline(line)[a:fc-1 : a:lc-1] : getline(line)
+ if f == 1 && snip !~ fx
+ continue
+ elseif f == -1 && snip =~ fx
+ continue
+ endif
+
+ if !has_key(a:all_tokens, line)
+ " Split line into the tokens by the delimiters
+ let [tokens, delims] = s:split_line(
+ \ line, a:nth, copy(a:modes), a:recur == 2,
+ \ a:fc, a:lc, d.pattern,
+ \ d.stick_to_left, d.ignore_unmatched, d.ignore_groups)
+
+ " Remember tokens for subsequent recursive calls
+ let a:all_tokens[line] = tokens
+ let a:all_delims[line] = delims
+ else
+ let tokens = a:all_tokens[line]
+ let delims = a:all_delims[line]
+ endif
+
+ " Skip empty lines
+ if empty(tokens)
+ continue
+ endif
+
+ " Calculate the maximum number of tokens for a line within the range
+ let max.tokens = max([max.tokens, len(tokens)])
+
+ if a:nth > 0 " Positive N-th
+ if len(tokens) < a:nth
+ continue
+ endif
+ let nth = a:nth - 1 " make it 0-based
+ else " -0 or Negative N-th
+ if a:nth == 0 && mode !=? 'l'
+ let nth = len(tokens) - 1
+ else
+ let nth = len(tokens) + a:nth
+ endif
+ if empty(delims[len(delims) - 1])
+ let nth -= 1
+ endif
+
+ if nth < 0 || nth == len(tokens)
+ continue
+ endif
+ endif
+
+ let prefix = nth > 0 ? join(tokens[0 : nth - 1], '') : ''
+ let delim = delims[nth]
+ let token = s:rtrim( tokens[nth] )
+ let token = s:rtrim( strpart(token, 0, len(token) - len(s:rtrim(delim))) )
+ if empty(delim) && !exists('tokens[nth + 1]') && d.ignore_unmatched
+ continue
+ endif
+
+ let indent = s:strwidth(matchstr(tokens[0], '^\s*'))
+ if min_indent < 0 || indent < min_indent
+ let min_indent = indent
+ endif
+ if mode ==? 'c'
+ let token .= substitute(matchstr(token, '^\s*'), '\t', repeat(' ', &tabstop), 'g')
+ endif
+ let [pw, tw] = [s:strwidth(prefix), s:strwidth(token)]
+ let max.indent = max([max.indent, indent])
+ let max.token_len = max([max.token_len, tw])
+ let max.just_len = max([max.just_len, pw + tw])
+ let max.delim_len = max([max.delim_len, s:strwidth(delim)])
+
+ if mode ==? 'c'
+ let pivot_len2 = pw * 2 + tw
+ if max.pivot_len2 < pivot_len2
+ let max.pivot_len2 = pivot_len2
+ endif
+ let max.strip_len = max([max.strip_len, s:strwidth(s:trim(token))])
+ endif
+ let lines[line] = [nth, prefix, token, delim]
+ endfor
+
+ " Phase 1-5: indentation handling (only on a:nth == 1)
+ if a:nth == 1
+ let idt = d.indentation
+ if idt ==? 'd'
+ let indent = max.indent
+ elseif idt ==? 's'
+ let indent = min_indent
+ elseif idt ==? 'n'
+ let indent = 0
+ elseif idt !=? 'k'
+ call s:exit('Invalid indentation: ' . idt)
+ end
+
+ if idt !=? 'k'
+ let max.just_len = 0
+ let max.token_len = 0
+ let max.pivot_len2 = 0
+
+ for [line, elems] in items(lines)
+ let [nth, prefix, token, delim] = elems
+
+ let tindent = matchstr(token, '^\s*')
+ while 1
+ let len = s:strwidth(tindent)
+ if len < indent
+ let tindent .= repeat(' ', indent - len)
+ break
+ elseif len > indent
+ let tindent = tindent[0 : -2]
+ else
+ break
+ endif
+ endwhile
+
+ let token = tindent . s:ltrim(token)
+ if mode ==? 'c'
+ let token = substitute(token, '\s*$', repeat(' ', indent), '')
+ endif
+ let [pw, tw] = [s:strwidth(prefix), s:strwidth(token)]
+ let max.token_len = max([max.token_len, tw])
+ let max.just_len = max([max.just_len, pw + tw])
+ if mode ==? 'c'
+ let pivot_len2 = pw * 2 + tw
+ if max.pivot_len2 < pivot_len2
+ let max.pivot_len2 = pivot_len2
+ endif
+ endif
+
+ let lines[line][2] = token
+ endfor
+ endif
+ endif
+
+ " Phase 2
+ for [line, elems] in items(lines)
+ let tokens = a:all_tokens[line]
+ let delims = a:all_delims[line]
+ let [nth, prefix, token, delim] = elems
+
+ " Remove the leading whitespaces of the next token
+ if len(tokens) > nth + 1
+ let tokens[nth + 1] = s:ltrim(tokens[nth + 1])
+ endif
+
+ " Pad the token with spaces
+ let [pw, tw] = [s:strwidth(prefix), s:strwidth(token)]
+ let rpad = ''
+ if mode ==? 'l'
+ let pad = repeat(' ', max.just_len - pw - tw)
+ if d.stick_to_left
+ let rpad = pad
+ else
+ let token = token . pad
+ endif
+ elseif mode ==? 'r'
+ let pad = repeat(' ', max.just_len - pw - tw)
+ let indent = matchstr(token, '^\s*')
+ let token = indent . pad . s:ltrim(token)
+ elseif mode ==? 'c'
+ let p1 = max.pivot_len2 - (pw * 2 + tw)
+ let p2 = max.token_len - tw
+ let pf1 = s:floor2(p1)
+ if pf1 < p1 | let p2 = s:ceil2(p2)
+ else | let p2 = s:floor2(p2)
+ endif
+ let strip = s:ceil2(max.token_len - max.strip_len) / 2
+ let indent = matchstr(token, '^\s*')
+ let token = indent. repeat(' ', pf1 / 2) .s:ltrim(token). repeat(' ', p2 / 2)
+ let token = substitute(token, repeat(' ', strip) . '$', '', '')
+
+ if d.stick_to_left
+ if empty(s:rtrim(token))
+ let center = len(token) / 2
+ let [token, rpad] = [strpart(token, 0, center), strpart(token, center)]
+ else
+ let [token, rpad] = [s:rtrim(token), matchstr(token, '\s*$')]
+ endif
+ endif
+ endif
+ let tokens[nth] = token
+
+ " Pad the delimiter
+ let dpadl = max.delim_len - s:strwidth(delim)
+ let da = d.delimiter_align
+ if da ==? 'l'
+ let [dl, dr] = ['', repeat(' ', dpadl)]
+ elseif da ==? 'c'
+ let dl = repeat(' ', dpadl / 2)
+ let dr = repeat(' ', dpadl - dpadl / 2)
+ elseif da ==? 'r'
+ let [dl, dr] = [repeat(' ', dpadl), '']
+ else
+ call s:exit('Invalid delimiter_align: ' . da)
+ endif
+
+ " Before and after the range (for blockwise visual mode)
+ let cline = getline(line)
+ let before = strpart(cline, 0, a:fc - 1)
+ let after = a:lc ? strpart(cline, a:lc) : ''
+
+ " Determine the left and right margin around the delimiter
+ let rest = join(tokens[nth + 1 : -1], '')
+ let nomore = empty(rest.after)
+ let ml = (empty(prefix . token) || empty(delim) && nomore) ? '' : d.ml
+ let mr = nomore ? '' : d.mr
+
+ " Adjust indentation of the lines starting with a delimiter
+ let lpad = ''
+ if nth == 0
+ let ipad = repeat(' ', min_indent - s:strwidth(token.ml))
+ if mode ==? 'l'
+ let token = ipad . token
+ else
+ let lpad = ipad
+ endif
+ endif
+
+ " Align the token
+ let aligned = join([lpad, token, ml, dl, delim, dr, mr, rpad], '')
+ let tokens[nth] = aligned
+
+ " Update the line
+ let a:todo[line] = before.join(tokens, '').after
+ endfor
+
+ if a:nth < max.tokens && (a:recur || len(a:modes) > 1)
+ call s:shift(a:modes, a:recur == 2)
+ return [a:todo, a:modes, a:all_tokens, a:all_delims,
+ \ a:fl, a:ll, a:fc, a:lc, a:nth + 1, a:recur, a:dict]
+ endif
+ return [a:todo]
+endfunction
+
+function! s:input(str, default, vis)
+ if a:vis
+ normal! gv
+ redraw
+ execute "normal! \"
+ else
+ " EasyAlign command can be called without visual selection
+ redraw
+ endif
+ let got = input(a:str, a:default)
+ return got
+endfunction
+
+function! s:atoi(str)
+ return (a:str =~ '^[0-9]\+$') ? str2nr(a:str) : a:str
+endfunction
+
+function! s:shift_opts(opts, key, vals)
+ let val = s:shift(a:vals, 1)
+ if type(val) == 0 && val == -1
+ call remove(a:opts, a:key)
+ else
+ let a:opts[a:key] = val
+ endif
+endfunction
+
+function! s:interactive(range, modes, n, d, opts, rules, vis, bvis)
+ let mode = s:shift(a:modes, 1)
+ let n = a:n
+ let d = a:d
+ let ch = ''
+ let opts = s:compact_options(a:opts)
+ let vals = deepcopy(s:option_values)
+ let regx = 0
+ let warn = ''
+ let undo = 0
+
+ while 1
+ " Live preview
+ let rdrw = 0
+ if undo
+ silent! undo
+ let undo = 0
+ let rdrw = 1
+ endif
+ if s:live && !empty(d)
+ let output = s:process(a:range, mode, n, d, s:normalize_options(opts), regx, a:rules, a:bvis)
+ let &undolevels = &undolevels " Break undo block
+ call s:update_lines(output.todo)
+ let undo = !empty(output.todo)
+ let rdrw = 1
+ endif
+ if rdrw
+ if a:vis
+ normal! gv
+ endif
+ redraw
+ if a:vis | execute "normal! \" | endif
+ endif
+ call s:echon(mode, n, -1, regx ? '/'.d.'/' : d, opts, warn)
+
+ let check = 0
+ let warn = ''
+
+ try
+ let c = getchar()
+ catch /^Vim:Interrupt$/
+ let c = 27
+ endtry
+ let ch = nr2char(c)
+ if c == 3 || c == 27 " CTRL-C / ESC
+ if undo
+ silent! undo
+ endif
+ throw 'exit'
+ elseif c == "\"
+ if !empty(d)
+ let d = ''
+ let regx = 0
+ elseif len(n) > 0
+ let n = strpart(n, 0, len(n) - 1)
+ endif
+ elseif c == 13 " Enter key
+ let mode = s:shift(a:modes, 1)
+ if has_key(opts, 'a')
+ let opts.a = mode . strpart(opts.a, 1)
+ endif
+ elseif ch == '-'
+ if empty(n) | let n = '-'
+ elseif n == '-' | let n = ''
+ else | let check = 1
+ endif
+ elseif ch == '*'
+ if empty(n) | let n = '*'
+ elseif n == '*' | let n = '**'
+ elseif n == '**' | let n = ''
+ else | let check = 1
+ endif
+ elseif empty(d) && ((c == 48 && len(n) > 0) || c > 48 && c <= 57) " Numbers
+ if n[0] == '*' | let check = 1
+ else | let n = n . ch
+ end
+ elseif ch == "\"
+ call s:shift_opts(opts, 'da', vals['delimiter_align'])
+ elseif ch == "\"
+ call s:shift_opts(opts, 'idt', vals['indentation'])
+ elseif ch == "\"
+ let lm = s:input("Left margin: ", get(opts, 'lm', ''), a:vis)
+ if empty(lm)
+ let warn = 'Set to default. Input 0 to remove it'
+ silent! call remove(opts, 'lm')
+ else
+ let opts['lm'] = s:atoi(lm)
+ endif
+ elseif ch == "\"
+ let rm = s:input("Right margin: ", get(opts, 'rm', ''), a:vis)
+ if empty(rm)
+ let warn = 'Set to default. Input 0 to remove it'
+ silent! call remove(opts, 'rm')
+ else
+ let opts['rm'] = s:atoi(rm)
+ endif
+ elseif ch == "\"
+ call s:shift_opts(opts, 'iu', vals['ignore_unmatched'])
+ elseif ch == "\"
+ call s:shift_opts(opts, 'ig', vals['ignore_groups'])
+ elseif ch == "\"
+ if s:live
+ if !empty(d)
+ let ch = d
+ break
+ else
+ let s:live = 0
+ endif
+ else
+ let s:live = 1
+ endif
+ elseif c == "\"
+ let opts['stl'] = 1
+ let opts['lm'] = 0
+ elseif c == "\"
+ let opts['stl'] = 0
+ let opts['lm'] = 1
+ elseif c == "\"
+ let opts['lm'] = 0
+ let opts['rm'] = 0
+ elseif c == "\"
+ silent! call remove(opts, 'stl')
+ silent! call remove(opts, 'lm')
+ silent! call remove(opts, 'rm')
+ elseif ch == "\" || ch == "\"
+ let modes = tolower(s:input("Alignment ([lrc...][[*]*]): ", get(opts, 'a', mode), a:vis))
+ if match(modes, '^[lrc]\+\*\{0,2}$') != -1
+ let opts['a'] = modes
+ let mode = modes[0]
+ while mode != s:shift(a:modes, 1)
+ endwhile
+ else
+ silent! call remove(opts, 'a')
+ endif
+ elseif ch == "\" || ch == "\"
+ if s:live && regx && !empty(d)
+ break
+ endif
+
+ let prompt = 'Regular expression: '
+ let ch = s:input(prompt, '', a:vis)
+ if !empty(ch) && s:valid_regexp(ch)
+ let regx = 1
+ let d = ch
+ if !s:live | break | endif
+ else
+ let warn = 'Invalid regular expression: '.ch
+ endif
+ elseif ch == "\"
+ let f = s:input("Filter (g/../ or v/../): ", get(opts, 'f', ''), a:vis)
+ let m = matchlist(f, '^[gv]/\(.\{-}\)/\?$')
+ if empty(f)
+ silent! call remove(opts, 'f')
+ elseif !empty(m) && s:valid_regexp(m[1])
+ let opts['f'] = f
+ else
+ let warn = 'Invalid filter expression'
+ endif
+ elseif ch =~ '[[:print:]]'
+ let check = 1
+ else
+ let warn = 'Invalid character'
+ endif
+
+ if check
+ if empty(d)
+ if has_key(a:rules, ch)
+ let d = ch
+ if !s:live
+ if a:vis
+ execute "normal! gv\"
+ endif
+ break
+ endif
+ else
+ let warn = 'Unknown delimiter key: '.ch
+ endif
+ else
+ if regx
+ let warn = 'Press to finish'
+ else
+ if d == ch
+ break
+ else
+ let warn = 'Press '''.d.''' again to finish'
+ endif
+ end
+ endif
+ endif
+ endwhile
+ if s:live
+ let copts = call('s:summarize', output.summarize)
+ let s:live = 0
+ let g:easy_align_last_command = s:echon('', n, regx, d, copts, '')
+ let s:live = 1
+ end
+ return [mode, n, ch, opts, regx]
+endfunction
+
+function! s:valid_regexp(regexp)
+ try
+ call matchlist('', a:regexp)
+ catch
+ return 0
+ endtry
+ return 1
+endfunction
+
+function! s:test_regexp(regexp)
+ let regexp = empty(a:regexp) ? @/ : a:regexp
+ if !s:valid_regexp(regexp)
+ call s:exit('Invalid regular expression: '. regexp)
+ endif
+ return regexp
+endfunction
+
+let s:shorthand_regex =
+ \ '\s*\%('
+ \ .'\(lm\?[0-9]\+\)\|\(rm\?[0-9]\+\)\|\(iu[01]\)\|\(\%(s\%(tl\)\?[01]\)\|[<>]\)\|'
+ \ .'\(da\?[clr]\)\|\(\%(ms\?\|a\)[lrc*]\+\)\|\(i\%(dt\)\?[kdsn]\)\|\([gv]/.*/\)\|\(ig\[.*\]\)'
+ \ .'\)\+\s*$'
+
+function! s:parse_shorthand_opts(expr)
+ let opts = {}
+ let expr = substitute(a:expr, '\s', '', 'g')
+ let regex = '^'. s:shorthand_regex
+
+ if empty(expr)
+ return opts
+ elseif expr !~ regex
+ call s:exit("Invalid expression: ". a:expr)
+ else
+ let match = matchlist(expr, regex)
+ for m in filter(match[ 1 : -1 ], '!empty(v:val)')
+ for key in ['lm', 'rm', 'l', 'r', 'stl', 's', '<', '>', 'iu', 'da', 'd', 'ms', 'm', 'ig', 'i', 'g', 'v', 'a']
+ if stridx(tolower(m), key) == 0
+ let rest = strpart(m, len(key))
+ if key == 'i' | let key = 'idt' | endif
+ if key == 'g' || key == 'v'
+ let rest = key.rest
+ let key = 'f'
+ endif
+
+ if key == 'idt' || index(['d', 'f', 'm', 'a'], key[0]) >= 0
+ let opts[key] = rest
+ elseif key == 'ig'
+ try
+ let arr = eval(rest)
+ if type(arr) == 3
+ let opts[key] = arr
+ else
+ throw 'Not an array'
+ endif
+ catch
+ call s:exit("Invalid ignore_groups: ". a:expr)
+ endtry
+ elseif key =~ '[<>]'
+ let opts['stl'] = key == '<'
+ else
+ let opts[key] = str2nr(rest)
+ endif
+ break
+ endif
+ endfor
+ endfor
+ endif
+ return s:normalize_options(opts)
+endfunction
+
+function! s:parse_args(args)
+ if empty(a:args)
+ return ['', '', {}, 0]
+ endif
+ let n = ''
+ let ch = ''
+ let args = a:args
+ let cand = ''
+ let opts = {}
+
+ " Poor man's option parser
+ let idx = 0
+ while 1
+ let midx = match(args, '\s*{.*}\s*$', idx)
+ if midx == -1 | break | endif
+
+ let cand = strpart(args, midx)
+ try
+ let [l, r, c, k, s, d, n] = ['l', 'r', 'c', 'k', 's', 'd', 'n']
+ let [L, R, C, K, S, D, N] = ['l', 'r', 'c', 'k', 's', 'd', 'n']
+ let o = eval(cand)
+ if type(o) == 4
+ let opts = o
+ if args[midx - 1 : midx] == '\ '
+ let midx += 1
+ endif
+ let args = strpart(args, 0, midx)
+ break
+ endif
+ catch
+ " Ignore
+ endtry
+ let idx = midx + 1
+ endwhile
+
+ " Invalid option dictionary
+ if len(substitute(cand, '\s', '', 'g')) > 2 && empty(opts)
+ call s:exit("Invalid option: ". cand)
+ else
+ let opts = s:normalize_options(opts)
+ endif
+
+ " Shorthand option notation
+ let sopts = matchstr(args, s:shorthand_regex)
+ if !empty(sopts)
+ let args = strpart(args, 0, len(args) - len(sopts))
+ let opts = extend(s:parse_shorthand_opts(sopts), opts)
+ endif
+
+ " Has /Regexp/?
+ let matches = matchlist(args, '^\(.\{-}\)\s*/\(.*\)/\s*$')
+
+ " Found regexp
+ if !empty(matches)
+ return [matches[1], s:test_regexp(matches[2]), opts, 1]
+ else
+ let tokens = matchlist(args, '^\([1-9][0-9]*\|-[0-9]*\|\*\*\?\)\?\s*\(.\{-}\)\?$')
+ " Try swapping n and ch
+ let [n, ch] = empty(tokens[2]) ? reverse(tokens[1:2]) : tokens[1:2]
+
+ " Resolving command-line ambiguity
+ " '\ ' => ' '
+ " '\' => ' '
+ if ch =~ '^\\\s*$'
+ let ch = ' '
+ " '\\' => '\'
+ elseif ch =~ '^\\\\\s*$'
+ let ch = '\'
+ endif
+
+ return [n, ch, opts, 0]
+ endif
+endfunction
+
+function! s:parse_filter(f)
+ let m = matchlist(a:f, '^\([gv]\)/\(.\{-}\)/\?$')
+ if empty(m)
+ return [0, '']
+ else
+ return [m[1] == 'g' ? 1 : -1, m[2]]
+ endif
+endfunction
+
+function! s:interactive_modes(bang)
+ return get(g:,
+ \ (a:bang ? 'easy_align_bang_interactive_modes' : 'easy_align_interactive_modes'),
+ \ (a:bang ? ['r', 'l', 'c'] : ['l', 'r', 'c']))
+endfunction
+
+function! s:alternating_modes(mode)
+ return a:mode ==? 'r' ? 'rl' : 'lr'
+endfunction
+
+function! s:update_lines(todo)
+ for [line, content] in items(a:todo)
+ call setline(line, s:rtrim(content))
+ endfor
+endfunction
+
+function! s:parse_nth(n)
+ let n = a:n
+ let recur = 0
+ if n == '*' | let [nth, recur] = [1, 1]
+ elseif n == '**' | let [nth, recur] = [1, 2]
+ elseif n == '-' | let nth = -1
+ elseif empty(n) | let nth = 1
+ elseif n == '0' || ( n != '-0' && n != string(str2nr(n)) )
+ call s:exit('Invalid N-th parameter: '. n)
+ else
+ let nth = n
+ endif
+ return [nth, recur]
+endfunction
+
+function! s:build_dict(delimiters, ch, regexp, opts)
+ if a:regexp
+ let dict = { 'pattern': a:ch }
+ else
+ if !has_key(a:delimiters, a:ch)
+ call s:exit('Unknown delimiter key: '. a:ch)
+ endif
+ let dict = copy(a:delimiters[a:ch])
+ endif
+ call extend(dict, a:opts)
+
+ let ml = get(dict, 'left_margin', ' ')
+ let mr = get(dict, 'right_margin', ' ')
+ if type(ml) == 0 | let ml = repeat(' ', ml) | endif
+ if type(mr) == 0 | let mr = repeat(' ', mr) | endif
+ call extend(dict, { 'ml': ml, 'mr': mr })
+
+ let dict.pattern = get(dict, 'pattern', a:ch)
+ let dict.delimiter_align =
+ \ get(dict, 'delimiter_align', get(g:, 'easy_align_delimiter_align', 'r'))[0]
+ let dict.indentation =
+ \ get(dict, 'indentation', get(g:, 'easy_align_indentation', 'k'))[0]
+ let dict.stick_to_left =
+ \ get(dict, 'stick_to_left', 0)
+ let dict.ignore_unmatched =
+ \ get(dict, 'ignore_unmatched', get(g:, 'easy_align_ignore_unmatched', 2))
+ let dict.ignore_groups =
+ \ get(dict, 'ignore_groups', get(dict, 'ignores', s:ignored_syntax()))
+ let dict.filter =
+ \ get(dict, 'filter', '')
+ return dict
+endfunction
+
+function! s:build_mode_sequence(expr, recur)
+ let [expr, recur] = [a:expr, a:recur]
+ let suffix = matchstr(a:expr, '\*\+$')
+ if suffix == '*'
+ let expr = expr[0 : -2]
+ let recur = 1
+ elseif suffix == '**'
+ let expr = expr[0 : -3]
+ let recur = 2
+ endif
+ return [tolower(expr), recur]
+endfunction
+
+function! s:process(range, mode, n, ch, opts, regexp, rules, bvis)
+ let [nth, recur] = s:parse_nth((empty(a:n) && exists('g:easy_align_nth')) ? g:easy_align_nth : a:n)
+ let dict = s:build_dict(a:rules, a:ch, a:regexp, a:opts)
+ let [mode_sequence, recur] = s:build_mode_sequence(
+ \ get(dict, 'align', recur == 2 ? s:alternating_modes(a:mode) : a:mode),
+ \ recur)
+
+ let ve = &virtualedit
+ set ve=all
+ let args = [
+ \ {}, split(mode_sequence, '\zs'),
+ \ {}, {}, a:range[0], a:range[1],
+ \ a:bvis ? min([virtcol("'<"), virtcol("'>")]) : 1,
+ \ (!recur && a:bvis) ? max([virtcol("'<"), virtcol("'>")]) : 0,
+ \ nth, recur, dict ]
+ let &ve = ve
+ while len(args) > 1
+ let args = call('s:do_align', args)
+ endwhile
+
+ " todo: lines to update
+ " summarize: arguments to s:summarize
+ return { 'todo': args[0], 'summarize': [ a:opts, recur, mode_sequence ] }
+endfunction
+
+function s:summarize(opts, recur, mode_sequence)
+ let copts = s:compact_options(a:opts)
+ let nbmode = s:interactive_modes(0)[0]
+ if !has_key(copts, 'a') && (
+ \ (a:recur == 2 && s:alternating_modes(nbmode) != a:mode_sequence) ||
+ \ (a:recur != 2 && (a:mode_sequence[0] != nbmode || len(a:mode_sequence) > 1))
+ \ )
+ call extend(copts, { 'a': a:mode_sequence })
+ endif
+ return copts
+endfunction
+
+function! s:align(bang, live, visualmode, first_line, last_line, expr)
+ " Heuristically determine if the user was in visual mode
+ if a:visualmode == 'command'
+ let vis = a:first_line == line("'<") && a:last_line == line("'>")
+ let bvis = vis && visualmode() == "\"
+ elseif empty(a:visualmode)
+ let vis = 0
+ let bvis = 0
+ else
+ let vis = 1
+ let bvis = a:visualmode == "\"
+ end
+ let range = [a:first_line, a:last_line]
+ let modes = s:interactive_modes(a:bang)
+ let mode = modes[0]
+ let s:live = a:live
+
+ let rules = s:easy_align_delimiters_default
+ if exists('g:easy_align_delimiters')
+ let rules = extend(copy(rules), g:easy_align_delimiters)
+ endif
+
+ let [n, ch, opts, regexp] = s:parse_args(a:expr)
+
+ let bypass_fold = get(g:, 'easy_align_bypass_fold', 0)
+ let ofm = &l:foldmethod
+ try
+ if bypass_fold | let &l:foldmethod = 'manual' | endif
+
+ if empty(n) && empty(ch) || s:live
+ let [mode, n, ch, opts, regexp] = s:interactive(range, copy(modes), n, ch, opts, rules, vis, bvis)
+ endif
+
+ if !s:live
+ let output = s:process(range, mode, n, ch, s:normalize_options(opts), regexp, rules, bvis)
+ call s:update_lines(output.todo)
+ let copts = call('s:summarize', output.summarize)
+ let g:easy_align_last_command = s:echon('', n, regexp, ch, copts, '')
+ endif
+ finally
+ if bypass_fold | let &l:foldmethod = ofm | endif
+ endtry
+endfunction
+
+function! easy_align#align(bang, live, visualmode, expr) range
+ try
+ call s:align(a:bang, a:live, a:visualmode, a:firstline, a:lastline, a:expr)
+ catch /^\%(Vim:Interrupt\|exit\)$/
+ if empty(a:visualmode)
+ echon "\r"
+ echon "\r"
+ else
+ normal! gv
+ endif
+ endtry
+endfunction
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
diff --git a/.vim/bundle/vim-easy-align/doc/easy_align.txt b/.vim/bundle/vim-easy-align/doc/easy_align.txt
new file mode 100644
index 0000000..5a82e5e
--- /dev/null
+++ b/.vim/bundle/vim-easy-align/doc/easy_align.txt
@@ -0,0 +1,891 @@
+easy-align.txt easy-align Last change: December 14 2014
+EASY-ALIGN - TABLE OF CONTENTS *easyalign* *easy-align* *easy-align-toc*
+==============================================================================
+
+ vim-easy-align
+ Demo |easy-align-1|
+ Features |easy-align-2|
+ Installation |easy-align-3|
+ TLDR - One-minute guide |easy-align-4|
+ Usage |easy-align-5|
+ Concept of alignment rule |easy-align-5-1|
+ Execution models |easy-align-5-2|
+ 1. Using mappings |easy-align-5-2-1|
+ 2. Using :EasyAlign command |easy-align-5-2-2|
+ Interactive mode |easy-align-5-3|
+ Predefined alignment rules |easy-align-5-3-1|
+ Examples |easy-align-5-3-2|
+ Using regular expressions |easy-align-5-3-3|
+ Alignment options in interactive mode |easy-align-5-3-4|
+ Live interactive mode |easy-align-5-4|
+ Non-interactive mode |easy-align-5-5|
+ Partial alignment in blockwise-visual mode |easy-align-5-6|
+ Alignment options |easy-align-6|
+ List of options |easy-align-6-1|
+ Filtering lines |easy-align-6-2|
+ Examples |easy-align-6-2-1|
+ Ignoring delimiters in comments or strings |easy-align-6-3|
+ Ignoring unmatched lines |easy-align-6-4|
+ Aligning delimiters of different lengths |easy-align-6-5|
+ Adjusting indentation |easy-align-6-6|
+ Alignments over multiple occurrences of delimiters |easy-align-6-7|
+ Extending alignment rules |easy-align-6-8|
+ Examples |easy-align-6-8-1|
+ Other options |easy-align-7|
+ Disabling &foldmethod during alignment |easy-align-7-1|
+ Left/right/center mode switch in interactive mode |easy-align-7-2|
+ Advanced examples and use cases |easy-align-8|
+ Related work |easy-align-9|
+ Author |easy-align-10|
+ License |easy-align-11|
+
+
+VIM-EASY-ALIGN *vim-easy-align*
+==============================================================================
+
+A simple, easy-to-use Vim alignment plugin.
+
+
+ *easy-align-1*
+DEMO *easy-align-demo*
+==============================================================================
+
+Screencast:
+https://raw.githubusercontent.com/junegunn/i/master/vim-easy-align.gif
+
+(Too fast? Slower GIF is {here}{1})
+
+{1} https://raw.githubusercontent.com/junegunn/i/master/vim-easy-align-slow.gif
+
+
+ *easy-align-2*
+FEATURES *easy-align-features*
+==============================================================================
+
+ - Easy to use
+ - Comes with a predefined set of alignment rules
+ - Provides a fast and intuitive interface
+ - Extensible
+ - You can define your own rules
+ - Supports arbitrary regular expressions
+ - Optimized for code editing
+ - Takes advantage of syntax highlighting feature to avoid unwanted
+ alignments
+
+
+ *easy-align-3*
+INSTALLATION *easy-align-installation*
+==============================================================================
+
+Use your favorite plugin manager.
+
+Using {vim-plug}{2}:
+>
+ Plug 'junegunn/vim-easy-align'
+<
+ {2} https://github.com/junegunn/vim-plug
+
+
+ *easy-align-4*
+TLDR - ONE-MINUTE GUIDE *easy-align-tldr-one-minute-guide*
+==============================================================================
+
+Add the following mappings to your .vimrc.
+
+ *(EasyAlign)*
+>
+ " Start interactive EasyAlign in visual mode (e.g. vip)
+ vmap (EasyAlign)
+
+ " Start interactive EasyAlign for a motion/text object (e.g. gaip)
+ nmap ga (EasyAlign)
+<
+And with the following lines of text,
+>
+ apple =red
+ grass+=green
+ sky-= blue
+<
+try these commands:
+
+ - vip=
+ - `v`isual-select `i`nner `p`aragraph
+ - Start EasyAlign command ()
+ - Align around `=`
+ - `gaip=`
+ - Start EasyAlign command (`ga`) for `i`nner `p`aragraph
+ - Align around `=`
+
+Notice that the commands are repeatable with `.` key if you have installed
+{repeat.vim}{3}. Install {visualrepeat}{4} as well if you want to repeat in
+visual mode.
+
+ {3} https://github.com/tpope/vim-repeat
+ {4} https://github.com/vim-scripts/visualrepeat
+
+
+ *easy-align-5*
+USAGE *easy-align-usage*
+==============================================================================
+
+
+< Concept of alignment rule >_________________________________________________~
+ *easy-align-concept-of-alignment-rule*
+ *easy-align-5-1*
+
+Though easy-align can align lines of text around any delimiter, it provides
+shortcuts for the most common use cases with the concept of "alignment rule".
+
+An alignment rule is a predefined set of options for common alignment tasks,
+which is identified by a single character, DELIMITER KEY, such as ,
+`=`, `:`, `.`, `|`, `&`, `#`, and `,`.
+
+Think of it as a shortcut. Instead of writing regular expression and setting
+several options, you can just type in a single character.
+
+
+< Execution models >__________________________________________________________~
+ *easy-align-execution-models*
+ *easy-align-5-2*
+
+There are two ways to use easy-align.
+
+
+1. Using mappings~
+ *easy-align-1-using-plug-mappings*
+ *easy-align-5-2-1*
+
+The recommended method is to use mappings as described earlier.
+
+ *(LiveEasyAlign)*
+
+ ----------------------+--------+-----------------------------------------------------
+ Mapping | Mode | Description ~
+ ----------------------+--------+-----------------------------------------------------
+ (EasyAlign) | normal | Start interactive mode for a motion/text object
+ (EasyAlign) | visual | Start interactive mode for the selection
+ (LiveEasyAlign) | normal | Start live-interactive mode for a motion/text object
+ (LiveEasyAlign) | visual | Start live-interactive mode for the selection
+ ----------------------+--------+-----------------------------------------------------
+
+
+2. Using :EasyAlign command~
+ *easy-align-2-using-easyalign-command*
+ *easy-align-5-2-2*
+
+ *:EasyAlign*
+
+If you prefer command-line or do not want to start interactive mode, you can
+use `:EasyAlign` command instead.
+
+ *:LiveEasyAlign*
+
+ -------------------------------------------+-----------------------------------------------
+ Mode | Command ~
+ -------------------------------------------+-----------------------------------------------
+ Interactive mode | `:EasyAlign[!] [OPTIONS]`
+ Live interactive mode | `:LiveEasyAlign[!] [...]`
+ Non-interactive mode (predefined rules) | `:EasyAlign[!] [N-th] DELIMITER_KEY [OPTIONS]`
+ Non-interactive mode (regular expressions) | `:EasyAlign[!] [N-th] /REGEXP/ [OPTIONS]`
+ -------------------------------------------+-----------------------------------------------
+
+
+< Interactive mode >__________________________________________________________~
+ *easy-align-interactive-mode*
+ *easy-align-5-3*
+
+The following sections will assume that you have (EasyAlign) mappings in
+your .vimrc as below:
+>
+ " Start interactive EasyAlign in visual mode (e.g. vip)
+ vmap (EasyAlign)
+
+ " Start interactive EasyAlign for a motion/text object (e.g. gaip)
+ nmap ga (EasyAlign)
+<
+With these mappings, you can align text with only a few keystrokes.
+
+ 1. key in visual mode, or `ga` followed by a motion or a text object to
+ start interactive mode
+ 2. Optional: Enter keys to select alignment mode (left, right, or center)
+ 3. Optional: N-th delimiter (default: 1)
+ - `1` Around the 1st occurrences of delimiters
+ - `2` Around the 2nd occurrences of delimiters
+ - ...
+ - `*` Around all occurrences of delimiters
+ - `**` Left-right alternating alignment around all delimiters
+ - `-` Around the last occurrences of delimiters (`-1`)
+ - `-2` Around the second to last occurrences of delimiters
+ - ...
+ 4. Delimiter key (a single keystroke; , `=`, `:`, `.`, `|`, `&`, `#`, `,`)
+
+
+Predefined alignment rules~
+ *easy-align-predefined-alignment-rules*
+ *easy-align-5-3-1*
+
+ --------------+--------------------------------------------------------------------
+ Delimiter key | Description/Use cases ~
+ --------------+--------------------------------------------------------------------
+ | General alignment around whitespaces
+ `=` | Operators containing equals sign ( `=` , `==,` `!=` , `+=` , `&&=` , ...)
+ `:` | Suitable for formatting JSON or YAML
+ `.` | Multi-line method chaining
+ `,` | Multi-line method arguments
+ `&` | LaTeX tables (matches `&` and `\\` )
+ `#` | Ruby/Python comments
+ `"` | Vim comments
+ | Table markdown
+ --------------+--------------------------------------------------------------------
+
+ *g:easy_align_delimiters*
+
+You can override these default rules or define your own rules with
+`g:easy_align_delimiters`, which will be described in {the later section}{5}.
+
+ {5} https://github.com/junegunn/vim-easy-align#extending-alignment-rules
+
+
+Examples~
+ *easy-align-examples*
+ *easy-align-5-3-2*
+
+ ------------------+------------------------------------+--------------------
+ With visual map | Description | Equivalent command ~
+ ------------------+------------------------------------+--------------------
+ | Around 1st whitespaces | :'<,'>EasyAlign\
+ 2 | Around 2nd whitespaces | :'<,'>EasyAlign2\
+ - | Around the last whitespaces | :'<,'>EasyAlign-\
+ -2 | Around the 2nd to last whitespaces | :'<,'>EasyAlign-2\
+ : | Around 1st colon ( `key: value` ) | :'<,'>EasyAlign:
+ : | Around 1st colon ( `key : value` ) | :'<,'>EasyAlign:= | Around 1st operators with = | :'<,'>EasyAlign=
+ 3= | Around 3rd operators with = | :'<,'>EasyAlign3=
+ *= | Around all operators with = | :'<,'>EasyAlign*=
+ **= | Left-right alternating around = | :'<,'>EasyAlign**=
+ = | Right alignment around 1st = | :'<,'>EasyAlign!=
+ **= | Right-left alternating around = | :'<,'>EasyAlign!**=
+ ------------------+------------------------------------+--------------------
+
+
+Using regular expressions~
+ *easy-align-using-regular-expressions*
+ *easy-align-5-3-3*
+
+Instead of finishing the command with a predefined delimiter key, you can type
+in a regular expression after CTRL-/ or CTRL-X key. For example, if you want
+to align text around all occurrences of numbers:
+
+ -
+ - `*`
+ - CTRL-X
+ - `[0-9]\+`
+
+
+Alignment options in interactive mode~
+ *easy-align-alignment-options-in-interactive-mode*
+ *easy-align-5-3-4*
+
+While in interactive mode, you can set alignment options using special
+shortcut keys listed below. The meaning of each option will be described in
+{the following sections}{6}.
+
+ --------+--------------------+---------------------------------------------------
+ Key | Option | Values ~
+ --------+--------------------+---------------------------------------------------
+ CTRL-F | `filter` | Input string ( `[gv]/.*/?` )
+ CTRL-I | `indentation` | shallow, deep, none, keep
+ CTRL-L | `left_margin` | Input number or string
+ CTRL-R | `right_margin` | Input number or string
+ CTRL-D | `delimiter_align` | left, center, right
+ CTRL-U | `ignore_unmatched` | 0, 1
+ CTRL-G | `ignore_groups` | [], ["String'], ["Comment'], ["String', "Comment']
+ CTRL-A | `align` | Input string ( `/[lrc]+\*{0,2}/` )
+ | `stick_to_left` | `{ 'stick_to_left': 1, 'left_margin': 0 }`
+ | `stick_to_left` | `{ 'stick_to_left': 0, 'left_margin': 1 }`
+ | `*_margin` | `{ 'left_margin': 0, 'right_margin': 0 }`
+ --------+--------------------+---------------------------------------------------
+
+ {6} https://github.com/junegunn/vim-easy-align#alignment-options
+
+
+< Live interactive mode >_____________________________________________________~
+ *easy-align-live-interactive-mode*
+ *easy-align-5-4*
+
+If you're performing a complex alignment where multiple options should be
+carefully adjusted, try "live interactive mode" where you can preview the
+result of the alignment on-the-fly as you type in.
+
+Live interactive mode can be started with either (LiveEasyAlign) map or
+`:LiveEasyAlign` command. Or you can switch to live interactive mode while in
+ordinary interactive mode by pressing CTRL-P. (P for Preview)
+
+In live interactive mode, you have to type in the same delimiter (or CTRL-X on
+regular expression) again to finalize the alignment. This allows you to
+preview the result of the alignment and freely change the delimiter using
+backspace key without leaving the interactive mode.
+
+
+< Non-interactive mode >______________________________________________________~
+ *easy-align-non-interactive-mode*
+ *easy-align-5-5*
+
+Instead of starting interactive mode, you can use declarative, non-interactive
+`:EasyAlign` command.
+>
+ " Using predefined alignment rules
+ " :EasyAlign[!] [N-th] DELIMITER_KEY [OPTIONS]
+ :EasyAlign :
+ :EasyAlign =
+ :EasyAlign *=
+ :EasyAlign 3\
+
+ " Using arbitrary regular expressions
+ " :EasyAlign[!] [N-th] /REGEXP/ [OPTIONS]
+ :EasyAlign /[:;]\+/
+ :EasyAlign 2/[:;]\+/
+ :EasyAlign */[:;]\+/
+ :EasyAlign **/[:;]\+/
+<
+A command can end with alignment options, {each of which will be discussed in
+detail later}{6}, in Vim dictionary format.
+
+ - `:EasyAlign * /[:;]\+/ { 'stick_to_left': 1, 'left_margin': 0 }`
+
+`stick_to_left` of 1 means that the matched delimiter should be positioned
+right next to the preceding token, and `left_margin` of 0 removes the margin
+on the left. So we get:
+>
+ apple;: banana:: cake
+ data;; exchange:; format
+<
+Option names are fuzzy-matched, so you can write as follows:
+
+ - `:EasyAlign * /[:;]\+/ { 'stl': 1, 'l': 0 }`
+
+You can even omit spaces between the arguments, so concisely (or cryptically):
+
+ - `:EasyAlign*/[:;]\+/{'s':1,'l':0}`
+
+Nice. But let's make it even shorter. Option values can be written in
+shorthand notation.
+
+ - `:EasyAlign*/[:;]\+/`
+ `ignore_unmatched` | `iu[01]`
+ `ignore_groups` | `ig\[.*\]`
+ `align` | `a[lrc*]*`
+ `delimiter_align` | `d[lrc]`
+ `indentation` | `i[ksdn]`
+ -------------------+-----------
+
+For your information, the same operation can be done in interactive mode as
+follows:
+
+ -
+ - `*`
+ -
+ - CTRL-X
+ - `[:;]\+`
+
+ {6} https://github.com/junegunn/vim-easy-align#alignment-options
+
+
+< Partial alignment in blockwise-visual mode >________________________________~
+ *easy-align-partial-alignment-in-blockwise-visual-mode*
+ *easy-align-5-6*
+
+In blockwise-visual mode (CTRL-V), EasyAlign command aligns only the selected
+text in the block, instead of the whole lines in the range.
+
+Consider the following case where you want to align text around `=>`
+operators.
+>
+ my_hash = { :a => 1,
+ :aa => 2,
+ :aaa => 3 }
+<
+In non-blockwise visual mode (`v` / `V`), = won't work since the
+assignment operator in the first line gets in the way. So we instead enter
+blockwise-visual mode (CTRL-V), and select the text around`=>` operators, then
+press =.
+>
+ my_hash = { :a => 1,
+ :aa => 2,
+ :aaa => 3 }
+<
+However, in this case, we don't really need blockwise visual mode since the
+same can be easily done using the negative N-th parameter: -=
+
+
+ *easy-align-6*
+ALIGNMENT OPTIONS *easy-align-alignment-options*
+==============================================================================
+
+
+< List of options >___________________________________________________________~
+ *easy-align-list-of-options*
+ *easy-align-6-1*
+
+ -------------------+---------+-----------------------+--------------------------------------------------------
+ Option | Type | Default | Description ~
+ -------------------+---------+-----------------------+--------------------------------------------------------
+ `filter` | string | | Line filtering expression: `g/../` or `v/../`
+ `left_margin` | number | 1 | Number of spaces to attach before delimiter
+ `left_margin` | string | `' '` | String to attach before delimiter
+ `right_margin` | number | 1 | Number of spaces to attach after delimiter
+ `right_margin` | string | `' '` | String to attach after delimiter
+ `stick_to_left` | boolean | 0 | Whether to position delimiter on the left-side
+ `ignore_groups` | list | ["String', "Comment'] | Delimiters in these syntax highlight groups are ignored
+ `ignore_unmatched` | boolean | 1 | Whether to ignore lines without matching delimiter
+ `indentation` | string | `k` | Indentation method (keep, deep, shallow, none)
+ `delimiter_align` | string | `r` | Determines how to align delimiters of different lengths
+ `align` | string | `l` | Alignment modes for multiple occurrences of delimiters
+ -------------------+---------+-----------------------+--------------------------------------------------------
+
+There are 4 ways to set alignment options (from lowest precedence to highest):
+
+ 1. Some option values can be set with corresponding global variables
+ 2. Option values can be specified in the definition of each alignment rule
+ 3. Option values can be given as arguments to `:EasyAlign` command
+ 4. Option values can be set in interactive mode using special shortcut keys
+
+ *g:easy_align_ignore_groups* *g:easy_align_ignore_unmatched*
+ *g:easy_align_indentation* *g:easy_align_delimiter_align*
+
+ -------------------+-----------------+-------------+--------------------------------
+ Option name | Shortcut key | Abbreviated | Global variable ~
+ -------------------+-----------------+-------------+--------------------------------
+ `filter` | CTRL-F | `[gv]/.*/` |
+ `left_margin` | CTRL-L | `l[0-9]+` |
+ `right_margin` | CTRL-R | `r[0-9]+` |
+ `stick_to_left` | , | `<` or `>` |
+ `ignore_groups` | CTRL-G | `ig\[.*\]` | `g:easy_align_ignore_groups`
+ `ignore_unmatched` | CTRL-U | `iu[01]` | `g:easy_align_ignore_unmatched`
+ `indentation` | CTRL-I | `i[ksdn]` | `g:easy_align_indentation`
+ `delimiter_align` | CTRL-D | `d[lrc]` | `g:easy_align_delimiter_align`
+ `align` | CTRL-A | `a[lrc*]*` |
+ -------------------+-----------------+-------------+--------------------------------
+
+
+< Filtering lines >___________________________________________________________~
+ *easy-align-filtering-lines*
+ *easy-align-6-2*
+
+With `filter` option, you can align lines that only match or do not match a
+given pattern. There are several ways to set the pattern.
+
+ 1. Press CTRL-F in interactive mode and type in `g/pat/` or `v/pat/`
+ 2. In command-line, it can be written in dictionary format: `{'filter':
+ 'g/pat/'}`
+ 3. Or in shorthand notation: `g/pat/` or `v/pat/`
+
+(You don't need to escape "/'s in the regular expression)
+
+
+Examples~
+
+ *easy-align-6-2-1*
+>
+ " Start interactive mode with filter option set to g/hello/
+ EasyAlign g/hello/
+
+ " Start live interactive mode with filter option set to v/goodbye/
+ LiveEasyAlign v/goodbye/
+
+ " Align the lines with 'hi' around the first colons
+ EasyAlign:g/hi/
+<
+
+< Ignoring delimiters in comments or strings >________________________________~
+ *easy-align-ignoring-delimiters-in-comments-or-strings*
+ *easy-align-6-3*
+
+EasyAlign can be configured to ignore delimiters in certain syntax highlight
+groups, such as code comments or strings. By default, delimiters that are
+highlighted as code comments or strings are ignored.
+>
+ " Default:
+ " If a delimiter is in a highlight group whose name matches
+ " any of the followings, it will be ignored.
+ let g:easy_align_ignore_groups = ['Comment', 'String']
+<
+For example, the following paragraph
+>
+ {
+ # Quantity of apples: 1
+ apple: 1,
+ # Quantity of bananas: 2
+ bananas: 2,
+ # Quantity of grape:fruits: 3
+ 'grape:fruits': 3
+ }
+<
+becomes as follows on : (or `:EasyAlign:`)
+>
+ {
+ # Quantity of apples: 1
+ apple: 1,
+ # Quantity of bananas: 2
+ bananas: 2,
+ # Quantity of grape:fruits: 3
+ 'grape:fruits': 3
+ }
+<
+Naturally, this feature only works when syntax highlighting is enabled.
+
+You can change the default rule by using one of these 4 methods.
+
+ 1. Press CTRL-G in interactive mode to switch groups
+ 2. Define global `g:easy_align_ignore_groups` list
+ 3. Define a custom rule in `g:easy_align_delimiters` with `ignore_groups` option
+ 4. Provide `ignore_groups` option to `:EasyAlign` command. e.g. `:EasyAlign:ig[]`
+
+For example if you set `ignore_groups` option to be an empty list, you get
+>
+ {
+ # Quantity of apples: 1
+ apple: 1,
+ # Quantity of bananas: 2
+ bananas: 2,
+ # Quantity of grape: fruits: 3
+ 'grape: fruits': 3
+ }
+<
+If a pattern in `ignore_groups` is prepended by a `!`, it will have the
+opposite meaning. For instance, if `ignore_groups` is given as `['!Comment']`,
+delimiters that are not highlighted as Comment will be ignored during the
+alignment.
+
+
+< Ignoring unmatched lines >__________________________________________________~
+ *easy-align-ignoring-unmatched-lines*
+ *easy-align-6-4*
+
+`ignore_unmatched` option determines how EasyAlign command processes lines
+that do not have N-th delimiter.
+
+ 1. In left-alignment mode, they are ignored
+ 2. In right or center-alignment mode, they are not ignored, and the last tokens
+ from those lines are aligned as well as if there is an invisible trailing
+ delimiter at the end of each line
+ 3. If `ignore_unmatched` is 1, they are ignored regardless of the alignment mode
+ 4. If `ignore_unmatched` is 0, they are not ignored regardless of the mode
+
+Let's take an example. When we align the following code block around the (1st)
+colons,
+>
+ {
+ apple: proc {
+ this_line_does_not_have_a_colon
+ },
+ bananas: 2,
+ grapefruits: 3
+ }
+<
+this is usually what we want.
+>
+ {
+ apple: proc {
+ this_line_does_not_have_a_colon
+ },
+ bananas: 2,
+ grapefruits: 3
+ }
+<
+However, we can override this default behavior by setting `ignore_unmatched`
+option to zero using one of the following methods.
+
+ 1. Press CTRL-U in interactive mode to toggle `ignore_unmatched` option
+ 2. Set the global `g:easy_align_ignore_unmatched` variable to 0
+ 3. Define a custom alignment rule with `ignore_unmatched` option set to 0
+ 4. Provide `ignore_unmatched` option to `:EasyAlign` command. e.g.
+ `:EasyAlign:iu0`
+
+Then we get,
+>
+ {
+ apple: proc {
+ this_line_does_not_have_a_colon
+ },
+ bananas: 2,
+ grapefruits: 3
+ }
+<
+
+< Aligning delimiters of different lengths >__________________________________~
+ *easy-align-aligning-delimiters-of-different-lengths*
+ *easy-align-6-5*
+
+Global `g:easy_align_delimiter_align` option and rule-wise/command-wise
+`delimiter_align` option determines how matched delimiters of different
+lengths are aligned.
+>
+ apple = 1
+ banana += apple
+ cake ||= banana
+<
+By default, delimiters are right-aligned as follows.
+>
+ apple = 1
+ banana += apple
+ cake ||= banana
+<
+However, with `:EasyAlign=dl`, delimiters are left-aligned.
+>
+ apple = 1
+ banana += apple
+ cake ||= banana
+<
+And on `:EasyAlign=dc`, center-aligned.
+>
+ apple = 1
+ banana += apple
+ cake ||= banana
+<
+In interactive mode, you can change the option value with CTRL-D key.
+
+
+< Adjusting indentation >_____________________________________________________~
+ *easy-align-adjusting-indentation*
+ *easy-align-6-6*
+
+By default :EasyAlign command keeps the original indentation of the lines. But
+then again we have `indentation` option. See the following example.
+>
+ # Lines with different indentation
+ apple = 1
+ banana = 2
+ cake = 3
+ daisy = 4
+ eggplant = 5
+
+ # Default: _k_eep the original indentation
+ # :EasyAlign=
+ apple = 1
+ banana = 2
+ cake = 3
+ daisy = 4
+ eggplant = 5
+
+ # Use the _s_hallowest indentation among the lines
+ # :EasyAlign=is
+ apple = 1
+ banana = 2
+ cake = 3
+ daisy = 4
+ eggplant = 5
+
+ # Use the _d_eepest indentation among the lines
+ # :EasyAlign=id
+ apple = 1
+ banana = 2
+ cake = 3
+ daisy = 4
+ eggplant = 5
+
+ # Indentation: _n_one
+ # :EasyAlign=in
+ apple = 1
+ banana = 2
+ cake = 3
+ daisy = 4
+ eggplant = 5
+<
+In interactive mode, you can change the option value with CTRL-I key.
+
+
+< Alignments over multiple occurrences of delimiters >________________________~
+ *easy-align-alignments-over-multiple-occurrences-of-delimiters*
+ *easy-align-6-7*
+
+As stated above, "N-th" parameter is used to target specific occurrences of
+the delimiter when it appears multiple times in each line.
+
+To recap:
+>
+ " Left-alignment around the FIRST occurrences of delimiters
+ :EasyAlign =
+
+ " Left-alignment around the SECOND occurrences of delimiters
+ :EasyAlign 2=
+
+ " Left-alignment around the LAST occurrences of delimiters
+ :EasyAlign -=
+
+ " Left-alignment around ALL occurrences of delimiters
+ :EasyAlign *=
+
+ " Left-right ALTERNATING alignment around all occurrences of delimiters
+ :EasyAlign **=
+
+ " Right-left ALTERNATING alignment around all occurrences of delimiters
+ :EasyAlign! **=
+<
+In addition to these, you can fine-tune alignments over multiple occurrences
+of the delimiters with "align' option. (The option can also be set in
+interactive mode with the special key CTRL-A)
+>
+ " Left alignment over the first two occurrences of delimiters
+ :EasyAlign = { 'align': 'll' }
+
+ " Right, left, center alignment over the 1st to 3rd occurrences of delimiters
+ :EasyAlign = { 'a': 'rlc' }
+
+ " Using shorthand notation
+ :EasyAlign = arlc
+
+ " Right, left, center alignment over the 2nd to 4th occurrences of delimiters
+ :EasyAlign 2=arlc
+
+ " (*) Repeating alignments (default: l, r, or c)
+ " Right, left, center, center, center, center, ...
+ :EasyAlign *=arlc
+
+ " (**) Alternating alignments (default: lr or rl)
+ " Right, left, center, right, left, center, ...
+ :EasyAlign **=arlc
+
+ " Right, left, center, center, center, ... repeating alignment
+ " over the 3rd to the last occurrences of delimiters
+ :EasyAlign 3=arlc*
+
+ " Right, left, center, right, left, center, ... alternating alignment
+ " over the 3rd to the last occurrences of delimiters
+ :EasyAlign 3=arlc**
+<
+
+< Extending alignment rules >_________________________________________________~
+ *easy-align-extending-alignment-rules*
+ *easy-align-6-8*
+
+Although the default rules should cover the most of the use cases, you can
+extend the rules by setting a dictionary named `g:easy_align_delimiters`.
+
+You may refer to the definitions of the default alignment rules {here}{7}.
+
+{7} https://github.com/junegunn/vim-easy-align/blob/2.9.6/autoload/easy_align.vim#L32-L46
+
+
+Examples~
+
+ *easy-align-6-8-1*
+>
+ let g:easy_align_delimiters = {
+ \ '>': { 'pattern': '>>\|=>\|>' },
+ \ '/': {
+ \ 'pattern': '//\+\|/\*\|\*/',
+ \ 'delimiter_align': 'l',
+ \ 'ignore_groups': ['!Comment'] },
+ \ ']': {
+ \ 'pattern': '[[\]]',
+ \ 'left_margin': 0,
+ \ 'right_margin': 0,
+ \ 'stick_to_left': 0
+ \ },
+ \ ')': {
+ \ 'pattern': '[()]',
+ \ 'left_margin': 0,
+ \ 'right_margin': 0,
+ \ 'stick_to_left': 0
+ \ },
+ \ 'd': {
+ \ 'pattern': ' \(\S\+\s*[;=]\)\@=',
+ \ 'left_margin': 0,
+ \ 'right_margin': 0
+ \ }
+ \ }
+<
+
+ *easy-align-7*
+OTHER OPTIONS *easy-align-other-options*
+==============================================================================
+
+
+< Disabling &foldmethod during alignment >____________________________________~
+ *easy-align-disabling-foldmethod-during-alignment*
+ *easy-align-7-1*
+
+ *g:easy_align_bypass_fold*
+
+{It is reported}{8} that 'foldmethod' value of `expr` or `syntax` can
+significantly slow down the alignment when editing a large, complex file with
+many folds. To alleviate this issue, EasyAlign provides an option to
+temporarily set 'foldmethod' to `manual` during the alignment task. In order
+to enable this feature, set `g:easy_align_bypass_fold` switch to 1.
+>
+ let g:easy_align_bypass_fold = 1
+<
+ {8} https://github.com/junegunn/vim-easy-align/issues/14
+
+
+< Left/right/center mode switch in interactive mode >_________________________~
+ *easy-align-left-right-center-mode-switch-in-interactive-mode*
+ *easy-align-7-2*
+
+In interactive mode, you can choose the alignment mode you want by pressing
+enter keys. The non-bang command, `:EasyAlign` starts in left-alignment mode
+and changes to right and center mode as you press enter keys, while the bang
+version first starts in right-alignment mode.
+
+ - `:EasyAlign`
+ - Left, Right, Center
+ - `:EasyAlign!`
+ - Right, Left, Center
+
+If you do not prefer this default mode transition, you can define your own
+settings as follows.
+
+ *g:easy_align_interactive_modes* *g:easy_align_bang_interactive_modes*
+>
+ let g:easy_align_interactive_modes = ['l', 'r']
+ let g:easy_align_bang_interactive_modes = ['c', 'r']
+<
+
+ *easy-align-8*
+ADVANCED EXAMPLES AND USE CASES *easy-align-advanced-examples-and-use-cases*
+==============================================================================
+
+See {EXAMPLES.md}{9} for more examples.
+
+ {9} https://github.com/junegunn/vim-easy-align/blob/master/EXAMPLES.md
+
+
+ *easy-align-9*
+RELATED WORK *easy-align-related-work*
+==============================================================================
+
+ - {DrChip's Alignment Tool for Vim}{10}
+ - {Tabular}{11}
+
+ {10} http://www.drchip.org/astronaut/vim/align.html
+ {11} https://github.com/godlygeek/tabular
+
+
+ *easy-align-10*
+AUTHOR *easy-align-author*
+==============================================================================
+
+{Junegunn Choi}{12}
+
+ {12} https://github.com/junegunn
+
+
+ *easy-align-11*
+LICENSE *easy-align-license*
+==============================================================================
+
+MIT
+
+==============================================================================
+vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap:
diff --git a/.vim/bundle/vim-easy-align/doc/tags b/.vim/bundle/vim-easy-align/doc/tags
new file mode 100644
index 0000000..0411c5f
--- /dev/null
+++ b/.vim/bundle/vim-easy-align/doc/tags
@@ -0,0 +1,84 @@
+:EasyAlign easy_align.txt /*:EasyAlign*
+:LiveEasyAlign easy_align.txt /*:LiveEasyAlign*
+(EasyAlign) easy_align.txt /*(EasyAlign)*
+(LiveEasyAlign) easy_align.txt /*(LiveEasyAlign)*
+easy-align easy_align.txt /*easy-align*
+easy-align-1 easy_align.txt /*easy-align-1*
+easy-align-1-using-plug-mappings easy_align.txt /*easy-align-1-using-plug-mappings*
+easy-align-10 easy_align.txt /*easy-align-10*
+easy-align-11 easy_align.txt /*easy-align-11*
+easy-align-2 easy_align.txt /*easy-align-2*
+easy-align-2-using-easyalign-command easy_align.txt /*easy-align-2-using-easyalign-command*
+easy-align-3 easy_align.txt /*easy-align-3*
+easy-align-4 easy_align.txt /*easy-align-4*
+easy-align-5 easy_align.txt /*easy-align-5*
+easy-align-5-1 easy_align.txt /*easy-align-5-1*
+easy-align-5-2 easy_align.txt /*easy-align-5-2*
+easy-align-5-2-1 easy_align.txt /*easy-align-5-2-1*
+easy-align-5-2-2 easy_align.txt /*easy-align-5-2-2*
+easy-align-5-3 easy_align.txt /*easy-align-5-3*
+easy-align-5-3-1 easy_align.txt /*easy-align-5-3-1*
+easy-align-5-3-2 easy_align.txt /*easy-align-5-3-2*
+easy-align-5-3-3 easy_align.txt /*easy-align-5-3-3*
+easy-align-5-3-4 easy_align.txt /*easy-align-5-3-4*
+easy-align-5-4 easy_align.txt /*easy-align-5-4*
+easy-align-5-5 easy_align.txt /*easy-align-5-5*
+easy-align-5-6 easy_align.txt /*easy-align-5-6*
+easy-align-6 easy_align.txt /*easy-align-6*
+easy-align-6-1 easy_align.txt /*easy-align-6-1*
+easy-align-6-2 easy_align.txt /*easy-align-6-2*
+easy-align-6-2-1 easy_align.txt /*easy-align-6-2-1*
+easy-align-6-3 easy_align.txt /*easy-align-6-3*
+easy-align-6-4 easy_align.txt /*easy-align-6-4*
+easy-align-6-5 easy_align.txt /*easy-align-6-5*
+easy-align-6-6 easy_align.txt /*easy-align-6-6*
+easy-align-6-7 easy_align.txt /*easy-align-6-7*
+easy-align-6-8 easy_align.txt /*easy-align-6-8*
+easy-align-6-8-1 easy_align.txt /*easy-align-6-8-1*
+easy-align-7 easy_align.txt /*easy-align-7*
+easy-align-7-1 easy_align.txt /*easy-align-7-1*
+easy-align-7-2 easy_align.txt /*easy-align-7-2*
+easy-align-8 easy_align.txt /*easy-align-8*
+easy-align-9 easy_align.txt /*easy-align-9*
+easy-align-adjusting-indentation easy_align.txt /*easy-align-adjusting-indentation*
+easy-align-advanced-examples-and-use-cases easy_align.txt /*easy-align-advanced-examples-and-use-cases*
+easy-align-aligning-delimiters-of-different-lengths easy_align.txt /*easy-align-aligning-delimiters-of-different-lengths*
+easy-align-alignment-options easy_align.txt /*easy-align-alignment-options*
+easy-align-alignment-options-in-interactive-mode easy_align.txt /*easy-align-alignment-options-in-interactive-mode*
+easy-align-alignments-over-multiple-occurrences-of-delimiters easy_align.txt /*easy-align-alignments-over-multiple-occurrences-of-delimiters*
+easy-align-author easy_align.txt /*easy-align-author*
+easy-align-concept-of-alignment-rule easy_align.txt /*easy-align-concept-of-alignment-rule*
+easy-align-demo easy_align.txt /*easy-align-demo*
+easy-align-disabling-foldmethod-during-alignment easy_align.txt /*easy-align-disabling-foldmethod-during-alignment*
+easy-align-examples easy_align.txt /*easy-align-examples*
+easy-align-execution-models easy_align.txt /*easy-align-execution-models*
+easy-align-extending-alignment-rules easy_align.txt /*easy-align-extending-alignment-rules*
+easy-align-features easy_align.txt /*easy-align-features*
+easy-align-filtering-lines easy_align.txt /*easy-align-filtering-lines*
+easy-align-ignoring-delimiters-in-comments-or-strings easy_align.txt /*easy-align-ignoring-delimiters-in-comments-or-strings*
+easy-align-ignoring-unmatched-lines easy_align.txt /*easy-align-ignoring-unmatched-lines*
+easy-align-installation easy_align.txt /*easy-align-installation*
+easy-align-interactive-mode easy_align.txt /*easy-align-interactive-mode*
+easy-align-left-right-center-mode-switch-in-interactive-mode easy_align.txt /*easy-align-left-right-center-mode-switch-in-interactive-mode*
+easy-align-license easy_align.txt /*easy-align-license*
+easy-align-list-of-options easy_align.txt /*easy-align-list-of-options*
+easy-align-live-interactive-mode easy_align.txt /*easy-align-live-interactive-mode*
+easy-align-non-interactive-mode easy_align.txt /*easy-align-non-interactive-mode*
+easy-align-other-options easy_align.txt /*easy-align-other-options*
+easy-align-partial-alignment-in-blockwise-visual-mode easy_align.txt /*easy-align-partial-alignment-in-blockwise-visual-mode*
+easy-align-predefined-alignment-rules easy_align.txt /*easy-align-predefined-alignment-rules*
+easy-align-related-work easy_align.txt /*easy-align-related-work*
+easy-align-tldr-one-minute-guide easy_align.txt /*easy-align-tldr-one-minute-guide*
+easy-align-toc easy_align.txt /*easy-align-toc*
+easy-align-usage easy_align.txt /*easy-align-usage*
+easy-align-using-regular-expressions easy_align.txt /*easy-align-using-regular-expressions*
+easyalign easy_align.txt /*easyalign*
+g:easy_align_bang_interactive_modes easy_align.txt /*g:easy_align_bang_interactive_modes*
+g:easy_align_bypass_fold easy_align.txt /*g:easy_align_bypass_fold*
+g:easy_align_delimiter_align easy_align.txt /*g:easy_align_delimiter_align*
+g:easy_align_delimiters easy_align.txt /*g:easy_align_delimiters*
+g:easy_align_ignore_groups easy_align.txt /*g:easy_align_ignore_groups*
+g:easy_align_ignore_unmatched easy_align.txt /*g:easy_align_ignore_unmatched*
+g:easy_align_indentation easy_align.txt /*g:easy_align_indentation*
+g:easy_align_interactive_modes easy_align.txt /*g:easy_align_interactive_modes*
+vim-easy-align easy_align.txt /*vim-easy-align*
diff --git a/.vim/bundle/vim-easy-align/plugin/easy_align.vim b/.vim/bundle/vim-easy-align/plugin/easy_align.vim
new file mode 100644
index 0000000..c71af4e
--- /dev/null
+++ b/.vim/bundle/vim-easy-align/plugin/easy_align.vim
@@ -0,0 +1,142 @@
+" Copyright (c) 2014 Junegunn Choi
+"
+" MIT License
+"
+" Permission is hereby granted, free of charge, to any person obtaining
+" a copy of this software and associated documentation files (the
+" "Software"), to deal in the Software without restriction, including
+" without limitation the rights to use, copy, modify, merge, publish,
+" distribute, sublicense, and/or sell copies of the Software, and to
+" permit persons to whom the Software is furnished to do so, subject to
+" the following conditions:
+"
+" The above copyright notice and this permission notice shall be
+" included in all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if exists("g:loaded_easy_align_plugin")
+ finish
+endif
+let g:loaded_easy_align_plugin = 1
+
+command! -nargs=* -range -bang EasyAlign ,call easy_align#align(0, 0, 'command', )
+command! -nargs=* -range -bang LiveEasyAlign ,call easy_align#align(0, 1, 'command', )
+
+let s:last_command = 'EasyAlign'
+
+function! s:abs(v)
+ return a:v >= 0 ? a:v : - a:v
+endfunction
+
+function! s:remember_visual(mode)
+ let s:last_visual = [a:mode, s:abs(line("'>") - line("'<")), s:abs(col("'>") - col("'<"))]
+endfunction
+
+function! s:repeat_visual()
+ let [mode, ldiff, cdiff] = s:last_visual
+ let cmd = 'normal! '.mode
+ if ldiff > 0
+ let cmd .= ldiff . 'j'
+ endif
+
+ let ve_save = &virtualedit
+ try
+ if mode == "\"
+ if cdiff > 0
+ let cmd .= cdiff . 'l'
+ endif
+ set virtualedit+=block
+ endif
+ execute cmd.":\=g:easy_align_last_command\\"
+ call s:set_repeat()
+ finally
+ if ve_save != &virtualedit
+ let &virtualedit = ve_save
+ endif
+ endtry
+endfunction
+
+function! s:repeat_in_visual()
+ if exists('g:easy_align_last_command')
+ call s:remember_visual(visualmode())
+ call s:repeat_visual()
+ endif
+endfunction
+
+function! s:set_repeat()
+ silent! call repeat#set("\(EasyAlignRepeat)")
+endfunction
+
+function! s:generic_easy_align_op(type, vmode, live)
+ if !&modifiable
+ if a:vmode
+ normal! gv
+ endif
+ return
+ endif
+ let sel_save = &selection
+ let &selection = "inclusive"
+
+ if a:vmode
+ let vmode = a:type
+ let [l1, l2] = ["'<", "'>"]
+ call s:remember_visual(vmode)
+ else
+ let vmode = ''
+ let [l1, l2] = [line("'["), line("']")]
+ unlet! s:last_visual
+ endif
+
+ try
+ let range = l1.','.l2
+ if get(g:, 'easy_align_need_repeat', 0)
+ execute range . g:easy_align_last_command
+ else
+ execute range . "call easy_align#align(0, a:live, vmode, '')"
+ end
+ call s:set_repeat()
+ finally
+ let &selection = sel_save
+ endtry
+endfunction
+
+function! s:easy_align_op(type, ...)
+ call s:generic_easy_align_op(a:type, a:0, 0)
+endfunction
+
+function! s:live_easy_align_op(type, ...)
+ call s:generic_easy_align_op(a:type, a:0, 1)
+endfunction
+
+function! s:easy_align_repeat()
+ if exists('s:last_visual')
+ call s:repeat_visual()
+ else
+ try
+ let g:easy_align_need_repeat = 1
+ normal! .
+ finally
+ unlet! g:easy_align_need_repeat
+ endtry
+ endif
+endfunction
+
+nnoremap (EasyAlign) :set opfunc=easy_align_opg@
+vnoremap (EasyAlign) :call easy_align_op(visualmode(), 1)
+nnoremap (LiveEasyAlign) :set opfunc=live_easy_align_opg@
+vnoremap (LiveEasyAlign) :call live_easy_align_op(visualmode(), 1)
+
+" vim-repeat support
+nnoremap (EasyAlignRepeat) :call easy_align_repeat()
+vnoremap (EasyAlignRepeat) :call repeat_in_visual()
+
+" Backward-compatibility (deprecated)
+nnoremap (EasyAlignOperator) :set opfunc=easy_align_opg@
+
diff --git a/.vim/bundle/vim-fswitch/doc/fswitch.txt b/.vim/bundle/vim-fswitch/doc/fswitch.txt
new file mode 100644
index 0000000..688f7ef
--- /dev/null
+++ b/.vim/bundle/vim-fswitch/doc/fswitch.txt
@@ -0,0 +1,605 @@
+*fswitch.txt* For Vim version 7.2 and above Last change: 2009 Mar 23
+
+ ---------------
+ File Switcher
+ ---------------
+
+Author: Derek Wyatt (derek at myfirstnamemylastname dot org)
+
+ *fswitch-copyright*
+Copyright: The VIM LICENSE applies to fswitch.vim, and fswitch.txt
+ (see |copyright|) except use "fswitch" instead of "Vim".
+ No warranty, express or implied.
+ Use At-Your-Own-Risk!
+
+==============================================================================
+ *fswitch* *fswitch-contents*
+1. Contents~
+
+ 1. Contents .............................: |fswitch-contents|
+ 2. About ................................: |fswitch-about|
+ 3. Features .............................: |fswitch-features|
+ 4. Setup ................................: |fswitch-setup|
+ 5. Configuration ........................: |fswitch-configure|
+ 6. "Creating" the Alternate File ........: |fswitch-altcreate|
+ 7. Useful Mappings ......................: |fswitch-mappings|
+ 8. FSwitch() ............................: |fswitch-function|
+ 9. FSReturnCompanionFilenameString().....: |fswitch-getcompanion|
+ 10. FSReturnReadableCompanionFilename()...: |fswitch-getreadablecomp|
+ 11. The Default Settings .................: |fswitch-defaults|
+ 12. Examples .............................: |fswitch-examples|
+ 13. Troubleshooting ......................: |fswitch-trouble|
+ A. Change History .......................: |fswitch-changes|
+
+==============================================================================
+ *fswitch-about*
+2. About~
+
+FSwitch is designed to allow you to switch between companion files of source
+code (e.g. "cpp" files and their corresponding "h" files). The source for
+this came from a home-grown script that was influenced later by the
+"Alternate" (a.vim) script.
+
+The original intention was to modify the existing a.vim script to do what the
+home-grown version could do (choose to open the other file in an existing
+window) but it was a rather complex script and modification looked difficult
+so the choice was made to simply move the home-grown script forward a couple
+of notches and produce a new plugin. This doc file is twice the length of the
+actual code at this point :)
+
+==============================================================================
+ *fswitch-features*
+3. Features~
+
+FSwitch has the following features:
+
+ - Switches between a file and its companion file
+ - Ability to create a new file using a preferential location
+ - Simple configuration using buffer-local variables
+ - It's got a really long doc file (... seriously, why is this thing so
+ bloody long?)
+ - Umm... other stuff?
+
+==============================================================================
+ *fswitch-setup*
+4. Setup~
+
+Most of the behaviour of FSwitch is customized via buffer-local variables.
+You set up the variables with auto commands:
+>
+ au! BufEnter *.cpp let b:fswitchdst = 'hpp,h' | let b:fswitchlocs = '../inc'
+<
+That |:autocmd| will set the 'fswitchdst' and 'fswitchlocs' variables when the
+|BufEnter| event takes place on a file whose name matches {*.cpp} (e.g. when
+you enter the buffer containing the {MyFile.cpp} file).
+
+The variables above state that the alternate file to {MyFile.cpp} are
+{MyFile.hpp} and {MyFile.h} preferred in that order, and located in the {inc}
+directory at the same level as the current directory.
+
+That should get you there but there's more capability here if you want. To
+get that move on to |fswitch-configure|.
+
+==============================================================================
+ *fswitch-configure*
+5. Configuration~
+
+
+ *'fswitchdst'*
+'fswitchdst' string (default depends on file in current buffer)
+ local to buffer
+
+ The 'fswitchdst' variable denotes the file extension that is the
+ target extension of the current file's companion file. For example:
+>
+ :let b:fswitchdst = 'cpp,cxx,C'
+<
+ The above specifies that the current buffer's file has a companion
+ filename which can be found by replacing the current extension with
+ {cpp}, {cxx} or {C}. The extensions will be tried in this order and
+ the first match wins.
+
+ 'fswitchdst' is taken relative to directories that are found in the
+ 'fswitchlocs' variable.
+
+ *'fswitchlocs'*
+'fswitchlocs' string (default depends on file in current buffer)
+ local to buffer
+
+ The 'fswitchlocs' variable contains a set of directives that indicate
+ directory names that should be formulated when trying to find the
+ alternate file. For example:
+>
+ " Create the destination path by substituting any
+ " 'include' string from the pathname with 'src'
+ :let b:fswitchlocs = 'reg:/include/src/'
+
+ " First try adding the relative path '../src' to the path
+ " in which the file in the buffer exists and if that fails
+ " then try using 'source' instead
+ :let b:fswitchlocs = 'rel:../src,source'
+
+ " Same as above but leaving off the optional 'rel:'
+ :let b:fswitchlocs = '../src,../source'
+<
+ The following types of directives are understood:
+
+ *fswitch_reg*
+ reg:~
+ A regular expression. The regular expression takes the form:
+>
+ {delim}{pat}{delim}{globsub}{delim}
+<
+ Where:
+
+ {delim} is something that doesn't appear in {pat} or
+ {globsub} used to delimit the {pat} and {globsub}
+
+ {pat} is a standard pattern to search on
+
+ {globsub} is a substitution string that will be run through
+ the |glob()| function.
+
+ *fswitch_rel*
+ rel:~
+ A relative path. The {rel:} is actually optional. If you
+ leave this off, then FSwitch will assume that the string is
+ denoting a relative path.
+
+ *fswitch_ifrel*
+ ifrel:~
+ Takes the same form as {:reg} but the {globsub} part of the
+ directive is a relative path. The relative path is only used
+ if the {pat} matches the existing path of the buffer.
+
+ *fswitch_abs*
+ abs:~
+ An absolute path. I have no idea why you'd ever want to do
+ this, but it's there if you want it.
+
+ *fswitch_ifabs*
+ ifabs:~
+ Takes the same form as {:reg} but the {globsub} part of the
+ directive is an absolute path. The absolute path is only used
+ if the {pat} matches the existing path of the buffer.
+
+ Why use the "if" variants?
+
+ Here's the situation: You've got the following file:
+>
+ ~/code/MyFile.h
+<
+ And you've set the following locations:
+>
+ For .h -> reg:/include/src/,../src,./
+ For .cpp -> reg:/src/include/,../include,./
+<
+ Here's what happens when run the following commands:
+>
+ FSwitch('%')
+ # Creates a new file ~/src/MyFile.cpp due to the first
+ # relative path in the list for .h
+ FSwitch('%')
+ # Creates a new file ~/include/MyFile.h due to the first
+ # regular expression in the list for .cpp
+<
+ The problem is that you've unconditionally said you want to use
+ {../src} for the alternate file but in reality you probably wanted to
+ use {./}. If you use {:ifrel} instead then you can say that you only
+ want to use {../src} if the path to the current buffer contains
+ {/include/} or something like that. If you did this FSwitch would not
+ have taken {../src} for the new file but would have chosen {./}
+
+ So the "right" setup is:
+>
+ For .h -> reg:/include/src/,ifrel:|/include/|../src|,./
+ For .cpp -> reg:/src/include/,ifrel:|/src/|../include|,./
+<
+ *'fswitchdisablegloc'*
+'fsdisablegloc'
+ string (default off)
+ local to buffer
+
+ Disables the appending of the default global locations to the local
+ buffer definition. Normally when processing alternate file locations
+ FSwitch will append some default values to the list of locations. If
+ you define this variable then this will not happen.
+
+ The default locations are currently set to "./" or ".\" depending on
+ what slash your configuration evaluates to.
+
+ *'fswitchfnames'*
+'fswitchfnames' string (default depends on file in current buffer)
+ local to buffer
+
+ The 'fswitchfnames' variable contains a comma-separated list
+ of possible substitution patterns over the base filename (without path
+ and extension) that should be formulated when trying to find the
+ alternate file. The format of the substitution pattern is the same
+ as |fswitch-reg|.
+
+ This may be useful when the companion file may can be formulated by
+ adding a suffix to the base filename. For instance, when the companion
+ of MyClass.java is MyClassTest.java.
+
+ For example:
+>
+ " Create the destination filename by appending 'Test'
+ " to the filename
+ :let b:fswitchfnames = '/$/Test/'
+
+ " Create the destination filename by removing 'Test'
+ " from the end of the filename.
+ :let b:fswitchfnames = '/Test$//'
+<
+ *'fswitchnonewfiles'*
+'fsnonewfiles'
+ string (default off)
+ local to buffer and global
+
+ This variable is both global and local. If you want to disable the
+ creation of the alternate file when it doesn't already exist you can
+ choose to do this on a per-extension basis or globally. Set the
+ global one to shut it off all the time and use the buffer version to
+ shut it off locally.
+
+ *'fsneednomatch'*
+'fsneednomatch'
+ string (default off)
+ local to buffer and global
+
+ Normally when doing a regular expression alteration of the path (see
+ {reg:} in 'fswitchdst' the pattern you're going to substitute the
+ value with must actually match in the string. When it doesn't matter
+ whether or not that the match actually takes place, you can set this
+ value.
+
+ If you do set this then the failure to match actually results in
+ nothing happening at all. So if the right filename exists in the same
+ directory as the one you're switching from then that's the one that
+ will be switched to.
+
+ Example:
+>
+
+ If the b:fswitchlocs is set to
+
+ reg:/src/include/,include
+
+ and
+
+ # This is the file we're editing
+ ~/code/program/myfile.c
+
+ # These choices exist for the header file
+ ~/code/program/myfile.h
+ ~/code/program/include/myfile.h
+<
+ Then the first substitution will result in the first header file being
+ chosen, not the second.
+
+==============================================================================
+ *fswitch-altcreate*
+6. "Creating" the Alternate File~
+
+If the file being switched to does not exist, and 'fsnonewfiles' has not been
+set, then it will be created as a new, unwritten buffer. If there are
+multiple possibilities here, FSwitch prefers the first possible match. For
+example if the current buffer has a filename called {/code/src/a/b/MyFile.cpp}
+and has the following set:
+>
+ let b:fswitchdst = 'h,hpp'
+ let b:fswitchlocs = 'reg:/src/include/,../include,../inc'
+<
+then the created filename will be {/code/include/a/b/MyFile.cpp}.
+
+As stated, this file hasn't actually been written to yet so you could easily
+delete the buffer and there's no harm done but you also may not be able to
+write the buffer very easily if the directory hierarchy doesn't yet exist. In
+this case, it's quite helpful to define a mapping for easily creating the
+directory for you:
+>
+ nmap md :!mkdir -p %:p:h
+<
+Then it's pretty easy to create the directory before writing the file.
+
+==============================================================================
+ *fswitch-mappings*
+7. Useful Mappings~
+
+I didn't bother putting mappings into the script directly as this might have
+caused conflicts and I don't know how to avoid those. I use the following
+mappings myself:
+
+ - Switch to the file and load it into the current window >
+ nmap of :FSHere
+<
+ - Switch to the file and load it into the window on the right >
+ nmap ol :FSRight
+<
+ - Switch to the file and load it into a new window split on the right >
+ nmap oL :FSSplitRight
+<
+ - Switch to the file and load it into the window on the left >
+ nmap oh :FSLeft
+<
+ - Switch to the file and load it into a new window split on the left >
+ nmap oH :FSSplitLeft
+<
+ - Switch to the file and load it into the window above >
+ nmap ok :FSAbove
+<
+ - Switch to the file and load it into a new window split above >
+ nmap oK :FSSplitAbove
+<
+ - Switch to the file and load it into the window below >
+ nmap oj :FSBelow
+<
+ - Switch to the file and load it into a new window split below >
+ nmap oJ :FSSplitBelow
+<
+==============================================================================
+ *FSwitch()*
+8. FSwitch()~
+
+The main work is done by the FSwitch() function. The reason it's documented
+here is because you can use it to do something more interesting if you wish.
+As it stands now, you get the "Split Above and Switch" functionality by
+calling FSwitch() like this:
+>
+ FSwitch('%', 'split \| wincmd k')
+<
+There's probably not much to stop anyone from doing something more interesting
+in the second argument. If this string is non-empty then it will be run
+through an |:execute| call.
+
+==============================================================================
+ *fswitch-getcompanion* *FSReturnCompanionFilenameString()*
+
+9. FSReturnCompanionFilenameString()~
+
+This function is used by |FSwitch()| to return the pathname to the preferred
+companion file. In this case, the file need not actually exist on the
+filesystem but would be the one created if you chose to do so. As an
+example:
+>
+ let path = FSReturnCompanionFilenameString('%')
+<
+The resultant path string contains the preferred companion file or nothing if
+no preferred file could be discovered.
+
+==============================================================================
+ *fswitch-getreadablecomp* *FSReturnReadableCompanionFilename()*
+
+10. FSReturnReadableCompanionFilename()~
+
+This function returns the companion file, but the companion file must be
+readable on the filesystem for it to be successfully returned.
+>
+ let path = FSReturnReadableCompanionFilename('%')
+<
+The resultant path string contains the preferred companion file or nothing if
+no preferred file could be found on the filesystem.
+
+In order to see what created the need for this function, see
+|fswitch-example3|.
+
+==============================================================================
+ *fswitch-defaults*
+11. The Default Settings~
+
+By default FSwitch handles {c}, {cc}, {cpp}, {cxx} and {C}. Note that the
+difference between {c} and {C} is only case sensitivity. This may mean that
+weird stuff happens if your OS is case insensitive.
+
+Also NOTE that when you use {cxx/hxx} or {C/H}, god kills a puppy.
+Consider that next time you want to do such a silly thing.
+
+For *.h files:
+>
+ let b:fswitchdst = 'c,cpp'
+ let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/,../src'
+<
+
+For *.hpp files:
+>
+ let b:fswitchdst = 'cpp'
+ let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/,../src'
+<
+
+For *.hxx files:
+>
+ let b:fswitchdst = 'cxx'
+ let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/,../src'
+<
+
+For *.H files:
+>
+ let b:fswitchdst = 'C'
+ let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/,../src'
+<
+
+For *.c
+>
+ let b:fswitchdst = 'h'
+ let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
+<
+
+For *.cpp
+>
+ let b:fswitchdst = 'hpp,h'
+ let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
+<
+
+For *.cxx
+>
+ let b:fswitchdst = 'hxx'
+ let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
+<
+
+For *.C
+>
+ let b:fswitchdst = 'H'
+ let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
+<
+
+==============================================================================
+ *fswitch-examples*
+12. Examples~
+ *fswitch-example1*
+Let's say you have a C++ codebase and it has the following properties (this
+level of insanity is a bit high but versions that are only slightly saner
+exist in real life):
+
+ - Source files with {.cpp}, {.cc} and {.C} extensions
+ - Header files with {.h} extensions
+ - Source files and header files in the same directory
+ - Source files in the {src} directory and include files in the
+ {include} directory
+ - Source files in the {src} directory and include files in the
+ {include/name/space} directory (i.e. subdirectories denoted by the
+ namespace).
+ - Source files in {src/name/space} and header files in
+ {include/name/space} (i.e. subdirectories denoted by the namespace).
+
+As a final part to this, the "new" way of doing things in this source tree is
+to put header files in a directory noted by namespace and to do the same with
+source files and to name source files with a {cpp} extension.
+
+In order to switch between files organized like this, you could specify the
+following:
+>
+ augroup mycppfiles
+ au!
+ au BufEnter *.h let b:fswitchdst = 'cpp,cc,C'
+ au BufEnter *.h let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/'
+ augroup END
+<
+Here the setting of b:fswitchdst to {cpp,cc,C} handles the different C++
+extensions, and prefers to use {cpp} and will create new files with that
+extension.
+
+The fswitchlocs setting allows for the following:
+
+ reg:/include/src/~
+
+ Take the pathname to the file in the current buffer and
+ substitute "src" for "include". This handles the following
+ possibilities:
+
+ - Files are in {include} and {src} respectively
+ - Files are in {include/name/space} and {src/name/space}
+ respectively
+
+ reg:/include.*/src/~
+
+ Take the pathname to the file in the current buffer and
+ substitute "src" for "include.*". This handles the following
+ possibility:
+
+ - Files are in {include/name/space} and {src} respectively
+
+ ./~
+ This one's a hiddden option. The default location is the
+ current directory already so we don't explicitly have to state
+ this, but it is the last possibility:
+
+ - Files are in the same directory
+
+ *fswitch-example2*
+Here we'll just show a quick example of making use of the globbing aspect of
+the system. Let's say you're working on a {cpp} file and you want to find the
+matching header file, and you have your destination and locations set to the
+following:
+>
+ let b:fswitchdst = 'h'
+ let b:fswitchlocs = 'reg:|src|include/**|'
+>
+then if you have the a file {src/MyFile.cpp} then this will find the file
+{include/name/space/MyFile.h}.
+
+ *fswitch-example3*
+At work I'm a Windows C++ programmer and at home I'm a OS X Objective-C
+programmer. There's a problem with this... C++ and Objective-C both use the
+same extension for header files ({.h}).
+
+At home I want to be able to use the XCode command line builder in the
+'makeprg' setting when I'm working on the code. I would like this to be set
+when I am on a {.m} file or its companion {.h} file. This is done with the
+following function:
+>
+ function! SetMakeForXCode(filename)
+ let isObjCFile = 0
+ let ext = expand(a:filename . ":e")
+ if ext == 'm' || ext == 'mm'
+ let isObjCFile = 1
+ elseif ext == 'h'
+ " Find the companion file
+ let companionfile = FSReturnReadableCompanionFilename('%')
+ " For some reason expand() doesn't work on the next line
+ let companionext = substitute(companionfile, '.*\.', '', '')
+ if companionext == 'm' || companionext == 'mm'
+ let isObjCFile = 1
+ endif
+ endif
+ if isObjCFile == 1
+ setl makeprg=xcodebuild\ -configuration\ Debug
+ endif
+ endfunction
+<
+Yup, this could have been easier by using the 'filetype' or using some sort of
+|grep| call but I wanted to use this particular hammer. :) I'll probably end
+up switching it to use the 'filetype' instead in the end...
+
+==============================================================================
+ *fswitch-trouble*
+13. TroubleShooting~
+ *fswitch-empty*
+You may get the following error:
+>
+ Alternate has evaluated to nothing. See :h fswitch-empty for more info.
+<
+It can happen... This is probably due to the fact that you've got a nicely
+strict set of rules for your locations. With |fswitch-reg| and
+|fswitch-ifrel| and |fswitch-ifabs| you can get rather specific about whether
+or not anything actually happens. If you aren't letting anything really
+happen, it's not going to happen and you're going to end up with an empty
+path.
+
+==============================================================================
+ *fswitch-changes*
+A. Change History~
+
+0.9.5
+ - Modified the autocommands to handle the myriad different
+ formulations of C++ files per Hong Xu's request. See:
+ https://github.com/derekwyatt/vim-fswitch/pull/3
+
+0.9.4
+ - Added fixes from Alexey Radkov to handle the 'sb' and 'spr' option
+ settings as well as the 'switchbuf' option. See:
+ https://github.com/derekwyatt/vim-fswitch/pull/2
+ - Made small fix to Alexey's fix to keep the 'sb' and 'spr' shadow
+ variable local to the buffer and unlet them at the end.
+ - I made a quick regression check on this update but did not test
+ Alexey's features directly - I trust that he did that :)
+
+0.9.3
+ - Made sure that there's a check for 7.0 (Thanks Timon Kelter)
+
+0.9.2
+ - Fix for the splitting commands (Thanks Michael Henry)
+
+0.9.1
+ - Added :ifrel (|fswitch_ifrel|)
+ - Added :ifabs (|fswitch_ifabs|)
+ - Added |FSReturnReadableCompanionFilename()|
+ - Added |FSReturnCompanionFilenameString()|
+ - Changed default settings for .h to use :ifrel instead of :rel
+ - Changed default settings for .c and .cpp to use :ifrel instead of
+ :rel
+
+0.9.0
+ - Initial release
+
+vim:tw=78:sts=8:ts=8:sw=8:noet:ft=help:
diff --git a/.vim/bundle/vim-fswitch/doc/tags b/.vim/bundle/vim-fswitch/doc/tags
new file mode 100644
index 0000000..7b28907
--- /dev/null
+++ b/.vim/bundle/vim-fswitch/doc/tags
@@ -0,0 +1,34 @@
+'fsneednomatch' fswitch.txt /*'fsneednomatch'*
+'fswitchdisablegloc' fswitch.txt /*'fswitchdisablegloc'*
+'fswitchdst' fswitch.txt /*'fswitchdst'*
+'fswitchfnames' fswitch.txt /*'fswitchfnames'*
+'fswitchlocs' fswitch.txt /*'fswitchlocs'*
+'fswitchnonewfiles' fswitch.txt /*'fswitchnonewfiles'*
+FSReturnCompanionFilenameString() fswitch.txt /*FSReturnCompanionFilenameString()*
+FSReturnReadableCompanionFilename() fswitch.txt /*FSReturnReadableCompanionFilename()*
+FSwitch() fswitch.txt /*FSwitch()*
+fswitch fswitch.txt /*fswitch*
+fswitch-about fswitch.txt /*fswitch-about*
+fswitch-altcreate fswitch.txt /*fswitch-altcreate*
+fswitch-changes fswitch.txt /*fswitch-changes*
+fswitch-configure fswitch.txt /*fswitch-configure*
+fswitch-contents fswitch.txt /*fswitch-contents*
+fswitch-copyright fswitch.txt /*fswitch-copyright*
+fswitch-defaults fswitch.txt /*fswitch-defaults*
+fswitch-empty fswitch.txt /*fswitch-empty*
+fswitch-example1 fswitch.txt /*fswitch-example1*
+fswitch-example2 fswitch.txt /*fswitch-example2*
+fswitch-example3 fswitch.txt /*fswitch-example3*
+fswitch-examples fswitch.txt /*fswitch-examples*
+fswitch-features fswitch.txt /*fswitch-features*
+fswitch-getcompanion fswitch.txt /*fswitch-getcompanion*
+fswitch-getreadablecomp fswitch.txt /*fswitch-getreadablecomp*
+fswitch-mappings fswitch.txt /*fswitch-mappings*
+fswitch-setup fswitch.txt /*fswitch-setup*
+fswitch-trouble fswitch.txt /*fswitch-trouble*
+fswitch.txt fswitch.txt /*fswitch.txt*
+fswitch_abs fswitch.txt /*fswitch_abs*
+fswitch_ifabs fswitch.txt /*fswitch_ifabs*
+fswitch_ifrel fswitch.txt /*fswitch_ifrel*
+fswitch_reg fswitch.txt /*fswitch_reg*
+fswitch_rel fswitch.txt /*fswitch_rel*
diff --git a/.vim/bundle/vim-fswitch/plugin/fswitch.vim b/.vim/bundle/vim-fswitch/plugin/fswitch.vim
new file mode 100644
index 0000000..a0396c8
--- /dev/null
+++ b/.vim/bundle/vim-fswitch/plugin/fswitch.vim
@@ -0,0 +1,364 @@
+" ============================================================================
+" File: fswitch.vim
+"
+" Description: Vim global plugin that provides decent companion source file
+" switching
+"
+" Maintainer: Derek Wyatt
+"
+" Last Change: March 23rd 2009
+"
+" License: This program is free software. It comes without any warranty,
+" to the extent permitted by applicable law. You can redistribute
+" it and/or modify it under the terms of the Do What The Fuck You
+" Want To Public License, Version 2, as published by Sam Hocevar.
+" See http://sam.zoy.org/wtfpl/COPYING for more details.
+" ============================================================================
+
+if exists("g:disable_fswitch")
+ finish
+endif
+
+if v:version < 700
+ echoerr "FSwitch requires Vim 7.0 or higher."
+ finish
+endif
+
+" Version
+let s:fswitch_version = '0.9.5'
+
+" Get the path separator right
+let s:os_slash = &ssl == 0 && (has("win16") || has("win32") || has("win64")) ? '\' : '/'
+
+" Default locations - appended to buffer locations unless otherwise specified
+let s:fswitch_global_locs = '.' . s:os_slash
+
+"
+" s:SetVariables
+"
+" There are two variables that need to be set in the buffer in order for things
+" to work correctly. Because we're using an autocmd to set things up we need to
+" be sure that the user hasn't already set them for us explicitly so we have
+" this function just to check and make sure. If the user's autocmd runs after
+" ours then they will override the value anyway.
+"
+function! s:SetVariables(dst, locs)
+ if !exists("b:fswitchdst")
+ let b:fswitchdst = a:dst
+ endif
+ if !exists("b:fswitchlocs")
+ let b:fswitchlocs = a:locs
+ endif
+endfunction
+
+"
+" s:FSGetLocations
+"
+" Return the list of possible locations
+"
+function! s:FSGetLocations()
+ let locations = []
+ if exists("b:fswitchlocs")
+ let locations = split(b:fswitchlocs, ',')
+ endif
+ if !exists("b:fsdisablegloc") || b:fsdisablegloc == 0
+ let locations += split(s:fswitch_global_locs, ',')
+ endif
+
+ return locations
+endfunction
+
+"
+" s:FSGetExtensions
+"
+" Return the list of destination extensions
+"
+function! s:FSGetExtensions()
+ return split(b:fswitchdst, ',')
+endfunction
+
+"
+" s:FSGetFilenameMutations
+"
+" Return the list of possible filename mutations
+"
+function! s:FSGetFilenameMutations()
+ if !exists("b:fswitchfnames")
+ " For backward-compatibility out default mutation is an identity.
+ return ['/^//']
+ else
+ return split(b:fswitchfnames, ',')
+ endif
+endfunction
+
+"
+" s:FSGetMustMatch
+"
+" Return a boolean on whether or not the regex must match
+"
+function! s:FSGetMustMatch()
+ let mustmatch = 1
+ if exists("b:fsneednomatch") && b:fsneednomatch != 0
+ let mustmatch = 0
+ endif
+
+ return mustmatch
+endfunction
+
+"
+" s:FSGetFullPathToDirectory
+"
+" Given the filename, return the fully qualified directory portion
+"
+function! s:FSGetFullPathToDirectory(filename)
+ return expand(a:filename . ':p:h')
+endfunction
+
+"
+" s:FSGetFileExtension
+"
+" Given the filename, returns the extension
+"
+function! s:FSGetFileExtension(filename)
+ return expand(a:filename . ':e')
+endfunction
+
+"
+" s:FSGetFileNameWithoutExtension
+"
+" Given the filename, returns just the file name without the path or extension
+"
+function! s:FSGetFileNameWithoutExtension(filename)
+ return expand(a:filename . ':t:r')
+endfunction
+
+"
+" s:FSMutateFilename
+"
+" Takes a filename and a filename mutation directive and applies the mutation
+" to it.
+function! s:FSMutateFilename(filename, directive)
+ let separator = strpart(a:directive, 0, 1)
+ let dirparts = split(strpart(a:directive, 1), separator)
+ if len(dirparts) < 2 || len(dirparts) > 3
+ throw 'Bad mutation directive "' . a:directive . '".'
+ else
+ let flags = ''
+ if len(dirparts) == 3
+ let flags = dirparts[2]
+ endif
+ return substitute(a:filename, dirparts[0], dirparts[1], flags)
+ endif
+endfunction
+
+"
+" s:FSGetAlternateFilename
+"
+" Takes the path, name and extension of the file in the current buffer and
+" applies the location to it. If the location is a regular expression pattern
+" then it will split that up and apply it accordingly. If the location pattern
+" is actually an explicit relative path or an implicit one (default) then it
+" will simply apply that to the file directly.
+"
+function! s:FSGetAlternateFilename(filepath, filename, newextension, location, mustmatch)
+ let parts = split(a:location, ':')
+ let cmd = 'rel'
+ let directive = parts[0]
+ if len(parts) == 2
+ let cmd = parts[0]
+ let directive = parts[1]
+ endif
+ if cmd == 'reg' || cmd == 'ifrel' || cmd == 'ifabs'
+ if strlen(directive) < 3
+ throw 'Bad directive "' . a:location . '".'
+ else
+ let separator = strpart(directive, 0, 1)
+ let dirparts = split(strpart(directive, 1), separator)
+ if len(dirparts) < 2 || len(dirparts) > 3
+ throw 'Bad directive "' . a:location . '".'
+ else
+ let part1 = dirparts[0]
+ let part2 = dirparts[1]
+ let flags = ''
+ if len(dirparts) == 3
+ let flags = dirparts[2]
+ endif
+ if cmd == 'reg'
+ if a:mustmatch == 1 && match(a:filepath, part1) == -1
+ let path = ""
+ else
+ let path = substitute(a:filepath, part1, part2, flags) . s:os_slash .
+ \ a:filename . '.' . a:newextension
+ endif
+ elseif cmd == 'ifrel'
+ if match(a:filepath, part1) == -1
+ let path = ""
+ else
+ let path = a:filepath . s:os_slash . part2 .
+ \ s:os_slash . a:filename . '.' . a:newextension
+ endif
+ elseif cmd == 'ifabs'
+ if match(a:filepath, part1) == -1
+ let path = ""
+ else
+ let path = part2 . s:os_slash . a:filename . '.' . a:newextension
+ endif
+ endif
+ endif
+ endif
+ elseif cmd == 'rel'
+ let path = a:filepath . s:os_slash . directive . s:os_slash . a:filename . '.' . a:newextension
+ elseif cmd == 'abs'
+ let path = directive . s:os_slash . a:filename . '.' . a:newextension
+ endif
+
+ return simplify(path)
+endfunction
+
+"
+" s:FSReturnCompanionFilename
+"
+" This function will return a path that is the best candidate for the companion
+" file to switch to. If mustBeReadable == 1 when then the companion file will
+" only be returned if it is readable on the filesystem, otherwise it will be
+" returned so long as it is non-empty.
+"
+function! s:FSReturnCompanionFilename(filename, mustBeReadable)
+ let fullpath = s:FSGetFullPathToDirectory(a:filename)
+ let ext = s:FSGetFileExtension(a:filename)
+ let justfile = s:FSGetFileNameWithoutExtension(a:filename)
+ let extensions = s:FSGetExtensions()
+ let filenameMutations = s:FSGetFilenameMutations()
+ let locations = s:FSGetLocations()
+ let mustmatch = s:FSGetMustMatch()
+ let newpath = ''
+ for currentExt in extensions
+ for loc in locations
+ for filenameMutation in filenameMutations
+ let mutatedFilename = s:FSMutateFilename(justfile, filenameMutation)
+ let newpath = s:FSGetAlternateFilename(fullpath, mutatedFilename, currentExt, loc, mustmatch)
+ if a:mustBeReadable == 0 && newpath != ''
+ return newpath
+ elseif a:mustBeReadable == 1
+ let newpath = glob(newpath)
+ if filereadable(newpath)
+ return newpath
+ endif
+ endif
+ endfor
+ endfor
+ endfor
+
+ return newpath
+endfunction
+
+"
+" FSReturnReadableCompanionFilename
+"
+" This function will return a path that is the best candidate for the companion
+" file to switch to, so long as that file actually exists on the filesystem and
+" is readable.
+"
+function! FSReturnReadableCompanionFilename(filename)
+ return s:FSReturnCompanionFilename(a:filename, 1)
+endfunction
+
+"
+" FSReturnCompanionFilenameString
+"
+" This function will return a path that is the best candidate for the companion
+" file to switch to. The file does not need to actually exist on the
+" filesystem in order to qualify as a proper companion.
+"
+function! FSReturnCompanionFilenameString(filename)
+ return s:FSReturnCompanionFilename(a:filename, 0)
+endfunction
+
+"
+" FSwitch
+"
+" This is the only externally accessible function and is what we use to switch
+" to the alternate file.
+"
+function! FSwitch(filename, precmd)
+ if !exists("b:fswitchdst") || strlen(b:fswitchdst) == 0
+ throw 'b:fswitchdst not set - read :help fswitch'
+ endif
+ if (!exists("b:fswitchlocs") || strlen(b:fswitchlocs) == 0) &&
+ \ (!exists("b:fsdisablegloc") || b:fsdisablegloc == 0)
+ throw "There are no locations defined (see :h fswitchlocs and :h fsdisablegloc)"
+ endif
+ let newpath = FSReturnReadableCompanionFilename(a:filename)
+ let openfile = 1
+ if !filereadable(newpath)
+ if exists("b:fsnonewfiles") || exists("g:fsnonewfiles")
+ let openfile = 0
+ else
+ let newpath = FSReturnCompanionFilenameString(a:filename)
+ endif
+ endif
+ if &switchbuf =~ "^use"
+ let i = 1
+ let bufnum = winbufnr(i)
+ while bufnum != -1
+ let filename = fnamemodify(bufname(bufnum), ':p')
+ if filename == newpath
+ execute ":sbuffer " . filename
+ return
+ endif
+ let i += 1
+ let bufnum = winbufnr(i)
+ endwhile
+ endif
+ if openfile == 1
+ if newpath != ''
+ if strlen(a:precmd) != 0
+ execute a:precmd
+ endif
+ let s:fname = fnameescape(newpath)
+
+ if (strlen(bufname(s:fname))) > 0
+ execute 'buffer ' . s:fname
+ else
+ execute 'edit ' . s:fname
+ endif
+ else
+ echoerr "Alternate has evaluated to nothing. See :h fswitch-empty for more info."
+ endif
+ else
+ echoerr "No alternate file found. 'fsnonewfiles' is set which denies creation."
+ endif
+endfunction
+
+"
+" The autocmds we set up to set up the buffer variables for us.
+"
+augroup fswitch_au_group
+ au!
+ au BufEnter *.c call s:SetVariables('h', 'reg:/src/include/,reg:|src|include/**|,ifrel:|/src/|../include|')
+ au BufEnter *.cc call s:SetVariables('hh', 'reg:/src/include/,reg:|src|include/**|,ifrel:|/src/|../include|')
+ au BufEnter *.cpp call s:SetVariables('hpp,h', 'reg:/src/include/,reg:|src|include/**|,ifrel:|/src/|../include|')
+ au BufEnter *.cxx call s:SetVariables('hxx', 'reg:/src/include/,reg:|src|include/**|,ifrel:|/src/|../include|')
+ au BufEnter *.C call s:SetVariables('H', 'reg:/src/include/,reg:|src|include/**|,ifrel:|/src/|../include|')
+ au BufEnter *.m call s:SetVariables('h', 'reg:/src/include/,reg:|src|include/**|,ifrel:|/src/|../include|')
+ au BufEnter *.h call s:SetVariables('c,cpp,m', 'reg:/include/src/,reg:/include.*/src/,ifrel:|/include/|../src|')
+ au BufEnter *.hh call s:SetVariables('cc', 'reg:/include/src/,reg:/include.*/src/,ifrel:|/include/|../src|')
+ au BufEnter *.hpp call s:SetVariables('cpp', 'reg:/include/src/,reg:/include.*/src/,ifrel:|/include/|../src|')
+ au BufEnter *.hxx call s:SetVariables('cxx', 'reg:/include/src/,reg:/include.*/src/,ifrel:|/include/|../src|')
+ au BufEnter *.H call s:SetVariables('C', 'reg:/include/src/,reg:/include.*/src/,ifrel:|/include/|../src|')
+augroup END
+
+"
+" The mappings used to do the good work
+"
+com! FSHere :call FSwitch('%', '')
+com! FSRight :call FSwitch('%', 'wincmd l')
+com! FSSplitRight :call FSwitch('%', 'let curspr=&spr | set nospr | vsplit | wincmd l | if curspr | set spr | endif')
+com! FSLeft :call FSwitch('%', 'wincmd h')
+com! FSSplitLeft :call FSwitch('%', 'let curspr=&spr | set nospr | vsplit | if curspr | set spr | endif')
+com! FSAbove :call FSwitch('%', 'wincmd k')
+com! FSSplitAbove :call FSwitch('%', 'let cursb=&sb | set nosb | split | if cursb | set sb | endif')
+com! FSBelow :call FSwitch('%', 'wincmd j')
+com! FSSplitBelow :call FSwitch('%', 'let cursb=&sb | set nosb | split | wincmd j | if cursb | set sb | endif')
+com! FSTab :call FSwitch('%', 'tabedit')
+
diff --git a/.vim/bundle/vim-gnupg/plugin/gnupg.vim b/.vim/bundle/vim-gnupg/plugin/gnupg.vim
new file mode 100644
index 0000000..da5768e
--- /dev/null
+++ b/.vim/bundle/vim-gnupg/plugin/gnupg.vim
@@ -0,0 +1,1459 @@
+" Name: gnupg.vim
+" Last Change: 2018 Jun 22
+" Maintainer: James McCoy
+" Original Author: Markus Braun
+" Summary: Vim plugin for transparent editing of gpg encrypted files.
+" License: This program is free software; you can redistribute it and/or
+" modify it under the terms of the GNU General Public License
+" as published by the Free Software Foundation; either version
+" 2 of the License, or (at your option) any later version.
+" See http://www.gnu.org/copyleft/gpl-2.0.txt
+"
+" Section: Documentation {{{1
+"
+" Description: {{{2
+"
+" This script implements transparent editing of gpg encrypted files. The
+" filename must have a ".gpg", ".pgp" or ".asc" suffix. When opening such
+" a file the content is decrypted, when opening a new file the script will
+" ask for the recipients of the encrypted file. The file content will be
+" encrypted to all recipients before it is written. The script turns off
+" viminfo, swapfile, and undofile to increase security.
+"
+" Installation: {{{2
+"
+" Copy the gnupg.vim file to the $HOME/.vim/plugin directory.
+" Refer to ':help add-plugin', ':help add-global-plugin' and ':help
+" runtimepath' for more details about Vim plugins.
+"
+" From "man 1 gpg-agent":
+"
+" ...
+" You should always add the following lines to your .bashrc or whatever
+" initialization file is used for all shell invocations:
+"
+" GPG_TTY=`tty`
+" export GPG_TTY
+"
+" It is important that this environment variable always reflects the out‐
+" put of the tty command. For W32 systems this option is not required.
+" ...
+"
+" Most distributions provide software to ease handling of gpg and gpg-agent.
+" Examples are keychain or seahorse.
+"
+" If there are specific actions that should take place when editing a
+" GnuPG-managed buffer, an autocmd for the User event and GnuPG pattern can
+" be defined. For example, the following will set 'textwidth' to 72 for all
+" GnuPG-encrypted buffers:
+"
+" autocmd User GnuPG setl textwidth=72
+"
+" This will be triggered before any BufRead or BufNewFile autocmds, and
+" therefore will not take precedence over settings specific to any filetype
+" that may get set.
+"
+" Commands: {{{2
+"
+" :GPGEditRecipients
+" Opens a scratch buffer to change the list of recipients. Recipients that
+" are unknown (not in your public key) are highlighted and have
+" a prepended "!". Closing the buffer makes the changes permanent.
+"
+" :GPGViewRecipients
+" Prints the list of recipients.
+"
+" :GPGEditOptions
+" Opens a scratch buffer to change the options for encryption (symmetric,
+" asymmetric, signing). Closing the buffer makes the changes permanent.
+" WARNING: There is no check of the entered options, so you need to know
+" what you are doing.
+"
+" :GPGViewOptions
+" Prints the list of options.
+"
+" Variables: {{{2
+"
+" g:GPGExecutable
+" If set used as gpg executable. If unset, defaults to
+" "gpg --trust-model always" if "gpg" is available, falling back to
+" "gpg2 --trust-model always" if not.
+"
+" g:GPGUseAgent
+" If set to 0 a possible available gpg-agent won't be used. Defaults to 1.
+"
+" g:GPGPreferSymmetric
+" If set to 1 symmetric encryption is preferred for new files. Defaults to 0.
+"
+" g:GPGPreferArmor
+" If set to 1 armored data is preferred for new files. Defaults to 0
+" unless a "*.asc" file is being edited.
+"
+" g:GPGPreferSign
+" If set to 1 signed data is preferred for new files. Defaults to 0.
+"
+" g:GPGDefaultRecipients
+" If set, these recipients are used as defaults when no other recipient is
+" defined. This variable is a Vim list. Default is unset.
+"
+" g:GPGPossibleRecipients
+" If set, these contents are loaded into the recipients dialog. This
+" allows to add commented lines with possible recipients to the list,
+" which can be uncommented to select the actual recipients. Default is
+" unset. Example:
+"
+" let g:GPGPossibleRecipients=[
+" \"Example User ",
+" \"Other User "
+" \]
+"
+"
+" g:GPGUsePipes
+" If set to 1, use pipes instead of temporary files when interacting with
+" gnupg. When set to 1, this can cause terminal-based gpg agents to not
+" display correctly when prompting for passwords. Defaults to 0.
+"
+" g:GPGHomedir
+" If set, specifies the directory that will be used for GPG's homedir.
+" This corresponds to gpg's --homedir option. This variable is a Vim
+" string. Default is unset.
+"
+" g:GPGFilePattern
+" If set, overrides the default set of file patterns that determine
+" whether this plugin will be activated. Defaults to
+" '*.\(gpg\|asc\|pgp\)'.
+"
+" Known Issues: {{{2
+"
+" In some cases gvim can't decrypt files
+
+" This is caused by the fact that a running gvim has no TTY and thus gpg is
+" not able to ask for the passphrase by itself. This is a problem for Windows
+" and Linux versions of gvim and could not be solved unless a "terminal
+" emulation" is implemented for gvim. To circumvent this you have to use any
+" combination of gpg-agent and a graphical pinentry program:
+"
+" - gpg-agent only:
+" you need to provide the passphrase for the needed key to gpg-agent
+" in a terminal before you open files with gvim which require this key.
+"
+" - pinentry only:
+" you will get a popup window every time you open a file that needs to
+" be decrypted.
+"
+" - gpgagent and pinentry:
+" you will get a popup window the first time you open a file that
+" needs to be decrypted.
+"
+" If you're using Vim <7.4.959, after the plugin runs any external command,
+" Vim will no longer be able to yank to/paste from the X clipboard or
+" primary selections. This is caused by a workaround for a different bug
+" where Vim no longer recognizes the key codes for keys such as the arrow
+" keys after running GnuPG. See the discussion at
+" https://github.com/jamessan/vim-gnupg/issues/36 for more details.
+"
+" Credits: {{{2
+"
+" - Mathieu Clabaut for inspirations through his vimspell.vim script.
+" - Richard Bronosky for patch to enable ".pgp" suffix.
+" - Erik Remmelzwaal for patch to enable windows support and patient beta
+" testing.
+" - Lars Becker for patch to make gpg2 working.
+" - Thomas Arendsen Hein for patch to convert encoding of gpg output.
+" - Karl-Heinz Ruskowski for patch to fix unknown recipients and trust model
+" and patient beta testing.
+" - Giel van Schijndel for patch to get GPG_TTY dynamically.
+" - Sebastian Luettich for patch to fix issue with symmetric encryption an set
+" recipients.
+" - Tim Swast for patch to generate signed files.
+" - James Vega for patches for better '*.asc' handling, better filename
+" escaping and better handling of multiple keyrings.
+"
+" Section: Plugin header {{{1
+
+" guard against multiple loads {{{2
+if (exists("g:loaded_gnupg") || &cp || exists("#GnuPG"))
+ finish
+endif
+let g:loaded_gnupg = '2.6.1-dev'
+let s:GPGInitRun = 0
+
+" check for correct vim version {{{2
+if (v:version < 702)
+ echohl ErrorMsg | echo 'plugin gnupg.vim requires Vim version >= 7.2' | echohl None
+ finish
+endif
+
+" Section: Autocmd setup {{{1
+
+if (!exists("g:GPGFilePattern"))
+ let g:GPGFilePattern = '*.\(gpg\|asc\|pgp\)'
+endif
+
+augroup GnuPG
+ autocmd!
+
+ " do the decryption
+ exe "autocmd BufReadCmd " . g:GPGFilePattern . " call s:GPGInit(1) |" .
+ \ " call s:GPGDecrypt(1)"
+ exe "autocmd FileReadCmd " . g:GPGFilePattern . " call s:GPGInit(0) |" .
+ \ " call s:GPGDecrypt(0)"
+
+ " convert all text to encrypted text before writing
+ " We check for GPGCorrespondingTo to avoid triggering on writes in GPG Options/Recipient windows
+ exe "autocmd BufWriteCmd,FileWriteCmd " . g:GPGFilePattern . " if !exists('b:GPGCorrespondingTo') |" .
+ \ " call s:GPGInit(0) |" .
+ \ " call s:GPGEncrypt() |" .
+ \ " endif"
+augroup END
+
+" Section: Constants {{{1
+
+let s:GPGMagicString = "\t \t"
+let s:keyPattern = '\%(0x\)\=[[:xdigit:]]\{8,16}'
+
+" Section: Highlight setup {{{1
+
+highlight default link GPGWarning WarningMsg
+highlight default link GPGError ErrorMsg
+highlight default link GPGHighlightUnknownRecipient ErrorMsg
+
+" Section: Functions {{{1
+
+" Function: s:shellescape(s[, dict]) {{{2
+"
+" Calls shellescape(), also taking into account 'shellslash'
+" when on Windows and using $COMSPEC as the shell.
+"
+" Recognized keys are:
+" special - Passed through as special argument for Vim's shellescape()
+" cygpath - When true and s:useCygpath is true, adjust the path to work with
+" Gpg4win from cygwin
+"
+" Returns: shellescaped string
+"
+function s:shellescape(s, ...)
+ let opts = a:0 ? a:1 : {}
+ let special = get(opts, 'special', 0)
+ let cygpath = get(opts, 'cygpath', 0)
+ let s = a:s
+ if cygpath && s:useCygpath
+ let s = matchstr(system('cygpath -am '.shellescape(s)), '[^\x0a\x0d]*')
+ endif
+ if exists('+shellslash') && &shell == $COMSPEC
+ let ssl = &shellslash
+ set noshellslash
+
+ let escaped = shellescape(s, special)
+
+ let &shellslash = ssl
+ else
+ let escaped = shellescape(s, special)
+ endif
+
+ return escaped
+endfunction
+
+" Function: s:unencrypted() {{{2
+"
+" Determines if the buffer corresponds to an existing, unencrypted file and,
+" if so, warns the user that GPG functionality has been disabled.
+"
+" Returns: true if current buffer corresponds to an existing, unencrypted file
+function! s:unencrypted()
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ return 1
+ endif
+
+ return 0
+endfunction
+
+" Function: s:GPGInit(bufread) {{{2
+"
+" initialize the plugin
+" The bufread argument specifies whether this was called due to BufReadCmd
+"
+function s:GPGInit(bufread)
+ call s:GPGDebug(3, printf(">>>>>>>> Entering s:GPGInit(%d)", a:bufread))
+
+ " For FileReadCmd, we're reading the contents into another buffer. If that
+ " buffer is also destined to be encrypted, then these settings will have
+ " already been set, otherwise don't set them since it limits the
+ " functionality of the cleartext buffer.
+ if a:bufread
+ " we don't want a swap file, as it writes unencrypted data to disk
+ setl noswapfile
+
+ " if persistent undo is present, disable it for this buffer
+ if exists('+undofile')
+ setl noundofile
+ endif
+
+ " first make sure nothing is written to ~/.viminfo while editing
+ " an encrypted file.
+ set viminfo=
+ endif
+
+ " the rest only has to be run once
+ if s:GPGInitRun
+ return
+ endif
+
+ " check what gpg command to use
+ if (!exists("g:GPGExecutable"))
+ if executable("gpg")
+ let g:GPGExecutable = "gpg --trust-model always"
+ else
+ let g:GPGExecutable = "gpg2 --trust-model always"
+ endif
+ endif
+
+ " check if gpg-agent is allowed
+ if (!exists("g:GPGUseAgent"))
+ let g:GPGUseAgent = 1
+ endif
+
+ " check if symmetric encryption is preferred
+ if (!exists("g:GPGPreferSymmetric"))
+ let g:GPGPreferSymmetric = 0
+ endif
+
+ " check if signed files are preferred
+ if (!exists("g:GPGPreferSign"))
+ let g:GPGPreferSign = 0
+ endif
+
+ " start with empty default recipients if none is defined so far
+ if (!exists("g:GPGDefaultRecipients"))
+ let g:GPGDefaultRecipients = []
+ endif
+
+ if (!exists("g:GPGPossibleRecipients"))
+ let g:GPGPossibleRecipients = []
+ endif
+
+
+ " prefer not to use pipes since it can garble gpg agent display
+ if (!exists("g:GPGUsePipes"))
+ let g:GPGUsePipes = 0
+ endif
+
+ " allow alternate gnupg homedir
+ if (!exists('g:GPGHomedir'))
+ let g:GPGHomedir = ''
+ endif
+
+ " print version
+ call s:GPGDebug(1, "gnupg.vim ". g:loaded_gnupg)
+
+ let s:GPGCommand = g:GPGExecutable
+
+ " don't use tty in gvim except for windows: we get their a tty for free.
+ " FIXME find a better way to avoid an error.
+ " with this solution only --use-agent will work
+ if (has("gui_running") && !has("gui_win32"))
+ let s:GPGCommand .= " --no-tty"
+ endif
+
+ " setup shell environment for unix and windows
+ let s:shellredirsave = &shellredir
+ let s:shellsave = &shell
+ let s:shelltempsave = &shelltemp
+ " noshelltemp isn't currently supported on Windows, but it doesn't cause any
+ " errors and this future proofs us against requiring changes if Windows
+ " gains noshelltemp functionality
+ let s:shelltemp = !g:GPGUsePipes
+ if (has("unix"))
+ " unix specific settings
+ let s:shellredir = ">%s 2>&1"
+ let s:shell = '/bin/sh'
+ let s:stderrredirnull = '2>/dev/null'
+ else
+ " windows specific settings
+ let s:shellredir = '>%s'
+ let s:shell = &shell
+ let s:stderrredirnull = '2>nul'
+ endif
+
+ call s:GPGDebug(3, "shellredirsave: " . s:shellredirsave)
+ call s:GPGDebug(3, "shellsave: " . s:shellsave)
+ call s:GPGDebug(3, "shelltempsave: " . s:shelltempsave)
+
+ call s:GPGDebug(3, "shell: " . s:shell)
+ call s:GPGDebug(3, "shellcmdflag: " . &shellcmdflag)
+ call s:GPGDebug(3, "shellxquote: " . &shellxquote)
+ call s:GPGDebug(3, "shellredir: " . s:shellredir)
+ call s:GPGDebug(3, "stderrredirnull: " . s:stderrredirnull)
+
+ call s:GPGDebug(3, "shell implementation: " . resolve(s:shell))
+
+ " find the supported algorithms
+ let output = s:GPGSystem({ 'level': 2, 'args': '--version' })
+
+ let gpgversion = substitute(output, '^gpg (GnuPG) \([0-9]\+\.\d\+\).*', '\1', '')
+ let s:GPGPubkey = substitute(output, ".*Pubkey: \\(.\\{-}\\)\n.*", "\\1", "")
+ let s:GPGCipher = substitute(output, ".*Cipher: \\(.\\{-}\\)\n.*", "\\1", "")
+ let s:GPGHash = substitute(output, ".*Hash: \\(.\\{-}\\)\n.*", "\\1", "")
+ let s:GPGCompress = substitute(output, ".*Compress.\\{-}: \\(.\\{-}\\)\n.*", "\\1", "")
+ let s:GPGHome = matchstr(output, '.*Home: \zs.\{-}\ze\r\=\n')
+
+ let s:useCygpath = 0
+ if has('win32unix') && s:GPGHome =~ '\a:[/\\]'
+ call s:GPGDebug(1, 'Enabling use of cygpath')
+ let s:useCygpath = 1
+ endif
+
+ " determine if gnupg can use the gpg-agent
+ if (str2float(gpgversion) >= 2.1 || (exists("$GPG_AGENT_INFO") && g:GPGUseAgent == 1))
+ if (!exists("$GPG_TTY") && !has("gui_running"))
+ " Need to determine the associated tty by running a command in the
+ " shell. We can't use system() here because that doesn't run in a shell
+ " connected to a tty, so it's rather useless.
+ "
+ " Save/restore &modified so the buffer isn't incorrectly marked as
+ " modified just by detecting the correct tty value.
+ " Do the &undolevels dance so the :read and :delete don't get added into
+ " the undo tree, as the user needn't be aware of these.
+ let [mod, levels] = [&l:modified, &undolevels]
+ set undolevels=-1
+ silent read !tty
+ let $GPG_TTY = getline('.')
+ silent delete
+ let [&l:modified, &undolevels] = [mod, levels]
+ " redraw is needed since we're using silent to run !tty, c.f. :help :!
+ redraw!
+ if (v:shell_error)
+ let $GPG_TTY = ""
+ echohl GPGWarning
+ echom "$GPG_TTY is not set and the `tty` command failed! gpg-agent might not work."
+ echohl None
+ endif
+ endif
+ let s:GPGCommand .= " --use-agent"
+ else
+ let s:GPGCommand .= " --no-use-agent"
+ endif
+
+ call s:GPGDebug(2, "public key algorithms: " . s:GPGPubkey)
+ call s:GPGDebug(2, "cipher algorithms: " . s:GPGCipher)
+ call s:GPGDebug(2, "hashing algorithms: " . s:GPGHash)
+ call s:GPGDebug(2, "compression algorithms: " . s:GPGCompress)
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGInit()")
+ let s:GPGInitRun = 1
+endfunction
+
+
+" Function: s:GPGDecrypt(bufread) {{{2
+"
+" decrypt the buffer and find all recipients of the encrypted file
+" The bufread argument specifies whether this was called due to BufReadCmd
+"
+function s:GPGDecrypt(bufread)
+ call s:GPGDebug(3, printf(">>>>>>>> Entering s:GPGDecrypt(%d)", a:bufread))
+
+ " get the filename of the current buffer
+ let filename = resolve(expand(":p"))
+
+ " clear GPGRecipients and GPGOptions
+ if type(g:GPGDefaultRecipients) == type([])
+ let b:GPGRecipients = copy(g:GPGDefaultRecipients)
+ else
+ let b:GPGRecipients = []
+ echohl GPGWarning
+ echom "g:GPGDefaultRecipients is not a Vim list, please correct this in your vimrc!"
+ echohl None
+ endif
+ let b:GPGOptions = []
+
+ " file name minus extension
+ let autocmd_filename = fnamemodify(filename, ':r')
+
+ " File doesn't exist yet, so nothing to decrypt
+ if !filereadable(filename)
+ if !a:bufread
+ redraw!
+ echohl GPGError
+ echom "E484: Can't open file" filename
+ echohl None
+ return
+ endif
+
+ " Allow the user to define actions for GnuPG buffers
+ silent doautocmd User GnuPG
+ silent execute ':doautocmd BufNewFile ' . fnameescape(autocmd_filename)
+ call s:GPGDebug(2, 'called BufNewFile autocommand for ' . autocmd_filename)
+
+ set buftype=acwrite
+ " Remove the buffer name ...
+ silent 0file
+ " ... so we can force it to be absolute
+ exe 'silent file' fnameescape(filename)
+
+ " This is a new file, so force the user to edit the recipient list if
+ " they open a new file and public keys are preferred
+ if (g:GPGPreferSymmetric == 0)
+ call s:GPGEditRecipients()
+ endif
+
+ return
+ endif
+
+ " Only let this if the file actually exists, otherwise GPG functionality
+ " will be disabled when editing a buffer that doesn't yet have a backing
+ " file
+ let b:GPGEncrypted = 0
+
+ " find the recipients of the file
+ let cmd = { 'level': 3 }
+ let cmd.args = '--verbose --decrypt --list-only --dry-run --no-use-agent --logger-fd 1 ' . s:shellescape(filename, { 'cygpath': 1 })
+ let output = s:GPGSystem(cmd)
+
+ " Suppress the "N more lines" message when editing a file, not when reading
+ " the contents of a file into a buffer
+ let silent = a:bufread ? 'silent ' : ''
+
+ let asymmPattern = 'gpg: public key is ' . s:keyPattern
+ " check if the file is symmetric/asymmetric encrypted
+ if (match(output, "gpg: encrypted with [[:digit:]]\\+ passphrase") >= 0)
+ " file is symmetric encrypted
+ let b:GPGEncrypted = 1
+ call s:GPGDebug(1, "this file is symmetric encrypted")
+
+ let b:GPGOptions += ["symmetric"]
+
+ " find the used cipher algorithm
+ let cipher = substitute(output, ".*gpg: \\([^ ]\\+\\) encrypted data.*", "\\1", "")
+ if (match(s:GPGCipher, "\\<" . cipher . "\\>") >= 0)
+ let b:GPGOptions += ["cipher-algo " . cipher]
+ call s:GPGDebug(1, "cipher-algo is " . cipher)
+ else
+ echohl GPGWarning
+ echom "The cipher " . cipher . " is not known by the local gpg command. Using default!"
+ echo
+ echohl None
+ endif
+ elseif (match(output, asymmPattern) >= 0)
+ " file is asymmetric encrypted
+ let b:GPGEncrypted = 1
+ call s:GPGDebug(1, "this file is asymmetric encrypted")
+
+ let b:GPGOptions += ["encrypt"]
+
+ " find the used public keys
+ let start = match(output, asymmPattern)
+ while (start >= 0)
+ let start = start + strlen("gpg: public key is ")
+ let recipient = matchstr(output, s:keyPattern, start)
+ call s:GPGDebug(1, "recipient is " . recipient)
+ " In order to support anonymous communication, GnuPG allows eliding
+ " information in the encryption metadata specifying what keys the file
+ " was encrypted to (c.f., --throw-keyids and --hidden-recipient). In
+ " that case, the recipient(s) will be listed as having used a key of all
+ " zeroes.
+ " Since this will obviously never actually be in a keyring, only try to
+ " convert to an ID or add to the recipients list if it's not a hidden
+ " recipient.
+ if recipient !~? '^0x0\+$'
+ let name = s:GPGNameToID(recipient)
+ if !empty(name)
+ let b:GPGRecipients += [name]
+ call s:GPGDebug(1, "name of recipient is " . name)
+ else
+ let b:GPGRecipients += [recipient]
+ echohl GPGWarning
+ echom "The recipient \"" . recipient . "\" is not in your public keyring!"
+ echohl None
+ end
+ end
+ let start = match(output, asymmPattern, start)
+ endwhile
+ else
+ " file is not encrypted
+ let b:GPGEncrypted = 0
+ call s:GPGDebug(1, "this file is not encrypted")
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ endif
+
+ let bufname = b:GPGEncrypted ? autocmd_filename : filename
+ if a:bufread
+ silent execute ':doautocmd BufReadPre ' . fnameescape(bufname)
+ call s:GPGDebug(2, 'called BufReadPre autocommand for ' . bufname)
+ else
+ silent execute ':doautocmd FileReadPre ' . fnameescape(bufname)
+ call s:GPGDebug(2, 'called FileReadPre autocommand for ' . bufname)
+ endif
+
+ if b:GPGEncrypted
+ " check if the message is armored
+ if (match(output, "gpg: armor header") >= 0)
+ call s:GPGDebug(1, "this file is armored")
+ let b:GPGOptions += ["armor"]
+ endif
+
+ " finally decrypt the buffer content
+ " since even with the --quiet option passphrase typos will be reported,
+ " we must redirect stderr (using shell temporarily)
+ call s:GPGDebug(1, "decrypting file")
+ let cmd = { 'level': 1, 'ex': silent . 'read ++edit !' }
+ let cmd.args = '--quiet --decrypt ' . s:shellescape(filename, { 'special': 1, 'cygpath': 1 })
+ call s:GPGExecute(cmd)
+
+ if (v:shell_error) " message could not be decrypted
+ echohl GPGError
+ let blackhole = input("Message could not be decrypted! (Press ENTER)")
+ echohl None
+ " Only wipeout the buffer if we were creating one to start with.
+ " FileReadCmd just reads the content into the existing buffer
+ if a:bufread
+ silent bwipeout!
+ endif
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()")
+ return
+ endif
+ if a:bufread
+ " Ensure the buffer is only saved by using our BufWriteCmd
+ set buftype=acwrite
+ " Always set the buffer name to the absolute path, otherwise Vim won't
+ " track the correct buffer name when changing directories (due to
+ " buftype=acwrite).
+ exe 'file' fnameescape(filename)
+ endif
+ else
+ execute silent 'read' fnameescape(filename)
+ endif
+
+ if a:bufread
+ " In order to make :undo a no-op immediately after the buffer is read,
+ " we need to do this dance with 'undolevels'. Actually discarding the undo
+ " history requires performing a change after setting 'undolevels' to -1 and,
+ " luckily, we have one we need to do (delete the extra line from the :r
+ " command)
+ let levels = &undolevels
+ set undolevels=-1
+ " :lockmarks doesn't actually prevent '[,'] from being overwritten, so we
+ " need to manually set them ourselves instead
+ silent 1delete
+ 1mark [
+ $mark ]
+ let &undolevels = levels
+ " The buffer should be readonly if
+ " - 'readonly' is already set (e.g., when using view/vim -R)
+ " - permissions don't allow writing
+ let &readonly = &readonly || (filereadable(filename) && filewritable(filename) == 0)
+ silent execute ':doautocmd BufReadPost ' . fnameescape(bufname)
+ call s:GPGDebug(2, 'called BufReadPost autocommand for ' . bufname)
+ else
+ silent execute ':doautocmd FileReadPost ' . fnameescape(bufname)
+ call s:GPGDebug(2, 'called FileReadPost autocommand for ' . bufname)
+ endif
+
+ if b:GPGEncrypted
+ " Allow the user to define actions for GnuPG buffers
+ silent doautocmd User GnuPG
+
+ " refresh screen
+ redraw!
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()")
+endfunction
+
+" Function: s:GPGEncrypt() {{{2
+"
+" encrypts the buffer to all previous recipients
+"
+function s:GPGEncrypt()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGEncrypt()")
+
+ " FileWriteCmd is only called when a portion of a buffer is being written to
+ " disk. Since Vim always sets the '[,'] marks to the part of a buffer that
+ " is being written, that can be used to determine whether BufWriteCmd or
+ " FileWriteCmd triggered us.
+ if [line("'["), line("']")] == [1, line('$')]
+ let auType = 'BufWrite'
+ else
+ let auType = 'FileWrite'
+ endif
+
+ " file name minus extension
+ let autocmd_filename = expand(':p:r')
+
+ silent exe ':doautocmd '. auType .'Pre '. fnameescape(autocmd_filename)
+ call s:GPGDebug(2, 'called '. auType .'Pre autocommand for ' . autocmd_filename)
+
+ " guard for unencrypted files
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGError
+ let blackhole = input("Message could not be encrypted! (Press ENTER)")
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
+ return
+ endif
+
+ let filename = resolve(expand(':p'))
+ " initialize GPGOptions if not happened before
+ if (!exists("b:GPGOptions") || empty(b:GPGOptions))
+ let b:GPGOptions = []
+ if (exists("g:GPGPreferSymmetric") && g:GPGPreferSymmetric == 1)
+ let b:GPGOptions += ["symmetric"]
+ let b:GPGRecipients = []
+ else
+ let b:GPGOptions += ["encrypt"]
+ endif
+ " Fallback to preference by filename if the user didn't indicate
+ " their preference.
+ let preferArmor = get(g:, 'GPGPreferArmor', -1)
+ if (preferArmor >= 0 && preferArmor) || filename =~ '\.asc$'
+ let b:GPGOptions += ["armor"]
+ endif
+ if (exists("g:GPGPreferSign") && g:GPGPreferSign == 1)
+ let b:GPGOptions += ["sign"]
+ endif
+ call s:GPGDebug(1, "no options set, so using default options: " . string(b:GPGOptions))
+ endif
+
+ " built list of options
+ let options = ""
+ for option in b:GPGOptions
+ let options = options . " --" . option . " "
+ endfor
+
+ if (!exists('b:GPGRecipients'))
+ let b:GPGRecipients = []
+ endif
+
+ " check here again if all recipients are available in the keyring
+ let recipients = s:GPGCheckRecipients(b:GPGRecipients)
+
+ " check if there are unknown recipients and warn
+ if !empty(recipients.unknown)
+ echohl GPGWarning
+ echom "Please use GPGEditRecipients to correct!!"
+ echo
+ echohl None
+
+ " Let user know whats happend and copy known_recipients back to buffer
+ let dummy = input("Press ENTER to quit")
+ endif
+
+ " built list of recipients
+ let options .= ' ' . join(map(recipients.valid, '"-r ".v:val'), ' ')
+
+ " encrypt the buffer
+ let destfile = tempname()
+ let cmd = { 'level': 1, 'ex': "'[,']write !" }
+ let cmd.args = '--quiet --no-encrypt-to ' . options
+ let cmd.redirect = '>' . s:shellescape(destfile, { 'special': 1, 'cygpath': 1 })
+ silent call s:GPGExecute(cmd)
+
+ if (v:shell_error) " message could not be encrypted
+ " Command failed, so clean up the tempfile
+ call delete(destfile)
+ echohl GPGError
+ let blackhole = input("Message could not be encrypted! (Press ENTER)")
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
+ return
+ endif
+
+ if rename(destfile, filename)
+ " Rename failed, so clean up the tempfile
+ call delete(destfile)
+ echohl GPGError
+ echom printf("\"%s\" E212: Can't open file for writing", filename)
+ echohl None
+ return
+ endif
+
+ if auType == 'BufWrite'
+ if expand('%:p') == filename
+ setl nomodified
+ endif
+ setl buftype=acwrite
+ let &readonly = filereadable(filename) && filewritable(filename) == 0
+ endif
+
+ silent exe ':doautocmd '. auType .'Post '. fnameescape(autocmd_filename)
+ call s:GPGDebug(2, 'called '. auType .'Post autocommand for ' . autocmd_filename)
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
+endfunction
+
+" Function: s:GPGViewRecipients() {{{2
+"
+" echo the recipients
+"
+function s:GPGViewRecipients()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGViewRecipients()")
+
+ " guard for unencrypted files
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGViewRecipients()")
+ return
+ endif
+
+ let recipients = s:GPGCheckRecipients(b:GPGRecipients)
+
+ echo 'This file has following recipients (Unknown recipients have a prepended "!"):'
+ if empty(recipients.valid)
+ echohl GPGError
+ echo 'There are no known recipients!'
+ echohl None
+ else
+ echo join(map(recipients.valid, 's:GPGIDToName(v:val)'), "\n")
+ endif
+
+ if !empty(recipients.unknown)
+ echohl GPGWarning
+ echo join(map(recipients.unknown, '"!".v:val'), "\n")
+ echohl None
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGViewRecipients()")
+endfunction
+
+" Function: s:GPGEditRecipients() {{{2
+"
+" create a scratch buffer with all recipients to add/remove recipients
+"
+function s:GPGEditRecipients()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGEditRecipients()")
+
+ if s:unencrypted()
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditRecipients()")
+ return
+ endif
+
+ " only do this if it isn't already a GPGRecipients_* buffer
+ if (!exists('b:GPGCorrespondingTo'))
+
+ " save buffer name
+ let buffername = bufnr("%")
+ let editbuffername = "GPGRecipients_" . buffername
+
+ " check if this buffer exists
+ if (!bufexists(editbuffername))
+ " create scratch buffer
+ execute 'silent! split ' . fnameescape(editbuffername)
+
+ " add a autocommand to regenerate the recipients after a write
+ autocmd BufHidden,BufUnload,BufWriteCmd call s:GPGFinishRecipientsBuffer()
+ else
+ if (bufwinnr(editbuffername) >= 0)
+ " switch to scratch buffer window
+ execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w"
+ else
+ " split scratch buffer window
+ execute 'silent! sbuffer ' . fnameescape(editbuffername)
+
+ " add a autocommand to regenerate the recipients after a write
+ autocmd BufHidden,BufUnload,BufWriteCmd call s:GPGFinishRecipientsBuffer()
+ endif
+
+ " empty the buffer
+ silent %delete
+ endif
+
+ " Mark the buffer as a scratch buffer
+ setlocal buftype=acwrite
+ setlocal bufhidden=hide
+ setlocal noswapfile
+ setlocal nowrap
+ setlocal nobuflisted
+ setlocal nonumber
+
+ " so we know for which other buffer this edit buffer is
+ let b:GPGCorrespondingTo = buffername
+
+ " put some comments to the scratch buffer
+ silent put ='GPG: ----------------------------------------------------------------------'
+ silent put ='GPG: Please edit the list of recipients, one recipient per line.'
+ silent put ='GPG: Unknown recipients have a prepended \"!\".'
+ silent put ='GPG: Lines beginning with \"GPG:\" are removed automatically.'
+ silent put ='GPG: Data after recipients between and including \"(\" and \")\" is ignored.'
+ silent put ='GPG: Closing this buffer commits changes.'
+ silent put ='GPG: ----------------------------------------------------------------------'
+
+ " get the recipients
+ let recipients = s:GPGCheckRecipients(getbufvar(b:GPGCorrespondingTo, "GPGRecipients"))
+
+ " if there are no known or unknown recipients, use the default ones
+ if (empty(recipients.valid) && empty(recipients.unknown))
+ if (type(g:GPGDefaultRecipients) == type([]))
+ let recipients = s:GPGCheckRecipients(g:GPGDefaultRecipients)
+ else
+ echohl GPGWarning
+ echom "g:GPGDefaultRecipients is not a Vim list, please correct this in your vimrc!"
+ echohl None
+ endif
+ endif
+
+ " put the recipients in the scratch buffer
+ for name in recipients.valid
+ let name = s:GPGIDToName(name)
+ silent put =name
+ endfor
+
+ " put the unknown recipients in the scratch buffer
+ let syntaxPattern = ''
+ if !empty(recipients.unknown)
+ let flaggedNames = map(recipients.unknown, '"!".v:val')
+ call append('$', flaggedNames)
+ let syntaxPattern = '\(' . join(flaggedNames, '\|') . '\)'
+ endif
+
+ for line in g:GPGPossibleRecipients
+ silent put ='GPG: '.line
+ endfor
+
+ " define highlight
+ if (has("syntax") && exists("g:syntax_on"))
+ highlight clear GPGUnknownRecipient
+ if !empty(syntaxPattern)
+ execute 'syntax match GPGUnknownRecipient "' . syntaxPattern . '"'
+ highlight link GPGUnknownRecipient GPGHighlightUnknownRecipient
+ endif
+
+ syntax match GPGComment "^GPG:.*$"
+ execute 'syntax match GPGComment "' . s:GPGMagicString . '.*$"'
+ highlight clear GPGComment
+ highlight link GPGComment Comment
+ endif
+
+ " delete the empty first line
+ silent 1delete
+
+ " jump to the first recipient
+ silent $
+
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditRecipients()")
+endfunction
+
+" Function: s:GPGFinishRecipientsBuffer() {{{2
+"
+" create a new recipient list from RecipientsBuffer
+"
+function s:GPGFinishRecipientsBuffer()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGFinishRecipientsBuffer()")
+
+ if s:unencrypted()
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishRecipientsBuffer()")
+ return
+ endif
+
+ " go to buffer before doing work
+ if (bufnr("%") != expand(""))
+ " switch to scratch buffer window
+ execute 'silent! ' . bufwinnr(expand(":p")) . "wincmd w"
+ endif
+
+ " delete the autocommand
+ autocmd! *
+
+ " get the recipients from the scratch buffer
+ let recipients = []
+ let lines = getline(1,"$")
+ for recipient in lines
+ let matches = matchlist(recipient, '^\(.\{-}\)\%(' . s:GPGMagicString . '(ID:\s\+\(' . s:keyPattern . '\)\s\+.*\)\=$')
+
+ let recipient = matches[2] ? matches[2] : matches[1]
+
+ " delete all spaces at beginning and end of the recipient
+ " also delete a '!' at the beginning of the recipient
+ let recipient = substitute(recipient, "^[[:space:]!]*\\(.\\{-}\\)[[:space:]]*$", "\\1", "")
+
+ " delete comment lines
+ let recipient = substitute(recipient, "^GPG:.*$", "", "")
+
+ " only do this if the line is not empty
+ if !empty(recipient)
+ let gpgid = s:GPGNameToID(recipient)
+ if !empty(gpgid)
+ if (match(recipients, gpgid) < 0)
+ let recipients += [gpgid]
+ endif
+ else
+ if (match(recipients, recipient) < 0)
+ let recipients += [recipient]
+ echohl GPGWarning
+ echom "The recipient \"" . recipient . "\" is not in your public keyring!"
+ echohl None
+ endif
+ endif
+ endif
+ endfor
+
+ " write back the new recipient list to the corresponding buffer and mark it
+ " as modified. Buffer is now for sure an encrypted buffer.
+ call setbufvar(b:GPGCorrespondingTo, "GPGRecipients", recipients)
+ call setbufvar(b:GPGCorrespondingTo, "&mod", 1)
+ call setbufvar(b:GPGCorrespondingTo, "GPGEncrypted", 1)
+
+ " check if there is any known recipient
+ if empty(recipients)
+ echohl GPGError
+ echom 'There are no known recipients!'
+ echohl None
+ endif
+
+ " reset modified flag
+ setl nomodified
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishRecipientsBuffer()")
+endfunction
+
+" Function: s:GPGViewOptions() {{{2
+"
+" echo the recipients
+"
+function s:GPGViewOptions()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGViewOptions()")
+
+ if s:unencrypted()
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGViewOptions()")
+ return
+ endif
+
+ if (exists("b:GPGOptions"))
+ echo 'This file has following options:'
+ echo join(b:GPGOptions, "\n")
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGViewOptions()")
+endfunction
+
+" Function: s:GPGEditOptions() {{{2
+"
+" create a scratch buffer with all recipients to add/remove recipients
+"
+function s:GPGEditOptions()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGEditOptions()")
+
+ if s:unencrypted()
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditOptions()")
+ return
+ endif
+
+ " only do this if it isn't already a GPGOptions_* buffer
+ if (!exists('b:GPGCorrespondingTo'))
+
+ " save buffer name
+ let buffername = bufnr("%")
+ let editbuffername = "GPGOptions_" . buffername
+
+ " check if this buffer exists
+ if (!bufexists(editbuffername))
+ " create scratch buffer
+ execute 'silent! split ' . fnameescape(editbuffername)
+
+ " add a autocommand to regenerate the options after a write
+ autocmd BufHidden,BufUnload,BufWriteCmd call s:GPGFinishOptionsBuffer()
+ else
+ if (bufwinnr(editbuffername) >= 0)
+ " switch to scratch buffer window
+ execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w"
+ else
+ " split scratch buffer window
+ execute 'silent! sbuffer ' . fnameescape(editbuffername)
+
+ " add a autocommand to regenerate the options after a write
+ autocmd BufHidden,BufUnload,BufWriteCmd call s:GPGFinishOptionsBuffer()
+ endif
+
+ " empty the buffer
+ silent %delete
+ endif
+
+ " Mark the buffer as a scratch buffer
+ setlocal buftype=nofile
+ setlocal noswapfile
+ setlocal nowrap
+ setlocal nobuflisted
+ setlocal nonumber
+
+ " so we know for which other buffer this edit buffer is
+ let b:GPGCorrespondingTo = buffername
+
+ " put some comments to the scratch buffer
+ silent put ='GPG: ----------------------------------------------------------------------'
+ silent put ='GPG: THERE IS NO CHECK OF THE ENTERED OPTIONS!'
+ silent put ='GPG: YOU NEED TO KNOW WHAT YOU ARE DOING!'
+ silent put ='GPG: IF IN DOUBT, QUICKLY EXIT USING :x OR :bd.'
+ silent put ='GPG: Please edit the list of options, one option per line.'
+ silent put ='GPG: Please refer to the gpg documentation for valid options.'
+ silent put ='GPG: Lines beginning with \"GPG:\" are removed automatically.'
+ silent put ='GPG: Closing this buffer commits changes.'
+ silent put ='GPG: ----------------------------------------------------------------------'
+
+ " put the options in the scratch buffer
+ let options = getbufvar(b:GPGCorrespondingTo, "GPGOptions")
+
+ for option in options
+ silent put =option
+ endfor
+
+ " delete the empty first line
+ silent 1delete
+
+ " jump to the first option
+ silent $
+
+ " define highlight
+ if (has("syntax") && exists("g:syntax_on"))
+ syntax match GPGComment "^GPG:.*$"
+ highlight clear GPGComment
+ highlight link GPGComment Comment
+ endif
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditOptions()")
+endfunction
+
+" Function: s:GPGFinishOptionsBuffer() {{{2
+"
+" create a new option list from OptionsBuffer
+"
+function s:GPGFinishOptionsBuffer()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGFinishOptionsBuffer()")
+
+ if s:unencrypted()
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishOptionsBuffer()")
+ return
+ endif
+
+ " go to buffer before doing work
+ if (bufnr("%") != expand(""))
+ " switch to scratch buffer window
+ execute 'silent! ' . bufwinnr(expand(":p")) . "wincmd w"
+ endif
+
+ " clear options and unknownOptions
+ let options = []
+ let unknownOptions = []
+
+ " delete the autocommand
+ autocmd! *
+
+ " get the options from the scratch buffer
+ let lines = getline(1, "$")
+ for option in lines
+ " delete all spaces at beginning and end of the option
+ " also delete a '!' at the beginning of the option
+ let option = substitute(option, "^[[:space:]!]*\\(.\\{-}\\)[[:space:]]*$", "\\1", "")
+ " delete comment lines
+ let option = substitute(option, "^GPG:.*$", "", "")
+
+ " only do this if the line is not empty
+ if (!empty(option) && match(options, option) < 0)
+ let options += [option]
+ endif
+ endfor
+
+ " write back the new option list to the corresponding buffer and mark it
+ " as modified
+ call setbufvar(b:GPGCorrespondingTo, "GPGOptions", options)
+ call setbufvar(b:GPGCorrespondingTo, "&mod", 1)
+
+ " reset modified flag
+ setl nomodified
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishOptionsBuffer()")
+endfunction
+
+" Function: s:GPGCheckRecipients(tocheck) {{{2
+"
+" check if recipients are known
+" Returns: dictionary of recipients, {'valid': [], 'unknown': []}
+"
+function s:GPGCheckRecipients(tocheck)
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGCheckRecipients()")
+
+ let recipients = {'valid': [], 'unknown': []}
+
+ if (type(a:tocheck) == type([]))
+ for recipient in a:tocheck
+ let gpgid = s:GPGNameToID(recipient)
+ if !empty(gpgid)
+ if (match(recipients.valid, gpgid) < 0)
+ call add(recipients.valid, gpgid)
+ endif
+ else
+ if (match(recipients.unknown, recipient) < 0)
+ call add(recipients.unknown, recipient)
+ echohl GPGWarning
+ echom "The recipient \"" . recipient . "\" is not in your public keyring!"
+ echohl None
+ endif
+ end
+ endfor
+ endif
+
+ call s:GPGDebug(2, "recipients are: " . string(recipients.valid))
+ call s:GPGDebug(2, "unknown recipients are: " . string(recipients.unknown))
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGCheckRecipients()")
+ return recipients
+endfunction
+
+" Function: s:GPGNameToID(name) {{{2
+"
+" find GPG key ID corresponding to a name
+" Returns: ID for the given name
+"
+function s:GPGNameToID(name)
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGNameToID()")
+
+ " ask gpg for the id for a name
+ let cmd = { 'level': 2 }
+ let cmd.args = '--quiet --with-colons --fixed-list-mode --list-keys ' . s:shellescape(a:name)
+ let output = s:GPGSystem(cmd)
+
+ " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8,
+ " so convert it, if necessary
+ if (&encoding != "utf-8")
+ let output = iconv(output, "utf-8", &encoding)
+ endif
+ let lines = split(output, "\n")
+
+ " parse the output of gpg
+ let pubseen = 0
+ let counter = 0
+ let gpgids = []
+ let seen_keys = {}
+ let skip_key = 0
+ let has_strftime = exists('*strftime')
+ let choices = "The name \"" . a:name . "\" is ambiguous. Please select the correct key:\n"
+ for line in lines
+
+ let fields = split(line, ":")
+
+ " search for the next pub
+ if (fields[0] == "pub")
+ " check if this key has already been processed
+ if has_key(seen_keys, fields[4])
+ let skip_key = 1
+ continue
+ endif
+ let skip_key = 0
+ let seen_keys[fields[4]] = 1
+
+ " Ignore keys which are not usable for encryption
+ if fields[11] !~? 'e'
+ continue
+ endif
+
+ let identity = fields[4]
+ let gpgids += [identity]
+ if has_strftime
+ let choices = choices . counter . ": ID: 0x" . identity . " created at " . strftime("%c", fields[5]) . "\n"
+ else
+ let choices = choices . counter . ": ID: 0x" . identity . "\n"
+ endif
+ let counter = counter+1
+ let pubseen = 1
+ " search for the next uid
+ elseif (!skip_key && fields[0] == "uid")
+ let choices = choices . " " . fields[9] . "\n"
+ endif
+
+ endfor
+
+ " counter > 1 means we have more than one results
+ let answer = 0
+ if (counter > 1)
+ let choices = choices . "Enter number: "
+ let answer = input(choices, "0")
+ while (answer == "")
+ let answer = input("Enter number: ", "0")
+ endwhile
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGNameToID()")
+ return get(gpgids, answer, "")
+endfunction
+
+" Function: s:GPGIDToName(identity) {{{2
+"
+" find name corresponding to a GPG key ID
+" Returns: Name for the given ID
+"
+function s:GPGIDToName(identity)
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGIDToName()")
+
+ " TODO is the encryption subkey really unique?
+
+ " ask gpg for the id for a name
+ let cmd = { 'level': 2 }
+ let cmd.args = '--quiet --with-colons --fixed-list-mode --list-keys ' . a:identity
+ let output = s:GPGSystem(cmd)
+
+ " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8,
+ " so convert it, if necessary
+ if (&encoding != "utf-8")
+ let output = iconv(output, "utf-8", &encoding)
+ endif
+ let lines = split(output, "\n")
+
+ " parse the output of gpg
+ let pubseen = 0
+ let uid = ""
+ for line in lines
+ let fields = split(line, ":")
+
+ if !pubseen " search for the next pub
+ if (fields[0] == "pub")
+ " Ignore keys which are not usable for encryption
+ if fields[11] !~? 'e'
+ continue
+ endif
+
+ let pubseen = 1
+ endif
+ else " search for the next uid
+ if (fields[0] == "uid")
+ let pubseen = 0
+ if exists("*strftime")
+ let uid = fields[9] . s:GPGMagicString . "(ID: 0x" . a:identity . " created at " . strftime("%c", fields[5]) . ")"
+ else
+ let uid = fields[9] . s:GPGMagicString . "(ID: 0x" . a:identity . ")"
+ endif
+ break
+ endif
+ endif
+ endfor
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGIDToName()")
+ return uid
+endfunction
+
+" Function: s:GPGPreCmd() {{{2
+"
+" Setup the environment for running the gpg command
+"
+function s:GPGPreCmd()
+ let &shellredir = s:shellredir
+ let &shell = s:shell
+ let &shelltemp = s:shelltemp
+ " Force C locale so GPG output is consistent
+ let s:messages = v:lang
+ language messages C
+endfunction
+
+
+" Function: s:GPGPostCmd() {{{2
+"
+" Restore the user's environment after running the gpg command
+"
+function s:GPGPostCmd()
+ let &shellredir = s:shellredirsave
+ let &shell = s:shellsave
+ let &shelltemp = s:shelltempsave
+ execute 'language messages' s:messages
+ " Workaround a bug in the interaction between console vim and
+ " pinentry-curses by forcing Vim to re-detect and setup its terminal
+ " settings
+ let &term = &term
+ silent doautocmd TermChanged
+endfunction
+
+" Function: s:GPGSystem(dict) {{{2
+"
+" run g:GPGCommand using system(), logging the commandline and output. This
+" uses temp files (regardless of how 'shelltemp' is set) to hold the output of
+" the command, so it must not be used for sensitive commands.
+" Recognized keys are:
+" level - Debug level at which the commandline and output will be logged
+" args - Arguments to be given to g:GPGCommand
+"
+" Returns: command output
+"
+function s:GPGSystem(dict)
+ let commandline = s:GPGCommand
+ if (!empty(g:GPGHomedir))
+ let commandline .= ' --homedir ' . s:shellescape(g:GPGHomedir, { 'cygpath': 1 })
+ endif
+ let commandline .= ' ' . a:dict.args
+ let commandline .= ' ' . s:stderrredirnull
+ call s:GPGDebug(a:dict.level, "command: ". commandline)
+
+ call s:GPGPreCmd()
+ let output = system(commandline)
+ call s:GPGPostCmd()
+
+ call s:GPGDebug(a:dict.level, "rc: ". v:shell_error)
+ call s:GPGDebug(a:dict.level, "output: ". output)
+ return output
+endfunction
+
+" Function: s:GPGExecute(dict) {{{2
+"
+" run g:GPGCommand using :execute, logging the commandline
+" Recognized keys are:
+" level - Debug level at which the commandline will be logged
+" args - Arguments to be given to g:GPGCommand
+" ex - Ex command which will be :executed
+" redirect - Shell redirect to use, if needed
+"
+function s:GPGExecute(dict)
+ let commandline = printf('%s%s', a:dict.ex, s:GPGCommand)
+ if (!empty(g:GPGHomedir))
+ let commandline .= ' --homedir ' . s:shellescape(g:GPGHomedir, { 'special': 1, 'cygpath': 1 })
+ endif
+ let commandline .= ' ' . a:dict.args
+ if (has_key(a:dict, 'redirect'))
+ let commandline .= ' ' . a:dict.redirect
+ endif
+ let commandline .= ' ' . s:stderrredirnull
+ call s:GPGDebug(a:dict.level, "command: " . commandline)
+
+ call s:GPGPreCmd()
+ execute commandline
+ call s:GPGPostCmd()
+
+ call s:GPGDebug(a:dict.level, "rc: ". v:shell_error)
+endfunction
+
+" Function: s:GPGDebug(level, text) {{{2
+"
+" output debug message, if this message has high enough importance
+" only define function if GPGDebugLevel set at all
+"
+function s:GPGDebug(level, text)
+ if exists("g:GPGDebugLevel") && g:GPGDebugLevel >= a:level
+ if exists("g:GPGDebugLog")
+ execute "redir >> " . g:GPGDebugLog
+ silent echom "GnuPG: " . a:text
+ redir END
+ else
+ echom "GnuPG: " . a:text
+ endif
+ endif
+endfunction
+
+" Section: Commands {{{1
+
+command! GPGViewRecipients call s:GPGViewRecipients()
+command! GPGEditRecipients call s:GPGEditRecipients()
+command! GPGViewOptions call s:GPGViewOptions()
+command! GPGEditOptions call s:GPGEditOptions()
+
+" Section: Menu {{{1
+
+if (has("menu"))
+ amenu Plugin.GnuPG.View\ Recipients :GPGViewRecipients
+ amenu Plugin.GnuPG.Edit\ Recipients :GPGEditRecipients
+ amenu Plugin.GnuPG.View\ Options :GPGViewOptions
+ amenu Plugin.GnuPG.Edit\ Options :GPGEditOptions
+endif
+
+" vim600: set foldmethod=marker foldlevel=0 :
diff --git a/.vim/bundle/vim-gph/ftdetect/gph.vim b/.vim/bundle/vim-gph/ftdetect/gph.vim
new file mode 100644
index 0000000..d331930
--- /dev/null
+++ b/.vim/bundle/vim-gph/ftdetect/gph.vim
@@ -0,0 +1 @@
+au BufNewFile,BufRead *.gph set ft=gph syn=gph
diff --git a/.vim/bundle/vim-gph/syntax/gph.vim b/.vim/bundle/vim-gph/syntax/gph.vim
new file mode 100644
index 0000000..1721e01
--- /dev/null
+++ b/.vim/bundle/vim-gph/syntax/gph.vim
@@ -0,0 +1,104 @@
+" Syntax colouring for gopher .gph files used by geomyidae
+" Muddled about a bit by dive @ freenode / #gopherproject
+" 2017-11-15
+
+set shiftwidth=4
+set tabstop=4
+set expandtab
+
+" modif by sdk
+setl enc=utf-8
+setl wrap
+setl linebreak
+setl nolist
+setl textwidth=72
+setl formatprg=par\ -w72qie
+setl nojs
+setl nosmartindent
+"setl spell
+setl nospell
+setl formatoptions=troqwbj
+" end
+
+if version < 600
+ syntax clear
+elseif exists("b:current_syntax")
+ finish
+endif
+
+" Use default terminal colours
+hi Normal ctermbg=NONE ctermfg=NONE guifg=NONE guibg=NONE
+
+" Use italics for comments. If this fails and you get reverse video
+" then you may want to comment it out.
+hi Comment cterm=italic
+
+" Err colour (not sure about this one. It's a bit bright).
+hi Err cterm=bold ctermbg=NONE ctermfg=130 guibg=NONE guifg=red
+
+hi def link gopherComment comment
+hi def link gopherType preproc
+hi def link gopherURL statement
+hi def link gopherHtml statement
+hi def link gopherLink statement
+hi def link gopherServerPort statement
+hi def link gopherBracket preproc
+hi def link gopherPipe preproc
+hi def link gopherCGI type
+hi def link gopherCGI2 type
+hi def link gopherQuery type
+hi def link gopherErr err
+hi def link SynError error
+
+" Format of lines:
+" [||||]
+
+" = description of gopher item. Most printable characters should work.
+"
+" = full path to gopher item (base value is "/" ). Use the "Err" path for
+"items not intended to be served.
+"
+" = hostname or IP hosting the gopher item. Must be resolvable for the
+"intended clients. If this is set to "server" , the server's hostname is used.
+"
+" = TCP port number (usually 70) If this is set to "port" , the default
+"port of the server is used.
+
+" Comments
+syn region gopherComment start=""
+
+" URLs
+syn match gopherURL "http:"
+syn region gopherLink start="http:"lc=5 end="|"me=e-1
+syn match gopherURL "gopher:"
+syn match gopherURL "URL:"
+syn match gopherURL "URI:"
+syn region gopherLink start="gopher:"lc=7 end="|"me=e-1
+
+" Pipes
+syn match gopherPipe "|" containedin=gopherServerPort
+
+" Queries and CGI
+syn match gopherQuery "^\[7"lc=1
+syn match gopherCGI "|[^|]*\.cgi[^|]*"lc=1
+syn match gopherCGI2 "|[^|]*\.dcgi[^|]*"lc=1
+
+" Server|Port
+syn match gopherServerPort "|[^|]*|[^|]*]"
+
+" Start and end brackets
+match gopherBracket "[\[\]]"
+
+" Entity
+syn region gopherType start="^\[[0123456789ghHmswITi\+:;
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/.vim/bundle/vim-ledger/README.md b/.vim/bundle/vim-ledger/README.md
new file mode 100644
index 0000000..2255836
--- /dev/null
+++ b/.vim/bundle/vim-ledger/README.md
@@ -0,0 +1,129 @@
+# vim-ledger
+
+[![Vint](https://github.com/ledger/vim-ledger/workflows/Vint/badge.svg)](https://github.com/ledger/vim-ledger/actions?workflow=Vint)
+[![Vader](https://github.com/ledger/vim-ledger/workflows/Vader/badge.svg)](https://github.com/ledger/vim-ledger/actions?workflow=Vader)
+
+Filetype detection, syntax highlighting, auto-formatting, auto-completion, and other tools for working with ledger files.
+Compatible with both [`ledger`][ledgercli] and [`hledger`][hledger].
+See [plaintextaccounting.org][pta] for background information and other useful links.
+
+## Usage
+
+Install as you would any other VIM plugin.
+There are a variety of ways depending on your plugin manager.
+For example with [Pathogen](https://github.com/tpope/vim-pathogen) you would clone this repository into your configuration directory.
+With [vim-plug](https://github.com/junegunn/vim-plug) and many similar ones, you would declare it in your rc file like this, then run `:PlugInstall`:
+
+
+```vimscript
+Plug 'ledger/vim-ledger'
+```
+
+You can also manually copy the corresponding directories into your VIM plugins directory.
+
+One installed this plugin will identify files ending with `.ldg`, `.ledger`, or `.journal` as ledger files automatically.
+Alaternatively if you use a different extension you can add a modeline to each like this:
+
+```ledger
+; vim: filetype=ledger
+```
+
+## Tips and useful commands
+
+* Try account-completion (as explained below)
+
+* `:call ledger#transaction_date_set(line('.'), 'auxiliary')`
+
+ will set today's date as the auxiliary date of the current transaction.
+ You can use also `primary` or `unshift` in place of `auxiliary`.
+ When you pass "unshift" the old primary date will be set as the auxiliary date and today's date will be set as the new primary date.
+ To use a different date pass a date measured in seconds since 1st Jan 1970 as the third argument.
+
+* `:call ledger#transaction_state_set(line('.'), '*')`
+
+ sets the state of the current transaction to '*'.
+ You can use this in custom mappings.
+
+* `:call ledger#transaction_state_toggle(line('.'), ' *?!')`
+
+ will toggle through the provided transaction states.
+ You can map this to double-clicking for example:
+
+ noremap <2-LeftMouse>\
+ :call ledger#transaction_state_toggle(line('.'), ' *?!')
+
+* Align commodities at the decimal point. See `help ledger-tips`.
+
+* `:call ledger#entry()`
+
+ will replace the text on the current line with a new transaction based on the replaced text.
+
+## Configuration
+
+Include the following let-statements somewhere in your `.vimrc` to modify the behaviour of the ledger filetype.
+
+* Number of columns that will be used to display the foldtext.
+ Set this when you think that the amount is too far off to the right.
+
+ let g:ledger_maxwidth = 80
+
+* String that will be used to fill the space between account name and amount in the foldtext.
+ Set this to get some kind of lines or visual aid.
+
+ let g:ledger_fillstring = ' -'
+
+* If you want the account completion to be sorted by level of detail/depth instead of alphabetical, include the following line:
+
+ let g:ledger_detailed_first = 1
+
+* By default vim will fold ledger transactions, leaving surrounding blank lines unfolded.
+ You can use `g:ledger_fold_blanks` to hide blank lines following a transaction.
+
+ let g:ledger_fold_blanks = 0
+
+ A value of 0 will disable folding of blank lines, 1 will allow folding of a single blank line between transactions; any larger value will enable folding undconditionally.
+
+ Note that only lines containing no trailing spaces are considered for folding.
+ You can take advantage of this to disable this feature on a case-by-case basis.
+
+## Completion
+
+Omni completion is implemented for transactions descriptions and posting account names.
+
+### Accounts
+
+Account names are matched by the start of every sub-level.
+When you insert an account name like this:
+
+ Asse