Versioning the rest of my vimfiles directory.
authorKevin Watters <[email protected]>
Fri, 10 Oct 2008 23:54:27 +0000 (10 19:54 -0400)
committerKevin Watters <[email protected]>
Fri, 10 Oct 2008 23:54:27 +0000 (10 19:54 -0400)
23 files changed:
autoload/genutils.vim [new file with mode: 0644]
autoload/lookupfile.vim [new file with mode: 0644]
doc/lookupfile.txt [new file with mode: 0644]
doc/surround.txt [new file with mode: 0644]
doc/tags [new file with mode: 0644]
doc/vcscommand.txt [new file with mode: 0644]
plugin/fuzzyfinder.vim [new file with mode: 0644]
plugin/fuzzyfinder_textmate.vim [new file with mode: 0644]
plugin/genutils.vim [new file with mode: 0644]
plugin/lookupfile.vim [new file with mode: 0644]
plugin/slime.vim [new file with mode: 0644]
plugin/supertab.vim [new file with mode: 0644]
plugin/surround.vim [new file with mode: 0644]
plugin/svndiff.vim [new file with mode: 0644]
plugin/vcscommand.vim [new file with mode: 0644]
plugin/vcscvs.vim [new file with mode: 0644]
plugin/vcsgit.vim [new file with mode: 0644]
plugin/vcssvk.vim [new file with mode: 0644]
plugin/vcssvn.vim [new file with mode: 0644]
syntax/CVSAnnotate.vim [new file with mode: 0644]
syntax/SVKAnnotate.vim [new file with mode: 0644]
syntax/SVNAnnotate.vim [new file with mode: 0644]
syntax/vcscommit.vim [new file with mode: 0644]

diff --git a/autoload/genutils.vim b/autoload/genutils.vim
new file mode 100644 (file)
index 0000000..c28a25c
--- /dev/null
@@ -0,0 +1,1954 @@
+" genutils.vim: Please see plugin/genutils.vim
+"
+" TODO:
+"   - Vim7: redir can be used with a variable.
+"   - fnamemodify() on Unix doesn't expand to full name if the filename doesn't
+"     really exist on the filesystem.
+"   - Is setting 'scrolloff' and 'sidescrolloff' to 0 required while moving the
+"     cursor?
+"   - http://www.vim.org/tips/tip.php?tip_id=1379
+"
+"   - EscapeCommand() didn't work for David Fishburn.
+"   - Save/RestoreWindowSettings doesn't work well.
+"
+"   Vim7:
+"   - Save/RestoreWindowSettings can use winsave/restview() functions.
+"
+
+" Make sure line-continuations won't cause any problem. This will be restored
+"   at the end
+let s:save_cpo = &cpo
+set cpo&vim
+
+
+let g:makeArgumentString = 'exec genutils#MakeArgumentString()'
+let g:makeArgumentList = 'exec genutils#MakeArgumentList()'
+
+let s:makeArgumentString = ''
+function! genutils#MakeArgumentString(...)
+  if s:makeArgumentString == ''
+    let s:makeArgumentString = genutils#ExtractFuncListing(s:SNR().
+          \ '_makeArgumentString', 0, 0)
+  endif
+  if a:0 > 0 && a:1 != ''
+    return substitute(s:makeArgumentString, '\<argumentString\>', a:1, 'g')
+  else
+    return s:makeArgumentString
+  endif
+endfunction
+
+
+let s:makeArgumentList = ''
+function! genutils#MakeArgumentList(...)
+  if s:makeArgumentList == ''
+    let s:makeArgumentList = genutils#ExtractFuncListing(s:SNR().
+          \ '_makeArgumentList', 0, 0)
+  endif
+  if a:0 > 0 && a:1 != ''
+    let mkArgLst = substitute(s:makeArgumentList, '\<argumentList\>', a:1, 'g')
+    if a:0 > 1 && a:2 != ''
+      let mkArgLst = substitute(s:makeArgumentList,
+            \ '\(\s\+let __argSeparator = \)[^'."\n".']*', "\\1'".a:2."'", '')
+    endif
+    return mkArgLst
+  else
+    return s:makeArgumentList
+  endif
+endfunction
+
+function! genutils#ExtractFuncListing(funcName, hLines, tLines)
+  let listing = genutils#GetVimCmdOutput('func '.a:funcName)
+  let listing = substitute(listing,
+        \ '^\%(\s\|'."\n".'\)*function '.a:funcName.'([^)]*)'."\n", '', '')
+  "let listing = substitute(listing, '\%(\s\|'."\n".'\)*endfunction\%(\s\|'."\n".'\)*$', '', '')
+  " Leave the last newline character.
+  let listing = substitute(listing, '\%('."\n".'\)\@<=\s*endfunction\s*$', '', '')
+  let listing = substitute(listing, '\(\%(^\|'."\n".'\)\s*\)\@<=\d\+',
+        \ '', 'g')
+  if a:hLines > 0
+    let listing = substitute(listing, '^\%([^'."\n".']*'."\n".'\)\{'.
+          \ a:hLines.'}', '', '')
+  endif
+  if a:tLines > 0
+    let listing = substitute(listing, '\%([^'."\n".']*'."\n".'\)\{'.
+          \ a:tLines.'}$', '', '')
+  endif
+  return listing
+endfunction
+
+function! genutils#CreateArgString(argList, sep, ...)
+  let sep = (a:0 == 0) ? a:sep : a:1 " This is no longer used.
+  " Matching multvals functionality means, we need to ignore the trailing
+  " separator.
+  let argList = split(substitute(a:argList, a:sep.'$', '', ''), a:sep, 1)
+  let argString = "'"
+  for nextArg in argList
+    " FIXME: I think this check is not required. If "'" is the separator, we
+    "   don't expect to see them in the elements.
+    if a:sep != "'"
+      let nextArg = substitute(nextArg, "'", "' . \"'\" . '", 'g')
+    endif
+    let argString = argString . nextArg . "', '"
+  endfor
+  let argString = strpart(argString, 0, strlen(argString) - 3)
+  return argString
+endfunction
+
+" {{{
+function! s:_makeArgumentString()
+  let __argCounter = 1
+  let argumentString = ''
+  while __argCounter <= a:0
+    if type(a:{__argCounter})
+      let __nextArg =  "'" .
+            \ substitute(a:{__argCounter}, "'", "' . \"'\" . '", "g") . "'"
+    else
+      let __nextArg = a:{__argCounter}
+    endif
+    let argumentString = argumentString. __nextArg .
+          \ ((__argCounter == a:0) ? '' : ', ')
+    let __argCounter = __argCounter + 1
+  endwhile
+  unlet __argCounter
+  if exists('__nextArg')
+    unlet __nextArg
+  endif
+endfunction
+
+function! s:_makeArgumentList()
+  let __argCounter = 1
+  let __argSeparator = ','
+  let argumentList = ''
+  while __argCounter <= a:0
+    let argumentList = argumentList . a:{__argCounter}
+    if __argCounter != a:0
+      let argumentList = argumentList . __argSeparator
+    endif
+    let __argCounter = __argCounter + 1
+  endwhile
+  unlet __argCounter
+  unlet __argSeparator
+endfunction
+" }}}
+
+
+function! genutils#DebugShowArgs(...)
+  let i = 0
+  let argString = ''
+  while i < a:0
+    let argString = argString . a:{i + 1} . ', '
+    let i = i + 1
+  endwhile
+  let argString = strpart(argString, 0, strlen(argString) - 2)
+  call input("Args: " . argString)
+endfunction
+
+" Window related functions {{{
+
+function! genutils#NumberOfWindows()
+  let i = 1
+  while winbufnr(i) != -1
+    let i = i+1
+  endwhile
+  return i - 1
+endfunction
+
+" Find the window number for the buffer passed.
+" The fileName argument is treated literally, unlike the bufnr() which treats
+"   the argument as a regex pattern.
+function! genutils#FindWindowForBuffer(bufferName, checkUnlisted)
+  return bufwinnr(genutils#FindBufferForName(a:bufferName))
+endfunction
+
+function! genutils#FindBufferForName(fileName)
+  " The name could be having extra backslashes to protect certain chars (such
+  "   as '#' and '%'), so first expand them.
+  return s:FindBufferForName(genutils#UnEscape(a:fileName, '#%'))
+endfunction
+
+function! s:FindBufferForName(fileName)
+  let fileName = genutils#Escape(a:fileName, '[?,{')
+  let _isf = &isfname
+  try
+    set isfname-=\
+    set isfname-=[
+    let i = bufnr('^' . fileName . '$')
+  finally
+    let &isfname = _isf
+  endtry
+  return i
+endfunction
+
+function! genutils#GetBufNameForAu(bufName)
+  let bufName = a:bufName
+  " Autocommands always require forward-slashes.
+  let bufName = substitute(bufName, "\\\\", '/', 'g')
+  let bufName = escape(bufName, '*?,{}[ ')
+  return bufName
+endfunction
+
+function! genutils#MoveCursorToWindow(winno)
+  if genutils#NumberOfWindows() != 1
+    execute a:winno . " wincmd w"
+  endif
+endfunction
+
+function! genutils#MoveCurLineToWinLine(n)
+  normal! zt
+  if a:n == 1
+    return
+  endif
+  let _wrap = &l:wrap
+  setl nowrap
+  let n = a:n
+  if n >= winheight(0)
+    let n = winheight(0)
+  endif
+  let n = n - 1
+  execute "normal! " . n . "\<C-Y>"
+  let &l:wrap = _wrap
+endfunction
+
+function! genutils#CloseWindow(win, force)
+  let _eventignore = &eventignore
+  try
+    set eventignore=all
+    call genutils#MarkActiveWindow()
+
+    let &eventignore = _eventignore
+    exec a:win 'wincmd w'
+    exec 'close'.(a:force ? '!' : '')
+    set eventignore=all
+
+    if a:win < t:curWinnr
+      let t:curWinnr = t:curWinnr - 1
+    endif
+    if a:win < t:prevWinnr
+      let t:prevWinnr = t:prevWinnr - 1
+    endif
+  finally
+    call genutils#RestoreActiveWindow()
+    let &eventignore = _eventignore
+  endtry
+endfunction
+
+function! genutils#MarkActiveWindow()
+  let t:curWinnr = winnr()
+  " We need to restore the previous-window also at the end.
+  silent! wincmd p
+  let t:prevWinnr = winnr()
+  silent! wincmd p
+endfunction
+
+function! genutils#RestoreActiveWindow()
+  if !exists('t:curWinnr')
+    return
+  endif
+
+  " Restore the original window.
+  if winnr() != t:curWinnr
+    exec t:curWinnr'wincmd w'
+  endif
+  if t:curWinnr != t:prevWinnr
+    exec t:prevWinnr'wincmd w'
+    wincmd p
+  endif
+endfunction
+
+function! genutils#IsOnlyVerticalWindow()
+  let onlyVertWin = 1
+  let _eventignore = &eventignore
+
+  try
+    "set eventignore+=WinEnter,WinLeave
+    set eventignore=all
+    call genutils#MarkActiveWindow()
+
+    wincmd j
+    if winnr() != t:curWinnr
+      let onlyVertWin = 0
+    else
+      wincmd k
+      if winnr() != t:curWinnr
+       let onlyVertWin = 0
+      endif
+    endif
+  finally
+    call genutils#RestoreActiveWindow()
+    let &eventignore = _eventignore
+  endtry
+  return onlyVertWin
+endfunction
+
+function! genutils#IsOnlyHorizontalWindow()
+  let onlyHorizWin = 1
+  let _eventignore = &eventignore
+  try
+    set eventignore=all
+    call genutils#MarkActiveWindow()
+    wincmd l
+    if winnr() != t:curWinnr
+      let onlyHorizWin = 0
+    else
+      wincmd h
+      if winnr() != t:curWinnr
+       let onlyHorizWin = 0
+      endif
+    endif
+  finally
+    call genutils#RestoreActiveWindow()
+    let &eventignore = _eventignore
+  endtry
+  return onlyHorizWin
+endfunction
+
+function! genutils#MoveCursorToNextInWinStack(dir)
+  let newwin = genutils#GetNextWinnrInStack(a:dir)
+  if newwin != 0
+    exec newwin 'wincmd w'
+  endif
+endfunction
+
+function! genutils#GetNextWinnrInStack(dir)
+  let newwin = winnr()
+  let _eventignore = &eventignore
+  try
+    set eventignore=all
+    call genutils#MarkActiveWindow()
+    let newwin = s:GetNextWinnrInStack(a:dir)
+  finally
+    call genutils#RestoreActiveWindow()
+    let &eventignore = _eventignore
+  endtry
+  return newwin
+endfunction
+
+function! genutils#MoveCursorToLastInWinStack(dir)
+  let newwin = genutils#GetLastWinnrInStack(a:dir)
+  if newwin != 0
+    exec newwin 'wincmd w'
+  endif
+endfunction
+
+function! genutils#GetLastWinnrInStack(dir)
+  let newwin = winnr()
+  let _eventignore = &eventignore
+  try
+    set eventignore=all
+    call genutils#MarkActiveWindow()
+    while 1
+      let wn = s:GetNextWinnrInStack(a:dir)
+      if wn != 0
+        let newwin = wn
+        exec newwin 'wincmd w'
+      else
+        break
+      endif
+    endwhile
+  finally
+    call genutils#RestoreActiveWindow()
+    let &eventignore = _eventignore
+  endtry
+  return newwin
+endfunction
+
+" Based on the WinStackMv() function posted by Charles E. Campbell, Jr. on vim
+"   mailing list on Jul 14, 2004.
+function! s:GetNextWinnrInStack(dir)
+  "call Decho("genutils#MoveCursorToNextInWinStack(dir<".a:dir.">)")
+
+  let isHorizontalMov = (a:dir ==# 'h' || a:dir ==# 'l') ? 1 : 0
+
+  let orgwin = winnr()
+  let orgdim = s:GetWinDim(a:dir, orgwin)
+
+  let _winwidth = &winwidth
+  let _winheight = &winheight
+  try
+    set winwidth=1
+    set winheight=1
+    exec 'wincmd' a:dir
+    let newwin = winnr()
+    if orgwin == newwin
+      " No more windows in this direction.
+      "call Decho("newwin=".newwin." stopped".winheight(newwin)."x".winwidth(newwin))
+      return 0
+    endif
+    if s:GetWinDim(a:dir, newwin) != orgdim
+      " Window dimension has changed, indicates a move across window stacks.
+      "call Decho("newwin=".newwin." height changed".winheight(newwin)."x".winwidth(newwin))
+      return 0
+    endif
+    " Determine if changing original window height affects current window
+    "   height.
+    exec orgwin 'wincmd w'
+    try
+      if orgdim == 1
+        exec 'wincmd' (isHorizontalMov ? '_' : '|')
+      else
+        exec 'wincmd' (isHorizontalMov ? '-' : '<')
+      endif
+      if s:GetWinDim(a:dir, newwin) != s:GetWinDim(a:dir, orgwin)
+        "call Decho("newwin=".newwin." different row".winheight(newwin)."x".winwidth(newwin))
+        return 0
+      endif
+      "call Decho("newwin=".newwin." same row".winheight(newwin)."x".winwidth(newwin))
+    finally
+      exec (isHorizontalMov ? '' : 'vert') 'resize' orgdim
+    endtry
+
+    "call Decho("genutils#MoveCursorToNextInWinStack")
+
+    return newwin
+  finally
+    let &winwidth = _winwidth
+    let &winheight = _winheight
+  endtry
+endfunction
+
+function! s:GetWinDim(dir, win)
+  return (a:dir ==# 'h' || a:dir ==# 'l') ? winheight(a:win) : winwidth(a:win)
+endfunction
+
+function! genutils#OpenWinNoEa(winOpenCmd)
+  call s:ExecWinCmdNoEa(a:winOpenCmd)
+endfunction
+
+function! genutils#CloseWinNoEa(winnr, force)
+  call s:ExecWinCmdNoEa(a:winnr.'wincmd w | close'.(a:force?'!':''))
+endfunction
+
+function! s:ExecWinCmdNoEa(winCmd)
+  let _eventignore = &eventignore
+  try
+    set eventignore=all
+    call genutils#MarkActiveWindow()
+    windo let w:_winfixheight = &winfixheight
+    windo set winfixheight
+    call genutils#RestoreActiveWindow()
+
+    let &eventignore = _eventignore
+    exec a:winCmd
+    set eventignore=all
+
+    call genutils#MarkActiveWindow()
+    silent! windo let &winfixheight = w:_winfixheight
+    silent! windo unlet w:_winfixheight
+    call genutils#RestoreActiveWindow()
+  finally
+    let &eventignore = _eventignore
+  endtry
+endfunction
+
+" Window related functions }}}
+
+function! genutils#SetupScratchBuffer()
+  setlocal nobuflisted
+  setlocal noswapfile
+  setlocal buftype=nofile
+  " Just in case, this will make sure we are always hidden.
+  setlocal bufhidden=delete
+  setlocal nolist
+  setlocal nonumber
+  setlocal foldcolumn=0 nofoldenable
+  setlocal noreadonly
+endfunction
+
+function! genutils#CleanDiffOptions()
+  setlocal nodiff
+  setlocal noscrollbind
+  setlocal scrollopt-=hor
+  setlocal wrap
+  setlocal foldmethod=manual
+  setlocal foldcolumn=0
+  normal zE
+endfunction
+
+function! genutils#ArrayVarExists(varName, index)
+  try
+    exec "let test = " . a:varName . "{a:index}"
+  catch /^Vim\%((\a\+)\)\=:E121/
+    return 0
+  endtry
+  return 1
+endfunction
+
+function! genutils#Escape(str, chars)
+  return substitute(a:str, '\\\@<!\(\%(\\\\\)*\)\([' . a:chars .']\)', '\1\\\2',
+        \ 'g')
+endfunction
+
+function! genutils#UnEscape(str, chars)
+  return substitute(a:str, '\\\@<!\(\\\\\)*\\\([' . a:chars . ']\)',
+        \ '\1\2', 'g')
+endfunction
+
+function! genutils#DeEscape(str)
+  let str = a:str
+  let str = substitute(str, '\\\(\\\|[^\\]\)', '\1', 'g')
+  return str
+endfunction
+
+" - For windoze+native, use double-quotes to sorround the arguments and for
+"   embedded double-quotes, just double them.
+" - For windoze+sh, use single-quotes to sorround the aruments and for embedded
+"   single-quotes, just replace them with '""'""' (if 'shq' or 'sxq' is a
+"   double-quote) and just '"'"' otherwise. Embedded double-quotes also need
+"   to be doubled.
+" - For Unix+sh, use single-quotes to sorround the arguments and for embedded
+"   single-quotes, just replace them with '"'"'. 
+function! genutils#EscapeCommand(cmd, args, pipe)
+  if type(a:args) == 3
+    let args = copy(a:args)
+  else
+    let args = split(a:args, genutils#CrUnProtectedCharsPattern(' '))
+  endif
+  " I am only worried about passing arguments with spaces as they are to the
+  "   external commands, I currently don't care about back-slashes
+  "   (backslashes are normally expected only on windows when 'shellslash'
+  "   option is set, but even then the 'shell' is expected to take care of
+  "   them.). However, for cygwin bash, there is a loss of one level
+  "   of the back-slashes somewhere in the chain of execution (most probably
+  "   between CreateProcess() and cygwin?), so we need to double them.
+  let shellEnvType = genutils#GetShellEnvType()
+  if shellEnvType ==# g:ST_WIN_CMD
+    let quoteChar = '"'
+    " genutils#Escape the existing double-quotes (by doubling them).
+    call map(args, "substitute(v:val, '\"', '\"\"', 'g')")
+  else
+    let quoteChar = "'"
+    if shellEnvType ==# g:ST_WIN_SH
+      " genutils#Escape the existing double-quotes (by doubling them).
+      call map(args, "substitute(v:val, '\"', '\"\"', 'g')")
+    endif
+    " Take care of existing single-quotes (by exposing them, as you can't have
+    "   single-quotes inside a single-quoted string).
+    if &shellquote == '"' || &shellxquote == '"'
+      let squoteRepl = "'\"\"'\"\"'"
+    else
+      let squoteRepl = "'\"'\"'"
+    endif
+    call map(args, "substitute(v:val, \"'\", squoteRepl, 'g')")
+  endif
+
+  " Now sorround the arguments with quotes, considering the protected
+  "   spaces. Skip the && or || construct from doing this.
+  call map(args, 'v:val=~"^[&|]\\{2}$"?(v:val):(quoteChar.v:val.quoteChar)')
+  let fullCmd = join(args, ' ')
+  " We delay adding pipe part so that we can avoid the above processing.
+  let pipe = ''
+  if type(a:pipe) == 3 && len(a:pipe) > 0
+    let pipe = join(a:pipe, ' ')
+  elseif type(a:pipe) == 1 && a:pipe !~# '^\s*$'
+    let pipe = a:pipe
+  endif
+  if pipe != ''
+    let fullCmd = fullCmd . ' ' . a:pipe
+  endif
+  if a:cmd !~# '^\s*$'
+    let fullCmd = a:cmd . ' ' . fullCmd
+  endif
+  if shellEnvType ==# g:ST_WIN_SH && &shell =~# '\<bash\>'
+    let fullCmd = substitute(fullCmd, '\\', '\\\\', 'g')
+  endif
+  return fullCmd
+endfunction
+
+let g:ST_WIN_CMD = 0 | let g:ST_WIN_SH = 1 | let g:ST_UNIX = 2
+function! genutils#GetShellEnvType()
+  " When 'shellslash' option is available, then the platform must be one of
+  "     those that support '\' as a pathsep.
+  if exists('+shellslash')
+    if stridx(&shell, 'cmd.exe') != -1 ||
+          \ stridx(&shell, 'command.com') != -1
+      return g:ST_WIN_CMD
+    else
+      return g:ST_WIN_SH
+    endif
+  else
+    return g:ST_UNIX
+  endif
+endfunction
+
+function! genutils#ExpandStr(str)
+  let str = substitute(a:str, '"', '\\"', 'g')
+  exec "let str = \"" . str . "\"" 
+  return str
+endfunction
+
+function! genutils#QuoteStr(str)
+  return "'".substitute(a:str, "'", "'.\"'\".'", 'g')."'"
+endfunction
+
+function! genutils#GetPreviewWinnr()
+  let _eventignore = &eventignore
+  let curWinNr = winnr()
+  let winnr = -1
+  try
+    set eventignore=all
+    exec "wincmd P"
+    let winnr = winnr()
+  catch /^Vim\%((\a\+)\)\=:E441/
+    " Ignore, winnr is already set to -1.
+  finally
+    if winnr() != curWinNr
+      exec curWinNr.'wincmd w'
+    endif
+    let &eventignore = _eventignore
+  endtry
+  return winnr
+endfunction
+
+" Save/Restore window settings {{{
+function! genutils#SaveWindowSettings()
+  call genutils#SaveWindowSettings2('SaveWindowSettings', 1)
+endfunction
+
+function! genutils#RestoreWindowSettings()
+  call genutils#RestoreWindowSettings2('SaveWindowSettings')
+endfunction
+
+
+function! genutils#ResetWindowSettings()
+  call genutils#ResetWindowSettings2('SaveWindowSettings')
+endfunction
+
+function! genutils#SaveWindowSettings2(id, overwrite)
+  if genutils#ArrayVarExists("t:winSettings", a:id) && ! a:overwrite
+    return
+  endif
+
+  let t:winSettings{a:id} = []
+  call add(t:winSettings{a:id}, genutils#NumberOfWindows())
+  call add(t:winSettings{a:id}, &lines)
+  call add(t:winSettings{a:id}, &columns)
+  call add(t:winSettings{a:id}, winnr())
+  let i = 1
+  while winbufnr(i) != -1
+    call add(t:winSettings{a:id}, winheight(i))
+    call add(t:winSettings{a:id}, winwidth(i))
+    let i = i + 1
+  endwhile
+  "let g:savedWindowSettings = t:winSettings{a:id} " Debug.
+endfunction
+
+function! genutils#RestoreWindowSettings2(id)
+  " Calling twice fixes most of the resizing issues. This seems to be how the
+  " :mksession with "winsize" in 'sesionoptions' seems to work.
+  call s:RestoreWindowSettings2(a:id)
+  call s:RestoreWindowSettings2(a:id)
+endfunction
+
+function! s:RestoreWindowSettings2(id)
+  "if ! exists("t:winSettings" . a:id)
+  if ! genutils#ArrayVarExists("t:winSettings", a:id)
+    return
+  endif
+
+  let nWindows = t:winSettings{a:id}[0]
+  if nWindows != genutils#NumberOfWindows()
+    unlet t:winSettings{a:id}
+    return
+  endif
+  let orgLines = t:winSettings{a:id}[1]
+  let orgCols = t:winSettings{a:id}[2]
+  let activeWindow = t:winSettings{a:id}[3]
+  let mainWinSizeSame = (orgLines == &lines && orgCols == &columns)
+
+  let winNo = 1
+  let i = 4
+  while i < len(t:winSettings{a:id})
+    let height = t:winSettings{a:id}[i]
+    let width = t:winSettings{a:id}[i+1]
+    let height = (mainWinSizeSame ? height :
+          \ ((&lines * height + (orgLines / 2)) / orgLines))
+    let width = (mainWinSizeSame ? width :
+          \ ((&columns * width + (orgCols / 2)) / orgCols))
+    if winheight(winnr()) != height
+      exec winNo'resize' height
+    endif
+    if winwidth(winnr()) != width
+      exec 'vert' winNo 'resize' width
+    endif
+    let winNo = winNo + 1
+    let i = i + 2
+  endwhile
+  
+  " Restore the current window.
+  call genutils#MoveCursorToWindow(activeWindow)
+  "unlet g:savedWindowSettings
+endfunction
+
+
+function! genutils#ResetWindowSettings2(id)
+  if genutils#ArrayVarExists("t:winSettings", a:id)
+    unlet t:winSettings{a:id}
+  endif
+endfunction
+
+" Save/Restore window settings }}}
+
+" Save/Restore selection {{{
+
+function! genutils#SaveVisualSelection(id)
+  let curmode = mode()
+  if curmode == 'n'
+    normal! gv
+  endif
+  let s:vismode{a:id} = mode()
+  let s:firstline{a:id} = line("'<")
+  let s:lastline{a:id} = line("'>")
+  let s:firstcol{a:id} = col("'<")
+  let s:lastcol{a:id} = col("'>")
+  if curmode !=# s:vismode{a:id}
+    exec "normal \<Esc>"
+  endif
+endfunction
+
+function! genutils#RestoreVisualSelection(id)
+  if mode() !=# 'n'
+    exec "normal \<Esc>"
+  endif
+  if exists('s:vismode{id}')
+    exec 'normal' s:firstline{a:id}.'gg'.s:firstcol{a:id}.'|'.
+          \ s:vismode{a:id}.(s:lastline{a:id}-s:firstline{a:id}).'j'.
+          \ (s:lastcol{a:id}-s:firstcol{a:id}).'l'
+  endif
+endfunction
+" Save/Restore selection }}}
+
+function! genutils#CleanupFileName(fileName)
+  return genutils#CleanupFileName2(a:fileName, '')
+endfunction
+
+function! genutils#CleanupFileName2(fileName, win32ProtectedChars)
+  let fileName = substitute(a:fileName, '^\s\+\|\s\+$', '', 'g')
+
+  " Expand relative paths and paths containing relative components (takes care
+  " of ~ also).
+  if ! genutils#PathIsAbsolute(fileName)
+    let fileName = fnamemodify(fileName, ':p')
+  endif
+
+  " I think we can have UNC paths on UNIX, if samba is installed.
+  if genutils#OnMS() && (match(fileName, '^//') == 0 ||
+        \ match(fileName, '^\\\\') == 0)
+    let uncPath = 1
+  else
+    let uncPath = 0
+  endif
+
+  " Remove multiple path separators.
+  if has('win32')
+    if a:win32ProtectedChars != ''
+      let fileName=substitute(fileName, '\\['.a:win32ProtectedChars.']\@!', '/',
+            \ 'g')
+    else
+      let fileName=substitute(fileName, '\\', '/', 'g')
+    endif
+  elseif genutils#OnMS()
+    " On non-win32 systems, the forward-slash is not supported, so leave
+    " back-slash.
+    let fileName=substitute(fileName, '\\\{2,}', '\', 'g')
+  endif
+  let fileName=substitute(fileName, '/\{2,}', '/', 'g')
+
+  " Remove ending extra path separators.
+  let fileName=substitute(fileName, '/$', '', '')
+  let fileName=substitute(fileName, '\\$', '', '')
+
+  " If it was an UNC path, add back an extra slash.
+  if uncPath
+    let fileName = '/'.fileName
+  endif
+
+  if genutils#OnMS()
+    let fileName=substitute(fileName, '^[A-Z]:', '\L&', '')
+
+    " Add drive letter if missing (just in case).
+    if !uncPath && match(fileName, '^/') == 0
+      let curDrive = substitute(getcwd(), '^\([a-zA-Z]:\).*$', '\L\1', '')
+      let fileName = curDrive . fileName
+    endif
+  endif
+  return fileName
+endfunction
+"echo genutils#CleanupFileName('\\a///b/c\')
+"echo genutils#CleanupFileName('C:\a/b/c\d')
+"echo genutils#CleanupFileName('a/b/c\d')
+"echo genutils#CleanupFileName('~/a/b/c\d')
+"echo genutils#CleanupFileName('~/a/b/../c\d')
+
+function! genutils#OnMS()
+  return has('win32') || has('dos32') || has('win16') || has('dos16') ||
+       \ has('win95')
+endfunction
+
+function! genutils#PathIsAbsolute(path)
+  let absolute=0
+  if has('unix') || genutils#OnMS()
+    if match(a:path, '^/') == 0
+      let absolute=1
+    endif
+  endif
+  if (! absolute) && genutils#OnMS()
+    if match(a:path, "^\\") == 0
+      let absolute=1
+    endif
+  endif
+  if (! absolute) && genutils#OnMS()
+    if match(a:path, "^[A-Za-z]:") == 0
+      let absolute=1
+    endif
+  endif
+  return absolute
+endfunction
+
+function! genutils#PathIsFileNameOnly(path)
+  return (match(a:path, "\\") < 0) && (match(a:path, "/") < 0)
+endfunction
+
+let s:mySNR = ''
+function! s:SNR()
+  if s:mySNR == ''
+    let s:mySNR = matchstr(expand('<sfile>'), '<SNR>\d\+_\zeSNR$')
+  endif
+  return s:mySNR
+endfun
+
+
+"" --- START save/restore position. {{{
+
+function! genutils#SaveSoftPosition(id)
+  let b:sp_startline_{a:id} = getline(".")
+  call genutils#SaveHardPosition(a:id)
+endfunction
+
+function! genutils#RestoreSoftPosition(id)
+  0
+  call genutils#RestoreHardPosition(a:id)
+  let stLine = b:sp_startline_{a:id}
+  if getline('.') !=# stLine
+    if ! search('\V\^'.escape(stLine, "\\").'\$', 'W') 
+      call search('\V\^'.escape(stLine, "\\").'\$', 'bW')
+    endif
+  endif
+  call genutils#MoveCurLineToWinLine(b:sp_winline_{a:id})
+endfunction
+
+function! genutils#ResetSoftPosition(id)
+  unlet b:sp_startline_{a:id}
+endfunction
+
+" A synonym for genutils#SaveSoftPosition.
+function! genutils#SaveHardPositionWithContext(id)
+  call genutils#SaveSoftPosition(a:id)
+endfunction
+
+" A synonym for genutils#RestoreSoftPosition.
+function! genutils#RestoreHardPositionWithContext(id)
+  call genutils#RestoreSoftPosition(a:id)
+endfunction
+
+" A synonym for genutils#ResetSoftPosition.
+function! genutils#ResetHardPositionWithContext(id)
+  call genutils#ResetSoftPosition(a:id)
+endfunction
+
+function! genutils#SaveHardPosition(id)
+  let b:sp_col_{a:id} = virtcol(".")
+  let b:sp_lin_{a:id} = line(".")
+  " Avoid accounting for wrapped lines.
+  let _wrap = &l:wrap
+  try
+    setl nowrap
+    let b:sp_winline_{a:id} = winline()
+  finally
+    let &l:wrap = _wrap
+  endtry
+endfunction
+
+function! genutils#RestoreHardPosition(id)
+  " This doesn't take virtual column.
+  "call cursor(b:sp_lin_{a:id}, b:sp_col_{a:id})
+  " Vim7 generates E16 if line number is invalid.
+  " TODO: Why is this leaving cursor on the last-but-one line when the
+  " condition meets?
+  execute ((line('$') < b:sp_lin_{a:id}) ? line('$') :
+        \ b:sp_lin_{a:id})
+  "execute b:sp_lin_{a:id}
+  execute ((line('$') < b:sp_lin_{a:id}) ? line('$') :
+        \ b:sp_lin_{a:id})
+  "execute b:sp_lin_{a:id}
+  execute "normal!" b:sp_col_{a:id} . "|"
+  call genutils#MoveCurLineToWinLine(b:sp_winline_{a:id})
+endfunction
+
+function! genutils#ResetHardPosition(id)
+  unlet b:sp_col_{a:id}
+  unlet b:sp_lin_{a:id}
+  unlet b:sp_winline_{a:id}
+endfunction
+
+function! genutils#GetLinePosition(id)
+  return b:sp_lin_{a:id}
+endfunction
+
+function! genutils#GetColPosition(id)
+  return b:sp_col_{a:id}
+endfunction
+
+function! genutils#IsPositionSet(id)
+  return exists('b:sp_col_' . a:id)
+endfunction
+
+"" --- END save/restore position. }}}
+
+
+
+""
+"" --- START: Notify window close -- {{{
+""
+
+let s:notifyWindow = {}
+function! genutils#AddNotifyWindowClose(windowTitle, functionName)
+  let bufName = a:windowTitle
+
+  let s:notifyWindow[bufName] = a:functionName
+
+  "let g:notifyWindow = s:notifyWindow " Debug.
+
+  " Start listening to events.
+  aug NotifyWindowClose
+    au!
+    au WinEnter * :call genutils#CheckWindowClose()
+    au BufEnter * :call genutils#CheckWindowClose()
+  aug END
+endfunction
+
+function! genutils#RemoveNotifyWindowClose(windowTitle)
+  let bufName = a:windowTitle
+
+  if has_key(s:notifyWindow, bufName)
+    call remove(s:notifyWindow, bufName)
+    if len(s:notifyWindow) == 0
+      "unlet g:notifyWindow " Debug.
+  
+      aug NotifyWindowClose
+        au!
+      aug END
+    endif
+  endif
+endfunction
+
+function! genutils#CheckWindowClose()
+  if !exists('s:notifyWindow')
+    return
+  endif
+
+  " Now iterate over all the registered window titles and see which one's are
+  "   closed.
+  for nextWin in keys(s:notifyWindow)
+    if bufwinnr(s:FindBufferForName(nextWin)) == -1
+      let funcName = s:notifyWindow[nextWin]
+      " Remove this entry as these are going to be processed. The caller can add
+      "   them back if needed.
+      unlet s:notifyWindow[nextWin]
+      "call input("cmd: " . cmd)
+      call call(funcName, [nextWin])
+    endif
+  endfor
+endfunction
+
+"function! NotifyWindowCloseF(title)
+"  call input(a:title . " closed")
+"endfunction
+"
+"function! RunNotifyWindowCloseTest()
+"  call input("Creating three windows, 'ABC', 'XYZ' and 'b':")
+"  split ABC
+"  split X Y Z
+"  split b
+"  redraw
+"  call genutils#AddNotifyWindowClose("ABC", "NotifyWindowCloseF")
+"  call genutils#AddNotifyWindowClose("X Y Z", "NotifyWindowCloseF")
+"  call input("notifyWindow: " . string(s:notifyWindow))
+"  au NotifyWindowClose WinEnter
+"  call input("Added notifications for 'ABC' and 'XYZ'\n".
+"       \ "Now closing the windows, you should see ONLY two notifications:")
+"  quit
+"  quit
+"  quit
+"endfunction
+
+""
+"" --- END: Notify window close -- }}}
+""
+
+" TODO: For large ranges, the cmd can become too big, so make it one cmd per
+"       line.
+function! genutils#ShowLinesWithSyntax() range
+  " This makes sure we start (subsequent) echo's on the first line in the
+  " command-line area.
+  "
+  echo ''
+
+  let cmd        = ''
+  let prev_group = ' x '     " Something that won't match any syntax group.
+
+  let show_line = a:firstline
+  let isMultiLine = ((a:lastline - a:firstline) > 1)
+  while show_line <= a:lastline
+    let cmd = ''
+    let length = strlen(getline(show_line))
+    let column = 1
+
+    while column <= length
+      let group = synIDattr(synID(show_line, column, 1), 'name')
+      if group != prev_group
+        if cmd != ''
+          let cmd = cmd . "'|"
+        endif
+        let cmd = cmd . 'echohl ' . (group == '' ? 'NONE' : group) . "|echon '"
+        let prev_group = group
+      endif
+      let char = strpart(getline(show_line), column - 1, 1)
+      if char == "'"
+        let char = "'."'".'"
+      endif
+      let cmd = cmd . char
+      let column = column + 1
+    endwhile
+
+    try
+      exec cmd."\n'"
+    catch
+      echo ''
+    endtry
+    let show_line = show_line + 1
+  endwhile
+  echohl NONE
+endfunction
+
+
+function! genutils#AlignWordWithWordInPreviousLine()
+  "First go to the first col in the word.
+  if getline('.')[col('.') - 1] =~ '\s'
+    normal! w
+  else
+    normal! "_yiw
+  endif
+  let orgVcol = virtcol('.')
+  let prevLnum = prevnonblank(line('.') - 1)
+  if prevLnum == -1
+    return
+  endif
+  let prevLine = getline(prevLnum)
+
+  " First get the column to align with.
+  if prevLine[orgVcol - 1] =~ '\s'
+    " column starts from 1 where as index starts from 0.
+    let nonSpaceStrInd = orgVcol " column starts from 1 where as index starts from 0.
+    while prevLine[nonSpaceStrInd] =~ '\s'
+      let nonSpaceStrInd = nonSpaceStrInd + 1
+    endwhile
+  else
+    if strlen(prevLine) < orgVcol
+      let nonSpaceStrInd = strlen(prevLine) - 1
+    else
+      let nonSpaceStrInd = orgVcol - 1
+    endif
+
+    while prevLine[nonSpaceStrInd - 1] !~ '\s' && nonSpaceStrInd > 0
+      let nonSpaceStrInd = nonSpaceStrInd - 1
+    endwhile
+  endif
+  let newVcol = nonSpaceStrInd + 1 " Convert to column number.
+
+  if orgVcol > newVcol " We need to reduce the spacing.
+    let sub = strpart(getline('.'), newVcol - 1, (orgVcol - newVcol))
+    if sub =~ '^\s\+$'
+      " Remove the excess space.
+      exec 'normal! ' . newVcol . '|'
+      exec 'normal! ' . (orgVcol - newVcol) . 'x'
+    endif
+  elseif orgVcol < newVcol " We need to insert spacing.
+    exec 'normal! ' . orgVcol . '|'
+    exec 'normal! ' . (newVcol - orgVcol) . 'i '
+  endif
+endfunction
+
+function! genutils#ShiftWordInSpace(dir)
+  if a:dir == 1 " forward.
+    " If currently on <Space>...
+    if getline(".")[col(".") - 1] == " "
+      let move1 = 'wf '
+    else
+      " If next col is a 
+      "if getline(".")[col(".") + 1]
+      let move1 = 'f '
+    endif
+    let removeCommand = "x"
+    let pasteCommand = "bi "
+    let move2 = 'w'
+    let offset = 0
+  else " backward.
+    " If currently on <Space>...
+    if getline(".")[col(".") - 1] == " "
+      let move1 = 'w'
+    else
+      let move1 = '"_yiW'
+    endif
+    let removeCommand = "hx"
+    let pasteCommand = 'h"_yiwEa '
+    let move2 = 'b'
+    let offset = -3
+  endif
+
+  let savedCol = col(".")
+  exec "normal!" move1
+  let curCol = col(".")
+  let possible = 0
+  " Check if there is a space at the end.
+  if col("$") == (curCol + 1) " Works only for forward case, as expected.
+    let possible = 1
+  elseif getline(".")[curCol + offset] == " "
+    " Remove the space from here.
+    exec "normal!" removeCommand
+    let possible = 1
+  endif
+
+  " Move back into the word.
+  "exec "normal!" savedCol . "|"
+  if possible == 1
+    exec "normal!" pasteCommand
+    exec "normal!" move2
+  else
+    " Move to the original location.
+    exec "normal!" savedCol . "|"
+  endif
+endfunction
+
+
+function! genutils#CenterWordInSpace()
+  let line = getline('.')
+  let orgCol = col('.')
+  " If currently on <Space>...
+  if line[orgCol - 1] == " "
+    let matchExpr = ' *\%'. orgCol . 'c *\w\+ \+'
+  else
+    let matchExpr = ' \+\(\w*\%' . orgCol . 'c\w*\) \+'
+  endif
+  let matchInd = match(line, matchExpr)
+  if matchInd == -1
+    return
+  endif
+  let matchStr = matchstr(line,  matchExpr)
+  let nSpaces = strlen(substitute(matchStr, '[^ ]', '', 'g'))
+  let word = substitute(matchStr, ' ', '', 'g')
+  let middle = nSpaces / 2
+  let left = nSpaces - middle
+  let newStr = ''
+  while middle > 0
+    let newStr = newStr . ' '
+    let middle = middle - 1
+  endwhile
+  let newStr = newStr . word
+  while left > 0
+    let newStr = newStr . ' '
+    let left = left - 1
+  endwhile
+
+  let newLine = strpart(line, 0, matchInd)
+  let newLine = newLine . newStr
+  let newLine = newLine . strpart (line, matchInd + strlen(matchStr))
+  silent! keepjumps call setline(line('.'), newLine)
+endfunction
+
+function! genutils#MapAppendCascaded(lhs, rhs, mapMode)
+
+  " Determine the map mode from the map command.
+  let mapChar = strpart(a:mapMode, 0, 1)
+
+  " Check if this is already mapped.
+  let oldrhs = maparg(a:lhs, mapChar)
+  if oldrhs != ""
+    let self = oldrhs
+  else
+    let self = a:lhs
+  endif
+  "echomsg a:mapMode . "oremap" . " " . a:lhs . " " . self . a:rhs
+  exec a:mapMode . "oremap" a:lhs self . a:rhs
+endfunction
+
+" smartSlash simply says whether to depend on shellslash and ArgLead for
+"   determining path separator. If it shouldn't depend, it will always assume
+"   that the required pathsep is forward-slash.
+function! genutils#UserFileComplete(ArgLead, CmdLine, CursorPos, smartSlash,
+      \ searchPath)
+  let glob = ''
+  let opathsep = "\\"
+  let npathsep = '/'
+  if exists('+shellslash') && ! &shellslash && a:smartSlash &&
+        \ stridx(a:ArgLead, "\\") != -1
+    let opathsep = '/'
+    let npathsep = "\\"
+  endif
+  if a:searchPath !=# ''
+    for nextPath in split(a:searchPath, genutils#CrUnProtectedCharsPattern(','))
+      let nextPath = genutils#CleanupFileName(nextPath)
+      let matches = glob(nextPath.'/'.a:ArgLead.'*')
+      if matches !~# '^\_s*$'
+        let matches = s:FixPathSep(matches, opathsep, npathsep)
+        let nextPath = substitute(nextPath, opathsep, npathsep, 'g')
+        let matches = substitute(matches, '\V'.escape(nextPath.npathsep, "\\"),
+              \ '', 'g')
+        let glob = glob . matches . "\n"
+      endif
+    endfor
+  else
+    let glob = s:FixPathSep(glob(a:ArgLead.'*'), opathsep, npathsep)
+  endif
+  " FIXME: Need an option to control if ArgLead should also be returned or
+  " not.
+  return glob."\n".a:ArgLead
+endfunction
+
+command! -complete=file -nargs=* GUDebugEcho :echo <q-args>
+function! genutils#UserFileExpand(fileArgs)
+  return substitute(genutils#GetVimCmdOutput(
+        \ 'GUDebugEcho ' . a:fileArgs), '^\_s\+\|\_s\+$', '', 'g')
+endfunction
+
+function! s:FixPathSep(matches, opathsep, npathsep)
+  let matches = a:matches
+  let matches = substitute(matches, a:opathsep, a:npathsep, 'g')
+  let matches = substitute(matches, "\\([^\n]\\+\\)", '\=submatch(1).'.
+        \ '(isdirectory(submatch(1)) ? a:npathsep : "")', 'g')
+  return matches
+endfunction
+
+function! genutils#GetVimCmdOutput(cmd)
+  let v:errmsg = ''
+  let output = ''
+  let _z = @z
+  let _shortmess = &shortmess
+  try
+    set shortmess=
+    redir @z
+    silent exec a:cmd
+  catch /.*/
+    let v:errmsg = substitute(v:exception, '^[^:]\+:', '', '')
+  finally
+    redir END
+    let &shortmess = _shortmess
+    if v:errmsg == ''
+      let output = @z
+    endif
+    let @z = _z
+  endtry
+  return output
+endfunction
+
+function! genutils#OptClearBuffer()
+  " Go as far as possible in the undo history to conserve Vim resources.
+  let _modifiable = &l:modifiable
+  let _undolevels = &undolevels
+  try
+    setl modifiable
+    set undolevels=-1
+    silent! keepjumps 0,$delete _
+  finally
+    let &undolevels = _undolevels
+    let &l:modifiable = _modifiable
+  endtry
+endfunction
+
+
+"" START: Sorting support. {{{
+""
+
+"
+" Comapare functions.
+"
+
+function! genutils#CmpByLineLengthNname(line1, line2, ...)
+  let direction = (a:0?a:1:1)
+  let cmp = genutils#CmpByLength(a:line1, a:line2, direction)
+  if cmp == 0
+    let cmp = genutils#CmpByString(a:line1, a:line2, direction)
+  endif
+  return cmp
+endfunction
+
+function! genutils#CmpByLength(line1, line2, ...)
+  let direction = (a:0?a:1:1)
+  let len1 = strlen(a:line1)
+  let len2 = strlen(a:line2)
+  return direction * (len1 - len2)
+endfunction
+
+" Compare first by name and then by length.
+" Useful for sorting Java imports.
+function! genutils#CmpJavaImports(line1, line2, ...)
+  let direction = (a:0?a:1:1)
+  " FIXME: Simplify this.
+  if stridx(a:line1, '.') == -1
+    let pkg1 = ''
+    let cls1 = substitute(a:line1, '.* \(^[ ]\+\)', '\1', '')
+  else
+    let pkg1 = substitute(a:line1, '.*import\s\+\(.*\)\.[^. ;]\+.*$', '\1', '')
+    let cls1 = substitute(a:line1, '^.*\.\([^. ;]\+\).*$', '\1', '')
+  endif
+  if stridx(a:line2, '.') == -1
+    let pkg2 = ''
+    let cls2 = substitute(a:line2, '.* \(^[ ]\+\)', '\1', '')
+  else
+    let pkg2 = substitute(a:line2, '.*import\s\+\(.*\)\.[^. ;]\+.*$', '\1', '')
+    let cls2 = substitute(a:line2, '^.*\.\([^. ;]\+\).*$', '\1', '')
+  endif
+
+  let cmp = genutils#CmpByString(pkg1, pkg2, direction)
+  if cmp == 0
+    let cmp = genutils#CmpByLength(cls1, cls2, direction)
+  endif
+  return cmp
+endfunction
+
+function! genutils#CmpByString(line1, line2, ...)
+  let direction = (a:0?a:1:1)
+  if a:line1 < a:line2
+    return -direction
+  elseif a:line1 > a:line2
+    return direction
+  else
+    return 0
+  endif
+endfunction
+
+function! genutils#CmpByStringIgnoreCase(line1, line2, ...)
+  let direction = (a:0?a:1:1)
+  if a:line1 <? a:line2
+    return -direction
+  elseif a:line1 >? a:line2
+    return direction
+  else
+    return 0
+  endif
+endfunction
+
+function! genutils#CmpByNumber(line1, line2, ...)
+  let direction = (a:0 ? a:1 :1)
+  let num1 = a:line1 + 0
+  let num2 = a:line2 + 0
+
+  if num1 < num2
+    return -direction
+  elseif num1 > num2
+    return direction
+  else
+    return 0
+  endif
+endfunction
+
+function! genutils#QSort(cmp, direction) range
+  call s:QSortR(a:firstline, a:lastline, a:cmp, a:direction,
+        \ 's:BufLineAccessor', 's:BufLineSwapper', '')
+endfunction
+
+function! genutils#QSort2(start, end, cmp, direction, accessor, swapper, context)
+  call s:QSortR(a:start, a:end, a:cmp, a:direction, a:accessor, a:swapper,
+        \ a:context)
+endfunction
+
+" The default swapper that swaps lines in the current buffer.
+function! s:BufLineSwapper(line1, line2, context)
+  let str2 = getline(a:line1)
+  silent! keepjumps call setline(a:line1, getline(a:line2))
+  silent! keepjumps call setline(a:line2, str2)
+endfunction
+
+" The default accessor that returns lines from the current buffer.
+function! s:BufLineAccessor(line, context)
+  return getline(a:line)
+endfunction
+
+" The default mover that moves lines from one place to another in the current
+" buffer.
+function! s:BufLineMover(from, to, context)
+  let line = getline(a:from)
+  exec a:from.'d_'
+  call append(a:to, line)
+endfunction
+
+"
+" Sort lines.  QSortR() is called recursively.
+"
+function! s:QSortR(start, end, cmp, direction, accessor, swapper, context)
+  if a:end > a:start
+    let low = a:start
+    let high = a:end
+
+    " Arbitrarily establish partition element at the midpoint of the data.
+    let midStr = {a:accessor}(((a:start + a:end) / 2), a:context)
+
+    " Loop through the data until indices cross.
+    while low <= high
+
+      " Find the first element that is greater than or equal to the partition
+      "   element starting from the left Index.
+      while low < a:end
+        let result = {a:cmp}({a:accessor}(low, a:context), midStr, a:direction)
+        if result < 0
+          let low = low + 1
+        else
+          break
+        endif
+      endwhile
+
+      " Find an element that is smaller than or equal to the partition element
+      "   starting from the right Index.
+      while high > a:start
+        let result = {a:cmp}({a:accessor}(high, a:context), midStr, a:direction)
+        if result > 0
+          let high = high - 1
+        else
+          break
+        endif
+      endwhile
+
+      " If the indexes have not crossed, swap.
+      if low <= high
+        " Swap lines low and high.
+        call {a:swapper}(high, low, a:context)
+        let low = low + 1
+        let high = high - 1
+      endif
+    endwhile
+
+    " If the right index has not reached the left side of data must now sort
+    "   the left partition.
+    if a:start < high
+      call s:QSortR(a:start, high, a:cmp, a:direction, a:accessor, a:swapper,
+            \ a:context)
+    endif
+
+    " If the left index has not reached the right side of data must now sort
+    "   the right partition.
+    if low < a:end
+      call s:QSortR(low, a:end, a:cmp, a:direction, a:accessor, a:swapper,
+            \ a:context)
+    endif
+  endif
+endfunction
+
+function! genutils#BinSearchForInsert(start, end, line, cmp, direction)
+  return genutils#BinSearchForInsert2(a:start, a:end, a:line, a:cmp,
+        \ a:direction, 's:BufLineAccessor', '')
+endfunction
+
+function! genutils#BinSearchForInsert2(start, end, line, cmp, direction,
+      \ accessor, context)
+  let start = a:start - 1
+  let end = a:end
+  while start < end
+    let middle = (start + end + 1) / 2
+    " Support passing both Funcref's as well as names.
+    if type(a:cmp) == 2
+      if type(a:accessor) == 2
+        let result = a:cmp(a:accessor(middle, a:context), a:line, a:direction)
+      else
+        let result = a:cmp({a:accessor}(middle, a:context), a:line, a:direction)
+      endif
+    else
+      if type(a:accessor) == 2
+        let result = {a:cmp}(a:accessor(middle, a:context), a:line, a:direction)
+      else
+        let result = {a:cmp}({a:accessor}(middle, a:context), a:line, a:direction)
+      endif
+    endif
+    if result < 0
+      let start = middle
+    else
+      let end = middle - 1
+    endif
+  endwhile
+  return start
+endfunction
+
+function! genutils#BinSearchList(list, start, end, item, cmp)
+  let start = a:start - 1
+  let end = a:end
+  while start < end
+    let middle = (start + end + 1) / 2
+    let result = call(a:cmp, [get(a:list, middle), a:item])
+    if result < 0
+      let start = middle
+    else
+      let end = middle - 1
+    endif
+  endwhile
+  return start
+endfunction
+
+function! genutils#BinInsertSort(cmp, direction) range
+  call genutils#BinInsertSort2(a:firstline, a:lastline, a:cmp, a:direction,
+        \ 's:BufLineAccessor', 's:BufLineMover', '')
+endfunction
+
+function! genutils#BinInsertSort2(start, end, cmp, direction, accessor, mover, context)
+  let i = a:start + 1
+  while i <= a:end
+    let low = s:BinSearchToAppend2(a:start, i, {a:accessor}(i, a:context),
+          \ a:cmp, a:direction, a:accessor, a:context)
+    " Move the object.
+    if low < i
+      call {a:mover}(i, low - 1, a:context)
+    endif
+    let i = i + 1
+  endwhile
+endfunction
+
+function! s:BinSearchToAppend(start, end, line, cmp, direction)
+  return s:BinSearchToAppend2(a:start, a:end, a:line, a:cmp, a:direction,
+        \ 's:BufLineAccessor', '')
+endfunction
+
+function! s:BinSearchToAppend2(start, end, line, cmp, direction, accessor,
+      \ context)
+  let low = a:start
+  let high = a:end
+  while low < high
+    let mid = (low + high) / 2
+    let diff = {a:cmp}({a:accessor}(mid, a:context), a:line, a:direction)
+    if diff > 0
+      let high = mid
+    else
+      let low = mid + 1
+      if diff == 0
+        break
+      endif
+    endif
+  endwhile
+  return low
+endfunction
+
+""" END: Sorting support. }}}
+
+
+" Eats character if it matches the given pattern.
+"
+" Originally,
+" From: Benji Fisher <[email protected]>
+" Date: Mon, 25 Mar 2002 15:05:14 -0500
+"
+" Based on Bram's idea of eating a character while type <Space> to expand an
+"   abbreviation. This solves the problem with abbreviations, where we are
+"   left with an extra space after the expansion.
+" Ex:
+"   inoreabbr \stdout\ System.out.println("");<Left><Left><Left><C-R>=genutils#EatChar('\s')<CR>
+function! genutils#EatChar(pat)
+   let c = nr2char(getchar())
+   "call input('Pattern: '.a:pat.' '.
+   "      \ ((c =~ a:pat) ? 'Returning empty' : 'Returning: '.char2nr(c)))
+   return (c =~ a:pat) ? '' : c
+endfun
+
+
+" Can return a spacer from 0 to 80 characters width.
+let s:spacer= "                                                               ".
+      \ "                 "
+function! genutils#GetSpacer(width)
+  return strpart(s:spacer, 0, a:width)
+endfunction
+
+function! genutils#SilentSubstitute(pat, cmd)
+  call genutils#SaveHardPosition('SilentSubstitute')
+  let _search = @/
+  try
+    let @/ = a:pat
+    keepjumps silent! exec a:cmd
+  finally
+    let @/ = _search
+    call genutils#RestoreHardPosition('SilentSubstitute')
+    call genutils#ResetHardPosition('SilentSubstitute')
+  endtry
+endfunction
+
+function! genutils#SilentDelete(arg1, ...)
+  " For backwards compatibility.
+  if a:0
+    let range = a:arg1
+    let pat = a:1
+  else
+    let range = ''
+    let pat = a:arg1
+  endif
+  let _search = @/
+  call genutils#SaveHardPosition('SilentDelete')
+  try
+    let @/ = pat
+    keepjumps silent! exec range'g//d _'
+  finally
+    let @/ = _search
+    call genutils#RestoreHardPosition('SilentDelete')
+    call genutils#ResetHardPosition('SilentDelete')
+  endtry
+endfunction
+
+" START: genutils#Roman2Decimal {{{
+let s:I = 1
+let s:V = 5
+let s:X = 10
+let s:L = 50
+let s:C = 100
+let s:D = 500
+let s:M = 1000
+
+function! s:Char2Num(c)
+  " A bit of magic on empty strings
+  if a:c == ""
+    return 0
+  endif
+  exec 'let n = s:' . toupper(a:c)
+  return n
+endfun
+
+function! genutils#Roman2Decimal(str)
+  if a:str !~? '^[IVXLCDM]\+$'
+    return a:str
+  endif
+  let sum = 0
+  let i = 0
+  let n0 = s:Char2Num(a:str[i])
+  let len = strlen(a:str)
+  while i < len
+    let i = i + 1
+    let n1 = s:Char2Num(a:str[i])
+    " Magic: n1=0 when i exceeds len
+    if n1 > n0
+      let sum = sum - n0
+    else
+      let sum = sum + n0
+    endif
+    let n0 = n1
+  endwhile
+  return sum
+endfun
+" END: genutils#Roman2Decimal }}}
+
+
+" BEGIN: Relative path {{{
+function! genutils#CommonPath(path1, path2, ...)
+  let path1 = genutils#CleanupFileName(a:path1) . ((a:0 > 0 ? a:1 : 0) ? '/' : '')
+  let path2 = genutils#CleanupFileName(a:path2) . ((a:0 > 1 ? a:2 : 0) ? '/' : '')
+  return genutils#CommonString(path1, path2)
+endfunction
+
+function! genutils#CommonString(str1, str2)
+  let str1 = a:str1
+  let str2 = a:str2
+  if str1 == str2
+    return str1
+  endif
+  let n = 0
+  while str1[n] == str2[n]
+    let n = n+1
+  endwhile
+  return strpart(str1, 0, n)
+endfunction
+
+function! genutils#RelPathFromFile(srcFile, tgtFile)
+  return genutils#RelPathFromDir(fnamemodify(a:srcFile, ':h'), a:tgtFile)
+endfunction
+
+function! genutils#RelPathFromDir(srcDir, tgtFile)
+  let cleanDir = genutils#CleanupFileName(a:srcDir).'/'
+  let cleanFile = genutils#CleanupFileName(a:tgtFile)
+  let cmnPath = genutils#CommonPath(cleanDir, cleanFile, 1)
+  let relDirFromCmnPath = strpart(cleanDir, strlen(cmnPath))
+  if relDirFromCmnPath == '/' " This means the file is under the srcDir.
+    let relDirFromCmnPath = ''
+  endif
+  let relFileFromCmnPath = strpart(cleanFile, strlen(cmnPath))
+  return substitute(relDirFromCmnPath, '[^/]\+', '..', 'g') .
+        \ relFileFromCmnPath
+endfunction
+
+" END: Relative path }}}
+
+
+" BEGIN: Persistent settings {{{
+if ! exists("g:genutilsNoPersist") || ! g:genutilsNoPersist
+  " Make sure the '!' option to store global variables that are upper cased are
+  "   stored in viminfo file.
+  " Make sure it is the first option, so that it will not interfere with the
+  "   'n' option ("Todd J. Cosgrove" (todd dot cosgrove at softechnics dot
+  "   com)).
+  " Also take care of empty value, when 'compatible' mode is on (Bram
+  "   Moolenar)
+  if &viminfo == ''
+    set viminfo=!,'20,"50,h
+  else
+    set viminfo^=!
+  endif
+endif
+
+function! genutils#PutPersistentVar(pluginName, persistentVar, value)
+  if ! exists("g:genutilsNoPersist") || ! g:genutilsNoPersist
+    let globalVarName = s:PersistentVarName(a:pluginName, a:persistentVar)
+    exec 'let ' . globalVarName . " = '" . a:value . "'"
+  endif
+endfunction
+
+function! genutils#GetPersistentVar(pluginName, persistentVar, default)
+  if ! exists("g:genutilsNoPersist") || ! g:genutilsNoPersist
+    let globalVarName = s:PersistentVarName(a:pluginName, a:persistentVar)
+    if (exists(globalVarName))
+      exec 'let value = ' . globalVarName
+      exec 'unlet ' . globalVarName
+    else
+      let value = a:default
+    endif
+    return value
+  else
+    return default
+  endif
+endfunction
+
+function! s:PersistentVarName(pluginName, persistentVar)
+  return 'g:GU_' . toupper(a:pluginName) . '_' . toupper(a:persistentVar)
+endfunction
+" END: Persistent settings }}}
+
+
+" FileChangedShell handling {{{
+if !exists('s:fcShellPreFuncs')
+  let s:fcShellPreFuncs = {}
+endif
+
+function! genutils#AddToFCShellPre(funcName)
+  let s:fcShellPreFuncs[a:funcName] = a:funcName
+endfunction
+
+function! genutils#RemoveFromFCShellPre(funcName)
+  if has_key(s:fcShellPreFuncs, a:funcName)
+    unlet s:fcShellPreFuncs[a:funcName]
+  endif
+endfunction
+
+let s:defFCShellInstalled = 0
+function! genutils#DefFCShellInstall()
+  if ! s:defFCShellInstalled
+    aug DefFCShell
+    au!
+    au FileChangedShell * nested call genutils#DefFileChangedShell()
+    aug END
+  endif
+  let s:defFCShellInstalled = s:defFCShellInstalled + 1
+endfunction
+
+function! genutils#DefFCShellUninstall()
+  if s:defFCShellInstalled <= 0
+    return
+  endif
+  let s:defFCShellInstalled = s:defFCShellInstalled - 1
+  if ! s:defFCShellInstalled
+    aug DefFCShell
+    au!
+    aug END
+  endif
+endfunction
+
+function! genutils#DefFileChangedShell()
+  let autoread = s:InvokeFuncs(s:fcShellPreFuncs)
+  let bufNo = expand('<abuf>') + 0
+  let fName = expand('<afile>')
+  let msg = ''
+  let v:fcs_choice = ''
+  if getbufvar(bufNo, '&modified')
+    let v:fcs_choice = 'ask'
+  elseif ! autoread
+    let v:fcs_choice = 'ask'
+  else
+    let v:fcs_choice = 'reload'
+  endif
+  return
+endfunction
+
+function! s:InvokeFuncs(funcList)
+  let autoread = &autoread
+  if len(a:funcList) != 0
+    for nextFunc in keys(a:funcList)
+      let result = call(nextFunc, [])
+      if result != -1
+        let autoread = autoread || result
+      endif
+    endfor
+  endif
+  return autoread
+endfunction
+" FileChangedShell handling }}}
+
+
+" Sign related utilities {{{
+function! genutils#CurLineHasSign()
+  let signs = genutils#GetVimCmdOutput('sign place buffer=' . bufnr('%'), 1)
+  return (match(signs,
+        \ 'line=' . line('.') . '\s\+id=\d\+\s\+name=VimBreakPt') != -1)
+endfunction
+
+function! genutils#ClearAllSigns()
+  let signs = genutils#GetVimCmdOutput('sign place buffer=' . bufnr('%'), 1)
+  let curIdx = 0
+  let pat = 'line=\d\+\s\+id=\zs\d\+\ze\s\+name=VimBreakPt'
+  let id = 0
+  while curIdx != -1 && curIdx < strlen(signs)
+    let id = matchstr(signs, pat, curIdx)
+    if id != ''
+      exec 'sign unplace ' . id . ' buffer=' . bufnr('%')
+    endif
+    let curIdx = matchend(signs, pat, curIdx)
+  endwhile
+endfunction
+" }}}
+
+let s:UNPROTECTED_CHAR_PRFX = '\%(\%([^\\]\|^\)\\\%(\\\\\)*\)\@<!' " Doesn't eat slashes.
+"let s:UNPROTECTED_CHAR_PRFX = '\\\@<!\%(\\\\\)*' " Eats slashes.
+function! genutils#CrUnProtectedCharsPattern(chars, ...)
+  let capture = (a:0 > 0?1:0)
+  let regex = s:UNPROTECTED_CHAR_PRFX
+  let chars = a:chars
+  if strlen(chars) > 1
+    let chars = '['.chars.']'
+  endif
+  if capture
+    let chars = '\('.chars.'\)'
+  endif
+  return regex.chars
+endfunction
+
+function! genutils#PromptForElement(array, default, msg, skip, useDialog,
+      \ nCols)
+  let nCols = a:nCols
+  let index = 0
+  let line = ""
+  let element = ""
+  let optionsMsg = ""
+  let colWidth = &columns / nCols - 1 " Leave a margin of one column as a gap.
+  let curCol = 1
+  let nElements = len(a:array)
+  let newArray = [] " Without the skip element.
+  if index(a:array, a:skip) != -1
+    let nElements = nElements - 1
+  endif
+  for element in a:array
+    if element ==# a:skip
+      continue
+    endif
+    call add(newArray, element)
+    let element = strpart(index."   ", 0, 4) . element
+    let eleColWidth = (strlen(element) - 1) / colWidth + 1
+    " Fill up the spacer for the rest of the partial column.
+    let element = element . genutils#GetSpacer(
+          \ eleColWidth * (colWidth + 1) - strlen(element) - 1)
+    let wouldBeLength = strlen(line) + strlen(element) + 1
+    if wouldBeLength > (curCol * (colWidth + eleColWidth)) &&
+          \ wouldBeLength > &columns
+      let splitLine = 2 " Split before adding the new element.
+    elseif curCol == nCols
+      let splitLine = 1 " Split after adding the new element.
+    else
+      let splitLine = 0
+    endif
+    if splitLine == 2
+      if strlen(line) == &columns
+        " Remove the last space as it otherwise results in an extra empty line
+        " on the screen.
+        let line = strpart(line, 0, strlen(line) - 1)
+      endif
+      let optionsMsg = optionsMsg . line . "\n"
+      let line = element . ' '
+      let curCol = strlen(element) / (colWidth + 1)
+    else
+      let line = line . element . ' '
+      if splitLine == 1
+        if strlen(line) == &columns
+          " Remove the last space as it otherwise results in an extra empty line
+          " on the screen.
+          let line = strpart(line, 0, strlen(line) - 1)
+        endif
+        let curCol = 0 " Reset col count.
+        let optionsMsg = optionsMsg . line . "\n"
+        let line = ""
+      endif
+    endif
+    let curCol = curCol + 1
+    let index = index + 1
+  endfor
+  " Finally if there is anything left in line, then append that too.
+  if line.'' != ''
+    let optionsMsg = optionsMsg . line . "\n"
+    let line = ""
+  endif
+
+  " Default index or empty string.
+  let default = ''
+  if type(a:default) == 0
+    let default = a:default
+  elseif a:default.'' != ''
+    let default = index(a:array, a:default)
+  endif
+  if a:default == -1
+    let default = ''
+  endif
+
+  while !exists("selectedElement")
+    if a:useDialog
+      let s:selection = inputdialog(optionsMsg . a:msg, default)
+    else
+      let s:selection = input(optionsMsg . a:msg, default)
+    endif
+    if s:selection.'' == ''
+      let selectedElement = ''
+      let s:selection = -1
+    else
+      let s:selection = (s:selection !~# '^\d\+$') ? -1 : (s:selection + 0)
+      if s:selection >= 0 && s:selection < nElements
+        let selectedElement = newArray[s:selection]
+      else
+        echohl ERROR | echo "\nInvalid selection, please try again" |
+              \ echohl NONE
+      endif
+    endif
+    echo "\n"
+  endwhile
+  return selectedElement
+endfunction
+
+let s:selection = -1
+function! genutils#GetSelectedIndex()
+  return s:selection
+endfunction
+
+" Always match() with 'ignorecase' and 'smartcase' off.
+function! s:Match(expr, pat, start)
+  let _ic = &ignorecase
+  let _scs = &smartcase
+  let result = -1
+  try
+    set noignorecase
+    set nosmartcase
+    let result = match(a:expr, a:pat, a:start)
+  finally
+    let &ignorecase = _ic
+    let &smartcase = _scs
+  endtry
+  return result
+endfunction
+" Restore cpo.
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim6:fdm=marker et
diff --git a/autoload/lookupfile.vim b/autoload/lookupfile.vim
new file mode 100644 (file)
index 0000000..0f6332c
--- /dev/null
@@ -0,0 +1,472 @@
+" lookupfile.vim: See plugin/lookupfile.vim
+
+" Make sure line-continuations won't cause any problem. This will be restored
+"   at the end
+let s:save_cpo = &cpo
+set cpo&vim
+
+" Some onetime initialization of variables
+if !exists('s:myBufNum')
+  let s:windowName = '[Lookup File]'
+  let s:myBufNum = -1
+  let s:popupIsHidden = 0
+endif
+let g:lookupfile#lastPattern = ""
+let g:lookupfile#lastResults = []
+let g:lookupfile#lastStatsMsg = []
+let g:lookupfile#recentFiles = []
+
+function! lookupfile#OpenWindow(bang, initPat)
+  let origWinnr = winnr()
+  let _isf = &isfname
+  let _splitbelow = &splitbelow
+  set nosplitbelow
+  try
+    if s:myBufNum == -1
+      " Temporarily modify isfname to avoid treating the name as a pattern.
+      set isfname-=\
+      set isfname-=[
+      if exists('+shellslash')
+        call genutils#OpenWinNoEa("1sp \\\\". escape(s:windowName, ' '))
+      else
+        call genutils#OpenWinNoEa("1sp \\". escape(s:windowName, ' '))
+      endif
+      let s:myBufNum = bufnr('%')
+    else
+      let winnr = bufwinnr(s:myBufNum)
+      if winnr == -1
+        call genutils#OpenWinNoEa('1sb '. s:myBufNum)
+      else
+        let wasVisible = 1
+        exec winnr 'wincmd w'
+      endif
+    endif
+  finally
+    let &isfname = _isf
+    let &splitbelow = _splitbelow
+  endtry
+
+  call s:SetupBuf()
+  let initPat = ''
+  if a:bang != ''
+    let initPat = ''
+  elseif a:initPat != ''
+    let initPat = a:initPat
+  elseif g:lookupfile#lastPattern != '' && g:LookupFile_PreserveLastPattern
+    let initPat = g:lookupfile#lastPattern
+  endif
+  $
+  if getline('.') !=# initPat
+    silent! put=''
+    call setline('.', initPat)
+  endif
+  startinsert!
+  if !g:LookupFile_OnCursorMovedI
+    " This is a hack to bring up the popup immediately, while reopening the
+    " window, just for a better response.
+    aug LookupFileCursorHoldImm
+      au!
+      au CursorMovedI <buffer> nested exec 'doautocmd LookupFile CursorHoldI' |
+            \ au! LookupFileCursorHoldImm
+    aug END
+  endif
+  call s:LookupFileSet()
+  aug LookupFileReset
+    au!
+    au CursorMovedI <buffer> call <SID>LookupFileSet()
+    au CursorMoved <buffer> call <SID>LookupFileSet()
+    au WinEnter <buffer> call <SID>LookupFileSet()
+    au TabEnter <buffer> call <SID>LookupFileSet()
+    au WinEnter * call <SID>LookupFileReset(0)
+    au TabEnter * call <SID>LookupFileReset(0)
+    au CursorMoved * call <SID>LookupFileReset(0)
+    " Better be safe than sorry.
+    au BufHidden <buffer> call <SID>LookupFileReset(1)
+  aug END
+endfunction
+
+function! lookupfile#CloseWindow()
+  if bufnr('%') != s:myBufNum
+    return
+  endif
+
+  call s:LookupFileReset(1)
+  close
+endfunction
+
+function! lookupfile#ClearCache()
+  let g:lookupfile#lastPattern = ""
+  let g:lookupfile#lastResults = []
+endfunction
+
+function! s:LookupFileSet()
+  if bufnr('%') != s:myBufNum || exists('s:_backspace')
+    return
+  endif
+  let s:_backspace = &backspace
+  set backspace=start
+  let s:_completeopt = &completeopt
+  set completeopt+=menuone
+  let s:_updatetime = &updatetime
+  let &updatetime = g:LookupFile_UpdateTime
+endfunction
+
+function! s:LookupFileReset(force)
+  if a:force
+    aug LookupFileReset
+      au!
+    aug END
+  endif
+  " Ignore the event while in the same buffer.
+  if exists('s:_backspace') && (a:force || (bufnr('%') != s:myBufNum))
+    let &backspace = s:_backspace
+    let &completeopt = s:_completeopt
+    let &updatetime = s:_updatetime
+    unlet s:_backspace s:_completeopt s:_updatetime
+  endif
+endfunction
+
+function! s:HidePopup()
+  let s:popupIsHidden = 1
+  return "\<C-E>"
+endfunction
+
+function! lookupfile#IsPopupHidden()
+  return s:popupIsHidden
+endfunction
+
+function! s:SetupBuf()
+  call genutils#SetupScratchBuffer()
+  resize 1
+  setlocal wrap
+  setlocal bufhidden=hide
+  setlocal winfixheight
+  setlocal wrapmargin=0
+  setlocal textwidth=0
+  setlocal completefunc=lookupfile#Complete
+  syn clear
+  set ft=lookupfile
+  " Setup maps to open the file.
+  inoremap <silent> <buffer> <expr> <C-E> <SID>HidePopup()
+  inoremap <silent> <buffer> <expr> <CR> <SID>AcceptFile(0, "\<CR>")
+  inoremap <silent> <buffer> <expr> <C-O> <SID>AcceptFile(1, "\<C-O>")
+  " This prevents the "Whole line completion" from getting triggered with <BS>,
+  " however this might make the dropdown kind of flash.
+  inoremap <buffer> <expr> <BS>       pumvisible()?"\<C-E>\<BS>":"\<BS>"
+  inoremap <buffer> <expr> <S-BS>       pumvisible()?"\<C-E>\<BS>":"\<BS>"
+  " Make <C-Y> behave just like <CR>
+  imap     <buffer> <C-Y>      <CR>
+  if g:LookupFile_EscCancelsPopup
+    inoremap <buffer> <expr> <Esc>      pumvisible()?"\<C-E>\<C-C>":"\<Esc>"
+  endif
+  inoremap <buffer> <expr> <silent> <Down> <SID>GetCommand(1, 1, "\<C-N>",
+        \ "\"\\<Lt>C-N>\"")
+  inoremap <buffer> <expr> <silent> <Up> <SID>GetCommand(1, 1, "\<C-P>",
+        \ "\"\\<Lt>C-P>\"")
+  inoremap <buffer> <expr> <silent> <PageDown> <SID>GetCommand(1, 0,
+        \ "\<PageDown>", '')
+  inoremap <buffer> <expr> <silent> <PageUp> <SID>GetCommand(1, 0,
+        \ "\<PageUp>", '')
+  nnoremap <silent> <buffer> o :OpenFile<CR>
+  nnoremap <silent> <buffer> O :OpenFile!<CR>
+  command! -buffer -nargs=0 -bang OpenFile
+        \ :call <SID>OpenCurFile('<bang>' != '')
+  command! -buffer -nargs=0 -bang AddPattern :call <SID>AddPattern()
+  nnoremap <buffer> <silent> <Plug>LookupFile :call lookupfile#CloseWindow()<CR>
+  inoremap <buffer> <silent> <Plug>LookupFile <C-E><C-C>:call lookupfile#CloseWindow()<CR>
+
+  aug LookupFile
+    au!
+    if g:LookupFile_ShowFiller
+      exec 'au' (g:LookupFile_OnCursorMovedI ? 'CursorMovedI' : 'CursorHoldI')
+            \ '<buffer> call <SID>ShowFiller()'
+    endif
+    exec 'au' (g:LookupFile_OnCursorMovedI ? 'CursorMovedI' : 'CursorHoldI')
+          \ '<buffer> call lookupfile#LookupFile(0)'
+  aug END
+endfunction
+
+function! s:GetCommand(withPopupTrigger, withSkipPat, actCmd, innerCmd)
+  let cmd = ''
+  if a:withPopupTrigger && !pumvisible()
+    let cmd .= "\<C-X>\<C-U>"
+  endif
+  let cmd .= a:actCmd. "\<C-R>=(getline('.') == lookupfile#lastPattern) ? ".
+        \ a:innerCmd." : ''\<CR>"
+  return cmd
+endfunction
+
+function! s:AddPattern()
+  if g:LookupFile_PreservePatternHistory
+    silent! put! =g:lookupfile#lastPattern
+    $
+  endif
+endfunction
+
+function! s:AcceptFile(splitWin, key)
+  if s:popupIsHidden
+    return a:key
+  endif
+  if !pumvisible()
+    call lookupfile#LookupFile(0, 1)
+  endif
+  let acceptCmd = ''
+  if type(g:LookupFile_LookupAcceptFunc) == 2 ||
+        \ (type(g:LookupFile_LookupAcceptFunc) == 1 &&
+        \  substitute(g:LookupFile_LookupAcceptFunc, '\s', '', 'g') != '')
+    let acceptCmd = call(g:LookupFile_LookupAcceptFunc, [a:splitWin, a:key])
+  else
+    let acceptCmd = lookupfile#AcceptFile(a:splitWin, a:key)
+  endif
+
+  return (!pumvisible() ? "\<C-X>\<C-U>" : '').acceptCmd
+endfunction
+
+function! s:IsValid(fileName)
+  if bufnr('%') != s:myBufNum || a:fileName == ''
+    return 0
+  endif
+  if !filereadable(a:fileName) && !isdirectory(a:fileName)
+    if g:LookupFile_AllowNewFiles
+      " Check if the parent directory exists, then we can create a new buffer
+      " (Ido feature)
+      let parent = fnamemodify(a:fileName, ':h')
+      if parent == '' || (parent != '' && !isdirectory(parent))
+        return 1
+      endif
+    endif
+    return 0
+  endif
+  return 1
+endfunction
+
+function! lookupfile#AcceptFile(splitWin, key)
+  if len(g:lookupfile#lastResults) == 0 && !s:IsValid(getline('.'))
+    return "\<C-O>:echohl ErrorMsg | echo 'No such file or directory' | echohl NONE\<CR>"
+  endif
+
+  " Skip the first match, which is essentially the same as pattern.
+  let nextCmd = "\<C-N>\<C-R>=(getline('.') == lookupfile#lastPattern)?\"\\<C-N>\":''\<CR>"
+  let acceptCmd = "\<C-Y>\<Esc>:AddPattern\<CR>:OpenFile".(a:splitWin?'!':'').
+        \ "\<CR>"
+  if getline('.') ==# g:lookupfile#lastPattern
+    if len(g:lookupfile#lastResults) == 0
+      " FIXME: shouldn't this be an error?
+      let acceptCmd = acceptCmd
+    elseif len(g:lookupfile#lastResults) == 1 || g:LookupFile_AlwaysAcceptFirst
+      " If there is only one file, we will also select it (if not already
+      " selected)
+      let acceptCmd = nextCmd.acceptCmd
+    else
+      let acceptCmd = nextCmd
+    endif
+  endif
+
+  return acceptCmd
+endfunction
+
+function! s:OpenCurFile(splitWin)
+  let fileName = getline('.')
+  if fileName =~ '^\s*$'
+    return
+  endif
+  if !s:IsValid(fileName)
+    echohl ErrorMsg | echo 'No such file or directory' | echohl NONE
+  endif
+
+  if type(g:LookupFile_LookupNotifyFunc) == 2 ||
+        \ (type(g:LookupFile_LookupNotifyFunc) == 1 &&
+        \  substitute(g:LookupFile_LookupNotifyFunc, '\s', '', 'g') != '')
+    call call(g:LookupFile_LookupNotifyFunc, [])
+  endif
+  call lookupfile#CloseWindow()
+
+  " Update the recent files list.
+  if g:LookupFile_RecentFileListSize > 0
+    let curPos = index(g:lookupfile#recentFiles, fileName)
+    call add(g:lookupfile#recentFiles, fileName)
+    if curPos != -1
+      call remove(g:lookupfile#recentFiles, curPos)
+    elseif len(g:lookupfile#recentFiles) > g:LookupFile_RecentFileListSize
+      let g:lookupfile#recentFiles = g:lookupfile#recentFiles[
+            \ -g:LookupFile_RecentFileListSize :]
+    endif
+  endif
+
+  let bufnr = genutils#FindBufferForName(fileName)
+  let winnr = bufwinnr(bufnr)
+  if winnr == -1 && g:LookupFile_SearchForBufsInTabs
+      for i in range(tabpagenr('$'))
+        if index(tabpagebuflist(i+1), bufnr) != -1
+          " Switch to the tab and set winnr.
+          exec 'tabnext' (i+1)
+          let winnr = bufwinnr(bufnr)
+        endif
+    endfor
+  endif
+  if winnr != -1
+    exec winnr.'wincmd w'
+  else
+    let splitOpen = 0
+    if &switchbuf ==# 'split' || a:splitWin
+      let splitOpen = 1
+    endif
+    " First try opening as a buffer, if it fails, we will open as a file.
+    try
+      if bufnr == -1
+        throw ''
+      endif
+      exec (splitOpen?'s':'').'buffer' bufnr
+    catch /^Vim\%((\a\+)\)\=:E325/
+      " Ignore, this anyway means the file was found.
+    catch
+      try
+        exec (splitOpen?'split':'edit') fileName
+      catch /^Vim\%((\a\+)\)\=:E325/
+        " Ignore, this anyway means the file was found.
+      endtry
+    endtry
+  endif
+endfunction
+
+function! s:ShowFiller()
+  return lookupfile#LookupFile(1)
+endfunction
+
+function! lookupfile#Complete(findstart, base)
+  if a:findstart
+    return 0
+  else
+    call lookupfile#LookupFile(0, 1, a:base)
+    return g:lookupfile#lastStatsMsg+g:lookupfile#lastResults
+  endif
+endfunction
+
+function! lookupfile#LookupFile(showingFiller, ...)
+  let generateMode = (a:0 == 0 ? 0 : a:1)
+  if generateMode
+    let pattern = (a:0 > 1) ? a:2 : getline('.')
+  else
+    let pattern = getline('.')
+    " The normal completion behavior is to stop completion when cursor is moved.
+    if col('.') == 1 || (col('.') != col('$'))
+      return ''
+    endif
+  endif
+  if pattern == '' || (pattern ==# g:lookupfile#lastPattern && pumvisible())
+    return ''
+  endif
+
+  if s:popupIsHidden && g:lookupfile#lastPattern ==# pattern
+    return ''
+  endif
+  let s:popupIsHidden = 0
+
+  let statusMsg = ''
+  if pattern == ' '
+    if len(g:lookupfile#recentFiles) == 0
+      let statusMsg = '<<< No recent files >>>'
+      let files = []
+    else
+      let statusMsg = '<<< Showing '.len(g:lookupfile#recentFiles).' recent files >>>'
+      let files = reverse(copy(g:lookupfile#recentFiles))
+    endif
+  elseif strlen(pattern) < g:LookupFile_MinPatLength
+    let statusMsg = '<<< Type at least '.g:LookupFile_MinPatLength.
+          \ ' characters >>>'
+    let files = []
+  " We ignore filler when we have the result in hand.
+  elseif g:lookupfile#lastPattern ==# pattern
+    " This helps at every startup as we start with the previous pattern.
+    let files = g:lookupfile#lastResults
+  elseif a:showingFiller
+    " Just show a filler and return. We could return this as the only match, but
+    " unless 'completeopt' has "menuone", menu doesn't get shown.
+    let statusMsg = '<<< Looking up files... hit ^C to break >>>'
+    let files = []
+  else
+    if type(g:LookupFile_LookupFunc) == 2 ||
+          \ (type(g:LookupFile_LookupFunc) == 1 &&
+          \  substitute(g:LookupFile_LookupFunc, '\s', '', 'g') != '')
+      let files = call(g:LookupFile_LookupFunc, [pattern])
+    else
+      let _tags = &tags
+      try
+        let &tags = eval(g:LookupFile_TagExpr)
+        let taglist = taglist(g:LookupFile_TagsExpandCamelCase ?
+              \ lookupfile#ExpandCamelCase(pattern) : pattern)
+      catch
+        echohl ErrorMsg | echo "Exception: " . v:exception | echohl NONE
+        return ''
+      finally
+        let &tags = _tags
+      endtry
+
+      " Show the matches for what is typed so far.
+      if g:LookupFile_UsingSpecializedTags
+        let files = map(taglist, '{'.
+              \ '"word": fnamemodify(v:val["filename"], ":p"), '.
+              \ '"abbr": v:val["name"], '.
+              \ '"menu": fnamemodify(v:val["filename"], ":h"), '.
+              \ '"dup": 1, '.
+              \ '}')
+      else
+        let files = map(taglist, 'fnamemodify(v:val["filename"], ":p")')
+      endif
+    endif
+
+    let pat = g:LookupFile_FileFilter
+    if pat != ''
+      call filter(files, '(type(v:val) == 4) ? v:val["word"] !~ pat : v:val !~ pat')
+    endif
+
+    if g:LookupFile_SortMethod ==# 'alpha'
+      " When UsingSpecializedTags, sort by the actual name (Timothy, Guo
+      " (firemeteor dot guo at gmail dot com)).
+      if type(get(files, 0)) == 4
+        call sort(files, "s:CmpByName")
+      else
+        call sort(files)
+      endif
+    endif
+    let g:lookupfile#lastPattern = pattern
+    let g:lookupfile#lastResults = files
+  endif
+  if statusMsg == ''
+    if len(files) > 0
+      let statusMsg = '<<< '.len(files).' Matching >>>'
+    else
+      let statusMsg = '<<< None Matching >>>'
+    endif
+  endif
+  let msgLine = [{'word': pattern, 'abbr': statusMsg, 'menu': pattern}]
+  let g:lookupfile#lastStatsMsg = msgLine
+  if !generateMode
+    call complete(1, msgLine+files)
+  endif
+  return ''
+endfunction
+
+function! lookupfile#ExpandCamelCase(str)
+  let pat = a:str
+  " Check if there are at least two consecutive uppercase letters to turn on
+  " the CamelCase expansion.
+  if match(a:str, '\u\u') != -1
+    let pat = '\C'.substitute(a:str, '\u\+',
+          \ '\=substitute(submatch(0), ".", '."'".'&\\U*'."'".', "g")', 'g')
+    let @*=pat
+  endif
+  return pat
+endfunction
+
+function! s:CmpByName(i1, i2)
+  let ileft = a:i1["abbr"]
+  let iright = a:i2["abbr"]
+  return ileft == iright ? 0 : ileft > iright ? 1 : -1
+endfunc
+
+" Restore cpo.
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim6:fdm=marker et sw=2
diff --git a/doc/lookupfile.txt b/doc/lookupfile.txt
new file mode 100644 (file)
index 0000000..16161ec
--- /dev/null
@@ -0,0 +1,765 @@
+*lookupfile.txt* Lookup files using Vim7 ins-completion
+                    Requires Vim 7.0
+            Last Change: 24-Aug-2007 @ 18:25
+                 Author: Hari Krishna Dara (hari.vim at gmail dot com)
+
+                                            *lookupfile-introduction*
+                                            *lookupfile-plugin*
+Lookupfile is a very simple approach to opening files by typing a pattern to
+represent the file you are looking for, and selecting a file from the completion
+dropdown to open in the current Vim session. It provides a consistent interface
+to lookup files by various means (tags, path, buffers, external tools etc.). It
+uses the new Vim7 insert-mode completion mechanism to show matching files. It
+is a new tool 
+
+
+The default functionality of the plugin is to lookup filenames from the tag
+files, by matching the pattern against tag names. However, using the standard
+tags (generated by ctags) can be very slow at best, so it is recommended to use
+specially generated tagfiles, that are meant only for finding the filenames. You
+don't however need to generate tag files at all to use the plugin, as there are
+ways to lookup files from sources other than tag files.
+
+==============================================================================
+OVERVIEW                                    *lookupfile-overview*
+
+|lookupfile-installation|     Installing the plugin.
+
+|lookupfile-usage|            A brief usage to get you quickly started.
+
+|lookupfile-maps|             Mappings defined in the lookup window.
+
+|lookupfile-tags|             Creating index of files for a fast lookup.
+
+|lookupfile-settings|         An explanation of various configure options.
+
+|lookupfile-extend|           Features that make the plugin extensible.
+
+|lookupfile-known-issues|     A list of known issues.
+
+|lookupfile-wishlist|         Wishlist items that may be worth doing for a future
+                              version.
+
+|lookupfile-changes|          A change list for current version from the previous
+                              versions.
+
+|lookupfile-acknowledgements| Acknowledgements.
+==============================================================================
+
+                                            *lookupfile-installation*
+The distribution comes as a zipfile that can be extracted straight into your
+runtime directory (vimfiles/.vim). Please note that it depends on genutils
+plugin that needs to be downloaded separately and installed. Also note that
+the version of genutils that this plugin depends on is a newer autoload
+version that is not backwards compatible with the older versions of genutils.
+This means, if you have other plugins that depend on the non-autoload version
+of genutils, you need to follow special instructions given in genutils such
+that you can have both installed at the same time.
+
+To install the help file, you need to run :helptags command on your runtime
+doc directory.
+
+                                            *lookupfile-map*
+                                            *lookupfile-default-cmd*
+                                            *LookupFile-command*
+                                            *:LookupFile*
+Once installed, you can optionally choose a key to be used to open the
+lookupfile window. The default is <F5>. To change this value, choose a key of
+your liking, say <A-S-L>, and add the below line to your vimrc: >
+
+       nmap <unique> <silent> <A-S-L> <Plug>LookupFile
+<
+Note that the plugin creates a mapping for the insert-mode as well, so unless
+you also explicitly specify a mapping for insert mode, the default <F5> will get
+mapped to invoke lookupfile in the insert-mode. >
+
+       imap <unique> <silent> <A-S-L> <C-O><Plug>LookupFile
+<
+The <Plug>LookupFile mapping is mapped to the :LookupFile command, which always
+assumes the last :LU command you use. This means, after using |:LUWalk|,
+pressing <F5> would result in executing the :LUWalk command until you type
+another command. Take a look at the |LookupFile_DefaultCmd| to specify the
+initial command that this map should get assigned to and
+|LookupFile_EnableRemapCmd| to disable this behavior. If you want a map to
+always execute a specific command, you can also create additional maps. E.g.,
+if you want <F7> to always execute :LUTags, irrespective of what command you
+last used, then you can define something like:
+>
+        nnoremap <silent> <F7> :LUTags<CR>
+<
+You can also disable the default map completely, see
+|LookupFile_DisableDefaultMap| for details.
+==============================================================================
+
+                                            *lookupfile-usage*
+To lookup a file, press the assigned key (default: F5) or use the :LookupFile
+command. This will open a small window above the current window, where you can
+type in a regex, and the matching filenames will be shown in the Vim7 completion
+style. Selecting a matching file and pressing Enter (<CR>) will open it in the
+previous window (or ^O (C-O) will open it in a split window).  You can also
+press <Esc> and scroll back to previous filenames to press o or O (or use
+:OpenFile[!]) to open the file in the previous window or a new window,
+respectively.
+
+If you want to quickly close the [Lookup File] window without making a file
+selection, you can press the same key that you assigned to open the plugin, to
+also cancel the completion menu and close the window.
+
+Note that this works only when you use the <Plug> mapping to create a mapping
+(see |lookupfile-map|), not when you assign a mapping directly while disablign
+the default <Plug> mapping (see |LookupFile_DisableDefaultMap|).
+
+Note that the completion menu will be shown only when you are in the insert
+mode and the cursor is at the end of the line (this is the normal Vim behavior).
+
+Here is a summary of all the commands that the plugin defines. All commands
+accept the initial pattern as an argument, and support appropriate
+|command-completion| mode.
+
+        Command         Lookup source ~
+        LUTags          Lookup files from tag files. This is a like a fast GNU
+                        find on name. It can lookup files from any Vim
+                        compatible tag file (which includes those from ctags),
+                        but at the moment it is advisable to generate
+                        specialized tag files using :find command (see
+                        |lookupfile-tags|).
+        LUPath          Lookup files from 'path' using |globpath()|. The :find
+                        command while being able to lookup files from 'path', it
+                        doesn't provide any completion mechanism, and it is
+                        clumsy when there are multiple files with the same name.
+                        The :find command doesn't even accept a pattern.
+        LUBufs          Lookup loaded files (buffers) using |bufname()|. This is
+                        a great addition to whatever buffer-explorer you are
+                        using. When there are too many buffers open, this
+                        sometimes makes it easy to find the right buffer, by
+                        typing part of its name.
+        LUWalk          Lookup files using |glob()|. This works like the Emacs
+                        ido.el, allowing you to walk up and down a path looking
+                        for files. If you use the filename completion with :edit
+                        command, then you will find this a lot more convenient
+                        and faster to use.
+        LUArgs          Lookup files from |:args| list.
+        LookupFile      A shortcut to the last command used from above set.
+
+It is also very easy to add new commands to or customize the plugin at various
+levels. At the simplest, you can create custom command on top of the above that
+either pass dynamic arguments or tmporarily change the settings (e.g., you could
+have a command that will start LUPath with the 'path' temporarily changed to
+find only the include header files). You can also take advantage of the the
+|lookupfile-extend| features to add new commands that lookup files from a
+completely new source. You can even add commands that lookup something
+completely different than a file (like e.g., a spelling using spellsuggest()).
+
+The :LookupFile and all the above commands accept an argument which is treated
+as the initial pattern. Specify a bang(!) after the command to always start with
+an empty line.
+
+                                            *:LUTags*
+                                            *lookupfile-from-tags-files*
+The LUTags command provides a fast lookup of files from tags files. This works
+similar to the "Open Resource" command in eclipse where you can type part of the
+name and all the resources whose name has matching part are shown for selection.
+
+To use the :LUTags command, it is recommended to generate and maintain
+separate tags files containing only filenames, for this plugin to work
+efficiently (see |lookupfile-tags|). Though it will work with the regular tags
+files, it could be very slow (due to the current performance of |taglist()| and
+might not return the right set of filenames. Configure the tag expression such
+that the plugin will use the right tag files. It is any valid Vim {rhs}, the
+evaluated value should be a valid 'tags' value. For a static string, use extra
+quotes. Ex:
+>
+        let g:LookupFile_TagExpr = string('./filenametags')
+
+< or >
+        let g:myLookupFileTagExpr = './filenanmetags'
+        let g:LookupFile_TagExpr = 'g:myLookupFileTagExpr'
+<
+Also see |LookupFile_UsingSpecializedTags| to enable better formatting of
+results once these specialized tags are generated and lookupfile is configured
+to take advantage of them.
+                                            *:LUPath*
+                                            *lookupfile-find-cmd-alternative*
+The LUPath command is a complete replacement for the built-in |:find| command.
+The completion popup alone is a good enough reason to use this command instead
+of the :find, but since it also recognizes the 'ignorecase' and 'smartcase'
+settings it makes it easier to type and still be able to do exact match when
+required.
+
+                                            *lookupfile-ido*
+The LUWalk and LUBufs commands are special in that they provide features that
+will be very fimilar to those who used Emacs ido lisp package.
+
+Similar to the ido, you can quickly switch from file lookup to buffer lookup
+using <C-B> and from buffer lookup to file lookup using <C-F> while in insert
+mode.
+
+                                            *:LUBufs*
+                                            *lookupfile-buffer-cmd-alternative*
+LUBufs command allows you to quickly search the already loaded buffers for a
+match and open it, so it can be used in the place of the built-in |:buffer|
+command and provides the same matching semantics by default. Take a look at the
+settings that start with "LookupFile_Bufs_" in their name to configure the
+behavior of this command. The :LUBufs command also supports an initial pattern
+as an arguments and supports filename completion.
+
+A good use case for :LUBufs command is when you have several files named
+similarly (e.g., having several buffers named Makefile or build.xml is very
+common). Using a traditional buffer explorer plugin, finding the right file can
+become cumbersome when you have a large set of buffers opened. In this case, the
+:LUBufs command can help you locate the buffer much faster, as only the buffers
+that match what you specify will be shown with their path clearly visible.
+
+                                            *:LUWalk*
+                                            *lookupfile-walk-path*
+The LUWalk command is very useful to quickly navigate or "walk" filesystem
+paths. The plugin provides a popup with matches for each path component as you
+type in a partial string. The matches are based on the Vim |glob()| expression
+rules.  The :LUWalk command also accepts the initial directory name with or
+without a pattern as an argument, and supports directory name completion.
+
+When no root is specified, the match starts from the current directory.
+Pressing <Tab> before typing in any pattern will result in inserting the
+directory of the last file automatically. Type an extra "/" after a directory
+name to have only the directories shown in the results.
+
+To accept the first entry in the matches, you can simply press <CR> or <C-O>,
+and if the entry happens to be a directory, new matches will be shown for that
+directory.
+
+                                            *lookupfile-recent-files*
+The plugin saves a list of most recently opened files using any of the above
+commands. To trigger showing this list, simply use a space by itself. For more
+details, see |LookupFile_RecentFileListSize|.
+==============================================================================
+
+                                            *lookupfile-maps*
+The maps that are available in the [Lookup File] window are listed below. Some
+of them are the same from |popupmenu-keys|, but have been modified to behave
+slightly differently. All the below maps are applicable only when the popup menu
+is visible, otherwise, they should have their default behavior.
+        Key         mode    Action ~
+        <CR>        i       Opens the selected file in the previous window/
+                            Select the first file.
+        <C-Y>       i       Same as <CR>
+        <C-O>       i       Same as <CR>, except that the file opens in a split
+                            window.
+        <Down>      i       Selects next file. Same as |i_CTRL-N|.
+        <Up>        i       Selects previous file. Same as |i_CTRL-P|.
+        <PageDown>  i       Selects a file several entries down.
+        <PageUp>    i       Selects a file several entries up.
+        <Esc>       i       Stops completion and insert mode, and restores
+                            pattern.
+        <BS>        i       Stops completion, restores pattern and executes
+        o           n       Opens the current file in the previous window.
+        O           n       Same as o, except the file opens in a split window.
+==============================================================================
+
+                                            *lookupfile-tags*
+Unless you want to lookup files from alternate sources such as 'path', you
+should generate special tags files for the lookup from filesystem to work well.
+
+It is very easy to generate a tag file for an efficient lookup of filenames.
+If you have Unixy tools installed, you can run the below shell command to
+generate a tags file (you can put it in a shell-script and schedule it to be run
+every now and then to keep this file up to date) >
+
+        (echo "!_TAG_FILE_SORTED       2       /2=foldcase/";
+         (find . -type f -printf "%f\t%p\t1\n" | \
+         sort -f)) > ./filenametags
+
+Typically you would want to exclude generated files such as .class and
+.o. You can do this easily by specifying additional expressions to
+the find command (see manpage for find command). E.g., replace the find
+command above with: >
+
+        find . -not -iname "*.class" -type f -printf "%f\t%p\t1\n" \
+
+< or, with something more useful: >
+
+        find . \( -name .svn -o -wholename ./classes \) -prune -o -not -iregex '.*\.\(jar\|gif\|jpg\|class\|exe\|dll\|pdd\|sw[op]\|xls\|doc\|pdf\|zip\|tar\|ico\|ear\|war\|dat\).*' -type f -printf "%f\t%p\t1\n" \
+<
+==============================================================================
+
+                                            *lookupfile-settings*
+The settings are global variables that you can set in your vimrc. All settings
+are optional, as they all have default values, but it is highly recommended that
+you fine tune the g:LookupFile_TagExpr setting, as per |lookupfile-tags|.
+                                            *LookupFile_DefaultCmd*
+- This specifies the |lookupfile-command| that should be invoked when the
+  |LookupFile-command| or |lookupfile-map| is used. This defaults to ":LUTags",
+  but you can change it to say, ":LUWalk". The :LookupFile command always
+  assumes the last command that you used, so this setting only specifies the
+  command to be invoked for the first time.
+  Ex: >
+        let g:LookupFile_DefaultCmd = ':LUWalk'
+<
+                                            *LookupFile_EnableRemapCmd*
+- When this is set (the default), the :LookupFile command takes the form of
+  the last command that is used. Disabling this while setting
+  |LookupFile_DefaultCmd| allows you to configure a custom command as the
+  command to run when you press the <Plug>LookupFile mapping. See
+  |LookupFile-command| for more details.
+
+                                            *LookupFile_DisableDefaultMap*
+- Setting this flag turns off the default maps created by the plugin such that
+  the user has full control on what maps to what command. >
+        let g:LookupFile_DisableDefaultMap = 1
+<
+NOTE: setting this doesn't prevent the <Plug> mapping from created, try
+setting no_lookupfile_maps or no_plugin_maps for this (see |usr_41.txt|).
+
+                                            *LookupFile_TagExpr*
+- Use g:LookupFile_TagExpr to use custom tagfiles. A valid Vim expression
+  resulting in a String should be used, which means "eval(g:LookupFile_TagExpr)"
+  should return a valid value for 'tags' setting. Once you set a value for this
+  setting and startup Vim, try >
+        echo eval(g:LookupFile_TagExpr)
+< to make sure it is valid. Ex: >
+        let g:LookupFile_TagExpr = string('./filenametags')
+<
+                                            *LookupFile_MinPatLength*
+- By default g:LookupFile_MinPatLength is set to 4, which means you have to type
+  at least 4 characters before the lookup is triggered. This is because the
+  current taglist() function is too slow if the tag file is large.  Depending on
+  the number of files you have, you may be able to set a lower value (or may
+  even have to increase the value) for this setting.
+                                            *LookupFile_ShowFiller*
+- If you don't want the filler to be shown while the tags are being
+  looked up, you can disable it by setting g:LookupFile_ShowFiller to 0. If you
+  know for sure that the lookup never takes too long, then disabling the filler
+  could make the completion menu less flashy.
+                                            *LookupFile_UsingSpecializedTags*
+- When you generate specialized tags as described in the |lookupfile-tags|
+  section, you can set g:LookupFile_UsingSpecializedTags to enabled better
+  formatting for showing the results.
+                                            *LookupFile_PreservePatternHistory*
+- By default, the plugin leaves a copy of the pattern that you used to
+  lookup the file, so that you can scroll-back and find them (this is like a
+  history), but you can disable this behavior by setting
+  g:LookupFile_PreservePatternHistory to 0.
+                                            *LookupFile_PreserveLastPattern*
+- By default, the lookup window is started with the last pattern, which you can
+  remove by quick pressing <Ctrl-U>, but if you always want to start blank, set
+  g:LookupFile_PreserveLastPattern to 0.
+                                            *LookupFile_LookupFunc*
+                                            *LookupFile_LookupNotifyFunc*
+                                            *LookupFile_LookupAcceptFunc*
+- If you want to lookup matches yourself using an alternative procedure, you
+  can specify a user function that should be called by the plugin, and
+  by-pass the default tag based lookup. Use g:LookupFile_LookupFunc
+  setting to specify the name or Funcref for such a function. The function
+  will be called with one argument, which is the pattern typed so far. The
+  results will be sorted by the plugin anyway, so your function need not.
+  You can also set g:LookupFile_LookupNotifyFunc to be notified when a
+  file is selected from the matches (not called with o or O commands), and
+  g:LookupFile_LookupAcceptFunc to override the default handling of selection.
+                                            *LookupFile_switchbuf*
+- If you have 'switchbuf' set to "split" for your quickfix commands to
+  always split open windows, the plugin recognizes this setting and split-opens
+  all files.
+                                            *LookupFile_AlwaysAcceptFirst*
+- Normally when <CR> or <C-O> is pressed while the popup is visible, the first
+  item in the list is selected, unless there is exactly only one item matching.
+  This means, if you want to select the first item, you have to press it
+  twice. You can change this behavior to always accept the first item by
+  setting g:LookupFile_AlwaysAcceptFirst (like ido.el).
+                                            *LookupFile_FileFilter*
+- Use the g:LookupFile_FileFilter to specify a Vim regular expression pattern
+  that, when matched against the filename (or path, depending on the mode)
+  results in filtered out. E.x: >
+
+    " Don't display binary files
+    let g:LookupFile_FileFilter = '\.class$\|\.o$\|\.obj$\|\.exe$\|\.jar$\|\.zip$\|\.war$\|\.ear$'
+<
+  NOTE that the |:LUPath| and |:LUWalk| commands use the Vim functions |globpath()|
+  and |glob()| that recognize the Vim setting 'wildignore', so setting this in
+  addition to 'wildignore' will have the matches filtered by both rules.
+                                            *LookupFile_AllowNewFiles*
+- Use the g:LookupFile_AllowNewFiles to specify whether entering non-existent
+  files should generate an error or result in getting a new buffer created.
+                                            *LookupFile_ignorecase*
+                                            *LookupFile_smartcase*
+- The plugin now recognizes 'ignorecase' and 'smartcase' settings to match files
+  even on systems that are case-sensitive. On unix like platforms, glob()
+  matches patterns in a case-sensitive manner, so setting 'ignorecase' will
+  enable special translation of patterns so that you get case-insensitive
+  results. On Windows, since glob() is already case-insensitive no special
+  handling is required for 'ignorecase', however, when 'smartcase' is set, there
+  is no way to force glob() to return case-sensitive results, so a
+  post-processing of the results is done to drop files that would not otherwise
+  have matched.
+                                            *LookupFile_SortMethod*
+- Use this setting to specify the sort method to use for sorting results. The
+  default value is "alpha", which means that they will be sorted alphabatically.
+  You can also set an empty value to disable sorting. See also
+  |LookupFile_Bufs_BufListExpr| to enable MRU sorting for |:LUBufs| command.
+                                            *LookupFile_Bufs_BufListExpr*
+- Use g:LookupFile_Bufs_BufListExpr to specify an expression that returns a
+  |List| of buffers to display. By default this is set to an empty string, but
+  you can set any valid Vim expression that results in a List. If you also have
+  the latest version of SelectBuf (http://www.vim.org/script.php?script_id=107)
+  installed, a useful value that you can set here is "g:SB_MRUlist", which will
+  then display buffers in the MRU order. >
+
+        let g:LookupFile_Bufs_BufListExpr = 'g:SB_MRUlist'
+<
+  You can use this setting to even specify your own global function.
+                                            *LookupFile_Bufs_SkipUnlisted*
+- By default, the |:LUBufs| commands skips all unlisted buffers from results,
+  but if you want them to be included, reset LookupFile_Bufs_SkipUnlisted
+  setting.
+                                            *LookupFile_Bufs_LikeBufCmd*
+- This setting impacts the behavior of |:LUBufs| command on how the pattern is
+  matched. By default, it matches just like the |:buffer| command, so the
+  pattern is treated as per the rules of |wildcard| matching against the current
+  |bufname()| of each buffer. Clearing this setting will cause the command to
+  match the pattern as a Vim regex against only the filename portion of each
+  buffer.
+                                            *LookupFile_UpdateTime*
+- LookupFile uses |CursorHoldI| autocommand to trigger the completion and this
+  event doesn't get triggered unless nothing is typed for 'updatetime'
+  milliseconds. Since the default value for 'updatetime' is rather large,  the
+  plugin changes this value to g:LookupFile_UpdateTime (which defaults to 300
+  milliseconds or 3/10th of a second) while the [Lookup File] window is active.
+                                            *LookupFile_EscCancelsPopup*
+- On some systems, the mapping for <Esc> to cancel the popup could interfere
+  with the arrow keys, so if you want the plugin to not map <Esc> key, then
+  reset |LookupFile_EscCancelsPopup| setting. >
+
+        let g:LookupFile_EscCancelsPopup = 0
+<
+                                            *LookupFile_SearchForBufsInTabs*
+- Normally, when you seleclt a file that is already visible in a window,
+  LookupFile simply makes that window the current window instead of opening
+  the file again. Setting this flag will make LookupFile also search in other
+  tabs for a window that already has this file visible, and switch to that tab
+  to set the focus. This setting is most useful with the |LUBufs| command. >
+
+        let g:LookupFile_SearchForBufsInTabs = 1
+<
+                                            *LookupFile_TagsExpandCamelCase*
+- When this setting is enabled (the default), typing consecutive upper case
+  letters are treated as abbreviations for type names that follow the
+  CamelCase naming pattern (like all the java class names are) and the pattern
+  is translated automatically. This is modelled after the "Open Type" dialog
+  in eclipse and works only for the tag lookup (|LUTags| command). To disable
+  this feature, set it to 0 >
+
+        let g:LookupFile_TagsExpandCamelCase = 0
+<
+  Ex:
+    - To match the filename VimTheSupremeEditor.txt, you could enter the pattern
+      in several variations such as: >
+        VTSE
+        VTSEditor
+        VTSEdi.*
+        VTS.*
+        VTheSE
+<
+NOTE: While using the CamelCase expansion, 'ignorecase' is disabled so
+something like "VTSEDi.*" will not match "VimTheSupremeEditor", but will match
+"VimTheSupremeEditorDictator", if it exists.
+                                            *LookupFile_RecentFileListSize*
+This setting allows you to control the number of most recently used filenames
+that should be rememberd by the plugin. The default size is 20, but it can be
+set to 0 to not remember any and any -ve value to remember all. The list is
+not persisted across Vim sessions. >
+
+        let g:LookupFile_RecentFileListSize = 30
+<
+See also, |lookupfile-recent-files|.
+==============================================================================
+
+                                            *lookupfile-extend*
+If you want to extend the functionality of the plugin, by creating new commands
+to lookup files in different ways, you can take advantage of the
+g:LookupFile_LookupFunc and lookupfile_LookupNotifyFunc settings as below:
+
+    - Create a function that can take a pattern and return file matches for it:
+      function! FileMatches(pattern)
+        let matches = []
+        " Find and fill up matches.
+        return matches
+      endfunction
+    - Create an entry function and a command:
+      function! MyLookup()
+        unlet! s:savedLookupFunc s:savedLookupNotifyFunc
+        let g:savedLookupFunc = g:LookupFile_LookupFunc
+        let g:savedLookupNotifyFunc = g:LookupFile_LookupNotifyFunc
+        unlet g:LookupFile_LookupFunc g:LookupFile_LookupNotifyFunc
+        let g:LookupFile_LookupFunc = 'FileMatches'
+        let g:LookupFile_LookupNotifyFunc = 'MyNotify'
+        LookupFile
+      endfunction
+      command! MyLookup call MyLookup()
+    - Define the MyNotify() function to clear the settings.
+      function! MyNotify()
+        unlet g:LookupFile_LookupFunc g:LookupFile_LookupNotifyFunc
+        let g:LookupFile_LookupFunc = g:savedLookupFunc
+        let g:LookupFile_LookupNotifyFunc = g:savedLookupNotifyFunc
+      endfunction
+
+Once you follow the above steps, you can use both the default LookupFile command
+as well as the new MyLookup command. The completion will behave differently
+based on how you opened the lookup window.
+
+You can also override the default action when the user presses <CR> or <C-O> to
+get your function invoked, by setting the g:LookupFile_LookupAcceptFunc. The
+function should have the below signature: >
+
+    " splitWin is 1 if a new window should be split.
+    " key is the exact key that user pressed ("\<CR>"/"\<C-O>").
+    function accept(splitWin, key)
+<
+The last pattern and the matching results for it are accessible in the global
+variables g:lookupfile#lastPattern and g:lookupfile#lastResults.
+
+You can also create ftplugin/lookupfile.vim in your vim runtime and put commands
+to further customize or override the behavior of the plugin. E.g., you could put
+a map such that pressing <Esc> twice in succession will close the window: >
+
+    nnoremap <buffer> <Esc><Esc> <C-W>q
+    inoremap <buffer> <Esc><Esc> <Esc><C-W>q
+<
+                                            *lookupfile-tips*
+- A pattern is a Vim regex, so you can e.g., start with an "^" to anchor the
+  pattern match to the start of the filename. This will also speed up the
+  lookup. You can also enter the extension (such as \.prop) to narrow down the
+  results further.
+- If the filename has special characters, don't forget to protect them while
+  entering the pattern, or you may not find what you are looking for (or
+  might find too much). E.g., to find a file named "abc*xyz" (not possible
+  on windows), you need to enter "abc\*" as pattern. Since the pattern is
+  used as if 'magic' is set, unless you have the "\" itself in the
+  filenames, you only have to worry about those characters that are special
+  without needing to prefix with a "\", and these are ".", "^", "[", "*" and
+  "$".
+- If the lookup is taking too long, you can stop it by pressing ^C. You may
+  still be able to see partial set of matches.
+- You can use <Ctrl-J> instead of <CR> to deliberately insert a newline into
+  the lookup buffer.
+- Pressing <Esc> after selecting a file, results in restoring the pattern. If
+  you actually want to cancel the completion menu, without restoring the
+  pattern, you can press <C-C>.
+- When you want to start entering a new pattern, press <Ctrl-U> to quickly
+  kill the previous pattern.
+- Use g:LookupFile_LookupFunc setting to modify the functionality of the
+  plugin. The plugin/lookupfile.vim comes with a few of alternative ways to
+  lookup files, such as looking up files from 'path' (see |lookupfile-usage|).
+  Other ideas for alternative lookup for files are:
+  - Use id-utils' lid to lookup files from ID database. You can e.g., use the
+    command "lid -R filenames -S newline -k token" to lookup for the typed
+    pattern as a token (there are other options for lid to treat token as regex)
+  - Use GNU find to find files matching the pattern, something like:
+    "find . -name '*pattern*'". There are a number of options for find, so this
+    could be made very flexible, but running find for every character typed
+    could be very slow.
+  - You can create an alternate tags file to store only directory names such
+    that you can lookup directory names instead of filenames. The following
+    command can create such a tags file: >
+
+        (echo "!_TAG_FILE_SORTED       2       /2=foldcase/";
+        find . -type d -printf "%f\t%p\t1\n") | \
+        sort -f > ./dirnametags
+<
+    Note that this generates tag file with relative paths, which is known to
+    cause problem with |taglist()| function when the tag file is not in the same
+    directory as Vim's current directory (see |lookupfile-known-issues|. To
+    workaround, you can generate absolute paths, you can use "`pwd`" instead of "."
+    as the root.
+- You can create custom commands/mappings that always start with a fixed or
+  computed pattern. E.g., to always start LUWalk in the root of c: drive, create
+  a command as: >
+
+        command! WalkRoot LUWalk c:/
+<
+  Another useful variation of the above is to start with the directory of the
+  current file: >
+
+        command! WalkCur :exec "LUWalk" expand('%:p:h').'/'
+<
+  Another example that works well with :LookupFile is to start with the current
+  word as pattern: >
+
+        command! CurWord :LookupFile <cword>
+<
+- The :LUWalk command maps your <BS> key such that it will remove a complete
+  directory component at a time (like ido.el). To remove only a part of the
+  directory name, first press <C-W> to remove the "/" and use <BS> to remove the
+  required number characters. You can also press <Esc> and use normal commands
+  to edit.
+- While using :LUWalk, when no pattern is entered yet, you can press <Tab> to
+  show the files in the directory of the current buffer.
+- While using :LUWalk, you can enter "**" after any directory in the path to
+  match keyword recursively under that path.
+- If you want to extend the funcationality of the plugin, take a look at the
+  plugin/lookupfile.vim for various examples.
+- Here is a sample function that you can use to lookup spellings as you type,
+  using spellsuggest() function. You need to first turn on 'spell' inside
+  lookup window for this to work. >
+    function! s:LookupSpell(pattern)
+      return spellsuggest(a:pattern)
+    endfunction
+<
+  This is a dummy accept function that prevents the word from being treated as a
+  file and open it: >
+    function! s:SpellAccept(splitWin, key)
+      call input('SpellAccept')
+    endfunction
+<
+==============================================================================
+
+                                            *lookupfile-known-issues*
+- Vim sometimes automatically replaces the pattern with the first filename
+  from the completion menu. You have to select back the original pattern using
+  ^P.
+- The taglist() function's performance (on which this plugin depends on)
+  seems to be bad. A newer version of Vim might accept additional arguments
+  for taglist() to limit the number of matches, which can speed up some of
+  the cases.
+- If you press the <Plug>LookupFile key while making a file selection, the
+  completion doesn't get aborted, but instead it gets selected and the 
+  [Lookup File] window still remains open.
+- There is a bug in taglist() that returns incorrect relative paths if the
+  current directory of Vim is not the same as the directory in which tags file
+  resides. This impacts the :LookupFile command. If this is an issue for you,
+  you should generate the tag files with absolute paths instead of relative
+  paths, by giving absolute directory names.
+==============================================================================
+
+                                            *lookupfile-wishlist*
+- Allow on the fly filtering of the filenames by prompting the user for a
+  pattern to match (or not match) against the whole filename.
+- Option to sort filenames by their extension, or by MRU.
+- Save and restore matching results, for patterns that take a long time to build
+  (especially those that involve "**").
+- Option to have separate window/buffers for each type of command (Timothy,
+  Guo)
+==============================================================================
+
+                                            *lookupfile-changes*
+
+                                            *lookupfile-changes-1.8*
+- New settings |LookupFile_EnableRemapCmd|, |LookupFile_SearchForBufsInTabs|,
+  |LookupFile_TagsExpandCamelCase|, |Lookupfile_RecentFileListSize|.
+- Even more control on the mappings with the new setting
+  |LookupFile_EnableRemapCmd|.
+- New feature to specify names that follow CamelCasing pattern by abbreviating
+  them. This works more or less like for the "Open Type" dialog in Eclipse.
+  For more information see, |LookupFile_TagsExpandCamelCase|.
+- New feature to remember the recent files, see
+  |Lookupfile_RecentFileListSize|.
+- Changed the message line such that the message is shown on the left side and
+  the pattern is shown on the right side. This prevents the more important
+  message from getting lost when the filenames are too long.
+
+                                            *lookupfile-changes-1.7*
+- Bug fix: LUPath and LUArgs were broken (stoning at gmail dot com).
+- Removed debugging code.
+
+                                            *lookupfile-changes-1.6*
+                                            *lookupfile-changes-1.5*
+- LookupFile now uses CursorHoldI instead of CursorMovedI to show matches. This
+  means, the matches are not computed until you stop typing, which should give a
+  better experience. See |LookupFile_UpdateTime| for more information.
+- The plugin now sets 'completefunc' such that you can now hit <C-X><C-U> to
+  trigger completions explicitly (instead of waiting for
+  g:LookupFile_UpdateTime). This is useful if you are a slow typer and so prefer
+  a large value for g:LookupFile_UpdateTime.
+- The plugin now recognizes 'ignorecase' and 'smartcase' settings to match files
+  even on systems that are case-sensitive. This is mainly significant for the
+  commands that use |globpath()| or |glob()| which are, |:LUPath| and |:LUWalk|,
+  as the others already respect these settings.
+- There is now a separate command called :LUTags that always does what
+  :LookupFile used to do, while the :LookupFile command itself now gets
+  dynamically assigned to the last command used. This also means, the map that
+  is choosen in |lookupfile-map| now invokes the last command that is used, use
+  |LookupFile_DefaultCmd| and |LookupFile_DisableDefaultMap| settings to control
+  this behavior.
+- LUWalk now supports showing only directory names in the results. Just type
+  an extra / to filter those that are not directories (the suffix will then be
+  two slashes).
+- The default behavior of |:LUBufs| is now to match the entire |bufname()|,
+  just like the |:buffer| command would do. To see the old behavior, reset
+  |LookupFile_Bufs_LikeBufCmd| setting.
+- If you have the new version of SelectBuf also installed, you can have
+  |:LUBufs| sort buffers in MRU order. See, |LookupFile_Bufs_BufListExpr|.
+- When tags file is not generated as per the requirements of |lookupfile-tags|,
+  the format of the matches doesn't look good, so by default the matches just
+  show full filenames. See |LookupFile_UsingSpecializedTags| to get better
+  formatted results.
+- New settings |LookupFile_UsingSpecializedTags|, |LookupFile_SortMethod|,
+  |LookupFile_DefaultCmd|, |LookupFile_DisableDefaultMap|,
+  |LookupFile_Bufs_LikeBufCmd|, |LookupFile_Bufs_BufListExpr|,
+  |LookupFile_EscCancelsPopup|.
+- Bug fix: exact matches were getting dropped from results  (Max Dyckhoff).
+- Bug fix: <BS> in |:LUWalk| right after selecting a directory match caused it
+  to misbehave.
+- Bug fix: if tags file is not in the current directory, the opening fails. Now
+  :LookupFile expands the filenames explicitly so that the paths are always
+  valid.
+- Bug fix: Unsetting |LookupFile_AllowNewFiles| didn't disable the feature.
+- Bug fix: <C-E> was not hiding the popup.
+- Bug fix: <S-BS> triggers the default insert-mode completion (Max Dyckhoff).
+- Fixed misc. bugs in opening files.
+- Workaround for <Esc> not working on some systems, allow disabling the
+  mapping.
+- Bug fix: When there is an exising swapfile, opening the files result in an
+  error message (David Fishburn).
+- When LookupFile_UsingSpecializedTags is set, sorting should be done on the
+  filename (patch by Timothy, Guo).
+
+                                            *lookupfile-changes-1.4*
+- Fixed a bug in the initial pattern getting ignored.
+- LUBufs now completes the full pathname of the buffers, so that it is less
+  dependent on the current directory of Vim.
+- LUBufs and LUPath commands now present the matches in a better format.
+- Pressing <F5> while popup is visible didn't close the lookupfile window.
+
+                                            *lookupfile-changes-1.3*
+- New feature to create a file, if the file doesn't already exist (Ido). Can be
+  disabled using g:LookupFile_AllowNewFiles setting.
+- Bug fix: while using LUWalk, if the first match is a directory, then selecting
+  files was offset by one.
+
+                                            *lookupfile-changes-1.2*
+- g:LookupFile_AlwaysAcceptFirst setting to always accept the first
+  entry (the default is 0).
+- g:LookupFile_FileFilter to specify a filter.
+- New LUWalk command that works very similar to Emacs ido.el to quickly navigate
+  paths.
+- All commands now accepts the initial pattern as argument. This provides
+  unlimited number of possibilities to create your own custom commands (see
+  |lookupfile-tips| for some examples).
+- The g:LookupFile_MinPatLength is automatically set to 0 except for tag and
+  'path' lookup.
+- When Lookup window is opened, the filetype is set to "lookupfile". This allows
+  you to create ftplugins to fine tune the behavior.
+- Renamed :LUBuf command to :LUBufs.
+
+                                            *lookupfile-changes-1.1*
+- Added LUBuf and LUPath commands.
+- Renamed the prefix for all the settings from g:lookupfile_ to g:LookupFile_.
+  This is required to support Funcref settings.
+- Now the cursor position is preserved, while opening a file that is already
+  loaded.
+- Using genutils 2.1.
+==============================================================================
+
+                                            *lookupfile-acknowledgements*
+- Max Dyckhoff (maxd at microsoft dot com) for beta testing and reporting
+  numerous issues and suggesting improvements etc. The plugin is a lot
+  more stable, thanks to him.
+  I would also like to specially thank him for proof reading the first version
+  of this documentation.
+- Eddy Zhao (eddy dot y dot zhao at gmail dot com> for suggesting several
+  improvements on the lines of emacs ido.el and rigorously testing them. The
+  LUWalk command is the result of this.
+- Eddy Zhao (eddy dot y dot zhao at gmail dot com> for suggesting several
+- Dane Summers (dsummersl at yahoo dot com) for feedback and doc updates.
+- Timothy, Guo (firemeteor dot guo at gmail dot com) for improving the sorting
+  and other feedback.
+
+
+ vim6:tw=80:ts=8:ft=help:ai:sw=4:et
diff --git a/doc/surround.txt b/doc/surround.txt
new file mode 100644 (file)
index 0000000..fec64a2
--- /dev/null
@@ -0,0 +1,218 @@
+*surround.txt*  Plugin for deleting, changing, and adding "surroundings"
+
+Author:  Tim Pope <[email protected]>        *surround-author*
+License: Same terms as Vim itself (see |license|)
+
+This plugin is only available if 'compatible' is not set.
+
+INTRODUCTION                                    *surround*
+
+This plugin is a tool for dealing with pairs of "surroundings."  Examples
+of surroundings include parentheses, quotes, and HTML tags.  They are
+closely related to what Vim refers to as |text-objects|.  Provided
+are mappings to allow for removing, changing, and adding surroundings.
+
+Details follow on the exact semantics, but first, consider the following
+examples.  An asterisk (*) is used to denote the cursor position.
+
+  Old text                  Command     New text ~
+  "Hello *world!"           ds"         Hello world!
+  [123+4*56]/2              cs])        (123+456)/2
+  "Look ma, I'm *HTML!"     cs"<q>      <q>Look ma, I'm HTML!</q>
+  if *x>3 {                 ysW(        if ( x>3 ) {
+  my $str = *whee!;         vlllls'     my $str = 'whee!';
+
+While a few features of this plugin will work in older versions of Vim,
+Vim 7 is recommended for full functionality.
+
+MAPPINGS                                        *surround-mappings*
+
+Delete surroundings is *ds* .  The next character given determines the target
+to delete.  The exact nature of the target are explained in |surround-targets|
+but essentially it is the last character of a |text-object|.  This mapping
+deletes the difference between the "inner" object and "an" object.  This is
+easiest to understand with some examples:
+
+  Old text                  Command     New text ~
+  "Hello *world!"           ds"         Hello world!
+  (123+4*56)/2              ds)         123+456/2
+  <div>Yo!*</div>           dst         Yo!
+
+Change surroundings is *cs* .  It takes two arguments, a target like with
+|ds|, and a replacement.  Details about the second argument can be found
+below in |surround-replacements|.  Once again, examples are in order.
+
+  Old text                  Command     New text ~
+  "Hello *world!"           cs"'        'Hello world!'
+  "Hello *world!"           cs"<q>      <q>Hello world!</q>
+  (123+4*56)/2              cs)]        [123+456]/2
+  (123+4*56)/2              cs)[        [ 123+456 ]/2
+  <div>Yo!*</div>           cst<p>      <p>Yo!</p>
+
+*ys* takes an valid Vim motion or text object as the first object, and wraps
+it using the second argument as with |cs|.  (Unfortunately there's no good
+mnemonic for "ys".)
+
+  Old text                  Command     New text ~
+  Hello w*orld!             ysiw)       Hello (world)!
+
+As a special case, *yss* operates on the current line, ignoring leading
+whitespace.
+
+  Old text                  Command     New text ~
+      Hello w*orld!         yssB            {Hello world!}
+
+There is also *yS* and *ySS* which indent the surrounded text and place it
+on a line of its own.
+
+In visual mode, a simple "s" with an argument wraps the selection.  This is
+referred to as the *vs* mapping, although ordinarily there will be
+additional keystrokes between the v and s.  In linewise visual mode, the
+surroundings are placed on separate lines.  In blockwise visual mode, each
+line is surrounded.
+
+An "S" in visual mode (*vS*) behaves similarly but always places the
+surroundings on separate lines.  Additionally, the surrounded text is
+indented.  In blockwise visual mode, using "S" instead of "s" instead skips
+trailing whitespace.
+
+Note that "s" and "S" already have valid meaning in visual mode, but it is
+identical to "c".  If you have muscle memory for "s" and would like to use a
+different key, add your own mapping and the existing one will be disabled.
+>
+  vmap <Leader>s <Plug>Vsurround
+  vmap <Leader>S <Plug>VSurround
+<
+                                                *i_CTRL-G_s* *i_CTRL-G_S*
+Finally, there is an experimental insert mode mapping on <C-G>s and <C-S>.
+Beware that the latter won't work on terminals with flow control (if you
+accidentally freeze your terminal, use <C-Q> to unfreeze it).  The mapping
+inserts the specified surroundings and puts the cursor between them.  If,
+immediately after the mapping and before the replacement, a second <C-S> or
+carriage return is pressed, the prefix, cursor, and suffix will be placed on
+three separate lines.  <C-G>S (not <C-G>s) also exhibits this behavior.
+
+TARGETS                                         *surround-targets*
+
+The |ds| and |cs| commands both take a target as their first argument.  The
+possible targets are based closely on the |text-objects| provided by Vim.
+In order for a target to work, the corresponding text object must be
+supported in the version of Vim used (Vim 7 adds several text objects, and
+thus is highly recommended).  All targets are currently just one character.
+
+Eight punctuation marks, (, ), {, }, [, ], <, and >, represent themselves
+and their counterpart.  If the opening mark is used, contained whitespace is
+also trimmed.  The targets b, B, r, and a are aliases for ), }, ], and >
+(the first two mirror Vim; the second two are completely arbitrary and
+subject to change).
+
+Three quote marks, ', ", `, represent themselves, in pairs.  They are only
+searched for on the current line.
+
+A t is a pair of HTML or XML tags.  See |tag-blocks| for details.  Remember
+that you can specify a numerical argument if you want to get to a tag other
+than the innermost one.
+
+The letters w, W, and s correspond to a |word|, a |WORD|, and a |sentence|,
+respectively.  These are special in that they have nothing to delete, and
+used with |ds| they are a no-op.  With |cs|, one could consider them a
+slight shortcut for ysi (cswb == ysiwb, more or less).
+
+A p represents a |paragraph|.  This behaves similarly to w, W, and s above;
+however, newlines are sometimes added and/or removed.
+
+REPLACEMENTS                                    *surround-replacements*
+
+A replacement argument is a single character, and is required by |cs|, |ys|,
+and |vs|.  Undefined replacement characters (with the exception of alphabetic
+characters) default to placing themselves at the beginning and end of the
+destination, which can be useful for characters like / and |.
+
+If either ), }, ], or > is used, the text is wrapped in the appropriate pair
+of characters.  Similar behavior can be found with (, {, and [ (but not <),
+which append an additional space to the inside.  Like with the targets above,
+b, B, r, and a are aliases for ), }, ], and >.  To fulfill the common need for
+code blocks in C-style languages, <C-}> (which is really <C-]>) adds braces on
+lines separate from the content.
+
+If t or < is used, Vim prompts for an HTML/XML tag to insert.  You may specify
+attributes here and they will be stripped from the closing tag.  End your
+input by pressing <CR> or >.  If <C-T> is used, the tags will appear on lines
+by themselves.
+
+A deprecated replacement of a LaTeX environment is provided on \ and l.  The
+name of the environment and any arguments will be input from a prompt.  This
+will be removed once a more fully functional customization system is
+implemented.  The following shows the resulting environment from
+csp\tabular}{lc<CR>
+>
+  \begin{tabular}{lc}
+  \end{tabular}
+<
+CUSTOMIZING                                     *surround-customizing*
+
+The following adds a potential replacement on "-" (ASCII 45) in PHP files.
+(To determine the ASCII code to use, :echo char2nr("-")).  The carriage
+return will be replaced by the original text.
+>
+  autocmd FileType php let b:surround_45 = "<?php \r ?>"
+<
+This can be used in a PHP file as in the following example.
+
+  Old text                  Command     New text ~
+  print "Hello *world!"     yss-        <?php print "Hello world!" ?>
+
+Additionally, one can use a global variable for globally available
+replacements.
+>
+  let g:surround_45 = "<% \r %>"
+  let g:surround_61 = "<%= \r %>"
+<
+Advanced, experimental, and subject to change:  One can also prompt for
+replacement text.  The syntax for this is to surround the replacement in pairs
+of low numbered control characters.  If this sounds confusing, that's because
+it is (but it makes the parsing easy).  Consider the following example for a
+LaTeX environment on the "l" replacement.
+>
+  let g:surround_108 = "\\begin{\1environment: \1}\r\\end{\1\1}"
+<
+When this replacement is used,  the user is prompted with an "environment: "
+prompt for input.  This input is inserted between each set of \1's.
+Additional inputs up to \7 can be used.
+
+Furthermore, one can specify a regular expression substitution to apply.
+>
+  let g:surround_108 = "\\begin{\1environment: \1}\r\\end{\1\r}.*\r\1}"
+<
+This will remove anything after the first } in the input when the text is
+placed within the \end{} slot.  The first \r marks where the pattern begins,
+and the second where the replacement text begins.
+
+Here's a second example for creating an HTML <div>.  The substitution cleverly
+prompts for an id, but only adds id="" if it is non-blank.  You may have to
+read this one a few times slowly before you understand it.
+>
+  let g:surround_{char2nr("d")} = "<div\1id: \r..*\r id=\"&\"\1>\r</div>"
+<
+Inputting text replacements is a proof of concept at this point. The ugly,
+unintuitive interface and the brevity of the documentation reflect this.
+
+Finally, It is possible to always append a string to surroundings in insert
+mode (and only insert mode).  This is useful with certain plugins and mappings
+that allow you to jump to such markings.
+>
+  let g:surround_insert_tail = "<++>"
+<
+ISSUES                                          *surround-issues*
+
+Vim could potentially get confused when deleting/changing occurs at the very
+end of the line.  Please report any repeatable instances of this.
+
+Do we need to use |inputsave()|/|inputrestore()| with the tag replacement?
+
+Indenting is handled haphazardly.  Need to decide the most appropriate
+behavior and implement it.  Right now one can do :let b:surround_indent = 1
+(or the global equivalent) to enable automatic re-indenting by Vim via |=|;
+should this be the default?
+
+ vim:tw=78:ts=8:ft=help:norl:
diff --git a/doc/tags b/doc/tags
new file mode 100644 (file)
index 0000000..8a05bfd
--- /dev/null
+++ b/doc/tags
@@ -0,0 +1,151 @@
+:CVSEdit       vcscommand.txt  /*:CVSEdit*
+:CVSEditors    vcscommand.txt  /*:CVSEditors*
+:CVSUnedit     vcscommand.txt  /*:CVSUnedit*
+:CVSWatch      vcscommand.txt  /*:CVSWatch*
+:CVSWatchAdd   vcscommand.txt  /*:CVSWatchAdd*
+:CVSWatchOff   vcscommand.txt  /*:CVSWatchOff*
+:CVSWatchOn    vcscommand.txt  /*:CVSWatchOn*
+:CVSWatchRemove        vcscommand.txt  /*:CVSWatchRemove*
+:CVSWatchers   vcscommand.txt  /*:CVSWatchers*
+:LUBufs        lookupfile.txt  /*:LUBufs*
+:LUPath        lookupfile.txt  /*:LUPath*
+:LUTags        lookupfile.txt  /*:LUTags*
+:LUWalk        lookupfile.txt  /*:LUWalk*
+:LookupFile    lookupfile.txt  /*:LookupFile*
+:VCSAdd        vcscommand.txt  /*:VCSAdd*
+:VCSAnnotate   vcscommand.txt  /*:VCSAnnotate*
+:VCSBlame      vcscommand.txt  /*:VCSBlame*
+:VCSCommit     vcscommand.txt  /*:VCSCommit*
+:VCSDelete     vcscommand.txt  /*:VCSDelete*
+:VCSDiff       vcscommand.txt  /*:VCSDiff*
+:VCSGotoOriginal       vcscommand.txt  /*:VCSGotoOriginal*
+:VCSInfo       vcscommand.txt  /*:VCSInfo*
+:VCSLock       vcscommand.txt  /*:VCSLock*
+:VCSLog        vcscommand.txt  /*:VCSLog*
+:VCSRemove     vcscommand.txt  /*:VCSRemove*
+:VCSRevert     vcscommand.txt  /*:VCSRevert*
+:VCSReview     vcscommand.txt  /*:VCSReview*
+:VCSStatus     vcscommand.txt  /*:VCSStatus*
+:VCSUnlock     vcscommand.txt  /*:VCSUnlock*
+:VCSUpdate     vcscommand.txt  /*:VCSUpdate*
+:VCSVimDiff    vcscommand.txt  /*:VCSVimDiff*
+LookupFile-command     lookupfile.txt  /*LookupFile-command*
+LookupFile_AllowNewFiles       lookupfile.txt  /*LookupFile_AllowNewFiles*
+LookupFile_AlwaysAcceptFirst   lookupfile.txt  /*LookupFile_AlwaysAcceptFirst*
+LookupFile_Bufs_BufListExpr    lookupfile.txt  /*LookupFile_Bufs_BufListExpr*
+LookupFile_Bufs_LikeBufCmd     lookupfile.txt  /*LookupFile_Bufs_LikeBufCmd*
+LookupFile_Bufs_SkipUnlisted   lookupfile.txt  /*LookupFile_Bufs_SkipUnlisted*
+LookupFile_DefaultCmd  lookupfile.txt  /*LookupFile_DefaultCmd*
+LookupFile_DisableDefaultMap   lookupfile.txt  /*LookupFile_DisableDefaultMap*
+LookupFile_EnableRemapCmd      lookupfile.txt  /*LookupFile_EnableRemapCmd*
+LookupFile_EscCancelsPopup     lookupfile.txt  /*LookupFile_EscCancelsPopup*
+LookupFile_FileFilter  lookupfile.txt  /*LookupFile_FileFilter*
+LookupFile_LookupAcceptFunc    lookupfile.txt  /*LookupFile_LookupAcceptFunc*
+LookupFile_LookupFunc  lookupfile.txt  /*LookupFile_LookupFunc*
+LookupFile_LookupNotifyFunc    lookupfile.txt  /*LookupFile_LookupNotifyFunc*
+LookupFile_MinPatLength        lookupfile.txt  /*LookupFile_MinPatLength*
+LookupFile_PreserveLastPattern lookupfile.txt  /*LookupFile_PreserveLastPattern*
+LookupFile_PreservePatternHistory      lookupfile.txt  /*LookupFile_PreservePatternHistory*
+LookupFile_RecentFileListSize  lookupfile.txt  /*LookupFile_RecentFileListSize*
+LookupFile_SearchForBufsInTabs lookupfile.txt  /*LookupFile_SearchForBufsInTabs*
+LookupFile_ShowFiller  lookupfile.txt  /*LookupFile_ShowFiller*
+LookupFile_SortMethod  lookupfile.txt  /*LookupFile_SortMethod*
+LookupFile_TagExpr     lookupfile.txt  /*LookupFile_TagExpr*
+LookupFile_TagsExpandCamelCase lookupfile.txt  /*LookupFile_TagsExpandCamelCase*
+LookupFile_UpdateTime  lookupfile.txt  /*LookupFile_UpdateTime*
+LookupFile_UsingSpecializedTags        lookupfile.txt  /*LookupFile_UsingSpecializedTags*
+LookupFile_ignorecase  lookupfile.txt  /*LookupFile_ignorecase*
+LookupFile_smartcase   lookupfile.txt  /*LookupFile_smartcase*
+LookupFile_switchbuf   lookupfile.txt  /*LookupFile_switchbuf*
+VCSCommandCVSDiffOpt   vcscommand.txt  /*VCSCommandCVSDiffOpt*
+VCSCommandCVSExec      vcscommand.txt  /*VCSCommandCVSExec*
+VCSCommandCommitOnWrite        vcscommand.txt  /*VCSCommandCommitOnWrite*
+VCSCommandDeleteOnHide vcscommand.txt  /*VCSCommandDeleteOnHide*
+VCSCommandDiffSplit    vcscommand.txt  /*VCSCommandDiffSplit*
+VCSCommandDisableExtensionMappings     vcscommand.txt  /*VCSCommandDisableExtensionMappings*
+VCSCommandDisableMappings      vcscommand.txt  /*VCSCommandDisableMappings*
+VCSCommandEdit vcscommand.txt  /*VCSCommandEdit*
+VCSCommandEnableBufferSetup    vcscommand.txt  /*VCSCommandEnableBufferSetup*
+VCSCommandResultBufferNameExtension    vcscommand.txt  /*VCSCommandResultBufferNameExtension*
+VCSCommandResultBufferNameFunction     vcscommand.txt  /*VCSCommandResultBufferNameFunction*
+VCSCommandSVKExec      vcscommand.txt  /*VCSCommandSVKExec*
+VCSCommandSVNDiffExt   vcscommand.txt  /*VCSCommandSVNDiffExt*
+VCSCommandSVNDiffOpt   vcscommand.txt  /*VCSCommandSVNDiffOpt*
+VCSCommandSVNExec      vcscommand.txt  /*VCSCommandSVNExec*
+VCSCommandSplit        vcscommand.txt  /*VCSCommandSplit*
+b:VCSCommandCommand    vcscommand.txt  /*b:VCSCommandCommand*
+b:VCSCommandOriginalBuffer     vcscommand.txt  /*b:VCSCommandOriginalBuffer*
+b:VCSCommandSourceFile vcscommand.txt  /*b:VCSCommandSourceFile*
+b:VCSCommandVCSType    vcscommand.txt  /*b:VCSCommandVCSType*
+cs     surround.txt    /*cs*
+cvscommand-changes     vcscommand.txt  /*cvscommand-changes*
+ds     surround.txt    /*ds*
+i_CTRL-G_S     surround.txt    /*i_CTRL-G_S*
+i_CTRL-G_s     surround.txt    /*i_CTRL-G_s*
+lookupfile-acknowledgements    lookupfile.txt  /*lookupfile-acknowledgements*
+lookupfile-buffer-cmd-alternative      lookupfile.txt  /*lookupfile-buffer-cmd-alternative*
+lookupfile-changes     lookupfile.txt  /*lookupfile-changes*
+lookupfile-changes-1.1 lookupfile.txt  /*lookupfile-changes-1.1*
+lookupfile-changes-1.2 lookupfile.txt  /*lookupfile-changes-1.2*
+lookupfile-changes-1.3 lookupfile.txt  /*lookupfile-changes-1.3*
+lookupfile-changes-1.4 lookupfile.txt  /*lookupfile-changes-1.4*
+lookupfile-changes-1.5 lookupfile.txt  /*lookupfile-changes-1.5*
+lookupfile-changes-1.6 lookupfile.txt  /*lookupfile-changes-1.6*
+lookupfile-changes-1.7 lookupfile.txt  /*lookupfile-changes-1.7*
+lookupfile-changes-1.8 lookupfile.txt  /*lookupfile-changes-1.8*
+lookupfile-default-cmd lookupfile.txt  /*lookupfile-default-cmd*
+lookupfile-extend      lookupfile.txt  /*lookupfile-extend*
+lookupfile-find-cmd-alternative        lookupfile.txt  /*lookupfile-find-cmd-alternative*
+lookupfile-from-tags-files     lookupfile.txt  /*lookupfile-from-tags-files*
+lookupfile-ido lookupfile.txt  /*lookupfile-ido*
+lookupfile-installation        lookupfile.txt  /*lookupfile-installation*
+lookupfile-introduction        lookupfile.txt  /*lookupfile-introduction*
+lookupfile-known-issues        lookupfile.txt  /*lookupfile-known-issues*
+lookupfile-map lookupfile.txt  /*lookupfile-map*
+lookupfile-maps        lookupfile.txt  /*lookupfile-maps*
+lookupfile-overview    lookupfile.txt  /*lookupfile-overview*
+lookupfile-plugin      lookupfile.txt  /*lookupfile-plugin*
+lookupfile-recent-files        lookupfile.txt  /*lookupfile-recent-files*
+lookupfile-settings    lookupfile.txt  /*lookupfile-settings*
+lookupfile-tags        lookupfile.txt  /*lookupfile-tags*
+lookupfile-tips        lookupfile.txt  /*lookupfile-tips*
+lookupfile-usage       lookupfile.txt  /*lookupfile-usage*
+lookupfile-walk-path   lookupfile.txt  /*lookupfile-walk-path*
+lookupfile-wishlist    lookupfile.txt  /*lookupfile-wishlist*
+lookupfile.txt lookupfile.txt  /*lookupfile.txt*
+surround       surround.txt    /*surround*
+surround-author        surround.txt    /*surround-author*
+surround-customizing   surround.txt    /*surround-customizing*
+surround-issues        surround.txt    /*surround-issues*
+surround-mappings      surround.txt    /*surround-mappings*
+surround-replacements  surround.txt    /*surround-replacements*
+surround-targets       surround.txt    /*surround-targets*
+surround.txt   surround.txt    /*surround.txt*
+vcscommand     vcscommand.txt  /*vcscommand*
+vcscommand-buffer-management   vcscommand.txt  /*vcscommand-buffer-management*
+vcscommand-buffer-variables    vcscommand.txt  /*vcscommand-buffer-variables*
+vcscommand-bugs        vcscommand.txt  /*vcscommand-bugs*
+vcscommand-commands    vcscommand.txt  /*vcscommand-commands*
+vcscommand-config      vcscommand.txt  /*vcscommand-config*
+vcscommand-contents    vcscommand.txt  /*vcscommand-contents*
+vcscommand-customize   vcscommand.txt  /*vcscommand-customize*
+vcscommand-events      vcscommand.txt  /*vcscommand-events*
+vcscommand-install     vcscommand.txt  /*vcscommand-install*
+vcscommand-intro       vcscommand.txt  /*vcscommand-intro*
+vcscommand-manual      vcscommand.txt  /*vcscommand-manual*
+vcscommand-mappings    vcscommand.txt  /*vcscommand-mappings*
+vcscommand-mappings-override   vcscommand.txt  /*vcscommand-mappings-override*
+vcscommand-naming      vcscommand.txt  /*vcscommand-naming*
+vcscommand-options     vcscommand.txt  /*vcscommand-options*
+vcscommand-ssh vcscommand.txt  /*vcscommand-ssh*
+vcscommand-ssh-config  vcscommand.txt  /*vcscommand-ssh-config*
+vcscommand-ssh-env     vcscommand.txt  /*vcscommand-ssh-env*
+vcscommand-ssh-other   vcscommand.txt  /*vcscommand-ssh-other*
+vcscommand-ssh-wrapper vcscommand.txt  /*vcscommand-ssh-wrapper*
+vcscommand-statusline  vcscommand.txt  /*vcscommand-statusline*
+vcscommand.txt vcscommand.txt  /*vcscommand.txt*
+vs     surround.txt    /*vs*
+yS     surround.txt    /*yS*
+ySS    surround.txt    /*ySS*
+ys     surround.txt    /*ys*
+yss    surround.txt    /*yss*
diff --git a/doc/vcscommand.txt b/doc/vcscommand.txt
new file mode 100644 (file)
index 0000000..0c16aac
--- /dev/null
@@ -0,0 +1,771 @@
+*vcscommand.txt*       vcscommand
+Copyright (c) 2007 Bob Hiestand
+
+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.
+
+For instructions on installing this file, type
+       :help add-local-help
+inside Vim.
+
+Author:  Bob Hiestand <[email protected]>
+Credits:  Benji Fisher's excellent MatchIt documentation
+
+==============================================================================
+1. Contents                                            *vcscommand-contents*
+
+       Installation            : |vcscommand-install|
+       vcscommand Intro        : |vcscommand|
+       vcscommand Manual       : |vcscommand-manual|
+       Customization           : |vcscommand-customize|
+       SSH "integration"       : |vcscommand-ssh|
+       Changes from cvscommand : |cvscommand-changes|
+       Bugs                    : |vcscommand-bugs|
+
+==============================================================================
+
+2. vcscommand Installation                             *vcscommand-install*
+
+The vcscommand plugin comprises five files: vcscommand.vim, vcssvn.vim,
+vcscvs.vim, vcssvk.vim and vcscommand.txt (this file).  In order to install
+the plugin, place the vcscommand.vim, vcssvn.vim, vcssvk.vim, and vcscvs.vim
+files into a plugin directory in your runtime path (please see
+|add-global-plugin| and |'runtimepath'|. 
+
+This help file can be included in the VIM help system by copying it into a
+'doc' directory in your runtime path and then executing the |:helptags|
+command, specifying the full path of the 'doc' directory.  Please see
+|add-local-help| for more details.
+
+vcscommand may be customized by setting variables, creating maps, and
+specifying event handlers.  Please see |vcscommand-customize| for more
+details.
+
+==============================================================================
+
+3. vcscommand Intro                                    *vcscommand*
+                                                       *vcscommand-intro*
+
+The vcscommand plugin provides global ex commands for manipulating
+version-controlled source files, currently those controlled either by CVS or
+Subversion.  In general, each command operates on the current buffer and
+accomplishes a separate source control function, such as update, commit, log,
+and others (please see |vcscommand-commands| for a list of all available
+commands).  The results of each operation are displayed in a scratch buffer.
+Several buffer variables are defined for those scratch buffers (please see
+|vcscommand-buffer-variables|).
+
+The notion of "current file" means either the current buffer, or, in the case
+of a directory buffer (such as Explorer or netrw buffers), the directory (and
+all subdirectories) represented by the the buffer.
+
+For convenience, any vcscommand invoked on a vcscommand scratch buffer acts as
+though it was invoked on the original file and splits the screen so that the
+output appears in a new window.
+
+Many of the commands accept revisions as arguments.  By default, most operate
+on the most recent revision on the current branch if no revision is specified.
+
+Each vcscommand is mapped to a key sequence starting with the <Leader>
+keystroke.  The default mappings may be overridden by supplying different
+mappings before the plugin is loaded, such as in the vimrc, in the standard
+fashion for plugin mappings.  For examples, please see
+|vcscommand-mappings-override|.
+
+The vcscommand plugin may be configured in several ways.  For more details,
+please see |vcscommand-customize|.
+
+==============================================================================
+
+4. vcscommand Manual                                   *vcscommand-manual*
+
+4.1 vcscommand commands                                        *vcscommand-commands*
+
+vcscommand defines the following commands:
+
+|:VCSAdd|
+|:VCSAnnotate|
+|:VCSBlame|
+|:VCSCommit|
+|:VCSDelete|
+|:VCSDiff|
+|:VCSGotoOriginal|
+|:VCSLog|
+|:VCSRemove|
+|:VCSRevert|
+|:VCSReview|
+|:VCSStatus|
+|:VCSUpdate|
+|:VCSVimDiff|
+
+The following commands are specific to CVS files:
+
+|:CVSEdit|
+|:CVSEditors|
+|:CVSUnedit|
+|:CVSWatch|
+|:CVSWatchAdd|
+|:CVSWatchOn|
+|:CVSWatchOff|
+|:CVSWatchRemove|
+|:CVSWatchers|
+
+:VCSAdd                                                        *:VCSAdd*
+
+This command adds the current file to source control.  Please note, this does
+not commit the newly-added file.  All parameters to the command are passed to
+the underlying VCS.
+
+:VCSAnnotate                                           *:VCSAnnotate*
+
+This command displays the current file with each line annotated with the
+version in which it was most recently changed.  If an argument is given, the
+argument is used as a revision number to display.  If not given an argument,
+it uses the most recent version of the file (on the current branch, if under
+CVS control).  Additionally, if the current buffer is a VCSAnnotate buffer
+already, the version number on the current line is used.
+
+For CVS buffers, the 'VCSCommandCVSAnnotateParent' option, if set to non-zero,
+will cause the above behavior to change.  Instead of annotating the version on
+the current line, the parent revision is used instead, crossing branches if
+necessary.
+
+The filetype of the vcscommand scratch buffer is set to one of 'CVSAnnotate',
+'SVNAnnotate', or 'SVKAnnotate' as appropriate, to take advantage of the
+bundled syntax files.
+
+:VCSBlame                                              *:VCSBlame*
+
+Alias for |:VCSAnnotate|.
+
+:VCSCommit[!]                                          *:VCSCommit*
+
+This command commits changes to the current file to source control.
+
+If called with arguments, the arguments are the log message.
+
+If '!' is used, an empty log message is committed.
+
+If called with no arguments, this is a two-step command.  The first step opens
+a buffer to accept a log message.  When that buffer is written, it is
+automatically closed and the file is committed using the information from that
+log message.  The commit can be abandoned if the log message buffer is deleted
+or wiped before being written.
+
+Alternatively, the mapping that is used to invoke :VCSCommit (by default
+<Leader>cc) can be used in the log message buffer to immediately commit.  This
+is useful if the |VCSCommandCommitOnWrite| variable is set to 0 to disable the
+normal commit-on-write behavior.
+
+:VCSDelete                                             *:VCSDelete*
+
+Deletes the current file and removes it from source control.  All parameters
+to the command are passed to the underlying VCS.
+
+:VCSDiff                                               *:VCSDiff*
+
+With no arguments, this displays the differences between the current file and
+its parent version under source control in a new scratch buffer.
+
+With one argument, the diff is performed on the current file against the
+specified revision.
+
+With two arguments, the diff is performed between the specified revisions of
+the current file.
+
+For CVS, this command uses the |VCSCommandCVSDiffOpt| variable to specify diff
+options.  If that variable does not exist, a plugin-specific default is used.
+If you wish to have no options, then set it to the empty string.
+
+For SVN, this command uses the |VCSCommandSVNDiffOpt| variable to specify diff
+options.  If that variable does not exist, the SVN default is used.
+Additionally, |VCSCommandSVNDiffExt| can be used to select an external diff
+application.
+
+:VCSGotoOriginal                                       *:VCSGotoOriginal*
+
+This command jumps to the source buffer if the current buffer is a VCS scratch
+buffer.
+
+:VCSGotoOriginal!
+
+Like ":VCSGotoOriginal" but also executes :bufwipeout on all VCS scrach
+buffers associated with the original file.
+
+:VCSInfo                                               *:VCSInfo*
+
+This command displays extended information about the current file in a new
+scratch buffer. 
+
+:VCSLock                                               *:VCSLock*
+
+This command locks the current file in order to prevent other users from
+concurrently modifying it.  The exact semantics of this command depend on the
+underlying VCS.  This does nothing in CVS.  All parameters are passed to the
+underlying VCS.
+
+:VCSLog                                                        *:VCSLog*
+
+Displays the version history of the current file in a new scratch buffer.  If
+there is one parameter supplied, it is taken as as a revision parameters to be
+passed through to the underlying VCS.  Otherwise, all parameters are passed to
+the underlying VCS.
+
+:VCSRemove                                             *:VCSRemove*
+
+Alias for |:VCSDelete|.
+
+:VCSRevert                                             *:VCSRevert*
+
+This command replaces the current file with the most recent version from the
+repository in order to wipe out any undesired changes.
+
+:VCSReview                                             *:VCSReview*
+
+Displays a particular version of the current file in a new scratch buffer.  If
+no argument is given, the most recent version of the file on the current
+branch is retrieved.
+
+:VCSStatus                                             *:VCSStatus*
+
+Displays versioning information about the current file in a new scratch
+buffer.  All parameters are passed to the underlying VCS.
+
+
+:VCSUnlock                                             *:VCSUnlock*
+
+Unlocks the current file in order to allow other users from concurrently
+modifying it.  The exact semantics of this command depend on the underlying
+VCS.  All parameters are passed to the underlying VCS.
+
+:VCSUpdate                                             *:VCSUpdate*
+
+Updates the current file with any relevant changes from the repository.  This
+intentionally does not automatically reload the current buffer, though vim
+should prompt the user to do so if the underlying file is altered by this
+command.
+
+:VCSVimDiff                                            *:VCSVimDiff*
+
+Uses vimdiff to display differences between versions of the current file.
+
+If no revision is specified, the most recent version of the file on the
+current branch is used.  With one argument, that argument is used as the
+revision as above.  With two arguments, the differences between the two
+revisions is displayed using vimdiff.
+                                                            
+With either zero or one argument, the original buffer is used to perform the
+vimdiff.  When the scratch buffer is closed, the original buffer will be
+returned to normal mode.
+                                                            
+Once vimdiff mode is started using the above methods, additional vimdiff
+buffers may be added by passing a single version argument to the command.
+There may be up to 4 vimdiff buffers total.
+                                                            
+Using the 2-argument form of the command resets the vimdiff to only those 2
+versions.  Additionally, invoking the command on a different file will close
+the previous vimdiff buffers.
+
+:CVSEdit                                               *:CVSEdit*
+
+This command performs "cvs edit" on the current file.  Yes, the output buffer
+in this case is almost completely useless.
+
+:CVSEditors                                            *:CVSEditors*
+
+This command performs "cvs edit" on the current file.
+
+:CVSUnedit                                             *:CVSUnedit*
+
+Performs "cvs unedit" on the current file.  Again, yes, the output buffer here
+is basically useless.
+
+:CVSWatch                                              *:CVSWatch*
+
+This command takes an argument which must be one of [on|off|add|remove].  The
+command performs "cvs watch" with the given argument on the current file.
+
+:CVSWatchAdd                                           *:CVSWatchAdd*
+
+This command is an alias for ":CVSWatch add"
+
+:CVSWatchOn                                            *:CVSWatchOn*
+
+This command is an alias for ":CVSWatch on"
+
+:CVSWatchOff                                           *:CVSWatchOff*
+
+This command is an alias for ":CVSWatch off"
+
+:CVSWatchRemove                                                *:CVSWatchRemove*
+
+This command is an alias for ":CVSWatch remove"
+
+:CVSWatchers                                           *:CVSWatchers*
+
+This command performs "cvs watchers" on the current file.
+
+4.2 Mappings                                           *vcscommand-mappings*
+
+By default, a mapping is defined for each command.  These mappings execute the
+default (no-argument) form of each command.
+
+<Leader>ca VCSAdd
+<Leader>cn VCSAnnotate
+<Leader>cc VCSCommit
+<Leader>cD VCSDelete
+<Leader>cd VCSDiff
+<Leader>cg VCSGotoOriginal
+<Leader>cG VCSGotoOriginal!
+<Leader>ci VCSInfo
+<Leader>cl VCSLog
+<Leader>cL VCSLock
+<Leader>cr VCSReview
+<Leader>cs VCSStatus
+<Leader>cu VCSUpdate
+<Leader>cU VCSUnlock
+<Leader>cv VCSVimDiff
+
+Only for CVS buffers:
+
+<Leader>ce CVSEdit
+<Leader>cE CVSEditors
+<Leader>ct CVSUnedit
+<Leader>cwv CVSWatchers
+<Leader>cwa CVSWatchAdd
+<Leader>cwn CVSWatchOn
+<Leader>cwf CVSWatchOff
+<Leader>cwf CVSWatchRemove
+
+                                               *vcscommand-mappings-override*
+
+The default mappings can be overriden by user-provided instead by mapping to
+<Plug>CommandName.  This is especially useful when these mappings collide with
+other existing mappings (vim will warn of this during plugin initialization,
+but will not clobber the existing mappings).
+
+For instance, to override the default mapping for :VCSAdd to set it to '\add',
+add the following to the vimrc:
+
+nmap \add <Plug>VCSAdd
+
+4.3 Automatic buffer variables                 *vcscommand-buffer-variables*
+
+Several buffer variables are defined in each vcscommand result buffer. These
+may be useful for additional customization in callbacks defined in the event
+handlers (please see |vcscommand-events|).
+
+The following variables are automatically defined:
+
+b:VCSCommandOriginalBuffer                     *b:VCSCommandOriginalBuffer*
+
+This variable is set to the buffer number of the source file.
+
+b:VCSCommandCommand                            *b:VCSCommandCommand*
+
+This variable is set to the name of the vcscommand that created the result
+buffer.
+
+b:VCSCommandSourceFile                         *b:VCSCommandSourceFile*
+
+This variable is set to the name of the original file under source control.
+
+b:VCSCommandVCSType                            *b:VCSCommandVCSType*
+
+This variable is set to the type of the source control.  This variable is also
+set on the original file itself.
+==============================================================================
+
+5. Configuration and customization                     *vcscommand-customize*
+                                                       *vcscommand-config*
+
+The vcscommand plugin can be configured in several ways:  by setting
+configuration variables (see |vcscommand-options|) or by defining vcscommand
+event handlers (see |vcscommand-events|).  Additionally, the vcscommand plugin
+supports a customized status line (see |vcscommand-statusline| and
+|vcscommand-buffer-management|).
+
+5.1 vcscommand configuration variables                 *vcscommand-options*
+
+Several variables affect the plugin's behavior.  These variables are checked
+at time of execution, and may be defined at the window, buffer, or global
+level and are checked in that order of precedence.
+
+
+The following variables are available:
+
+|VCSCommandCommitOnWrite|
+|VCSCommandCVSDiffOpt|
+|VCSCommandCVSExec|
+|VCSCommandDeleteOnHide|
+|VCSCommandDiffSplit|
+|VCSCommandDisableMappings|
+|VCSCommandDisableExtensionMappings|
+|VCSCommandEdit|
+|VCSCommandEnableBufferSetup|
+|VCSCommandResultBufferNameExtension|
+|VCSCommandResultBufferNameFunction|
+|VCSCommandSplit|
+|VCSCommandSVKExec|
+|VCSCommandSVNDiffExt|
+|VCSCommandSVNDiffOpt|
+|VCSCommandSVNExec|
+
+VCSCommandCommitOnWrite                                *VCSCommandCommitOnWrite*
+
+This variable, if set to a non-zero value, causes the pending commit
+to take place immediately as soon as the log message buffer is written.
+If set to zero, only the VCSCommit mapping will cause the pending commit to
+occur. If not set, it defaults to 1.
+
+VCSCommandCVSExec                              *VCSCommandCVSExec*
+
+This variable controls the executable used for all CVS commands  If not set,
+it defaults to "cvs".
+
+VCSCommandDeleteOnHide                         *VCSCommandDeleteOnHide*
+
+This variable, if set to a non-zero value, causes the temporary result buffers
+to automatically delete themselves when hidden.
+
+VCSCommandCVSDiffOpt                           *VCSCommandCVSDiffOpt*
+
+This variable, if set, determines the options passed to the diff command of
+CVS.  If not set, it defaults to 'u'.
+
+VCSCommandDiffSplit                            *VCSCommandDiffSplit*
+
+This variable overrides the |VCSCommandSplit| variable, but only for buffers
+created with |:VCSVimDiff|.
+
+VCSCommandDisableMappings                      *VCSCommandDisableMappings*
+
+This variable, if set to a non-zero value, prevents the default command
+mappings from being set.  This supercedes 
+|VCSCommandDisableExtensionMappings|.
+
+VCSCommandDisableExtensionMappings     *VCSCommandDisableExtensionMappings*
+
+This variable, if set to a non-zero value, prevents the default command
+mappings from being set for commands specific to an individual VCS.
+
+VCSCommandEdit                                 *VCSCommandEdit*
+
+This variable controls whether the original buffer is replaced ('edit') or
+split ('split').  If not set, it defaults to 'split'.
+
+VCSCommandEnableBufferSetup                    *VCSCommandEnableBufferSetup*
+
+This variable, if set to a non-zero value, activates VCS buffer management
+mode see (|vcscommand-buffer-management|).  This mode means that the
+'VCSCommandBufferInfo' variable is filled with version information if the file
+is VCS-controlled.  This is useful for displaying version information in the
+status bar.
+
+VCSCommandResultBufferNameExtension    *VCSCommandResultBufferNameExtension*
+
+This variable, if set to a non-blank value, is appended to the name of the VCS
+command output buffers.  For example, '.vcs'.  Using this option may help
+avoid problems caused by autocommands dependent on file extension.
+
+VCSCommandResultBufferNameFunction     *VCSCommandResultBufferNameFunction*
+
+This variable, if set, specifies a custom function for naming VCS command
+output buffers.  This function is expected to return the new buffer name, and
+will be passed the following arguments:
+
+  command - name of the VCS command being executed (such as 'Log' or
+  'Diff').
+  
+  originalBuffer - buffer number of the source file.
+  
+  vcsType - type of VCS controlling this file (such as 'CVS' or 'SVN').
+  
+  statusText - extra text associated with the VCS action (such as version
+  numbers).
+
+VCSCommandSplit                                        *VCSCommandSplit*
+
+This variable controls the orientation of the various window splits that
+may occur.
+
+If set to 'horizontal', the resulting windows will be on stacked on top of
+one another.  If set to 'vertical', the resulting windows will be
+side-by-side.  If not set, it defaults to 'horizontal' for all but
+VCSVimDiff windows.
+
+VCSCommandSVKExec                              *VCSCommandSVKExec*
+
+This variable controls the executable used for all SVK commands  If not set,
+it defaults to "svk".
+
+VCSCommandSVNDiffExt                           *VCSCommandSVNDiffExt*
+
+This variable, if set, is passed to SVN via the --diff-cmd command to select
+an external application for performing the diff.
+
+VCSCommandSVNDiffOpt                           *VCSCommandSVNDiffOpt*
+
+This variable, if set, determines the options passed with the '-x' parameter
+to the SVN diff command.  If not set, no options are passed.
+
+VCSCommandSVNExec                              *VCSCommandSVNExec*
+
+This variable controls the executable used for all SVN commands  If not set,
+it defaults to "svn".
+
+5.2 VCSCommand events                          *vcscommand-events*
+
+For additional customization, vcscommand can trigger user-defined events.
+Event handlers are provided by defining User event autocommands (see
+|autocommand|, |User|) in the vcscommand group with patterns matching the
+event name.
+
+For instance, the following could be added to the vimrc to provide a 'q'
+mapping to quit a vcscommand scratch buffer:
+
+augroup VCSCommand
+  au User VCSBufferCreated silent! nmap <unique> <buffer> q: bwipeout<cr>
+augroup END
+
+The following hooks are available:
+
+VCSBufferCreated               This event is fired just after a vcscommand
+                                result buffer is created and populated.  It is
+                                executed within the context of the vcscommand
+                                buffer.  The vcscommand buffer variables may
+                                be useful for handlers of this event (please
+                                see |vcscommand-buffer-variables|).
+
+VCSBufferSetup                 This event is fired just after vcscommand buffer
+                                setup occurs, if enabled.
+
+VCSPluginInit                  This event is fired when the vcscommand plugin
+                               first loads.
+
+VCSPluginFinish                        This event is fired just after the vcscommand
+                               plugin loads.
+
+VCSVimDiffFinish               This event is fired just after the VCSVimDiff
+                               command executes to allow customization of,
+                               for instance, window placement and focus.
+
+Additionally, there is another hook which is used internally to handle loading
+the multiple scripts in order.  This hook should probably not be used by an
+end user without a good idea of how it works.  Among other things, any events
+associated with this hook are cleared after they are executed (during
+vcscommand.vim script initialization).
+
+VCSLoadExtensions              This event is fired just before the
+                                VCSPluginFinish.  It is used internally to
+                                execute any commands from the VCS
+                                implementation plugins that needs to be
+                                deferred until the primary plugin is
+                                initialized.
+
+5.3 vcscommand buffer naming                           *vcscommand-naming*
+
+vcscommand result buffers use the following naming convention:
+[{VCS type} {VCS command} {Source file name}]
+
+If additional buffers are created that would otherwise conflict, a
+distinguishing number is added:
+
+[{VCS type} {VCS command} {Source file name}] (1,2, etc)
+
+5.4 vcscommand status line support                     *vcscommand-statusline*
+
+It is intended that the user will customize the |'statusline'| option to
+include vcscommand result buffer attributes.  A sample function that may be
+used in the |'statusline'| option is provided by the plugin,
+VCSCommandGetStatusLine().  In order to use that function in the status line, do
+something like the following:
+
+set statusline=%<%f\ %{VCSCommandGetStatusLine()}\ %h%m%r%=%l,%c%V\ %P
+
+of which %{VCSCommandGetStatusLine()} is the relevant portion.
+
+The sample VCSCommandGetStatusLine() function handles both vcscommand result
+buffers and VCS-managed files if vcscommand buffer management is enabled
+(please see |vcscommand-buffer-management|).
+
+5.5 vcscommand buffer management               *vcscommand-buffer-management*
+
+The vcscommand plugin can operate in buffer management mode, which means that
+it attempts to set a buffer variable ('VCSCommandBufferInfo') upon entry into
+a buffer.  This is rather slow because it means that the VCS will be invoked
+at each entry into a buffer (during the |BufEnter| autocommand).
+
+This mode is disabled by default.  In order to enable it, set the
+|VCSCommandEnableBufferSetup| variable to a true (non-zero) value.  Enabling
+this mode simply provides the buffer variable mentioned above.  The user must
+explicitly include information from the variable in the |'statusline'| option
+if they are to appear in the status line (but see |vcscommand-statusline| for
+a simple way to do that).
+
+The 'VCSCommandBufferInfo' variable is a list which contains, in order, the
+revision of the current file, the latest revision of the file in the
+repository, and (for CVS) the name of the branch.  If those values cannot be
+determined, the list is a single element:  'Unknown'.
+
+==============================================================================
+
+6. SSH "integration"                                   *vcscommand-ssh*
+
+The following instructions are intended for use in integrating the
+vcscommand.vim plugin with an SSH-based CVS environment.
+
+Familiarity with SSH and CVS are assumed.
+
+These instructions assume that the intent is to have a message box pop up in
+order to allow the user to enter a passphrase.  If, instead, the user is
+comfortable using certificate-based authentication, then only instructions
+6.1.1 and 6.1.2 (and optionally 6.1.4) need to be followed; ssh should then
+work transparently.
+
+6.1 Environment settings                               *vcscommand-ssh-env*
+
+6.1.1 CVSROOT should be set to something like:
+
+       :ext:user@host:/path_to_repository
+
+6.1.2 CVS_RSH should be set to:
+
+       ssh
+
+       Together, those settings tell CVS to use ssh as the transport when
+       performing CVS calls.
+
+6.1.3 SSH_ASKPASS should be set to the password-dialog program.  In my case,
+       running gnome, it's set to:
+
+       /usr/libexec/openssh/gnome-ssh-askpass
+
+       This tells SSH how to get passwords if no input is available.
+
+6.1.4 OPTIONAL.  You may need to set SSH_SERVER to the location of the cvs
+       executable on the remote (server) machine.
+
+6.2 CVS wrapper program                                *vcscommand-ssh-wrapper*
+
+Now you need to convince SSH to use the password-dialog program.  This means
+you need to execute SSH (and therefore CVS) without standard input.  The
+following script is a simple perl wrapper that dissasociates the CVS command
+from the current terminal.  Specific steps to do this may vary from system to
+system; the following example works for me on linux.
+
+#!/usr/bin/perl -w
+use strict;
+use POSIX qw(setsid);
+open STDIN, '/dev/null';
+fork and do {wait; exit;};
+setsid;
+exec('cvs', @ARGV);
+
+6.3 Configuring vcscommand.vim                 *vcscommand-ssh-config*
+
+At this point, you should be able to use your wrapper script to invoke CVS with
+various commands, and get the password dialog.  All that's left is to make CVS
+use your newly-created wrapper script.
+
+6.3.1 Tell vcscommand.vim what CVS executable to use.  The easiest way to do this
+       is globally, by putting the following in your .vimrc:
+
+       let VCSCommandCVSExec=/path/to/cvs/wrapper/script
+
+6.4 Where to go from here                      *vcscommand-ssh-other*
+
+The script given above works even when non-SSH CVS connections are used,
+except possibly when interactively entering the message for CVS commit log
+(depending on the editor you use... VIM works fine).  Since the vcscommand.vim
+plugin handles that message without a terminal, the wrapper script can be used
+all the time.
+
+This allows mixed-mode operation, where some work is done with SSH-based CVS
+repositories, and others with pserver or local access.
+
+It is possible, though beyond the scope of the plugin, to dynamically set the
+CVS executable based on the CVSROOT for the file being edited.  The user
+events provided (such as VCSBufferCreated and VCSBufferSetup) can be used to
+set a buffer-local value (b:VCSCommandCVSExec) to override the CVS executable
+on a file-by-file basis.  Alternatively, much the same can be done (less
+automatically) by the various project-oriented plugins out there.
+
+It is highly recommended for ease-of-use that certificates with no passphrase
+or ssh-agent are employed so that the user is not given the password prompt
+too often.
+
+==============================================================================
+
+7. Changes from cvscommandi                            *cvscommand-changes*
+
+1.  Require Vim 7 in order to leverage several convenient features; also
+because I wanted to play with Vim 7.
+
+2.  Renamed commands to start with 'VCS' instead of 'CVS'.  The exceptions are
+the 'CVSEdit' and 'CVSWatch' family of commands, which are specific to CVS.
+
+3.  Renamed options, events to start with 'VCSCommand'.
+
+4.  Removed option to jump to the parent version of the current line in an
+annotated buffer, as opposed to the version on the current line.  This made
+little sense in the branching scheme used by subversion, where jumping to a
+parent branch required finding a different location in the repository.  It
+didn't work consistently in CVS anyway.
+
+5.  Removed option to have nameless scratch buffers.
+
+6.  Changed default behavior of scratch buffers to split the window instead of
+displaying in the current window.  This may still be overridden using the
+'VCSCommandEdit' option.
+
+7.  Split plugin into multiple plugins.
+
+8.  Added 'VCSLock' and 'VCSUnlock' commands.  These are implemented for
+subversion but not for CVS.  These were not kept specific to subversion as they
+seemed more general in nature and more likely to be supported by any future VCS
+supported by this plugin.
+
+9.  Changed name of buffer variables set by commands.
+
+'b:cvsOrigBuffNR' became 'b:VCSCommandOriginalBuffer'
+'b:cvscmd' became 'b:VCSCommandCommand'
+
+10.  Added new automatic variables to command result buffers.
+
+'b:VCSCommandSourceFile'
+'b:VCSCommandVCSType'
+
+==============================================================================
+
+8. Known bugs                                          *vcscommand-bugs*
+
+Please let me know if you run across any.
+
+CVSUnedit may, if a file is changed from the repository, provide prompt text
+to determine whether the changes should be thrown away.  Currently, that text
+shows up in the CVS result buffer as information; there is no way for the user
+to actually respond to the prompt and the CVS unedit command does nothing.  If
+this really bothers anyone, please let me know.
+
+VCSVimDiff, when using the original (real) source buffer as one of the diff
+buffers, uses some hacks to try to restore the state of the original buffer
+when the scratch buffer containing the other version is destroyed.  There may
+still be bugs in here, depending on many configuration details.
+
+vim:tw=78:ts=8:ft=help
diff --git a/plugin/fuzzyfinder.vim b/plugin/fuzzyfinder.vim
new file mode 100644 (file)
index 0000000..8e758ac
--- /dev/null
@@ -0,0 +1,1676 @@
+"=============================================================================
+" fuzzyfinder.vim : Fuzzy/Partial pattern explorer for
+"                   buffer/file/MRU/command/favorite/tag/etc.
+"=============================================================================
+"
+" Author:  Takeshi NISHIDA <[email protected]>
+" Version: 2.13, for Vim 7.1
+" Licence: MIT Licence
+" URL:     http://www.vim.org/scripts/script.php?script_id=1984
+"
+" GetLatestVimScripts: 1984 1 :AutoInstall: fuzzyfinder.vim
+"
+"=============================================================================
+" DOCUMENT: {{{1
+"   Japanese: http://vim.g.hatena.ne.jp/keyword/fuzzyfinder.vim
+"
+"-----------------------------------------------------------------------------
+" Description:
+"   Fuzzyfinder provides convenient ways to quickly reach the buffer/file you
+"   want. Fuzzyfinder finds matching files/buffers with a fuzzy/partial
+"   pattern to which it converted the entered pattern.
+"
+"   E.g.: entered pattern -> fuzzy pattern / partial pattern
+"         abc             -> *a*b*c*       / *abc*
+"         a?c             -> *a?c*         / *a?c*
+"         dir/file        -> dir/*f*i*l*e* / dir/*file*
+"         d*r/file        -> d*r/*f*i*l*e* / d*r/*file*
+"         ../**/s         -> ../**/*s*     / ../**/*s*
+"
+"     (** allows searching a directory tree.)
+"
+"   You will be happy when:
+"     "./OhLongLongLongLongLongFile.txt"
+"     "./AhLongLongLongLongLongName.txt"
+"     "./AhLongLongLongLongLongFile.txt" <- you want :O
+"     Type "AF" and "AhLongLongLongLongLongFile.txt" will be select. :D
+"
+"   Fuzzyfinder has some modes:
+"     - Buffer mode
+"     - File mode
+"     - Directory mode (yet another :cd command)
+"     - MRU-file mode (most recently used files)
+"     - MRU-command mode (most recently used command-lines)
+"     - Favorite-file mode
+"     - Tag mode (yet another :tag command)
+"     - Tagged-file mode (files which are included in current tags)
+"
+"   Fuzzyfinder supports the multibyte.
+"
+"-----------------------------------------------------------------------------
+" Installation:
+"   Drop this file in your plugin directory.
+"
+"-----------------------------------------------------------------------------
+" Usage:
+"   Starting Fuzzyfinder:
+"     You can start Fuzzyfinder by the following commands:
+"
+"       :FuzzyFinderBuffer      - launchs buffer-mode Fuzzyfinder.
+"       :FuzzyFinderFile        - launchs file-mode Fuzzyfinder.
+"       :FuzzyFinderDir         - launchs directory-mode Fuzzyfinder.
+"       :FuzzyFinderMruFile     - launchs MRU-file-mode Fuzzyfinder.
+"       :FuzzyFinderMruCmd      - launchs MRU-command-mode Fuzzyfinder.
+"       :FuzzyFinderFavFile     - launchs favorite-file-mode Fuzzyfinder.
+"       :FuzzyFinderTag         - launchs tag-mode Fuzzyfinder.
+"       :FuzzyFinderTaggedFile  - launchs tagged-file-mode Fuzzyfinder.
+"
+"     It is recommended to map these commands. These commands can take initial
+"     text as a command argument. The text will be entered after Fuzzyfinder
+"     launched. If a command was executed with a ! modifier (e.g.
+"     :FuzzyFinderTag!), it enables the partial matching instead of the fuzzy
+"     matching.
+"
+"
+"   In Fuzzyfinder:
+"     The entered pattern is converted to the fuzzy pattern and buffers/files
+"     which match the pattern is shown in a completion menu.
+"
+"     A completion menu is shown when you type at the end of the line and the
+"     length of entered pattern is more than setting value. By default, it is
+"     shown at the beginning.
+"
+"     If too many items (200, by default) were matched, the completion is
+"     aborted to reduce nonresponse.
+"
+"     If an item were matched with entered pattern exactly, it is shown first.
+"     The item whose file name has longer prefix matching is placed upper.
+"     Also, an item which matched more sequentially is placed upper. The item
+"     whose index were matched with a number suffixed with entered pattern is
+"     placed lower. the first item in the completion menu will be selected
+"     automatically.
+"
+"     You can open a selected item in various ways:
+"       <CR>  - opens in a previous window.
+"       <C-j> - opens in a split window.
+"       <C-k> - opens in a vertical-split window.
+"       <C-]> - opens in a new tab page.
+"     In MRU-command mode, <CR> executes a selected command and others just
+"     put it into a command-line. These key mappings are customizable.
+"
+"     To cancel and return to previous window, leave Insert mode.
+"
+"     To Switch the mode without leaving Insert mode, use <C-l> or <C-o>.
+"     This key mapping is customizable.
+"
+"     If you want to temporarily change whether or not to ignore case, use
+"     <C-t>. This key mapping is customizable.
+"
+"   To Hide The Completion Temporarily Menu In Fuzzyfinder:
+"     You can close it by <C-e> and reopen it by <C-x><C-u>.
+"
+"   About Highlighting:
+"     Fuzzyfinder highlights the buffer with "Error" group when the completion
+"     item was not found or the completion process was aborted.
+"
+"   About Alternative Approach For Tag Jump:
+"     Following mappings are replacements for :tag and <C-]>:
+"
+"       nnoremap <silent> <C-f><C-t> :FuzzyFinderTag!<CR>
+"       nnoremap <silent> <C-]>      :FuzzyFinderTag! <C-r>=expand('<cword>')<CR><CR>
+"
+"     In the tag mode, it is recommended to use partial matching instead of
+"     fuzzy matching.
+"
+"   About Tagged File Mode:
+"     The files which are included in the current tags are the ones which are
+"     related to the current working environment. So this mode is a pseudo
+"     project mode.
+"
+"   About Usage Of Command Argument:
+"     As an example, if you want to launch file-mode Fuzzyfinder with the full
+"     path of current directory, map like below:
+"
+"       nnoremap <C-p> :FuzzyFinderFile <C-r>=fnamemodify(getcwd(), ':p')<CR><CR>
+"
+"     Instead, if you want the directory of current buffer and not current
+"     directory:
+"
+"       nnoremap <C-p> :FuzzyFinderFile <C-r>=expand('%:~:.')[:-1-len(expand('%:~:.:t'))]<CR><CR>
+"
+"   About Abbreviations And Multiple Search:
+"     You can use abbreviations and multiple search in each mode. For example,
+"     set as below:
+"
+"       let g:FuzzyFinderOptions.Base.abbrev_map  = {
+"             \   "^WORK" : [
+"             \     "~/project/**/src/",
+"             \     ".vim/plugin/",
+"             \   ],
+"             \ }
+"
+"     And type "WORKtxt" in file-mode Fuzzyfinder, then it searches by
+"     following patterns:
+"
+"       "~/project/**/src/*t*x*t*"
+"       ".vim/plugin/*t*x*t*"
+"
+"   Adding Favorite Files:
+"     You can add a favorite file by the following commands:
+"
+"       :FuzzyFinderAddFavFile {filename}
+"
+"     If you do not specify the filename, current file name is used.
+"
+"   About Information File:
+"     Fuzzyfinder writes information of the MRU, favorite, etc to the file by
+"     default (~/.vimfuzzyfinder).
+
+"     :FuzzyFinderEditInfo command is helpful in editing your information
+"     file. This command reads the information file in new unnamed buffer.
+"     Write the buffer and the information file will be updated.
+"
+"   About Cache:
+"     Once a cache was created, It is not updated automatically to improve
+"     response by default. To update it, use :FuzzyFinderRemoveCache command.
+"
+"   About Migemo:
+"     Migemo is a search method for Japanese language.
+"
+"-----------------------------------------------------------------------------
+" Options:
+"   You can set options via g:FuzzyFinderOptions which is a dictionary. See
+"   the folded section named "GLOBAL OPTIONS:" for details. To easily set
+"   options for customization, put necessary entries from GLOBAL OPTIONS into
+"   your vimrc file and edit those values.
+"
+"-----------------------------------------------------------------------------
+" Setting Example:
+"   let g:FuzzyFinderOptions = { 'Base':{}, 'Buffer':{}, 'File':{}, 'Dir':{}, 'MruFile':{}, 'MruCmd':{}, 'FavFile':{}, 'Tag':{}, 'TaggedFile':{}}
+"   let g:FuzzyFinderOptions.Base.ignore_case = 1
+"   let g:FuzzyFinderOptions.Base.abbrev_map  = {
+"         \   '\C^VR' : [
+"         \     '$VIMRUNTIME/**',
+"         \     '~/.vim/**',
+"         \     '$VIM/.vim/**',
+"         \     '$VIM/vimfiles/**',
+"         \   ],
+"         \ }
+"   let g:FuzzyFinderOptions.MruFile.max_item = 200
+"   let g:FuzzyFinderOptions.MruCmd.max_item = 200
+"   nnoremap <silent> <C-n>      :FuzzyFinderBuffer<CR>
+"   nnoremap <silent> <C-m>      :FuzzyFinderFile <C-r>=expand('%:~:.')[:-1-len(expand('%:~:.:t'))]<CR><CR>
+"   nnoremap <silent> <C-j>      :FuzzyFinderMruFile<CR>
+"   nnoremap <silent> <C-k>      :FuzzyFinderMruCmd<CR>
+"   nnoremap <silent> <C-p>      :FuzzyFinderDir <C-r>=expand('%:p:~')[:-1-len(expand('%:p:~:t'))]<CR><CR>
+"   nnoremap <silent> <C-f><C-d> :FuzzyFinderDir<CR>
+"   nnoremap <silent> <C-f><C-f> :FuzzyFinderFavFile<CR>
+"   nnoremap <silent> <C-f><C-t> :FuzzyFinderTag!<CR>
+"   nnoremap <silent> <C-f><C-g> :FuzzyFinderTaggedFile<CR>
+"   noremap  <silent> g]         :FuzzyFinderTag! <C-r>=expand('<cword>')<CR><CR>
+"   nnoremap <silent> <C-f>F     :FuzzyFinderAddFavFile<CR>
+"   nnoremap <silent> <C-f><C-e> :FuzzyFinderEditInfo<CR>
+"
+"-----------------------------------------------------------------------------
+" Special Thanks:
+"   Vincent Wang
+"   Ingo Karkat
+"   Nikolay Golubev
+"   Brian Doyle
+"   id:secondlife
+"   Matt Tolton
+"
+"-----------------------------------------------------------------------------
+" ChangeLog:
+"   2.13:
+"     - Fixed a bug that a directory disappeared when a file in that directroy
+"       was being opened in File/Mru-File mode.
+"
+"   2.12:
+"     - Changed to be able to show completion items in the order of recently
+"       used in Buffer mode.
+"     - Added g:FuzzyFinderOptions.Buffer.mru_order option.
+"
+"   2.11:
+"     - Changed that a dot sequence of entered pattern is expanded to parent
+"       directroies in File/Dir mode.
+"       E.g.: "foo/...bar" -> "foo/../../bar"
+"     - Fixed a bug that a prompt string was excessively inserted.
+"
+"   2.10:
+"     - Changed not to show a current buffer in a completion menu.
+"     - Fixed a bug that a filename to open was not been escaped.
+"     - Added 'prompt' option.
+"     - Added 'prompt_highlight' option.
+"     - Removed g:FuzzyFinderOptions.MruFile.no_special_buffer option.
+"
+"   2.9:
+"     - Enhanced <BS> behavior in Fuzzyfinder and added 'smart_bs' option.
+"     - Fixed a bug that entered pattern was not been escaped.
+"     - Fixed not to insert "zv" with "c/pattern<CR>" command in Normal mode.
+"     - Avoid the slow down problem caused by filereadable() check for the MRU
+"       information in BufEnter/BufWritePost.
+"
+"   2.8.1:
+"     - Fixed a bug caused by the non-escaped buffer name "[Fuzzyfinder]".
+"     - Fixed a command to open in a new tab page in Buffer mode.
+"   2.8:
+"     - Added 'trim_length' option.
+"     - Added 'switch_order' option.
+"     - Fixed a bug that entered command did not become the newest in the
+"       history.
+"     - Fixed a bug that folds could not open with <CR> in a command-line when
+"       searching.
+"     - Removed 'excluded_indicator' option. Now a completion list in Buffer
+"       mode is the same as a result of :buffers.
+"
+"   2.7:
+"     - Changed to find an item whose index is matched with the number
+"       suffixed with entered pattern.
+"     - Fixed the cache bug after changing current directroy in File mode.
+"
+"   2.6.2:
+"     - Fixed not to miss changes in options when updates the MRU information.
+"
+"   2.6.1:
+"     - Fixed a bug related to floating-point support.
+"     - Added support for GetLatestVimScripts.
+"
+"   2.6:
+"     - Revived MRU-command mode. The problem with a command-line abbreviation
+"       was solved.
+"     - Changed the specification of the information file.
+"     - Added :FuzzyFinderEditInfo command.
+
+"   2.5.1:
+"     - Fixed to be able to match "foo/./bar" by "foo/**/bar" in File mode.
+"     - Fixed to be able to open a space-containing file in File mode.
+"     - Fixed to honor the current working directory properly in File mode.
+"
+"   2.5:
+"     - Fixed the bug that a wrong initial text is entered after switching to a
+"       next mode.
+"     - Fixed the bug that it does not return to previous window after leaving
+"       Fuzzyfinder one.
+"
+"   2.4:
+"     - Fixed the bug that Fuzzyfinder fails to open a file caused by auto-cd
+"       plugin/script.
+"
+"   2.3:
+"     - Added a key mapping to open items in a new tab page and
+"       g:FuzzyFinderOptions.Base.key_open_tab opton.
+"     - Changed to show Fuzzyfinder window above last window even if
+"       'splitbelow' was set.
+"     - Changed to set nocursorline and nocursorcolumn in Fuzzyfinder.
+"     - Fixed not to push up a buffer number unlimitedly.
+"
+"   2.2:
+"     - Added new feature, which is the partial matching.
+"     - Fixed the bug that an error occurs when "'" was entered.
+"
+"   2.1:
+"     - Restructured the option system AGAIN. Sorry :p
+"     - Changed to inherit a typed text when switching a mode without leaving
+"       Insert mode.
+"     - Changed commands which launch explorers to be able to take a argument
+"       for initial text.
+"     - Changed to complete file names by relative path and not full path in
+"       the buffer/mru-file/tagged-file mode.
+"     - Changed to highlight a typed text when the completion item was not
+"       found or the completion process was aborted.
+"     - Changed to create caches for each tag file and not working directory
+"       in the tag/tagged-file mode.
+"     - Fixed the bug that the buffer mode couldn't open a unnamed buffer.
+"     - Added 'matching_limit' option.
+"     - Removed 'max_match' option. Use 'matching_limit' option instead.
+"     - Removed 'initial_text' option. Use command argument instead.
+"     - Removed the MRU-command mode.
+"
+"   2.0:
+"     - Added the tag mode.
+"     - Added the tagged-file mode.
+"     - Added :FuzzyFinderRemoveCache command.
+"     - Restructured the option system. many options are changed names or
+"       default values of some options.
+"     - Changed to hold and reuse caches of completion lists by default.
+"     - Changed to set filetype 'fuzzyfinder'.
+"     - Disabled the MRU-command mode by default because there are problems.
+"     - Removed FuzzyFinderAddMode command.
+"
+"   1.5:
+"     - Added the directory mode.
+"     - Fixed the bug that it caused an error when switch a mode in Insert
+"       mode.
+"     - Changed g:FuzzyFinder_KeySwitchMode type to a list.
+"
+"   1.4:
+"     - Changed the specification of the information file.
+"     - Added the MRU-commands mode.
+"     - Renamed :FuzzyFinderAddFavorite command to :FuzzyFinderAddFavFile.
+"     - Renamed g:FuzzyFinder_MruModeVars option to
+"       g:FuzzyFinder_MruFileModeVars.
+"     - Renamed g:FuzzyFinder_FavoriteModeVars option to
+"       g:FuzzyFinder_FavFileModeVars.
+"     - Changed to show registered time of each item in MRU/favorite mode.
+"     - Added 'timeFormat' option for MRU/favorite modes.
+"
+"   1.3:
+"     - Fixed a handling of multi-byte characters.
+"
+"   1.2:
+"     - Added support for Migemo. (Migemo is Japanese search method.)
+"
+"   1.1:
+"     - Added the favorite mode.
+"     - Added new features, which are abbreviations and multiple search.
+"     - Added 'abbrevMap' option for each mode.
+"     - Added g:FuzzyFinder_MruModeVars['ignoreSpecialBuffers'] option.
+"     - Fixed the bug that it did not work correctly when a user have mapped
+"       <C-p> or <Down>.
+"
+"   1.0:
+"     - Added the MRU mode.
+"     - Added commands to add and use original mode.
+"     - Improved the sorting algorithm for completion items.
+"     - Added 'initialInput' option to automatically insert a text at the
+"       beginning of a mode.
+"     - Changed that 'excludedPath' option works for the entire path.
+"     - Renamed some options. 
+"     - Changed default values of some options. 
+"     - Packed the mode-specific options to dictionaries.
+"     - Removed some options.
+"
+"   0.6:
+"     - Fixed some bugs.
+
+"   0.5:
+"     - Improved response by aborting processing too many items.
+"     - Changed to be able to open a buffer/file not only in previous window
+"       but also in new window.
+"     - Fixed a bug that recursive searching with '**' does not work.
+"     - Added g:FuzzyFinder_CompletionItemLimit option.
+"     - Added g:FuzzyFinder_KeyOpen option.
+"
+"   0.4:
+"     - Improved response of the input.
+"     - Improved the sorting algorithm for completion items. It is based on
+"       the matching level. 1st is perfect matching, 2nd is prefix matching,
+"       and 3rd is fuzzy matching.
+"     - Added g:FuzzyFinder_ExcludePattern option.
+"     - Removed g:FuzzyFinder_WildIgnore option.
+"     - Removed g:FuzzyFinder_EchoPattern option.
+"     - Removed g:FuzzyFinder_PathSeparator option.
+"     - Changed the default value of g:FuzzyFinder_MinLengthFile from 1 to 0.
+"
+"   0.3:
+"     - Added g:FuzzyFinder_IgnoreCase option.
+"     - Added g:FuzzyFinder_KeyToggleIgnoreCase option.
+"     - Added g:FuzzyFinder_EchoPattern option.
+"     - Changed the open command in a buffer mode from ":edit" to ":buffer" to
+"       avoid being reset cursor position.
+"     - Changed the default value of g:FuzzyFinder_KeyToggleMode from
+"       <C-Space> to <F12> because <C-Space> does not work on some CUI
+"       environments.
+"     - Changed to avoid being loaded by Vim before 7.0.
+"     - Fixed a bug with making a fuzzy pattern which has '\'.
+"
+"   0.2:
+"     - A bug it does not work on Linux is fixed.
+"
+"   0.1:
+"     - First release.
+"
+" }}}1
+"=============================================================================
+" INCLUDE GUARD: {{{1
+if exists('loaded_fuzzyfinder') || v:version < 701
+  finish
+endif
+let loaded_fuzzyfinder = 1
+
+" }}}1
+"=============================================================================
+" FUNCTION: {{{1
+"-----------------------------------------------------------------------------
+" LIST FUNCTIONS:
+
+function! s:Unique(in)
+  let sorted = sort(a:in)
+  if len(sorted) < 2
+    return sorted
+  endif
+  let last = remove(sorted, 0)
+  let result = [last]
+  for item in sorted
+    if item != last
+      call add(result, item)
+      let last = item
+    endif
+  endfor
+  return result
+endfunction
+
+" [ [0], [1,2], [3] ] -> [ 0, 1, 2, 3 ]
+function! s:Concat(in)
+  let result = []
+  for l in a:in
+    let result += l
+  endfor
+  return result
+endfunction
+
+" [ [ 0, 1 ], [ 2, 3, 4 ], ] -> [ [0,2], [0,3], [0,4], [1,2], [1,3], [1,4] ]
+function! s:CartesianProduct(lists)
+  if empty(a:lists)
+    return []
+  endif
+  "let result = map((a:lists[0]), '[v:val]')
+  let result = [ [] ]
+  for l in a:lists
+    let temp = []
+    for r in result
+      let temp += map(copy(l), 'add(copy(r), v:val)')
+    endfor
+    let result = temp
+  endfor
+  return result
+endfunction
+
+" copy + filter + limit
+function! s:FilterEx(in, expr, limit)
+  if a:limit <= 0
+    return filter(copy(a:in), a:expr)
+  endif
+  let result = []
+  let stride = a:limit * 3 / 2 " x1.5
+  for i in range(0, len(a:in) - 1, stride)
+    let result += filter(a:in[i : i + stride - 1], a:expr)
+    if len(result) >= a:limit
+      return remove(result, 0, a:limit - 1)
+    endif
+  endfor
+  return result
+endfunction
+
+" 
+function! s:FilterMatching(entries, key, pattern, index, limit)
+  return s:FilterEx(a:entries, 'v:val[''' . a:key . '''] =~ ' . string(a:pattern) . ' || v:val.index == ' . a:index, a:limit)
+endfunction
+
+function! s:ExtendIndexToEach(in, offset)
+  for i in range(len(a:in))
+    let a:in[i].index = i + a:offset
+  endfor
+  return a:in
+endfunction
+
+function! s:UpdateMruList(mrulist, new_item, key, max_item, excluded)
+  let result = copy(a:mrulist)
+  let result = filter(result,'v:val[a:key] != a:new_item[a:key]')
+  let result = insert(result, a:new_item)
+  let result = filter(result, 'v:val[a:key] !~ a:excluded')
+  return result[0 : a:max_item - 1]
+endfunction
+
+"-----------------------------------------------------------------------------
+" STRING FUNCTIONS:
+
+" trims a:str and add a:mark if a length of a:str is more than a:len
+function! s:TrimLast(str, len)
+  if a:len <= 0 || len(a:str) <= a:len
+    return a:str
+  endif
+  return a:str[:(a:len - len(s:ABBR_TRIM_MARK) - 1)] . s:ABBR_TRIM_MARK
+endfunction
+
+" takes suffix numer. if no digits, returns -1
+function! s:SuffixNumber(str)
+  let s = matchstr(a:str, '\d\+$')
+  return (len(s) ? str2nr(s) : -1)
+endfunction
+
+function! s:ConvertWildcardToRegexp(expr)
+  let re = escape(a:expr, '\')
+  for [pat, sub] in [ [ '*', '\\.\\*' ], [ '?', '\\.' ], [ '[', '\\[' ], ]
+    let re = substitute(re, pat, sub, 'g')
+  endfor
+  return '\V' . re
+endfunction
+
+" "foo/bar/hoge" -> { head: "foo/bar/", tail: "hoge" }
+function! s:SplitPath(path)
+  let dir = matchstr(a:path, '^.*[/\\]')
+  return  {
+        \   'head' : dir,
+        \   'tail' : a:path[strlen(dir):]
+        \ }
+endfunction
+
+function! s:EscapeFilename(fn)
+  return escape(a:fn, " \t\n*?[{`$%#'\"|!<")
+endfunction
+
+" "foo/.../bar/...hoge" -> "foo/.../bar/../../hoge"
+function! s:ExpandTailDotSequenceToParentDir(base)
+  return substitute(a:base, '^\(.*[/\\]\)\?\zs\.\(\.\+\)\ze[^/\\]*$',
+        \           '\=repeat(".." . s:PATH_SEPARATOR, len(submatch(2)))', '')
+endfunction
+
+"-----------------------------------------------------------------------------
+" FUNCTIONS FOR COMPLETION ITEM:
+
+function! s:FormatCompletionItem(expr, number, abbr, trim_len, time, base_pattern, evals_path_tail)
+  if a:evals_path_tail
+    let rate = s:EvaluateMatchingRate(s:SplitPath(matchstr(a:expr, '^.*[^/\\]')).tail,
+          \                           s:SplitPath(a:base_pattern).tail)
+  else
+    let rate = s:EvaluateMatchingRate(a:expr, a:base_pattern)
+  endif
+  return  {
+        \   'word'  : a:expr,
+        \   'abbr'  : s:TrimLast((a:number >= 0 ? printf('%2d: ', a:number) : '') . a:abbr, a:trim_len),
+        \   'menu'  : printf('%s[%s]', (len(a:time) ? a:time . ' ' : ''), s:MakeRateStar(rate, 5)),
+        \   'ranks' : [-rate, (a:number >= 0 ? a:number : a:expr)]
+        \ }
+endfunction
+
+function! s:EvaluateMatchingRate(expr, pattern)
+  if a:expr == a:pattern
+    return s:MATCHING_RATE_BASE
+  endif
+  let rate = 0
+  let rate_increment = (s:MATCHING_RATE_BASE * 9) / (len(a:pattern) * 10) " zero divide ok
+  let matched = 1
+  let i_pattern = 0
+  for i_expr in range(len(a:expr))
+    if a:expr[i_expr] == a:pattern[i_pattern]
+      let rate += rate_increment
+      let matched = 1
+      let i_pattern += 1
+      if i_pattern >= len(a:pattern)
+        break
+      endif
+    elseif matched
+      let rate_increment = rate_increment / 2
+      let matched = 0
+    endif
+  endfor
+  return rate
+endfunction
+
+function! s:MakeRateStar(rate, base)
+  let len = (a:base * a:rate) / s:MATCHING_RATE_BASE
+  return repeat('*', len) . repeat('.', a:base - len)
+endfunction
+
+"-----------------------------------------------------------------------------
+" MISC FUNCTIONS:
+
+function! s:IsAvailableMode(mode)
+  return exists('a:mode.mode_available') && a:mode.mode_available
+endfunction
+
+function! s:GetAvailableModes()
+  return filter(values(g:FuzzyFinderMode), 's:IsAvailableMode(v:val)')
+endfunction
+
+function! s:GetSortedAvailableModes()
+  let modes = filter(items(g:FuzzyFinderMode), 's:IsAvailableMode(v:val[1])')
+  let modes = map(modes, 'extend(v:val[1], { "ranks" : [v:val[1].switch_order, v:val[0]] })')
+  return sort(modes, 's:CompareRanks')
+endfunction
+
+function! s:GetSidPrefix()
+  return matchstr(expand('<sfile>'), '<SNR>\d\+_')
+endfunction
+
+function! s:OnCmdCR()
+  for m in s:GetAvailableModes()
+    call m.extend_options()
+    call m.on_command_pre(getcmdtype() . getcmdline())
+  endfor
+  " lets last entry become the newest in the history
+  if getcmdtype() =~ '[:/=@]'
+    call histadd(getcmdtype(), getcmdline())
+  endif
+
+  " this is not mapped again (:help recursive_mapping)
+  return "\<CR>"
+endfunction
+
+function! s:ExpandAbbrevMap(base, abbrev_map)
+  let result = [a:base]
+
+  " expand
+  for [pattern, sub_list] in items(a:abbrev_map)
+    let exprs = result
+    let result = []
+    for expr in exprs
+      let result += map(copy(sub_list), 'substitute(expr, pattern, v:val, "g")')
+    endfor
+  endfor
+
+  return s:Unique(result)
+endfunction
+
+" "**" is expanded to ["**", "."]. E.g.: "foo/**/bar" -> [ "foo/./bar", "foo/**/bar" ]
+function! s:ExpandEx(dir)
+  if a:dir !~ '\S'
+    return ['']
+  endif
+
+  " [ ["foo/"], ["**/", "./" ], ["bar/"] ]
+  let lists = []
+  for i in split(a:dir, '[/\\]\zs')
+    let m = matchlist(i, '^\*\{2,}\([/\\]*\)$')
+    call add(lists, (empty(m) ? [i] : [i, '.' . m[1]]))
+  endfor
+
+  " expand wlidcards
+  return split(join(map(s:CartesianProduct(lists), 'expand(join(v:val, ""))'), "\n"), "\n")
+endfunction
+
+function! s:EnumExpandedDirsEntries(dir, excluded)
+  let dirs = s:ExpandEx(a:dir)
+  let entries = s:Concat(map(copy(dirs), 'split(glob(v:val . ".*"), "\n") + ' .
+        \                                'split(glob(v:val . "*" ), "\n")'))
+  if len(dirs) <= 1
+    call map(entries, 'extend(s:SplitPath(v:val), { "suffix" : (isdirectory(v:val) ? s:PATH_SEPARATOR : ""), "head" : a:dir })')
+  else
+    call map(entries, 'extend(s:SplitPath(v:val), { "suffix" : (isdirectory(v:val) ? s:PATH_SEPARATOR : "") })')
+  endif
+  if len(a:excluded)
+    call filter(entries, '(v:val.head . v:val.tail . v:val.suffix) !~ a:excluded')
+  endif
+  return entries
+endfunction
+
+function! s:GetTagList(tagfile)
+  return map(readfile(a:tagfile), 'matchstr(v:val, ''^[^!\t][^\t]*'')')
+endfunction
+
+function! s:GetTaggedFileList(tagfile)
+  execute 'cd ' . fnamemodify(a:tagfile, ':h')
+  let result = map(readfile(a:tagfile), 'fnamemodify(matchstr(v:val, ''^[^!\t][^\t]*\t\zs[^\t]\+''), '':p:~'')')
+  cd -
+  return result
+endfunction
+
+function! s:HighlightPrompt(prompt, highlight)
+  syntax clear
+  execute printf('syntax match %s /^\V%s/', a:highlight, escape(a:prompt, '\'))
+endfunction
+
+function! s:HighlightError()
+  syntax clear
+  syntax match Error  /^.*$/
+endfunction
+
+function! s:CompareTimeDescending(i1, i2)
+      return a:i1.time == a:i2.time ? 0 : a:i1.time > a:i2.time ? -1 : +1
+endfunction
+
+function! s:CompareRanks(i1, i2)
+  if exists('a:i1.ranks') && exists('a:i2.ranks')
+    for i in range(min([len(a:i1.ranks), len(a:i2.ranks)]))
+      if     a:i1.ranks[i] > a:i2.ranks[i]
+        return +1
+      elseif a:i1.ranks[i] < a:i2.ranks[i]
+        return -1
+      endif
+    endfor
+  endif
+  return 0
+endfunction
+
+function! s:GetCurrentTagFiles()
+  return sort(filter(map(tagfiles(), 'fnamemodify(v:val, '':p'')'), 'filereadable(v:val)'))
+endfunction
+
+" }}}1
+"=============================================================================
+" OBJECT: {{{1
+"-----------------------------------------------------------------------------
+let g:FuzzyFinderMode = { 'Base' : {} }
+
+function! g:FuzzyFinderMode.Base.launch(initial_text, partial_matching, prev_bufnr, tag_files)
+  " initializes this object
+  call self.extend_options()
+  let self.partial_matching = a:partial_matching
+  let self.prev_bufnr = a:prev_bufnr
+  let self.tag_files = a:tag_files " to get local value of current buffer
+  let self.last_col = -1
+  call s:InfoFileManager.load()
+  if !s:IsAvailableMode(self)
+    echo 'This mode is not available: ' . self.to_str()
+    return
+  endif
+
+  call s:WindowManager.activate(self.make_complete_func('CompleteFunc'))
+  call s:OptionManager.set('completeopt', 'menuone')
+  call s:OptionManager.set('ignorecase', self.ignore_case)
+
+  " local autocommands
+  augroup FuzzyfinderLocal
+    autocmd!
+    execute 'autocmd CursorMovedI <buffer>        call ' . self.to_str('on_cursor_moved_i()')
+    execute 'autocmd InsertLeave  <buffer> nested call ' . self.to_str('on_insert_leave()'  )
+  augroup END
+
+  " local mapping
+  for [lhs, rhs] in [
+        \   [ self.key_open       , self.to_str('on_cr(0, 0)'            ) ],
+        \   [ self.key_open_split , self.to_str('on_cr(1, 0)'            ) ],
+        \   [ self.key_open_vsplit, self.to_str('on_cr(2, 0)'            ) ],
+        \   [ self.key_open_tab   , self.to_str('on_cr(3, 0)'            ) ],
+        \   [ '<BS>'              , self.to_str('on_bs()'                ) ],
+        \   [ '<C-h>'             , self.to_str('on_bs()'                ) ],
+        \   [ self.key_next_mode  , self.to_str('on_switch_mode(+1)'     ) ],
+        \   [ self.key_prev_mode  , self.to_str('on_switch_mode(-1)'     ) ],
+        \   [ self.key_ignore_case, self.to_str('on_switch_ignore_case()') ],
+        \ ]
+    " hacks to be able to use feedkeys().
+    execute printf('inoremap <buffer> <silent> %s <C-r>=%s ? "" : ""<CR>', lhs, rhs)
+  endfor
+
+  call self.on_mode_enter()
+
+  " Starts Insert mode and makes CursorMovedI event now. Command prompt is
+  " needed to forces a completion menu to update every typing.
+  call setline(1, self.prompt . a:initial_text)
+  call feedkeys("A", 'n') " startinsert! does not work in InsertLeave handler
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_cursor_moved_i()
+  let ln = getline('.')
+  let cl = col('.')
+  if !self.exists_prompt(ln)
+    " if command prompt is removed
+    "call setline('.', self.prompt . ln)
+    call setline('.', self.restore_prompt(ln))
+    call feedkeys(repeat("\<Right>", len(getline('.')) - len(ln)), 'n')
+  elseif cl <= len(self.prompt)
+    " if the cursor is moved before command prompt
+    call feedkeys(repeat("\<Right>", len(self.prompt) - cl + 1), 'n')
+  elseif cl > strlen(ln) && cl != self.last_col
+    " if the cursor is placed on the end of the line and has been actually moved.
+    let self.last_col = cl
+    call feedkeys("\<C-x>\<C-u>", 'n')
+  endif
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_insert_leave()
+  let text = getline('.')
+  call self.on_mode_leave()
+  call self.empty_cache_if_existed(0)
+  call s:OptionManager.restore_all()
+  call s:WindowManager.deactivate()
+
+  " switchs to next mode, or finishes fuzzyfinder.
+  if exists('s:reserved_switch_mode')
+    let m = self.next_mode(s:reserved_switch_mode < 0)
+    call m.launch(self.remove_prompt(text), self.partial_matching, self.prev_bufnr, self.tag_files)
+    unlet s:reserved_switch_mode
+  else
+    if exists('s:reserved_command')
+      call feedkeys(self.on_open(s:reserved_command[0], s:reserved_command[1]), 'n')
+      unlet s:reserved_command
+    endif
+  endif
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_buf_enter()
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_buf_write_post()
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_command_pre(cmd)
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_cr(index, check_dir)
+  if pumvisible()
+    call feedkeys(printf("\<C-y>\<C-r>=%s(%d, 1) ? '' : ''\<CR>", self.to_str('on_cr'), a:index), 'n')
+  elseif !a:check_dir || getline('.') !~ '[/\\]$'
+    let s:reserved_command = [self.remove_prompt(getline('.')), a:index]
+    call feedkeys("\<Esc>", 'n')
+  endif
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_bs()
+  let bs_count = 1
+  if self.smart_bs && col('.') > 2 && getline('.')[col('.') - 2] =~ '[/\\]'
+    let bs_count = len(matchstr(getline('.')[:col('.') - 3], '[^/\\]*$')) + 1
+  endif
+  call feedkeys((pumvisible() ? "\<C-e>" : "") . repeat("\<BS>", bs_count), 'n')
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_mode_enter()
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_mode_leave()
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_open(expr, mode)
+  return [
+        \   ':edit ',
+        \   ':split ',
+        \   ':vsplit ',
+        \   ':tabedit ',
+        \ ][a:mode] . s:EscapeFilename(a:expr) . "\<CR>"
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_switch_mode(next_prev)
+  let s:reserved_switch_mode = a:next_prev
+  call feedkeys("\<Esc>", 'n')
+endfunction
+
+function! g:FuzzyFinderMode.Base.on_switch_ignore_case()
+  let &ignorecase = !&ignorecase
+  echo "ignorecase = " . &ignorecase
+  let self.last_col = -1
+  call self.on_cursor_moved_i()
+endfunction
+
+" export string list
+function! g:FuzzyFinderMode.Base.serialize_info()
+  let header = self.to_key() . "\t"
+  return map(copy(self.info), 'header . string(v:val)')
+endfunction
+
+" import related items from string list
+function! g:FuzzyFinderMode.Base.deserialize_info(lines)
+  let header = self.to_key() . "\t"
+  let self.info = map(filter(copy(a:lines), 'v:val[: len(header) - 1] ==# header'),
+        \             'eval(v:val[len(header) :])')
+endfunction
+
+function! g:FuzzyFinderMode.Base.complete(findstart, base)
+  if a:findstart
+    return 0
+  elseif  !self.exists_prompt(a:base) || len(self.remove_prompt(a:base)) < self.min_length
+    return []
+  endif
+  call s:HighlightPrompt(self.prompt, self.prompt_highlight)
+  " FIXME: ExpandAbbrevMap duplicates index
+  let result = []
+  for expanded_base in s:ExpandAbbrevMap(self.remove_prompt(a:base), self.abbrev_map)
+    let result += self.on_complete(expanded_base)
+  endfor
+  call sort(result, 's:CompareRanks')
+  if empty(result)
+    call s:HighlightError()
+  else
+    call feedkeys("\<C-p>\<Down>", 'n')
+  endif
+  return result
+endfunction
+
+" This function is set to 'completefunc' which doesn't accept dictionary-functions.
+function! g:FuzzyFinderMode.Base.make_complete_func(name)
+  execute printf("function! s:%s(findstart, base)\n" .
+        \        "  return %s.complete(a:findstart, a:base)\n" .
+        \        "endfunction", a:name, self.to_str())
+  return s:GetSidPrefix() . a:name
+endfunction
+
+" fuzzy  : 'str' -> {'base':'str', 'wi':'*s*t*r*', 're':'\V\.\*s\.\*t\.\*r\.\*'}
+" partial: 'str' -> {'base':'str', 'wi':'*str*', 're':'\V\.\*str\.\*'}
+function! g:FuzzyFinderMode.Base.make_pattern(base)
+  if self.partial_matching
+    let wi = (a:base !~ '^[*?]'  ? '*' : '') . a:base .
+          \  (a:base =~ '[^*?]$' ? '*' : '')
+    let re = s:ConvertWildcardToRegexp(wi)
+    return { 'base': a:base, 'wi':wi, 're': re }
+  else
+    let wi = ''
+    for char in split(a:base, '\zs')
+      if wi !~ '[*?]$' && char !~ '[*?]'
+        let wi .= '*'. char
+      else
+        let wi .= char
+      endif
+    endfor
+
+    if wi !~ '[*?]$'
+      let wi .= '*'
+    endif
+
+    let re = s:ConvertWildcardToRegexp(wi)
+
+    if self.migemo_support && a:base !~ '[^\x01-\x7e]'
+      let re .= '\|\m.*' . substitute(migemo(a:base), '\\_s\*', '.*', 'g') . '.*'
+    endif
+
+    return { 'base': a:base, 'wi':wi, 're': re }
+  endif
+endfunction
+
+" glob with caching-feature, etc.
+function! g:FuzzyFinderMode.Base.glob_ex(dir, file, excluded, index, matching_limit)
+  let key = fnamemodify(a:dir, ':p')
+  call extend(self, { 'cache' : {} }, 'keep')
+  if !exists('self.cache[key]')
+    echo 'Caching file list...'
+    let self.cache[key] = s:EnumExpandedDirsEntries(key, a:excluded)
+    call s:ExtendIndexToEach(self.cache[key], 1)
+  endif
+  echo 'Filtering file list...'
+  "return map(s:FilterEx(self.cache[key], 'v:val.tail =~ ' . string(a:file), a:matching_limit),
+  return map(s:FilterMatching(self.cache[key], 'tail', a:file, a:index, a:matching_limit),
+        \ '{ "index" : v:val.index, "path" : (v:val.head == key ? a:dir : v:val.head) . v:val.tail . v:val.suffix }')
+endfunction
+
+function! g:FuzzyFinderMode.Base.glob_dir_ex(dir, file, excluded, index, matching_limit)
+  let key = fnamemodify(a:dir, ':p')
+  call extend(self, { 'cache' : {} }, 'keep')
+  if !exists('self.cache[key]')
+    echo 'Caching file list...'
+    let self.cache[key] = filter(s:EnumExpandedDirsEntries(key, a:excluded), 'len(v:val.suffix)')
+    call insert(self.cache[key], { 'head' : key, 'tail' : '..', 'suffix' : s:PATH_SEPARATOR })
+    call insert(self.cache[key], { 'head' : key, 'tail' : '.' , 'suffix' : '' })
+    call s:ExtendIndexToEach(self.cache[key], 1)
+  endif
+  echo 'Filtering file list...'
+  "return map(s:FilterEx(self.cache[key], 'v:val.tail =~ ' . string(a:file), a:matching_limit),
+  return map(s:FilterMatching(self.cache[key], 'tail', a:file, a:index, a:matching_limit),
+        \ '{ "index" : v:val.index, "path" : (v:val.head == key ? a:dir : v:val.head) . v:val.tail . v:val.suffix }')
+endfunction
+
+function! g:FuzzyFinderMode.Base.empty_cache_if_existed(force)
+  if exists('self.cache') && (a:force || !exists('self.lasting_cache') || !self.lasting_cache)
+    unlet self.cache
+    "let self.cache = (type(self.cache) == type({}) ? {} :
+    "      \           type(self.cache) == type([]) ? [] :
+    "      \           type(self.cache) == type('') ? '' : 0)
+  endif
+endfunction
+
+function! g:FuzzyFinderMode.Base.to_key()
+  return filter(keys(g:FuzzyFinderMode), 'g:FuzzyFinderMode[v:val] is self')[0]
+endfunction
+
+" returns 'g:FuzzyFinderMode.{key}{.argument}'
+function! g:FuzzyFinderMode.Base.to_str(...)
+  return 'g:FuzzyFinderMode.' . self.to_key() . (a:0 > 0 ? '.' . a:1 : '')
+endfunction
+
+" takes in g:FuzzyFinderOptions
+function! g:FuzzyFinderMode.Base.extend_options()
+  let n = filter(keys(g:FuzzyFinderMode), 'g:FuzzyFinderMode[v:val] is self')[0]
+  call extend(self, g:FuzzyFinderOptions.Base, 'force')
+  call extend(self, g:FuzzyFinderOptions[self.to_key()], 'force')
+endfunction
+
+function! g:FuzzyFinderMode.Base.next_mode(rev)
+  let modes = (a:rev ? s:GetSortedAvailableModes() : reverse(s:GetSortedAvailableModes()))
+  let m_last = modes[-1]
+  for m in modes
+    if m is self
+      break
+    endif
+    let m_last = m
+  endfor
+  return m_last
+  " vim crashed using map()
+endfunction
+
+function! g:FuzzyFinderMode.Base.exists_prompt(in)
+  return  strlen(a:in) >= strlen(self.prompt) && a:in[:strlen(self.prompt) -1] ==# self.prompt
+endfunction
+
+function! g:FuzzyFinderMode.Base.remove_prompt(in)
+  return a:in[(self.exists_prompt(a:in) ? strlen(self.prompt) : 0):]
+endfunction
+
+function! g:FuzzyFinderMode.Base.restore_prompt(in)
+  let i = 0
+  while i < len(self.prompt) && i < len(a:in) && self.prompt[i] ==# a:in[i]
+    let i += 1
+  endwhile
+  return self.prompt . a:in[i : ]
+endfunction
+
+"-----------------------------------------------------------------------------
+let g:FuzzyFinderMode.Buffer = copy(g:FuzzyFinderMode.Base)
+
+function! g:FuzzyFinderMode.Buffer.on_complete(base)
+  let patterns = self.make_pattern(a:base)
+  let result = s:FilterMatching(self.cache, 'path', patterns.re, s:SuffixNumber(patterns.base), 0)
+  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, v:val.time, a:base, 1)')
+endfunction
+
+function! g:FuzzyFinderMode.Buffer.on_open(expr, mode)
+  " attempts to convert the path to the number for handling unnamed buffer
+  return printf([
+        \   ':%sbuffer',
+        \   ':%ssbuffer',
+        \   ':vertical :%ssbuffer',
+        \   ':tab :%ssbuffer',
+        \ ][a:mode] . "\<CR>", filter(self.cache, 'v:val.path == a:expr')[0].buf_nr)
+endfunction
+
+function! g:FuzzyFinderMode.Buffer.on_mode_enter()
+  let self.cache = map(filter(range(1, bufnr('$')), 'buflisted(v:val) && v:val != self.prev_bufnr'),
+        \              'self.make_item(v:val)')
+  if self.mru_order
+    call s:ExtendIndexToEach(sort(self.cache, 's:CompareTimeDescending'), 1)
+  endif
+endfunction
+
+function! g:FuzzyFinderMode.Buffer.on_buf_enter()
+  call self.update_buf_times()
+endfunction
+
+function! g:FuzzyFinderMode.Buffer.on_buf_write_post()
+  call self.update_buf_times()
+endfunction
+
+function! g:FuzzyFinderMode.Buffer.update_buf_times()
+  if !exists('self.buf_times')
+    let self.buf_times = {}
+  endif
+  let self.buf_times[bufnr('%')] = localtime()
+endfunction
+
+function! g:FuzzyFinderMode.Buffer.make_item(nr)
+  return  {
+        \   'index'  : a:nr,
+        \   'buf_nr' : a:nr,
+        \   'path'   : empty(bufname(a:nr)) ? '[No Name]' : fnamemodify(bufname(a:nr), ':~:.'),
+        \   'time'   : (exists('self.buf_times[a:nr]') ? strftime(self.time_format, self.buf_times[a:nr]) : ''),
+        \ }
+endfunction
+
+"-----------------------------------------------------------------------------
+let g:FuzzyFinderMode.File = copy(g:FuzzyFinderMode.Base)
+
+function! g:FuzzyFinderMode.File.on_complete(base)
+  let base = s:ExpandTailDotSequenceToParentDir(a:base)
+  let patterns = map(s:SplitPath(base), 'self.make_pattern(v:val)')
+  let result = self.glob_ex(patterns.head.base, patterns.tail.re, self.excluded_path, s:SuffixNumber(patterns.tail.base), self.matching_limit)
+  let result = filter(result, 'bufnr("^" . v:val.path . "$") != self.prev_bufnr')
+  if len(result) >= self.matching_limit
+    call s:HighlightError()
+  endif
+  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, "", base, 1)')
+endfunction
+
+"-----------------------------------------------------------------------------
+let g:FuzzyFinderMode.Dir = copy(g:FuzzyFinderMode.Base)
+
+function! g:FuzzyFinderMode.Dir.on_complete(base)
+  let base = s:ExpandTailDotSequenceToParentDir(a:base)
+  let patterns = map(s:SplitPath(base), 'self.make_pattern(v:val)')
+  let result = self.glob_dir_ex(patterns.head.base, patterns.tail.re, self.excluded_path, s:SuffixNumber(patterns.tail.base), 0)
+  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, "", base, 1)')
+endfunction
+
+function! g:FuzzyFinderMode.Dir.on_open(expr, mode)
+  return ':cd ' . escape(a:expr, ' ') . [
+        \   "\<CR>",
+        \   "",
+        \   "",
+        \   "",
+        \ ][a:mode]
+endfunction
+
+"-----------------------------------------------------------------------------
+let g:FuzzyFinderMode.MruFile = copy(g:FuzzyFinderMode.Base)
+
+function! g:FuzzyFinderMode.MruFile.on_complete(base)
+  let patterns = self.make_pattern(a:base)
+  let result = s:FilterMatching(self.cache, 'path', patterns.re, s:SuffixNumber(patterns.base), 0)
+  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, v:val.time, a:base, 1)')
+endfunction
+
+function! g:FuzzyFinderMode.MruFile.on_mode_enter()
+  let self.cache = copy(self.info)
+  let self.cache = filter(self.cache, 'bufnr("^" . v:val.path . "$") != self.prev_bufnr')
+  let self.cache = filter(self.cache, 'filereadable(v:val.path)')
+  let self.cache = map(self.cache, '{ "path" : fnamemodify(v:val.path, ":~:."), "time" : strftime(self.time_format, v:val.time) }')
+  let self.cache = s:ExtendIndexToEach(self.cache, 1)
+endfunction
+
+function! g:FuzzyFinderMode.MruFile.on_buf_enter()
+  call self.update_info()
+endfunction
+
+function! g:FuzzyFinderMode.MruFile.on_buf_write_post()
+  call self.update_info()
+endfunction
+
+function! g:FuzzyFinderMode.MruFile.update_info()
+  "if !empty(&buftype) || !filereadable(expand('%'))
+  if !empty(&buftype)
+    return
+  endif
+  call s:InfoFileManager.load()
+  let self.info = s:UpdateMruList(self.info, { 'path' : expand('%:p'), 'time' : localtime() },
+        \                         'path', self.max_item, self.excluded_path)
+  call s:InfoFileManager.save()
+endfunction
+
+"-----------------------------------------------------------------------------
+let g:FuzzyFinderMode.MruCmd = copy(g:FuzzyFinderMode.Base)
+
+function! g:FuzzyFinderMode.MruCmd.on_complete(base)
+  let patterns = self.make_pattern(a:base)
+  let result = s:FilterMatching(self.cache, 'command', patterns.re, s:SuffixNumber(patterns.base), 0)
+  return map(result, 's:FormatCompletionItem(v:val.command, v:val.index, v:val.command, self.trim_length, v:val.time, a:base, 0)')
+endfunction
+
+function! g:FuzzyFinderMode.MruCmd.on_open(expr, mode)
+  redraw
+  " use feedkeys to remap <CR>
+  return a:expr . [
+        \   "\<C-r>=feedkeys(\"\\<CR>\", 'm')?'':''\<CR>",
+        \   "",
+        \   "",
+        \   "",
+        \ ][a:mode]
+endfunction
+
+function! g:FuzzyFinderMode.MruCmd.on_mode_enter()
+  let self.cache = s:ExtendIndexToEach(map(copy(self.info),
+        \ '{ "command" : v:val.command, "time" : strftime(self.time_format, v:val.time) }'), 1)
+endfunction
+
+function! g:FuzzyFinderMode.MruCmd.on_command_pre(cmd)
+  call self.update_info(a:cmd)
+endfunction
+
+function! g:FuzzyFinderMode.MruCmd.update_info(cmd)
+  call s:InfoFileManager.load()
+  let self.info = s:UpdateMruList(self.info, { 'command' : a:cmd, 'time' : localtime() },
+        \                         'command', self.max_item, self.excluded_command)
+  call s:InfoFileManager.save()
+endfunction
+
+"-----------------------------------------------------------------------------
+let g:FuzzyFinderMode.FavFile = copy(g:FuzzyFinderMode.Base)
+
+function! g:FuzzyFinderMode.FavFile.on_complete(base)
+  let patterns = self.make_pattern(a:base)
+  let result = s:FilterMatching(self.cache, 'path', patterns.re, s:SuffixNumber(patterns.base), 0)
+  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, v:val.time, a:base, 1)')
+endfunction
+
+function! g:FuzzyFinderMode.FavFile.on_mode_enter()
+  let self.cache = copy(self.info)
+  let self.cache = filter(self.cache, 'bufnr("^" . v:val.path . "$") != self.prev_bufnr')
+  let self.cache = map(self.cache, '{ "path" : fnamemodify(v:val.path, ":~:."), "time" : strftime(self.time_format, v:val.time) }')
+  let self.cache = s:ExtendIndexToEach(self.cache, 1)
+endfunction
+
+function! g:FuzzyFinderMode.FavFile.add(in_file, adds)
+  call s:InfoFileManager.load()
+
+  let file = fnamemodify((empty(a:in_file) ? expand('%') : a:in_file), ':p:~')
+
+  call filter(self.info, 'v:val.path != file')
+  if a:adds
+    call add(self.info, { 'path' : file, 'time' : localtime() })
+  endif
+
+  call s:InfoFileManager.save()
+endfunction
+
+"-----------------------------------------------------------------------------
+let g:FuzzyFinderMode.Tag = copy(g:FuzzyFinderMode.Base)
+
+function! g:FuzzyFinderMode.Tag.on_complete(base)
+  let patterns = self.make_pattern(a:base)
+  let result = self.find_tag(patterns.re, self.matching_limit)
+  if len(result) >= self.matching_limit
+    call s:HighlightError()
+  endif
+  return map(result, 's:FormatCompletionItem(v:val, -1, v:val, self.trim_length, "", a:base, 1)')
+endfunction
+
+function! g:FuzzyFinderMode.Tag.on_open(expr, mode)
+  return [
+        \   ':tjump ',
+        \   ':stjump ',
+        \   ':vertical :stjump ',
+        \   ':tab :stjump ',
+        \ ][a:mode] . a:expr . "\<CR>"
+endfunction
+
+function! g:FuzzyFinderMode.Tag.find_tag(pattern, matching_limit)
+  if !len(self.tag_files)
+    return []
+  endif
+
+  let key = join(self.tag_files, "\n")
+
+  " cache not created or tags file updated? 
+  call extend(self, { 'cache' : {} }, 'keep')
+  if !exists('self.cache[key]') || max(map(copy(self.tag_files), 'getftime(v:val) >= self.cache[key].time'))
+    echo 'Caching tag list...'
+    let self.cache[key] = {
+          \   'time' : localtime(),
+          \   'data' : s:Unique(s:Concat(map(copy(self.tag_files), 's:GetTagList(v:val)'))),
+          \ }
+  endif
+
+  echo 'Filtering tag list...'
+  return s:FilterEx(self.cache[key].data, 'v:val =~ ' . string(a:pattern), a:matching_limit)
+endfunction
+
+"-----------------------------------------------------------------------------
+let g:FuzzyFinderMode.TaggedFile = copy(g:FuzzyFinderMode.Base)
+
+function! g:FuzzyFinderMode.TaggedFile.on_complete(base)
+  let patterns = self.make_pattern(a:base)
+  echo 'Making tagged file list...'
+  let result = self.find_tagged_file(patterns.re, self.matching_limit)
+  if len(result) >= self.matching_limit
+    call s:HighlightError()
+  endif
+  return map(result, 's:FormatCompletionItem(v:val, -1, v:val, self.trim_length, "", a:base, 1)')
+endfunction
+
+function! g:FuzzyFinderMode.TaggedFile.find_tagged_file(pattern, matching_limit)
+  if !len(self.tag_files)
+    return []
+  endif
+
+  let key = join(self.tag_files, "\n")
+
+  " cache not created or tags file updated? 
+  call extend(self, { 'cache' : {} }, 'keep')
+  if !exists('self.cache[key]') || max(map(copy(self.tag_files), 'getftime(v:val) >= self.cache[key].time'))
+    echo 'Caching tagged-file list...'
+    let self.cache[key] = {
+          \   'time' : localtime(),
+          \   'data' : s:Unique(s:Concat(map(copy(self.tag_files), 's:GetTaggedFileList(v:val)'))),
+          \ }
+  endif
+
+  echo 'Filtering tagged-file list...'
+  return s:FilterEx(map(self.cache[key].data, 'fnamemodify(v:val, '':.'')'),
+        \               'v:val =~ ' . string(a:pattern),
+        \           a:matching_limit)
+
+endfunction
+
+"-----------------------------------------------------------------------------
+" sets or restores temporary options
+let s:OptionManager = { 'originals' : {} }
+
+function! s:OptionManager.set(name, value)
+  call extend(self.originals, { a:name : eval('&' . a:name) }, 'keep')
+  execute printf('let &%s = a:value', a:name)
+endfunction
+
+function! s:OptionManager.restore_all()
+  for [name, value] in items(self.originals)
+    execute printf('let &%s = value', name)
+  endfor
+  let self.originals = {}
+endfunction
+
+"-----------------------------------------------------------------------------
+" manages buffer/window for fuzzyfinder
+let s:WindowManager = { 'buf_nr' : -1 }
+
+function! s:WindowManager.activate(complete_func)
+  let self.prev_winnr = winnr()
+  let cwd = getcwd()
+
+  if !bufexists(self.buf_nr)
+    leftabove 1new
+    file `='[Fuzzyfinder]'`
+    let self.buf_nr = bufnr('%')
+  elseif bufwinnr(self.buf_nr) == -1
+    leftabove 1split
+    execute self.buf_nr . 'buffer'
+    delete _
+  elseif bufwinnr(self.buf_nr) != bufwinnr('%')
+    execute bufwinnr(self.buf_nr) . 'wincmd w'
+  endif
+
+  " countermeasure for auto-cd script
+  execute ':lcd ' . cwd
+
+  setlocal filetype=fuzzyfinder
+  setlocal bufhidden=delete
+  setlocal buftype=nofile
+  setlocal noswapfile
+  setlocal nobuflisted
+  setlocal modifiable
+  setlocal nocursorline   " for highlighting
+  setlocal nocursorcolumn " for highlighting
+  let &l:completefunc = a:complete_func
+
+  redraw " for 'lazyredraw'
+
+  " suspend autocomplpop.vim
+  if exists(':AutoComplPopLock')
+    :AutoComplPopLock
+  endif
+endfunction
+
+function! s:WindowManager.deactivate()
+  " resume autocomplpop.vim
+  if exists(':AutoComplPopUnlock')
+    :AutoComplPopUnlock
+  endif
+
+  close
+  execute self.prev_winnr . 'wincmd w'
+endfunction
+
+"-----------------------------------------------------------------------------
+let s:InfoFileManager = { 'originals' : {} }
+
+function! s:InfoFileManager.load()
+  for m in s:GetAvailableModes()
+    let m.info = []
+  endfor
+
+  try
+    let lines = readfile(expand(self.get_info_file()))
+  catch /.*/ 
+    return
+  endtry
+
+  " compatibility check
+  if !count(lines, self.get_info_version_line())
+      call self.warn_old_info()
+      let g:FuzzyFinderOptions.Base.info_file = ''
+      return
+  endif
+
+  for m in s:GetAvailableModes()
+    call m.deserialize_info(lines)
+  endfor
+endfunction
+
+function! s:InfoFileManager.save()
+  let lines = [ self.get_info_version_line() ]
+  for m in s:GetAvailableModes()
+    let lines += m.serialize_info()
+  endfor
+
+  try
+    call writefile(lines, expand(self.get_info_file()))
+  catch /.*/ 
+  endtry
+endfunction
+
+function! s:InfoFileManager.edit()
+
+  new
+  file `='[FuzzyfinderInfo]'`
+  let self.bufnr = bufnr('%')
+
+  setlocal filetype=vim
+  setlocal bufhidden=delete
+  setlocal buftype=acwrite
+  setlocal noswapfile
+
+  augroup FuzzyfinderInfo
+    autocmd!
+    autocmd BufWriteCmd <buffer> call s:InfoFileManager.on_buf_write_cmd()
+  augroup END
+
+  execute '0read ' . expand(self.get_info_file())
+  setlocal nomodified
+
+endfunction
+
+function! s:InfoFileManager.on_buf_write_cmd()
+  for m in s:GetAvailableModes()
+    call m.deserialize_info(getline(1, '$'))
+  endfor
+  call self.save()
+  setlocal nomodified
+  execute printf('%dbdelete! ', self.bufnr)
+  echo "Information file updated"
+endfunction
+
+function! s:InfoFileManager.get_info_version_line()
+  return "VERSION\t206"
+endfunction
+
+function! s:InfoFileManager.get_info_file()
+  return g:FuzzyFinderOptions.Base.info_file
+endfunction
+
+function! s:InfoFileManager.warn_old_info()
+  echohl WarningMsg
+  echo printf("==================================================\n" .
+      \       "  Your Fuzzyfinder information file is no longer  \n" .
+      \       "  supported. Please remove                        \n" .
+      \       "  %-48s\n" .
+      \       "==================================================\n" ,
+      \       '"' . expand(self.get_info_file()) . '".')
+  echohl None
+endfunction
+
+" }}}1
+"=============================================================================
+" GLOBAL OPTIONS: {{{1
+" stores user-defined g:FuzzyFinderOptions ------------------------------ {{{2
+let user_options = (exists('g:FuzzyFinderOptions') ? g:FuzzyFinderOptions : {})
+" }}}2
+
+" Initializes g:FuzzyFinderOptions.
+let g:FuzzyFinderOptions = { 'Base':{}, 'Buffer':{}, 'File':{}, 'Dir':{}, 'MruFile':{}, 'MruCmd':{}, 'FavFile':{}, 'Tag':{}, 'TaggedFile':{}}
+"-----------------------------------------------------------------------------
+" [All Mode] This is mapped to select completion item or finish input and
+" open a buffer/file in previous window.
+let g:FuzzyFinderOptions.Base.key_open = '<CR>'
+" [All Mode] This is mapped to select completion item or finish input and
+" open a buffer/file in split new window
+let g:FuzzyFinderOptions.Base.key_open_split = '<C-j>'
+" [All Mode] This is mapped to select completion item or finish input and
+" open a buffer/file in vertical-split new window.
+let g:FuzzyFinderOptions.Base.key_open_vsplit = '<C-k>'
+" [All Mode] This is mapped to select completion item or finish input and
+" open a buffer/file in a new tab page.
+let g:FuzzyFinderOptions.Base.key_open_tab = '<C-]>'
+" [All Mode] This is mapped to switch to the next mode.
+let g:FuzzyFinderOptions.Base.key_next_mode = '<C-l>'
+" [All Mode] This is mapped to switch to the previous mode.
+let g:FuzzyFinderOptions.Base.key_prev_mode = '<C-o>'
+" [All Mode] This is mapped to temporarily switch whether or not to ignore
+" case.
+let g:FuzzyFinderOptions.Base.key_ignore_case = '<C-t>'
+" [All Mode] This is the file name to write information of the MRU, etc. If
+" "" was set, Fuzzyfinder does not write to the file.
+let g:FuzzyFinderOptions.Base.info_file = '~/.vimfuzzyfinder'
+" [All Mode] Fuzzyfinder does not start a completion if a length of entered
+" text is less than this.
+let g:FuzzyFinderOptions.Base.min_length = 0
+" [All Mode] This is a dictionary. Each value must be a list. All matchs of a
+" key in entered text is expanded with the value.
+let g:FuzzyFinderOptions.Base.abbrev_map = {}
+" [All Mode] Fuzzyfinder ignores case in search patterns if non-zero is set.
+let g:FuzzyFinderOptions.Base.ignore_case = 1
+" [All Mode] This is a string to format time string. See :help strftime() for
+" details.
+let g:FuzzyFinderOptions.Base.time_format = '(%x %H:%M:%S)'
+" [All Mode] If a length of completion item is more than this, it is trimmed
+" when shown in completion menu.
+let g:FuzzyFinderOptions.Base.trim_length = 80
+" [All Mode] Fuzzyfinder does not remove caches of completion lists at the end
+" of explorer to reuse at the next time if non-zero was set.
+let g:FuzzyFinderOptions.Base.lasting_cache = 1
+" [All Mode] Fuzzyfinder uses Migemo if non-zero is set.
+let g:FuzzyFinderOptions.Base.migemo_support = 0
+"-----------------------------------------------------------------------------
+" [Buffer Mode] This disables all functions of this mode if zero was set.
+let g:FuzzyFinderOptions.Buffer.mode_available = 1
+" [Buffer Mode] The prompt string.
+let g:FuzzyFinderOptions.Buffer.prompt = '>Buffer>'
+" [Buffer Mode] The highlight group name for a prompt string.
+let g:FuzzyFinderOptions.Buffer.prompt_highlight = 'Question'
+" [Buffer Mode] Pressing <BS> after a path separator deletes one directory
+" name if non-zero is set.
+let g:FuzzyFinderOptions.Buffer.smart_bs = 1
+" [Buffer Mode] The completion items is sorted in the order of recently used
+" if non-zero is set.
+let g:FuzzyFinderOptions.Buffer.mru_order = 1
+" [Buffer Mode] This is used to sort modes for switching to the next/previous
+" mode.
+let g:FuzzyFinderOptions.Buffer.switch_order = 10
+"-----------------------------------------------------------------------------
+" [File Mode] This disables all functions of this mode if zero was set.
+let g:FuzzyFinderOptions.File.mode_available = 1
+" [File Mode] The prompt string.
+let g:FuzzyFinderOptions.File.prompt = '>File>'
+" [File Mode] The highlight group name for a prompt string.
+let g:FuzzyFinderOptions.File.prompt_highlight = 'Question'
+" [File Mode] Pressing <BS> after a path separator deletes one directory name
+" if non-zero is set.
+let g:FuzzyFinderOptions.File.smart_bs = 1
+" [File Mode] This is used to sort modes for switching to the next/previous
+" mode.
+let g:FuzzyFinderOptions.File.switch_order = 20
+" [File Mode] The items matching this are excluded from the completion list.
+let g:FuzzyFinderOptions.File.excluded_path = '\v\~$|\.o$|\.exe$|\.bak$|\.swp$|((^|[/\\])\.[/\\]$)'
+" [File Mode] If a number of matched items was over this, the completion
+" process is aborted.
+let g:FuzzyFinderOptions.File.matching_limit = 200
+"-----------------------------------------------------------------------------
+" [Directory Mode] This disables all functions of this mode if zero was set.
+let g:FuzzyFinderOptions.Dir.mode_available = 1
+" [Directory Mode] The prompt string.
+let g:FuzzyFinderOptions.Dir.prompt = '>Dir>'
+" [Directory Mode] The highlight group name for a prompt string.
+let g:FuzzyFinderOptions.Dir.prompt_highlight = 'Question'
+" [Directory Mode] Pressing <BS> after a path separator deletes one directory
+" name if non-zero is set.
+let g:FuzzyFinderOptions.Dir.smart_bs = 1
+" [Directory Mode] This is used to sort modes for switching to the
+" next/previous mode.
+let g:FuzzyFinderOptions.Dir.switch_order = 30
+" [Directory Mode] The items matching this are excluded from the completion
+" list.
+let g:FuzzyFinderOptions.Dir.excluded_path = '\v(^|[/\\])\.{1,2}[/\\]$'
+"-----------------------------------------------------------------------------
+" [Mru-File Mode] This disables all functions of this mode if zero was set.
+let g:FuzzyFinderOptions.MruFile.mode_available = 1
+" [Mru-File Mode] The prompt string.
+let g:FuzzyFinderOptions.MruFile.prompt = '>MruFile>'
+" [Mru-File Mode] The highlight group name for a prompt string.
+let g:FuzzyFinderOptions.MruFile.prompt_highlight = 'Question'
+" [Mru-File Mode] Pressing <BS> after a path separator deletes one directory
+" name if non-zero is set.
+let g:FuzzyFinderOptions.MruFile.smart_bs = 1
+" [Mru-File Mode] This is used to sort modes for switching to the
+" next/previous mode.
+let g:FuzzyFinderOptions.MruFile.switch_order = 40
+" [Mru-File Mode] The items matching this are excluded from the completion
+" list.
+let g:FuzzyFinderOptions.MruFile.excluded_path = '\v\~$|\.bak$|\.swp$'
+" [Mru-File Mode] This is an upper limit of MRU items to be stored.
+let g:FuzzyFinderOptions.MruFile.max_item = 99
+"-----------------------------------------------------------------------------
+" [Mru-Cmd Mode] This disables all functions of this mode if zero was set.
+let g:FuzzyFinderOptions.MruCmd.mode_available = 1
+" [Mru-Cmd Mode] The prompt string.
+let g:FuzzyFinderOptions.MruCmd.prompt = '>MruCmd>'
+" [Mru-Cmd Mode] The highlight group name for a prompt string.
+let g:FuzzyFinderOptions.MruCmd.prompt_highlight = 'Question'
+" [Mru-Cmd Mode] Pressing <BS> after a path separator deletes one directory
+" name if non-zero is set.
+let g:FuzzyFinderOptions.MruCmd.smart_bs = 0
+" [Mru-Cmd Mode] This is used to sort modes for switching to the next/previous
+" mode.
+let g:FuzzyFinderOptions.MruCmd.switch_order = 50
+" [Mru-Cmd Mode] The items matching this are excluded from the completion
+" list.
+let g:FuzzyFinderOptions.MruCmd.excluded_command = '^$'
+" [Mru-Cmd Mode] This is an upper limit of MRU items to be stored.
+let g:FuzzyFinderOptions.MruCmd.max_item = 99
+"-----------------------------------------------------------------------------
+" [Favorite-File Mode] This disables all functions of this mode if zero was
+" set.
+let g:FuzzyFinderOptions.FavFile.mode_available = 1
+" [Favorite-File Mode] The prompt string.
+let g:FuzzyFinderOptions.FavFile.prompt = '>FavFile>'
+" [Favorite-File Mode] The highlight group name for a prompt string.
+let g:FuzzyFinderOptions.FavFile.prompt_highlight = 'Question'
+" [Favorite-File Mode] Pressing <BS> after a path separator deletes one
+" directory name if non-zero is set.
+let g:FuzzyFinderOptions.FavFile.smart_bs = 1
+" [Favorite-File Mode] This is used to sort modes for switching to the
+" next/previous mode.
+let g:FuzzyFinderOptions.FavFile.switch_order = 60
+"-----------------------------------------------------------------------------
+" [Tag Mode] This disables all functions of this mode if zero was set.
+let g:FuzzyFinderOptions.Tag.mode_available = 1
+" [Tag Mode] The prompt string.
+let g:FuzzyFinderOptions.Tag.prompt = '>Tag>'
+" [Tag Mode] The highlight group name for a prompt string.
+let g:FuzzyFinderOptions.Tag.prompt_highlight = 'Question'
+" [Tag Mode] Pressing <BS> after a path separator deletes one directory name
+" if non-zero is set.
+let g:FuzzyFinderOptions.Tag.smart_bs = 0
+" [Tag Mode] This is used to sort modes for switching to the next/previous
+" mode.
+let g:FuzzyFinderOptions.Tag.switch_order = 70
+" [Tag Mode] The items matching this are excluded from the completion list.
+let g:FuzzyFinderOptions.Tag.excluded_path = '\v\~$|\.bak$|\.swp$'
+" [Tag Mode] If a number of matched items was over this, the completion
+" process is aborted.
+let g:FuzzyFinderOptions.Tag.matching_limit = 200
+"-----------------------------------------------------------------------------
+" [Tagged-File Mode] This disables all functions of this mode if zero was set.
+let g:FuzzyFinderOptions.TaggedFile.mode_available = 1
+" [Tagged-File Mode] The prompt string.
+let g:FuzzyFinderOptions.TaggedFile.prompt = '>TaggedFile>'
+" [Tagged-File Mode] The highlight group name for a prompt string.
+let g:FuzzyFinderOptions.TaggedFile.prompt_highlight = 'Question'
+" [Tagged-File Mode] Pressing <BS> after a path separator deletes one
+" directory name if non-zero is set.
+let g:FuzzyFinderOptions.TaggedFile.smart_bs = 0
+" [Tagged-File Mode] This is used to sort modes for switching to the
+" next/previous mode.
+let g:FuzzyFinderOptions.TaggedFile.switch_order = 80
+" [Tagged-File Mode] If a number of matched items was over this, the
+" completion process is aborted.
+let g:FuzzyFinderOptions.TaggedFile.matching_limit = 200
+
+" overwrites default values of g:FuzzyFinderOptions with user-defined values - {{{2
+call map(user_options, 'extend(g:FuzzyFinderOptions[v:key], v:val, ''force'')')
+call map(copy(g:FuzzyFinderMode), 'v:val.extend_options()')
+" }}}2
+
+" }}}1
+"=============================================================================
+" COMMANDS/AUTOCOMMANDS/MAPPINGS/ETC.: {{{1
+
+let s:PATH_SEPARATOR = (has('win32') || has('win64') ? '\' : '/')
+let s:MATCHING_RATE_BASE = 10000000
+let s:ABBR_TRIM_MARK = '...'
+
+augroup FuzzyfinderGlobal
+  autocmd!
+  autocmd BufEnter     * for m in s:GetAvailableModes() | call m.extend_options() | call m.on_buf_enter() | endfor
+  autocmd BufWritePost * for m in s:GetAvailableModes() | call m.extend_options() | call m.on_buf_write_post() | endfor
+augroup END
+
+" cnoremap has a problem, which doesn't expand cabbrev.
+cmap <silent> <expr> <CR> <SID>OnCmdCR()
+
+command! -bang -narg=? -complete=buffer FuzzyFinderBuffer      call g:FuzzyFinderMode.Buffer.launch    (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles())
+command! -bang -narg=? -complete=file   FuzzyFinderFile        call g:FuzzyFinderMode.File.launch      (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles())
+command! -bang -narg=? -complete=dir    FuzzyFinderDir         call g:FuzzyFinderMode.Dir.launch       (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles())
+command! -bang -narg=? -complete=file   FuzzyFinderMruFile     call g:FuzzyFinderMode.MruFile.launch   (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles())
+command! -bang -narg=? -complete=file   FuzzyFinderMruCmd      call g:FuzzyFinderMode.MruCmd.launch    (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles())
+command! -bang -narg=? -complete=file   FuzzyFinderFavFile     call g:FuzzyFinderMode.FavFile.launch   (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles())
+command! -bang -narg=? -complete=tag    FuzzyFinderTag         call g:FuzzyFinderMode.Tag.launch       (<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles())
+command! -bang -narg=? -complete=file   FuzzyFinderTaggedFile  call g:FuzzyFinderMode.TaggedFile.launch(<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles())
+command! -bang -narg=? -complete=file   FuzzyFinderEditInfo    call s:InfoFileManager.edit()
+command! -bang -narg=? -complete=file   FuzzyFinderAddFavFile  call g:FuzzyFinderMode.FavFile.add(<q-args>, 1)
+command! -bang -narg=0                  FuzzyFinderRemoveCache for m in s:GetAvailableModes() | call m.empty_cache_if_existed(1) | endfor
+
+" }}}1
+"=============================================================================
+" vim: set fdm=marker:
diff --git a/plugin/fuzzyfinder_textmate.vim b/plugin/fuzzyfinder_textmate.vim
new file mode 100644 (file)
index 0000000..9c42278
--- /dev/null
@@ -0,0 +1,101 @@
+if has("ruby")
+
+" ====================================================================================
+" COPIED FROM FUZZYFINDER.VIM {{{
+" since they can't be called from outside fuzzyfinder.vim
+" ====================================================================================
+function! s:GetCurrentTagFiles()
+  return sort(filter(map(tagfiles(), 'fnamemodify(v:val, '':p'')'), 'filereadable(v:val)'))
+endfunction
+
+function! s:HighlightPrompt(prompt, highlight)
+  syntax clear
+  execute printf('syntax match %s /^\V%s/', a:highlight, escape(a:prompt, '\'))
+endfunction
+
+function! s:HighlightError()
+  syntax clear
+  syntax match Error  /^.*$/
+endfunction
+" ------------------------------------------------------------------------------------
+" }}}
+" ====================================================================================
+
+command! -bang -narg=? -complete=file   FuzzyFinderTextMate   call FuzzyFinderTextMateLauncher(<q-args>, len(<q-bang>), bufnr('%'), s:GetCurrentTagFiles())
+
+function! InstantiateTextMateMode() "{{{
+ruby << RUBY
+  $LOAD_PATH << "#{ENV['HOME']}/.vim/ruby"
+
+  begin
+    require 'rubygems'
+    gem 'fuzzy_file_finder'
+  rescue LoadError
+  end
+
+  require 'fuzzy_file_finder'
+RUBY
+
+  ruby def finder; @finder ||= FuzzyFileFinder.new; end
+
+  let g:FuzzyFinderMode.TextMate = copy(g:FuzzyFinderMode.Base)
+
+  " ================================================================================
+  " This function is copied almost whole-sale from fuzzyfinder.vim. Ideally, I could
+  " used the on_complete callback to more cleanly add the new behavior, but the
+  " TextMate-style completion broke a few of fuzzyfinder.vim's assumptions, and the
+  " only way to patch that up was to override Base.complete...which required me to
+  " copy-and-paste much of the original implementation.
+  "
+  " Ugly. But effective.
+  " ================================================================================
+  function! g:FuzzyFinderMode.TextMate.complete(findstart, base)
+    if a:findstart
+      return 0
+    elseif  !self.exists_prompt(a:base) || len(self.remove_prompt(a:base)) < self.min_length
+      return []
+    endif
+    call s:HighlightPrompt(self.prompt, self.prompt_highlight)
+
+    let result = []
+    ruby << RUBY
+      matches = finder.find(VIM.evaluate('self.remove_prompt(a:base)'), VIM.evaluate('self.matching_limit').to_i + 1)
+      matches.sort_by { |a| [-a[:score], a[:path]] }.each_with_index do |match, index|
+        word = match[:path]
+        abbr = "%2d: %s" % [index+1, match[:abbr]]
+        menu = "[%5d]" % [match[:score] * 10000]
+        VIM.evaluate("add(result, { 'word' : #{word.inspect}, 'abbr' : #{abbr.inspect}, 'menu' : #{menu.inspect} })")
+      end
+RUBY
+    if empty(result) || len(result) >= self.matching_limit
+      call s:HighlightError()
+    endif
+
+    if !empty(result)
+      call feedkeys("\<C-p>\<Down>", 'n')
+    endif
+
+    return result
+  endfunction
+
+  function! FuzzyFinderTextMateLauncher(initial_text, partial_matching, prev_bufnr, tag_files)
+    call g:FuzzyFinderMode.TextMate.launch(a:initial_text, a:partial_matching, a:prev_bufnr, a:tag_files)
+  endfunction
+
+  let g:FuzzyFinderOptions.TextMate = copy(g:FuzzyFinderOptions.File)
+endfunction "}}}
+
+if !exists('loaded_fuzzyfinder') "{{{
+  function! FuzzyFinderTextMateLauncher(initial_text, partial_matching, prev_bufnr, tag_files)
+    call InstantiateTextMateMode()
+    function! FuzzyFinderTextMateLauncher(initial_text, partial_matching, prev_bufnr, tag_files)
+      call g:FuzzyFinderMode.TextMate.launch(a:initial_text, a:partial_matching, a:prev_bufnr, a:tag_files)
+    endfunction
+    call g:FuzzyFinderMode.TextMate.launch(a:initial_text, a:partial_matching, a:prev_bufnr, a:tag_files)
+  endfunction
+  finish
+end "}}}
+
+call InstantiateTextMateMode()
+
+endif
diff --git a/plugin/genutils.vim b/plugin/genutils.vim
new file mode 100644 (file)
index 0000000..35edd74
--- /dev/null
@@ -0,0 +1,996 @@
+" genutils: Useful buffer, file and window related functions.
+" Author: Hari Krishna Dara (hari_vim at yahoo dot com)
+" Last Change: 08-Jun-2007 @ 17:36
+" Requires: Vim-7.0
+" Version: 2.4.0
+" Licence: This program is free software; you can redistribute it and/or
+"          modify it under the terms of the GNU General Public License.
+"          See http://www.gnu.org/copyleft/gpl.txt 
+" Acknowledgements:
+"     - The genutils#GetNextWinnrInStack() function is based on the WinStackMv()
+"       function posted by Charles E. Campbell, Jr. on vim mailing list on Jul
+"       14, 2004.
+"     - The genutils#CommonPath() function is based on the thread,
+"       "computing relative path" on Jul 29, 2002.
+"     - The genutils#ShowLinesWithSyntax() function is based on a posting by
+"       Gary Holloway (gary at castandcrew dot com) on Jan, 16 2002.
+"     - Robert Webb for the original "quick sort" algorithm from eval.txt.
+"     - Peit Delport's (pjd at 303 dot za dot net) for his original BISort()
+"       algorithm on which the genutils#BinInsertSort() and
+"       genutils#BinInsertSort2() functions are based on.
+" Download From:
+"     http://www.vim.org/script.php?script_id=197
+" See Also: autoload/genutils.vim
+"
+" Description:
+"   - Read the "Documentation With Function Prototypes" section below.
+"   - Misc. window/buffer related functions, genutils#NumberOfWindows(),
+"     genutils#FindBufferForName(), genutils#MoveCursorToWindow(),
+"     genutils#MoveCurLineToWinLine(), genutils#SetupScratchBuffer(),
+"     genutils#MapAppendCascaded()
+"   - Save/Restore all the window height/width settings to be restored later.
+"   - Save/Restore position in the buffer to be restored later. Works like the
+"     built-in marks feature, but has more to it.
+"   - genutils#AddNotifyWindowClose() to get notifications *after* a window
+"     with the specified buffer has been closed or the buffer is unloaded. The
+"     built-in autocommands can only notify you *before* the window is closed.
+"     You can use this with the Save/Restore window settings feature to
+"     restore the dimensions of existing windows, after your window is closed
+"     (just like how Vim does while closing help windows). See selectbuf.vim
+"     or perforce.vim for examples.
+"     There is also a test function called RunNotifyWindowCloseTest() that
+"     demos the usage (you need to uncomment RunNotifyWindowCloseTest and
+"     NotifyWindowCloseF functions).
+"   - genutils#ShowLinesWithSyntax() function to echo lines with syntax coloring.
+"   - genutils#ShiftWordInSpace(), genutils#CenterWordInSpace() and
+"     genutils#AlignWordWithWordInPreviousLine() utility functions to move
+"     words in the space without changing the width of the field. A
+"     genutils#GetSpacer() function to return a spacer of specified width.
+"   - Binary search function genutils#BinSearchList() for sorted lists, to
+"     find the index after which a given item can be inserted to keep the list
+"     in sorted order. You can also use these functions to just search for
+"     boundaries.
+"     There are also a couple of functions genutils#BinSearchForInsert() and
+"     genutils#BinSearchForInsert2() to find the location for a newline to be
+"     inserted in an already sorted buffer or arbitrary data.
+"     There are also a few comparison functions that can be used with sort() or
+"     the above functions.
+"   - ExecMap function has now been separated as a plugin called execmap.vim.
+"   - New genutils#CommonPath() function to extract the common part of two
+"     paths, and genutils#RelPathFromFile() and genutils#RelPathFromDir() to
+"     find relative paths (useful HTML href's). A side effect is the
+"     genutils#CommonString() function to find the common string of two
+"     strings.
+"   - genutils#UnEscape() and genutils#DeEscape() functions to reverse and
+"     genutils#Escape() to compliment what built-in escape() does. There is
+"     also an genutils#EscapeCommand() function to escape external command
+"     strings.
+"   - Utility functions genutils#CurLineHasSign() and genutils#ClearAllSigns()
+"     to fill in the gaps left by Vim.
+"   - genutils#GetVimCmdOutput() function to capture the output of Vim built-in
+"     commands, in a safe manner.
+"   - genutils#OptClearBuffer() function to clear the contents and undo
+"     history of the current buffer in an optimal manner. Ideal to be used
+"     when plugins need to refresh their windows and don't care about
+"     preserving the current contents (which is the most usual case).
+"   - genutils#GetPreviewWinnr() function.
+"   - Functions to have persistent data, genutils#PutPersistentVar() and
+"     genutils#GetPersistentVar(). You don't need to worry about saving in
+"     files and reading them back. To disable, set g:genutilsNoPersist in your
+"     vimrc.
+"   - A function to emulate the default Vim behavior for |timestamp| changes.
+"     It also provides hooks to get call backs before and after handling the
+"     default FileChangedShell autocommand (effectively splitting it into a
+"     Pre and a Post event). Suggested usage is to use
+"     genutils#AddToFCShellPre() and either install a default event handling
+"     mechanism for all files by calling genutils#DefFCShellInstall() or
+"     create your own autocommand on a matching pattern to call
+"     genutils#DefFileChangedShell() function. Most useful for the source
+"     control plugins to conditionally reload a file, while being able to
+"     default to the Vim's standard behavior of asking the user. See
+"     perforce.vim for usage examples.
+"   - Utility function genutils#ExtractFuncListing() that is useful to to
+"     create snippets (see breakpts.vim, ntservices.vim and ntprocesses.vim
+"     for interesting ideas on how to use this function).
+"
+"   Function Prototypes:
+"       The types in prototypes of the functions mimic Java.
+"       This is just a full list for a quick reference, see
+"         "Documentation With Function Prototypes" for more information on the
+"         functions.
+"
+"   void    genutils#DebugShowArgs(...)
+"   String  genutils#ExtractFuncListing(String funcName, String hLines, String tLines)
+"   int     genutils#NumberOfWindows()
+"   int     genutils#FindBufferForName(String fileName)
+"   String  genutils#GetBufNameForAu(String bufName)
+"   void    genutils#MoveCursorToWindow(int winno)
+"   void    genutils#MoveCurLineToWinLine(int winLine)
+"   void    genutils#CloseWindow(int winnr, boolean force)
+"   void    genutils#MarkActiveWindow()
+"   void    genutils#RestoreActiveWindow()
+"   void    genutils#IsOnlyVerticalWindow()
+"   void    genutils#IsOnlyHorizontalWindow()
+"   int     genutils#GetNextWinnrInStack(char dir)
+"   int     genutils#GetLastWinnrInStack(char dir)
+"   void    genutils#MoveCursorToNextInWinStack(char dir)
+"   void    genutils#MoveCursorToLastInWinStack(char dir)
+"   void    genutils#OpenWinNoEa(String openWinCmd)
+"   void    genutils#CloseWinNoEa(int winnr, boolean force)
+"   void    genutils#SetupScratchBuffer()
+"   void    genutils#CleanDiffOptions()
+"   boolean genutils#ArrayVarExists(String varName, int index)
+"   void    genutils#MapAppendCascaded(String lhs, String rhs, String mapMode)
+"   void    genutils#SaveWindowSettings()
+"   void    genutils#RestoreWindowSettings()
+"   void    genutils#ResetWindowSettings()
+"   void    genutils#SaveWindowSettings2(String id, boolean overwrite)
+"   void    genutils#RestoreWindowSettings2(String id)
+"   void    genutils#ResetWindowSettings2(String id)
+"   void    genutils#SaveVisualSelection(String id)
+"   void    genutils#RestoreVisualSelection(String id)
+"   void    genutils#SaveSoftPosition(String id)
+"   void    genutils#RestoreSoftPosition(String id)
+"   void    genutils#ResetSoftPosition(String id)
+"   void    genutils#SaveHardPosition(String id)
+"   void    genutils#RestoreHardPosition(String id)
+"   void    genutils#ResetHardPosition(String id)
+"   int     genutils#GetLinePosition(String id)
+"   int     genutils#GetColPosition(String id)
+"   boolean genutils#IsPositionSet(String id)
+"   String  genutils#CleanupFileName(String fileName)
+"   String  genutils#CleanupFileName2(String fileName, String win32ProtectedChars)
+"   boolean genutils#OnMS()
+"   boolean genutils#PathIsAbsolute(String path)
+"   boolean genutils#PathIsFileNameOnly(String path)
+"   void    genutils#AddNotifyWindowClose(String windowTitle, String functionName)
+"   void    genutils#RemoveNotifyWindowClose(String windowTitle)
+"   void    genutils#CheckWindowClose()
+"   void    genutils#ShowLinesWithSyntax() range
+"   void    genutils#ShiftWordInSpace(int direction)
+"   void    genutils#CenterWordInSpace()
+"   int     genutils#BinSearchList(List list, int start, int end, Object item,
+"                              [Funcref|String] cmp, int direction)
+"   int     genutils#BinSearchForInsert(int start, int end, String line,
+"                              String cmp, int direction)
+"   int     genutils#BinSearchForInsert2(int start, int end, line, String cmp,
+"                               int direction, String accessor, String context)
+"   String  genutils#CommonPath(String path1, String path2)
+"   String  genutils#CommonString(String str1, String str2)
+"   String  genutils#RelPathFromFile(String srcFile, String tgtFile)
+"   String  genutils#RelPathFromDir(String srcDir, String tgtFile)
+"   String  genutils#Roman2Decimal(String str)
+"   String  genutils#Escape(String str, String chars)
+"   String  genutils#UnEscape(String str, String chars)
+"   String  genutils#DeEscape(String str)
+"   String  genutils#CrUnProtectedCharsPattern(String chars)
+"   String  genutils#EscapeCommand(String cmd, List/String args, List/String pipe)
+"   int     genutils#GetShellEnvType()
+"   String  genutils#ExpandStr(String str)
+"   String  genutils#QuoteStr(String str)
+"   boolean genutils#CurLineHasSign()
+"   void    genutils#ClearAllSigns()
+"   String  genutils#UserFileComplete(String ArgLead, String CmdLine,
+"                  String CursorPos, String smartSlash, String searchPath)
+"   String  genutils#UserFileExpand(String fileArgs)
+"   String  genutils#GetVimCmdOutput(String cmd)
+"   void    genutils#OptClearBuffer()
+"   int     genutils#GetPreviewWinnr()
+"   void    genutils#PutPersistentVar(String pluginName, String persistentVar,
+"                  String value)
+"   void    genutils#GetPersistentVar(String pluginName, String persistentVar,
+"                  String default)
+"   void    genutils#AddToFCShellPre(String funcName)
+"   void    genutils#RemoveFromFCShellPre(String funcName)
+"   void    genutils#DefFCShellInstall()
+"   void    genutils#DefFCShellUninstall()
+"   boolean genutils#DefFileChangedShell()
+"   void    genutils#SilentSubstitute(String pat, String cmd)
+"   void    genutils#SilentDelete(String pat)
+"   void    genutils#SilentDelete(String range, String pat)
+"   String  genutils#GetSpacer(int width)
+"   String  genutils#PromptForElement(List array,
+"                 [String defaultValue | int defaultIndex], String msg,
+"                 String skip, boolean useDialog, int nCols)
+"   int     genutils#GetSelectedIndex()
+"
+" Documentation With Function Prototypes:
+" -----------------------
+" Useful function to debug passing arguments to functions. See exactly what
+"   you would receive on the other side.
+" Ex: :exec 'call genutils#DebugShowArgs('. genutils#CreateArgString("a 'b' c", ' ') . ')' 
+"
+" void    genutils#DebugShowArgs(...)
+" -----------------------
+" This function returns the body of the specified function ( the name should be
+"   complete, including any scriptid prefix in case of a script local
+"   function), without the function header and tail. You can also pass in the
+"   number of additional lines to be removed from the head and or tail of the
+"   function.
+"
+" String  genutils#ExtractFuncListing(String funcName, String hLines, String tLines)
+" -----------------------
+" -----------------------
+" Return the number of windows open currently.
+"
+" int     genutils#NumberOfWindows()
+" -----------------------
+" Returns the buffer number of the given fileName if it is already loaded.
+" The fileName argument is treated literally, unlike the bufnr() which treats
+"   the argument as a filename-pattern. The function first escape all the
+"   |filename-pattern| characters before passing it to bufnr(). It should work
+"   in most of the cases, except when backslashes are used in non-windows
+"   platforms, when the result could be unpredictable.
+"
+" Note: The function removes protections for "#%" characters because, these
+"   are special characters on Vim commandline, and so are usually escaped
+"   themselves, but bufnr() wouldn't like them.
+"
+" int     genutils#FindBufferForName(String fileName)
+" -----------------------
+" Returns the transformed buffer name that is suitable to be used in
+"   autocommands.
+"
+" String  genutils#GetBufNameForAu(String bufName)
+" -----------------------
+" Given the window number, moves the cursor to that window.
+"
+" void    genutils#MoveCursorToWindow(int winno)
+" -----------------------
+" Moves the current line such that it is going to be the nth line in the window
+"   without changing the column position.
+"
+" void    genutils#MoveCurLineToWinLine(int winLine)
+" -----------------------
+" Closes the given window and returns to the original window. It the simplest,
+" this is equivalent to:
+"
+"   let curWin = winnr()
+"   exec winnr 'wincmd w'
+"   close
+"   exec curWin 'wincmd w'
+"
+" But the function keeps track of the change in window numbers and restores
+" the current window correctly. It also restores the previous window (the
+" window that the cursor would jump to when executing "wincmd p" command).
+" This is something that all plugins should do while moving around in the
+" windows, behind the scenes.
+"
+" Pass 1 to force closing the window (:close!).
+"
+" void    genutils#CloseWindow(int winnr, boolean force)
+" -----------------------
+" Remembers the number of the current window as well as the previous-window
+" (the one the cursor would jump to when executing "wincmd p" command). To
+" determine the window number of the previous-window, the function temporarily
+" jumps to the previous-window, so if your script intends to avoid generating
+" unnecessary window events, consider disabling window events before calling
+" this function (see :h 'eventignore').
+"
+" void    genutils#MarkActiveWindow()
+" -----------------------
+" Restore the cursor to the window that was previously marked as "active", as
+" well as its previous-window (the one the cursor would jump to when executing
+" "wincmd p" command). To restore the window number of the previous-window,
+" the function temporarily jumps to the previous-window, so if your script
+" intends to avoid generating unnecessary window events, consider disabling
+" window events before calling this function (see :h 'eventignore').
+"
+" void    genutils#RestoreActiveWindow()
+" -----------------------
+" Returns 1 if the current window is the only window vertically.
+"
+" void    genutils#IsOnlyVerticalWindow()
+" -----------------------
+" Returns 1 if the current window is the only window horizontally.
+"
+" void    genutils#IsOnlyHorizontalWindow()
+" -----------------------
+" Returns the window number of the next window while remaining in the same
+"   horizontal or vertical window stack (or 0 when there are no more). Pass
+"   hjkl characters to indicate direction.
+"   Usage:
+"     let wn = genutils#GetNextWinnrInStack('h') left  window number in stack.
+"     let wn = genutils#GetNextWinnrInStack('l') right window number in stack.
+"     let wn = genutils#GetNextWinnrInStack('j') upper window number in stack.
+"     let wn = genutils#GetNextWinnrInStack('k') lower window number in stack.
+"
+" int     genutils#GetNextWinnrInStack(char dir)
+" -----------------------
+" Returns the window number of the last window while remaining in the same
+"   horizontal or vertical window stack (or 0 when there are no more, or it is
+"   already the last window). Pass hjkl characters to indicate direction.
+"   Usage:
+"     let wn = genutils#GetLastWinnrInStack('h') leftmost  window number in stack.
+"     let wn = genutils#GetLastWinnrInStack('l') rightmost window number in stack.
+"     let wn = genutils#GetLastWinnrInStack('j') top       window number in stack.
+"     let wn = genutils#GetLastWinnrInStack('k') bottom    window number in stack.
+"
+" int     genutils#GetLastWinnrInStack(char dir)
+" -----------------------
+" Move cursor to the next window in stack. See genutils#GetNextWinnrInStack()
+"   for more information.
+"
+" void    genutils#MoveCursorToNextInWinStack(char dir)
+" -----------------------
+" Move cursor to the last window in stack. See genutils#GetLastWinnrInStack()
+"   for more information.
+"
+" void    genutils#MoveCursorToLastInWinStack(char dir)
+" -----------------------
+" This function, which stands for "execute the given command that creates a
+"   window, while disabling the 'equalalways' setting", is a means for plugins
+"   to create new windows without disturbing the existing window dimensions as
+"   much as possible. This function would not be required if 'equalalways' is
+"   not set by the user. Even if set, the below code, though intuitive,
+"   wouldn't work:
+"       let _equalalways = &equalalways
+"       set noequalalways
+"       " open window now.
+"       let &equalalways = _equalalways
+"
+" The problem is that while restoring the value of equalalways, if the user
+"   originally had it set, Vim would immediately try to equalize all the
+"   window dimensions, which is exactly what we tried to avoid by setting
+"   'noequalalways'. The function works around the problem by temporarily
+"   setting 'winfixheight' in all the existing windows and restoring them
+"   after done.
+"   Usage:
+"     call genutils#OpenWinNoEa('sb ' pluginBuf)
+"
+" Note: The function doesn't catch any exceptions that are generated by the
+"   operations, so it is advisable to catch them by the caller itself.
+"
+" void    genutils#OpenWinNoEa(String openWinCmd)
+" -----------------------
+" This is for the same purpose as described for genutils#OpenWinNoEa()
+"   function, except that it is used to close a given window. This is just a
+"   convenience function.
+"
+" void    genutils#CloseWinNoEa(int winnr, boolean force)
+" -----------------------
+" Turn on some buffer settings that make it suitable to be a scratch buffer.
+"
+" void    genutils#SetupScratchBuffer()
+" -----------------------
+" Turns off those options that are set by diff to the current window.
+"   Also removes the 'hor' option from scrollopt (which is a global option).
+" Better alternative would be to close the window and reopen the buffer in a
+"   new window. 
+"
+" void    genutils#CleanDiffOptions()
+" -----------------------
+" This function is an alternative to exists() function, for those odd array
+"   index names for which the built-in function fails. The var should be
+"   accessible to this functions, so it shouldn't be a local or script local
+"   variable.
+"     if genutils#ArrayVarExists("array", id)
+"       let val = array{id}
+"     endif
+"
+" boolean genutils#ArrayVarExists(String varName, int index)
+" -----------------------
+" If lhs is already mapped, this function makes sure rhs is appended to it
+"   instead of overwriting it. If you are rhs has any script local functions,
+"   make sure you use the <SNR>\d\+_ prefix instead of the <SID> prefix (or the
+"   <SID> will be replaced by the SNR number of genutils script, instead of
+"   yours).
+" mapMode is used to prefix to "oremap" and used as the map command. E.g., if
+"   mapMode is 'n', then the function call results in the execution of noremap
+"   command.
+"
+" void    genutils#MapAppendCascaded(String lhs, String rhs, String mapMode)
+" -----------------------
+" -----------------------
+" Saves the heights and widths of the currently open windows for restoring
+"   later.
+"
+" void    genutils#SaveWindowSettings()
+" -----------------------
+" Restores the heights of the windows from the information that is saved by
+"  genutils#SaveWindowSettings(). Works only when the number of windows
+"  haven't changed since the genutils#SaveWindowSettings is called.
+"
+" void    genutils#RestoreWindowSettings()
+" -----------------------
+" Reset the previously saved window settings using genutils#SaveWindowSettings.
+"
+" void    genutils#ResetWindowSettings()
+" -----------------------
+" Same as genutils#SaveWindowSettings, but uses the passed in id to create a
+"   private copy for the calling script. Pass in a unique id to avoid
+"   conflicting with other callers. If overwrite is zero and if the settings
+"   are already stored for the passed in id, it will overwrite previously
+"   saved settings.
+"
+" void    genutils#SaveWindowSettings2(String id, boolean overwrite)
+" -----------------------
+" Same as genutils#RestoreWindowSettings, but uses the passed in id to get the
+"   settings. The settings must have been previously saved using this
+"   id. Call genutils#ResetWindowSettings2() to explicitly reset the saved
+"   settings.
+"
+" void    genutils#RestoreWindowSettings2(String id)
+" -----------------------
+" Reset the previously saved window settings using genutils#SaveWindowSettings2.
+"   Releases the variables.
+"
+" void    genutils#ResetWindowSettings2(String id)
+" -----------------------
+" -----------------------
+" Save the current/last visual selection such that it can be later restored
+"   using genutils#RestoreVisualSelection(). Pass a unique id such that it will
+"   not interfere with the other callers to this function. Saved selections
+"   are not associated with the window so you can later restore the selection
+"   in any window, provided there are enough lines/columns.
+"
+" void    genutils#SaveVisualSelection(String id)
+" -----------------------
+" Restore the visual selection that was previuosly saved using
+"   genutils#SaveVisualSelection().
+"
+" void    genutils#RestoreVisualSelection(String id)
+" -----------------------
+" -----------------------
+" This method tries to save the hard position along with the line context This
+"   is like the vim builtin marker. Pass in a unique id to avoid
+"   conflicting with other callers.
+"
+" void    genutils#SaveSoftPosition(String id)
+" -----------------------
+" Restore the cursor position using the information saved by the previous call
+"   to genutils#SaveSoftPosition. This first calls
+"   genutils#RestoreHardPosition() and then searches for the original line
+"   first in the forward direction and then in the backward and positions the
+"   cursor on the line if found. If the original line is not found it still
+"   behaves like a call to genutils#RestoreHardPosition. This is similar to
+"   the functionality of the built-in marker, as Vim is capable of maintaining
+"   the marker even when the line is moved up or down. However, if there are
+"   identical lines in the buffer and the original line has moved, this
+"   function might get confused.
+"
+" void    genutils#RestoreSoftPosition(String id)
+" -----------------------
+" Reset the previously cursor position using genutils#SaveSoftPosition.
+"   Releases the variables.
+"
+" void    genutils#ResetSoftPosition(String id)
+" -----------------------
+" Useful when you want to go to the exact (line, col), but marking will not
+"   work, or if you simply don't want to disturb the marks. Pass in a unique
+"   id.
+"
+" void    genutils#SaveHardPosition(String id)
+" -----------------------
+" Restore the cursor position using the information saved by the previous call
+"   to genutils#SaveHardPosition. 
+"
+" void    genutils#RestoreHardPosition(String id)
+" -----------------------
+" Reset the previously cursor position using genutils#SaveHardPosition.
+"   Releases the variables.
+"
+" void    genutils#ResetHardPosition(String id)
+" -----------------------
+" Return the line number of the previously saved position for the id.
+"   This is like calling line() builtin function for a mark.
+"
+" int     genutils#GetLinePosition(String id)
+" -----------------------
+" Return the column number of the previously saved position for the id.
+"   This is like calling col() builtin function for a mark.
+"
+" int     genutils#GetColPosition(String id)
+" -----------------------
+" A convenience function to check if a position has been saved (and not reset)
+"   using the id given.
+"
+" boolean genutils#IsPositionSet(String id)
+" -----------------------
+" -----------------------
+" Cleanup file name such that two *cleaned up* file names are easy to be
+"   compared. This probably works only on windows and unix platforms. Also
+"   recognizes UNC paths. Always returns paths with forward slashes only,
+"   irrespective of what your 'shellslash' setting is. The return path will
+"   always be a valid path for use in Vim, provided the original path itself
+"   was valid for the platform (a valid cygwin path after the cleanup will
+"   still be valid in a cygwin vim). The CleanupFileName2() variant is meant
+"   for win32, to avoid translating some backslash protections to be treated
+"   as regular path separators. Pass the characters that are protected, and
+"   the backslashes infront of them are preserved.
+"
+" String  genutils#CleanupFileName(String fileName)
+" String  genutils#CleanupFileName2(String fileName, String win32ProtectedChars)
+" -----------------------
+" Returns true if the current OS is any of the Microsoft OSes. Most useful to
+"   know if the path separator is "\".
+"
+" boolean genutils#OnMS()
+" -----------------------
+" Returns true if the given path could be an absolute path. Probably works
+"   only on Unix and Windows platforms.
+"
+" boolean genutils#PathIsAbsolute(String path)
+" -----------------------
+" Returns true if the given path doesn't have any directory components.
+"   Probably works only on Unix and Windows platforms.
+"
+" boolean genutils#PathIsFileNameOnly(String path)
+" -----------------------
+" -----------------------
+" Add a notification to know when a buffer with the given name (referred to as
+"   windowTitle) is no longer visible in any window. This by functionality is
+"   like a BufWinLeavePost event. The function functionName is called back
+"   with the title (buffer name) as an argument. The notification gets removed
+"   after excuting it, so for future notifications, you need to reregister
+"   your function. You can only have one notification for any buffer. The
+"   function should be accessible from the script's local context.
+"
+" void    genutils#AddNotifyWindowClose(String windowTitle, String functionName)
+" -----------------------
+" Remove the notification previously added using genutils#AddNotifyWindowClose
+"   function.
+"
+" void    genutils#RemoveNotifyWindowClose(String windowTitle)
+" -----------------------
+" Normally the plugin checks for closed windows for every WinEnter event, but
+"   you can force a check at anytime by calling this function.
+"
+" void    genutils#CheckWindowClose()
+" -----------------------
+" -----------------------
+" Displays the given line(s) from the current file in the command area (i.e.,
+"   echo), using that line's syntax highlighting (i.e., WYSIWYG).  If no line
+"   number is given, display the current line.
+" Originally,
+"   From: Gary Holloway "gary at castandcrew dot com"
+"   Date: Wed, 16 Jan 2002 14:31:56 -0800
+"
+" void    genutils#ShowLinesWithSyntax() range
+" -----------------------
+" This function shifts the current word in the space without changing the
+"   column position of the next word. Doesn't work for tabs.
+"
+" void    genutils#ShiftWordInSpace(int direction)
+" -----------------------
+" This function centers the current word in the space without changing the
+"   column position of the next word. Doesn't work for tabs.
+" 
+" void    genutils#CenterWordInSpace()
+" -----------------------
+" -----------------------
+" Find common path component of two filenames.
+"   Based on the thread, "computing relative path".
+"   Date: Mon, 29 Jul 2002 21:30:56 +0200 (CEST)
+" The last two arguments are optional and default to 0 (false), but you can
+"   pass a value of 1 (true) to indicate that the path represents a directory.
+" Ex:
+"   genutils#CommonPath('/a/b/c/d.e', '/a/b/f/g/h.i') => '/a/b/'
+"   genutils#CommonPath('/a/b/c/d.e', '/a/b/') => '/a/b'
+"   genutils#CommonPath('/a/b/c/d.e', '/a/b/', 0, 1) => '/a/b/'
+"
+" String  genutils#CommonPath(String path1, String path2 [, boolean path1IsDir, boolean path2IsDir])
+" -----------------------
+" Find common string component of two strings.
+"   Based on the tread, "computing relative path".
+"   Date: Mon, 29 Jul 2002 21:30:56 +0200 (CEST)
+" Ex:
+"   genutils#CommonString('abcde', 'abfghi') => 'ab'
+"
+" String  genutils#CommonString(String str1, String str2)
+" -----------------------
+" Find the relative path of tgtFile from the directory of srcFile.
+"   Based on the tread, "computing relative path".
+"   Date: Mon, 29 Jul 2002 21:30:56 +0200 (CEST)
+" Ex:
+"   genutils#RelPathFromFile('/a/b/c/d.html', '/a/b/e/f.html') => '../f/g.html'
+"
+" String  genutils#RelPathFromFile(String srcFile, String tgtFile)
+" -----------------------
+" Find the relative path of tgtFile from the srcDir.
+"   Based on the tread, "computing relative path".
+"   Date: Mon, 29 Jul 2002 21:30:56 +0200 (CEST)
+" Ex:
+"   genutils#RelPathFromDir('/a/b/c/d', '/a/b/e/f/g.html') => '../../e/f/g.html'
+"
+" String  genutils#RelPathFromDir(String srcDir, String tgtFile)
+" -----------------------
+" -----------------------
+" Convert Roman numerals to decimal. Doesn't detect format errors.
+" Originally,
+"   From: "Preben Peppe Guldberg" <[email protected]>
+"   Date: Fri, 10 May 2002 14:28:19 +0200
+"
+" String  genutils#Roman2Decimal(String str)
+" -----------------------
+" -----------------------
+" Works like the built-in escape(), except that it escapes the specified
+"   characters only if they are not already escaped, so something like
+"   genutils#Escape('a\bc\\bd', 'b') would give 'a\bc\\\bd'. The chars value
+"   directly goes into the [] collection, so it can be anything that is
+"   accepted in [].
+"
+" String  genutils#Escape(String str, String chars)
+" -----------------------
+" Works like the reverse of the builtin escape() function, but un-escapes the
+"   specified characters only if they are already escaped (essentially the
+"   opposite of genutils#Escape()). The chars value directly goes into the []
+"   collection, so it can be anything that is acceptable to [].
+"
+" String  genutils#UnEscape(String str, String chars)
+" -----------------------
+" Works like the reverse of the built-in escape() function. De-escapes all the
+"   escaped characters. Essentially removes one level of escaping from the
+"   string, so something like: 'a\b\\\\c\\d' would become 'ab\\c\d'.
+"
+" String  genutils#DeEscape(String str)
+" ----------------------- 
+" This function creates a pattern that avoids the given protected characters'
+"   from getting treated as separators, when used with split(). The argument
+"   goes directly into the [] atom, so make sure you pass in a valid string.
+"   When optional argument capture is true, the characters are placed in a
+"   capturing group.
+" Ex:
+"   let paths = split(&path, genutils#CrUnProtectedCharsPattern(','))
+"
+" String  genutils#CrUnProtectedCharsPattern(String chars, [boolean capture = false])
+" ----------------------- 
+" genutils#Escape the passed in shell command with quotes and backslashes such
+"   a way that the arguments reach the command literally (avoids shell
+"   interpretations). See the function header for the kind of escapings that
+"   are done. The first argument is the actual command name, the second
+"   argument is the arguments to the command and third argument is any pipe
+"   command that should be appended to the command. The reason the function
+"   requires them to be passed separately is that the escaping is minimized
+"   for the first and third arguments. It is preferable to pass args as a Vim7
+"   List, but it can be passed as a single string with spaces separating the
+"   arguments (spaces in side each argument then needs to be protected)
+"   Usage:
+"     let fullCmd = genutils#EscapeCommand('ls', ['-u', expand('%:h')], ['|', 'grep', 'xxx'])
+"   Note:
+"     If the escaped command is used on Vim command-line (such as with ":w !",
+"     ":r !" and ":!"), you need to further protect '%', '#' and '!' chars,
+"     even if they are in quotes, to avoid getting expanded by Vim before
+"     invoking external cmd. However this is not required for using it with
+"     system() function. The easiest way to escape them is by using the
+"     genutils#Escape() function as in "Escape(fullCmd, '%#!')".
+" String  genutils#EscapeCommand(String cmd, List/String args, List/String pipe)
+" -----------------------
+" Returns the global ST_* constants (g:ST_WIN_CMD, g:ST_WIN_SH, g:ST_UNIX)
+" based on the values of shell related settings and the OS on which Vim is
+" running.
+"
+" int     genutils#GetShellEnvType()
+" -----------------------
+"
+" Expands the string for the special characters. The return value should
+"   essentially be what you would see if it was a string constant with
+"   double-quotes.
+" Ex:
+"   genutils#ExpandStr('a\tA') => 'a     A'
+" String  genutils#ExpandStr(String str)
+" -----------------------
+" Quotes the passed in string such that it can be used as a string expression
+" in :execute. It sorrounds the passed in string with single-quotes while
+" escaping any existing single-quotes in the string.
+"
+" String  genutils#QuoteStr(String str)
+" -----------------------
+" -----------------------
+" Returns true if the current line has a sign placed.
+"
+" boolean genutils#CurLineHasSign()
+" -----------------------
+" Clears all signs in the current buffer.
+"
+" void    genutils#ClearAllSigns()
+" -----------------------
+" -----------------------
+" This function is suitable to be used by custom command completion functions
+"   for expanding filenames conditionally. The function could based on the
+"   context, decide whether to do a file completion or a different custom
+"   completion. See breakpts.vim and perforce.vim for examples.
+" If you pass non-zero value to smartSlash, the function decides to use
+"   backslash or forwardslash as the path separator based on the user settings
+"   and the ArgLead, but if you always want to use only forwardslash as the
+"   path separator, then pass 0. If you pass in a comma separated list of
+"   directories as searchPath, then the file expansion is limited to the files
+"   under these directories. This means, you can implement your own commands
+"   that don't expect the user to type in the full path name to the file
+"   (e.g., if the user types in the command while in the explorer window, you
+"   could assume that the path is relative to the directory being viewed). Most
+"   useful with a single directory, but also useful in combination with vim
+"   'runtimepath' in loading scripts etc. (see Runtime command in
+"   breakpts.vim).
+"
+" String  genutils#UserFileComplete(String ArgLead, String CmdLine, String
+"                          CursorPos, String smartSlash, String searchPath)
+" -----------------------
+" This is a convenience function to expand filename meta-sequences in the
+"   given arguments just as Vim would have if given to a user-defined command
+"   as arguments with completion mode set to "file". Useful
+"   if you set the completion mode of your command to anything
+"   other than the "file", and later conditionally expand arguments (for
+"   characters such as % and # and other sequences such as #10 and <cword>)
+"   after deciding which arguments represent filenames/patterns.
+"
+" String  genutils#UserFileExpand(String fileArgs)
+" -----------------------
+" This returns the output of the vim command as a string, without corrupting
+"   any registers. Returns empty string on errors. Check for v:errmsg after
+"   calling this function for any error messages.
+"
+" String  genutils#GetVimCmdOutput(String cmd)
+" -----------------------
+" Clear the contents of the current buffer in an optimum manner. For plugins
+" that keep redrawing the contents of its buffer, executing "1,$d" or its
+" equivalents result in overloading Vim's undo mechanism. Using this function
+" avoids that problem.
+"
+" void    genutils#OptClearBuffer()
+" -----------------------
+" Returns the window number of the preview window if open or -1 if not.
+" int     genutils#GetPreviewWinnr()
+" -----------------------
+" -----------------------
+" These functions provide a persistent storage mechanism.
+"
+"     Example: Put the following in a file called t.vim in your plugin
+"     directory and watch the magic. You can set new value using SetVar() and
+"     see that it returns the same value across session when GetVar() is
+"     called.
+"     >>>>t.vim<<<<
+"       au VimEnter * call LoadSettings()
+"       au VimLeavePre * call SaveSettings()
+"       
+"       function! LoadSettings()
+"         let s:tVar = genutils#GetPersistentVar("T", "tVar", "defVal")
+"       endfunction
+"       
+"       function! SaveSettings()
+"         call genutils#PutPersistentVar("T", "tVar", s:tVar)
+"       endfunction
+"       
+"       function! SetVar(val)
+"         let s:tVar = a:val
+"       endfunction
+"       
+"       function! GetVar()
+"         return s:tVar
+"       endfunction
+"     <<<<t.vim>>>>
+"
+" The pluginName and persistentVar have to be unique and are case insensitive.
+"   Ideally called from your VimLeavePre autocommand handler of your plugin.
+"   This simply creates a global variable which will be persisted by Vim
+"   through viminfo. The variable can be read back in the next session by the
+"   plugin using genutils#GetPersistentVar() function, ideally from your
+"   VimEnter autocommand handler. The pluginName is to provide a name space
+"   for different plugins, and avoid conflicts in using the same persistentVar
+"   name.
+" This feature uses the '!' option of viminfo, to avoid storing all the
+"   temporary and other plugin specific global variables getting saved.
+"
+" void    genutils#PutPersistentVar(String pluginName, String persistentVar,
+"                          String value)
+" -----------------------
+" Ideally called from VimEnter, this simply reads the value of the global
+"   variable for the persistentVar that is saved in the viminfo in a previous
+"   session using genutils#PutPersistentVar() and returns it (and default if
+"   the variable is not found). It removes the variable from global space
+"   before returning the value, so can be called only once. It also means that
+"   genutils#PutPersistentVar should be called again in the next VimLeavePre
+"   if the variable continues to be persisted.
+"
+" void    genutils#GetPersistentVar(String pluginName, String persistentVar,
+"                          String default)
+" -----------------------
+" -----------------------
+" These functions channel the FileChangedShell autocommand and extend it to
+" create an additional fictitious FileChangedShellPre and FileChangedShellPost
+" events.
+"
+" Add the given noarg function to the list of functions that need to be
+"   notified before processing the FileChangedShell event. The function when
+"   called can expand "<abuf>" or "<afile>" to get the details of the buffer
+"   for which this autocommand got executed. It should return 0 to mean
+"   noautoread and 1 to mean autoread the current buffer. It can also return
+"   -1 to make its return value ignored and use default autoread mechanism
+"   (which could still be overridden by the return value of other functions).
+"   The return value of all the functions is ORed to determine the effective
+"   autoread value.
+"
+" void    genutils#AddToFCShellPre(String funcName)
+" -----------------------
+" Remove the given function previously added by calling
+"   genutils#AddToFCShellPre.
+"
+" void    genutils#RemoveFromFCShellPre(String funcName)
+" -----------------------
+" Same as genutils#AddToFCShellPre except that the function is called after
+"   the event is processed, so this is like a fictitious FileChangedShellPost
+"   event.
+" 
+" void    genutils#DefFCShellInstall()
+" -----------------------
+" Uninstall the default autocommand handler that was previously installed
+"   using genutils#DefFCShellInstall. Calling this function may not actually
+"   result in removing the handler, in case there are other callers still
+"   dependent on it (which is kept track of by the number of times
+"   genutils#DefFCShellInstall has been called).
+"
+" void    genutils#DefFCShellUninstall()
+" -----------------------
+" This function emulates the Vim's default behavior when a |timestamp| change
+"   is detected. Register your functions by calling genutils#AddToFCShellPre
+"   and have this function called during the FileChangedShell event (or just
+"   install the default handler by calling genutils#DefFCShellInstall).  From
+"   your callbacks, return 1 to mean autoread, 0 to mean noautoread and -1 to
+"   mean system default (or ignore).  The return value of this method is 1 if
+"   the file was reloaded and 0 otherwise. The return value of all the
+"   functions is ORed to determine the effective autoread value. See my
+"   perforce plugin for usage example.
+"
+" boolean genutils#DefFileChangedShell()
+" -----------------------
+" Execute a substitute command silently and without corrupting the search
+"   register. It also preserves the cursor position.
+" Ex:
+"   To insert a tab infrontof all lines:
+"         call genutils#SilentSubstitute('^', '%s//\t/e')
+"   To remote all carriage returns at the line ending:
+"         call genutils#SilentSubstitute("\<CR>$", '%s///e')
+"
+" void    genutils#SilentSubstitute(String pat, String cmd)
+" -----------------------
+" Delete all lines matching the given pattern silently and without corrupting
+"   the search register. The range argument if passed should be a valid prefix
+"   for the :global command. It also preserves the cursor position.
+" Ex:
+"   To delete all lines that are empty:
+"         call genutils#SilentDelete('^\s*$')
+"   To delete all lines that are empty only in the range 10 to 100:
+"         call genutils#SilentDelete('10,100', '^\s*$')
+"
+" void    genutils#SilentDelete(String pat)
+" void    genutils#SilentDelete(String range, String pat)
+" -----------------------
+" Can return a spacer from 0 to 80 characters width.
+"
+" String  genutils#GetSpacer(int width)
+" -----------------------
+" Function to prompt user for an element out of the passed in array. The
+"   user will be prompted with a list of choices to make. The elements will be
+"   formatted in to the given number of columns. Each element will be given a
+"   number that the user can enter to indicate the selection. This is very
+"   much like the inputlist() method, but better for a large number of options
+"   formatted into multiple columns (instead of one per row). However, if the
+"   formatted options run for multiple pages, no special handling is done.
+" Params:
+"   default - The default value for the selection. Default can be the
+"               element-index or the element itself. If number (type() returns
+"               0), it is treated as an index.
+"   msg - The message that should appear in the prompt (passed to input()).
+"   skip - The element that needs to be skipped from selection (pass a
+"            non-existent element to disable this, such as an empty value '').
+"   useDialog - if true, uses dialogs for prompts, instead of the command-line(
+"                 inputdialog() instead of input()). But personally, I don't
+"                 like this because the power user then can't use the
+"                 expression register.
+"   nCols - Number of columns to use for formatting the options. Using "1"
+"          will make the output look very like that of inputlist()
+" Returns:
+"   the selected element or empty string, "" if nothing is selected. Call
+"   genutils#GetSelectedIndex() for the index entered by the user.
+"
+" Ex:
+"   echo genutils#PromptForElement(map(range(0,25),
+"        \ "nr2char(char2nr('a')+v:val)") , 'd', 'Enter: ', 'x', 1, 5)
+" String  genutils#PromptForElement(List array,
+"         [String defaultValue | int defaultIndex], String msg,
+"         String skip, boolean useDialog, int nCols)
+"
+" Returns the index of the element selected by the user in the previous
+"   genutils#PromptForElement call. Returns -1 when the user didn't select
+"   any element (aborted the selection). This function is useful if there are
+"   empty or duplicate elements in the selection.
+" int     genutils#GetSelectedIndex()
+" -----------------------
+" Deprecations:
+"   - CleanDiffOptions() is deprecated as Vim now has the :diffoff command.
+"   - MakeArgumentString, MakeArgumentList and CreateArgString are deprecated.
+"     Vim7 now includes call() function to receive and pass argument lists
+"     around.
+"   - The g:makeArgumentString and g:makeArgumentList are obsolete and are
+"     deprecated, please use MakeArgumentString() and MakeArgumentList()
+"     instead.
+"   - FindWindowForBuffer() function is now deprecated, as the corresponding
+"     Vim bugs are fixed. Use the below expr instead:
+"       bufwinnr(genutils#FindBufferForName(fileName))
+"   - QSort(), QSort2(), BinInsertSort() and BinInsertSort2() functions are
+"     now deprecated in favor of sort() function.
+"       
+"
+" Sample Usages Or Tips:
+"   - Add the following commands to create simple sort commands.
+"       command! -nargs=0 -range=% SortByLength <line1>,<line2>call
+"           \ genutils#QSort('genutils#CmpByLineLengthNname', 1)
+"       command! -nargs=0 -range=% RSortByLength <line1>,<line2>call
+"           \ genutils#QSort('genutils#CmpByLineLengthNname', -1)
+"       command! -nargs=0 -range=% SortJavaImports <line1>,<line2>call
+"           \ genutils#QSort('genutils#CmpJavaImports', 1)
+"
+"   - You might like the following mappings to adjust spacing:
+"       nnoremap <silent> <C-Space> :call genutils#ShiftWordInSpace(1)<CR>
+"       nnoremap <silent> <C-BS> :call genutils#ShiftWordInSpace(-1)<CR>
+"       nnoremap <silent> \cw :call genutils#CenterWordInSpace()<CR>
+"       nnoremap <silent> \va :call
+"           \ genutils#AlignWordWithWordInPreviousLine()<CR>
+"
+"   - The :find command is very useful to search for a file in path, but it
+"     doesn't support file completion. Add the following command in your vimrc
+"     to add this functionality:
+"       command! -nargs=1 -bang -complete=custom,<SID>PathComplete FindInPath
+"             \ :find<bang> <args>
+"       function! s:PathComplete(ArgLead, CmdLine, CursorPos)
+"         return genutils#UserFileComplete(a:ArgLead, a:CmdLine, a:CursorPos, 1,
+"             \ &path)
+"       endfunction
+"
+"   - If you are running commands that generate multiple pages of output, you
+"     might find it useful to redirect the output to a new buffer. Put the
+"     following command in your vimrc:
+"       command! -nargs=* -complete=command Redir
+"             \ :new | put! =genutils#GetVimCmdOutput('<args>') |
+"             \ setl bufhidden=wipe | setl nomodified
+"
+" Changes in 2.4:
+"   - Fixed some corner cases in RelPathFromDir()/RelPathFromFile().
+"   - Made the default comparators sort() function friendly.
+" Changes in 2.3:
+"   - SilentSubstitute() and SilentDelete() should preserve cursor position.
+"   - CleanupFileName() should also remove any leading or trailing whitespace.
+" Changes in 2.2:
+"   - EscapeCommand() now supports Lists as arguments.
+"   - CrUnProtectedCharsPattern() now accepts an optional "capture" argument.
+"   - Renamed PromptForElement2 to PromptForElement. It was a typo.
+" Changes in 2.1:
+"   - Fixed a typo in AddNotifyWindowClose() in the previous release.
+"   - Added BinSearchList() function.
+" Changes in 2.0:
+"   - Converted to Vim7 autoload script. Since there is no common prefix to
+"     find all the usages of genutils functions in your script, Use the
+"     pattern \<\(:\|>\|#\)\@<!\zs\u\w\+( to find all the global functions and
+"     prefix the ones from genutils with genutils#.
+"   - The new version is not backwards compatible with prior versions. If you
+"     have plugins that depend on the older versions of genutils, you should try
+"     to request the author to port their plugin to use the new genutils. If
+"     having them to coexist is a must, then use the below trick:
+"      - Install the latest version of genutils first. Overwriting all existing
+"        files.
+"      - Open the plugin/genutils.vim file and note the value set to
+"        loaded_genutils variable.
+"      - Install the older version of genutils (non autoload version) in to
+"        plugin directory, overwriting the existing file.
+"      - Open the plugin/genutils.vim again and change the value of
+"        loaded_genutils variable to the value you noted before and save it.
+"   - Fix for Save/RestoreHardPosition() not working right when there are
+"     wrapped lines in the window.
+"   - Dropped the AddToFCShell and RemoveFromFCShell functions as these can't be
+"     implemented in Vim7 because of new restrictions on FileChangedShell
+"     autocommand. Use AddToFcShellPre and RemoveFromFCShellPre functions
+"     instead.
+"   - No longer depends on multvals plugin. Inherits some useful functions from
+"     multvals to make way for it to be retired. New functions are:
+"     genutils#CrUnProtectedCharsPattern
+"     PromptForElement/GetSelectedIndex
+
+if exists('loaded_genutils')
+  finish
+endif
+if v:version < 700
+  echomsg 'genutils: You need at least Vim 7.0'
+  finish
+endif
+
+let loaded_genutils = 204
diff --git a/plugin/lookupfile.vim b/plugin/lookupfile.vim
new file mode 100644 (file)
index 0000000..6f49d06
--- /dev/null
@@ -0,0 +1,576 @@
+" lookupfile.vim: Lookup filenames by pattern
+" Author: Hari Krishna Dara (hari.vim at gmail dot com)
+" Last Change: 14-Jun-2007 @ 18:30
+" Created:     11-May-2006
+" Requires:    Vim-7.1, genutils.vim(2.3)
+" Version:     1.8.0
+" Licence: This program is free software; you can redistribute it and/or
+"          modify it under the terms of the GNU General Public License.
+"          See http://www.gnu.org/copyleft/gpl.txt 
+" Download From:
+"     http://www.vim.org//script.php?script_id=1581
+" Usage:
+"     See :help lookupfile.txt
+
+if exists('loaded_lookupfile')
+  finish
+endif
+if v:version < 701
+  echomsg 'lookupfile: You need at least Vim 7.1'
+  finish
+endif
+if !exists('loaded_genutils')
+  runtime plugin/genutils.vim
+endif
+if !exists('loaded_genutils') || loaded_genutils < 203
+  echomsg 'lookupfile: You need a newer version of genutils.vim plugin'
+  finish
+endif
+
+let g:loaded_lookupfile = 108
+
+" Make sure line-continuations won't cause any problem. This will be restored
+"   at the end
+let s:save_cpo = &cpo
+set cpo&vim
+
+if !exists('g:LookupFile_TagExpr')
+  let g:LookupFile_TagExpr = '&tags'
+endif
+
+if !exists('g:LookupFile_LookupFunc')
+  let g:LookupFile_LookupFunc = ''
+endif
+
+if !exists('g:LookupFile_LookupNotifyFunc')
+  let g:LookupFile_LookupNotifyFunc = ''
+endif
+
+if !exists('g:LookupFile_LookupAcceptFunc')
+  let g:LookupFile_LookupAcceptFunc = ''
+endif
+
+if !exists('g:LookupFile_MinPatLength')
+  let g:LookupFile_MinPatLength = 4
+endif
+
+if !exists('g:LookupFile_PreservePatternHistory')
+  let g:LookupFile_PreservePatternHistory = 1
+endif
+
+if !exists('g:LookupFile_PreserveLastPattern')
+  let g:LookupFile_PreserveLastPattern = 1
+endif
+
+if !exists('g:LookupFile_ShowFiller')
+  let g:LookupFile_ShowFiller = 1
+endif
+
+if !exists('g:LookupFile_AlwaysAcceptFirst')
+  let g:LookupFile_AlwaysAcceptFirst = 0
+endif
+
+if !exists('g:LookupFile_FileFilter')
+  let g:LookupFile_FileFilter = ''
+endif
+
+if !exists('g:LookupFile_AllowNewFiles')
+  let g:LookupFile_AllowNewFiles = 1
+endif
+
+if !exists('g:LookupFile_SortMethod')
+  let g:LookupFile_SortMethod = 'alpha'
+endif
+
+if !exists('g:LookupFile_Bufs_BufListExpr')
+  let g:LookupFile_Bufs_BufListExpr = ''
+endif
+
+if !exists('g:LookupFile_Bufs_SkipUnlisted')
+  let g:LookupFile_Bufs_SkipUnlisted = 1
+endif
+
+if !exists('g:LookupFile_Bufs_LikeBufCmd')
+  let g:LookupFile_Bufs_LikeBufCmd = 1
+endif
+
+if !exists('g:LookupFile_UsingSpecializedTags')
+  let g:LookupFile_UsingSpecializedTags = 0
+endif
+
+if !exists('g:LookupFile_DefaultCmd')
+  let g:LookupFile_DefaultCmd = ':LUTags'
+endif
+
+if !exists('g:LookupFile_EnableRemapCmd')
+  let g:LookupFile_EnableRemapCmd = 1
+endif
+
+if !exists('g:LookupFile_DisableDefaultMap')
+  let g:LookupFile_DisableDefaultMap = 0
+endif
+
+if !exists('g:LookupFile_UpdateTime')
+  let g:LookupFile_UpdateTime = 300
+endif
+
+if !exists('g:LookupFile_OnCursorMovedI')
+  let g:LookupFile_OnCursorMovedI = 0
+endif
+
+if !exists('g:LookupFile_EscCancelsPopup')
+  let g:LookupFile_EscCancelsPopup = 1
+endif
+
+if !exists('g:LookupFile_SearchForBufsInTabs')
+  let g:LookupFile_SearchForBufsInTabs = 1
+endif
+
+if !exists('g:LookupFile_TagsExpandCamelCase')
+  let g:LookupFile_TagsExpandCamelCase = 1
+endif
+
+if !exists('g:LookupFile_RecentFileListSize')
+  let g:LookupFile_RecentFileListSize = 20
+endif
+
+if (! exists("no_plugin_maps") || ! no_plugin_maps) &&
+      \ (! exists("no_lookupfile_maps") || ! no_lookupfile_maps)
+  noremap <script> <silent> <Plug>LookupFile :LookupFile<CR>
+
+  if ! g:LookupFile_DisableDefaultMap
+    if !hasmapto('<Plug>LookupFile', 'n')
+      nmap <unique> <silent> <F5> <Plug>LookupFile
+    endif
+    if !hasmapto('<Plug>LookupFile', 'i')
+      inoremap <Plug>LookupFileCE <C-E>
+      imap <unique> <expr> <silent> <F5> (pumvisible() ? "\<Plug>LookupFileCE" :
+            \ "")."\<Esc>\<Plug>LookupFile"
+    endif
+  endif
+endif
+
+command! -nargs=? -bang -complete=file LookupFile :call
+      \ <SID>LookupUsing('lookupfile', "<bang>", <q-args>, 0)
+
+command! -nargs=? -bang -complete=tag LUTags :call
+      \ <SID>LookupUsing('Tags', "<bang>", <q-args>, 0)
+command! -nargs=? -bang -complete=file LUPath :call
+      \ <SID>LookupUsing('Path', "<bang>", <q-args>, g:LookupFile_MinPatLength)
+command! -nargs=? -bang -complete=file LUArgs :call
+      \ <SID>LookupUsing('Args', "<bang>", <q-args>, 0)
+command! -nargs=? -bang -complete=file LUBufs :call
+      \ <SID>LookupUsing('Bufs', "<bang>", <q-args>, 0)
+command! -nargs=? -bang -complete=dir LUWalk :call
+      \ <SID>LookupUsing('Walk', "<bang>", <q-args>, 0)
+
+function! s:RemapLookupFile(cmd)
+  let cmd = (a:cmd != '') ? a:cmd : ':LUTags'
+  " It is not straight-forward to determine the right completion method.
+  exec 'command! -nargs=? -bang -complete=file LookupFile' cmd
+endfunction
+call s:RemapLookupFile(g:LookupFile_DefaultCmd)
+
+let s:mySNR = ''
+function! s:SNR()
+  if s:mySNR == ''
+    let s:mySNR = matchstr(expand('<sfile>'), '<SNR>\d\+_\zeSNR$')
+  endif
+  return s:mySNR
+endfun
+
+let s:baseBufNr = 0
+function! s:LookupUsing(ftr, bang, initPat, minPatLen)
+  let cmd = ':LUTags'
+  if a:ftr != 'Tags'
+    call s:SaveSett('LookupFunc')
+    call s:SaveSett('LookupNotifyFunc')
+    call s:SaveSett('MinPatLength')
+    unlet! g:LookupFile_LookupFunc g:LookupFile_LookupNotifyFunc
+    let g:LookupFile_LookupFunc = function(s:SNR().'Lookup'.a:ftr)
+    let g:LookupFile_LookupNotifyFunc = function(s:SNR().'LookupReset')
+    let g:LookupFile_MinPatLength = a:minPatLen
+    let s:baseBufNr = bufnr('%')
+    let cmd = ':LU'.a:ftr
+  endif
+  if g:LookupFile_EnableRemapCmd
+    call s:RemapLookupFile(cmd)
+  endif
+  call lookupfile#OpenWindow(a:bang, a:initPat)
+
+  if exists('*s:Config'.a:ftr)
+    call s:Config{a:ftr}()
+  endif
+
+  aug LookupReset
+    au!
+    au BufHidden <buffer> call <SID>LookupReset()
+  aug END
+endfunction
+
+function! s:LookupReset()
+  if exists('s:saved')
+    for sett in keys(s:saved)
+      unlet! g:LookupFile_{sett}
+      let g:LookupFile_{sett} = s:saved[sett]
+    endfor
+    unlet s:saved
+  endif
+  if exists('s:cleanup')
+    for cmd in s:cleanup
+      try
+        exec cmd
+      catch
+        echoerr v:exception . ', while executing cleanup command: ' . cmd
+      endtry
+    endfor
+    unlet s:cleanup
+  endif
+  aug ConfigIdo
+    au!
+  aug END
+endfunction
+
+function! s:SaveSett(sett)
+  if !exists('s:saved')
+    let s:saved = {}
+  endif
+  " Avoid overwriting the original value.
+  if !has_key(s:saved, a:sett)
+    let s:saved[a:sett] = g:LookupFile_{a:sett}
+  endif
+endfunction
+
+function! s:AddCleanup(cmd)
+  if !exists('s:cleanup')
+    let s:cleanup = []
+  endif
+  if index(s:cleanup, a:cmd) == -1
+    call add(s:cleanup, a:cmd)
+  endif
+endfunction
+
+function! s:LookupPath(pattern)
+  let filePat = a:pattern
+  let matchingExactCase = s:MatchingExactCase(filePat)
+  " Remove leading or trailing '*'s as we add a star anyway. This also removes
+  " '**' unless it is followed by a slash.
+  let filePat = substitute(filePat, '^\*\+\|\*\+$', '', 'g')
+  " On windows, the case is anyway ignored.
+  if !genutils#OnMS() && !matchingExactCase
+    let filePat = s:FilePatIgnoreCase(filePat)
+  endif
+  let fl = split(globpath(&path, (filePat != '') ? '*'.filePat.'*' : '*'),
+        \ "\n")
+  let regexPat = s:TranslateFileRegex(filePat)
+  " This is a psuedo case-sensitive match for windows, when 'smartcase' is
+  " set.
+  if genutils#OnMS() && matchingExactCase
+    set verbose=15
+    call filter(fl, 'v:val =~# regexPat')
+    set verbose=0
+  endif
+  return map(fl,
+        \ '{'.
+        \ ' "word": v:val,'.
+        \ ' "abbr": fnamemodify(v:val, ":t"), '.
+        \ ' "menu": fnamemodify(v:val, ":h"), '.
+        \ ' "dup": 1'.
+        \ '}')
+endfunction
+
+function! s:LookupArgs(pattern)
+  return map(filter(argv(), 'v:val =~ a:pattern'),
+        \ '{'.
+        \ ' "word":fnamemodify(v:val, ":p"), '.
+        \ ' "abbr": v:val, '.
+        \ ' "menu": substitute(v:val, a:pattern, "[&]", ""), '.
+        \ ' "dup": 1'.
+        \ '}')
+endfunction
+
+let s:bufList = [1]
+function! s:LookupBufs(pattern)
+  let results = []
+
+  if g:LookupFile_Bufs_BufListExpr != ''
+    let buflist = eval(g:LookupFile_Bufs_BufListExpr)
+  else
+    " Since we need to generate the same range again and again, it is better to
+    " cache the list.
+    if s:bufList[-1] != bufnr('$')
+      call extend(s:bufList, range(s:bufList[-1], bufnr('$')))
+    endif
+    let buflist = s:bufList
+  endif
+  let lastBufNr = bufnr('$')
+  let i = 1
+  if g:LookupFile_Bufs_LikeBufCmd
+    let pattern = s:TranslateFileRegex(a:pattern)
+  else
+    let pattern = a:pattern
+  endif
+  for bufNr in buflist
+    if ! bufexists(bufNr)
+      call remove(buflist, i)
+      continue
+    endif
+    try
+      if g:LookupFile_Bufs_SkipUnlisted && ! buflisted(bufNr)
+        continue
+      endif
+      let fname = expand('#'.bufNr.':p')
+      if g:LookupFile_Bufs_LikeBufCmd
+        let bname = bufname(bufNr)
+        let dir = ''
+      else
+        let bname = fnamemodify(bufname(bufNr), ':t')
+        let dir = fnamemodify(bufname(bufNr), ':h').'/'
+      endif
+      if bname =~ pattern
+        call add(results, {
+              \ 'word': fname,
+              \ 'abbr': bname,
+              \ 'menu': dir.substitute(bname, pattern, '[&]', ''),
+              \ 'dup': 1,
+              \ })
+      endif
+    finally
+      let i = i + 1
+    endtry
+  endfor
+  return results
+endfunction
+
+function! s:LookupWalk(pattern)
+  " We will wait till '/' is typed
+  if a:pattern =~ '\*\*$'
+    return []
+  endif
+  let showOnlyDirs = 0
+  " Determine the parent dir.
+  if a:pattern =~ '//$'
+    let parent = strpart(a:pattern, 0, strlen(a:pattern)-1)
+    let filePat = ''
+    if parent ==# g:lookupfile#lastPattern
+      return filter(g:lookupfile#lastResults, 'v:val["kind"] == "/"')
+    endif
+    let showOnlyDirs = 1
+  else
+    let parent = matchstr(a:pattern, '^.*/')
+    let filePat = strpart(a:pattern, len(parent))
+  endif
+
+  let matchingExactCase = s:MatchingExactCase(filePat)
+
+  " Remove leading or trailing '*'s as we add a star anyway. This also makes
+  " '**' as '', but we rule this case out already.
+  let filePat = substitute(filePat, '^\*\+\|\*\+$', '', 'g')
+  " On windows, the case is anyway ignored.
+  if !genutils#OnMS() && !matchingExactCase
+    let filePat = s:FilePatIgnoreCase(filePat)
+  endif
+  "exec BPBreak(1)
+  let _shellslash = &shellslash
+  set shellslash
+  try
+    let files = glob(parent.((filePat != '') ? '*'.filePat.'*' : '*'))
+  catch
+    " Ignore errors in patterns.
+    let files = ''
+  finally
+    let &shellslash = _shellslash
+  endtry
+  let fl = split(files, "\<NL>")
+  let regexPat = s:TranslateFileRegex(filePat)
+  " This is a psuedo case-sensitive match for windows, when 'smartcase' is
+  " set.
+  if genutils#OnMS() && matchingExactCase
+    call filter(fl, 'fnamemodify(v:val, ":t") =~# regexPat')
+  endif
+  " Find the start of path component that uses any of the *, [], ? or {
+  " wildcard. Path until this is unambiguously common to all, so we can strip
+  " it off, for brevity.
+  let firstWildIdx = match(a:pattern, '[^/]*\%(\*\|\[\|?\|{\)')
+  return s:FormatFileResults(fl, firstWildIdx!=-1 ? firstWildIdx :
+        \ strlen(parent), regexPat, matchingExactCase, showOnlyDirs)
+endfunction
+
+function! s:FormatFileResults(fl, parentLen, matchPat, matchingCase, dirsOnly)
+  let entries = []
+  for f in a:fl
+    if isdirectory(f)
+      let suffx = '/'
+    else
+      if a:dirsOnly
+        continue
+      endif
+      let suffx = ''
+    endif
+    let word = f.suffx
+    let fname = matchstr(f, '[^/]*$')
+    let dir = fnamemodify(f, ':h').'/'
+    if dir != '/' && a:parentLen != -1
+      let dir = strpart(dir, a:parentLen)
+    else
+      let dir = ''
+    endif
+    "let dir = (dir == '/'?'':dir)
+    call add(entries, {
+          \ 'word': word,
+          \ 'abbr': fname.suffx,
+          \ 'menu': (a:matchPat!='') ? dir.substitute(fname,
+          \   (a:matchingCase?'\C':'\c').a:matchPat, '[&]', '') :
+          \    dir.fname,
+          \ 'kind': suffx,
+          \ 'dup': 1
+          \ })
+  endfor
+  return entries
+endfunction
+
+function! s:ConfigBufs()
+  " Allow switching to file mode.
+  inoremap <expr> <buffer> <C-F> <SID>IdoSwitchTo('file')
+  call s:AddCleanup('iunmap <buffer> <C-F>')
+  if g:LookupFile_Bufs_BufListExpr != ''
+    call s:SaveSett('SortMethod')
+    let g:LookupFile_SortMethod = ''
+  endif
+endfunction
+
+function! s:ConfigWalk()
+  call s:SaveSett('LookupAcceptFunc')
+  unlet! g:LookupFile_LookupAcceptFunc
+  let g:LookupFile_LookupAcceptFunc = function(s:SNR().'IdoAccept')
+  " Make sure we have the right slashes, in case user passed in init path
+  " with wrong slashes.
+  call setline('.', substitute(getline('.'), '\\', '/', 'g'))
+
+  inoremap <buffer> <expr> <BS> <SID>IdoBS()
+  inoremap <buffer> <expr> <S-BS> <SID>IdoBS()
+  call s:AddCleanup('iunmap <buffer> <BS>')
+  imap <buffer> <expr> <Tab> <SID>IdoTab()
+  call s:AddCleanup('iunmap <buffer> <Tab>')
+  inoremap <expr> <buffer> <C-B> <SID>IdoSwitchTo('buffer')
+  call s:AddCleanup('iunmap <buffer> <C-B>')
+endfunction
+
+function! s:IdoSwitchTo(mode)
+  call s:LookupReset()
+  if a:mode == 'buffer'
+    let tok = matchstr(getline('.'), '[^/]*$')
+    let cmd = 'LUBufs'.(tok == "" ? '!' : ' '.tok)
+  else
+    let cmd = 'LUWalk '.s:GetDefDir().getline('.')
+  endif
+  return (pumvisible()?"\<C-E>":'')."\<Esc>:".cmd."\<CR>"
+endfunction
+
+function! s:IdoAccept(splitWin, key)
+  let refreshCmd = "\<C-O>:call lookupfile#LookupFile(0)\<CR>\<C-O>:\<BS>"
+  if getline('.') !=# g:lookupfile#lastPattern && getline('.')[strlen(getline('.'))-1] == '/'
+    return refreshCmd
+  elseif getline('.') ==# g:lookupfile#lastPattern
+        \ && len(g:lookupfile#lastResults) > 0
+        \ && g:lookupfile#lastResults[0]['kind'] == '/'
+    " When the first entry is a directory, accept it, and trigger a fresh
+    " completion on that.
+    return "\<C-N>\<C-R>=(getline('.') == lookupfile#lastPattern)?\"\\<C-N>\":''\<CR>".refreshCmd
+  endif
+  return lookupfile#AcceptFile(a:splitWin, a:key)
+endfunction
+
+function! s:IdoBS()
+  if lookupfile#IsPopupHidden() == 1
+    return "\<BS>"
+  endif
+  if getline('.') !~ '/$'
+    return (pumvisible() ? "\<C-E>" : '')."\<BS>"
+  else
+    " Determine the number of <BS>'s required to remove the patch component.
+    let lastComp = matchstr(getline('.'), '[^/]*/$')
+    return (pumvisible() ? (getline('.') ==# g:lookupfile#lastPattern ?
+          \ "\<C-E>" : "\<C-Y>") : '') . repeat("\<BS>", strlen(lastComp))
+  endif
+endfunction
+
+function! s:IdoTab()
+  " When no pattern yet, fill up with current directory.
+  if !pumvisible() && getline('.') == ''
+    return s:GetDefDir()
+  else
+    return "\<Tab>"
+  endif
+endfunction
+
+function! s:GetDefDir()
+  return substitute(expand('#'.s:baseBufNr.':p:h'), '\\', '/', 'g').'/'
+endfunction
+
+" Convert file wildcards ("*", "?" etc. |file-pattern|) to a Vim string
+"   regex metachars (see |pattern.txt|). Returns metachars that work in "very
+"   nomagic" mode.
+let s:fileWild = {}
+function! s:TranslateFileWild(fileWild)
+  let strRegex = ''
+  if a:fileWild ==# '*'
+    let strRegex = '\[^/]\*'
+  elseif a:fileWild ==# '**'
+    let strRegex = '\.\*'
+  elseif a:fileWild ==# '?'
+    let strRegex = '\.'
+  elseif a:fileWild ==# '['
+    let strRegex = '\['
+  endif
+  return strRegex
+endfunction
+
+" Convert a |file-pattern| to a Vim string regex (see |pattern.txt|).
+"   No error checks for now, for simplicity.
+function! s:TranslateFileRegex(filePat)
+  let pat = substitute(a:filePat, '\(\*\*\|\*\|\[\)',
+        \ '\=s:TranslateFileWild(submatch(1))', 'g')
+  let unprotectedMeta = genutils#CrUnProtectedCharsPattern('?,', 1)
+  let pat = substitute(pat, unprotectedMeta,
+        \ '\=s:TranslateFileWild(submatch(1))', 'g')
+  return (pat == '') ? pat : '\V'.pat
+endfunction
+" Translates the file pattern to ignore case on non-case-insensitive systems.
+function! s:FilePatIgnoreCase(filePat)
+  return substitute(a:filePat, '\(\[.\{-}]\)\|\(\a\)',
+        \ '\=s:TranslateAlpha(submatch(0))', 'g')
+endfunction
+
+function! s:TranslateAlpha(pat)
+  if a:pat =~"^["
+    return substitute(substitute(a:pat, '-\@<!\a-\@!', '&\u&', 'g'),
+          \ '\(\a\)-\(\a\)', '\1-\2\u\1-\u\2', 'g')
+  else
+    return substitute(a:pat, '\a', '[\l&\u&]', 'g')
+  endif
+endfunction
+
+function! s:MatchingExactCase(filePat)
+  if &ignorecase
+    if &smartcase && a:filePat =~# '\u'
+      let matchingExactCase = 1
+    else
+      let matchingExactCase = 0
+    endif
+  else
+    if genutils#OnMS()
+      let matchingExactCase = 0
+    else
+      let matchingExactCase = 1
+    endif
+  endif
+  return matchingExactCase
+endfunction
+
+" Restore cpo.
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim6:fdm=marker et sw=2
diff --git a/plugin/slime.vim b/plugin/slime.vim
new file mode 100644 (file)
index 0000000..0e9aa54
--- /dev/null
@@ -0,0 +1,28 @@
+function Send_to_Screen(text)
+  if !exists("g:screen_sessionname") || !exists("g:screen_windowname")
+    call Screen_Vars()
+  end
+
+  echo system("screen -S " . g:screen_sessionname . " -p " . g:screen_windowname . " -X stuff '" . substitute(a:text, "'", "'\\\\''", 'g') . "'")
+endfunction
+
+function Screen_Session_Names(A,L,P)
+  return system("screen -ls | awk '/Attached/ {print $1}'")
+endfunction
+
+function Screen_Vars()
+  if !exists("g:screen_sessionname") || !exists("g:screen_windowname")
+    let g:screen_sessionname = ""
+    let g:screen_windowname = "0"
+  end
+
+  let g:screen_sessionname = input("session name: ", "", "custom,Screen_Session_Names")
+  let g:screen_windowname = input("window name: ", g:screen_windowname)
+endfunction
+
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+vmap <C-c><C-c> "ry :call Send_to_Screen(@r)<CR>
+nmap <C-c><C-c> vip<C-c><C-c>
+
+nmap <C-c>v :call Screen_Vars()<CR>
diff --git a/plugin/supertab.vim b/plugin/supertab.vim
new file mode 100644 (file)
index 0000000..6d79d6b
--- /dev/null
@@ -0,0 +1,463 @@
+" Author:
+"   Original: Gergely Kontra <[email protected]>
+"   Current:  Eric Van Dewoestine <[email protected]> (as of version 0.4)
+"   Please direct all correspondence to Eric.
+" Version: 0.45
+"
+" Description: {{{
+"   Use your tab key to do all your completion in insert mode!
+"   You can cycle forward and backward with the <Tab> and <S-Tab> keys
+"   (<S-Tab> will not work in the console version)
+"   Note: you must press <Tab> once to be able to cycle back
+"
+"   http://www.vim.org/scripts/script.php?script_id=1643
+" }}}
+"
+" License: {{{
+"   Software License Agreement (BSD License)
+"
+"   Copyright (c) 2002 - 2007
+"   All rights reserved.
+"
+"   Redistribution and use of this software in source and binary forms, with
+"   or without modification, are permitted provided that the following
+"   conditions are met:
+"
+"   * Redistributions of source code must retain the above
+"     copyright notice, this list of conditions and the
+"     following disclaimer.
+"
+"   * Redistributions in binary form must reproduce the above
+"     copyright notice, this list of conditions and the
+"     following disclaimer in the documentation and/or other
+"     materials provided with the distribution.
+"
+"   * Neither the name of Gergely Kontra or Eric Van Dewoestine nor the names
+"   of its contributors may be used to endorse or promote products derived
+"   from this software without specific prior written permission of Gergely
+"   Kontra or Eric Van Dewoestine.
+"
+"   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+"   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+"   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+"   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+"   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+"   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+"   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+"   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+"   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+"   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+"   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+" }}}
+
+if exists('complType') "Integration with other completion functions.
+  finish
+endif
+
+" Global Variables {{{
+
+  " Used to set the default completion type.
+  " There is no need to escape this value as that will be done for you when
+  " the type is set.
+  " Ex.  let g:SuperTabDefaultCompletionType = "<C-X><C-U>"
+  if !exists("g:SuperTabDefaultCompletionType")
+    let g:SuperTabDefaultCompletionType = "<C-P>"
+  endif
+
+  " Used to set a list of variable, completion type pairs used to determine
+  " the default completion type to use for the current buffer.  If the
+  " variable is non-zero and non-empty then the associated completion type
+  " will be used.
+  " Ex. To use omni or user completion when available, but fall back to the
+  " global default otherwise.
+  "   let g:SuperTabDefaultCompletionTypeDiscovery = "&omnifunc:<C-X><C-O>,&completefunc:<C-X><C-U>"
+  if !exists("g:SuperTabDefaultCompletionTypeDiscovery")
+    let g:SuperTabDefaultCompletionTypeDiscovery = ""
+  endif
+
+  " Determines if, and for how long, the current completion type is retained.
+  " The possible values include:
+  " 0 - The current completion type is only retained for the current completion.
+  "     Once you have chosen a completion result or exited the completion
+  "     mode, the default completion type is restored.
+  " 1 - The current completion type is saved for the duration of your vim
+  "     session or until you enter a different completion mode.
+  "     (SuperTab default).
+  " 2 - The current completion type is saved until you exit insert mode (via
+  "     ESC).  Once you exit insert mode the default completion type is
+  "     restored.
+  if !exists("g:SuperTabRetainCompletionType")
+    let g:SuperTabRetainCompletionType = 1
+  endif
+
+  " Sets whether or not mid word completion is enabled.
+  " When enabled, <tab> will kick off completion when ever a word character is
+  " to the left of the cursor.  When disabled, completion will only occur if
+  " the char to the left is a word char and the char to the right is not (you
+  " are at the end of the word).
+  if !exists("g:SuperTabMidWordCompletion")
+    let g:SuperTabMidWordCompletion = 1
+  endif
+
+  " The following two variables allow you to set the key mapping used to kick
+  " off the current completion.  By default this is <tab> and <s-tab>.  To
+  " change to something like <c-space> and <s-c-space>, you can add the
+  " following to your vimrc.
+  "
+  "   let g:SuperTabMappingForward = '<c-space>'
+  "   let g:SuperTabMappingBackward = '<s-c-space>'
+  "
+  " Note: if the above does not have the desired effect (which may happen in
+  " console version of vim), you can try the following mappings.  Although the
+  " backwards mapping still doesn't seem to work in the console for me, your
+  " milage may vary.
+  "
+  "   let g:SuperTabMappingForward = '<nul>'
+  "   let g:SuperTabMappingBackward = '<s-nul>'
+  "
+  if !exists("g:SuperTabMappingForward")
+    let g:SuperTabMappingForward = '<tab>'
+  endif
+  if !exists("g:SuperTabMappingBackward")
+    let g:SuperTabMappingBackward = '<s-tab>'
+  endif
+
+  " Sets whether or not to pre-highlight first match when completeopt has
+  " the popup menu enabled and the 'longest' option as well.
+  " When enabled, <tab> will kick off completion and pre-select the first
+  " entry in the popup menu, allowing you to simply hit <enter> to use it.
+  if !exists("g:SuperTabLongestHighlight")
+    let g:SuperTabLongestHighlight = 0
+  endif
+
+" }}}
+
+" Script Variables {{{
+
+  " construct the help text.
+  let s:tabHelp =
+    \ "Hit <CR> or CTRL-] on the completion type you wish to switch to.\n" .
+    \ "Use :help ins-completion for more information.\n" .
+    \ "\n" .
+    \ "|<C-N>|      - Keywords in 'complete' searching down.\n" .
+    \ "|<C-P>|      - Keywords in 'complete' searching up (SuperTab default).\n" .
+    \ "|<C-X><C-L>| - Whole lines.\n" .
+    \ "|<C-X><C-N>| - Keywords in current file.\n" .
+    \ "|<C-X><C-K>| - Keywords in 'dictionary'.\n" .
+    \ "|<C-X><C-T>| - Keywords in 'thesaurus', thesaurus-style.\n" .
+    \ "|<C-X><C-I>| - Keywords in the current and included files.\n" .
+    \ "|<C-X><C-]>| - Tags.\n" .
+    \ "|<C-X><C-F>| - File names.\n" .
+    \ "|<C-X><C-D>| - Definitions or macros.\n" .
+    \ "|<C-X><C-V>| - Vim command-line."
+  if v:version >= 700
+    let s:tabHelp = s:tabHelp . "\n" .
+      \ "|<C-X><C-U>| - User defined completion.\n" .
+      \ "|<C-X><C-O>| - Omni completion.\n" .
+      \ "|<C-X>s|     - Spelling suggestions."
+  endif
+
+  " set the available completion types and modes.
+  let s:types =
+    \ "\<C-E>\<C-Y>\<C-L>\<C-N>\<C-K>\<C-T>\<C-I>\<C-]>\<C-F>\<C-D>\<C-V>\<C-N>\<C-P>"
+  let s:modes = '/^E/^Y/^L/^N/^K/^T/^I/^]/^F/^D/^V/^P'
+  if v:version >= 700
+    let s:types = s:types . "\<C-U>\<C-O>\<C-N>\<C-P>s"
+    let s:modes = s:modes . '/^U/^O/s'
+  endif
+  let s:types = s:types . "np"
+  let s:modes = s:modes . '/n/p'
+
+" }}}
+
+" CtrlXPP() {{{
+" Handles entrance into completion mode.
+function! CtrlXPP()
+  if &smd
+    echo '' | echo '-- ^X++ mode (' . s:modes . ')'
+  endif
+  let complType = nr2char(getchar())
+  if stridx(s:types, complType) != -1
+    if stridx("\<C-E>\<C-Y>", complType) != -1 " no memory, just scroll...
+      return "\<C-x>" . complType
+    elseif stridx('np', complType) != -1
+      let complType = nr2char(char2nr(complType) - 96)  " char2nr('n')-char2nr("\<C-n")
+    else
+      let complType="\<C-x>" . complType
+    endif
+
+    if g:SuperTabRetainCompletionType
+      let b:complType = complType
+    endif
+
+    return complType
+  else
+    echohl "Unknown mode"
+    return complType
+  endif
+endfunction " }}}
+
+" SuperTabSetCompletionType(type) {{{
+" Globally available function that user's can use to create mappings to
+" quickly switch completion modes.  Useful when a user wants to restore the
+" default or switch to another mode without having to kick off a completion
+" of that type or use SuperTabHelp.
+" Example mapping to restore SuperTab default:
+"   nmap <F6> :call SetSuperTabCompletionType("<C-P>")<cr>
+function! SuperTabSetCompletionType (type)
+  exec "let b:complType = \"" . escape(a:type, '<') . "\""
+endfunction " }}}
+
+" s:Init {{{
+" Global initilization when supertab is loaded.
+function! s:Init ()
+  augroup supertab_init
+    autocmd!
+    autocmd BufEnter * call <SID>InitBuffer()
+  augroup END
+  " ensure InitBuffer gets called for the first buffer.
+  call s:InitBuffer()
+
+  " Setup mechanism to restore orignial completion type upon leaving insert
+  " mode if g:SuperTabRetainCompletionType == 2
+  if g:SuperTabRetainCompletionType == 2
+    " pre vim 7, must map <esc>
+    if v:version < 700
+      im <silent> <ESC> <ESC>:call s:SetDefaultCompletionType()<cr>
+
+    " since vim 7, we can use InsertLeave autocmd.
+    else
+      augroup supertab_retain
+        autocmd!
+        autocmd InsertLeave * call s:SetDefaultCompletionType()
+      augroup END
+    endif
+  endif
+endfunction " }}}
+
+" s:InitBuffer {{{
+" Per buffer initilization.
+function! s:InitBuffer ()
+  if exists("b:complType")
+    return
+  endif
+
+  if !exists("b:SuperTabDefaultCompletionType")
+    " loop through discovery list to find the default
+    if g:SuperTabDefaultCompletionTypeDiscovery != ''
+      let dlist = g:SuperTabDefaultCompletionTypeDiscovery
+      while dlist != ''
+        let pair = substitute(dlist, '\(.\{-}\)\(,.*\|$\)', '\1', '')
+        let dlist = substitute(dlist, '.\{-}\(,.*\|$\)', '\1', '')
+        let dlist = substitute(dlist, '^,', '\1', '')
+
+        let var = substitute(pair, '\(.*\):.*', '\1', '')
+        let type = substitute(pair, '.*:\(.*\)', '\1', '')
+
+        exec 'let value = ' . var
+        if value !~ '^\s*$' && value != '0'
+          let b:SuperTabDefaultCompletionType = type
+          break
+        endif
+      endwhile
+    endif
+
+    " fallback to configured default.
+    if !exists("b:SuperTabDefaultCompletionType")
+      let b:SuperTabDefaultCompletionType = g:SuperTabDefaultCompletionType
+    endif
+  endif
+
+  " set the default completion type.
+  call SuperTabSetCompletionType(b:SuperTabDefaultCompletionType)
+endfunction " }}}
+
+" s:IsWordChar(char) {{{
+" Determines if the supplied character is a word character or matches value
+" defined by 'iskeyword'.
+function! s:IsWordChar (char)
+  if a:char =~ '\w'
+    return 1
+  endif
+
+  " check against 'iskeyword'
+  let values = &iskeyword
+  let index = stridx(values, ',')
+  while index > 0 || values != ''
+    if index > 0
+      let value = strpart(values, 0, index)
+      let values = strpart(values, index + 1)
+    else
+      let value = values
+      let values = ''
+    endif
+
+    " exception case for '^,'
+    if value == '^'
+      let value = '^,'
+
+    " execption case for ','
+    elseif value =~ '^,,'
+      let values .= strpart(value, 2)
+      let value = ','
+
+    " execption case after a ^,
+    elseif value =~ '^,'
+      let value = strpart(value, 1)
+    endif
+
+    " keyword values is an ascii number range
+    if value =~ '[0-9]\+-[0-9]\+'
+      let charnum = char2nr(a:char)
+      exec 'let start = ' . substitute(value, '\([0-9]\+\)-.*', '\1', '')
+      exec 'let end = ' . substitute(value, '.*-\([0-9]\+\)', '\1', '')
+
+      if charnum >= start && charnum <= end
+        return 1
+      endif
+
+    " keyword value is a set of include or exclude characters
+    else
+      let include = 1
+      if value =~ '^\^'
+        let value = strpart(value, 1)
+        let include = 0
+      endif
+
+      if a:char =~ '[' . escape(value, '[]') . ']'
+        return include
+      endif
+    endif
+    let index = stridx(values, ',')
+  endwhile
+
+  return 0
+endfunction " }}}
+
+" s:SetCompletionType() {{{
+" Sets the completion type based on what the user has chosen from the help
+" buffer.
+function! s:SetCompletionType ()
+  let chosen = substitute(getline('.'), '.*|\(.*\)|.*', '\1', '')
+  if chosen != getline('.')
+    let winnr = b:winnr
+    close
+    exec winnr . 'winc w'
+    call SuperTabSetCompletionType(chosen)
+  endif
+endfunction " }}}
+
+" s:SetDefaultCompletionType () {{{
+function! s:SetDefaultCompletionType ()
+  if exists('b:SuperTabDefaultCompletionType')
+    call SuperTabSetCompletionType(b:SuperTabDefaultCompletionType)
+  endif
+endfunction " }}}
+
+" s:SuperTab(command) {{{
+" Used to perform proper cycle navigtion as the user requests the next or
+" previous entry in a completion list, and determines whether or not to simply
+" retain the normal usage of <tab> based on the cursor position.
+function! s:SuperTab(command)
+  if s:WillComplete()
+    let key = ''
+    " highlight first result if longest enabled
+    if g:SuperTabLongestHighlight && !pumvisible() && &completeopt =~ 'longest'
+      let key = (b:complType == "\<C-P>") ? "\<C-P>" : "\<C-N>"
+    endif
+
+    " exception: if in <c-p> mode, then <c-n> should move up the list, and
+    " <c-p> down the list.
+    if a:command == 'p' && b:complType == "\<C-P>"
+      return "\<C-N>"
+    endif
+    return b:complType . key
+  endif
+
+  return "\<Tab>"
+endfunction " }}}
+
+" s:SuperTabHelp() {{{
+" Opens a help window where the user can choose a completion type to enter.
+function! s:SuperTabHelp()
+  let winnr = winnr()
+  if bufwinnr("SuperTabHelp") == -1
+    botright split SuperTabHelp
+
+    setlocal noswapfile
+    setlocal buftype=nowrite
+    setlocal bufhidden=delete
+
+    let saved = @"
+    let @" = s:tabHelp
+    silent put
+    call cursor(1, 1)
+    silent 1,delete
+    call cursor(4, 1)
+    let @" = saved
+    exec "resize " . line('$')
+
+    syntax match Special "|.\{-}|"
+
+    setlocal readonly
+    setlocal nomodifiable
+
+    nmap <silent> <buffer> <cr> :call <SID>SetCompletionType()<cr>
+    nmap <silent> <buffer> <c-]> :call <SID>SetCompletionType()<cr>
+  else
+    exec bufwinnr("SuperTabHelp") . "winc w"
+  endif
+  let b:winnr = winnr
+endfunction " }}}
+
+" s:WillComplete () {{{
+" Determines if completion should be kicked off at the current location.
+function! s:WillComplete ()
+  let line = getline('.')
+  let cnum = col('.')
+
+  " Start of line.
+  let prev_char = strpart(line, cnum - 2, 1)
+  if prev_char =~ '^\s*$'
+    return 0
+  endif
+
+  " Within a word, but user does not have mid word completion enabled.
+  let next_char = strpart(line, cnum - 1, 1)
+  if !g:SuperTabMidWordCompletion && s:IsWordChar(next_char)
+    return 0
+  endif
+
+  " In keyword completion mode and no preceding word characters.
+  "if (b:complType == "\<C-N>" || b:complType == "\<C-P>") && !s:IsWordChar(prev_char)
+  "  return 0
+  "endif
+
+  return 1
+endfunction " }}}
+
+" Key Mappings {{{
+  im <C-X> <C-r>=CtrlXPP()<CR>
+
+  " From the doc |insert.txt| improved
+  exec 'im ' . g:SuperTabMappingForward . ' <C-n>'
+  exec 'im ' . g:SuperTabMappingBackward . ' <C-p>'
+
+  " After hitting <Tab>, hitting it once more will go to next match
+  " (because in XIM mode <C-n> and <C-p> mappings are ignored)
+  " and wont start a brand new completion
+  " The side effect, that in the beginning of line <C-n> and <C-p> inserts a
+  " <Tab>, but I hope it may not be a problem...
+  ino <C-n> <C-R>=<SID>SuperTab('n')<CR>
+  ino <C-p> <C-R>=<SID>SuperTab('p')<CR>
+" }}}
+
+" Command Mappings {{{
+  if !exists(":SuperTabHelp")
+    command SuperTabHelp :call <SID>SuperTabHelp()
+  endif
+" }}}
+
+call <SID>Init()
+
+" vim:ft=vim:fdm=marker
diff --git a/plugin/surround.vim b/plugin/surround.vim
new file mode 100644 (file)
index 0000000..182cdee
--- /dev/null
@@ -0,0 +1,628 @@
+" surround.vim - Surroundings
+" Author:       Tim Pope <[email protected]>
+" GetLatestVimScripts: 1697 1 :AutoInstall: surround.vim
+" $Id: surround.vim,v 1.34 2008-02-15 21:43:42 tpope Exp $
+"
+" See surround.txt for help.  This can be accessed by doing
+"
+" :helptags ~/.vim/doc
+" :help surround
+"
+" Licensed under the same terms as Vim itself.
+
+" ============================================================================
+
+" Exit quickly when:
+" - this plugin was already loaded or disabled
+" - when 'compatible' is set
+if (exists("g:loaded_surround") && g:loaded_surround) || &cp
+    finish
+endif
+let g:loaded_surround = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Input functions {{{1
+
+function! s:getchar()
+    let c = getchar()
+    if c =~ '^\d\+$'
+        let c = nr2char(c)
+    endif
+    return c
+endfunction
+
+function! s:inputtarget()
+    let c = s:getchar()
+    while c =~ '^\d\+$'
+        let c = c . s:getchar()
+    endwhile
+    if c == " "
+        let c = c . s:getchar()
+    endif
+    if c =~ "\<Esc>\|\<C-C>\|\0"
+        return ""
+    else
+        return c
+    endif
+endfunction
+
+function! s:inputreplacement()
+    "echo '-- SURROUND --'
+    let c = s:getchar()
+    if c == " "
+        let c = c . s:getchar()
+    endif
+    if c =~ "\<Esc>" || c =~ "\<C-C>"
+        return ""
+    else
+        return c
+    endif
+endfunction
+
+function! s:beep()
+    exe "norm! \<Esc>"
+    return ""
+endfunction
+
+function! s:redraw()
+    redraw
+    return ""
+endfunction
+
+" }}}1
+
+" Wrapping functions {{{1
+
+function! s:extractbefore(str)
+    if a:str =~ '\r'
+        return matchstr(a:str,'.*\ze\r')
+    else
+        return matchstr(a:str,'.*\ze\n')
+    endif
+endfunction
+
+function! s:extractafter(str)
+    if a:str =~ '\r'
+        return matchstr(a:str,'\r\zs.*')
+    else
+        return matchstr(a:str,'\n\zs.*')
+    endif
+endfunction
+
+function! s:repeat(str,count)
+    let cnt = a:count
+    let str = ""
+    while cnt > 0
+        let str = str . a:str
+        let cnt = cnt - 1
+    endwhile
+    return str
+endfunction
+
+function! s:fixindent(str,spc)
+    let str = substitute(a:str,'\t',s:repeat(' ',&sw),'g')
+    let spc = substitute(a:spc,'\t',s:repeat(' ',&sw),'g')
+    let str = substitute(str,'\(\n\|\%^\).\@=','\1'.spc,'g')
+    if ! &et
+        let str = substitute(str,'\s\{'.&ts.'\}',"\t",'g')
+    endif
+    return str
+endfunction
+
+function! s:process(string)
+    let i = 0
+    while i < 7
+        let i = i + 1
+        let repl_{i} = ''
+        let m = matchstr(a:string,nr2char(i).'.\{-\}\ze'.nr2char(i))
+        if m != ''
+            let m = substitute(strpart(m,1),'\r.*','','')
+            let repl_{i} = input(substitute(m,':\s*$','','').': ')
+        endif
+    endwhile
+    let s = ""
+    let i = 0
+    while i < strlen(a:string)
+        let char = strpart(a:string,i,1)
+        if char2nr(char) < 8
+            let next = stridx(a:string,char,i+1)
+            if next == -1
+                let s = s . char
+            else
+                let insertion = repl_{char2nr(char)}
+                let subs = strpart(a:string,i+1,next-i-1)
+                let subs = matchstr(subs,'\r.*')
+                while subs =~ '^\r.*\r'
+                    let sub = matchstr(subs,"^\r\\zs[^\r]*\r[^\r]*")
+                    let subs = strpart(subs,strlen(sub)+1)
+                    let r = stridx(sub,"\r")
+                    let insertion = substitute(insertion,strpart(sub,0,r),strpart(sub,r+1),'')
+                endwhile
+                let s = s . insertion
+                let i = next
+            endif
+        else
+            let s = s . char
+        endif
+        let i = i + 1
+    endwhile
+    return s
+endfunction
+
+function! s:wrap(string,char,type,...)
+    let keeper = a:string
+    let newchar = a:char
+    let type = a:type
+    let linemode = type ==# 'V' ? 1 : 0
+    let special = a:0 ? a:1 : 0
+    let before = ""
+    let after  = ""
+    if type == "V"
+        let initspaces = matchstr(keeper,'\%^\s*')
+    else
+        let initspaces = matchstr(getline('.'),'\%^\s*')
+    endif
+    " Duplicate b's are just placeholders (removed)
+    let pairs = "b()B{}r[]a<>"
+    let extraspace = ""
+    if newchar =~ '^ '
+        let newchar = strpart(newchar,1)
+        let extraspace = ' '
+    endif
+    let idx = stridx(pairs,newchar)
+    if newchar == ' '
+        let before = ''
+        let after  = ''
+    elseif exists("b:surround_".char2nr(newchar))
+        let all    = s:process(b:surround_{char2nr(newchar)})
+        let before = s:extractbefore(all)
+        let after  =  s:extractafter(all)
+    elseif exists("g:surround_".char2nr(newchar))
+        let all    = s:process(g:surround_{char2nr(newchar)})
+        let before = s:extractbefore(all)
+        let after  =  s:extractafter(all)
+    elseif newchar ==# "p"
+        let before = "\n"
+        let after  = "\n\n"
+    elseif newchar =~# "[tT\<C-T><,]"
+        let dounmapp = 0
+        let dounmapb = 0
+        if !maparg(">","c")
+            let dounmapb= 1
+            " Hide from AsNeeded
+            exe "cn"."oremap > <CR>"
+            exe "cn"."oremap % %<C-V>"
+            "cm ap > <C-R>=getcmdline() =~ '^[^%?].*[%?]$' ? "\026\076" : "\026\076\015"<CR>
+        endif
+        let default = ""
+        if !maparg("%","c")
+            " This is to help when typing things like
+            " <a href="/images/<%= @image.filename %>">
+            " The downside is it breaks backspace, so lets disable it for now
+            "let dounmapp= 1
+            "exe "cn"."oremap % %<C-V>"
+        endif
+        if newchar ==# "T"
+            if !exists("s:lastdel")
+                let s:lastdel = ""
+            endif
+            let default = matchstr(s:lastdel,'<\zs.\{-\}\ze>')
+        endif
+        let tag = input("<",default)
+        echo "<".substitute(tag,'>*$','>','')
+        "if dounmapr
+            "silent! cunmap <CR>
+        "endif
+        if dounmapb
+            silent! cunmap >
+        endif
+        if dounmapp
+            silent! cunmap %
+        endif
+        if tag != ""
+            let tag = substitute(tag,'>*$','','')
+            let before = '<'.tag.'>'
+            if tag =~ '/$'
+                let after = ''
+            else
+                let after  = '</'.substitute(tag,' .*','','').'>'
+            endif
+            if newchar == "\<C-T>" || newchar == ","
+                if type ==# "v" || type ==# "V"
+                    let before = before . "\n\t"
+                endif
+                if type ==# "v"
+                    let after  = "\n". after
+                endif
+            endif
+        endif
+    elseif newchar ==# 'l' || newchar == '\'
+        " LaTeX
+        let env = input('\begin{')
+        let env = '{' . env
+        let env = env . s:closematch(env)
+        echo '\begin'.env
+        if env != ""
+            let before = '\begin'.env
+            let after  = '\end'.matchstr(env,'[^}]*').'}'
+        endif
+        "if type ==# 'v' || type ==# 'V'
+            "let before = before ."\n\t"
+        "endif
+        "if type ==# 'v'
+            "let after  = "\n".initspaces.after
+        "endif
+    elseif newchar ==# 'f' || newchar ==# 'F'
+        let fnc = input('function: ')
+        if fnc != ""
+            let before = substitute(fnc,'($','','').'('
+            let after  = ')'
+            if newchar ==# 'F'
+                let before = before . ' '
+                let after  = ' ' . after
+            endif
+        endif
+    elseif idx >= 0
+        let spc = (idx % 3) == 1 ? " " : ""
+        let idx = idx / 3 * 3
+        let before = strpart(pairs,idx+1,1) . spc
+        let after  = spc . strpart(pairs,idx+2,1)
+    elseif newchar == "\<C-[>" || newchar == "\<C-]>"
+        let before = "{\n\t"
+        let after  = "\n}"
+    elseif newchar !~ '\a'
+        let before = newchar
+        let after  = newchar
+    else
+        let before = ''
+        let after  = ''
+    endif
+    "let before = substitute(before,'\n','\n'.initspaces,'g')
+    let after  = substitute(after ,'\n','\n'.initspaces,'g')
+    "let after  = substitute(after,"\n\\s*\<C-U>\\s*",'\n','g')
+    if type ==# 'V' || (special && type ==# "v")
+        let before = substitute(before,' \+$','','')
+        let after  = substitute(after ,'^ \+','','')
+        if after !~ '^\n'
+            let after  = initspaces.after
+        endif
+        if keeper !~ '\n$' && after !~ '^\n'
+            let keeper = keeper . "\n"
+        elseif keeper =~ '\n$' && after =~ '^\n'
+            let after = strpart(after,1)
+        endif
+        if before !~ '\n\s*$'
+            let before = before . "\n"
+            if special
+                let before = before . "\t"
+            endif
+        endif
+    endif
+    if type ==# 'V'
+        let before = initspaces.before
+    endif
+    if before =~ '\n\s*\%$'
+        if type ==# 'v'
+            let keeper = initspaces.keeper
+        endif
+        let padding = matchstr(before,'\n\zs\s\+\%$')
+        let before  = substitute(before,'\n\s\+\%$','\n','')
+        let keeper = s:fixindent(keeper,padding)
+    endif
+    if type ==# 'V'
+        let keeper = before.keeper.after
+    elseif type =~ "^\<C-V>"
+        " Really we should be iterating over the buffer
+        let repl = substitute(before,'[\\~]','\\&','g').'\1'.substitute(after,'[\\~]','\\&','g')
+        let repl = substitute(repl,'\n',' ','g')
+        let keeper = substitute(keeper."\n",'\(.\{-\}\)\('.(special ? '\s\{-\}' : '').'\n\)',repl.'\n','g')
+        let keeper = substitute(keeper,'\n\%$','','')
+    else
+        let keeper = before.extraspace.keeper.extraspace.after
+    endif
+    return keeper
+endfunction
+
+function! s:wrapreg(reg,char,...)
+    let orig = getreg(a:reg)
+    let type = substitute(getregtype(a:reg),'\d\+$','','')
+    let special = a:0 ? a:1 : 0
+    let new = s:wrap(orig,a:char,type,special)
+    call setreg(a:reg,new,type)
+endfunction
+" }}}1
+
+function! s:insert(...) " {{{1
+    " Optional argument causes the result to appear on 3 lines, not 1
+    "call inputsave()
+    let linemode = a:0 ? a:1 : 0
+    let char = s:inputreplacement()
+    while char == "\<CR>" || char == "\<C-S>"
+        " TODO: use total count for additional blank lines
+        let linemode = linemode + 1
+        let char = s:inputreplacement()
+    endwhile
+    "call inputrestore()
+    if char == ""
+        return ""
+    endif
+    "call inputsave()
+    let cb_save = &clipboard
+    let reg_save = @@
+    call setreg('"',"\r",'v')
+    call s:wrapreg('"',char,linemode)
+    " If line mode is used and the surrounding consists solely of a suffix,
+    " remove the initial newline.  This fits a use case of mine but is a
+    " little inconsistent.  Is there anyone that would prefer the simpler
+    " behavior of just inserting the newline?
+    if linemode && match(getreg('"'),'^\n\s*\zs.*') == 0
+        call setreg('"',matchstr(getreg('"'),'^\n\s*\zs.*'),getregtype('"'))
+    endif
+    " This can be used to append a placeholder to the end
+    if exists("g:surround_insert_tail")
+        call setreg('"',g:surround_insert_tail,"a".getregtype('"'))
+    endif
+    "if linemode
+        "call setreg('"',substitute(getreg('"'),'^\s\+','',''),'c')
+    "endif
+    if col('.') >= col('$')
+        norm! ""p
+    else
+        norm! ""P
+    endif
+    if linemode
+        call s:reindent()
+    endif
+    norm! `]
+    call search('\r','bW')
+    let @@ = reg_save
+    let &clipboard = cb_save
+    return "\<Del>"
+endfunction " }}}1
+
+function! s:reindent() " {{{1
+    if exists("b:surround_indent") ? b:surround_indent : (exists("g:surround_indent") && g:surround_indent)
+        silent norm! '[=']
+    endif
+endfunction " }}}1
+
+function! s:dosurround(...) " {{{1
+    let scount = v:count1
+    let char = (a:0 ? a:1 : s:inputtarget())
+    let spc = ""
+    if char =~ '^\d\+'
+        let scount = scount * matchstr(char,'^\d\+')
+        let char = substitute(char,'^\d\+','','')
+    endif
+    if char =~ '^ '
+        let char = strpart(char,1)
+        let spc = 1
+    endif
+    if char == 'a'
+        let char = '>'
+    endif
+    if char == 'r'
+        let char = ']'
+    endif
+    let newchar = ""
+    if a:0 > 1
+        let newchar = a:2
+        if newchar == "\<Esc>" || newchar == "\<C-C>" || newchar == ""
+            return s:beep()
+        endif
+    endif
+    let cb_save = &clipboard
+    set clipboard-=unnamed
+    let append = ""
+    let original = getreg('"')
+    let otype = getregtype('"')
+    call setreg('"',"")
+    let strcount = (scount == 1 ? "" : scount)
+    if char == '/'
+        exe 'norm '.strcount.'[/d'.strcount.']/'
+    else
+        exe 'norm d'.strcount.'i'.char
+    endif
+    let keeper = getreg('"')
+    let okeeper = keeper " for reindent below
+    if keeper == ""
+        call setreg('"',original,otype)
+        let &clipboard = cb_save
+        return ""
+    endif
+    let oldline = getline('.')
+    let oldlnum = line('.')
+    if char ==# "p"
+        call setreg('"','','V')
+    elseif char ==# "s" || char ==# "w" || char ==# "W"
+        " Do nothing
+        call setreg('"','')
+    elseif char =~ "[\"'`]"
+        exe "norm! i \<Esc>d2i".char
+        call setreg('"',substitute(getreg('"'),' ','',''))
+    elseif char == '/'
+        norm! "_x
+        call setreg('"','/**/',"c")
+        let keeper = substitute(substitute(keeper,'^/\*\s\=','',''),'\s\=\*$','','')
+    else
+        " One character backwards
+        call search('.','bW')
+        exe "norm da".char
+    endif
+    let removed = getreg('"')
+    let rem2 = substitute(removed,'\n.*','','')
+    let oldhead = strpart(oldline,0,strlen(oldline)-strlen(rem2))
+    let oldtail = strpart(oldline,  strlen(oldline)-strlen(rem2))
+    let regtype = getregtype('"')
+    if char =~# '[\[({<T]' || spc
+        let keeper = substitute(keeper,'^\s\+','','')
+        let keeper = substitute(keeper,'\s\+$','','')
+    endif
+    if col("']") == col("$") && col('.') + 1 == col('$')
+        if oldhead =~# '^\s*$' && a:0 < 2
+            let keeper = substitute(keeper,'\%^\n'.oldhead.'\(\s*.\{-\}\)\n\s*\%$','\1','')
+        endif
+        let pcmd = "p"
+    else
+        let pcmd = "P"
+    endif
+    if line('.') < oldlnum && regtype ==# "V"
+        let pcmd = "p"
+    endif
+    call setreg('"',keeper,regtype)
+    if newchar != ""
+        call s:wrapreg('"',newchar)
+    endif
+    silent exe 'norm! ""'.pcmd.'`['
+    if removed =~ '\n' || okeeper =~ '\n' || getreg('"') =~ '\n'
+        call s:reindent()
+    endif
+    if getline('.') =~ '^\s\+$' && keeper =~ '^\s*\n'
+        silent norm! cc
+    endif
+    call setreg('"',removed,regtype)
+    let s:lastdel = removed
+    let &clipboard = cb_save
+    if newchar == ""
+        silent! call repeat#set("\<Plug>Dsurround".char,scount)
+    else
+        silent! call repeat#set("\<Plug>Csurround".char.newchar,scount)
+    endif
+endfunction " }}}1
+
+function! s:changesurround() " {{{1
+    let a = s:inputtarget()
+    if a == ""
+        return s:beep()
+    endif
+    let b = s:inputreplacement()
+    if b == ""
+        return s:beep()
+    endif
+    call s:dosurround(a,b)
+endfunction " }}}1
+
+function! s:opfunc(type,...) " {{{1
+    let char = s:inputreplacement()
+    if char == ""
+        return s:beep()
+    endif
+    let reg = '"'
+    let sel_save = &selection
+    let &selection = "inclusive"
+    let cb_save  = &clipboard
+    set clipboard-=unnamed
+    let reg_save = getreg(reg)
+    let reg_type = getregtype(reg)
+    "call setreg(reg,"\n","c")
+    let type = a:type
+    if a:type == "char"
+        silent exe 'norm! v`[o`]"'.reg.'y'
+        let type = 'v'
+    elseif a:type == "line"
+        silent exe 'norm! `[V`]"'.reg.'y'
+        let type = 'V'
+    elseif a:type ==# "v" || a:type ==# "V" || a:type ==# "\<C-V>"
+        silent exe 'norm! gv"'.reg.'y'
+    elseif a:type =~ '^\d\+$'
+        let type = 'v'
+        silent exe 'norm! ^v'.a:type.'$h"'.reg.'y'
+        if mode() == 'v'
+            norm! v
+            return s:beep()
+        endif
+    else
+        let &selection = sel_save
+        let &clipboard = cb_save
+        return s:beep()
+    endif
+    let keeper = getreg(reg)
+    if type == "v" && a:type != "v"
+        let append = matchstr(keeper,'\_s\@<!\s*$')
+        let keeper = substitute(keeper,'\_s\@<!\s*$','','')
+    endif
+    call setreg(reg,keeper,type)
+    call s:wrapreg(reg,char,a:0)
+    if type == "v" && a:type != "v" && append != ""
+        call setreg(reg,append,"ac")
+    endif
+    silent exe 'norm! gv'.(reg == '"' ? '' : '"' . reg).'p`['
+    if type == 'V' || (getreg(reg) =~ '\n' && type == 'v')
+        call s:reindent()
+    endif
+    call setreg(reg,reg_save,reg_type)
+    let &selection = sel_save
+    let &clipboard = cb_save
+    if a:type =~ '^\d\+$'
+        silent! call repeat#set("\<Plug>Y".(a:0 ? "S" : "s")."surround".char,a:type)
+    endif
+endfunction
+
+function! s:opfunc2(arg)
+    call s:opfunc(a:arg,1)
+endfunction " }}}1
+
+function! s:closematch(str) " {{{1
+    " Close an open (, {, [, or < on the command line.
+    let tail = matchstr(a:str,'.[^\[\](){}<>]*$')
+    if tail =~ '^\[.\+'
+        return "]"
+    elseif tail =~ '^(.\+'
+        return ")"
+    elseif tail =~ '^{.\+'
+        return "}"
+    elseif tail =~ '^<.+'
+        return ">"
+    else
+        return ""
+    endif
+endfunction " }}}1
+
+nnoremap <silent> <Plug>Dsurround  :<C-U>call <SID>dosurround(<SID>inputtarget())<CR>
+nnoremap <silent> <Plug>Csurround  :<C-U>call <SID>changesurround()<CR>
+nnoremap <silent> <Plug>Yssurround :<C-U>call <SID>opfunc(v:count1)<CR>
+nnoremap <silent> <Plug>YSsurround :<C-U>call <SID>opfunc2(v:count1)<CR>
+" <C-U> discards the numerical argument but there's not much we can do with it
+nnoremap <silent> <Plug>Ysurround  :<C-U>set opfunc=<SID>opfunc<CR>g@
+nnoremap <silent> <Plug>YSurround  :<C-U>set opfunc=<SID>opfunc2<CR>g@
+vnoremap <silent> <Plug>Vsurround  :<C-U>call <SID>opfunc(visualmode())<CR>
+vnoremap <silent> <Plug>VSurround  :<C-U>call <SID>opfunc2(visualmode())<CR>
+inoremap <silent> <Plug>Isurround  <C-R>=<SID>insert()<CR>
+inoremap <silent> <Plug>ISurround  <C-R>=<SID>insert(1)<CR>
+
+if !exists("g:surround_no_mappings") || ! g:surround_no_mappings
+    nmap          ds   <Plug>Dsurround
+    nmap          cs   <Plug>Csurround
+    nmap          ys   <Plug>Ysurround
+    nmap          yS   <Plug>YSurround
+    nmap          yss  <Plug>Yssurround
+    nmap          ySs  <Plug>YSsurround
+    nmap          ySS  <Plug>YSsurround
+    if !hasmapto("<Plug>Vsurround","v")
+        if exists(":xmap")
+            xmap  s    <Plug>Vsurround
+        else
+            vmap  s    <Plug>Vsurround
+        endif
+    endif
+    if !hasmapto("<Plug>VSurround","v")
+        if exists(":xmap")
+            xmap  S    <Plug>VSurround
+        else
+            vmap  S    <Plug>VSurround
+        endif
+    endif
+    if !hasmapto("<Plug>Isurround","i") && "" == mapcheck("<C-S>","i")
+        imap     <C-S> <Plug>Isurround
+    endif
+    imap        <C-G>s <Plug>Isurround
+    imap        <C-G>S <Plug>ISurround
+    "Implemented internally instead
+    "imap     <C-S><C-S> <Plug>ISurround
+endif
+
+let &cpo = s:cpo_save
+
+" vim:set ft=vim sw=4 sts=4 et:
diff --git a/plugin/svndiff.vim b/plugin/svndiff.vim
new file mode 100644 (file)
index 0000000..e32bb53
--- /dev/null
@@ -0,0 +1,336 @@
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+" svndiff (C) 2007 Ico Doornekamp
+"
+" 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.
+"
+" Introduction
+" ------------
+"
+" NOTE: This plugin is unix-only!
+"
+" An small vim 7.0 plugin for showing svn diff information in a file while
+" editing. This plugin runs a diff between the current buffer and the original
+" subversion file, and shows coloured signs indicating where the buffer
+" differs from the original file from the subversion repository. The original
+" text is not shown, only signs are used to indicate where changes were made.
+"
+" The following symbols and syntax highlight groups are used for the signs:
+"
+"   > DiffAdd:    Newly added lines. (default=blue)
+"
+"   ! DiffChange: Lines which are changed from the original. (default=cyan)
+"
+"   < DiffDel:    Applied to the lines directly above and below a deleted block
+"                 (default=magenta) 
+" Usage
+" -----
+"
+" The plugin defines one function: Svndiff(). This function figures out the
+" difference between the current buffer and it's subversion original, and adds
+" the signs at the places where the buffer differs from the original file from
+" subversion. You'll need to call this function after making changes to update
+" the highlighting.
+"
+" The function takes one argument specifying an additional action to perform:
+"
+"   "prev"  : jump to the previous different block 
+"   "next"  : jump to the next different block
+"   "clear" : clean up all signs
+"
+" You might want to map some keys to run the Svndiff function. For
+" example, add to your .vimrc:
+"
+"   noremap <F3> :call Svndiff("prev")<CR> 
+"   noremap <F4> :call Svndiff("next")<CR>
+"   noremap <F5> :call Svndiff("clear")<CR>
+"
+"
+" Configuration
+" -------------
+"
+" The following configuration variables are availabe:
+" 
+" * g:svndiff_autoupdate
+"
+"   If this variable is defined, svndiff will automatically update the signs
+"   when the user stops typing for a short while, and when leaving insert
+"   mode. This might slow things down on large files, so use with caution.
+"   The vim variable 'updatetime' can be used to set the auto-update intervar,
+"   but not that changing this variable other effects as well. (refer to the 
+"   vim docs for more info) 
+"   To use, add to your .vimrc:
+"
+"   let g:svndiff_autoupdate = 1
+"
+" * g:svndiff_one_sign_delete
+"
+"   Normally, two 'delete' signs are placed around the location where
+"   text was deleted. When this variable is defined, only one sign is
+"   placed, above the location of the deleted text.
+"   To use, add to your .vimrc:
+"
+"   let g:svndiff_one_sign_delete = 1
+"
+" Colors
+" ------
+"
+" Personally, I find the following colours more intuitive for diff colours:
+" red=deleted, green=added, yellow=changed. If you want to use these colours,
+" try adding the following lines to your .vimrc
+"
+" hi DiffAdd      ctermfg=0 ctermbg=2 guibg='green'
+" hi DiffDelete   ctermfg=0 ctermbg=1 guibg='red'
+" hi DiffChange   ctermfg=0 ctermbg=3 guibg='yellow'
+"
+" Changelog
+" ---------
+"
+" 1.0 2007-04-02       Initial version
+"
+" 1.1 2007-04-02       Added goto prev/next diffblock commands
+"
+" 1.2 2007-06-14  Updated diff arguments from -u0 (obsolete) to -U0
+"
+" 2.0 2007-08-16  Changed from syntax highlighting to using signs, thanks
+"                 to Noah Spurrier for the idea. NOTE: the name of the
+"                 function changed from Svndiff_show() to Svndiff(), so
+"                 you might need to update your .vimrc mappings!
+"
+" 3.0 2008-02-02  Redesign with some ideas from Jan Bezdekovsky. The
+"                 diff is only updated when the buffer actually changes,
+"                 cleanup of signs is now done properly and some info
+"                 about each diff block is printed in the status line.
+"
+" 3.1 2008-02-04  Fixed bug that broke plugin in non-english locales, thanks
+"                 to Bernhard Walle for the patch
+"
+" 3.2 2008-02-27       The latest rewrite broke vim 6 compatiblity. The plugin
+"                 is now simply disabled for older vim versions to avoid
+"                 a lot of warnings when loading.
+"
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+if v:version < 700
+       finish
+endif
+
+
+let s:sign_base = 200000  " Base for our sign id's, hoping to avoid colisions
+let s:is_active = {}      " dictionary with buffer names that have svndiff active
+let s:diff_signs = {}     " dict with list of ids of all signs, per file
+let s:diff_blocks = {}    " dict with list of ids of first line of each diff block, per file
+let s:changedtick = {}    " dict with changedticks of each buffer since last invocation
+
+"
+" Do the diff and update signs.
+"
+
+function s:Svndiff_update(...)
+
+       let fname = bufname("%")
+
+       if ! exists("s:is_active[fname]")
+               return 0
+       end
+
+       " Check if this file is managed by subversion, exit otherwise
+       
+       let info = system("LANG=C svn info " . fname)
+       if match(info, "Path") == -1
+               echom "Svndiff: Warning, file " . fname . " is not managed by subversion, or error running svn."
+               unlet s:is_active[fname]
+               return 0
+       end
+
+       " Check if the changedticks changed since the last invocation of this
+       " function. If nothing changed, there's no need to update the signs.
+
+       if exists("s:changedtick[fname]") && s:changedtick[fname] == b:changedtick
+               return 1
+       end
+       let s:changedtick[fname] = b:changedtick
+
+       " The diff has changed since the last time, so we need to update the signs.
+       " This is where the magic happens: pipe the current buffer contents to a
+       " shell command calculating the diff in a friendly parsable format.
+
+       let contents = join(getbufline("%", 1, "$"), "\n")
+       let diff = system("diff -U0 <(svn cat " . fname . ") <(cat;echo)", contents)
+
+       " clear the old signs
+
+       call s:Svndiff_clear()
+
+       " Parse the output of the diff command and put signs at changed, added and
+       " removed lines
+
+       for line in split(diff, '\n')
+               
+    let part = matchlist(line, '@@ -\([0-9]*\),*\([0-9]*\) +\([0-9]*\),*\([0-9]*\) @@')
+
+               if ! empty(part)
+                       let old_from  = part[1]
+                       let old_count = part[2] == '' ? 1 : part[2]
+                       let new_from  = part[3]
+                       let new_count = part[4] == '' ? 1 : part[4]
+
+                       " Figure out if text was added, removed or changed.
+                       
+                       if old_count == 0
+                               let from  = new_from
+                               let to    = new_from + new_count - 1
+                               let name  = 'svndiff_add'
+                               let info  = new_count . " lines added"
+                       elseif new_count == 0
+                               let from  = new_from
+                               let to    = new_from 
+                               let name  = 'svndiff_delete'
+                               let info  = old_count . " lines deleted"
+                               if ! exists("g:svndiff_one_sign_delete")
+                                       let to += 1
+                               endif
+                       else
+                               let from  = new_from
+                               let to    = new_from + new_count - 1
+                               let name  = 'svndiff_change'
+                               let info  = new_count . " lines changed"
+                       endif
+
+                       let id = from + s:sign_base     
+                       let s:diff_blocks[fname] += [{ 'id': id, 'info': info }]
+
+                       " Add signs to mark the changed lines 
+                       
+                       let line = from
+                       while line <= to
+                               let id = line + s:sign_base
+                               exec 'sign place ' . id . ' line=' . line . ' name=' . name . ' file=' . fname
+                               let s:diff_signs[fname] += [id]
+                               let line = line + 1
+                       endwhile
+
+               endif
+       endfor
+
+endfunction
+
+
+
+"
+" Remove all signs we placed earlier 
+"
+
+function s:Svndiff_clear(...)
+       let fname = bufname("%")
+       if exists("s:diff_signs[fname]") 
+               for id in s:diff_signs[fname]
+                       exec 'sign unplace ' . id . ' file=' . fname
+               endfor
+       end
+       let s:diff_blocks[fname] = []
+       let s:diff_signs[fname] = []
+endfunction
+
+
+"
+" Jump to previous diff block sign above the current line
+"
+
+function s:Svndiff_prev(...)
+       let fname = bufname("%")
+       let diff_blocks_reversed = reverse(copy(s:diff_blocks[fname]))
+       for block in diff_blocks_reversed
+               let line = block.id - s:sign_base
+               if line < line(".") 
+                       call setpos(".", [ 0, line, 1, 0 ])
+                       echom 'svndiff: ' . block.info
+                       return
+               endif
+       endfor
+       echom 'svndiff: no more diff blocks above cursor'
+endfunction
+
+
+"
+" Jump to next diff block sign below the current line
+"
+
+function s:Svndiff_next(...)
+       let fname = bufname("%")
+       for block in s:diff_blocks[fname]
+               let line = block.id - s:sign_base
+               if line > line(".") 
+                       call setpos(".", [ 0, line, 1, 0 ])
+                       echom 'svndiff: ' . block.info
+                       return
+               endif
+       endfor
+       echom 'svndiff: no more diff blocks below cursor'
+endfunction
+
+
+"
+" Wrapper function: Takes one argument, which is the action to perform:
+" {next|prev|clear}
+"
+
+function Svndiff(...)
+
+       let cmd = exists("a:1") ? a:1 : ''
+       let fname = bufname("%")
+       if fname == ""
+               echom "Buffer has no file name, can not do a svn diff"
+               return
+       endif
+
+       if cmd == 'clear'
+               let s:changedtick[fname] = 0
+               if exists("s:is_active[fname]") 
+                       unlet s:is_active[fname]
+               endif
+               call s:Svndiff_clear()
+       end
+       
+       if cmd == 'prev'
+               let s:is_active[fname] = 1
+               let ok = s:Svndiff_update()
+               if ok
+                       call s:Svndiff_prev()
+               endif
+       endif
+
+       if cmd == 'next'
+               let s:is_active[fname] = 1
+               let ok = s:Svndiff_update()
+               if ok
+                       call s:Svndiff_next()
+               endif
+       endif
+
+endfunction
+
+
+" Define sign characters and colors
+
+sign define svndiff_add    text=> texthl=diffAdd
+sign define svndiff_delete text=< texthl=diffDelete
+sign define svndiff_change text=! texthl=diffChange
+
+
+" Define autocmds if autoupdate is enabled
+
+if exists("g:svndiff_autoupdate")
+       autocmd CursorHold,CursorHoldI * call s:Svndiff_update()
+       autocmd InsertLeave * call s:Svndiff_update()
+endif
+
+" vi: ts=2 sw=2
+
diff --git a/plugin/vcscommand.vim b/plugin/vcscommand.vim
new file mode 100644 (file)
index 0000000..bfe8520
--- /dev/null
@@ -0,0 +1,1255 @@
+" vim600: set foldmethod=marker:
+"
+" Vim plugin to assist in working with files under control of CVS or SVN.
+"
+" Version:       Beta 26
+" Maintainer:    Bob Hiestand <[email protected]>
+" License:
+" Copyright (c) 2008 Bob Hiestand
+"
+" 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.
+"
+" Section: Documentation {{{1
+"
+" Provides functions to invoke various source control commands on the current
+" file (either the current buffer, or, in the case of an directory buffer, the
+" directory and all subdirectories associated with the current buffer).  The
+" output of the commands is captured in a new scratch window.
+"
+" This plugin needs additional extension plugins, each  specific to a source
+" control system, to function.  Those plugins should be placed in a
+" subdirectory of the standard plugin directory named 'vcscommand'.  Several
+" options include the name of the version control system in the option name.
+" Such options use the placeholder text '{VCSType}', which would be replaced
+" in actual usage with 'CVS' or 'SVN', for instance.
+"
+" Command documentation {{{2
+"
+" VCSAdd           Adds the current file to source control.
+"
+" VCSAnnotate      Displays the current file with each line annotated with the
+"                  version in which it was most recently changed.  If an
+"                  argument is given, the argument is used as a revision
+"                  number to display.  If not given an argument, it uses the
+"                  most recent version of the file on the current branch.
+"                  Additionally, if the current buffer is a VCSAnnotate buffer
+"                  already, the version number on the current line is used.
+"
+" VCSBlame         Alias for 'VCSAnnotate'.
+"
+" VCSCommit[!]     Commits changes to the current file to source control.
+"
+"                  If called with arguments, the arguments are the log message.
+"
+"                  If '!' is used, an empty log message is committed.
+"
+"                  If called with no arguments, this is a two-step command.
+"                  The first step opens a buffer to accept a log message.
+"                  When that buffer is written, it is automatically closed and
+"                  the file is committed using the information from that log
+"                  message.  The commit can be abandoned if the log message
+"                  buffer is deleted or wiped before being written.
+"
+" VCSDelete        Deletes the current file and removes it from source control.
+"
+" VCSDiff          With no arguments, this displays the differences between
+"                  the current file and its parent version under source
+"                  control in a new scratch buffer.
+"
+"                  With one argument, the diff is performed on the
+"                  current file against the specified revision.
+"
+"                  With two arguments, the diff is performed between the
+"                  specified revisions of the current file.
+"
+"                  This command uses the 'VCSCommand{VCSType}DiffOpt' variable
+"                  to specify diff options.  If that variable does not exist,
+"                  a plugin-specific default is used.  If you wish to have no
+"                  options, then set it to the empty string.
+"
+" VCSGotoOriginal  Jumps to the source buffer if the current buffer is a VCS
+"                  scratch buffer.  If VCSGotoOriginal[!] is used, remove all
+"                  VCS scratch buffers associated with the original file.
+"
+" VCSInfo          Displays extended information about the current file in a
+"                  new scratch buffer. 
+"
+" VCSLock          Locks the current file in order to prevent other users from
+"                  concurrently modifying it.  The exact semantics of this
+"                  command depend on the underlying VCS.
+"
+" VCSLog           Displays the version history of the current file in a new
+"                  scratch buffer.
+"
+" VCSRemove        Alias for 'VCSDelete'.
+"
+" VCSRevert        Replaces the modified version of the current file with the
+"                  most recent version from the repository.
+"
+" VCSReview        Displays a particular version of the current file in a new
+"                  scratch buffer.  If no argument is given, the most recent
+"                  version of the file on the current branch is retrieved.
+"
+" VCSStatus        Displays versioning information about the current file in a
+"                  new scratch buffer.
+"
+" VCSUnlock        Unlocks the current file in order to allow other users from
+"                  concurrently modifying it.  The exact semantics of this
+"                  command depend on the underlying VCS.
+"
+" VCSUpdate        Updates the current file with any relevant changes from the
+"                  repository.
+"
+" VCSVimDiff       Uses vimdiff to display differences between versions of the
+"                  current file.
+"
+"                  If no revision is specified, the most recent version of the
+"                  file on the current branch is used.  With one argument,
+"                  that argument is used as the revision as above.  With two
+"                  arguments, the differences between the two revisions is
+"                  displayed using vimdiff.
+"
+"                  With either zero or one argument, the original buffer is
+"                  used to perform the vimdiff.  When the scratch buffer is
+"                  closed, the original buffer will be returned to normal
+"                  mode.
+"
+"                  Once vimdiff mode is started using the above methods,
+"                  additional vimdiff buffers may be added by passing a single
+"                  version argument to the command.  There may be up to 4
+"                  vimdiff buffers total.
+"
+"                  Using the 2-argument form of the command resets the vimdiff
+"                  to only those 2 versions.  Additionally, invoking the
+"                  command on a different file will close the previous vimdiff
+"                  buffers.
+"
+" Mapping documentation: {{{2
+"
+" By default, a mapping is defined for each command.  User-provided mappings
+" can be used instead by mapping to <Plug>CommandName, for instance:
+"
+" nmap ,ca <Plug>VCSAdd
+"
+" The default mappings are as follow:
+"
+"   <Leader>ca VCSAdd
+"   <Leader>cn VCSAnnotate
+"   <Leader>cc VCSCommit
+"   <Leader>cD VCSDelete
+"   <Leader>cd VCSDiff
+"   <Leader>cg VCSGotoOriginal
+"   <Leader>cG VCSGotoOriginal!
+"   <Leader>ci VCSInfo
+"   <Leader>cl VCSLog
+"   <Leader>cL VCSLock
+"   <Leader>cr VCSReview
+"   <Leader>cs VCSStatus
+"   <Leader>cu VCSUpdate
+"   <Leader>cU VCSUnlock
+"   <Leader>cv VCSVimDiff
+"
+" Options documentation: {{{2
+"
+" Several variables are checked by the script to determine behavior as follow:
+"
+" VCSCommandCommitOnWrite
+"   This variable, if set to a non-zero value, causes the pending commit to
+"   take place immediately as soon as the log message buffer is written.  If
+"   set to zero, only the VCSCommit mapping will cause the pending commit to
+"   occur.  If not set, it defaults to 1.
+"
+" VCSCommandDeleteOnHide
+"   This variable, if set to a non-zero value, causes the temporary VCS result
+"   buffers to automatically delete themselves when hidden.
+"
+" VCSCommand{VCSType}DiffOpt
+"   This variable, if set, determines the options passed to the diff command
+"   of the underlying VCS.  Each VCS plugin defines a default value.
+"
+" VCSCommandDiffSplit
+"   This variable overrides the VCSCommandSplit variable, but only for buffers
+"   created with VCSVimDiff.
+"
+" VCSCommandDisableMappings
+"   This variable, if set to a non-zero value, prevents the default command
+"   mappings from being set.
+"
+" VCSCommandDisableExtensionMappings
+"   This variable, if set to a non-zero value, prevents the default command
+"   mappings from being set for commands specific to an individual VCS.
+"
+" VCSCommandEdit
+"   This variable controls whether to split the current window to display a
+"   scratch buffer ('split'), or to display it in the current buffer ('edit').
+"   If not set, it defaults to 'split'.
+"
+" VCSCommandEnableBufferSetup
+"   This variable, if set to a non-zero value, activates VCS buffer management
+"   mode.  This mode means that the buffer variable 'VCSRevision' is set if
+"   the file is VCS-controlled.  This is useful for displaying version
+"   information in the status bar.  Additional options may be set by
+"   individual VCS plugins.
+"
+" VCSCommandResultBufferNameExtension
+"   This variable, if set to a non-blank value, is appended to the name of the
+"   VCS command output buffers.  For example, '.vcs'.  Using this option may
+"   help avoid problems caused by autocommands dependent on file extension.
+"
+" VCSCommandResultBufferNameFunction
+"   This variable, if set, specifies a custom function for naming VCS command
+"   output buffers.  This function will be passed the following arguments:
+"
+"   command - name of the VCS command being executed (such as 'Log' or
+"   'Diff').
+"
+"   originalBuffer - buffer number of the source file.
+"
+"   vcsType - type of VCS controlling this file (such as 'CVS' or 'SVN').
+"
+"   statusText - extra text associated with the VCS action (such as version
+"   numbers).
+"
+" VCSCommandSplit
+"   This variable controls the orientation of the various window splits that
+"   may occur (such as with VCSVimDiff, when using a VCS command on a VCS
+"   command buffer, or when the 'VCSCommandEdit' variable is set to 'split'.
+"   If set to 'horizontal', the resulting windows will be on stacked on top of
+"   one another.  If set to 'vertical', the resulting windows will be
+"   side-by-side.  If not set, it defaults to 'horizontal' for all but
+"   VCSVimDiff windows.
+"
+" Event documentation {{{2
+"   For additional customization, VCSCommand.vim uses User event autocommand
+"   hooks.  Each event is in the VCSCommand group, and different patterns
+"   match the various hooks.
+"
+"   For instance, the following could be added to the vimrc to provide a 'q'
+"   mapping to quit a VCS scratch buffer:
+"
+"   augroup VCSCommand
+"     au VCSCommand User VCSBufferCreated silent! nmap <unique> <buffer> q :bwipeout<cr> 
+"   augroup END
+"
+"   The following hooks are available:
+"
+"   VCSBufferCreated           This event is fired just after a VCS command
+"                              output buffer is created.  It is executed
+"                              within the context of the new buffer.
+"
+"   VCSBufferSetup             This event is fired just after VCS buffer setup
+"                              occurs, if enabled.
+"
+"   VCSPluginInit              This event is fired when the VCSCommand plugin
+"                              first loads.
+"
+"   VCSPluginFinish            This event is fired just after the VCSCommand
+"                              plugin loads.
+"
+"   VCSVimDiffFinish           This event is fired just after the VCSVimDiff
+"                              command executes to allow customization of,
+"                              for instance, window placement and focus.
+"
+" Section: Plugin header {{{1
+
+" loaded_VCSCommand is set to 1 when the initialization begins, and 2 when it
+" completes.  This allows various actions to only be taken by functions after
+" system initialization.
+
+if exists('loaded_VCSCommand')
+       finish
+endif
+let loaded_VCSCommand = 1
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Event group setup {{{1
+
+augroup VCSCommand
+augroup END
+
+augroup VCSCommandCommit
+augroup END
+
+" Section: Plugin initialization {{{1
+silent do VCSCommand User VCSPluginInit
+
+" Section: Constants declaration {{{1
+
+let g:VCSCOMMAND_IDENTIFY_EXACT = 1
+let g:VCSCOMMAND_IDENTIFY_INEXACT = -1
+
+" Section: Script variable initialization {{{1
+
+" plugin-specific information:  {vcs -> [script, {command -> function}, {key -> mapping}]}
+let s:plugins = {}
+
+" temporary values of overridden configuration variables
+let s:optionOverrides = {}
+
+" state flag used to vary behavior of certain automated actions
+let s:isEditFileRunning = 0
+
+" commands needed to restore diff buffers to their original state
+unlet! s:vimDiffRestoreCmd
+
+" original buffer currently reflected in vimdiff windows
+unlet! s:vimDiffSourceBuffer
+
+" 
+unlet! s:vimDiffScratchList
+
+" Section: Utility functions {{{1
+
+" Function: s:ReportError(mapping) {{{2
+" Displays the given error in a consistent faction.  This is intended to be
+" invoked from a catch statement.
+
+function! s:ReportError(error)
+       echohl WarningMsg|echomsg 'VCSCommand:  ' . a:error|echohl None
+endfunction
+
+" Function: s:ExecuteExtensionMapping(mapping) {{{2
+" Invokes the appropriate extension mapping depending on the type of the
+" current buffer.
+
+function! s:ExecuteExtensionMapping(mapping)
+       let buffer = bufnr('%')
+       let vcsType = VCSCommandGetVCSType(buffer)
+       if !has_key(s:plugins, vcsType)
+               throw 'Unknown VCS type:  ' . vcsType
+       endif
+       if !has_key(s:plugins[vcsType][2], a:mapping)
+               throw 'This extended mapping is not defined for ' . vcsType
+       endif
+       silent execute 'normal' ':' .  s:plugins[vcsType][2][a:mapping] . "\<CR>"
+endfunction
+
+" Function: s:ExecuteVCSCommand(command, argList) {{{2
+" Calls the indicated plugin-specific VCS command on the current buffer.
+" Returns: buffer number of resulting output scratch buffer, or -1 if an error
+" occurs.
+
+function! s:ExecuteVCSCommand(command, argList)
+       try
+               let buffer = bufnr('%')
+
+               let vcsType = VCSCommandGetVCSType(buffer)
+               if !has_key(s:plugins, vcsType)
+                       throw 'Unknown VCS type:  ' . vcsType
+               endif
+
+               let originalBuffer = VCSCommandGetOriginalBuffer(buffer)
+               let bufferName = bufname(originalBuffer)
+
+               " It is already known that the directory is under VCS control.  No further
+               " checks are needed.  Otherwise, perform some basic sanity checks to avoid
+               " VCS-specific error messages from confusing things.
+               if !isdirectory(bufferName)
+                       if !filereadable(bufferName)
+                               throw 'No such file ' . bufferName
+                       endif
+               endif
+
+               let functionMap = s:plugins[vcsType][1]
+               if !has_key(functionMap, a:command)
+                       throw 'Command ''' . a:command . ''' not implemented for ' . vcsType
+               endif
+               return functionMap[a:command](a:argList)
+       catch
+               call s:ReportError(v:exception)
+               return -1
+       endtry
+endfunction
+
+" Function: s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText) {{{2
+" Default method of generating the name for VCS result buffers.  This can be
+" overridden with the VCSResultBufferNameFunction variable.
+
+function! s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText)
+       let fileName = bufname(a:originalBuffer)
+       let bufferName = a:vcsType . ' ' . a:command
+       if strlen(a:statusText) > 0
+               let bufferName .= ' ' . a:statusText
+       endif
+       let bufferName .= ' ' . fileName
+       let counter = 0
+       let versionedBufferName = bufferName
+       while buflisted(versionedBufferName)
+               let counter += 1
+               let versionedBufferName = bufferName . ' (' . counter . ')'
+       endwhile
+       return versionedBufferName
+endfunction
+
+" Function: s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText) {{{2
+" Method of generating the name for VCS result buffers that uses the original
+" file name with the VCS type and command appended as extensions.
+
+function! s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText)
+       let fileName = bufname(a:originalBuffer)
+       let bufferName = a:vcsType . ' ' . a:command
+       if strlen(a:statusText) > 0
+               let bufferName .= ' ' . a:statusText
+       endif
+       let bufferName .= ' ' . fileName . VCSCommandGetOption('VCSCommandResultBufferNameExtension', '.vcs')
+       let counter = 0
+       let versionedBufferName = bufferName
+       while buflisted(versionedBufferName)
+               let counter += 1
+               let versionedBufferName = '(' . counter . ') ' . bufferName
+       endwhile
+       return versionedBufferName
+endfunction
+
+" Function: s:EditFile(command, originalBuffer, statusText) {{{2
+" Creates a new buffer of the given name and associates it with the given
+" original buffer.
+
+function! s:EditFile(command, originalBuffer, statusText)
+       let vcsType = getbufvar(a:originalBuffer, 'VCSCommandVCSType')
+
+       let nameExtension = VCSCommandGetOption('VCSCommandResultBufferNameExtension', '')
+       if nameExtension == ''
+               let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferName')
+       else
+               let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferNameWithExtension')
+       endif
+
+       let resultBufferName = call(nameFunction, [a:command, a:originalBuffer, vcsType, a:statusText])
+
+       " Protect against useless buffer set-up
+       let s:isEditFileRunning += 1
+       try
+               let editCommand = VCSCommandGetOption('VCSCommandEdit', 'split')
+               if editCommand == 'split'
+                       if VCSCommandGetOption('VCSCommandSplit', 'horizontal') == 'horizontal'
+                               rightbelow split
+                       else
+                               vert rightbelow split
+                       endif
+               endif
+
+               enew
+
+               let b:VCSCommandCommand = a:command
+               let b:VCSCommandOriginalBuffer = a:originalBuffer
+               let b:VCSCommandSourceFile = bufname(a:originalBuffer)
+               let b:VCSCommandVCSType = vcsType
+
+               setlocal buftype=nofile
+               setlocal noswapfile
+               let &filetype = vcsType . a:command
+
+               if a:statusText != ''
+                       let b:VCSCommandStatusText = a:statusText
+               endif
+
+               if VCSCommandGetOption('VCSCommandDeleteOnHide', 0)
+                       setlocal bufhidden=delete
+               endif
+               silent noautocmd file `=resultBufferName`
+       finally
+               let s:isEditFileRunning -= 1
+       endtry
+endfunction
+
+" Function: s:SetupBuffer() {{{2
+" Attempts to set the b:VCSCommandBufferInfo variable
+
+function! s:SetupBuffer()
+       if (exists('b:VCSCommandBufferSetup') && b:VCSCommandBufferSetup)
+               " This buffer is already set up.
+               return
+       endif
+
+       if !isdirectory(@%) && (strlen(&buftype) > 0 || !filereadable(@%))
+               " No special status for special buffers other than directory buffers.
+               return
+       endif
+
+       if !VCSCommandGetOption('VCSCommandEnableBufferSetup', 0) || s:isEditFileRunning > 0
+               unlet! b:VCSCommandBufferSetup
+               return
+       endif
+
+       try
+               let vcsType = VCSCommandGetVCSType(bufnr('%'))
+               let b:VCSCommandBufferInfo = s:plugins[vcsType][1].GetBufferInfo()
+               silent do VCSCommand User VCSBufferSetup
+       catch /No suitable plugin/
+               " This is not a VCS-controlled file.
+               let b:VCSCommandBufferInfo = []
+       endtry
+
+       let b:VCSCommandBufferSetup = 1
+endfunction
+
+" Function: s:MarkOrigBufferForSetup(buffer) {{{2
+" Resets the buffer setup state of the original buffer for a given VCS scratch
+" buffer.
+" Returns:  The VCS buffer number in a passthrough mode.
+
+function! s:MarkOrigBufferForSetup(buffer)
+       checktime
+       if a:buffer > 0 
+               let origBuffer = VCSCommandGetOriginalBuffer(a:buffer)
+               " This should never not work, but I'm paranoid
+               if origBuffer != a:buffer
+                       call setbufvar(origBuffer, 'VCSCommandBufferSetup', 0)
+               endif
+       endif
+       return a:buffer
+endfunction
+
+" Function: s:OverrideOption(option, [value]) {{{2
+" Provides a temporary override for the given VCS option.  If no value is
+" passed, the override is disabled.
+
+function! s:OverrideOption(option, ...)
+       if a:0 == 0
+               call remove(s:optionOverrides[a:option], -1)
+       else
+               if !has_key(s:optionOverrides, a:option)
+                       let s:optionOverrides[a:option] = []
+               endif
+               call add(s:optionOverrides[a:option], a:1)
+       endif
+endfunction
+
+" Function: s:WipeoutCommandBuffers() {{{2
+" Clears all current VCS output buffers of the specified type for a given source.
+
+function! s:WipeoutCommandBuffers(originalBuffer, VCSCommand)
+       let buffer = 1
+       while buffer <= bufnr('$')
+               if getbufvar(buffer, 'VCSCommandOriginalBuffer') == a:originalBuffer
+                       if getbufvar(buffer, 'VCSCommandCommand') == a:VCSCommand
+                               execute 'bw' buffer
+                       endif
+               endif
+               let buffer = buffer + 1
+       endwhile
+endfunction
+
+" Function: s:VimDiffRestore(vimDiffBuff) {{{2
+" Checks whether the given buffer is one whose deletion should trigger
+" restoration of an original buffer after it was diffed.  If so, it executes
+" the appropriate setting command stored with that original buffer.
+
+function! s:VimDiffRestore(vimDiffBuff)
+       let s:isEditFileRunning += 1
+       try
+               if exists('s:vimDiffSourceBuffer')
+                       if a:vimDiffBuff == s:vimDiffSourceBuffer
+                               " Original file is being removed.
+                               unlet! s:vimDiffSourceBuffer
+                               unlet! s:vimDiffRestoreCmd
+                               unlet! s:vimDiffScratchList
+                       else
+                               let index = index(s:vimDiffScratchList, a:vimDiffBuff)
+                               if index >= 0
+                                       call remove(s:vimDiffScratchList, index)
+                                       if len(s:vimDiffScratchList) == 0
+                                               if exists('s:vimDiffRestoreCmd')
+                                                       " All scratch buffers are gone, reset the original.
+                                                       " Only restore if the source buffer is still in Diff mode
+
+                                                       let sourceWinNR = bufwinnr(s:vimDiffSourceBuffer)
+                                                       if sourceWinNR != -1
+                                                               " The buffer is visible in at least one window
+                                                               let currentWinNR = winnr()
+                                                               while winbufnr(sourceWinNR) != -1
+                                                                       if winbufnr(sourceWinNR) == s:vimDiffSourceBuffer
+                                                                               execute sourceWinNR . 'wincmd w'
+                                                                               if getwinvar(0, '&diff')
+                                                                                       execute s:vimDiffRestoreCmd
+                                                                               endif
+                                                                       endif
+                                                                       let sourceWinNR = sourceWinNR + 1
+                                                               endwhile
+                                                               execute currentWinNR . 'wincmd w'
+                                                       else
+                                                               " The buffer is hidden.  It must be visible in order to set the
+                                                               " diff option.
+                                                               let currentBufNR = bufnr('')
+                                                               execute 'hide buffer' s:vimDiffSourceBuffer
+                                                               if getwinvar(0, '&diff')
+                                                                       execute s:vimDiffRestoreCmd
+                                                               endif
+                                                               execute 'hide buffer' currentBufNR
+                                                       endif
+
+                                                       unlet s:vimDiffRestoreCmd
+                                               endif 
+                                               " All buffers are gone.
+                                               unlet s:vimDiffSourceBuffer
+                                               unlet s:vimDiffScratchList
+                                       endif
+                               endif
+                       endif
+               endif
+       finally
+               let s:isEditFileRunning -= 1
+       endtry
+endfunction
+
+" Section: Generic VCS command functions {{{1
+
+" Function: s:VCSCommit() {{{2
+function! s:VCSCommit(bang, message)
+       try
+               let vcsType = VCSCommandGetVCSType(bufnr('%'))
+               if !has_key(s:plugins, vcsType)
+                       throw 'Unknown VCS type:  ' . vcsType
+               endif
+
+               let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+
+               " Handle the commit message being specified.  If a message is supplied, it
+               " is used; if bang is supplied, an empty message is used; otherwise, the
+               " user is provided a buffer from which to edit the commit message.
+
+               if strlen(a:message) > 0 || a:bang == '!'
+                       return s:VCSFinishCommit([a:message], originalBuffer)
+               endif
+
+               call s:EditFile('commitlog', originalBuffer, '')
+               setlocal ft=vcscommit
+
+               " Create a commit mapping.
+
+               nnoremap <silent> <buffer> <Plug>VCSCommit :call <SID>VCSFinishCommitWithBuffer()<CR>
+
+               silent 0put ='VCS: ----------------------------------------------------------------------'
+               silent put ='VCS: Please enter log message.  Lines beginning with ''VCS:'' are removed automatically.'
+               silent put ='VCS: To finish the commit, Type <leader>cc (or your own <Plug>VCSCommit mapping)'
+
+               if VCSCommandGetOption('VCSCommandCommitOnWrite', 1) == 1
+                       setlocal buftype=acwrite
+                       au VCSCommandCommit BufWriteCmd <buffer> call s:VCSFinishCommitWithBuffer()
+                       silent put ='VCS: or write this buffer'
+               endif
+
+               silent put ='VCS: ----------------------------------------------------------------------'
+               $
+               setlocal nomodified
+       catch
+               call s:ReportError(v:exception)
+               return -1
+       endtry
+endfunction
+
+" Function: s:VCSFinishCommitWithBuffer() {{{2
+" Wrapper for s:VCSFinishCommit which is called only from a commit log buffer
+" which removes all lines starting with 'VCS:'.
+
+function! s:VCSFinishCommitWithBuffer()
+       setlocal nomodified
+       let currentBuffer = bufnr('%') 
+       let logMessageList = getbufline('%', 1, '$')
+       call filter(logMessageList, 'v:val !~ ''^\s*VCS:''')
+       let resultBuffer = s:VCSFinishCommit(logMessageList, b:VCSCommandOriginalBuffer)
+       if resultBuffer >= 0
+               execute 'bw' currentBuffer
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:VCSFinishCommit(logMessageList, originalBuffer) {{{2
+function! s:VCSFinishCommit(logMessageList, originalBuffer)
+       let shellSlashBak = &shellslash
+       try
+               set shellslash
+               let messageFileName = tempname()
+               call writefile(a:logMessageList, messageFileName)
+               try
+                       let resultBuffer = s:ExecuteVCSCommand('Commit', [messageFileName])
+                       if resultBuffer < 0
+                               return resultBuffer
+                       endif
+                       return s:MarkOrigBufferForSetup(resultBuffer)
+               finally
+                       call delete(messageFileName)
+               endtry
+       finally
+               let &shellslash = shellSlashBak
+       endtry
+endfunction
+
+" Function: s:VCSGotoOriginal(bang) {{{2
+function! s:VCSGotoOriginal(bang)
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       if originalBuffer > 0
+               let origWinNR = bufwinnr(originalBuffer)
+               if origWinNR == -1
+                       execute 'buffer' originalBuffer
+               else
+                       execute origWinNR . 'wincmd w'
+               endif
+               if a:bang == '!'
+                       let buffnr = 1
+                       let buffmaxnr = bufnr('$')
+                       while buffnr <= buffmaxnr
+                               if getbufvar(buffnr, 'VCSCommandOriginalBuffer') == originalBuffer
+                                       execute 'bw' buffnr
+                               endif
+                               let buffnr = buffnr + 1
+                       endwhile
+               endif
+       endif
+endfunction
+
+" Function: s:VCSVimDiff(...) {{{2
+function! s:VCSVimDiff(...)
+       try
+               let vcsType = VCSCommandGetVCSType(bufnr('%'))
+               if !has_key(s:plugins, vcsType)
+                       throw 'Unknown VCS type:  ' . vcsType
+               endif
+               let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+               let s:isEditFileRunning = s:isEditFileRunning + 1
+               try
+                       " If there's already a VimDiff'ed window, restore it.
+                       " There may only be one VCSVimDiff original window at a time.
+
+                       if exists('s:vimDiffSourceBuffer') && s:vimDiffSourceBuffer != originalBuffer
+                               " Clear the existing vimdiff setup by removing the result buffers.
+                               call s:WipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
+                       endif
+
+                       " Split and diff
+                       if(a:0 == 2)
+                               " Reset the vimdiff system, as 2 explicit versions were provided.
+                               if exists('s:vimDiffSourceBuffer')
+                                       call s:WipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
+                               endif
+                               let resultBuffer = s:plugins[vcsType][1].Review([a:1])
+                               if resultBuffer < 0
+                                       echomsg 'Can''t open revision ' . a:1
+                                       return resultBuffer
+                               endif
+                               let b:VCSCommandCommand = 'vimdiff'
+                               diffthis
+                               let s:vimDiffScratchList = [resultBuffer]
+                               " If no split method is defined, cheat, and set it to vertical.
+                               try
+                                       call s:OverrideOption('VCSCommandSplit', VCSCommandGetOption('VCSCommandDiffSplit', VCSCommandGetOption('VCSCommandSplit', 'vertical')))
+                                       let resultBuffer = s:plugins[vcsType][1].Review([a:2])
+                               finally
+                                       call s:OverrideOption('VCSCommandSplit')
+                               endtry
+                               if resultBuffer < 0
+                                       echomsg 'Can''t open revision ' . a:1
+                                       return resultBuffer
+                               endif
+                               let b:VCSCommandCommand = 'vimdiff'
+                               diffthis
+                               let s:vimDiffScratchList += [resultBuffer]
+                       else
+                               " Add new buffer
+                               call s:OverrideOption('VCSCommandEdit', 'split')
+                               try
+                                       " Force splitting behavior, otherwise why use vimdiff?
+                                       call s:OverrideOption('VCSCommandSplit', VCSCommandGetOption('VCSCommandDiffSplit', VCSCommandGetOption('VCSCommandSplit', 'vertical')))
+                                       try
+                                               if(a:0 == 0)
+                                                       let resultBuffer = s:plugins[vcsType][1].Review([])
+                                               else
+                                                       let resultBuffer = s:plugins[vcsType][1].Review([a:1])
+                                               endif
+                                       finally
+                                               call s:OverrideOption('VCSCommandSplit')
+                                       endtry
+                               finally
+                                       call s:OverrideOption('VCSCommandEdit')
+                               endtry
+                               if resultBuffer < 0
+                                       echomsg 'Can''t open current revision'
+                                       return resultBuffer
+                               endif
+                               let b:VCSCommandCommand = 'vimdiff'
+                               diffthis
+
+                               if !exists('s:vimDiffSourceBuffer')
+                                       " New instance of vimdiff.
+                                       let s:vimDiffScratchList = [resultBuffer]
+
+                                       " This could have been invoked on a VCS result buffer, not the
+                                       " original buffer.
+                                       wincmd W
+                                       execute 'buffer' originalBuffer
+                                       " Store info for later original buffer restore
+                                       let s:vimDiffRestoreCmd = 
+                                                               \    'call setbufvar('.originalBuffer.', ''&diff'', '.getbufvar(originalBuffer, '&diff').')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&foldcolumn'', '.getbufvar(originalBuffer, '&foldcolumn').')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&foldenable'', '.getbufvar(originalBuffer, '&foldenable').')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&foldmethod'', '''.getbufvar(originalBuffer, '&foldmethod').''')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&foldlevel'', '''.getbufvar(originalBuffer, '&foldlevel').''')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&scrollbind'', '.getbufvar(originalBuffer, '&scrollbind').')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&wrap'', '.getbufvar(originalBuffer, '&wrap').')'
+                                                               \ . '|if &foldmethod==''manual''|execute ''normal zE''|endif'
+                                       diffthis
+                                       wincmd w
+                               else
+                                       " Adding a window to an existing vimdiff
+                                       let s:vimDiffScratchList += [resultBuffer]
+                               endif
+                       endif
+
+                       let s:vimDiffSourceBuffer = originalBuffer
+
+                       " Avoid executing the modeline in the current buffer after the autocommand.
+
+                       let currentBuffer = bufnr('%')
+                       let saveModeline = getbufvar(currentBuffer, '&modeline')
+                       try
+                               call setbufvar(currentBuffer, '&modeline', 0)
+                               silent do VCSCommand User VCSVimDiffFinish
+                       finally
+                               call setbufvar(currentBuffer, '&modeline', saveModeline)
+                       endtry
+                       return resultBuffer
+               finally
+                       let s:isEditFileRunning = s:isEditFileRunning - 1
+               endtry
+       catch
+               call s:ReportError(v:exception)
+               return -1
+       endtry
+endfunction
+
+" Section: Public functions {{{1
+
+" Function: VCSCommandGetVCSType() {{{2
+" Sets the b:VCSCommandVCSType variable in the given buffer to the
+" appropriate source control system name.
+"
+" This uses the Identify extension function to test the buffer.  If the
+" Identify function returns VCSCOMMAND_IDENTIFY_EXACT, the match is considered
+" exact.  If the Identify function returns VCSCOMMAND_IDENTIFY_INEXACT, the
+" match is considered inexact, and is only applied if no exact match is found.
+" Multiple inexact matches is currently considered an error.
+
+function! VCSCommandGetVCSType(buffer)
+       let vcsType = getbufvar(a:buffer, 'VCSCommandVCSType')
+       if strlen(vcsType) > 0
+               return vcsType
+       endif
+       let matches = []
+       for vcsType in keys(s:plugins)
+               let identified = s:plugins[vcsType][1].Identify(a:buffer)
+               if identified
+                       if identified == g:VCSCOMMAND_IDENTIFY_EXACT
+                               let matches = [vcsType]
+                               break
+                       else
+                               let matches += [vcsType]
+                       endif
+               endif
+       endfor
+       if len(matches) == 1
+               call setbufvar(a:buffer, 'VCSCommandVCSType', matches[0])
+               return matches[0]
+       elseif len(matches) == 0
+               throw 'No suitable plugin'
+       else
+               throw 'Too many matching VCS:  ' . join(matches)
+       endif
+endfunction
+
+" Function: VCSCommandChdir(directory) {{{2
+" Changes the current directory, respecting :lcd changes.
+
+function! VCSCommandChdir(directory)
+       let command = 'cd'
+       if exists("*haslocaldir") && haslocaldir()
+               let command = 'lcd'
+       endif
+       execute command escape(a:directory, ' ')
+endfunction
+
+" Function: VCSCommandChangeToCurrentFileDir() {{{2
+" Go to the directory in which the given file is located.
+
+function! VCSCommandChangeToCurrentFileDir(fileName)
+       let oldCwd = getcwd()
+       let newCwd = fnamemodify(resolve(a:fileName), ':p:h')
+       if strlen(newCwd) > 0
+               call VCSCommandChdir(newCwd)
+       endif
+       return oldCwd
+endfunction
+
+" Function: VCSCommandGetOriginalBuffer(vcsBuffer) {{{2
+" Attempts to locate the original file to which VCS operations were applied
+" for a given buffer.
+
+function! VCSCommandGetOriginalBuffer(vcsBuffer)
+       let origBuffer = getbufvar(a:vcsBuffer, 'VCSCommandOriginalBuffer')
+       if origBuffer
+               if bufexists(origBuffer)
+                       return origBuffer
+               else
+                       " Original buffer no longer exists.
+                       throw 'Original buffer for this VCS buffer no longer exists.'
+               endif
+       else
+               " No original buffer
+               return a:vcsBuffer
+       endif
+endfunction
+
+" Function: VCSCommandRegisterModule(name, file, commandMap) {{{2
+" Allows VCS modules to register themselves.
+
+function! VCSCommandRegisterModule(name, path, commandMap, mappingMap)
+       let s:plugins[a:name] = [a:path, a:commandMap, a:mappingMap]
+       if !empty(a:mappingMap)
+                               \ && !VCSCommandGetOption('VCSCommandDisableMappings', 0)
+                               \ && !VCSCommandGetOption('VCSCommandDisableExtensionMappings', 0)
+               for mapname in keys(a:mappingMap)
+                       execute 'noremap <silent> <Leader>' . mapname ':call <SID>ExecuteExtensionMapping(''' . mapname . ''')<CR>'
+               endfor
+       endif
+endfunction
+
+" Function: VCSCommandDoCommand(cmd, cmdName, statusText, [options]) {{{2
+" General skeleton for VCS function execution.  The given command is executed
+" after appending the current buffer name (or substituting it for
+" <VCSCOMMANDFILE>, if such a token is present).  The output is captured in a
+" new buffer.
+"
+" The optional 'options' Dictionary may contain the following options:
+"      allowNonZeroExit:  if non-zero, if the underlying VCS command has a
+"              non-zero exit status, the command is still considered
+"              successfuly.  This defaults to zero.
+" Returns: name of the new command buffer containing the command results
+
+function! VCSCommandDoCommand(cmd, cmdName, statusText, options)
+       let allowNonZeroExit = 0
+       if has_key(a:options, 'allowNonZeroExit')
+               let allowNonZeroExit = a:options.allowNonZeroExit
+       endif
+
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       if originalBuffer == -1 
+               throw 'Original buffer no longer exists, aborting.'
+       endif
+
+       let path = resolve(bufname(originalBuffer))
+
+       " Work with netrw or other systems where a directory listing is displayed in
+       " a buffer.
+
+       if isdirectory(path)
+               let fileName = '.'
+       else
+               let fileName = fnamemodify(path, ':t')
+       endif
+
+       if match(a:cmd, '<VCSCOMMANDFILE>') > 0
+               let fullCmd = substitute(a:cmd, '<VCSCOMMANDFILE>', fileName, 'g')
+       else
+               let fullCmd = a:cmd . ' "' . fileName . '"'
+       endif
+
+       " Change to the directory of the current buffer.  This is done for CVS, but
+       " is left in for other systems as it does not affect them negatively.
+
+       let oldCwd = VCSCommandChangeToCurrentFileDir(path)
+       try
+               let output = system(fullCmd)
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+
+       " HACK:  if line endings in the repository have been corrupted, the output
+       " of the command will be confused.
+       let output = substitute(output, "\r", '', 'g')
+
+       if v:shell_error && !allowNonZeroExit
+               if strlen(output) == 0
+                       throw 'Version control command failed'
+               else
+                       let output = substitute(output, '\n', '  ', 'g')
+                       throw 'Version control command failed:  ' . output
+               endif
+       endif
+
+       if strlen(output) == 0
+               " Handle case of no output.  In this case, it is important to check the
+               " file status, especially since cvs edit/unedit may change the attributes
+               " of the file with no visible output.
+
+               checktime
+               return 0
+       endif
+
+       call s:EditFile(a:cmdName, originalBuffer, a:statusText)
+
+       silent 0put=output
+
+       " The last command left a blank line at the end of the buffer.  If the
+       " last line is folded (a side effect of the 'put') then the attempt to
+       " remove the blank line will kill the last fold.
+       "
+       " This could be fixed by explicitly detecting whether the last line is
+       " within a fold, but I prefer to simply unfold the result buffer altogether.
+
+       if has('folding')
+               normal zR
+       endif
+
+       $d
+       1
+
+       " Define the environment and execute user-defined hooks.
+
+       silent do VCSCommand User VCSBufferCreated
+       return bufnr('%')
+endfunction
+
+" Function: VCSCommandGetOption(name, default) {{{2
+" Grab a user-specified option to override the default provided.  Options are
+" searched in the window, buffer, then global spaces.
+
+function! VCSCommandGetOption(name, default)
+       if has_key(s:optionOverrides, a:name) && len(s:optionOverrides[a:name]) > 0
+               return s:optionOverrides[a:name][-1]
+       elseif exists('w:' . a:name)
+               return w:{a:name}
+       elseif exists('b:' . a:name)
+               return b:{a:name}
+       elseif exists('g:' . a:name)
+               return g:{a:name}
+       else
+               return a:default
+       endif
+endfunction
+
+" Function: VCSCommandDisableBufferSetup() {{{2
+" Global function for deactivating the buffer autovariables.
+
+function! VCSCommandDisableBufferSetup()
+       let g:VCSCommandEnableBufferSetup = 0
+       silent! augroup! VCSCommandPlugin
+endfunction
+
+" Function: VCSCommandEnableBufferSetup() {{{2
+" Global function for activating the buffer autovariables.
+
+function! VCSCommandEnableBufferSetup()
+       let g:VCSCommandEnableBufferSetup = 1
+       augroup VCSCommandPlugin
+               au!
+               au BufEnter * call s:SetupBuffer()
+       augroup END
+
+       " Only auto-load if the plugin is fully loaded.  This gives other plugins a
+       " chance to run.
+       if g:loaded_VCSCommand == 2
+               call s:SetupBuffer()
+       endif
+endfunction
+
+" Function: VCSCommandGetStatusLine() {{{2
+" Default (sample) status line entry for VCS-controlled files.  This is only
+" useful if VCS-managed buffer mode is on (see the VCSCommandEnableBufferSetup
+" variable for how to do this).
+
+function! VCSCommandGetStatusLine()
+       if exists('b:VCSCommandCommand')
+               " This is a result buffer.  Return nothing because the buffer name
+               " contains information already.
+               return ''
+       endif
+
+       if exists('b:VCSCommandVCSType')
+                               \ && exists('g:VCSCommandEnableBufferSetup')
+                               \ && g:VCSCommandEnableBufferSetup
+                               \ && exists('b:VCSCommandBufferInfo')
+               return '[' . join(extend([b:VCSCommandVCSType], b:VCSCommandBufferInfo), ' ') . ']'
+       else
+               return ''
+       endif
+endfunction
+
+" Section: Command definitions {{{1
+" Section: Primary commands {{{2
+com! -nargs=* VCSAdd call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Add', [<f-args>]))
+com! -nargs=* VCSAnnotate call s:ExecuteVCSCommand('Annotate', [<f-args>])
+com! -nargs=* VCSBlame call s:ExecuteVCSCommand('Annotate', [<f-args>])
+com! -nargs=? -bang VCSCommit call s:VCSCommit(<q-bang>, <q-args>)
+com! -nargs=* VCSDelete call s:ExecuteVCSCommand('Delete', [<f-args>])
+com! -nargs=* VCSDiff call s:ExecuteVCSCommand('Diff', [<f-args>])
+com! -nargs=0 -bang VCSGotoOriginal call s:VCSGotoOriginal(<q-bang>)
+com! -nargs=* VCSInfo call s:ExecuteVCSCommand('Info', [<f-args>])
+com! -nargs=* VCSLock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Lock', [<f-args>]))
+com! -nargs=* VCSLog call s:ExecuteVCSCommand('Log', [<f-args>])
+com! -nargs=* VCSRemove call s:ExecuteVCSCommand('Delete', [<f-args>])
+com! -nargs=0 VCSRevert call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Revert', []))
+com! -nargs=? VCSReview call s:ExecuteVCSCommand('Review', [<f-args>])
+com! -nargs=* VCSStatus call s:ExecuteVCSCommand('Status', [<f-args>])
+com! -nargs=* VCSUnlock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Unlock', [<f-args>]))
+com! -nargs=0 VCSUpdate call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Update', []))
+com! -nargs=* VCSVimDiff call s:VCSVimDiff(<f-args>)
+
+" Section: VCS buffer management commands {{{2
+com! VCSCommandDisableBufferSetup call VCSCommandDisableBufferSetup()
+com! VCSCommandEnableBufferSetup call VCSCommandEnableBufferSetup()
+
+" Allow reloading VCSCommand.vim
+com! VCSReload let savedPlugins = s:plugins|let s:plugins = {}|aunmenu Plugin.VCS|unlet! g:loaded_VCSCommand|runtime plugin/vcscommand.vim|for plugin in values(savedPlugins)|execute 'source' plugin[0]|endfor|unlet savedPlugins
+
+" Section: Plugin command mappings {{{1
+nnoremap <silent> <Plug>VCSAdd :VCSAdd<CR>
+nnoremap <silent> <Plug>VCSAnnotate :VCSAnnotate<CR>
+nnoremap <silent> <Plug>VCSCommit :VCSCommit<CR>
+nnoremap <silent> <Plug>VCSDelete :VCSDelete<CR>
+nnoremap <silent> <Plug>VCSDiff :VCSDiff<CR>
+nnoremap <silent> <Plug>VCSGotoOriginal :VCSGotoOriginal<CR>
+nnoremap <silent> <Plug>VCSClearAndGotoOriginal :VCSGotoOriginal!<CR>
+nnoremap <silent> <Plug>VCSInfo :VCSInfo<CR>
+nnoremap <silent> <Plug>VCSLock :VCSLock<CR>
+nnoremap <silent> <Plug>VCSLog :VCSLog<CR>
+nnoremap <silent> <Plug>VCSRevert :VCSRevert<CR>
+nnoremap <silent> <Plug>VCSReview :VCSReview<CR>
+nnoremap <silent> <Plug>VCSStatus :VCSStatus<CR>
+nnoremap <silent> <Plug>VCSUnlock :VCSUnlock<CR>
+nnoremap <silent> <Plug>VCSUpdate :VCSUpdate<CR>
+nnoremap <silent> <Plug>VCSVimDiff :VCSVimDiff<CR>
+
+" Section: Default mappings {{{1
+
+if !VCSCommandGetOption('VCSCommandDisableMappings', 0)
+       if !hasmapto('<Plug>VCSAdd')
+               nmap <unique> <Leader>ca <Plug>VCSAdd
+       endif
+       if !hasmapto('<Plug>VCSAnnotate')
+               nmap <unique> <Leader>cn <Plug>VCSAnnotate
+       endif
+       if !hasmapto('<Plug>VCSClearAndGotoOriginal')
+               nmap <unique> <Leader>cG <Plug>VCSClearAndGotoOriginal
+       endif
+       if !hasmapto('<Plug>VCSCommit')
+               nmap <unique> <Leader>cc <Plug>VCSCommit
+       endif
+       if !hasmapto('<Plug>VCSDelete')
+               nmap <unique> <Leader>cD <Plug>VCSDelete
+       endif
+       if !hasmapto('<Plug>VCSDiff')
+               nmap <unique> <Leader>cd <Plug>VCSDiff
+       endif
+       if !hasmapto('<Plug>VCSGotoOriginal')
+               nmap <unique> <Leader>cg <Plug>VCSGotoOriginal
+       endif
+       if !hasmapto('<Plug>VCSInfo')
+               nmap <unique> <Leader>ci <Plug>VCSInfo
+       endif
+       if !hasmapto('<Plug>VCSLock')
+               nmap <unique> <Leader>cL <Plug>VCSLock
+       endif
+       if !hasmapto('<Plug>VCSLog')
+               nmap <unique> <Leader>cl <Plug>VCSLog
+       endif
+       if !hasmapto('<Plug>VCSRevert')
+               nmap <unique> <Leader>cq <Plug>VCSRevert
+       endif
+       if !hasmapto('<Plug>VCSReview')
+               nmap <unique> <Leader>cr <Plug>VCSReview
+       endif
+       if !hasmapto('<Plug>VCSStatus')
+               nmap <unique> <Leader>cs <Plug>VCSStatus
+       endif
+       if !hasmapto('<Plug>VCSUnlock')
+               nmap <unique> <Leader>cU <Plug>VCSUnlock
+       endif
+       if !hasmapto('<Plug>VCSUpdate')
+               nmap <unique> <Leader>cu <Plug>VCSUpdate
+       endif
+       if !hasmapto('<Plug>VCSVimDiff')
+               nmap <unique> <Leader>cv <Plug>VCSVimDiff
+       endif
+endif
+
+" Section: Menu items {{{1
+amenu <silent> &Plugin.VCS.&Add        <Plug>VCSAdd
+amenu <silent> &Plugin.VCS.A&nnotate   <Plug>VCSAnnotate
+amenu <silent> &Plugin.VCS.&Commit     <Plug>VCSCommit
+amenu <silent> &Plugin.VCS.Delete      <Plug>VCSDelete
+amenu <silent> &Plugin.VCS.&Diff       <Plug>VCSDiff
+amenu <silent> &Plugin.VCS.&Info       <Plug>VCSInfo
+amenu <silent> &Plugin.VCS.&Log        <Plug>VCSLog
+amenu <silent> &Plugin.VCS.Revert      <Plug>VCSRevert
+amenu <silent> &Plugin.VCS.&Review     <Plug>VCSReview
+amenu <silent> &Plugin.VCS.&Status     <Plug>VCSStatus
+amenu <silent> &Plugin.VCS.&Update     <Plug>VCSUpdate
+amenu <silent> &Plugin.VCS.&VimDiff    <Plug>VCSVimDiff
+
+" Section: Autocommands to restore vimdiff state {{{1
+augroup VimDiffRestore
+       au!
+       au BufUnload * call s:VimDiffRestore(str2nr(expand('<abuf>')))
+augroup END
+
+" Section: Optional activation of buffer management {{{1
+
+if VCSCommandGetOption('VCSCommandEnableBufferSetup', 0)
+       call VCSCommandEnableBufferSetup()
+endif
+
+" Section: VIM shutdown hook {{{1
+
+" Close all result buffers when VIM exits, to prevent them from being restored
+" via viminfo.
+
+" Function: s:CloseAllResultBuffers() {{{2
+" Closes all vcscommand result buffers.
+function! s:CloseAllResultBuffers()
+       " This avoids using bufdo as that may load buffers already loaded in another
+       " vim process, resulting in an error.
+       let buffnr = 1
+       let buffmaxnr = bufnr('$')
+       while buffnr <= buffmaxnr
+               if getbufvar(buffnr, 'VCSCommandOriginalBuffer') != "" 
+                       execute 'bw' buffnr
+               endif
+               let buffnr = buffnr + 1
+       endwhile
+endfunction
+
+augroup VCSCommandVIMShutdown
+       au!
+       au VimLeavePre * call s:CloseAllResultBuffers()
+augroup END
+
+" Section: Plugin completion {{{1
+
+let loaded_VCSCommand = 2
+
+silent do VCSCommand User VCSPluginFinish
+
+let &cpo = s:save_cpo
diff --git a/plugin/vcscvs.vim b/plugin/vcscvs.vim
new file mode 100644 (file)
index 0000000..e2930e5
--- /dev/null
@@ -0,0 +1,437 @@
+" vim600: set foldmethod=marker:
+"
+" CVS extension for VCSCommand.
+"
+" Version:       VCS development
+" Maintainer:    Bob Hiestand <[email protected]>
+" License:
+" Copyright (c) 2007 Bob Hiestand
+"
+" 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.
+"
+" Section: Documentation {{{1
+"
+" Command documentation {{{2
+"
+" The following commands only apply to files under CVS source control.
+"
+" CVSEdit          Performs "cvs edit" on the current file.
+"   
+" CVSEditors       Performs "cvs editors" on the current file.
+"   
+" CVSUnedit        Performs "cvs unedit" on the current file.
+"   
+" CVSWatch         Takes an argument which must be one of [on|off|add|remove].
+"                  Performs "cvs watch" with the given argument on the current
+"                  file.
+"   
+" CVSWatchers      Performs "cvs watchers" on the current file.
+"   
+" CVSWatchAdd      Alias for "CVSWatch add"
+"   
+" CVSWatchOn       Alias for "CVSWatch on"
+"   
+" CVSWatchOff      Alias for "CVSWatch off"
+"   
+" CVSWatchRemove   Alias for "CVSWatch remove"
+"
+" Mapping documentation: {{{2
+"
+" By default, a mapping is defined for each command.  User-provided mappings
+" can be used instead by mapping to <Plug>CommandName, for instance:
+"
+" nnoremap ,ce <Plug>CVSEdit
+"
+" The default mappings are as follow:
+"
+"   <Leader>ce CVSEdit
+"   <Leader>cE CVSEditors
+"   <Leader>ct CVSUnedit
+"   <Leader>cwv CVSWatchers
+"   <Leader>cwa CVSWatchAdd
+"   <Leader>cwn CVSWatchOn
+"   <Leader>cwf CVSWatchOff
+"   <Leader>cwr CVSWatchRemove
+"
+" Options documentation: {{{2
+"
+" VCSCommandCVSExec
+"   This variable specifies the CVS executable.  If not set, it defaults to
+"   'cvs' executed from the user's executable path.
+"
+" VCSCommandCVSDiffOpt
+"   This variable, if set, determines the options passed to the cvs diff
+"   command.  If not set, it defaults to 'u'.
+
+" Section: Plugin header {{{1
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandCVSExec', 'cvs'))
+       " CVS is not installed
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:cvsFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the CVS executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+       if VCSCommandGetVCSType(expand('%')) == 'CVS'
+               let fullCmd = VCSCommandGetOption('VCSCommandCVSExec', 'cvs') . ' ' . a:cmd
+               return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+       else
+               throw 'CVS VCSCommand plugin called on non-CVS item.'
+       endif
+endfunction
+
+" Function: GetRevision() {{{2
+" Function for retrieving the current buffer's revision number.
+" Returns: Revision number or an empty string if an error occurs.
+
+function! GetRevision()
+       if !exists('b:VCSCommandBufferInfo')
+               let b:VCSCommandBufferInfo =  s:cvsFunctions.GetBufferInfo()
+       endif
+
+       if len(b:VCSCommandBufferInfo) > 0
+               return b:VCSCommandBufferInfo[0]
+       else
+               return ''
+       endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:cvsFunctions.Identify(buffer) {{{2
+function! s:cvsFunctions.Identify(buffer)
+       let fileName = resolve(bufname(a:buffer))
+       if isdirectory(fileName)
+               let directoryName = fileName
+       else
+               let directoryName = fnamemodify(fileName, ':h')
+       endif
+       if strlen(directoryName) > 0
+               let CVSRoot = directoryName . '/CVS/Root'
+       else
+               let CVSRoot = 'CVS/Root'
+       endif
+       if filereadable(CVSRoot)
+               return 1
+       else
+               return 0
+       endif
+endfunction
+
+" Function: s:cvsFunctions.Add(argList) {{{2
+function! s:cvsFunctions.Add(argList)
+       return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:cvsFunctions.Annotate(argList) {{{2
+function! s:cvsFunctions.Annotate(argList)
+       if len(a:argList) == 0
+               if &filetype == 'CVSAnnotate'
+                       " This is a CVSAnnotate buffer.  Perform annotation of the version
+                       " indicated by the current line.
+                       let caption = matchstr(getline('.'),'\v^[0-9.]+')
+
+                       if VCSCommandGetOption('VCSCommandCVSAnnotateParent', 0) != 0
+                               if caption != '1.1'
+                                       let revmaj = matchstr(caption,'\v[0-9.]+\ze\.[0-9]+')
+                                       let revmin = matchstr(caption,'\v[0-9.]+\.\zs[0-9]+') - 1
+                                       if revmin == 0
+                                               " Jump to ancestor branch
+                                               let caption = matchstr(revmaj,'\v[0-9.]+\ze\.[0-9]+')
+                                       else
+                                               let caption = revmaj . "." .  revmin
+                                       endif
+                               endif
+                       endif
+
+                       let options = ['-r' . caption]
+               else
+                       " CVS defaults to pulling HEAD, regardless of current branch.
+                       " Therefore, always pass desired revision.
+                       let caption = ''
+                       let options = ['-r' .  GetRevision()]
+               endif
+       elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+               let caption = a:argList[0]
+               let options = ['-r' . caption]
+       else
+               let caption = join(a:argList)
+               let options = a:argList
+       endif
+
+       let resultBuffer = s:DoCommand(join(['-q', 'annotate'] + options), 'annotate', caption, {})
+       if resultBuffer > 0
+               set filetype=CVSAnnotate
+               " Remove header lines from standard error
+               silent v/^\d\+\%(\.\d\+\)\+/d
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:cvsFunctions.Commit(argList) {{{2
+function! s:cvsFunctions.Commit(argList)
+       let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {})
+       if resultBuffer == 0
+               echomsg 'No commit needed.'
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:cvsFunctions.Delete() {{{2
+" By default, use the -f option to remove the file first.  If options are
+" passed in, use those instead.
+function! s:cvsFunctions.Delete(argList)
+       let options = ['-f']
+       let caption = ''
+       if len(a:argList) > 0
+               let options = a:argList
+               let caption = join(a:argList, ' ')
+       endif
+       return s:DoCommand(join(['remove'] + options, ' '), 'delete', caption, {})
+endfunction
+
+" Function: s:cvsFunctions.Diff(argList) {{{2
+function! s:cvsFunctions.Diff(argList)
+       if len(a:argList) == 0
+               let revOptions = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let revOptions = ['-r' . join(a:argList, ' -r')]
+               let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')'
+       else
+               " Pass-through
+               let caption = join(a:argList, ' ')
+               let revOptions = a:argList
+       endif
+
+       let cvsDiffOpt = VCSCommandGetOption('VCSCommandCVSDiffOpt', 'u')
+       if cvsDiffOpt == ''
+               let diffOptions = []
+       else
+               let diffOptions = ['-' . cvsDiffOpt]
+       endif
+
+       let resultBuffer = s:DoCommand(join(['diff'] + diffOptions + revOptions), 'diff', caption, {'allowNonZeroExit': 1})
+       if resultBuffer > 0
+               set filetype=diff
+       else
+               echomsg 'No differences found'
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:cvsFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.  This CVS extension adds branch name to the return
+" list as well.
+" Returns: List of results:  [revision, repository, branch]
+
+function! s:cvsFunctions.GetBufferInfo()
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       let fileName = bufname(originalBuffer)
+       if isdirectory(fileName)
+               let tag = ''
+               if filereadable(fileName . '/CVS/Tag')
+                       let tagFile = readfile(fileName . '/CVS/Tag')
+                       if len(tagFile) == 1
+                               let tag = substitute(tagFile[0], '^T', '', '')
+                       endif
+               endif
+               return [tag]
+       endif
+       let realFileName = fnamemodify(resolve(fileName), ':t')
+       if !filereadable(fileName)
+               return ['Unknown']
+       endif
+       let oldCwd = VCSCommandChangeToCurrentFileDir(fileName)
+       try
+               let statusText=system(VCSCommandGetOption('VCSCommandCVSExec', 'cvs') . ' status "' . realFileName . '"')
+               if(v:shell_error)
+                       return []
+               endif
+               let revision=substitute(statusText, '^\_.*Working revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\)\_.*$', '\1', '')
+
+               " We can still be in a CVS-controlled directory without this being a CVS
+               " file
+               if match(revision, '^New file!$') >= 0 
+                       let revision='New'
+               elseif match(revision, '^\d\+\.\d\+\%(\.\d\+\.\d\+\)*$') <0
+                       return ['Unknown']
+               endif
+
+               let branch=substitute(statusText, '^\_.*Sticky Tag:\s\+\(\d\+\%(\.\d\+\)\+\|\a[A-Za-z0-9-_]*\|(none)\).*$', '\1', '')
+               let repository=substitute(statusText, '^\_.*Repository revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\|No revision control file\)\_.*$', '\1', '')
+               let repository=substitute(repository, '^New file!\|No revision control file$', 'New', '')
+               return [revision, repository, branch]
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+endfunction
+
+" Function: s:cvsFunctions.Log() {{{2
+function! s:cvsFunctions.Log(argList)
+       if len(a:argList) == 0
+               let options = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let options = ['-r' . join(a:argList, ':')]
+               let caption = options[0]
+       else
+               " Pass-through
+               let options = a:argList
+               let caption = join(a:argList, ' ')
+       endif
+
+       let resultBuffer=s:DoCommand(join(['log'] + options), 'log', caption, {})
+       if resultBuffer > 0
+               set filetype=rcslog
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:cvsFunctions.Revert(argList) {{{2
+function! s:cvsFunctions.Revert(argList)
+       return s:DoCommand('update -C', 'revert', '', {})
+endfunction
+
+" Function: s:cvsFunctions.Review(argList) {{{2
+function! s:cvsFunctions.Review(argList)
+       if len(a:argList) == 0
+               let versiontag = '(current)'
+               let versionOption = ''
+       else
+               let versiontag = a:argList[0]
+               let versionOption = ' -r ' . versiontag . ' '
+       endif
+
+       let resultBuffer = s:DoCommand('-q update -p' . versionOption, 'review', versiontag, {})
+       if resultBuffer > 0
+               let &filetype=getbufvar(b:VCSCommandOriginalBuffer, '&filetype')
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:cvsFunctions.Status(argList) {{{2
+function! s:cvsFunctions.Status(argList)
+       return s:DoCommand(join(['status'] + a:argList, ' '), 'status', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:cvsFunctions.Update(argList) {{{2
+function! s:cvsFunctions.Update(argList)
+       return s:DoCommand('update', 'update', '', {})
+endfunction
+
+" Section: CVS-specific functions {{{1
+
+" Function: s:CVSEdit() {{{2
+function! s:CVSEdit()
+       return s:DoCommand('edit', 'cvsedit', '', {})
+endfunction
+
+" Function: s:CVSEditors() {{{2
+function! s:CVSEditors()
+       return s:DoCommand('editors', 'cvseditors', '', {})
+endfunction
+
+" Function: s:CVSUnedit() {{{2
+function! s:CVSUnedit()
+       return s:DoCommand('unedit', 'cvsunedit', '', {})
+endfunction
+
+" Function: s:CVSWatch(onoff) {{{2
+function! s:CVSWatch(onoff)
+       if a:onoff !~ '^\c\%(on\|off\|add\|remove\)$'
+               echoerr 'Argument to CVSWatch must be one of [on|off|add|remove]'
+               return -1
+       end
+       return s:DoCommand('watch ' . tolower(a:onoff), 'cvswatch', '', {})
+endfunction
+
+" Function: s:CVSWatchers() {{{2
+function! s:CVSWatchers()
+       return s:DoCommand('watchers', 'cvswatchers', '', {})
+endfunction
+
+" Section: Command definitions {{{1
+" Section: Primary commands {{{2
+com! CVSEdit call s:CVSEdit()
+com! CVSEditors call s:CVSEditors()
+com! CVSUnedit call s:CVSUnedit()
+com! -nargs=1 CVSWatch call s:CVSWatch(<f-args>)
+com! CVSWatchAdd call s:CVSWatch('add')
+com! CVSWatchOn call s:CVSWatch('on')
+com! CVSWatchOff call s:CVSWatch('off')
+com! CVSWatchRemove call s:CVSWatch('remove')
+com! CVSWatchers call s:CVSWatchers()
+
+" Section: Plugin command mappings {{{1
+
+let s:cvsExtensionMappings = {}
+let mappingInfo = [
+                       \['CVSEdit', 'CVSEdit', 'ce'],
+                       \['CVSEditors', 'CVSEditors', 'cE'],
+                       \['CVSUnedit', 'CVSUnedit', 'ct'],
+                       \['CVSWatchers', 'CVSWatchers', 'cwv'],
+                       \['CVSWatchAdd', 'CVSWatch add', 'cwa'],
+                       \['CVSWatchOff', 'CVSWatch off', 'cwf'],
+                       \['CVSWatchOn', 'CVSWatch on', 'cwn'],
+                       \['CVSWatchRemove', 'CVSWatch remove', 'cwr']
+                       \]
+
+for [pluginName, commandText, shortCut] in mappingInfo
+       execute 'nnoremap <silent> <Plug>' . pluginName . ' :' . commandText . '<CR>'
+       if !hasmapto('<Plug>' . pluginName)
+               let s:cvsExtensionMappings[shortCut] = commandText
+       endif
+endfor
+
+" Section: Menu items {{{1
+silent! aunmenu Plugin.VCS.CVS
+amenu <silent> &Plugin.VCS.CVS.&Edit       <Plug>CVSEdit
+amenu <silent> &Plugin.VCS.CVS.Ed&itors    <Plug>CVSEditors
+amenu <silent> &Plugin.VCS.CVS.Unedi&t     <Plug>CVSUnedit
+amenu <silent> &Plugin.VCS.CVS.&Watchers   <Plug>CVSWatchers
+amenu <silent> &Plugin.VCS.CVS.WatchAdd    <Plug>CVSWatchAdd
+amenu <silent> &Plugin.VCS.CVS.WatchOn     <Plug>CVSWatchOn
+amenu <silent> &Plugin.VCS.CVS.WatchOff    <Plug>CVSWatchOff
+amenu <silent> &Plugin.VCS.CVS.WatchRemove <Plug>CVSWatchRemove
+
+" Section: Plugin Registration {{{1
+call VCSCommandRegisterModule('CVS', expand('<sfile>'), s:cvsFunctions, s:cvsExtensionMappings)
+
+let &cpo = s:save_cpo
diff --git a/plugin/vcsgit.vim b/plugin/vcsgit.vim
new file mode 100644 (file)
index 0000000..6508806
--- /dev/null
@@ -0,0 +1,254 @@
+" vim600: set foldmethod=marker:
+"
+" git extension for VCSCommand.
+"
+" Version:       VCS development
+" Maintainer:    Bob Hiestand <[email protected]>
+" License:
+" Copyright (c) 2008 Bob Hiestand
+"
+" 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.
+"
+" Section: Documentation {{{1
+"
+" Options documentation: {{{2
+"
+" VCSCommandGitExec
+"   This variable specifies the git executable.  If not set, it defaults to
+"   'git' executed from the user's executable path.
+"
+" VCSCommandGitDiffOpt
+"   This variable, if set, determines the default options passed to the
+"   VCSDiff command.  If any options (starting with '-') are passed to the
+"   command, this variable is not used.
+
+" Section: Plugin header {{{1
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandGitExec', 'git'))
+       " git is not installed
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:gitFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the git executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+       if VCSCommandGetVCSType(expand('%')) == 'git'
+               let fullCmd = VCSCommandGetOption('VCSCommandGitExec', 'git',) . ' ' . a:cmd
+               return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+       else
+               throw 'git VCSCommand plugin called on non-git item.'
+       endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:gitFunctions.Identify(buffer) {{{2
+" This function only returns an inexact match due to the detection method used
+" by git, which simply traverses the directory structure upward.
+function! s:gitFunctions.Identify(buffer)
+       let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname(a:buffer)))
+       try
+               call system(VCSCommandGetOption('VCSCommandGitExec', 'git') . ' rev-parse --is-inside-work-tree')
+               if(v:shell_error)
+                       return 0
+               else
+                       return g:VCSCOMMAND_IDENTIFY_INEXACT
+               endif
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+endfunction
+
+" Function: s:gitFunctions.Add(argList) {{{2
+function! s:gitFunctions.Add(argList)
+       return s:DoCommand(join(['add'] + ['-v'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:gitFunctions.Annotate(argList) {{{2
+function! s:gitFunctions.Annotate(argList)
+       if len(a:argList) == 0
+               if &filetype == 'gitAnnotate'
+                       " Perform annotation of the version indicated by the current line.
+                       let options = matchstr(getline('.'),'^\x\+')
+               else
+                       let options = ''
+               endif
+       elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+               let options = a:argList[0]
+       else
+               let options = join(a:argList, ' ')
+       endif
+
+       let resultBuffer = s:DoCommand('blame ' . options . ' -- ', 'annotate', options, {})
+       if resultBuffer > 0
+               normal 1G
+               set filetype=gitAnnotate
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:gitFunctions.Commit(argList) {{{2
+function! s:gitFunctions.Commit(argList)
+       let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {})
+       if resultBuffer == 0
+               echomsg 'No commit needed.'
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:gitFunctions.Delete() {{{2
+" All options are passed through.
+function! s:gitFunctions.Delete(argList)
+       let options = a:argList
+       let caption = join(a:argList, ' ')
+       return s:DoCommand(join(['rm'] + options, ' '), 'delete', caption, {})
+endfunction
+
+" Function: s:gitFunctions.Diff(argList) {{{2
+" Pass-through call to git-diff.  If no options (starting with '-') are found,
+" then the options in the 'VCSCommandGitDiffOpt' variable are added.
+function! s:gitFunctions.Diff(argList)
+       let gitDiffOpt = VCSCommandGetOption('VCSCommandGitDiffOpt', '')
+       if gitDiffOpt == ''
+               let diffOptions = []
+       else
+               let diffOptions = [gitDiffOpt]
+               for arg in a:argList
+                       if arg =~ '^-'
+                               let diffOptions = []
+                               break
+                       endif
+               endfor
+       endif
+
+       let resultBuffer = s:DoCommand(join(['diff'] + diffOptions + a:argList), 'diff', join(a:argList), {})
+       if resultBuffer > 0
+               set filetype=diff
+       else
+               echomsg 'No differences found'
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:gitFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.  This CVS extension adds branch name to the return
+" list as well.
+" Returns: List of results:  [revision, repository, branch]
+
+function! s:gitFunctions.GetBufferInfo()
+       let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname('%')))
+       try
+               let branch = substitute(system(VCSCommandGetOption('VCSCommandGitExec', 'git') . ' symbolic-ref -q HEAD'), '\n$', '', '')
+               if v:shell_error
+                       let branch = 'DETACHED'
+               else
+                       let branch = substitute(branch, '^refs/heads/', '', '')
+               endif
+
+               let info = [branch]
+
+               for method in split(VCSCommandGetOption('VCSCommandGitDescribeArgList', (',tags,all,always')), ',', 1)
+                       if method != ''
+                               let method = ' --' . method
+                       endif
+                       let tag = substitute(system(VCSCommandGetOption('VCSCommandGitExec', 'git') . ' describe' . method), '\n$', '', '')
+                       if !v:shell_error
+                               call add(info, tag)
+                               break
+                       endif
+               endfor
+
+               return info
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+endfunction
+
+" Function: s:gitFunctions.Log() {{{2
+function! s:gitFunctions.Log(argList)
+       let resultBuffer=s:DoCommand(join(['log'] + a:argList), 'log', join(a:argList, ' '), {})
+       if resultBuffer > 0
+               set filetype=gitlog
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:gitFunctions.Revert(argList) {{{2
+function! s:gitFunctions.Revert(argList)
+       return s:DoCommand('checkout', 'revert', '', {})
+endfunction
+
+" Function: s:gitFunctions.Review(argList) {{{2
+function! s:gitFunctions.Review(argList)
+       if len(a:argList) == 0
+               let revision = 'HEAD'
+       else
+               let revision = a:argList[0]
+       endif
+
+       let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname(VCSCommandGetOriginalBuffer('%'))))
+       try
+               let prefix = system(VCSCommandGetOption('VCSCommandGitExec', 'git') . ' rev-parse --show-prefix')
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+
+       let prefix = substitute(prefix, '\n$', '', '')
+       let blob = '"' . revision . ':' . prefix . '<VCSCOMMANDFILE>"'
+       let resultBuffer = s:DoCommand('show ' . blob, 'review', revision, {})
+       if resultBuffer > 0
+               let &filetype=getbufvar(b:VCSCommandOriginalBuffer, '&filetype')
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:gitFunctions.Status(argList) {{{2
+function! s:gitFunctions.Status(argList)
+       return s:DoCommand(join(['status'] + a:argList), 'status', join(a:argList), {'allowNonZeroExit': 1})
+endfunction
+
+" Function: s:gitFunctions.Update(argList) {{{2
+function! s:gitFunctions.Update(argList)
+       throw "This command is not implemented for git because file-by-file update doesn't make much sense in that context.  If you have an idea for what it should do, please let me know."
+endfunction
+
+
+" Section: Plugin Registration {{{1
+call VCSCommandRegisterModule('git', expand('<sfile>'), s:gitFunctions, [])
+
+let &cpo = s:save_cpo
diff --git a/plugin/vcssvk.vim b/plugin/vcssvk.vim
new file mode 100644 (file)
index 0000000..116f953
--- /dev/null
@@ -0,0 +1,258 @@
+" vim600: set foldmethod=marker:
+"
+" SVK extension for VCSCommand.
+"
+" Version:       VCS development
+" Maintainer:    Bob Hiestand <[email protected]>
+" License:
+" Copyright (c) 2007 Bob Hiestand
+"
+" 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.
+"
+" Section: Documentation {{{1
+"
+" Options documentation: {{{2
+"
+" VCSCommandSVKExec
+"   This variable specifies the SVK executable.  If not set, it defaults to
+"   'svk' executed from the user's executable path.
+
+" Section: Plugin header {{{1
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandSVKExec', 'svk'))
+       " SVK is not installed
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:svkFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the SVK executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+       if VCSCommandGetVCSType(expand('%')) == 'SVK'
+               let fullCmd = VCSCommandGetOption('VCSCommandSVKExec', 'svk') . ' ' . a:cmd
+               return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+       else
+               throw 'SVK VCSCommand plugin called on non-SVK item.'
+       endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:svkFunctions.Identify(buffer) {{{2
+function! s:svkFunctions.Identify(buffer)
+       let fileName = resolve(bufname(a:buffer))
+       if isdirectory(fileName)
+               let directoryName = fileName
+       else
+               let directoryName = fnamemodify(fileName, ':p:h')
+       endif
+       let statusText = system(VCSCommandGetOption('VCSCommandSVKExec', 'svk') . ' info "' . directoryName . '"')
+       if(v:shell_error)
+               return 0
+       else
+               return 1
+       endif
+endfunction
+
+" Function: s:svkFunctions.Add() {{{2
+function! s:svkFunctions.Add(argList)
+       return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Annotate(argList) {{{2
+function! s:svkFunctions.Annotate(argList)
+       if len(a:argList) == 0
+               if &filetype == 'SVKAnnotate'
+                       " Perform annotation of the version indicated by the current line.
+                       let caption = matchstr(getline('.'),'\v^\s+\zs\d+')
+                       let options = ' -r' . caption
+               else
+                       let caption = ''
+                       let options = ''
+               endif
+       elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+               let caption = a:argList[0]
+               let options = ' -r' . caption
+       else
+               let caption = join(a:argList, ' ')
+               let options = ' ' . caption
+       endif
+
+       let resultBuffer = s:DoCommand('blame' . options, 'annotate', caption, {})
+       if resultBuffer > 0
+               normal 1G2dd
+               set filetype=SVKAnnotate
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:svkFunctions.Commit(argList) {{{2
+function! s:svkFunctions.Commit(argList)
+       let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {})
+       if resultBuffer == 0
+               echomsg 'No commit needed.'
+       endif
+endfunction
+
+" Function: s:svkFunctions.Delete() {{{2
+function! s:svkFunctions.Delete(argList)
+       return s:DoCommand(join(['delete'] + a:argList, ' '), 'delete', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Diff(argList) {{{2
+function! s:svkFunctions.Diff(argList)
+       if len(a:argList) == 0
+               let revOptions = [] 
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let revOptions = ['-r' . join(a:argList, ':')]
+               let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')'
+       else
+               " Pass-through
+               let caption = join(a:argList, ' ')
+               let revOptions = a:argList
+       endif
+
+       let resultBuffer = s:DoCommand(join(['diff'] + revOptions), 'diff', caption, {})
+       if resultBuffer > 0
+               set filetype=diff
+       else
+               echomsg 'No differences found'
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:svkFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.
+" Returns: List of results:  [revision, repository]
+
+function! s:svkFunctions.GetBufferInfo()
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       let fileName = resolve(bufname(originalBuffer))
+       let statusText = system(VCSCommandGetOption('VCSCommandSVKExec', 'svk') . ' status -v "' . fileName . '"')
+       if(v:shell_error)
+               return []
+       endif
+
+       " File not under SVK control.
+       if statusText =~ '^?'
+               return ['Unknown']
+       endif
+
+       let [flags, revision, repository] = matchlist(statusText, '^\(.\{3}\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s')[1:3]
+       if revision == ''
+               " Error
+               return ['Unknown']
+       elseif flags =~ '^A'
+               return ['New', 'New']
+       else
+               return [revision, repository]
+       endif
+endfunction
+
+" Function: s:svkFunctions.Info(argList) {{{2
+function! s:svkFunctions.Info(argList)
+       return s:DoCommand(join(['info'] + a:argList, ' '), 'info', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Lock(argList) {{{2
+function! s:svkFunctions.Lock(argList)
+       return s:DoCommand(join(['lock'] + a:argList, ' '), 'lock', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Log() {{{2
+function! s:svkFunctions.Log(argList)
+       if len(a:argList) == 0
+               let options = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let options = ['-r' . join(a:argList, ':')]
+               let caption = options[0]
+       else
+               " Pass-through
+               let options = a:argList
+               let caption = join(a:argList, ' ')
+       endif
+
+       let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {})
+       return resultBuffer
+endfunction
+
+" Function: s:svkFunctions.Revert(argList) {{{2
+function! s:svkFunctions.Revert(argList)
+       return s:DoCommand('revert', 'revert', '', {})
+endfunction
+
+" Function: s:svkFunctions.Review(argList) {{{2
+function! s:svkFunctions.Review(argList)
+       if len(a:argList) == 0
+               let versiontag = '(current)'
+               let versionOption = ''
+       else
+               let versiontag = a:argList[0]
+               let versionOption = ' -r ' . versiontag . ' '
+       endif
+
+       let resultBuffer = s:DoCommand('cat' . versionOption, 'review', versiontag, {})
+       if resultBuffer > 0
+               let &filetype=getbufvar(b:VCSCommandOriginalBuffer, '&filetype')
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:svkFunctions.Status(argList) {{{2
+function! s:svkFunctions.Status(argList)
+       let options = ['-v']
+       if len(a:argList) == 0
+               let options = a:argList
+       endif
+       return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Unlock(argList) {{{2
+function! s:svkFunctions.Unlock(argList)
+       return s:DoCommand(join(['unlock'] + a:argList, ' '), 'unlock', join(a:argList, ' '), {})
+endfunction
+" Function: s:svkFunctions.Update(argList) {{{2
+function! s:svkFunctions.Update(argList)
+       return s:DoCommand('update', 'update', '', {})
+endfunction
+
+" Section: Plugin Registration {{{1
+call VCSCommandRegisterModule('SVK', expand('<sfile>'), s:svkFunctions, [])
+
+let &cpo = s:save_cpo
diff --git a/plugin/vcssvn.vim b/plugin/vcssvn.vim
new file mode 100644 (file)
index 0000000..97bcbd5
--- /dev/null
@@ -0,0 +1,284 @@
+" vim600: set foldmethod=marker:
+"
+" SVN extension for VCSCommand.
+"
+" Version:       VCS development
+" Maintainer:    Bob Hiestand <[email protected]>
+" License:
+" Copyright (c) 2007 Bob Hiestand
+"
+" 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.
+"
+" Section: Documentation {{{1
+"
+" Options documentation: {{{2
+"
+" VCSCommandSVNExec
+"   This variable specifies the SVN executable.  If not set, it defaults to
+"   'svn' executed from the user's executable path.
+"
+" VCSCommandSVNDiffExt
+"   This variable, if set, sets the external diff program used by Subversion.
+"
+" VCSCommandSVNDiffOpt
+"   This variable, if set, determines the options passed to the svn diff
+"   command (such as 'u', 'w', or 'b').
+
+" Section: Plugin header {{{1
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandSVNExec', 'svn'))
+       " SVN is not installed
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:svnFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the SVN executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+       if VCSCommandGetVCSType(expand('%')) == 'SVN'
+               let fullCmd = VCSCommandGetOption('VCSCommandSVNExec', 'svn') . ' ' . a:cmd
+               return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+       else
+               throw 'SVN VCSCommand plugin called on non-SVN item.'
+       endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:svnFunctions.Identify(buffer) {{{2
+function! s:svnFunctions.Identify(buffer)
+       let fileName = resolve(bufname(a:buffer))
+       if isdirectory(fileName)
+               let directoryName = fileName
+       else
+               let directoryName = fnamemodify(fileName, ':h')
+       endif
+       if strlen(directoryName) > 0
+               let svnDir = directoryName . '/.svn'
+       else
+               let svnDir = '.svn'
+       endif
+       if isdirectory(svnDir)
+               return 1
+       else
+               return 0
+       endif
+endfunction
+
+" Function: s:svnFunctions.Add() {{{2
+function! s:svnFunctions.Add(argList)
+       return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Annotate(argList) {{{2
+function! s:svnFunctions.Annotate(argList)
+       if len(a:argList) == 0
+               if &filetype == 'SVNAnnotate'
+                       " Perform annotation of the version indicated by the current line.
+                       let caption = matchstr(getline('.'),'\v^\s+\zs\d+')
+                       let options = ' -r' . caption
+               else
+                       let caption = ''
+                       let options = ''
+               endif
+       elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+               let caption = a:argList[0]
+               let options = ' -r' . caption
+       else
+               let caption = join(a:argList, ' ')
+               let options = ' ' . caption
+       endif
+
+       let resultBuffer = s:DoCommand('blame' . options, 'annotate', caption, {})
+       if resultBuffer > 0
+               set filetype=SVNAnnotate
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:svnFunctions.Commit(argList) {{{2
+function! s:svnFunctions.Commit(argList)
+       let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {})
+       if resultBuffer == 0
+               echomsg 'No commit needed.'
+       endif
+endfunction
+
+" Function: s:svnFunctions.Delete() {{{2
+function! s:svnFunctions.Delete(argList)
+       return s:DoCommand(join(['delete'] + a:argList, ' '), 'delete', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Diff(argList) {{{2
+function! s:svnFunctions.Diff(argList)
+       if len(a:argList) == 0
+               let revOptions = [] 
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let revOptions = ['-r' . join(a:argList, ':')]
+               let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')'
+       else
+               " Pass-through
+               let caption = join(a:argList, ' ')
+               let revOptions = a:argList
+       endif
+
+       let svnDiffExt = VCSCommandGetOption('VCSCommandSVNDiffExt', '')
+       if svnDiffExt == ''
+               let diffExt = []
+       else
+               let diffExt = ['--diff-cmd ' . svnDiffExt]
+       endif
+
+       let svnDiffOpt = VCSCommandGetOption('VCSCommandSVNDiffOpt', '')
+       if svnDiffOpt == ''
+               let diffOptions = []
+       else
+               let diffOptions = ['-x -' . svnDiffOpt]
+       endif
+
+       let resultBuffer = s:DoCommand(join(['diff'] + diffExt + diffOptions + revOptions), 'diff', caption, {})
+       if resultBuffer > 0
+               set filetype=diff
+       else
+               if svnDiffExt == ''
+                       echomsg 'No differences found'
+               endif
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:svnFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.
+" Returns: List of results:  [revision, repository, branch]
+
+function! s:svnFunctions.GetBufferInfo()
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       let fileName = bufname(originalBuffer)
+       let statusText = system(VCSCommandGetOption('VCSCommandSVNExec', 'svn') . ' status -vu "' . fileName . '"')
+       if(v:shell_error)
+               return []
+       endif
+
+       " File not under SVN control.
+       if statusText =~ '^?'
+               return ['Unknown']
+       endif
+
+       let [flags, revision, repository] = matchlist(statusText, '^\(.\{8}\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s')[1:3]
+       if revision == ''
+               " Error
+               return ['Unknown']
+       elseif flags =~ '^A'
+               return ['New', 'New']
+       else
+               return [revision, repository]
+       endif
+endfunction
+
+" Function: s:svnFunctions.Info(argList) {{{2
+function! s:svnFunctions.Info(argList)
+       return s:DoCommand(join(['info'] + a:argList, ' '), 'info', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Lock(argList) {{{2
+function! s:svnFunctions.Lock(argList)
+       return s:DoCommand(join(['lock'] + a:argList, ' '), 'lock', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Log(argList) {{{2
+function! s:svnFunctions.Log(argList)
+       if len(a:argList) == 0
+               let options = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let options = ['-r' . join(a:argList, ':')]
+               let caption = options[0]
+       else
+               " Pass-through
+               let options = a:argList
+               let caption = join(a:argList, ' ')
+       endif
+
+       let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {})
+       return resultBuffer
+endfunction
+
+" Function: s:svnFunctions.Revert(argList) {{{2
+function! s:svnFunctions.Revert(argList)
+       return s:DoCommand('revert', 'revert', '', {})
+endfunction
+
+" Function: s:svnFunctions.Review(argList) {{{2
+function! s:svnFunctions.Review(argList)
+       if len(a:argList) == 0
+               let versiontag = '(current)'
+               let versionOption = ''
+       else
+               let versiontag = a:argList[0]
+               let versionOption = ' -r ' . versiontag . ' '
+       endif
+
+       let resultBuffer = s:DoCommand('cat' . versionOption, 'review', versiontag, {})
+       if resultBuffer > 0
+               let &filetype = getbufvar(b:VCSCommandOriginalBuffer, '&filetype')
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:svnFunctions.Status(argList) {{{2
+function! s:svnFunctions.Status(argList)
+       let options = ['-u', '-v']
+       if len(a:argList) == 0
+               let options = a:argList
+       endif
+       return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Unlock(argList) {{{2
+function! s:svnFunctions.Unlock(argList)
+       return s:DoCommand(join(['unlock'] + a:argList, ' '), 'unlock', join(a:argList, ' '), {})
+endfunction
+" Function: s:svnFunctions.Update(argList) {{{2
+function! s:svnFunctions.Update(argList)
+       return s:DoCommand('update', 'update', '', {})
+endfunction
+
+" Section: Plugin Registration {{{1
+call VCSCommandRegisterModule('SVN', expand('<sfile>'), s:svnFunctions, [])
+
+let &cpo = s:save_cpo
diff --git a/syntax/CVSAnnotate.vim b/syntax/CVSAnnotate.vim
new file mode 100644 (file)
index 0000000..4af5809
--- /dev/null
@@ -0,0 +1,45 @@
+" Vim syntax file
+" Language:    CVS annotate output
+" Maintainer:  Bob Hiestand <[email protected]>
+" Remark:      Used by the cvscommand plugin.  Originally written by Mathieu
+" Clabaut
+" License:
+" Copyright (c) 2007 Bob Hiestand
+"
+" 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 version < 600
+       syntax clear
+elseif exists("b:current_syntax")
+       finish
+endif
+
+syn match cvsDate      /\d\d-...-\d\d/                 contained
+syn match cvsName      /(\S* /hs=s+1,he=e-1            contained nextgroup=cvsDate
+syn match cvsVer       /^\d\+\(\.\d\+\)\+/             contained nextgroup=cvsName
+syn region cvsHead     start="^\d\+\.\d\+" end="):"    contains=cvsVer,cvsName,cvsDate
+
+if !exists("did_cvsannotate_syntax_inits")
+       let did_cvsannotate_syntax_inits = 1
+       hi link cvsDate         Comment
+       hi link cvsName Type
+       hi link cvsVer  Statement
+endif
+
+let b:current_syntax="CVSAnnotate"
diff --git a/syntax/SVKAnnotate.vim b/syntax/SVKAnnotate.vim
new file mode 100644 (file)
index 0000000..3c53c3c
--- /dev/null
@@ -0,0 +1,42 @@
+" Vim syntax file
+" Language:    SVK annotate output
+" Maintainer:  Bob Hiestand <[email protected]>
+" Remark:      Used by the vcscommand plugin.
+" License:
+" Copyright (c) 2007 Bob Hiestand
+"
+" 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("b:current_syntax")
+       finish
+endif
+
+syn match svkDate /\d\{4}-\d\{1,2}-\d\{1,2}/ skipwhite contained
+syn match svkName /(\s*\zs\S\+/ contained nextgroup=svkDate skipwhite
+syn match svkVer /^\s*\d\+/ contained nextgroup=svkName skipwhite
+syn region svkHead start=/^/ end="):" contains=svkVer,svkName,svkDate oneline
+
+if !exists("did_svkannotate_syntax_inits")
+       let did_svkannotate_syntax_inits = 1
+       hi link svkName Type
+       hi link svkDate Comment
+       hi link svkVer Statement
+endif
+
+let b:current_syntax="svkAnnotate"
diff --git a/syntax/SVNAnnotate.vim b/syntax/SVNAnnotate.vim
new file mode 100644 (file)
index 0000000..d46f771
--- /dev/null
@@ -0,0 +1,40 @@
+" Vim syntax file
+" Language:    SVN annotate output
+" Maintainer:  Bob Hiestand <[email protected]>
+" Remark:      Used by the vcscommand plugin.
+" License:
+" Copyright (c) 2007 Bob Hiestand
+"
+" 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("b:current_syntax")
+       finish
+endif
+
+syn match svnName /\S\+/ contained
+syn match svnVer /^\s\+\zs\d\+/ contained nextgroup=svnName skipwhite
+syn match svnHead /^\s\+\d\+\s\+\S\+/ contains=svnVer,svnName
+
+if !exists("did_svnannotate_syntax_inits")
+       let did_svnannotate_syntax_inits = 1
+       hi link svnName Type
+       hi link svnVer Statement
+endif
+
+let b:current_syntax="svnAnnotate"
diff --git a/syntax/vcscommit.vim b/syntax/vcscommit.vim
new file mode 100644 (file)
index 0000000..0cd4c5e
--- /dev/null
@@ -0,0 +1,31 @@
+" Vim syntax file
+" Language:    VCS commit file
+" Maintainer:  Bob Hiestand ([email protected])
+" License:
+" Copyright (c) 2007 Bob Hiestand
+"
+" 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("b:current_syntax")
+       finish
+endif
+
+syntax region vcsComment start="^VCS: " end="$"
+highlight link vcsComment Comment
+let b:current_syntax = "vcscommit"