Added Perforce plugin.
authorIain Patterson <me@iain.cx>
Thu, 27 Nov 2008 15:28:57 +0000 (15:28 +0000)
committerIain Patterson <me@iain.cx>
Thu, 27 Nov 2008 15:28:57 +0000 (15:28 +0000)
Added gvim titlestring.
Fixed mapping typo.

git-svn-id: https://svn.cambridge.iain.cx/profile/trunk@156 6be0d1a5-5cfe-0310-89b6-964be062b18b

16 files changed:
.vim/autoload/genutils.vim [new file with mode: 0755]
.vim/autoload/perforce.vim [new file with mode: 0755]
.vim/autoload/perforceutils.vim [new file with mode: 0755]
.vim/doc/perforce.txt [new file with mode: 0755]
.vim/ftplugin/perforce.vim [new file with mode: 0755]
.vim/ftplugin/selectbuf_perforce.vim [new file with mode: 0755]
.vim/perforce/bakup.sh [new file with mode: 0755]
.vim/perforce/p4Utils.sh [new file with mode: 0755]
.vim/perforce/perforcebugrep.vim [new file with mode: 0755]
.vim/perforce/perforcemenu.vim [new file with mode: 0755]
.vim/perforce/perforceutils.vim [new file with mode: 0755]
.vim/perforce/restor.sh [new file with mode: 0755]
.vim/plugin/genutils.vim [new file with mode: 0755]
.vim/plugin/perforce.vim [new file with mode: 0755]
.vim/syntax/perforce.vim [new file with mode: 0755]
.vimrc

diff --git a/.vim/autoload/genutils.vim b/.vim/autoload/genutils.vim
new file mode 100755 (executable)
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 <fisherbb@bc.edu>
+" 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/.vim/autoload/perforce.vim b/.vim/autoload/perforce.vim
new file mode 100755 (executable)
index 0000000..460ef9c
--- /dev/null
@@ -0,0 +1,3715 @@
+" perforce.vim: Please see plugin/perforce.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
+
+
+""" BEGIN: Initializations {{{
+
+" Determine the script id.
+function! s:MyScriptId()
+  map <SID>xx <SID>xx
+  let s:sid = maparg("<SID>xx")
+  unmap <SID>xx
+  return substitute(s:sid, "xx$", "", "")
+endfunction
+let s:myScriptId = s:MyScriptId()
+delfunction s:MyScriptId " This is not needed anymore.
+
+""" BEGIN: One-time initialization of some script variables {{{
+let s:lastMsg = ''
+let s:lastMsgGrp = 'None'
+" Indicates the current recursion level for executing p4 commands.
+let s:recLevel = 0
+
+if genutils#OnMS() && match(&shell, '\<bash\>') != -1
+  " When using cygwin bash with native vim, p4 gets confused by the PWD, which
+  "   is in cygwin style.
+  let s:p4CommandPrefix = "unset PWD && "
+else
+  let s:p4CommandPrefix = ""
+endif
+
+" Special characters in a filename that are not acceptable in a filename (as a
+"   window title) on windows.
+let s:specialChars = '\([*:?"<>|]\)' 
+let s:specialCharsMap = {
+      \   '*': 'S',
+      \   ':': 'C',
+      \   '?': 'Q',
+      \   '"': 'D',
+      \   '<': 'L',
+      \   '>': 'G',
+      \   '|': 'P',
+      \ }
+
+"
+" A lot of metadata on perforce command syntax and handling.
+"
+
+let s:p4KnownCmds = split('add,admin,annotate,branch,branches,change,changes,' .
+      \ 'client,clients,counter,counters,delete,depot,depots,describe,diff,' .
+      \ 'diff2,dirs,edit,filelog,files,fix,fixes,flush,fstat,get,group,' .
+      \ 'groups,have,help,info,integ,integrate,integrated,job,jobs,jobspec,' .
+      \ 'label,labels,labelsync,lock,logger,login,monitor,obliterate,opened,' .
+      \ 'passwd,print,protect,rename,reopen,resolve,resolved,revert,review,' .
+      \ 'reviews,set,submit,sync,triggers,typemap,unlock,user,users,verify,' .
+      \ 'where,workspaces,', ',')
+" Add some built-in commands to this list.
+let s:builtinCmds = split('vdiff,vdiff2,exec,', ',')
+let s:allCommands = s:p4KnownCmds + s:builtinCmds
+let s:p4KnownCmdsCompStr = ''
+
+" Map between the option and the commands that reqire us to pass an argument
+"   with this option.
+let s:p4OptCmdMap = {}
+let s:p4OptCmdMap['b'] = split('diff2,integrate', ',')
+let s:p4OptCmdMap['c'] = split('add,delete,edit,fix,fstat,integrate,lock,' .
+      \ 'opened,reopen,r[ver],review,reviews,submit,unlock', ',')
+let s:p4OptCmdMap['e'] = ['jobs']
+let s:p4OptCmdMap['j'] = ['fixes']
+let s:p4OptCmdMap['l'] = ['labelsync']
+let s:p4OptCmdMap['m'] = split('changes,filelog,jobs', ',')
+let s:p4OptCmdMap['o'] = ['print']
+let s:p4OptCmdMap['s'] = split('changes,integrate', ',')
+let s:p4OptCmdMap['t'] = split('add,client,edit,label,reopen', ',')
+let s:p4OptCmdMap['O'] = ['passwd']
+let s:p4OptCmdMap['P'] = ['passwd']
+let s:p4OptCmdMap['S'] = ['set']
+
+" These built-in options require us to pass an argument. These options start
+"   with a '+'.
+let s:biOptCmdMap = {}
+let s:biOptCmdMap['c'] = ['diff']
+
+" Map the commands with short name to their long versions.
+let s:shortCmdMap = {}
+let s:shortCmdMap['p'] = 'print'
+let s:shortCmdMap['d'] = 'diff'
+let s:shortCmdMap['e'] = 'edit'
+let s:shortCmdMap['a'] = 'add'
+let s:shortCmdMap['r'] = 'revert'
+let s:shortCmdMap['g'] = 'get'
+let s:shortCmdMap['o'] = 'open'
+let s:shortCmdMap['d2'] = 'diff2'
+let s:shortCmdMap['h'] = 'help'
+
+
+" NOTE: The current file is used as the default argument, only when the
+"   command is not one of the s:askUserCmds and it is not one of
+"   s:curFileNotDefCmds or s:nofileArgsCmds.
+" For these commands, we don't need to default to the current file, as these
+"   commands can work without any arguments.
+let s:curFileNotDefCmds = split('change,changes,client,files,integrate,job,' .
+      \ 'jobs,jobspec,labels,labelsync,opened,resolve,submit,user,', ',')
+" For these commands, we need to ask user for the argument, as we can't assume
+"   the current file is the default.
+let s:askUserCmds = split('admin,branch,counter,depot,fix,group,label,', ',')
+" A subset of askUserCmds, that should use a more generic prompt.
+let s:genericPromptCmds = split('admin,counter,fix,', ',')
+" Commands that essentially display a list of files.
+let s:filelistCmds = split('files,have,integrate,opened,', ',')
+" Commands that work with a spec.
+let s:specCmds = split('branch,change,client,depot,group,job,jobspec,label,' .
+      \ 'protect,submit,triggers,typemap,user,', ',')
+" Out of the above specCmds, these are the only commands that don't
+"   support '-o' option. Consequently we have to have our own template.
+let s:noOutputCmds = ['submit']
+" The following are used only to create a specification, not to view them.
+"   Consequently, they don't accept a '-d' option to delete the spec.
+let s:specOnlyCmds = split('jobspec,submit,', ',')
+" These commands might change the fstat of files, requiring an update on some
+"   or all the buffers loaded into vim.
+"let s:statusUpdReqCmds = 'add,delete,edit,get,lock,reopen,revert,sync,unlock,'
+"" For these commands we need to call :checktime, as the command might have
+""   changed the state of the file.
+"let s:checktimeReqCmds = 'edit,get,reopen,revert,sync,'
+" For these commands, we can even set 'autoread' along with doing a :checktime.
+let s:autoreadCmds = split('edit,get,reopen,revert,sync,', ',')
+" These commands don't expect filename arguments, so no special processing for
+"   file expansion.
+let s:nofileArgsCmds = split('branch,branches,change,client,clients,counters,' .
+      \ 'depot,depots,describe,dirs,group,groups,help,info,job,jobspec,label,' .
+      \ 'logger,passwd,protect,rename,review,triggers,typemap,user,users,', ',')
+" For these commands, the output should not be set to perforce type.
+let s:ftNotPerforceCmds = split('diff,diff2,print,vdiff,vdiff2', ',')
+" Allows navigation keys in the command window.
+let s:navigateCmds = ['help']
+" These commands accept a '-m' argument to limit the list size.
+let s:limitListCmds = split('filelog,jobs,changes,', ',')
+" These commands take the diff option -dx.
+let s:diffCmds = split('describe,diff,diff2,', ',')
+" The following commands prefer dialog output. If the output exceeds
+"   g:p4MaxLinesInDialog, we should switch to showing the output in a window.
+let s:dlgOutputCmds =
+      \ split('add,delete,edit,get,lock,reopen,revert,sync,unlock,', ',')
+
+" If there is a confirm message, then PFIF() will prompt user before
+"   continuing with the run.
+let s:confirmMsgs{'revert'} = "Reverting file(s) will overwrite any edits to " .
+      \ "the files(s)\n Do you want to continue?"
+let s:confirmMsgs{'submit'} = "This will commit the changelist to the depot." .
+      \ "\n Do you want to continue?"
+
+" Settings that are not directly exposed to the user. These can be accessed
+"   using the public API.
+" Refresh the contents of perforce windows, even if the window is already open.
+let s:refreshWindowsAlways = 1
+
+" List of the global variable names of the user configurable settings.
+let s:settings = split('ClientRoot,CmdPath,Presets,' .
+      \ 'DefaultOptions,DefaultDiffOptions,EnableMenu,EnablePopupMenu,' .
+      \ 'UseExpandedMenu,UseExpandedPopupMenu,EnableRuler,RulerWidth,' .
+      \ 'DefaultListSize,EnableActiveStatus,OptimizeActiveStatus,' .
+      \ 'ASIgnoreDefPattern,ASIgnoreUsrPattern,PromptToCheckout,' .
+      \ 'CheckOutDefault,UseGUIDialogs,MaxLinesInDialog,SortSettings,' .
+      \ 'TempDir,SplitCommand,UseVimDiff2,EnableFileChangedShell,' .
+      \ 'BufHidden,Depot,Autoread,UseClientViewMap,DefaultPreset', ',')
+let s:settingsCompStr = ''
+
+let s:helpWinName = 'P4\ help'
+
+" Unprotected space.
+let s:SPACE_AS_SEP = genutils#CrUnProtectedCharsPattern(' ')
+let s:EMPTY_STR = '^\_s*$'
+
+if !exists('s:p4Client') || s:p4Client =~# s:EMPTY_STR
+  let s:p4Client = $P4CLIENT
+endif
+if !exists('s:p4User') || s:p4User =~# s:EMPTY_STR
+  if exists("$P4USER") && $P4USER !~# s:EMPTY_STR
+    let s:p4User = $P4USER
+  elseif genutils#OnMS() && exists("$USERNAME")
+    let s:p4User = $USERNAME
+  elseif exists("$LOGNAME")
+    let s:p4User = $LOGNAME
+  elseif exists("$USERNAME") " Happens if you are on cygwin too.
+    let s:p4User = $USERNAME
+  else
+    let s:p4User = ''
+  endif
+endif
+if !exists('s:p4Port') || s:p4Port =~# s:EMPTY_STR
+  let s:p4Port = $P4PORT
+endif
+let s:p4Password = $P4PASSWD
+
+let s:CM_RUN = 'run' | let s:CM_FILTER = 'filter' | let s:CM_DISPLAY = 'display'
+let s:CM_PIPE = 'pipe'
+
+let s:changesExpr  = "matchstr(getline(\".\"), '" .
+      \ '^Change \zs\d\+\ze ' . "')"
+let s:branchesExpr = "matchstr(getline(\".\"), '" .
+      \ '^Branch \zs[^ ]\+\ze ' . "')"
+let s:labelsExpr   = "matchstr(getline(\".\"), '" .
+      \ '^Label \zs[^ ]\+\ze ' . "')"
+let s:clientsExpr  = "matchstr(getline(\".\"), '" .
+      \ '^Client \zs[^ ]\+\ze ' . "')"
+let s:usersExpr    = "matchstr(getline(\".\"), '" .
+      \ '^[^ ]\+\ze <[^@>]\+@[^>]\+> ([^)]\+)' . "')"
+let s:jobsExpr     = "matchstr(getline(\".\"), '" .
+      \ '^[^ ]\+\ze on ' . "')"
+let s:depotsExpr   = "matchstr(getline(\".\"), '" .
+      \ '^Depot \zs[^ ]\+\ze ' . "')"
+let s:describeExpr = 's:DescribeGetCurrentItem()'
+let s:filelogExpr  = 's:GetCurrentDepotFile(line("."))'
+let s:groupsExpr   = 'expand("<cword>")'
+
+let s:fileBrowseExpr = 's:ConvertToLocalPath(s:GetCurrentDepotFile(line(".")))'
+let s:openedExpr   = s:fileBrowseExpr
+let s:filesExpr    = s:fileBrowseExpr
+let s:haveExpr     = s:fileBrowseExpr
+let s:integrateExpr = s:fileBrowseExpr
+" Open in describe window should open the local file.
+let s:describeOpenItemExpr = s:fileBrowseExpr
+
+" If an explicit handler is defined, then it will override the default rule of
+"   finding the command with the singular form.
+let s:filelogItemHandler = "s:printHdlr"
+let s:changesItemHandler = "s:changeHdlr"
+let s:openedItemHandler = "s:OpenFile"
+let s:describeItemHandler = "s:OpenFile"
+let s:filesItemHandler = "s:OpenFile"
+let s:haveItemHandler = "s:OpenFile"
+
+" Define handlers for built-in commands. These have no arguments, they will
+"   use the existing parsed command-line vars. Set s:errCode on errors.
+let s:builtinCmdHandler{'vdiff'} = 's:VDiffHandler' 
+let s:builtinCmdHandler{'vdiff2'} = 's:VDiff2Handler' 
+let s:builtinCmdHandler{'exec'} = 's:ExecHandler' 
+
+let s:vdiffCounter = 0
+
+" A stack of contexts.
+let s:p4Contexts = []
+
+" Cache of client view mappings, with client name as the key.
+let s:fromDepotMapping = {}
+let s:toDepotMapping = {}
+
+aug Perforce | aug END " Define autocommand group.
+call genutils#AddToFCShellPre('perforce#FileChangedShell')
+
+""" END: One-time initialization of some script variables }}}
+
+""" END: Initializations }}}
+
+
+""" BEGIN: Command specific functions {{{
+
+function! s:printHdlr(scriptOrigin, outputType, ...)
+  let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'print']
+        \ +a:000)
+
+  if s:StartBufSetup()
+    let undo = 0
+    " The first line printed by p4 for non-q operation causes vim to misjudge
+    " the filetype.
+    if getline(1) =~# '//[^#]\+#\d\+ - '
+      setlocal modifiable
+      let firstLine = getline(1)
+      silent! 1delete _
+    endif
+
+    set ft=
+    doautocmd filetypedetect BufNewFile
+    " If automatic detection doesn't work...
+    if &ft == ""
+      let &ft=s:GuessFileTypeForCurrentWindow()
+    endif
+
+    if exists('firstLine')
+      silent! 1put! =firstLine
+      setlocal nomodifiable
+    endif
+
+    call s:EndBufSetup()
+  endif
+  return retVal
+endfunction
+
+function! s:describeHdlr(scriptOrigin, outputType, ...)
+  if !a:scriptOrigin
+    call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'describe']+a:000)
+  endif
+  " If -s doesn't exist, and user doesn't intent to see a diff, then let us
+  "   add -s option. In any case he can press enter on the <SHOW DIFFS> to see
+  "   it later.
+  if index(s:p4CmdOptions, '-s') == -1 &&
+        \ s:indexMatching(s:p4CmdOptions, '^-d.\+$') == -1
+    call add(s:p4CmdOptions, '-s')
+    let s:p4WinName = s:MakeWindowName() " Adjust window name.
+  endif
+
+  let retVal = perforce#PFIF(2, a:outputType, 'describe')
+  if s:StartBufSetup() && getline(1) !~# ' - no such changelist'
+    call s:SetupFileBrowse()
+    if index(s:p4CmdOptions, '-s') != -1
+      setlocal modifiable
+      silent! 2,$g/^Change \d\+ by \|\%$/
+            \ call append(line('.')-1, ['', "\t<SHOW DIFFS>", ''])
+      setlocal nomodifiable
+    else
+      call s:SetupDiff()
+    endif
+
+    call s:EndBufSetup()
+  endif
+  return retVal
+endfunction
+
+function! s:diffHdlr(scriptOrigin, outputType, ...)
+  if !a:scriptOrigin
+    call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'diff']+a:000)
+  endif
+
+  " If a change number is specified in the diff, we need to handle it
+  "   ourselves, as p4 doesn't understand this.
+  let changeOptIdx = index(s:p4CmdOptions, '++c')
+  let changeNo = ''
+  if changeOptIdx != -1 " If a change no. is specified.
+    let changeNo = s:p4CmdOptions[changeOptIdx+1]
+    call s:PushP4Context()
+    try
+      call extend(s:p4Options, ['++T', '++N'], 0) " Testmode.
+      let retVal = perforce#PFIF(2, a:outputType, 'diff') " Opens window.
+      if s:errCode == 0
+        setlocal modifiable
+        exec '%PF ++f opened -c' changeNo
+      endif
+    finally
+      let cntxtStr = s:PopP4Context()
+    endtry
+  else
+    " Any + option is treated like a signal to run external diff.
+    let externalDiffOptExists = (s:indexMatching(s:p4CmdOptions, '^+\S\+$') != -1)
+    if externalDiffOptExists
+      if len(s:p4Arguments) > 1
+        return s:SyntaxError('External diff options can not be used with multiple files.')
+      endif
+      let needsPop = 0
+      try
+        let _p4Options = copy(s:p4Options)
+        call insert(s:p4Options, '++T', 0) " Testmode, just open the window.
+        let retVal = perforce#PFIF(2, 0, 'diff')
+        let s:p4Options = _p4Options
+        if s:errCode != 0
+          return
+        endif
+        call s:PushP4Context() | let needsPop = 1
+        PW print -q
+        if s:errCode == 0
+          setlocal modifiable
+          let fileName = s:ConvertToLocalPath(s:p4Arguments[0])
+          call s:PeekP4Context()
+          " Gather and process only external options.
+          " Sample:
+          " '-x +width=10 -du -y +U=20 -z -a -db +tabsize=4'
+          "   to
+          " '--width=10 -U 20 --tabsize=4'
+          let diffOpts = []
+          for opt in s:p4CmdOptions
+            if opt =~ '^+'
+              call add(diffOpts, substitute(opt, '^+\([^= ]\+\)=\(.*\)$',
+                    \ '\=(strlen(submatch(1)) > 1 ? '.
+                    \     '("--".submatch(1).'.
+                    \      '(submatch(2) != "" ? "=".submatch(2) : "")) : '.
+                    \     '("-".submatch(1).'.
+                    \      '(submatch(2) != "" ? " ".submatch(2) : "")))',
+                    \ 'g'))
+            endif
+          endfor
+          if getbufvar(bufnr('#'), '&ff') ==# 'dos'
+            setlocal ff=dos
+          endif
+          silent! exec '%!'.
+                \ genutils#EscapeCommand('diff', diffOpts+['--', '-', fileName],
+                \ '')
+          if v:shell_error > 1
+            call s:EchoMessage('Error executing external diff command. '.
+                  \ 'Verify that GNU (or a compatible) diff is in your path.',
+                  \ 'ERROR')
+            return ''
+          endif
+          call genutils#SilentSubstitute("\<CR>$", '%s///')
+          call genutils#SilentSubstitute('^--- -', '1s;;--- '.
+                \ s:ConvertToDepotPath(fileName))
+          1
+        endif
+      finally
+        setlocal nomodifiable
+        if needsPop
+          call s:PopP4Context()
+        endif
+      endtry
+    else
+      let retVal = perforce#PFIF(2, exists('$P4DIFF') ? 5 : a:outputType, 'diff')
+    endif
+  endif
+
+  if s:StartBufSetup()
+    call s:SetupDiff()
+
+    if changeNo != '' && getline(1) !~# 'ile(s) not opened on this client\.'
+      setl modifiable
+      call genutils#SilentSubstitute('#.*', '%s///e')
+      call s:SetP4ContextVars(cntxtStr) " Restore original diff context.
+      call perforce#PFIF(1, 0, '-x', '-', '++f', '++n', 'diff')
+      setl nomodifiable
+    endif
+
+    call s:EndBufSetup()
+  endif
+  return retVal
+endfunction
+
+function! s:diff2Hdlr(scriptOrigin, outputType, ...)
+  if !a:scriptOrigin
+    call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'diff2']+a:000)
+  endif
+
+  let s:p4Arguments = s:GetDiff2Args()
+
+  let retVal = perforce#PFIF(2, exists('$P4DIFF') ? 5 : a:outputType, 'diff2')
+  if s:StartBufSetup()
+    call s:SetupDiff()
+
+    call s:EndBufSetup()
+  endif
+  return retVal
+endfunction
+
+function! s:passwdHdlr(scriptOrigin, outputType, ...)
+  if !a:scriptOrigin
+    call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'passwd']+a:000)
+  endif
+
+  let oldPasswd = ""
+  if index(s:p4CmdOptions, '-O') == -1
+    let oldPasswd = input('Enter old password: ')
+    " FIXME: Handle empty passwords.
+    call add(add(s:p4CmdOptions, '-O'), oldPasswd)
+  endif
+  let newPasswd = ""
+  if index(s:p4CmdOptions, '-P') == -1
+    while 1
+      let newPasswd = input('Enter new password: ')
+      if (input('Re-enter new password: ') != newPasswd)
+        call s:EchoMessage("Passwords don't match", 'Error')
+      else
+        " FIXME: Handle empty passwords.
+        call add(add(s:p4CmdOptions, '-P'), newPasswd)
+        break
+      endif
+    endwhile
+  endif
+  let retVal = perforce#PFIF(2, a:outputType, 'passwd')
+  return retVal
+endfunction
+
+" Only to avoid confirming for -n and -a options.
+function! s:revertHdlr(scriptOrigin, outputType, ...)
+  if !a:scriptOrigin
+    call call('s:ParseOptionsIF', [1, line('$'), 1,
+          \ a:outputType, 'passwd']+a:000)
+  endif
+
+  if index(s:p4CmdOptions, '-n') != -1 || index(s:p4CmdOptions, '-a') != -1
+    call add(s:p4Options, '++y')
+  endif
+  let retVal = perforce#PFIF(2, a:outputType, 'revert')
+  return retVal
+endfunction
+
+function! s:changeHdlrImpl(outputType)
+  let _p4Arguments = s:p4Arguments
+  " If argument(s) is not a number...
+  if len(s:p4Arguments) != 0 && s:indexMatching(s:p4Arguments, '^\d\+$') == -1
+    let s:p4Arguments = [] " Let a new changelist be created.
+  endif
+  let retVal = perforce#PFIF(2, a:outputType, 'change')
+  let s:p4Arguments = _p4Arguments
+  if s:errCode == 0 && s:indexMatching(s:p4Arguments, '^\d\+$') == -1
+        \ && (s:StartBufSetup() || s:commandMode ==# s:CM_FILTER)
+    if len(s:p4Arguments) != 0
+      if search('^Files:\s*$', 'w') && line('.') != line('$')
+        +
+        call s:PushP4Context()
+        try
+          call call('perforce#PFrangeIF', [line("."), line("$"), 1, 0]+
+                \ s:p4Options+['++f', 'opened', '-c', 'default']+
+                \ s:p4Arguments)
+        finally
+          call s:PopP4Context()
+        endtry
+
+        if s:errCode == 0
+          call genutils#SilentSubstitute('^', '.,$s//\t/e')
+          call genutils#SilentSubstitute('#\d\+ - \(\S\+\) .*$',
+                \ '.,$s//\t# \1/e')
+        endif
+      endif
+    endif
+
+    call s:EndBufSetup()
+    setl nomodified
+    if len(s:p4Arguments) != 0 && &cmdheight > 1
+      " The message about W and WQ must have gone by now.
+      redraw | call perforce#LastMessage()
+    endif
+  else
+    " Save the filelist in this changelist so that we can update their status
+    " later.
+    if search('Files:\s*$', 'w')
+      let b:p4OrgFilelist = getline(line('.')+1, line('$'))
+    endif
+  endif
+  return retVal
+endfunction
+
+function! s:changeHdlr(scriptOrigin, outputType, ...)
+  if !a:scriptOrigin
+    call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'change']+a:000)
+  endif
+  let retVal = s:changeHdlrImpl(a:outputType)
+  if s:StartBufSetup()
+    command! -buffer -nargs=* PChangeSubmit :call call('<SID>W',
+          \ [0]+b:p4Options+['submit']+split(<q-args>, '\s'))
+
+    call s:EndBufSetup()
+  endif
+  return retVal
+endfunction
+
+" Create a template for submit.
+function! s:submitHdlr(scriptOrigin, outputType, ...)
+  if !a:scriptOrigin
+    call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'submit']+a:000)
+  endif
+
+  if index(s:p4CmdOptions, '-c') != -1
+    " Non-interactive.
+    let retVal = perforce#PFIF(2, a:outputType, 'submit')
+  else
+    call s:PushP4Context()
+    try
+      " This is done just to get the :W and :WQ commands defined properly and
+      " open the window with a proper name. The actual job is done by the call
+      " to s:changeHdlrImpl() which is then run in filter mode to avoid the
+      " side-effects (such as :W and :WQ getting overwritten etc.)
+      call extend(s:p4Options, ['++y', '++T'], 0) " Don't confirm, and testmode.
+      call perforce#PFIF(2, 0, 'submit')
+      if s:errCode == 0
+        call s:PeekP4Context()
+        let s:p4CmdOptions = [] " These must be specific to 'submit'.
+        let s:p4Command = 'change'
+        let s:commandMode = s:CM_FILTER | let s:filterRange = '.'
+        let retVal = s:changeHdlrImpl(a:outputType)
+        setlocal nomodified
+        if s:errCode != 0
+          return
+        endif
+       endif
+    finally
+      call s:PopP4Context()
+    endtry
+
+    if s:StartBufSetup()
+      command! -buffer -nargs=* PSubmitPostpone :call call('<SID>W',
+            \ [0]+b:p4Options+['change']+split(<q-args>, '\s'))
+      set ft=perforce " Just to get the cursor placement right.
+      call s:EndBufSetup()
+    endif
+
+    if s:errCode
+      call s:EchoMessage("Error creating submission template.", 'Error')
+    endif
+  endif
+  return s:errCode
+endfunction
+
+function! s:resolveHdlr(scriptOrigin, outputType, ...)
+  if !a:scriptOrigin
+    call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'resolve']+a:000)
+  endif
+
+  if (s:indexMatching(s:p4CmdOptions, '^-a[fmsty]$') == -1) &&
+        \ (index(s:p4CmdOptions, '-n') == -1)
+    return s:SyntaxError("Interactive resolve not implemented (yet).")
+  endif
+  let retVal = perforce#PFIF(2, a:outputType, 'resolve')
+  return retVal
+endfunction
+
+function! s:filelogHdlr(scriptOrigin, outputType, ...)
+  let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'filelog']+a:000)
+
+  if s:StartBufSetup()
+    " No meaning for delete.
+    silent! nunmap <buffer> D
+    silent! delcommand PItemDelete
+    command! -range -buffer -nargs=0 PFilelogDiff
+          \ :call s:FilelogDiff2(<line1>, <line2>)
+    vnoremap <silent> <buffer> D :PFilelogDiff<CR>
+    command! -buffer -nargs=0 PFilelogPrint :call perforce#PFIF(0, 0, 'print',
+          \ <SID>GetCurrentItem())
+    nnoremap <silent> <buffer> p :PFilelogPrint<CR>
+    command! -buffer -nargs=0 PFilelogSync :call <SID>FilelogSyncToCurrentItem()
+    nnoremap <silent> <buffer> S :PFilelogSync<CR>
+    command! -buffer -nargs=0 PFilelogDescribe
+          \ :call <SID>FilelogDescribeChange()
+    nnoremap <silent> <buffer> C :PFilelogDescribe<CR>
+
+    call s:EndBufSetup()
+  endif
+endfunction
+
+function! s:clientsHdlr(scriptOrigin, outputType, ...)
+  let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'clients']+a:000)
+
+  if s:StartBufSetup()
+    command! -buffer -nargs=0 PClientsTemplate
+          \ :call perforce#PFIF(0, 0, '++A', 'client', '-t', <SID>GetCurrentItem())
+    nnoremap <silent> <buffer> P :PClientsTemplate<CR>
+
+    call s:EndBufSetup()
+  endif
+  return retVal
+endfunction
+
+function! s:changesHdlr(scriptOrigin, outputType, ...)
+  let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'changes']+a:000)
+
+  if s:StartBufSetup()
+    command! -buffer -nargs=0 PItemDescribe
+          \ :call <SID>PChangesDescribeCurrentItem()
+    command! -buffer -nargs=0 PChangesSubmit
+          \ :call <SID>ChangesSubmitChangeList()
+    nnoremap <silent> <buffer> S :PChangesSubmit<CR>
+    command! -buffer -nargs=0 PChangesOpened
+          \ :if getline('.') =~# " \\*pending\\* '" |
+          \    call perforce#PFIF(1, 0, 'opened', '-c', <SID>GetCurrentItem()) |
+          \  endif
+    nnoremap <silent> <buffer> o :PChangesOpened<CR>
+    command! -buffer -nargs=0 PChangesDiff
+          \ :if getline('.') =~# " \\*pending\\* '" |
+          \    call perforce#PFIF(0, 0, 'diff', '++c', <SID>GetCurrentItem()) |
+          \  else |
+          \    call perforce#PFIF(0, 0, 'describe', (<SID>('DefaultDiffOptions')
+          \                 =~ '^\s*$' ? '-dd' : <SID>('DefaultDiffOptions')),
+          \                   <SID>GetCurrentItem()) |
+          \  endif
+    nnoremap <silent> <buffer> d :PChangesDiff<CR>
+    command! -buffer -nargs=0 PItemOpen
+          \ :if getline('.') =~# " \\*pending\\* '" |
+          \    call perforce#PFIF(0, 0, 'change', <SID>GetCurrentItem()) |
+          \  else |
+          \    call perforce#PFIF(0, 0, 'describe', '-dd', <SID>GetCurrentItem()) |
+          \  endif
+
+    call s:EndBufSetup()
+  endif
+endfunction
+
+function! s:labelsHdlr(scriptOrigin, outputType, ...)
+  let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'labels']+a:000)
+
+  if s:StartBufSetup()
+    command! -buffer -nargs=0 PLabelsSyncClient
+          \ :call <SID>LabelsSyncClientToLabel()
+    nnoremap <silent> <buffer> S :PLabelsSyncClient<CR>
+    command! -buffer -nargs=0 PLabelsSyncLabel
+          \ :call <SID>LabelsSyncLabelToClient()
+    nnoremap <silent> <buffer> C :PLabelsSyncLabel<CR>
+    command! -buffer -nargs=0 PLabelsFiles :call perforce#PFIF(0, 0, '++n', 'files',
+          \ '//...@'. <SID>GetCurrentItem())
+    nnoremap <silent> <buffer> I :PLabelsFiles<CR>
+    command! -buffer -nargs=0 PLabelsTemplate :call perforce#PFIF(0, 0, '++A',
+          \ 'label', '-t', <SID>GetCurrentItem())
+    nnoremap <silent> <buffer> P :PLabelsTemplate<CR>
+
+    call s:EndBufSetup()
+  endif
+  return retVal
+endfunction
+
+function! s:helpHdlr(scriptOrigin, outputType, ...)
+  call genutils#SaveWindowSettings2("PerforceHelp", 1)
+  " If there is a help window already open, then we need to reuse it.
+  let helpWin = bufwinnr(s:helpWinName)
+  let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'help']+a:000)
+
+  if s:StartBufSetup()
+    command! -buffer -nargs=0 PHelpSelect
+          \ :call perforce#PFIF(0, 0, 'help', expand("<cword>"))
+    nnoremap <silent> <buffer> <CR> :PHelpSelect<CR>
+    nnoremap <silent> <buffer> K :PHelpSelect<CR>
+    nnoremap <silent> <buffer> <2-LeftMouse> :PHelpSelect<CR>
+    call genutils#AddNotifyWindowClose(s:helpWinName, s:myScriptId .
+          \ "RestoreWindows")
+    if helpWin == -1 " Resize only when it was not already visible.
+      exec "resize " . 20
+    endif
+    redraw | echo
+          \ "Press <CR>/K/<2-LeftMouse> to drilldown on perforce help keywords."
+
+    call s:EndBufSetup()
+  endif
+  return retVal
+endfunction
+
+" Built-in command handlers {{{
+function! s:VDiffHandler()
+  let nArgs = len(s:p4Arguments)
+  if nArgs > 2
+    return s:SyntaxError("vdiff: Too many arguments.")
+  endif
+
+  let firstFile = ''
+  let secondFile = ''
+  if nArgs == 2
+    let firstFile = s:p4Arguments[0]
+    let secondFile = s:p4Arguments[1]
+  elseif nArgs == 1
+    let secondFile = s:p4Arguments[0]
+  else
+    let secondFile = s:EscapeFileName(s:GetCurFileName())
+  endif
+  if firstFile == ''
+    let firstFile = s:ConvertToDepotPath(secondFile)
+  endif
+  call s:VDiffImpl(firstFile, secondFile, 0)
+endfunction
+
+function! s:VDiff2Handler()
+  if len(s:p4Arguments) > 2
+    return s:SyntaxError("vdiff2: Too many arguments")
+  endif
+
+  let s:p4Arguments = s:GetDiff2Args()
+  call s:VDiffImpl(s:p4Arguments[0], s:p4Arguments[1], 1)
+endfunction
+
+function! s:VDiffImpl(firstFile, secondFile, preferDepotPaths)
+  let firstFile = a:firstFile
+  let secondFile = a:secondFile
+
+  if a:preferDepotPaths || s:PathRefersToDepot(firstFile)
+    let firstFile = s:ConvertToDepotPath(firstFile)
+    let tempFile1 = s:MakeTempName(firstFile)
+  else
+    let tempFile1 = firstFile
+  endif
+  if a:preferDepotPaths || s:PathRefersToDepot(secondFile)
+    let secondFile = s:ConvertToDepotPath(secondFile)
+    let tempFile2 = s:MakeTempName(secondFile)
+  else
+    let tempFile2 = secondFile
+  endif
+  if firstFile =~# s:EMPTY_STR || secondFile =~# s:EMPTY_STR ||
+        \ (tempFile1 ==# tempFile2)
+    return s:SyntaxError("diff requires two distinct files as arguments.")
+  endif
+
+  let s:vdiffCounter = s:vdiffCounter + 1
+
+  if s:IsDepotPath(firstFile)
+    let s:p4Command = 'print'
+    let s:p4CmdOptions = ['-q']
+    let s:p4WinName = tempFile1
+    let s:p4Arguments = [firstFile]
+    call perforce#PFIF(2, 0, 'print')
+    if s:errCode != 0
+      return
+    endif
+  else
+    let v:errmsg = ''
+    silent! exec 'split' firstFile
+    if v:errmsg != ""
+      return s:ShowVimError("Error opening file: ".firstFile."\n".v:errmsg, '')
+    endif
+  endif
+  diffthis
+  let w:p4VDiffWindow = s:vdiffCounter
+  wincmd K
+
+  " CAUTION: If there is a buffer or window local value, then this will get
+  " overridden, but it is OK.
+  if exists('t:p4SplitCommand')
+    let _splitCommand = t:p4SplitCommand
+  endif
+  let t:p4SplitCommand = 'vsplit'
+  let _splitright = &splitright
+  set splitright
+  try
+    if s:IsDepotPath(secondFile)
+      let s:p4Command = 'print'
+      let s:p4CmdOptions = ['-q']
+      let s:p4WinName = tempFile2
+      let s:p4Arguments = [secondFile]
+      call perforce#PFIF(2, 0, 'print')
+      if s:errCode != 0
+        return
+      endif
+    else
+      let v:errmsg = ''
+      silent! exec 'vsplit' secondFile
+      if v:errmsg != ""
+        return s:ShowVimError("Error opening file: ".secondFile."\n".v:errmsg, '')
+      endif
+    endif
+  finally
+    if exists('_splitCommand')
+      let t:p4SplitCommand = _splitCommand
+    else
+      unlet t:p4SplitCommand
+    endif
+    let &splitright = _splitright
+  endtry
+  diffthis
+  let w:p4VDiffWindow = s:vdiffCounter
+  wincmd _
+endfunction
+
+" Returns a fileName in the temp directory that is unique for the branch and
+"   revision specified in the fileName.
+function! s:MakeTempName(filePath)
+  let depotPath = s:ConvertToDepotPath(a:filePath)
+  if depotPath =~# s:EMPTY_STR
+    return ''
+  endif
+  let tmpName = s:_('TempDir') . '/'
+  let branch = s:GetBranchName(depotPath)
+  if branch !~# s:EMPTY_STR
+    let tmpName = tmpName . branch . '-'
+  endif
+  let revSpec = s:GetRevisionSpecifier(depotPath)
+  if revSpec !~# s:EMPTY_STR
+    let tmpName = tmpName . substitute(strpart(revSpec, 1), '/', '_', 'g') . '-'
+  endif
+  return tmpName . fnamemodify(substitute(a:filePath, '\\*#\d\+$', '', ''),
+        \ ':t')
+endfunction
+
+function! s:ExecHandler()
+  if len(s:p4Arguments) != 0
+    echo join(s:p4Arguments, ' ')
+    let cmdHasBang = 0
+    if s:p4Arguments[0] =~# '^!'
+      let cmdHasBang = 1
+      " FIXME: Pipe itself needs to be escaped, and they could be chained.
+      let cmd = genutils#EscapeCommand(substitute(s:p4Arguments[0], '^!', '',
+            \ ''), s:p4Arguments[1:], s:p4Pipe)
+    else
+      let cmd = join(s:p4Arguments, ' ')
+    endif
+    let cmd = genutils#Escape(cmd, '#%!')
+    try
+      exec (cmdHasBang ? '!' : '').cmd
+    catch
+      let v:errmsg = substitute(v:exception, '^[^:]\+:', '', '')
+      call s:ShowVimError(v:errmsg, v:throwpoint)
+    endtry
+  endif
+endfunction
+
+" Built-in command handlers }}}
+
+""" END: Command specific functions }}}
+
+
+""" BEGIN: Helper functions {{{
+
+" Open a file from an alternative codeline.
+" If mode == 0, first file is opened and all other files are added to buffer
+"   list.
+" If mode == 1, the files are not really opened, the list is just returned.
+" If mode == 2, it behaves the same as mode == 0, except that the file is
+"   split opened.
+" If there are no arguments passed, user is prompted to enter. He can then
+"   enter a codeline followed by a list of filenames.
+" If only one argument is passed, it is assumed to be the codeline and the
+"   current filename is assumed (user is not prompted).
+function! perforce#PFOpenAltFile(mode, ...) " {{{
+  let argList = copy(a:000)
+  if a:0 < 2
+    if a:0 == 0
+      " Prompt for codeline string (codeline optionally followed by filenames).
+      let codelineStr = s:PromptFor(0, s:_('UseGUIDialogs'),
+            \ "Enter the alternative codeline string: ", '')
+      if codelineStr =~# s:EMPTY_STR
+        return ""
+      endif
+      let argList = split(codelineStr, s:SPACE_AS_SEP)
+    endif
+    if len(argList) == 1
+      call add(argList, s:EscapeFileName(s:GetCurFileName()))
+    endif
+  endif
+
+  let altFileNames = call('s:PFGetAltFiles', ['']+argList)
+  if a:mode == 0 || a:mode == 2
+    let firstFile = 1
+    for altFileName in altFileNames
+      if firstFile
+        execute ((a:mode == 0) ? ":edit " : ":split ") . altFileName
+        let firstFile = 0
+      else
+        execute ":badd " . altFileName
+      endif
+    endfor
+  else
+    return join(altFileNames, ' ')
+  endif
+endfunction " }}}
+
+" Interactively change the port/client/user. {{{
+function! perforce#SwitchPortClientUser()
+  let p4Port = s:PromptFor(0, s:_('UseGUIDialogs'), "Port: ", s:_('p4Port'))
+  let p4Client = s:PromptFor(0, s:_('UseGUIDialogs'), "Client: ", s:_('p4Client'))
+  let p4User = s:PromptFor(0, s:_('UseGUIDialogs'), "User: ", s:_('p4User'))
+  call perforce#PFSwitch(1, p4Port, p4Client, p4User)
+endfunction
+
+" No args: Print presets and prompt user to select a preset.
+" Number: Select that numbered preset.
+" port [client] [user]: Set the specified settings.
+function! perforce#PFSwitch(updateClientRoot, ...)
+  if a:0 == 0 || match(a:1, '^\d\+$') == 0
+    let selPreset = ''
+    let presets = split(s:_('Presets'), ',')
+    if a:0 == 0
+      if len(presets) == 0
+        call s:EchoMessage("No presets to select from.", 'Error')
+        return
+      endif
+
+      let selPreset = genutils#PromptForElement(presets, -1,
+            \ "Select the setting: ", -1, s:_('UseGUIDialogs'), 1)
+    else
+      let index = a:1 + 0
+      if index >= len(presets)
+        call s:EchoMessage("Not that many presets.", 'Error')
+        return
+      endif
+      let selPreset = presets[index]
+    endif
+    if selPreset == ''
+      return
+    endif
+    let argList = split(selPreset, s:SPACE_AS_SEP)
+  else
+    if a:0 == 1
+      let argList = split(a:1, ' ')
+    else
+      let argList = a:000
+    endif
+  endif
+  call call('s:PSwitchHelper', [a:updateClientRoot]+argList)
+
+  " Loop through all the buffers and invalidate the filestatuses.
+  let lastBufNr = bufnr('$')
+  let i = 1
+  while i <= lastBufNr
+    if bufexists(i) && getbufvar(i, '&buftype') == ''
+      call s:ResetFileStatusForBuffer(i)
+    endif
+    let i = i + 1
+  endwhile
+endfunction
+
+function! s:PSwitchHelper(updateClientRoot, ...)
+  let p4Port = a:1
+  let p4Client = s:_('p4Client')
+  let p4User = s:_('p4User')
+  if a:0 > 1
+    let p4Client = a:2
+  endif
+  if a:0 > 2
+    let p4User = a:3
+  endif
+  if ! s:SetPortClientUser(p4Port, p4Client, p4User)
+    return
+  endif
+
+  if a:updateClientRoot
+    if s:p4Port !=# 'P4CONFIG'
+      call s:GetClientInfo()
+    else
+      let g:p4ClientRoot = '' " Since the client is chosen dynamically.
+    endif
+  endif
+endfunction
+
+function! s:SetPortClientUser(port, client, user)
+  if s:p4Port ==# a:port && s:p4Client ==# a:client && s:p4User ==# a:user
+    return 0
+  endif
+
+  let s:p4Port = a:port
+  let s:p4Client = a:client
+  let s:p4User = a:user
+  let s:p4Password = ''
+  return 1
+endfunction
+
+function! perforce#PFSwitchComplete(ArgLead, CmdLine, CursorPos)
+  return substitute(s:_('Presets'), ',', "\n", 'g')
+endfunction
+" port/client/user }}}
+
+function! s:PHelpComplete(ArgLead, CmdLine, CursorPos)
+  if s:p4KnownCmdsCompStr == ''
+    let s:p4KnownCmdsCompStr = join(s:p4KnownCmds, "\n")
+  endif
+  return s:p4KnownCmdsCompStr.
+          \ "simple\ncommands\nenvironment\nfiletypes\njobview\nrevisions\n".
+          \ "usage\nviews\n"
+endfunction
+" Handler for opened command.
+function! s:OpenFile(scriptOrigin, outputType, fileName) " {{{
+  if filereadable(a:fileName)
+    if a:outputType == 0
+      let curWin = winnr()
+      let bufNr = genutils#FindBufferForName(a:fileName)
+      let winnr = bufwinnr(bufNr)
+      if winnr != -1
+        exec winnr.'wincmd w'
+      else
+        wincmd p
+      endif
+      if curWin != winnr() && &previewwindow
+        wincmd p " Don't use preview window.
+      endif
+      " Avoid loosing temporary buffers accidentally.
+      if winnr() == curWin || getbufvar('%', '&bufhidden') != ''
+        split
+      endif
+      if winbufnr(winnr()) != bufNr
+        if bufNr != -1
+          exec "buffer" bufNr | " Preserves cursor position.
+        else
+          exec "edit " . a:fileName
+        endif
+      endif
+    else
+      exec "pedit " . a:fileName
+    endif
+  else
+    call perforce#PFIF(0, a:outputType, 'print', a:fileName)
+  endif
+endfunction " }}}
+
+function! s:DescribeGetCurrentItem() " {{{
+  if getline(".") ==# "\t<SHOW DIFFS>"
+    let [changeHdrLine, col] = searchpos('^Change \zs\d\+ by ', 'bnW')
+    if changeHdrLine != 0
+      let changeNo = matchstr(getline(changeHdrLine), '\d\+', col-1)
+      let _modifiable = &l:modifiable
+      try
+        setlocal modifiable
+        call genutils#SaveHardPosition('DescribeGetCurrentItem')
+        exec changeHdrLine.',.PF ++f describe' s:_('DefaultDiffOptions') changeNo
+        call genutils#RestoreHardPosition('DescribeGetCurrentItem')
+        call genutils#ResetHardPosition('DescribeGetCurrentItem')
+      finally
+        let &l:modifiable = _modifiable
+      endtry
+      call s:SetupDiff()
+    endif
+    return ""
+  endif
+  return s:GetCurrentDepotFile(line('.'))
+endfunction " }}}
+
+function! s:getCommandItemHandler(outputType, command, args) " {{{
+  let itemHandler = ""
+  if exists("s:{a:command}ItemHandler")
+    let itemHandler = s:{a:command}ItemHandler
+  elseif match(a:command, 'e\?s$') != -1
+    let handlerCmd = substitute(a:command, 'e\?s$', '', '')
+    if exists('*s:{handlerCmd}Hdlr')
+      let itemHandler = 's:' . handlerCmd . 'Hdlr'
+    else
+      let itemHandler = 'perforce#PFIF'
+    endif
+  endif
+  if itemHandler ==# 'perforce#PFIF'
+    return "call perforce#PFIF(1, " . a:outputType . ", '" . handlerCmd . "', " .
+          \ a:args . ")"
+  elseif itemHandler !~# s:EMPTY_STR
+    return 'call ' . itemHandler . '(0, ' . a:outputType . ', ' . a:args . ')'
+  endif
+  return itemHandler
+endfunction " }}}
+
+function! s:OpenCurrentItem(outputType) " {{{
+  let curItem = s:GetOpenItem(a:outputType)
+  if curItem !~# s:EMPTY_STR
+    let commandHandler = s:getCommandItemHandler(a:outputType, b:p4Command,
+          \ "'" . curItem . "'")
+    if commandHandler !~# s:EMPTY_STR
+      exec commandHandler
+    endif
+  endif
+endfunction " }}}
+
+function! s:GetCurrentItem() " {{{
+  if exists("b:p4Command") && exists("s:{b:p4Command}Expr")
+    exec "return " s:{b:p4Command}Expr
+  endif
+  return ""
+endfunction " }}}
+
+function! s:GetOpenItem(outputType) " {{{
+  " For non-preview open.
+  if exists("b:p4Command") && a:outputType == 0 &&
+        \ exists("s:{b:p4Command}OpenItemExpr")
+    exec "return " s:{b:p4Command}OpenItemExpr
+  endif
+  return s:GetCurrentItem()
+endfunction " }}}
+
+function! s:DeleteCurrentItem() " {{{
+  let curItem = s:GetCurrentItem()
+  if curItem !~# s:EMPTY_STR
+    let answer = s:ConfirmMessage("Are you sure you want to delete " .
+          \ curItem . "?", "&Yes\n&No", 2, "Question")
+    if answer == 1
+      let commandHandler = s:getCommandItemHandler(2, b:p4Command,
+            \ "'-d', '" . curItem . "'")
+      if commandHandler !~# s:EMPTY_STR
+        exec commandHandler
+      endif
+      if v:shell_error == ""
+        call perforce#PFRefreshActivePane()
+      endif
+    endif
+  endif
+endfunction " }}}
+
+function! s:LaunchCurrentFile() " {{{
+  if g:p4FileLauncher =~# s:EMPTY_STR
+    call s:ConfirmMessage("There was no launcher command configured to launch ".
+          \ "this item, use g:p4FileLauncher to configure." , "OK", 1, "Error")
+    return
+  endif
+  let curItem = s:GetCurrentItem()
+  if curItem !~# s:EMPTY_STR
+    exec 'silent! !'.g:p4FileLauncher curItem
+  endif
+endfunction " }}}
+
+function! s:FilelogDiff2(line1, line2) " {{{
+  let line1 = a:line1
+  let line2 = a:line2
+  if line1 == line2
+    if line2 < line("$")
+      let line2 = line2 + 1
+    elseif line1 > 1
+      let line1 = line1 - 1
+    else
+      return
+    endif
+  endif
+
+  let file1 = s:GetCurrentDepotFile(line1)
+  if file1 !~# s:EMPTY_STR
+    let file2 = s:GetCurrentDepotFile(line2)
+    if file2 !~# s:EMPTY_STR && file2 != file1
+      " file2 will be older than file1.
+      exec "call perforce#PFIF(0, 0, \"" . (s:_('UseVimDiff2') ? 'vdiff2' : 'diff2') .
+            \ "\", file2, file1)"
+    endif
+  endif
+endfunction " }}}
+
+function! s:FilelogSyncToCurrentItem() " {{{
+  let curItem = s:GetCurrentItem()
+  if curItem !~# s:EMPTY_STR
+    let answer = s:ConfirmMessage("Do you want to sync to: " . curItem . " ?",
+          \ "&Yes\n&No", 2, "Question")
+    if answer == 1
+      call perforce#PFIF(1, 2, 'sync', curItem)
+    endif
+  endif
+endfunction " }}}
+
+function! s:ChangesSubmitChangeList() " {{{
+  let curItem = s:GetCurrentItem()
+  if curItem !~# s:EMPTY_STR
+    let answer = s:ConfirmMessage("Do you want to submit change list: " .
+          \ curItem . " ?", "&Yes\n&No", 2, "Question")
+    if answer == 1
+      call perforce#PFIF(0, 0, '++y', 'submit', '-c', curItem)
+    endif
+  endif
+endfunction " }}}
+
+function! s:LabelsSyncClientToLabel() " {{{
+  let curItem = s:GetCurrentItem()
+  if curItem !~# s:EMPTY_STR
+    let answer = s:ConfirmMessage("Do you want to sync client to the label: " .
+          \ curItem . " ?", "&Yes\n&No", 2, "Question")
+    if answer == 1
+      let retVal = call('perforce#PFIF', [1, 1, 'sync',
+            \ '//".s:_('Depot')."/...@'.curItem])
+      return retVal
+    endif
+  endif
+endfunction " }}}
+
+function! s:LabelsSyncLabelToClient() " {{{
+  let curItem = s:GetCurrentItem()
+  if curItem !~# s:EMPTY_STR
+    let answer = s:ConfirmMessage("Do you want to sync label: " . curItem .
+          \ " to client " . s:_('p4Client') . " ?", "&Yes\n&No", 2, "Question")
+    if answer == 1
+      let retVal = perforce#PFIF(1, 1, 'labelsync', '-l', curItem)
+      return retVal
+    endif
+  endif
+endfunction " }}}
+
+function! s:FilelogDescribeChange() " {{{
+  let changeNo = matchstr(getline("."), ' change \zs\d\+\ze ')
+  if changeNo !~# s:EMPTY_STR
+    exec "call perforce#PFIF(0, 1, 'describe', changeNo)"
+  endif
+endfunction " }}}
+
+function! s:SetupFileBrowse() " {{{
+  " For now, assume that a new window is created and we are in the new window.
+  exec "setlocal includeexpr=P4IncludeExpr(v:fname)"
+
+  " No meaning for delete.
+  silent! nunmap <buffer> D
+  silent! delcommand PItemDelete
+  command! -buffer -nargs=0 PFileDiff :call perforce#PFIF(0, 1, 'diff',
+        \ <SID>GetCurrentDepotFile(line(".")))
+  nnoremap <silent> <buffer> D :PFileDiff<CR>
+  command! -buffer -nargs=0 PFileProps :call perforce#PFIF(1, 0, 'fstat', '-C',
+        \ <SID>GetCurrentDepotFile(line(".")))
+  nnoremap <silent> <buffer> P :PFileProps<CR>
+  command! -buffer -nargs=0 PFileLog :call perforce#PFIF(1, 0, 'filelog',
+        \ <SID>GetCurrentDepotFile(line(".")))
+  command! -buffer -nargs=0 PFileEdit :call perforce#PFIF(1, 2, 'edit',
+        \ <SID>GetCurrentItem())
+  nnoremap <silent> <buffer> I :PFileEdit<CR>
+  command! -buffer -bar -nargs=0 PFileRevert :call perforce#PFIF(1, 2, 'revert',
+        \ <SID>GetCurrentItem())
+  nnoremap <silent> <buffer> R :PFileRevert \| PFRefreshActivePane<CR>
+  command! -buffer -nargs=0 PFilePrint
+        \ :if getline('.') !~# '(\%(u\|ux\)binary)$' |
+        \   call perforce#PFIF(0, 0, 'print',
+        \   substitute(<SID>GetCurrentDepotFile(line('.')), '#[^#]\+$', '', '').
+        \   '#'.
+        \   ((getline(".") =~# '#\d\+ - delete change') ?
+        \    matchstr(getline('.'), '#\zs\d\+\ze - ') - 1 :
+        \    matchstr(getline('.'), '#\zs\d\+\ze - '))
+        \   ) |
+        \ else |
+        \   echo 'PFilePrint: Binary file... ignored.' |
+        \ endif
+  nnoremap <silent> <buffer> p :PFilePrint<CR>
+  command! -buffer -nargs=0 PFileGet :call perforce#PFIF(1, 2, 'sync',
+        \ <SID>GetCurrentDepotFile(line(".")))
+  command! -buffer -nargs=0 PFileSync :call perforce#PFIF(1, 2, 'sync',
+        \ <SID>GetCurrentItem())
+  nnoremap <silent> <buffer> S :PFileSync<CR>
+  command! -buffer -nargs=0 PFileChange :call perforce#PFIF(0, 0, 'change', 
+        \ <SID>GetCurrentChangeNumber(line(".")))
+  nnoremap <silent> <buffer> C :PFileChange<CR>
+  command! -buffer -nargs=0 PFileLaunch :call <SID>LaunchCurrentFile()
+  nnoremap <silent> <buffer> A :PFileLaunch<CR>
+endfunction " }}}
+
+function! s:SetupDiff() " {{{
+  setlocal ft=diff
+endfunction " }}}
+
+function! s:SetupSelectItem() " {{{
+  nnoremap <buffer> <silent> D :PItemDelete<CR>
+  nnoremap <buffer> <silent> O :PItemOpen<CR>
+  nnoremap <buffer> <silent> <CR> :PItemDescribe<CR>
+  nnoremap <buffer> <silent> <2-LeftMouse> :PItemDescribe<CR>
+  command! -buffer -nargs=0 PItemDescribe :call <SID>OpenCurrentItem(1)
+  command! -buffer -nargs=0 PItemOpen :call <SID>OpenCurrentItem(0)
+  command! -buffer -nargs=0 PItemDelete :call <SID>DeleteCurrentItem()
+  cnoremap <buffer> <C-X><C-I> <C-R>=<SID>GetCurrentItem()<CR>
+endfunction " }}}
+
+function! s:RestoreWindows(dummy) " {{{
+  call genutils#RestoreWindowSettings2("PerforceHelp")
+endfunction " }}}
+
+function! s:NavigateBack() " {{{
+  call s:Navigate('u')
+  if line('$') == 1 && getline(1) == ''
+    call s:NavigateForward()
+  endif
+endfunction " }}}
+
+function! s:NavigateForward() " {{{
+  call s:Navigate("\<C-R>")
+endfunction " }}}
+
+function! s:Navigate(key) " {{{
+  let _modifiable = &l:modifiable
+  try
+    setlocal modifiable
+    " Use built-in markers as Vim takes care of remembering and restoring them
+    "   during the undo/redo.
+    normal! mt
+
+    silent! exec "normal" a:key
+
+    if line("'t") > 0 && line("'t") <= line('$')
+      normal! `t
+    endif
+  finally
+    let &l:modifiable = _modifiable
+  endtry
+endfunction " }}}
+
+function! s:GetCurrentChangeNumber(lineNo) " {{{
+  let line = getline(a:lineNo)
+  let changeNo = matchstr(line, ' - \S\+ change \zs\S\+\ze (')
+  if changeNo ==# 'default'
+    let changeNo = ''
+  endif
+  return changeNo
+endfunction " }}}
+
+function! s:PChangesDescribeCurrentItem() " {{{
+  let currentChangeNo = s:GetCurrentItem()
+  if currentChangeNo !~# s:EMPTY_STR
+    call perforce#PFIF(0, 1, 'describe', '-s', currentChangeNo)
+  endif
+endfunction " }}}
+
+" {{{
+function! perforce#PFSettings(...)
+  if s:_('SortSettings ')
+    if exists("s:sortedSettings")
+      let settings = s:sortedSettings
+    else
+      let settings = sort(s:settings)
+      let s:sortedSettings = settings
+    endif
+  else
+    let settings = s:settings
+  endif
+  if a:0 > 0
+    let selectedSetting = a:1
+  else
+    let selectedSetting = genutils#PromptForElement(settings, -1,
+          \ "Select the setting: ", -1, 0, 3)
+  endif
+  if selectedSetting !~# s:EMPTY_STR
+    let oldVal = s:_(selectedSetting)
+    if a:0 > 1
+      let newVal = a:2
+      echo 'Current value for' selectedSetting.': "'.oldVal.'" New value: "'.
+            \ newVal.'"'
+    else
+      let newVal = input('Current value for ' . selectedSetting . ' is: ' .
+            \ oldVal . "\nEnter new value: ", oldVal)
+    endif
+    if newVal != oldVal
+      let g:p4{selectedSetting} = newVal
+      call perforce#Initialize(1)
+    endif
+  endif
+endfunction
+
+function! perforce#PFSettingsComplete(ArgLead, CmdLine, CursorPos)
+  if s:settingsCompStr == ''
+    let s:settingsCompStr = join(s:settings, "\n")
+  endif
+  return s:settingsCompStr
+endfunction
+" }}}
+
+function! s:MakeRevStr(ver) " {{{
+  let verStr = ''
+  if a:ver =~# '^[#@&]'
+    let verStr = a:ver
+  elseif a:ver =~# '^[-+]\?\d\+\>\|^none\>\|^head\>\|^have\>'
+    let verStr = '#' . a:ver
+  elseif a:ver !~# s:EMPTY_STR
+    let verStr = '@' . a:ver
+  endif
+  return verStr
+endfunction " }}}
+
+function! s:GetBranchName(fileName) " {{{
+  if s:IsFileUnderDepot(a:fileName)
+    " TODO: Need to run where command at this phase.
+  elseif stridx(a:fileName, '//') == 0
+    return matchstr(a:fileName, '^//[^/]\+/\zs[^/]\+\ze')
+  else
+    return ''
+  endif
+endfunction " }}}
+
+function! s:GetDiff2Args()
+  let p4Arguments = s:p4Arguments
+  if len(p4Arguments) < 2
+    if len(p4Arguments) == 0
+      let file = s:EscapeFileName(s:GetCurFileName())
+    else
+      let file = p4Arguments[0]
+    endif
+    let ver1 = s:PromptFor(0, s:_('UseGUIDialogs'), "Version1? ", '')
+    let ver2 = s:PromptFor(0, s:_('UseGUIDialogs'), "Version2? ", '')
+    let p4Arguments = [file.s:MakeRevStr(ver1), file.s:MakeRevStr(ver2)]
+  endif
+  return p4Arguments
+endfunction
+
+function! perforce#ToggleCheckOutPrompt(interactive)
+  aug P4CheckOut
+  au!
+  if g:p4PromptToCheckout
+    let g:p4PromptToCheckout = 0
+  else
+    let g:p4PromptToCheckout = 1
+    au FileChangedRO * nested :call <SID>CheckOutFile()
+  endif
+  aug END
+  if a:interactive
+    echomsg "PromptToCheckout is now " . ((g:p4PromptToCheckout) ? "enabled." :
+          \ "disabled.")
+  endif
+endfunction
+
+function! perforce#PFDiffOff(diffCounter)
+  " Cycle through all windows and turn off diff options for the specified diff
+  " run, or all, if none specified.
+  let curWinNr = winnr()
+  let eventignore = &eventignore
+  set eventignore=all
+  try
+    let i = 1
+    while winbufnr(i) != -1
+      try
+        exec i 'wincmd w'
+        if ! exists('w:p4VDiffWindow')
+          continue
+        endif
+
+        if a:diffCounter == -1 || w:p4VDiffWindow == a:diffCounter
+          call genutils#CleanDiffOptions()
+          unlet w:p4VDiffWindow
+        endif
+      finally
+        let i = i + 1
+      endtry
+    endwhile
+  finally
+    " Return to the original window.
+    exec curWinNr 'wincmd w'
+    let &eventignore = eventignore
+  endtry
+endfunction
+""" END: Helper functions }}}
+
+
+""" BEGIN: Middleware functions {{{
+
+" Filter contents through p4.
+function! perforce#PW(fline, lline, scriptOrigin, ...) range
+  if a:scriptOrigin != 2
+    call call('s:ParseOptions', [a:fline, a:lline, 0, '++f'] + a:000)
+  else
+    let s:commandMode = s:CM_FILTER
+  endif
+  setlocal modifiable
+  let retVal = perforce#PFIF(2, 5, s:p4Command)
+  return retVal
+endfunction
+
+" Generate raw output into a new window.
+function! perforce#PFRaw(...)
+  call call('s:ParseOptions', [1, line('$'), 0] + a:000)
+
+  let retVal = s:PFImpl(1, 0)
+  return retVal
+endfunction
+
+function! s:W(quitWhenDone, commandName, ...)
+  call call('s:ParseOptionsIF', [1, line('$'), 0, 5, a:commandName]+a:000)
+  if index(s:p4CmdOptions, '-i') == -1
+    call insert(s:p4CmdOptions, '-i', 0)
+  endif
+  let retVal = perforce#PW(1, line('$'), 2)
+  if s:errCode == 0
+    setl nomodified
+    if a:quitWhenDone
+      close
+    else
+      if s:p4Command ==# 'change' || s:p4Command ==# 'submit'
+        " Change number fixed, or files added/removed.
+        if s:FixChangeNo() || search('^Change \d\+ updated, ', 'w')
+          silent! undo
+          if search('^Files:\s*$', 'w')
+            let b:p4NewFilelist = getline(line('.')+1, line('$'))
+            if !exists('b:p4OrgFilelist')
+              let filelist = b:p4NewFilelist
+            else
+              " Find outersection.
+              let filelist = copy(b:p4NewFilelist)
+              call filter(filelist, 'index(b:p4OrgFilelist, v:val)==-1')
+              call extend(filelist, filter(copy(b:p4OrgFilelist), 'index(b:p4NewFilelist, v:val)==-1'))
+            endif
+            let files = map(filelist, 's:ConvertToLocalPath(v:val)')
+            call s:ResetFileStatusForFiles(files)
+          endif
+          silent! redo
+        endif
+      endif
+    endif
+  else
+    call s:FixChangeNo()
+  endif
+endfunction
+
+function! s:FixChangeNo()
+  if search('^Change \d\+ created', 'w') ||
+        \ search('^Change \d\+ renamed change \d\+ and submitted', 'w')
+    let newChangeNo = matchstr(getline('.'), '\d\+\ze\%(created\|and\)')
+    let _undolevels=&undolevels
+    try
+      let allLines = getline(1, line('$'))
+      silent! undo
+      " Make the below changes such a way that they can't be undo. This in a
+      "   way, forces Vim to create an undo point, so that user can later
+      "   undo and see these changes, with proper change number and status
+      "   in place. This has the side effect of loosing the previous undo
+      "   history, which can be considered desirable, as otherwise the user
+      "   can undo this change and back to the new state.
+      set undolevels=-1
+      if search("^Change:\t\%(new\|\d\+\)$")
+        silent! keepjumps call setline('.', "Change:\t" . newChangeNo)
+        " If no Date is present (happens for new changelists)
+        if !search("^Date:\t", 'w')
+          call append('.', ['', "Date:\t".strftime('%Y/%m/%d %H:%M:%S')])
+        endif
+      endif
+      if search("^Status:\tnew$")
+        silent! keepjumps call setline('.', "Status:\tpending")
+      endif
+      setl nomodified
+      let &undolevels=_undolevels
+      silent! 0,$delete _
+      call setline(1, allLines)
+      setl nomodified
+      call s:PFSetupForSpec()
+    finally
+      let &undolevels=_undolevels
+    endtry
+    let b:p4Command = 'change'
+    let b:p4CmdOptions = ['-i']
+    return 1
+  else
+    return 0
+  endif
+endfunction
+
+function! s:ParseOptionsIF(fline, lline, scriptOrigin, outputType, commandName,
+      \ ...) " range
+  " There are multiple possibilities here:
+  "   - scriptOrigin, in which case the commandName contains the name of the
+  "     command, but the varArgs also may contain it.
+  "   - commandOrigin, in which case the commandName may actually be the
+  "     name of the command, or it may be the first argument to p4 itself, in
+  "     any case we will let p4 handle the error cases.
+  if index(s:allCommands, a:commandName) != -1 && a:scriptOrigin
+    call call('s:ParseOptions', [a:fline, a:lline, a:outputType] + a:000)
+    " Add a:commandName only if it doesn't already exist in the var args.
+    " Handles cases like "PF help submit" and "PF -c <client> change changeno#",
+    "   where the commandName need not be at the starting and there could be
+    "   more than one valid commandNames (help and submit).
+    if s:p4Command != a:commandName
+      call call('s:ParseOptions',
+            \ [a:fline, a:lline, a:outputType, a:commandName] + a:000)
+    endif
+  else
+    call call('s:ParseOptions',
+          \ [a:fline, a:lline, a:outputType, a:commandName] + a:000)
+  endif
+endfunction
+
+" PFIF {{{
+" The commandName may not be the perforce command when it is not of script
+"   origin (called directly from a command), but it should be always command
+"   name, when it is script origin.
+" scriptOrigin: An integer indicating the origin of the call. 
+"   0 - Originated directly from the user, so should redirect to the specific
+"       command handler (if exists), after some basic processing.
+"   1 - Originated from the script, continue with the full processing (makes
+"       difference in some command parsing).
+"   2 - Same as 1 but, avoid processing arguments (they are already processing
+"       by the caller).
+function! perforce#PFIF(scriptOrigin, outputType, commandName, ...)
+  return call('perforce#PFrangeIF', [1, line('$'), a:scriptOrigin, a:outputType,
+        \ a:commandName]+a:000)
+endfunction
+
+function! perforce#PFrangeIF(fline, lline, scriptOrigin, outputType,
+      \ commandName, ...)
+  if a:scriptOrigin != 2
+    call call('s:ParseOptionsIF', [a:fline, a:lline,
+          \ a:scriptOrigin, a:outputType, a:commandName]+a:000)
+  endif
+  if ! a:scriptOrigin
+    " Save a copy of the arguments so that the refresh can invoke this exactly
+    " as it was before.
+    let s:userArgs = [a:fline, a:lline, a:scriptOrigin, a:outputType, a:commandName]
+          \ +a:000
+  endif
+
+
+  let outputOptIdx = index(s:p4Options, '++o')
+  if outputOptIdx != -1
+    " Get the argument followed by ++o.
+    let s:outputType = get(s:p4Options, outputIdx + 1) + 0
+  endif
+  " If this command prefers a dialog output, then just take care of
+  "   it right here.
+  if index(s:dlgOutputCmds, s:p4Command) != -1
+    let s:outputType = 2
+  endif
+  if ! a:scriptOrigin
+    if exists('*s:{s:p4Command}Hdlr')
+      return s:{s:p4Command}Hdlr(1, s:outputType, a:commandName)
+    endif
+  endif
+  let modifyWindowName = 0
+  let dontProcess = (index(s:p4Options, '++n') != -1)
+  let noDefaultArg = (index(s:p4Options, '++N') != -1)
+  " If there is a confirm message for this command, then first prompt user.
+  let dontConfirm = (index(s:p4Options, '++y') != -1)
+  if exists('s:confirmMsgs{s:p4Command}') && ! dontConfirm
+    let option = s:ConfirmMessage(s:confirmMsgs{s:p4Command}, "&Yes\n&No", 2,
+          \ "Question")
+    if option == 2
+      let s:errCode = 2
+      return ''
+    endif
+  endif
+
+  if index(s:limitListCmds, s:p4Command) != -1 &&
+        \ index(s:p4CmdOptions, '-m') == -1 && s:_('DefaultListSize') > -1
+    call extend(s:p4CmdOptions, ['-m', s:_('DefaultListSize')], 0)
+    let modifyWindowName = 1
+  endif
+  if index(s:diffCmds, s:p4Command) != -1 &&
+        \ s:indexMatching(s:p4CmdOptions, '^-d.*$') == -1
+        \ && s:_('DefaultDiffOptions') !~# s:EMPTY_STR
+    " FIXME: Avoid split().
+    call extend(s:p4CmdOptions, split(s:_('DefaultDiffOptions'), '\s'), 0)
+    let modifyWindowName = 1
+  endif
+
+  " Process p4Arguments, unless explicitly not requested to do so, or the '-x'
+  "   option to read arguments from a file is given.
+  let unprotectedSpecPat = genutils#CrUnProtectedCharsPattern('&#@')
+  if ! dontProcess && ! noDefaultArg && len(s:p4Arguments) == 0 &&
+        \ index(s:p4Options, '-x') == -1
+    if (index(s:askUserCmds, s:p4Command) != -1 &&
+          \ index(s:p4CmdOptions, '-i') == -1) ||
+          \ index(s:p4Options, '++A') != -1
+      if index(s:genericPromptCmds, s:p4Command) != -1
+        let prompt = 'Enter arguments for ' . s:p4Command . ': '
+      else
+        let prompt = "Enter the " . s:p4Command . " name: "
+      endif
+      let additionalArg = s:PromptFor(0, s:_('UseGUIDialogs'), prompt, '')
+      if additionalArg =~# s:EMPTY_STR
+        if index(s:genericPromptCmds, s:p4Command) != -1
+          call s:EchoMessage('Arguments required for '. s:p4Command, 'Error')
+        else
+          call s:EchoMessage(substitute(s:p4Command, "^.", '\U&', '') .
+                \ " name required.", 'Error')
+        endif
+        let s:errCode = 2
+        return ''
+      endif
+      let s:p4Arguments = [additionalArg]
+    elseif ! dontProcess &&
+          \ index(s:curFileNotDefCmds, s:p4Command) == -1 &&
+          \ index(s:nofileArgsCmds, s:p4Command) == -1
+      let s:p4Arguments = [s:EscapeFileName(s:GetCurFileName())]
+      let modifyWindowName = 1
+    endif
+  elseif ! dontProcess && s:indexMatching(s:p4Arguments, unprotectedSpecPat) != -1
+    let branchModifierSpecified = 0
+    let unprotectedAmp = genutils#CrUnProtectedCharsPattern('&')
+    if s:indexMatching(s:p4Arguments, unprotectedAmp) != -1
+      let branchModifierSpecified = 1
+      " CAUTION: We make sure the view mappings are generated before
+      "   s:PFGetAltFiles() gets invoked, otherwise the call results in a
+      "   recursive |sub-replace-special| and corrupts the mappings.
+      call s:CondUpdateViewMappings()
+    endif
+
+    " This is like running substitute(v:val, 'pat', '\=expr', 'g') on each
+    "   element.
+    " Pattern is (the start of line or series of non-space chars) followed by
+    "   an unprotected [#@&] with a revision/codeline specifier.
+    " Expression is a concatenation of each of:
+    "   (<no filename specified>?<current file>:<specified file>)
+    "   <revision specifier>
+    "   (<offset specified>?<adjusted revision>:<specified revision>)
+    call map(s:p4Arguments, "substitute(v:val, '".
+          \ '\(^\|\%(\S\)\+\)\('.unprotectedSpecPat.'\)\%(\2\)\@!\([-+]\?\d\+\|\S\+\)'."', ".
+          \ "'\\=s:ExpandRevision(submatch(1), submatch(2), submatch(3))', 'g')")
+    if s:errCode != 0
+      return ''
+    endif
+
+    if branchModifierSpecified
+      call map(s:p4Arguments, 
+            \ '(v:val =~ unprotectedAmp ? s:ApplyBranchSpecs(v:val, unprotectedAmp) : v:val)')
+    endif
+    " Unescape them, as user is required to escape them to avoid the above
+    " processing.
+    call map(s:p4Arguments, "genutils#UnEscape(v:val, '&@')")
+
+    let modifyWindowName = 1
+  endif
+
+  let testMode = 0
+  if index(s:p4Options, '++T') != -1
+    let testMode = 1 " Dry run, opens the window.
+  elseif index(s:p4Options, '++D') != -1
+    let testMode = 2 " Debug. Opens the window and displays the command.
+  endif
+
+  let oldOptLen = len(s:p4Options)
+  " Remove all the built-in options.
+  call filter(s:p4Options, "v:val !~ '".'++\S\+\%(\s\+[^-+]\+\|\s\+\)\?'."'")
+  if len(s:p4Options) != oldOptLen
+    let modifyWindowName = 1
+  endif
+  if index(s:diffCmds, s:p4Command) != -1
+    " Remove the dummy option, if exists (see |perforce-default-diff-format|).
+    call filter(s:p4CmdOptions, 'v:val != "-d"')
+    let modifyWindowName = 1
+  endif
+
+  if s:p4Command ==# 'help'
+    " Use simple window name for all the help commands.
+    let s:p4WinName = s:helpWinName
+  elseif modifyWindowName
+    let s:p4WinName = s:MakeWindowName() 
+  endif
+
+  " If the command is a built-in command, then don't pass it to external p4.
+  if exists('s:builtinCmdHandler{s:p4Command}')
+    let s:errCode = 0
+    return {s:builtinCmdHandler{s:p4Command}}()
+  endif
+
+  let specMode = 0
+  if index(s:specCmds, s:p4Command) != -1
+    if index(s:p4CmdOptions, '-d') == -1
+          \ && index(s:p4CmdOptions, '-o') == -1
+          \ && index(s:noOutputCmds, s:p4Command) == -1
+      call add(s:p4CmdOptions, '-o')
+    endif
+
+    " Go into specification mode only if the user intends to edit the output.
+    if ((s:p4Command ==# 'submit' && index(s:p4CmdOptions, '-c') == -1) ||
+          \ (index(s:specOnlyCmds, s:p4Command) == -1 &&
+          \  index(s:p4CmdOptions, '-d') == -1)) &&
+          \ s:outputType == 0
+      let specMode = 1
+    endif
+  endif
+  
+  let navigateCmd = 0
+  if index(s:navigateCmds, s:p4Command) != -1
+    let navigateCmd = 1
+  endif
+
+  let retryCount = 0
+  let retVal = ''
+  " FIXME: When is "not clearing" (value 2) useful at all?
+  let clearBuffer = (testMode != 2 ? ! navigateCmd : 2)
+  " CAUTION: This is like a do..while loop, but not exactly the same, be
+  " careful using continue, the counter will not get incremented.
+  while 1
+    let retVal = s:PFImpl(clearBuffer, testMode)
+
+    " Everything else in this loop is for password support.
+    if s:errCode == 0
+      break
+    else
+      if retVal =~# s:EMPTY_STR
+        let retVal = getline(1)
+      endif
+      " FIXME: Works only with English as the language.
+      if retVal =~# 'Perforce password (P4PASSWD) invalid or unset.'
+        let p4Password = inputsecret("Password required for user " .
+              \ s:_('p4User') . ": ", s:p4Password)
+        if p4Password ==# s:p4Password
+          break
+        endif
+        let s:p4Password = p4Password
+      else
+        break
+      endif
+    endif
+    let retryCount = retryCount + 1
+    if retryCount > 2
+      break
+    endif
+  endwhile
+
+  if s:errCode == 0 && index(s:autoreadCmds, s:p4Command) != -1
+    call s:ResetFileStatusForFiles(s:p4Arguments)
+  endif
+
+  if s:errCode != 0
+    return retVal
+  endif
+
+  call s:SetupWindow(specMode)
+
+  return retVal
+endfunction
+
+function! s:SetupWindow(specMode)
+  if s:StartBufSetup()
+    " If this command has a handler for the individual items, then enable the
+    " item selection commands.
+    if s:getCommandItemHandler(0, s:p4Command, '') !~# s:EMPTY_STR
+      call s:SetupSelectItem()
+    endif
+
+    if index(s:ftNotPerforceCmds, s:p4Command) == -1
+      setlocal ft=perforce
+    endif
+
+    if index(s:filelistCmds, s:p4Command) != -1
+      call s:SetupFileBrowse()
+    endif
+
+    if s:NewWindowCreated()
+      if a:specMode
+        " It is not possible to have an s:p4Command which is in s:allCommands
+        "         and still not be the actual intended command.
+        command! -buffer -nargs=* W :call call('<SID>W',
+              \ [0]+b:p4Options+[b:p4Command]+b:p4CmdOptions+split(<q-args>,
+              \ '\s'))
+        command! -buffer -nargs=* WQ :call call('<SID>W',
+              \ [1]+b:p4Options+[b:p4Command]+b:p4CmdOptions+split(<q-args>,
+              \ '\s'))
+        call s:EchoMessage("When done, save " . s:p4Command .
+              \ " spec by using :W or :WQ command. Undo on errors.", 'None')
+        call s:PFSetupForSpec()
+      else " Define q to quit the read-only perforce windows (David Fishburn)
+        nnoremap <buffer> q <C-W>q
+      endif
+    endif
+
+    if index(s:navigateCmds, s:p4Command) != -1
+      nnoremap <silent> <buffer> <C-O> :call <SID>NavigateBack()<CR>
+      nnoremap <silent> <buffer> <BS> :call <SID>NavigateBack()<CR>
+      nnoremap <silent> <buffer> <Tab> :call <SID>NavigateForward()<CR>
+    endif
+
+    call s:EndBufSetup()
+  endif
+endfunction
+
+function! s:ExpandRevision(fName, revType, revSpec)
+  return (a:fName==''?s:EscapeFileName(s:GetCurFileName()):a:fName).
+        \ a:revType.
+        \ (a:revSpec=~'^[-+]'?s:AdjustRevision(a:fName, a:revSpec):a:revSpec)
+endfunction
+
+function! s:ApplyBranchSpecs(arg, unprotectedAmp)
+  " The first arg will be filename, followed by multiple specifiers.
+  let toks = split(a:arg, a:unprotectedAmp)
+  " FIXME: Handle "&" in the filename.
+  " FIXME: Handle other revision specifiers occuring in any order
+  " (e.g., <file>&branch#3).
+  " Ex: c:/dev/docs/Triage/Tools/Machine\ Configurations\ &\ Codeline\ Requirements.xls
+  if len(toks) == 0 || toks[0] == ''
+    return a:arg
+  endif
+  let fname = remove(toks, 0)
+  " Reduce filename according to the branch tokens.
+  for altBranch in toks
+    let fname = s:PFGetAltFiles("&", altBranch, fname)[0]
+  endfor
+  return fname
+endfunction
+
+function! perforce#PFComplete(ArgLead, CmdLine, CursorPos)
+  if s:p4KnownCmdsCompStr == ''
+    let s:p4KnownCmdsCompStr = join(s:p4KnownCmds, "\n")
+  endif
+  if a:CmdLine =~ '^\s*P[FW] '
+    let argStr = strpart(a:CmdLine, matchend(a:CmdLine, '^\s*PF '))
+    let s:p4Command = ''
+    if argStr !~# s:EMPTY_STR
+      if exists('g:p4EnableUserFileExpand')
+        let _p4EnableUserFileExpand = g:p4EnableUserFileExpand
+      endif
+      try
+        " WORKAROUND for :redir broken, when called from here.
+        let g:p4EnableUserFileExpand = 0
+        exec 'call s:ParseOptionsIF(-1, -1, 0, 0, ' .
+              \ genutils#CreateArgString(argStr, s:SPACE_AS_SEP).')'
+      finally
+        if exists('_p4EnableUserFileExpand')
+          let g:p4EnableUserFileExpand = _p4EnableUserFileExpand
+        else
+          unlet g:p4EnableUserFileExpand
+        endif
+      endtry
+    endif
+    if s:p4Command ==# '' || s:p4Command ==# a:ArgLead
+      return s:p4KnownCmdsCompStr."\n".join(s:builtinCmds, "\n")
+    endif
+  else
+    let userCmd = substitute(a:CmdLine, '^\s*P\(.\)\(\w*\).*', '\l\1\2', '')
+    if strlen(userCmd) < 3
+      if !has_key(s:shortCmdMap, userCmd)
+        throw "Perforce internal error: no map found for short command: ".
+              \ userCmd
+      endif
+      let userCmd = s:shortCmdMap[userCmd]
+    endif
+    let s:p4Command = userCmd
+  endif
+  if s:p4Command ==# 'help'
+    return s:PHelpComplete(a:ArgLead, a:CmdLine, a:CursorPos)
+  endif
+  if index(s:nofileArgsCmds, s:p4Command) != -1
+    return ''
+  endif
+
+  " FIXME: Can't set command-line from user function.
+  "let argLead = genutils#UserFileExpand(a:ArgLead)
+  "if argLead !=# a:ArgLead
+  "  let cmdLine = strpart(a:CmdLine, 0, a:CursorPos-strlen(a:ArgLead)) .
+  "        \ argLead . strpart(a:CmdLine, a:CursorPos)
+  "  exec "normal! \<C-\>e'".cmdLine."'\<CR>"
+  "  call setcmdpos(a:CursorPos+(strlen(argLead) - strlen(a:ArgLead)))
+  "  return ''
+  "endif
+  if a:ArgLead =~ '^//'.s:_('Depot').'/'
+    " Get directory matches.
+    let dirMatches = s:GetOutput('dirs', a:ArgLead, "\n", '/&')
+    " Get file matches.
+    let fileMatches = s:GetOutput('files', a:ArgLead, '#\d\+[^'."\n".']\+', '')
+    if dirMatches !~ s:EMPTY_STR || fileMatches !~ s:EMPTY_STR
+      return dirMatches.fileMatches
+    else
+      return ''
+    endif
+  endif
+  return genutils#UserFileComplete(a:ArgLead, a:CmdLine, a:CursorPos, 1, '')
+endfunction
+
+function! s:GetOutput(p4Cmd, arg, pat, repl)
+  let matches = perforce#PFIF(0, 4, a:p4Cmd, a:arg.'*')
+  if s:errCode == 0
+    if matches =~ 'no such file(s)'
+      let matches = ''
+    else
+      let matches = substitute(substitute(matches, a:pat, a:repl, 'g'),
+            \ "\n\n", "\n", 'g')
+    endif
+  endif
+  return matches
+endfunction
+" PFIF }}}
+
+""" START: Adopted from Tom's perforce plugin. {{{
+
+"---------------------------------------------------------------------------
+" Produce string for ruler output
+function! perforce#RulerStatus()
+  if exists('b:p4RulerStr') && b:p4RulerStr !~# s:EMPTY_STR
+    return b:p4RulerStr
+  endif
+  if !exists('b:p4FStatDone') || !b:p4FStatDone
+    return ''
+  endif
+
+  "let b:p4RulerStr = '[p4 '
+  let b:p4RulerStr = '['
+  if exists('b:p4RulerErr') && b:p4RulerErr !~# s:EMPTY_STR
+    let b:p4RulerStr = b:p4RulerStr . b:p4RulerErr
+  elseif !exists('b:p4HaveRev')
+    let b:p4RulerStr = ''
+  elseif b:p4Action =~# s:EMPTY_STR
+    if b:p4OtherOpen =~# s:EMPTY_STR
+      let b:p4RulerStr = b:p4RulerStr . 'unopened'
+    else
+      let b:p4RulerStr = b:p4RulerStr . b:p4OtherOpen . ':' . b:p4OtherAction
+    endif
+  else
+    if b:p4Change ==# 'default' || b:p4Change =~# s:EMPTY_STR
+      let b:p4RulerStr = b:p4RulerStr . b:p4Action
+    else
+      let b:p4RulerStr = b:p4RulerStr . b:p4Action . ':' . b:p4Change
+    endif
+  endif
+  if exists('b:p4HaveRev') && b:p4HaveRev !~# s:EMPTY_STR
+    let b:p4RulerStr = b:p4RulerStr . ' #' . b:p4HaveRev . '/' . b:p4HeadRev
+  endif
+
+  if b:p4RulerStr !~# s:EMPTY_STR
+    let b:p4RulerStr = b:p4RulerStr . ']'
+  endif
+  return b:p4RulerStr
+endfunction
+
+function! s:GetClientInfo()
+  let infoStr = ''
+  call s:PushP4Context()
+  try
+    let infoStr = perforce#PFIF(0, 4, 'info')
+    if s:errCode != 0
+      return s:ConfirmMessage((v:errmsg != '') ? v:errmsg : infoStr, 'OK', 1,
+            \ 'Error')
+    endif
+  finally
+    call s:PopP4Context(0)
+  endtry
+  let g:p4ClientRoot = genutils#CleanupFileName(s:StrExtract(infoStr,
+        \ '\CClient root: [^'."\n".']\+', 13))
+  let s:p4Client = s:StrExtract(infoStr, '\CClient name: [^'."\n".']\+', 13)
+  let s:p4User = s:StrExtract(infoStr, '\CUser name: [^'."\n".']\+', 11)
+endfunction
+
+" Get/refresh filestatus for the specified buffer with optimizations.
+function! perforce#GetFileStatus(buf, refresh)
+  if ! type(a:buf) " If number.
+    let bufNr = (a:buf == 0) ? bufnr('%') : a:buf
+  else
+    let bufNr = bufnr(a:buf)
+  endif
+
+  " If it is not a normal buffer, then ignore it.
+  if getbufvar(bufNr, '&buftype') != '' || bufname(bufNr) == ''
+    return ""
+  endif
+  if bufNr == -1 || (!a:refresh && s:_('OptimizeActiveStatus') &&
+        \ getbufvar(bufNr, "p4FStatDone"))
+    return ""
+  endif
+
+  " This is an optimization by restricting status to the files under the
+  "   client root only.
+  if !s:IsFileUnderDepot(expand('#'.bufNr.':p'))
+    return ""
+  endif
+
+  return s:GetFileStatusImpl(bufNr)
+endfunction
+
+function! s:ResetFileStatusForFiles(files)
+  for file in a:files
+    let bufNr = genutils#FindBufferForName(file)
+    if bufNr != -1
+      " FIXME: Check for other tabs also.
+      if bufwinnr(bufNr) != -1 " If currently visible.
+        call perforce#GetFileStatus(bufNr, 1)
+      else
+        call s:ResetFileStatusForBuffer(bufNr)
+      endif
+    endif
+  endfor
+endfunction
+
+function! s:ResetFileStatusForBuffer(bufNr)
+  " Avoid proliferating this buffer variable.
+  if getbufvar(a:bufNr, 'p4FStatDone') != 0
+    call setbufvar(a:bufNr, 'p4FStatDone', 0)
+  endif
+endfunction
+
+"---------------------------------------------------------------------------
+" Obtain file status information
+function! s:GetFileStatusImpl(bufNr)
+  if bufname(a:bufNr) == ""
+    return ''
+  endif
+  let fileName = fnamemodify(bufname(a:bufNr), ':p')
+  let bufNr = a:bufNr
+  " If the filename matches with one of the ignore patterns, then don't do
+  " status.
+  if s:_('ASIgnoreDefPattern') !~# s:EMPTY_STR &&
+        \ match(fileName, s:_('ASIgnoreDefPattern')) != -1
+    return ''
+  endif
+  if s:_('ASIgnoreUsrPattern') !~# s:EMPTY_STR &&
+        \ match(fileName, s:_('ASIgnoreUsrPattern')) != -1
+    return ''
+  endif
+
+  call setbufvar(bufNr, 'p4RulerStr', '') " Let this be reconstructed.
+
+  " This could very well be a recursive call, so we should save the current
+  "   state.
+  call s:PushP4Context()
+  try
+    let fileStatusStr = perforce#PFIF(1, 4, 'fstat', fileName)
+    call setbufvar(bufNr, 'p4FStatDone', '1')
+
+    if s:errCode != 0
+      call setbufvar(bufNr, 'p4RulerErr', "<ERROR>")
+      return ''
+    endif
+  finally
+    call s:PopP4Context(0)
+  endtry
+
+  if match(fileStatusStr, ' - file(s) not in client view\.') >= 0
+    call setbufvar(bufNr, 'p4RulerErr', "<Not In View>")
+    " Required for optimizing out in future runs.
+    call setbufvar(bufNr, 'p4HeadRev', '')
+    return ''
+  elseif match(fileStatusStr, ' - no such file(s).') >= 0
+    call setbufvar(bufNr, 'p4RulerErr', "<Not In Depot>")
+    " Required for optimizing out in future runs.
+    call setbufvar(bufNr, 'p4HeadRev', '')
+    return ''
+  else
+    call setbufvar(bufNr, 'p4RulerErr', '')
+  endif
+
+  call setbufvar(bufNr, 'p4HeadRev',
+        \ s:StrExtract(fileStatusStr, '\CheadRev [0-9]\+', 8))
+  "call setbufvar(bufNr, 'p4DepotFile',
+  "      \ s:StrExtract(fileStatusStr, '\CdepotFile [^'."\n".']\+', 10))
+  "call setbufvar(bufNr, 'p4ClientFile',
+  "      \ s:StrExtract(fileStatusStr, '\CclientFile [^'."\n".']\+', 11))
+  call setbufvar(bufNr, 'p4HaveRev',
+        \ s:StrExtract(fileStatusStr, '\ChaveRev [0-9]\+', 8))
+  let headAction = s:StrExtract(fileStatusStr, '\CheadAction [^[:space:]]\+',
+        \ 11)
+  if headAction ==# 'delete'
+    call setbufvar(bufNr, 'p4Action', '<Deleted>')
+    call setbufvar(bufNr, 'p4Change', '')
+  else
+    call setbufvar(bufNr, 'p4Action',
+          \ s:StrExtract(fileStatusStr, '\Caction [^[:space:]]\+', 7))
+    call setbufvar(bufNr, 'p4OtherOpen',
+          \ s:StrExtract(fileStatusStr, '\CotherOpen0 [^[:space:]@]\+', 11))
+    call setbufvar(bufNr, 'p4OtherAction',
+          \ s:StrExtract(fileStatusStr, '\CotherAction0 [^[:space:]@]\+', 13))
+    call setbufvar(bufNr, 'p4Change',
+          \ s:StrExtract(fileStatusStr, '\Cchange [^[:space:]]\+', 7))
+  endif
+
+  return fileStatusStr
+endfunction
+
+function! s:StrExtract(str, pat, pos)
+  let part = matchstr(a:str, a:pat)
+  let part = strpart(part, a:pos)
+  return part
+endfunction
+
+function! s:AdjustRevision(file, adjustment)
+  let s:errCode = 0
+  let revNum = a:adjustment
+  if revNum =~# '[-+]\d\+'
+    let revNum = substitute(revNum, '^+', '', '')
+    if getbufvar(a:file, 'p4HeadRev') =~# s:EMPTY_STR
+      " If fstat is not done yet, do it now.
+      call perforce#GetFileStatus(a:file, 1)
+      if getbufvar(a:file, 'p4HeadRev') =~# s:EMPTY_STR
+        call s:EchoMessage("Current revision is not available. " .
+              \ "To be able to use negative revisions, see help on " .
+              \ "'perforce-active-status'.", 'Error')
+        let s:errCode = 1
+        return -1
+      endif
+    endif
+    let revNum = getbufvar(a:file, 'p4HaveRev') + revNum
+    if revNum < 1
+      call s:EchoMessage("Not that many revisions available. Try again " .
+            \ "after running PFRefreshFileStatus command.", 'Error')
+      let s:errCode = 1
+      return -1
+    endif
+  endif
+  return revNum
+endfunction
+
+"---------------------------------------------------------------------------
+" One of a set of functions that returns fields from the p4 fstat command
+function! s:IsCurrent()
+  let revdiff = b:p4HeadRev - b:p4HaveRev
+  if revdiff == 0
+    return 0
+  else
+    return -1
+  endif
+endfunction
+
+function! s:CheckOutFile()
+  if ! g:p4PromptToCheckout || ! s:IsFileUnderDepot(expand('%:p'))
+    return
+  endif
+  " If we know that the file is deleted from the depot, don't prompt.
+  if exists('b:p4Action') && b:p4Action == '<Deleted>'
+    return
+  endif
+
+  if filereadable(expand("%")) && ! filewritable(expand("%"))
+    let option = s:ConfirmMessage("Readonly file, do you want to checkout " .
+          \ "from perforce?", "&Yes\n&No\n&Cancel", s:_('CheckOutDefault'),
+          \ "Question")
+    if option == 1
+      call perforce#PFIF(1, 2, 'edit')
+      if ! s:errCode
+        edit
+        call perforce#GetFileStatus(expand('<abuf>') + 0, 1)
+      endif
+    elseif option == 3
+      call s:CancelEdit(0)
+    endif
+  endif
+endfunction
+
+function! s:CancelEdit(stage)
+  aug P4CancelEdit
+    au!
+    if a:stage == 0
+      au CursorMovedI <buffer> nested :call <SID>CancelEdit(1)
+      au CursorMoved <buffer> nested :call <SID>CancelEdit(1)
+    elseif a:stage == 1
+      stopinsert
+      silent undo
+      setl readonly
+    endif
+  aug END
+endfunction
+
+function! perforce#FileChangedShell()
+  let bufNr = expand("<abuf>") + 0
+  if s:_('EnableActiveStatus')
+    call s:ResetFileStatusForBuffer(bufNr)
+  endif
+  let autoread = -1
+  if index(s:autoreadCmds, s:currentCommand) != -1
+    let autoread = s:_('Autoread')
+    if autoread
+      call setbufvar(bufNr, '&readonly', 0)
+    endif
+  endif
+  return autoread
+endfunction
+""" END: Adapted from Tom's perforce plugin. }}}
+
+""" END: Middleware functions }}}
+
+
+""" BEGIN: Infrastructure {{{
+
+" Assumes that the arguments are already parsed and are ready to be used in
+"   the script variables.
+" Low level interface with the p4 command.
+" clearBuffer: If the buffer contents should be cleared before
+"     adding the new output (See s:GotoWindow).
+" testMode (number):
+"   0 - Run normally.
+"   1 - testing, ignore.
+"   2 - debugging, display the command-line instead of the actual output..
+" Returns the output if available. If there is any error, the error code will
+"   be available in s:errCode variable.
+function! s:PFImpl(clearBuffer, testMode) " {{{
+  try " [-2f]
+
+  let s:errCode = 0
+  let p4Options = s:GetP4Options()
+  let fullCmd = s:CreateFullCmd(s:MakeP4ArgList(p4Options, 0))
+  " Save the name of the current file.
+  let p4OrgFileName = s:GetCurFileName()
+
+  let s:currentCommand = ''
+  " Make sure all the already existing changes are detected. We don't have
+  "     s:currentCommand set here, so the user will get an appropriate prompt.
+  try
+    checktime
+  catch
+    " FIXME: Ignore error for now.
+  endtry
+
+  " If the output has to be shown in a window, position cursor appropriately,
+  " creating a new window if required.
+  let v:errmsg = ""
+  " Ignore outputType in this case.
+  if s:commandMode != s:CM_PIPE && s:commandMode != s:CM_FILTER
+    if s:outputType == 0 || s:outputType == 1
+      " Only when "clear with undo" is selected, we optimize out the call.
+      if s:GotoWindow(s:outputType,
+            \ (!s:refreshWindowsAlways && (a:clearBuffer == 1)) ?
+            \  2 : a:clearBuffer, p4OrgFileName, 0) != 0
+        return s:errCode
+      endif
+    endif
+  endif
+
+  let output = ''
+  if s:errCode == 0
+    if ! a:testMode
+      let s:currentCommand = s:p4Command
+      if s:_('EnableFileChangedShell')
+        call genutils#DefFCShellInstall()
+      endif
+
+      try
+        if s:commandMode ==# s:CM_RUN
+          " Only when "clear with undo" is selected, we optimize out the call.
+          if s:refreshWindowsAlways ||
+                \ ((!s:refreshWindowsAlways && (a:clearBuffer == 1)) &&
+                \  (line('$') == 1 && getline(1) =~ '^\s*$'))
+            " If we are placing the output in a new window, then we should
+            "   avoid system() for performance reasons, imagine doing a
+            "   'print' on a huge file.
+            " These two outputType's correspond to placing the output in a
+            "   window.
+            if s:outputType != 0 && s:outputType != 1
+              let output = s:System(fullCmd)
+            else
+              exec '.call s:Filter(fullCmd, 1)'
+              let output = ''
+            endif
+          endif
+        elseif s:commandMode ==# s:CM_FILTER
+          exec s:filterRange . 'call s:Filter(fullCmd, 1)'
+        elseif s:commandMode ==# s:CM_PIPE
+          exec s:filterRange . 'call s:Filter(fullCmd, 2)'
+        endif
+        " Detect any new changes to the loaded buffers.
+        " CAUTION: This actually results in a reentrant call back to this
+        "   function, but our Push/Pop mechanism for the context should take
+        "   care of it.
+        try
+          checktime
+        catch
+          " FIXME: Ignore error for now.
+        endtry
+      finally
+        if s:_('EnableFileChangedShell')
+          call genutils#DefFCShellUninstall()
+        endif
+        let s:currentCommand = ''
+      endtry
+    elseif a:testMode != 1
+      let output = fullCmd
+    endif
+
+    let v:errmsg = ""
+    " If we have non-null output, then handling it is still pending.
+    if output !~# s:EMPTY_STR
+      let echoGrp = 'NONE' " The default
+      let maxLinesInDlg = s:_('MaxLinesInDialog')
+      if s:outputType == 2 && maxLinesInDlg != -1
+        " Count NLs.
+        let nNLs = 0
+        let nlIdx = 0
+        while 1
+          let nlIdx = stridx(output, "\n", nlIdx+1)
+          if nlIdx != -1
+            let nNLs += 1
+            if nNLs > maxLinesInDlg
+              break
+            endif
+          else
+            break
+          endif
+        endwhile
+        if nNLs > maxLinesInDlg
+          " NOTE: Keep in sync with that at the start of the function.
+          if s:GotoWindow(s:outputType,
+                \ (!s:refreshWindowsAlways && (a:clearBuffer == 1)) ?
+                \  2 : a:clearBuffer, p4OrgFileName, 0) == 0
+            let s:outputType = 0
+          else
+            let s:outputType = 3
+            let echoGrp = 'WarningMsg'
+          endif
+        endif
+      endif
+      " If the output has to be shown in a dialog, bring up a dialog with the
+      "   output, otherwise show it in the current window.
+      if s:outputType == 0 || s:outputType == 1
+        silent! put! =output
+        1
+      elseif s:outputType == 2
+        call s:ConfirmMessage(output, "OK", 1, "Info")
+      elseif s:outputType == 3
+        call s:EchoMessage(output, echoGrp)
+      elseif s:outputType == 4
+        " Do nothing we will just return it.
+      endif
+    endif
+  endif
+  return output
+
+  finally " [+2s]
+    call s:InitWindow(p4Options)
+  endtry
+endfunction " }}}
+
+function! s:NewWindowCreated()
+  if (s:outputType == 0 || s:outputType == 1) && s:errCode == 0 &&
+        \ (s:commandMode ==# s:CM_RUN)
+    return 1
+  else
+    return 0
+  endif
+endfunction
+
+function! s:setBufSetting(opt, set)
+  let optArg = matchstr(b:p4Options, '\%(\S\)\@<!-'.a:opt.'\s\+\S\+')
+  if optArg !~# s:EMPTY_STR
+    let b:p4Options = substitute(b:p4Options, '\V'.optArg, '', '')
+    let b:{a:set} = matchstr(optArg, '-'.a:opt.'\s\+\zs.*')
+  endif
+endfunction
+
+" These p4Options are frozen according to the current s:p4Options.
+function! s:InitWindow(p4Options)
+  if s:NewWindowCreated()
+    let b:p4Command = s:p4Command
+    let b:p4Options = a:p4Options
+    let b:p4CmdOptions = s:p4CmdOptions
+    let b:p4Arguments = s:p4Arguments
+    let b:p4UserArgs = s:userArgs
+    " Separate -p port -c client -u user options and set them individually.
+    " Leave the rest in the b:p4Options variable.
+    call s:setBufSetting('c', 'p4Client')
+    call s:setBufSetting('p', 'p4Port')
+    call s:setBufSetting('u', 'p4User')
+    " Remove any ^M's at the end (for windows), without corrupting the search
+    " register or its history.
+    call genutils#SilentSubstitute("\<CR>$", '%s///e')
+    setlocal nomodifiable
+    setlocal nomodified
+    if s:outputType == 1
+      wincmd p
+    endif
+  endif
+endfunction
+
+" External command execution {{{
+
+function! s:System(fullCmd)
+  return s:ExecCmd(a:fullCmd, 0)
+endfunction
+
+function! s:Filter(fullCmd, mode) range
+  " For command-line, we need to protect '%', '#' and '!' chars, even if they
+  "   are in quotes, to avoid getting expanded by Vim before invoking external
+  "   cmd.
+  let fullCmd = genutils#Escape(a:fullCmd, '%#!')
+  exec a:firstline.",".a:lastline.
+        \ "call s:ExecCmd(fullCmd, a:mode)"
+endfunction
+
+function! s:ExecCmd(fullCmd, mode) range
+  let v:errmsg = ''
+  let output = ''
+  try
+    " Assume the shellredir is set correctly to capture the error messages.
+    if a:mode == 0
+      let output = system(a:fullCmd)
+    elseif a:mode == 1
+      silent! exec a:firstline.",".a:lastline."!".a:fullCmd
+    else
+      silent! exec a:firstline.",".a:lastline."write !".a:fullCmd
+    endif
+
+    call s:CheckShellError(output, s:outputType)
+    return output
+  catch /^Vim\%((\a\+)\)\=:E/ " 48[2-5]
+    let v:errmsg = substitute(v:exception, '^[^:]\+:', '', '')
+    call s:CheckShellError(output, s:outputType)
+  catch /^Vim:Interrupt$/
+    let s:errCode = 1
+    let v:errmsg = 'Interrupted'
+  catch " Ignore.
+  endtry
+endfunction
+
+function! s:EvalExpr(expr, def)
+  let result = a:def
+  if a:expr !~# s:EMPTY_STR
+    exec "let result = " . a:expr
+  endif
+  return result
+endfunction
+
+function! s:GetP4Options()
+  let addOptions = []
+
+  " If there are duplicates, perfore takes the first option, so let
+  "   s:p4Options or b:p4Options come before g:p4DefaultOptions.
+  " LIMITATATION: We choose either s:p4Options or b:p4Options only. But this
+  "   shouldn't be a big issue as this feature is meant for executing more
+  "   commands on the p4 result windows only.
+  if len(s:p4Options) != 0
+    call extend(addOptions, s:p4Options)
+  elseif exists('b:p4Options') && len(b:p4Options) != 0
+    call extend(addOptions, b:p4Options)
+  endif
+
+  " FIXME: avoid split here.
+  call extend(addOptions, split(s:_('DefaultOptions'), ' '))
+
+  let p4Client = s:p4Client
+  let p4User = s:p4User
+  let p4Port = s:p4Port
+  try
+    if s:p4Port !=# 'P4CONFIG'
+      if s:_('CurPresetExpr') !~# s:EMPTY_STR
+        let preset = s:EvalExpr(s:_('CurPresetExpr'), '')
+        if preset ~= s:EMPTY_STR
+          call perforce#PFSwitch(0, preset)
+        endif
+      endif
+
+      if s:_('p4Client') !~# s:EMPTY_STR && index(addOptions, '-c') == -1
+        call add(add(addOptions, '-c'), s:_('p4Client'))
+      endif
+      if s:_('p4User') !~# s:EMPTY_STR && index(addOptions, '-u') == -1
+        call add(add(addOptions, '-u'), s:_('p4User'))
+      endif
+      if s:_('p4Port') !~# s:EMPTY_STR && index(addOptions, '-p') == -1
+        call add(add(addOptions, '-p'), s:_('p4Port'))
+      endif
+      " Don't pass password with '-P' option, it will be too open (ps will show
+      "   it up).
+      let $P4PASSWD = s:p4Password
+    else
+    endif
+  finally
+    let s:p4Client = p4Client
+    let s:p4User = p4User
+    let s:p4Port = p4Port
+  endtry
+  
+  return addOptions
+endfunction
+
+function! s:CreateFullCmd(argList)
+  let fullCmd = genutils#EscapeCommand(s:p4CommandPrefix.s:_('CmdPath'), a:argList,
+        \ s:p4Pipe)
+  let g:p4FullCmd = fullCmd
+  return fullCmd
+endfunction
+
+" Generates a command string as the user typed, using the script variables.
+function! s:MakeP4ArgList(p4Options, useBufLocal)
+  if a:useBufLocal && exists('b:p4Command')
+    let p4Command = b:p4Command
+  else
+    let p4Command = s:p4Command
+  endif
+  if a:useBufLocal && exists('b:p4CmdOptions')
+    let p4CmdOptions = b:p4CmdOptions
+  else
+    let p4CmdOptions = s:p4CmdOptions
+  endif
+  if a:useBufLocal && exists('b:p4Arguments')
+    let p4Arguments = b:p4Arguments
+  else
+    let p4Arguments = s:p4Arguments
+  endif
+  let cmdList = a:p4Options+[p4Command]+p4CmdOptions+p4Arguments
+  " Remove the protection from the characters that we treat specially (Note: #
+  "   and % are treated specially by Vim command-line itself, and the
+  "   back-slashes are removed even before we see them.)
+  call map(cmdList, "genutils#UnEscape(v:val, '&')")
+  return cmdList
+endfunction
+
+" In case of outputType == 4, it assumes the caller wants to see the output as
+" it is, so no error message is given. The caller is expected to check for
+" error code, though.
+function! s:CheckShellError(output, outputType)
+  if (v:shell_error != 0 || v:errmsg != '') && a:outputType != 4
+    let output = "There was an error executing external p4 command.\n"
+    if v:errmsg != ''
+      let output = output . "\n" . "errmsg = " . v:errmsg
+    endif
+    " When commandMode ==# s:CM_RUN, the error message may already be there in
+    "   the current window.
+    if a:output !~# s:EMPTY_STR
+      let output = output . "\n" . a:output
+    elseif a:output =~# s:EMPTY_STR &&
+          \ (s:commandMode ==# s:CM_RUN && line('$') == 1 && col('$') == 1)
+      let output = output . "\n\n" .
+            \ "Check if your 'shellredir' option captures error messages."
+    endif
+    call s:ConfirmMessage(output, "OK", 1, "Error")
+  endif
+  let s:errCode = v:shell_error
+  return v:shell_error
+endfunction
+
+" External command execution }}}
+
+" Push/Pop/Peek context {{{
+function! s:PushP4Context()
+  call add(s:p4Contexts, s:GetP4ContextVars())
+endfunction
+
+function! s:PeekP4Context()
+  return s:PopP4ContextImpl(1, 1)
+endfunction
+
+function! s:PopP4Context(...)
+  " By default carry forward error.
+  return s:PopP4ContextImpl(0, (a:0 ? a:1 : 1))
+endfunction
+
+function! s:NumP4Contexts()
+  return len(s:p4Contexts)
+endfunction
+
+function! s:PopP4ContextImpl(peek, carryFwdErr)
+  let nContexts = len(s:p4Contexts)
+  if nContexts <= 0
+    echoerr "PopP4Context: Contexts stack is empty"
+    return
+  endif
+  let context = s:p4Contexts[-1]
+  if !a:peek
+    call remove(s:p4Contexts, nContexts-1)
+  endif
+
+  call s:SetP4ContextVars(context, a:carryFwdErr)
+  return context
+endfunction
+
+" Serialize p4 context variables.
+function! s:GetP4ContextVars()
+  return [s:p4Options , s:p4Command , s:p4CmdOptions , s:p4Arguments ,
+        \ s:p4Pipe , s:p4WinName , s:commandMode , s:filterRange ,
+        \ s:outputType , s:errCode, s:userArgs]
+endfunction
+
+" De-serialize p4 context variables.
+function! s:SetP4ContextVars(context, ...)
+  let carryFwdErr = 0
+  if a:0 && a:1
+    let carryFwdErr = s:errCode
+  endif
+
+  let [s:p4Options, s:p4Command, s:p4CmdOptions, s:p4Arguments, s:p4Pipe,
+        \ s:p4WinName, s:commandMode, s:filterRange, s:outputType, s:errCode,
+        \ s:userArgs] = a:context
+  let s:errCode = s:errCode + carryFwdErr
+endfunction
+" Push/Pop/Peek context }}}
+
+""" BEGIN: Argument parsing {{{
+function! s:ResetP4ContextVars()
+  " Syntax is:
+  "   PF <p4Options> <p4Command> <p4CmdOptions> <p4Arguments> | <p4Pipe>
+  " Ex: PF -c hari integrate -b branch -s <fromFile> <toFile>
+  let s:p4Options = []
+  let s:p4Command = ""
+  let s:p4CmdOptions = []
+  let s:p4Arguments = []
+  let s:p4Pipe = []
+  let s:p4WinName = ""
+  " commandMode:
+  "   run - Execute p4 using system() or its equivalent.
+  "   filter - Execute p4 as a filter for the current window contents. Use
+  "            commandPrefix to restrict the filter range.
+  "   display - Don't execute p4. The output is already passed in.
+  let s:commandMode = "run"
+  let s:filterRange = ""
+  let s:outputType = 0
+  let s:errCode = 0
+
+  " Special variable to keep track of full user arguments.
+  let s:userArgs = []
+endfunction
+call s:ResetP4ContextVars() " Let them get initialized the first time.
+
+" Parses the arguments into 4 parts, "options to p4", "p4 command",
+" "options to p4 command", "actual arguments". Also generates the window name.
+" outputType (string):
+"   0 - Execute p4 and place the output in a new window.
+"   1 - Same as above, but use preview window.
+"   2 - Execute p4 and show the output in a dialog for confirmation.
+"   3 - Execute p4 and echo the output.
+"   4 - Execute p4 and return the output.
+"   5 - Execute p4 no output expected. Essentially same as 4 when the current
+"       commandMode doesn't produce any output, just for clarification.
+function! s:ParseOptions(fline, lline, outputType, ...) " range
+  call s:ResetP4ContextVars()
+  let s:outputType = a:outputType
+  if a:0 == 0
+    return
+  endif
+
+  let s:filterRange = a:fline . ',' . a:lline
+  let i = 1
+  let prevArg = ""
+  let curArg = ""
+  let s:pendingPipeArg = ''
+  while i <= a:0
+    try " Just for the sake of loop variables. [-2f]
+
+    if s:pendingPipeArg !~# s:EMPTY_STR
+      let curArg = s:pendingPipeArg
+      let s:pendingPipeArg = ''
+    elseif len(s:p4Pipe) == 0
+      let curArg = a:000[i-1]
+      " The user can't specify a null string on the command-line, this is an
+      "   argument originating from the script, so just ignore it (just for
+      "   the sake of convenience, see PChangesDiff for a possibility).
+      if curArg == ''
+        continue
+      endif
+      let pipeIndex = match(curArg, '\\\@<!\%(\\\\\)*\zs|')
+      if pipeIndex != -1
+        let pipePart = strpart(curArg, pipeIndex)
+        let p4Part = strpart(curArg, 0, pipeIndex)
+        if p4Part !~# s:EMPTY_STR
+          let curArg = p4Part
+          let s:pendingPipeArg = pipePart
+        else
+          let curArg = pipePart
+        endif
+      endif
+    else
+      let curArg = a:000[i-1]
+    endif
+
+    if curArg ==# '<pfitem>'
+      let curItem = s:GetCurrentItem()
+      if curItem !~# s:EMPTY_STR
+        let curArg = curItem
+      endif
+    endif
+
+    " As we use custom completion mode, the filename meta-sequences in the
+    "   arguments will not be expanded by Vim automatically, so we need to
+    "   expand them manually here. On the other hand, this provides us control
+    "   on what to expand, so we can avoid expanding perforce file revision
+    "   numbers as buffernames (escaping is no longer required by the user on
+    "   the commandline).
+    let fileRev = ''
+    let fileRevIndex = match(curArg, '#\(-\?\d\+\|none\|head\|have\)$')
+    if fileRevIndex != -1
+      let fileRev = strpart(curArg, fileRevIndex)
+      let curArg = strpart(curArg, 0, fileRevIndex)
+    endif
+    if curArg != '' && (!exists('g:p4EnableUserFileExpand') ||
+          \ g:p4EnableUserFileExpand)
+      let curArg = genutils#UserFileExpand(curArg)
+    endif
+    if fileRev != ''
+      let curArg = curArg.fileRev
+    endif
+
+    if curArg =~# '^|' || len(s:p4Pipe) != 0
+      call add(s:p4Pipe, curArg)
+      continue
+    endif
+
+    if ! s:IsAnOption(curArg) " If not an option.
+      if s:p4Command =~# s:EMPTY_STR &&
+            \ index(s:allCommands, curArg) != -1
+        " If the previous one was an option to p4 that takes in an argument.
+        if prevArg =~# '^-[cCdHLpPux]$' || prevArg =~# '^++o$' " See :PH usage.
+          call add(s:p4Options, curArg)
+          if prevArg ==# '++o' && (curArg == '0' || curArg == 1)
+            let s:outputType = curArg
+          endif
+        else
+          let s:p4Command = curArg
+        endif
+      else " Argument is not a perforce command.
+        if s:p4Command =~# s:EMPTY_STR
+          call add(s:p4Options, curArg)
+        else
+          let optArg = 0
+          " Look for options that have an argument, so we can collect this
+          " into p4CmdOptions instead of p4Arguments.
+          if len(s:p4Arguments) == 0 && s:IsAnOption(prevArg)
+            " We could as well just check for the option here, but combining
+            " this with the command name will increase the accuracy of finding
+            " the starting point for p4Arguments.
+            if (prevArg[0] ==# '-' && has_key(s:p4OptCmdMap, prevArg[1]) &&
+                  \ index(s:p4OptCmdMap[prevArg[1]], s:p4Command) != -1) ||
+             \ (prevArg =~# '^++' && has_key(s:biOptCmdMap, prevArg[2]) &&
+                  \ index(s:biOptCmdMap[prevArg[2]], s:p4Command) != -1)
+              let optArg = 1
+            endif
+          endif
+
+          if optArg
+            call add(s:p4CmdOptions, curArg)
+          else
+            call add(s:p4Arguments, curArg)
+          endif
+        endif
+      endif
+    else
+      if len(s:p4Arguments) == 0
+        if s:p4Command =~# s:EMPTY_STR
+          if curArg =~# '^++[pfdr]$'
+            if curArg ==# '++p'
+              let s:commandMode = s:CM_PIPE
+            elseif curArg ==# '++f'
+              let s:commandMode = s:CM_FILTER
+            elseif curArg ==# '++r'
+              let s:commandMode = s:CM_RUN
+            endif
+            continue
+          endif
+          call add(s:p4Options, curArg)
+        else
+          call add(s:p4CmdOptions, curArg)
+        endif
+      else
+        call add(s:p4Arguments,  curArg)
+      endif
+    endif
+   " The "-x -" option requires it to act like a filter.
+    if s:p4Command =~# s:EMPTY_STR && prevArg ==# '-x' && curArg ==# '-'
+      let s:commandMode = s:CM_FILTER
+    endif
+
+    finally " [+2s]
+      if s:pendingPipeArg =~# s:EMPTY_STR
+        let i = i + 1
+      endif
+      let prevArg = curArg
+    endtry
+  endwhile
+
+  if index(s:p4Options, '-d') == -1
+    let curDir = s:EvalExpr(s:_('CurDirExpr'), '')
+    if curDir !=# ''
+      call add(add(s:p4Options, '-d'), s:EscapeFileName(curDir))
+    endif
+  endif
+  let s:p4WinName = s:MakeWindowName()
+endfunction
+
+function! s:IsAnOption(arg)
+  if a:arg =~# '^-.$' || a:arg =~# '^-d\%([cnsubw]\|\d\+\)*$' ||
+        \ a:arg =~# '^-a[fmsty]$' || a:arg =~# '^-s[ader]$' ||
+        \ a:arg =~# '^-qu$' || a:arg =~# '^+'
+    return 1
+  else
+    return 0
+  endif
+endfunction
+
+function! s:CleanSpaces(str)
+  " Though not complete, it is enough to just say,
+  "   "spaces that are not preceded by \'s".
+  return substitute(substitute(a:str, '^ \+\|\%(\\\@<! \)\+$', '', 'g'),
+        \ '\%(\\\@<! \)\+', ' ', 'g')
+endfunction
+
+function! s:_(set)
+  let set = a:set
+  if set =~# '^\u'
+    let set = 'p4'.set
+  endif
+  if exists('b:'.set)
+    return b:{set}
+  elseif exists('w:'.set)
+    return w:{set}
+  elseif exists('t:'.set)
+    return t:{set}
+  elseif exists('s:'.set)
+    return s:{set}
+  elseif exists('g:'.set)
+    return g:{set}
+  else
+    echoerr 'No setting found for: ' set
+  endif
+endfunction
+
+function! s:indexMatching(list, pat)
+  let i = 0
+  for item in a:list
+    if item =~ a:pat
+      return i
+    endif
+    let i += 1
+  endfor
+  return -1
+endfunction
+""" END: Argument parsing }}}
+
+""" BEGIN: Messages and dialogs {{{
+function! s:SyntaxError(msg)
+  let s:errCode = 1
+  call s:ConfirmMessage("Syntax Error:\n".a:msg, "OK", 1, "Error")
+  return s:errCode
+endfunction
+
+function! s:ShowVimError(errmsg, stack)
+  call s:ConfirmMessage("There was an error executing a Vim command.\n\t" .
+        \ a:errmsg.(a:stack != '' ? "\nCurrent stack: ".a:stack : ''), "OK", 1,
+        \ "Error")
+  echohl ErrorMsg | echomsg a:errmsg | echohl None
+  if a:stack != ''
+    echomsg "Current stack:" a:stack
+  endif
+  redraw " Cls, such that it is only available in the message list.
+  let s:errCode = 1
+  return s:errCode
+endfunction
+
+function! s:EchoMessage(msg, type)
+  let s:lastMsg = a:msg
+  let s:lastMsgGrp = a:type
+  redraw | exec 'echohl' a:type | echo a:msg | echohl NONE
+endfunction
+
+function! s:ConfirmMessage(msg, opts, def, type)
+  let s:lastMsg = a:msg
+  let s:lastMsgGrp = 'None'
+  if a:type ==# 'Error'
+    let s:lastMsgGrp = 'Error'
+  endif
+  return confirm(a:msg, a:opts, a:def, a:type)
+endfunction
+
+function! s:PromptFor(loop, useDialogs, msg, default)
+  let result = ""
+  while result =~# s:EMPTY_STR
+    if a:useDialogs
+      let result = inputdialog(a:msg, a:default)
+    else
+      let result = input(a:msg, a:default)
+    endif
+    if ! a:loop
+      break
+    endif
+  endwhile
+  return result
+endfunction
+
+function! perforce#LastMessage()
+  call s:EchoMessage(s:lastMsg, s:lastMsgGrp)
+endfunction
+""" END: Messages and dialogs }}}
+
+""" BEGIN: Filename handling {{{
+" Escape all the special characters (as the user would if he typed the name
+"   himself).
+function! s:EscapeFileName(fName)
+  " If there is a -d option existing, then it is better to use the full path
+  "   name.
+  if index(s:p4Options, '-d')  != -1
+    let fName = fnamemodify(a:fName, ':p')
+  else
+    let fName = a:fName
+  endif
+  return genutils#Escape(fName, ' &|')
+endfunction
+
+function! s:GetCurFileName()
+  " When the current window itself is a perforce window, then carry over the
+  " existing value.
+  return (exists('b:p4OrgFileName') &&
+        \              b:p4OrgFileName !~# s:EMPTY_STR) ?
+        \             b:p4OrgFileName : expand('%:p')
+endfunction
+
+function! s:GuessFileTypeForCurrentWindow()
+  let fileExt = s:GuessFileType(b:p4OrgFileName)
+  if fileExt =~# s:EMPTY_STR
+    let fileExt = s:GuessFileType(expand("%"))
+  endif
+  return fileExt
+endfunction
+
+function! s:GuessFileType(name)
+  let fileExt = fnamemodify(a:name, ":e")
+  return matchstr(fileExt, '\w\+')
+endfunction
+
+function! s:IsDepotPath(path)
+  if match(a:path, '^//'.s:_('Depot').'/') == 0
+        " \ || match(a:path, '^//'. s:_('p4Client') . '/') == 0
+    return 1
+  else
+    return 0
+  endif
+endfunction
+
+function! s:PathRefersToDepot(path)
+  if s:IsDepotPath(a:path) || s:GetRevisionSpecifier(a:path) !~# s:EMPTY_STR
+    return 1
+  else
+    return 0
+  endif
+endfunction
+
+function! s:GetRevisionSpecifier(fileName)
+  return matchstr(a:fileName,
+        \ '^\(\%(\S\|\\\@<!\%(\\\\\)*\\ \)\+\)[\\]*\zs[#@].*$')
+endfunction
+
+" Removes the //<depot> or //<client> prefix from fileName.
+function! s:StripRemotePath(fileName)
+  "return substitute(a:fileName, '//\%('.s:_('Depot').'\|'.s:_('p4Client').'\)', '', '')
+  return substitute(a:fileName, '//\%('.s:_('Depot').'\)', '', '')
+endfunction
+
+" Client view translation {{{
+" Convert perforce file wildcards ("*", "..." and "%[1-9]") to a Vim string
+"   regex (see |pattern.txt|). Returns patterns that work when "very nomagic"
+"   is set.
+let s:p4Wild = {}
+function! s:TranlsateP4Wild(p4Wild, rhsView)
+  let strRegex = ''
+  if a:rhsView
+    if a:p4Wild[0] ==# '%'
+      let pos = s:p4WildMap[a:p4Wild[1]]
+    else
+      let pos = s:p4WildCount
+    endif
+    let strRegex = '\'.pos
+  else
+    if a:p4Wild ==# '*'
+      let strRegex = '\(\[^/]\*\)'
+    elseif a:p4Wild ==# '...'
+      let strRegex = '\(\.\*\)'
+    elseif a:p4Wild[0] ==# '%'
+      let strRegex = '\(\[^/]\*\)'
+      let s:p4WildMap[a:p4Wild[1]] = s:p4WildCount
+    endif
+  endif
+  let s:p4WildCount = s:p4WildCount + 1
+  return strRegex
+endfunction
+
+" Convert perforce file regex (containing "*", "..." and "%[1-9]") to a Vim
+"   string regex. No error checks for now, for simplicity.
+function! s:TranslateP4FileRegex(p4Regex, rhsView)
+  let s:p4WildCount = 1
+  " Note: We don't expect backslashes in the views, so no special handling.
+  return substitute(a:p4Regex,
+        \ '\(\*\|\%(\.\)\@<!\.\.\.\%(\.\)\@!\|%\([1-9]\)\)',
+        \ '\=s:TranlsateP4Wild(submatch(1), a:rhsView)', 'g')
+endfunction
+
+function! s:CondUpdateViewMappings()
+  if s:_('UseClientViewMap') &&
+        \ (!has_key(s:toDepotMapping, s:_("p4Client")) ||
+        \  (len(s:toDepotMapping[s:_('p4Client')]) < 0))
+    call perforce#UpdateViewMappings()
+  endif
+endfunction
+
+function! perforce#UpdateViewMappings()
+  if s:_('p4Client') =~# s:EMPTY_STR
+    return
+  endif
+  let view = ''
+  call s:PushP4Context()
+  try
+    let view = substitute(perforce#PFIF(1, 4, '-c', s:_('p4Client'), 'client'),
+          \ "\\_.*\nView:\\ze\n", '', 'g')
+    if s:errCode != 0
+      return
+    endif
+  finally
+    call s:PopP4Context(0)
+  endtry
+  let fromDepotMapping = []
+  let toDepotMapping = []
+  for nextMap in reverse(split(view, "\n"))
+    " We need to inverse the order of mapping such that the mappings that come
+    "   later in the view take more priority.
+    " Also, don't care about exclusionary mappings for simplicity (this could
+    "   be considered a feature too).
+    exec substitute(nextMap,
+          \ '\s*-\?//'.s:_('Depot').'/\([^ ]\+\)\s*//'.s:_("p4Client").'/\(.\+\)',
+          \ 'call add(fromDepotMapping, [s:TranslateP4FileRegex('.
+          \ "'".'\1'."'".', 0), s:TranslateP4FileRegex('."'".'\2'."'".
+          \ ', 1)])', '')
+    exec substitute(nextMap,
+          \ '\s*-\?//'.s:_('Depot').'/\([^ ]\+\)\s*//'.s:_("p4Client").'/\(.\+\)',
+          \ 'call add(toDepotMapping, [s:TranslateP4FileRegex('.
+          \ "'".'\2'."'".', 0), s:TranslateP4FileRegex('."'".'\1'."'".
+          \ ', 1)])', '')
+  endfor
+  let s:fromDepotMapping[s:_('p4Client')] = fromDepotMapping
+  let s:toDepotMapping[s:_('p4Client')] = toDepotMapping
+endfunction
+
+function! P4IncludeExpr(path)
+  return s:ConvertToLocalPath(a:path)
+endfunction
+
+function! s:ConvertToLocalPath(path)
+  let fileName = substitute(a:path, '^\s\+\|\s*#[^#]\+$', '', 'g')
+  if s:IsDepotPath(fileName)
+    if s:_('UseClientViewMap')
+      call s:CondUpdateViewMappings()
+      for nextMap in s:fromDepotMapping[s:_('p4Client')]
+        let [lhs, rhs] = nextMap
+        if fileName =~# '\V'.lhs
+          let fileName = substitute(fileName, '\V'.lhs, rhs, '')
+          break
+        endif
+      endfor
+    endif
+    if s:IsDepotPath(fileName)
+      let fileName = s:_('ClientRoot') . s:StripRemotePath(fileName)
+    endif
+  endif
+  return fileName
+endfunction
+
+function! s:ConvertToDepotPath(path)
+  " If already a depot path, just return it without any changes.
+  if s:IsDepotPath(a:path)
+    let fileName = a:path
+  else
+    let fileName = genutils#CleanupFileName(a:path)
+    if s:IsFileUnderDepot(fileName)
+      if s:_('UseClientViewMap')
+        call s:CondUpdateViewMappings()
+        for nextMap in s:toDepotMapping[s:_('p4Client')]
+          let [lhs, rhs] = nextMap
+          if fileName =~# '\V'.lhs
+            let fileName = substitute(fileName, '\V'.lhs, rhs, '')
+            break
+          endif
+        endfor
+      endif
+      if ! s:IsDepotPath(fileName)
+        let fileName = substitute(fileName, '^'.s:_('ClientRoot'),
+              \ '//'.s:_('Depot'), '')
+      endif
+    endif
+  endif
+  return fileName
+endfunction
+" Client view translation }}}
+
+" Requires at least 2 arguments.
+" Returns a List of alternative filenames.
+function! s:PFGetAltFiles(protectedChars, codeline, ...)
+  if a:0 == 0
+    return []
+  endif
+
+  let altCodeLine = a:codeline
+
+  let i = 1
+  let altFiles = []
+  while i <= a:0
+    let fileName = a:000[i-1]
+    let fileName = genutils#CleanupFileName2(fileName, a:protectedChars)
+
+    if altCodeLine ==# 'local' && s:IsDepotPath(fileName)
+      let altFile = s:ConvertToLocalPath(fileName)
+    elseif ! s:IsDepotPath(fileName)
+      let fileName = s:ConvertToDepotPath(fileName)
+
+      if altCodeLine ==# s:_('Depot')
+        " We do nothing, it is already converted to depot path.
+        let altFile = fileName
+      else
+        " FIXME: Assumes that the current branch name has single path component.
+        let altFile = substitute(fileName, '//'.s:_('Depot').'/[^/]\+',
+              \ '//'.s:_('Depot').'/' . altCodeLine, "")
+        let altFile = s:ConvertToLocalPath(altFile)
+      endif
+    endif
+    call add(altFiles, altFile)
+    let i = i + 1
+  endwhile
+  return altFiles
+endfunction
+
+function! s:IsFileUnderDepot(fileName)
+  let fileName = genutils#CleanupFileName(a:fileName)
+  if fileName =~? '^\V'.s:_('ClientRoot')
+    return 1
+  else
+    return 0
+  endif
+endfunction
+
+" This better take the line as argument, but I need the context of current
+"   buffer contents anyway...
+" I don't need to handle other revision specifiers here, as I won't expect to
+"   see them here (perforce converts them to the appropriate revision number). 
+function! s:GetCurrentDepotFile(lineNo)
+  " Local submissions.
+  let fileName = ""
+  let line = getline(a:lineNo)
+  if match(line, '//'.s:_('Depot').'/.*\(#\d\+\)\?') != -1
+        " \ || match(line, '^//'. s:_('p4Client') . '/.*\(#\d\+\)\?') != -1
+    let fileName = matchstr(line, '//[^/]\+/[^#]*\(#\d\+\)\?')
+  elseif match(line, '\.\.\. #\d\+ .*') != -1
+    " Branches, integrations etc.
+    let fileVer = matchstr(line, '\d\+')
+    call genutils#SaveHardPosition('Perforce')
+    exec a:lineNo
+    if search('^//'.s:_('Depot').'/', 'bW') == 0
+      let fileName = ""
+    else
+      let fileName = substitute(s:GetCurrentDepotFile(line(".")), '#\d\+$', '',
+            \ '')
+      let fileName = fileName . "#" . fileVer
+    endif
+    call genutils#RestoreHardPosition('Perforce')
+    call genutils#ResetHardPosition('Perforce')
+  endif
+  return fileName
+endfunction
+""" END: Filename handling }}}
+
+""" BEGIN: Buffer management, etc. {{{
+" Must be followed by a call to s:EndBufSetup()
+function! s:StartBufSetup()
+  " If the command created a new window, then only do setup.
+  if !s:errCode
+    if s:NewWindowCreated()
+      if s:outputType == 1
+        wincmd p
+      endif
+
+      return 1
+    endif
+  endif
+  return 0
+endfunction
+
+function! s:EndBufSetup()
+  if s:NewWindowCreated()
+    if s:outputType == 1
+      wincmd p
+    endif
+  endif
+endfunction
+
+" Goto/Open window for the current command.
+" clearBuffer (number):
+"   0 - clear with undo.
+"   1 - clear with no undo.
+"   2 - don't clear
+function! s:GotoWindow(outputType, clearBuffer, p4OrgFileName, cmdCompleted)
+  let bufNr = genutils#FindBufferForName(s:p4WinName)
+  " NOTE: Precautionary measure to avoid accidentally matching an existing
+  "   buffer and thus overwriting the contents.
+  if bufNr != -1 && getbufvar(bufNr, '&buftype') == ''
+    return s:BufConflictError(a:cmdCompleted)
+  endif
+
+  " If there is a window for this buffer already, then we will just move
+  "   cursor into it.
+  let curBufnr = bufnr('%')
+  let maxBufNr = bufnr('$')
+  let bufWinnr = bufwinnr(bufNr)
+  let nWindows = genutils#NumberOfWindows()
+  let _eventignore = &eventignore
+  try
+    "set eventignore=BufRead,BufReadPre,BufEnter,BufNewFile
+    set eventignore=all
+    if a:outputType == 1 " Preview
+      let alreadyOpen = 0
+      try
+        wincmd P
+        " No exception, meaning preview window is already open.
+        if winnr() == bufWinnr
+          " The buffer is already visible in the preview window. We don't have
+          " to do anything in this case.
+          let alreadyOpen = 1
+        endif
+      catch /^Vim\%((\a\+)\)\=:E441/
+        " Ignore.
+      endtry
+      if !alreadyOpen
+        call s:EditP4WinName(1, nWindows)
+        wincmd P
+      endif
+    elseif bufWinnr != -1
+      call genutils#MoveCursorToWindow(bufWinnr)
+    else
+      exec s:_('SplitCommand')
+      call s:EditP4WinName(0, nWindows)
+    endif
+    if s:errCode == 0
+      " FIXME: If the name didn't originally match with a buffer, we expect
+      "   the s:EditP4WinName() to create a new buffer, but there is a bug in
+      "   Vim, that treats "..." in filenames as ".." resulting in multiple
+      "   names matching the same buffer ( "p4 diff ../.../*.java" and
+      "   "p4 submit ../.../*.java" e.g.). Though I worked around this
+      "   particular bug by avoiding "..." in filenames, this is a good check
+      "   in any case.
+      if bufNr == -1 && bufnr('%') <= maxBufNr
+        return s:BufConflictError(a:cmdCompleted)
+      endif
+      " For navigation.
+      normal! mt
+    endif
+  catch /^Vim\%((\a\+)\)\=:E788/ " Happens during FileChangedRO.
+    return 788 " E788
+  catch
+    return s:ShowVimError("Exception while opening new window.\n" . v:exception,
+          \ v:throwpoint)
+  finally
+    let &eventignore = _eventignore
+  endtry
+  " We now have a new window created, but may be with errors.
+  if s:errCode == 0
+    setlocal noreadonly
+    setlocal modifiable
+    if s:commandMode ==# s:CM_RUN
+      if a:clearBuffer == 1
+        call genutils#OptClearBuffer()
+      elseif a:clearBuffer == 0
+        silent! 0,$delete _
+      endif
+    endif
+
+    let b:p4OrgFileName = a:p4OrgFileName
+    call s:PFSetupBuf(expand('%'))
+  else
+    " Window is created but with an error. We might actually miss the cases
+    "   where a preview operation when the preview window is already open
+    "   fails, and so no additional windows are created, but detecting such
+    "   cases could be error prone, so it is better to leave the buffer in
+    "   this case, rather than making a mistake.
+    if genutils#NumberOfWindows() > nWindows
+      if winbufnr(winnr()) == curBufnr " Error creating buffer itself.
+        close
+      elseif bufname('%') == s:p4WinName
+        " This should even close the window.
+        silent! exec "bwipeout " . bufnr('%')
+      endif
+    endif
+  endif
+  return 0
+endfunction
+
+function! s:BufConflictError(cmdCompleted)
+  return s:ShowVimError('This perforce command resulted in matching an '.
+        \ 'existing buffer. To prevent any demage this could cause '.
+        \ 'the command will be aborted at this point.'.
+        \ (a:cmdCompleted ? ("\nHowever the command completed ".
+        \ (s:errCode ? 'un' : ''). 'successfully.') : ''), '')
+endfunction
+
+function! s:EditP4WinName(preview, nWindows)
+  let fatal = 0
+  let bug = 0
+  let exception = ''
+  let pWindowWasOpen = (genutils#GetPreviewWinnr() != -1)
+  " Some patterns can cause problems.
+  let _wildignore = &wildignore
+  try
+    set wildignore=
+    exec (a:preview?'p':'').'edit' s:p4WinName
+  catch /^Vim\%((\a\+)\)\=:E303/
+    " This is a non-fatal error.
+    let bug = 1 | let exception = v:exception
+    let stack = v:throwpoint
+  catch /^Vim\%((\a\+)\)\=:\%(E77\|E480\)/
+    let bug = 1 | let exception = v:exception | let fatal = 1
+    let stack = v:throwpoint
+  catch
+    let exception = v:exception | let fatal = 1
+    let stack = v:throwpoint
+  finally
+    let &wildignore = _wildignore
+  endtry
+  if fatal
+    call s:ShowVimError(exception, '')
+  endif
+  if bug
+    echohl ERROR
+    echomsg "Please report this error message:"
+    echomsg "\t".exception
+    echomsg
+    echomsg "with the following information:"
+    echomsg "\ts:p4WinName:" s:p4WinName
+    echomsg "\tCurrent stack:" stack
+    echohl NONE
+  endif
+  " For non preview operation, or for preview window operation when the preview
+  "   window is not already visible, we expect the number of windows to go up.
+  if !a:preview || (a:preview && !pWindowWasOpen)
+    if a:nWindows >= genutils#NumberOfWindows()
+      let s:errCode = 1
+    endif
+  endif
+endfunction
+
+function! s:MakeWindowName(...)
+  " Let only the options that are explicitly specified appear in the window
+  "   name.
+  if a:0 > 0
+    let cmdStr = a:1
+  else
+    let cmdStr = 'p4 '.join(s:MakeP4ArgList(s:p4Options, 0), ' ')
+  endif
+  let winName = cmdStr
+  "let winName = genutils#DeEscape(winName)
+  " HACK: Work-around for some weird handling of buffer names that have "..."
+  "   (the perforce wildcard) at the end of the filename or in the middle
+  "   followed by a space. The autocommand is not getting triggered to clean
+  "   the buffer. If we append another character to this, I observed that the
+  "   autocommand gets triggered. Using "/" instead of "'" would probably be
+  "   more appropriate, but this is causing unexpected FileChangedShell
+  "   autocommands on certain filenames (try "PF submit ../..." e.g.). There
+  "   is also another issue with "..." (anywhere) getting treated as ".."
+  "   resulting in two names matching the same buffer(
+  "     "p4 diff ../.../*.java" and "p4 submit ../.../*.java" e.g.). This
+  "   could also change the name of the buffer during the :cd operations
+  "   (though applies only to spec buffers).
+  "let winName = substitute(winName, '\.\.\%( \|$\)\@=', '&/', 'g')
+  "let winName = substitute(winName, '\.\.\%( \|$\)\@=', "&'", 'g')
+  let winName = substitute(winName, '\.\.\.', '..,', 'g')
+  " The intention is to do the substitute only on systems like windoze that
+  "   don't allow all characters in the filename, but I can't generalize it
+  "   enough, so as a workaround I a just assuming any system supporting
+  "   'shellslash' option to be a windoze like system. In addition, cygwin
+  "   vim thinks that it is on Unix and tries to allow all characters, but
+  "   since the underlying OS doesn't support it, we need the same treatment
+  "   here also.
+  if exists('+shellslash') || has('win32unix')
+    " Some characters are not allowed in a filename on windows so substitute
+    " them with something else.
+    let winName = substitute(winName, s:specialChars,
+          \ '\="[" . s:specialCharsMap[submatch(1)] . "]"', 'g')
+    "let winName = substitute(winName, s:specialChars, '\\\1', 'g')
+  endif
+  " Finally escape some characters again.
+  let winName = genutils#Escape(winName, " #%\t")
+  if ! exists('+shellslash') " Assuming UNIX environment.
+    let winName = substitute(winName, '\\\@<!\(\%(\\\\\)*\\[^ ]\)', '\\\1', 'g')
+    let winName = escape(winName, "'~$`{\"")
+  endif
+  return winName
+endfunction
+
+function! s:PFSetupBuf(bufName)
+  call genutils#SetupScratchBuffer()
+  let &l:bufhidden=s:_('BufHidden')
+endfunction
+
+function! s:PFSetupForSpec()
+  setlocal modifiable
+  setlocal buftype=
+  exec 'aug Perforce'.bufnr('%')
+    au!
+    au BufWriteCmd <buffer> nested :W
+  aug END
+endfunction
+
+function! perforce#WipeoutP4Buffers(...)
+  let testMode = 1
+  if a:0 > 0 && a:1 ==# '++y'
+    let testMode = 0
+  endif
+  let i = 1
+  let lastBuf = bufnr('$')
+  let cleanedBufs = ''
+  while i <= lastBuf
+    if bufexists(i) && expand('#'.i) =~# '\<p4 ' && bufwinnr(i) == -1
+      if testMode
+        let cleanedBufs = cleanedBufs . ', ' . expand('#'.i)
+      else
+        silent! exec 'bwipeout' i
+        let cleanedBufs = cleanedBufs + 1
+      endif
+    endif
+    let i = i + 1
+  endwhile
+  if testMode
+    echo "Buffers that will be wipedout (Use ++y to perform action):" .
+          \ cleanedBufs
+  else
+    echo "Total Perforce buffers wipedout (start with 'p4 '): " . cleanedBufs
+  endif
+endfunction
+
+function! perforce#PFRefreshActivePane()
+  if exists("b:p4UserArgs")
+    call genutils#SaveSoftPosition('Perforce')
+
+    try
+      silent! undo
+      call call('perforce#PFrangeIF', b:p4UserArgs)
+    catch
+      call s:ShowVimError(v:exception, v:throwpoint)
+    endtry
+
+    call genutils#RestoreSoftPosition('Perforce')
+    call genutils#ResetSoftPosition('Perforce')
+  endif
+endfunction
+""" END: Buffer management, etc. }}}
+
+""" BEGIN: Testing {{{
+" Ex: PFTestCmdParse -c client -u user integrate -b branch -s source target1 target2
+command! -nargs=* -range=% -complete=file PFTestCmdParse
+      \ :call <SID>TestParseOptions(<f-args>)
+function! s:TestParseOptions(commandName, ...) range
+  call call('s:ParseOptionsIF', [a:firstline, a:lastline, 0, 0, a:commandName]+
+        \ a:000)
+  call s:DebugP4Status()
+endfunction
+
+function! s:DebugP4Status()
+  echo "p4Options :" . join(s:p4Options, ' ') . ":"
+  echo "p4Command :" . s:p4Command . ":"
+  echo "p4CmdOptions :" . join(s:p4CmdOptions, ' ') . ":"
+  echo "p4Arguments :" . join(s:p4Arguments, ' ') . ":"
+  echo "p4Pipe :" . join(s:p4Pipe, ' ') . ":"
+  echo "p4WinName :" . s:p4WinName . ":"
+  echo "outputType :" . s:outputType . ":"
+  echo "errCode :" . s:errCode . ":"
+  echo "commandMode :" . s:commandMode . ":"
+  echo "filterRange :" . s:filterRange . ":"
+  echo "Cmd :" . s:CreateFullCmd(s:MakeP4ArgList([''], 0)) . ":"
+endfunction
+
+"function! s:TestPushPopContexts()
+"  let s:p4Options = ["options1"]
+"  let s:p4Command = "command1"
+"  let s:p4CmdOptions = ["cmdOptions1"]
+"  let s:p4Arguments = ["arguments1"]
+"  let s:p4WinName = "winname1"
+"  call s:PushP4Context()
+"
+"  let s:p4Options = ["options2"]
+"  let s:p4Command = "command2"
+"  let s:p4CmdOptions = ["cmdOptions2"]
+"  let s:p4Arguments = ["arguments2"]
+"  let s:p4WinName = "winname2"
+"  call s:PushP4Context()
+"
+"  call s:ResetP4ContextVars()
+"  echo "After reset: " . s:CreateFullCmd(s:MakeP4ArgList([''], 0))
+"  call s:PopP4Context()
+"  echo "After pop1: " . s:CreateFullCmd(s:MakeP4ArgList([''], 0))
+"  call s:PopP4Context()
+"  echo "After pop2: " . s:CreateFullCmd(s:MakeP4ArgList([''], 0))
+"endfunction
+
+""" END: Testing }}}
+
+""" BEGIN: Experimental API {{{
+
+function! perforce#PFGet(var)
+  return {a:var}
+endfunction
+
+function! perforce#PFSet(var, val)
+  let {a:var} = a:val
+endfunction
+
+function! perforce#PFCall(func, ...)
+  let result = call(a:func, a:000)
+  return result
+endfunction
+
+function! perforce#PFEval(expr)
+  exec "let result = ".a:expr
+  return result
+endfunction
+
+""" END: Experimental API }}}
+
+function! perforce#Initialize(initMenu) " {{{
+
+" User Options {{{
+
+if g:p4ClientRoot != ''
+  let g:p4ClientRoot = genutils#CleanupFileName(g:p4ClientRoot)
+endif
+if type(g:p4DefaultListSize) == 0
+  let g:p4DefaultListSize = string(g:p4DefaultListSize)
+endif
+if g:p4FileLauncher == '' && genutils#OnMS()
+  let g:p4FileLauncher = "start rundll32 SHELL32.DLL,ShellExec_RunDLL"
+endif
+if g:p4DefaultPreset != -1 &&
+      \ g:p4DefaultPreset.'' !~# s:EMPTY_STR
+  call perforce#PFSwitch(1, g:p4DefaultPreset)
+endif
+
+
+" Assume the user already has the preferred rulerformat set (which is anyway
+"   going to be done through the .vimrc file which should have been sourced by
+"   now).
+if g:p4EnableRuler
+  " Take care of rerunning this code, as the reinitialization can happen any
+  "   time.
+  if !exists("s:orgRulerFormat")
+    let s:orgRulerFormat = &rulerformat
+  else
+    let &rulerformat = s:orgRulerFormat
+  endif
+
+  if &rulerformat != ""
+    if match(&rulerformat, '^%\d\+') == 0
+      let orgWidth = substitute(&rulerformat, '^%\(\d\+\)(.*$',
+            \ '\1', '')
+      let orgRuler = substitute(&rulerformat, '^%\d\+(\(.*\)%)$', '\1', '')
+    else
+      let orgWidth = strlen(&rulerformat) " Approximate.
+      let orgRuler = &rulerformat
+    endif
+  else
+    let orgWidth = 20
+    let orgRuler = '%l,%c%V%=%5(%p%%%)'
+  endif
+  let &rulerformat = '%' . (orgWidth + g:p4RulerWidth) .  '(%{' .
+        \ 'perforce#RulerStatus()}%=' . orgRuler . '%)'
+else
+  if exists("s:orgRulerFormat")
+    let &rulerformat = s:orgRulerFormat
+  else
+    set rulerformat&
+  endif
+endif
+
+aug P4Active
+  au!
+  if g:p4EnableActiveStatus
+    au BufRead * call perforce#GetFileStatus(expand('<abuf>') + 0, 0)
+  endif
+aug END
+
+" User Options }}}
+
+if a:initMenu
+runtime! perforce/perforcemenu.vim
+let v:errmsg = ''
+endif
+
+let g:p4PromptToCheckout = ! g:p4PromptToCheckout
+call perforce#ToggleCheckOutPrompt(0)
+
+endfunction " s:Initialize }}}
+
+""" END: Infrastructure }}}
+" Do some initializations.
+if g:p4DefaultPreset != -1 &&
+      \ g:p4DefaultPreset.'' !~# s:EMPTY_STR
+  call perforce#PFSwitch(0, g:p4DefaultPreset)
+endif
+
+aug P4ClientRoot
+  au!
+  if g:p4ClientRoot =~# s:EMPTY_STR || s:p4Client =~# s:EMPTY_STR ||
+        \ s:p4User =~# s:EMPTY_STR
+    if s:_('EnableActiveStatus')
+      " If Vim is still starting up (construct suggested by Eric Arnold).
+      if bufnr("$") == 1 && !bufloaded(1)
+        au VimEnter * call <SID>GetClientInfo() | au! P4ClientRoot
+      else
+        call s:GetClientInfo()
+      endif
+    else
+      let g:p4ClientRoot = fnamemodify(".", ":p")
+    endif
+  endif
+aug END
+
+call perforce#Initialize(0)
+
+" WORKAROUND for :redir broken, when called from completion function... just
+" make sure this is initialized early.
+call genutils#MakeArgumentString()
+
+" Restore cpo.
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim6:fdm=marker et sw=2
diff --git a/.vim/autoload/perforceutils.vim b/.vim/autoload/perforceutils.vim
new file mode 100755 (executable)
index 0000000..2009413
--- /dev/null
@@ -0,0 +1,260 @@
+" perforceutils.vim: Please see perforce/perforceutils.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
+
+" Determine the script id.
+function! s:MyScriptId()
+  map <SID>xx <SID>xx
+  let s:sid = maparg("<SID>xx")
+  unmap <SID>xx
+  return substitute(s:sid, "xx$", "", "")
+endfunction
+let s:myScriptId = s:MyScriptId()
+delfunction s:MyScriptId " This is not needed anymore.
+
+" CAUTION: Don't assume the existence of plugin/perforce.vim (or any other
+"   plugins) at the time this file is sourced.
+
+" DiffLink {{{
+" Open the source line for the current line from the diff.
+function! perforceutils#DiffOpenSrc(preview) " {{{
+  let s:EMPTY_STR = perforce#PFEval('s:EMPTY_STR')
+  if perforce#PFEval('s:GetCurrentItem()') !~# s:EMPTY_STR
+    PItemOpen
+  endif
+  call genutils#SaveHardPosition('DiffOpenSrc')
+  " Move to the end of next line (if possible), so that the search will work
+  " correctly when the cursor is ON the header (should find the current line).
+  normal $
+  let filePat = '\zs[^#]\+\%(#\d\+\)\=\ze\%( ([^)]\+)\)\='
+  let diffHdr = '^diff \%(-\S\+\s\+\)*'
+  " Search backwards to find the header for this diff (could contain two
+  " depot files or one depot file with or without a local file).
+  if search('^==== '.filePat.'\%( - '.filePat.'\)\= ====', 'bW')
+    let firstFile = matchstr(getline('.'), '^==== \zs'.filePat.
+          \ '\%( - \| ====\)')
+    let secondFile = matchstr(getline('.'), ' - '.filePat.' ====',
+          \ strlen(firstFile)+5)
+    let foundHeader = 1
+
+  " GNU diff header.
+  elseif search('^--- '.filePat.'.*\n\_^+++ '.filePat, 'bW')
+    let firstFile = matchstr(getline(line('.')-1), '^--- \zs.\{-}\ze\t')
+    let secondFile = matchstr(getline('.'), '^+++ \zs.\{-}\ze\t')
+    let foundHeader = 1
+
+  " Another GNU diff header, for default output (typically for -r option).
+  elseif search(diffHdr.filePat.' '.filePat, 'bW')
+    exec substitute(substitute(getline('.'),
+          \ diffHdr.'\('.filePat.'\) \('.filePat.'\)',
+          \ ":::let firstFile = '\\1' | let secondFile = '\\2'", ''),
+          \ '^.*:::', '', '')
+    let foundHeader = 1
+  else
+    let foundHeader = 0
+  endif
+  if foundHeader
+    call genutils#RestoreHardPosition('DiffOpenSrc')
+    if firstFile =~# s:EMPTY_STR
+      return
+    elseif secondFile =~# s:EMPTY_STR
+      " When there is only one file, then it is treated as the secondFile.
+      let secondFile = firstFile
+      let firstFile = ''
+    endif
+
+    " Search for the start of the diff segment. We could be in default,
+    " context or unified mode. Determine context, stLine and offset.
+    if search('^\d\+\%(,\d\+\)\=[adc]\d\+\%(,\d\+\)\=$', 'bW') " default.
+      let segStLine = line('.')
+      let segHeader = getline('.')
+      call genutils#RestoreHardPosition('DiffOpenSrc')
+      let context = 'depot'
+      let regPre = '^'
+      if getline('.') =~# '^>'
+        let context = 'local'
+        let regPre = '[cad]'
+        if search('^---$', 'bW') && line('.') > segStLine
+          let segStLine = line('.')
+        endif
+      endif
+      let stLine = matchstr(segHeader, regPre.'\zs\d\+\ze')
+      call genutils#RestoreHardPosition('DiffOpenSrc')
+      let offset = line('.') - segStLine - 1
+    elseif search('\([*-]\)\1\1 \d\+,\d\+ \1\{4}', 'bW') " context.
+      if getline('.') =~# '^-'
+        let context = 'local'
+      else
+        let context = 'depot'
+      endif
+      let stLine = matchstr(getline('.'), '^[*-]\{3} \zs\d\+\ze,')
+      let segStLine = line('.')
+      call genutils#RestoreHardPosition('DiffOpenSrc')
+      let offset = line('.') - segStLine - 1
+    elseif search('^@@ -\=\d\+,\d\+ +\=\d\+,\d\+ @@$', 'bW') " unified
+      let segStLine = line('.')
+      let segHeader = getline('.')
+      call genutils#RestoreHardPosition('DiffOpenSrc')
+      let context = 'local'
+      let sign = '+'
+      if getline('.') =~# '^-'
+        let context = 'depot'
+        let sign = '-'
+      endif
+      let stLine = matchstr(segHeader, ' '.sign.'\zs\d\+\ze,\d\+')
+      let _ma = &l:modifiable
+      try
+        setl modifiable
+        " Count the number of lines that come from the other side (those lines
+        "   that start with an opposite sign).
+        let _ss = @/ | let @/ = '^'.substitute('-+', sign, '', '') |
+              \ let offOffset = matchstr(genutils#GetVimCmdOutput( segStLine.',.s//&/'),
+              \ '\d\+\ze substitutions\? on \d\+ lines\?') + 0 | let @/ = _ss
+        if offOffset > 0
+          silent! undo
+          call genutils#RestoreHardPosition('DiffOpenSrc')
+        endif
+        let offset = line('.') - segStLine - 1 - offOffset
+      finally
+        let &l:modifiable = _ma
+      endtry
+    else " Not inside a diff context, just use 1.
+      let context = 'local'
+      let stLine = 1
+      let offset = 0
+    endif
+
+    try
+      if context ==# 'depot' && firstFile =~# s:EMPTY_STR
+        " Assume previous revision as the "before" file if none specified.
+        if perforce#PFCall('s:IsDepotPath', secondFile) && secondFile =~# '#\d\+'
+          let depotRev = s:GetFileRevision(secondFile)
+          if depotRev == ''
+            return
+          endif
+          let firstFile = substitute(secondFile, '#\d\+', '', '').'#'.(depotRev-1)
+        else
+          return
+        endif
+      endif
+      if context ==# 'local'
+        let file = secondFile
+      else
+        let file = firstFile
+      endif
+      " If the path refers to a depot file, check if the local file is currently
+      " open in Vim and if so has the same version number as the depot file.
+      if context ==# 'local' && perforce#PFCall('s:IsDepotPath', file)
+        let localFile = perforce#PFCall('s:ConvertToLocalPath', file)
+        let bufNr = bufnr(localFile) + 0
+        if bufNr != -1
+          let haveRev = getbufvar(bufNr, 'p4HaveRev')
+          let depotRev = s:GetFileRevision(file)
+          if haveRev == depotRev
+            let file = localFile
+          endif
+          " else " We could also try to run 'fstat' command and open up the file.
+        endif
+      endif
+      if perforce#PFCall('s:IsDepotPath', file)
+        let refresh = perforce#PFGet('s:refreshWindowsAlways')
+        try
+          call perforce#PFSet('s:refreshWindowsAlways', 0)
+          call perforce#PFIF(1, a:preview, 'print', file)
+        finally
+          call perforce#PFSet('s:refreshWindowsAlways', refresh)
+        endtry
+        let offset = offset + 1 " For print header.
+      else
+        call perforce#PFCall('s:OpenFile', 1, a:preview, genutils#CleanupFileName(file))
+      endif
+      if perforce#PFEval('s:errCode') == 0
+        if a:preview
+          wincmd P
+        endif
+        mark '
+        exec (stLine + offset)
+        if a:preview
+          " Also works as a work-around for the buffer not getting scrolled.
+          normal! z.
+          wincmd p
+        endif
+      endif
+    catch
+      call perforce#PFCall('s:EchoMessage', v:exception, 'Error')
+    endtry
+  endif
+endfunction " }}}
+
+function! perforceutils#SetupDiffLink()
+  command! -buffer -nargs=0 PDiffLink :PFDiffLink
+  command! -buffer -nargs=0 PDiffPLink :PFDiffPLink
+  nnoremap <buffer> <silent> O :PDiffLink<CR>
+  nnoremap <buffer> <silent> <CR> :PDiffPLink<CR>
+endfunction
+
+function! s:GetFileRevision(depotPath)
+  let rev = matchstr(a:depotPath, '#\zs\d\+$')
+  return (rev !~# s:EMPTY_STR) ? rev + 0 : ''
+endfunction
+
+" DiffLink }}}
+
+" ShowConflicts {{{
+function! perforceutils#ShowConflicts()
+  let _splitright = &splitright
+  set splitright
+  try
+    let curFile = expand('%:p')
+    "exec 'split' curFile.'.Original'
+    exec 'edit' curFile.'.Original'
+    silent! exec 'read' curFile
+    silent! 1delete _
+    call genutils#SilentSubstitute('^==== THEIRS \_.\{-}\%(^<<<<$\)\@=', '%s///e')
+    call genutils#SilentDelete('\%(^>>>> ORIGINAL \|^==== THEIRS\|^==== YOURS \|^<<<<$\)')
+    call genutils#SetupScratchBuffer()
+    setlocal nomodifiable
+    diffthis
+
+    exec 'vsplit' curFile.'.Theirs'
+    silent! exec 'read' curFile
+    1delete _
+    call genutils#SilentSubstitute('^>>>> ORIGINAL \_.\{-}\%(^==== THEIRS \)\@=', '%s///e')
+    call genutils#SilentSubstitute('^==== YOURS \_.\{-}\%(^<<<<$\)\@=', '%s///e')
+    call genutils#SilentDelete('\%(^>>>> ORIGINAL \|^==== THEIRS\|^==== YOURS \|^<<<<$\)')
+    call genutils#SetupScratchBuffer()
+    setlocal nomodifiable
+    diffthis
+
+    exec 'vsplit' curFile.'.Yours'
+    silent! exec 'read' curFile
+    1delete _
+    call genutils#SilentSubstitute('^>>>> ORIGINAL \_.\{-}\%(^==== YOURS \)\@=', '%s///e')
+    call genutils#SilentDelete('\%(^>>>> ORIGINAL \|^==== THEIRS\|^==== YOURS \|^<<<<$\)')
+    call genutils#SetupScratchBuffer()
+    setlocal buftype=
+    setlocal nomodified
+    exec "au Perforce BufWriteCmd <buffer> :call <SID>SaveYours('".curFile."')"
+    diffthis
+  finally
+    let &splitright = _splitright
+  endtry
+endfunction
+
+function! s:SaveYours(orgFile)
+  if confirm('Do you want to accept the changes in "'.expand("%:p:t").'"?',
+        \ "&Yes\n&No", 2, "Question") == 1
+    exec 'w!' a:orgFile
+  endif
+endfunction
+" ShowConflicts }}}
+
+" Restore cpo.
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim6:fdm=marker et sw=2
diff --git a/.vim/doc/perforce.txt b/.vim/doc/perforce.txt
new file mode 100755 (executable)
index 0000000..32d6985
--- /dev/null
@@ -0,0 +1,2441 @@
+*perforce.txt*  A feature Rich Perforce SCM Integration for Vim.
+                    Requires Vim 6.0
+            Last Change: 01-Sep-2006 @ 16:54
+          Since Version: 1.4
+               Revision: 1.3.0
+         Plugin Version: 4.1
+                 Author: Hari Krishna Dara (hari_vim at yahoo dot com)
+
+                                            *perforce-introduction*
+                                            *perforce-plugin*
+This is a fairly complete integration with the perforce version control system
+for the most commonly used operations, including many administrative commands.
+It includes a great menu that is modelled very close to the p4win (the perforce
+GUI client) and is quite extensive.
+
+==============================================================================
+OVERVIEW                                    *perforce-overview*
+
+|perforce-installation|     How to install the plugin.
+
+|perforce-filetype|         Perforce filetype plugin and setting it up for p4.
+
+|perforce-settings|         An explanation of how to configure the plugin, for
+                            both the perforce related parameters and the plugin
+                            customizations, such as enabling menu.
+
+|perforce-ruler|            Setting up the ruler to show an active status of the
+                            file.
+
+|perforce-syntax|           Perforce syntax plugin.
+
+|perforce-help|             Browse the p4 help conveniently from within vim.
+
+|perforce-reinitialize|     Describes how to reinitialize the plugin to load the
+                            latest settings from the environment without
+                            restarting vim.
+
+|perforce-commands|         A description of various commands that can be
+                            issued on Vim command-line.
+
+|perforce-revisions|        Specifying perforce revisions conveniently.
+
+|perforce-interactive-commands|
+                            How to execute interactive perforce commands.
+
+|perforce-forms|            How to edit the perforce forms (specifications) from
+                            within vim.
+
+|perforce-submit|           Special handling of submit command.
+
+|perforce-list-commands|    Commands that can be used in a list of items window.
+
+|perforce-extensions|       Some useful extensions in the form of new options
+                            and commands.
+
+|perforce-misc-commands|    Some additional useful commands.
+
+|perforce-special-commands| Some useful commands and mappings.
+
+|perforce-utils|            Additional utilties (perforce/perforceutils.vim).
+
+|perforce-API|              New API provided by the plugin (experimental).
+
+|perforce-tips|             Some useful tips.
+
+|perforce-limitations|      Current limitations of the plugin.
+
+|perforce-troubleshooting|  Some notes how to trouble shoot problems.
+
+|perforce-changes|          A change list for current version from previous
+                            versions.
+
+|perforce-known-issues|     A list of known bugs.
+
+|perforce-wishlist|         Wishlist items that may be worth doing for a future
+                            version.
+
+|perforce-bugreporting|     Reporting bugs.
+
+|perforce-acknowledgements| Acknowledgements.
+
+==============================================================================
+
+                                            *perforce-installation*
+
+To install, place the perforce.vim script in one of the runtime plugin
+directories along with the genutils.vim scripts that it depends on. This
+typically is .vim/plugin or vimfiles/plugin directory under your home directory.
+To get the latest versions of these plugins, goto the following webplages:
+     perforce.vim: http://www.vim.org/script.php?script_id=240
+     genutils.vim: http://www.vim.org/script.php?script_id=197
+
+The distribution contains the following files:
+    - plugin/perforce.vim
+        - Thin interface to the plugin (autoload/perforce.vim).
+    - autoload/perforce.vim
+        - The perforce plugin itself loaded on demand.
+    - ftplugin/perforce.vim
+        - The perforce filetype plugin file. See below on how to configure the
+          perforce filetype.
+    - syntax/perforce.vim.
+        - Syntax definition file for perforce forms.
+    - doc/perforce.txt.
+        - The online help file (this file).
+    - perforce/perforcemenu.vim.
+        - Additional module to install and configure a menu for the plugin.
+          See |perforce-menu|.
+    - perforce/perforcebugrep.vim.
+        - An utility script that can be used to generate useful information
+          while sending bugreports about the plugin. See
+          |perforce-bugreporting|.
+    - perforce/perforceutils.vim.
+        - Thin interface to autoload/perforceutils.vim
+    - autoload/perforceutils.vim
+        - Additional misc. utilities loaded on demand.
+
+If you obtained the plugin as a zip archive,
+    - Just extract it in your runtime directory.
+    - Start a new instance or go to an existing instance of vim.
+    - Execute:
+>
+        :helpt <your runtime directory>/doc
+<
+      This should generate the help tags for the perforce plugin help.
+    - Setup perforce filetype as described in |perforce-filetype| section.
+    - Make sure p4 command is in your path or set |p4CmdPath| property.
+    - Set the |p4ClientRoot| property and additionally any other
+      |perforce-settings| that you like.
+    - Optionally set 'cmdheight' property to at least 2 to avoid seeing the more
+      prompt for most of the messages the plugin gives.
+    - The plugin requires that you have Vim 7.0 version installed.
+    - If you want to enable the perforceutils.vim plugin, add the following line
+      to your vimrc:
+>
+        runtime perforce/perforceutils.vim
+<
+    - If you want to enable the menu, add the following line to your vimrc, at
+      the end of perforce configuration lines:
+>
+        runtime perforce/perforcemenu.vim
+<
+    - The core of the plugin is autoloaded by Vim the first time a perforce
+      command needs to be executed. This improves the startup time of Vim
+      considerably in some situations. If you want the plugin to be loaded
+      immediately, you can force an autoload by adding the following command to
+      your vimrc:
+>
+        PFIntialize
+<
+
+Note: It is important to make sure your 'shellredir' properly set such that, vim
+captures both the standard output and standard error messages from the external
+perforce command. If this is not correctly set, the plugin will not be able to
+show the error messages generated by p4 command to you.
+
+Also note that on Windows, if you use a UNIX like shell for your 'shell'
+setting, the plugin will not work correctly unless the 'shellslash' option is
+set.
+
+                                            *loaded_perforce*
+Later, if you need to temporarily disable the plugin without needing to remove
+the files, you can set the loaded_perforce variable in your vimrc. You can also
+set the no_perforce_maps to disable just the mappings defined in this plugin or
+no_plugin_maps to disable mappings in all the plugins (provided they all honor
+this setting), in your vimrc.
+==============================================================================
+
+                                            *perforce-filetype*
+                                            *perforce-ftplugin*
+The package comes with a perforce ftplugin (|filetype-plugin|) which sets a
+few text mode options suitable for editing the forms and a syntax plugin to
+colorize the spec files. The ftplugin also positions the cursor at the
+appropriate line based on the type of spec you are editing. When you open
+perforce forms from within vim using |perforce-interactive-commands|, you
+don't have to do anything special for this to work, but if you want the same
+to work while editing forms using p4 command directly, then you need to add
+the following lines in your scripts.vim:
+>
+        if getline(1) =~ '^# A Perforce '
+          setfiletype perforce
+        endif
+<
+If you do not have this file already, then you need to create it under
+vimfiles or .vim directory (or anywhere in your 'rtp'). For details see help
+on |new-filetype-scripts|. Note that you also need to enable filetype plugins
+for this to work, see |filetype-plugin| for more information.
+
+==============================================================================
+
+                                            *perforce-settings*
+                                            *perforce-customizing*
+The plugin allows a lot of customization by reading some global variables. You
+can define a set of global variables in your startup script (.vimrc/_vimrc) and
+they are automatically read by the plugin during the Vim startup and are used
+for customizing the behavior.
+
+Note that all the setting names are case sensitive and the boolean settings can
+be set to the value 0 to disable and any non-zero number (for the matter of
+fact, any string will also do) to enable. All the settings can be changed at
+runtime without restarting vim; see |perforce-reinitialize|.
+
+The settings can also be changed at runtime, see |perforce-reinitialize|.
+
+Here is a list of all the options:
+    |p4CmdPath|, |p4DefaultPreset|, |p4DefaultOptions|, |p4ClientRoot|,
+    |p4EnableRuler|, |p4RulerWidth|, |p4EnableActiveStatus|,
+    |p4ASIgnoreDefPattern|, |p4ASIgnoreUsrPattern|, |p4OptimizeActiveStatus|,
+    |p4UseGUIDialogs|, |p4PromptToCheckout|, |p4DefaultListSize|,
+    |p4DefaultDiffOptions|, |p4EnableMenu|, |p4UseExpandedMenu|,
+    |p4EnablePopupMenu|, |p4UseExpandedPopupMenu|, |p4Presets|,
+    |p4MaxLinesInDialog|, |p4CheckOutDefault|, |p4SortSettings|, |p4TempDir|,
+    |p4SplitCommand|, |p4UseVimDiff2|, |p4EnableFileChangedShell|,
+    |p4HideOnBufHidden|, |p4Depot|, |p4Autoread|, |p4FileLauncher|,
+    |p4CurPresetExpr|, |p4CurDirExpr|, |p4UseClientViewMap|
+
+                                            *perforce-command-path*
+                                            *p4CmdPath*
+The plugin executes the external p4 command for all its operations. By default
+the command is found in the PATH, but if it doesn't exist in the path you can
+still specify the location by using the p4CmdPath variable.
+>
+        :let g:p4CmdPath = '<path_to_p4_command>'
+<
+    Example: >
+        :let g:p4CmdPath = '~/bin/perforce/bin/p4'
+<
+                                            *perforce-presets*
+                                            *p4Presets*
+This is a useful setting when you work with multiple perforce installations at
+the same time. This allows you to predefine a set of configurations that you
+can switch between without needing to restart vim or manually enter the
+details while using the |PFSwitch| command. Set this variable in your vimrc
+with a comma separated list of settings. Each setting should be of
+the form (separated with one or more spaces):
+>
+            <port> <client> <user>
+<
+You can include as many of these specifications as you want, just separate them
+with commas.
+>
+            :let g:p4Presets = 'port1 client1 user1,port2 client2 user2'
+<
+Once set, you can use the |PFSwitch| command in one of three ways to choose the
+setting.
+
+        1. If you know the index of the setting to which you want to
+           switch to (starting with 0), just pass that as an argument to the
+           command as
+>
+            " Switch to the third setting.
+            :PFSwitch 2
+<
+        2. If you don't know the index, just invoke the command without any
+           arguments. You will be prompted to enter the index after displaying
+           a list of available settings.
+
+        3. If you have expanded menu enabled (see |p4UseExpandedMenu| or
+           |p4UseExpandedPopupMenu|), then you can choose the desired setting
+           to switch to using the "Settings" sub-menu under "Perforce" group.
+
+                                            *perforce-P4CONFIG*
+                                            *P4CONFIG*
+As a special case, you can have one of the specifications as "P4CONFIG",
+allowing you to switch to the P4CONFIG feature of external p4 command
+interactively, see |perforce-dynamic-client|.
+>
+            :let g:p4Presets = 'p1 c1 u1,P4CONFIG,p2 c2 u2'
+<
+Setting port field to "P4CONFIG" also has the same effect. This results in
+plugin not passing the options for port, client and user explicitly such that p4
+can determine them based on the P4CONFIG file. However you can still override
+them by explicitly specifying the corresponding option to PF command.
+
+                                                    *p4DefaultPreset*
+If you don't have the P4CLIENT, P4USER and P4PORT environment variables set, you
+can use this setting to initialize the plugin with corresponding values. The
+format for the value is exactly same as a single entry in |p4Presets| setting.
+In fact, you can also set this to an index in the |p4Presets|.
+>
+        :let g:p4DefaultPreset = '<your_perforce_port> <your_perforce_client_name> <your_perforce_user_name>'
+<
+    Example: >
+        :let g:p4DefaultPreset = 'localhost:1666 hari_client hari'
+        :let g:p4DefaultPreset = 2 " Start with the second setting in p4Presets.
+<
+Note that the plugin automatically chooses some defaults for the above based on
+your environment, if you don't explicitly set them in your startup script.
+
+                                            *perforce-default-options*
+                                            *perforce-common-options*
+                                            *p4DefaultOptions*
+If you need to pass in additional arguments every time the external p4 command
+is run, you can use the following setting. The arguments are passed to p4
+literally. For the set of options that you can specify here, see perforce help
+for "usage" (use ":PH usage" command, see |perforce-help|)
+>
+        :let g:p4DefaultOptions = '<default_options_to_p4>'
+<
+    Example: >
+        :let g:p4DefaultOptions = '-H hkrishna'
+<
+                                            *perforce-buffer-local-options*
+In addition you can also use the buffer local variables "b:p4Options",
+"b:p4Client", "b:p4User", "b:p4Port" and "b:clientRoot" to override the global
+options (see ":PH usage" for option list) at a buffer level. The plugin
+automatically sets these options whenever you create a new perforce result
+window such that any future commands originating from the same window
+automatically inherit them.  This is especially useful when you temporarily
+switch to a different client/user by providing one at command-line, and later do
+more operations on the results (provided the given client/user is valid on the
+current m/c). >
+
+        :PF -c hari_tmp opened
+        :PFileDiff
+>
+The PFileDiff will automatically use the "hari_tmp" client as b:p4Client is set
+to that value. You can also use b:p4Options to set any option that is accepted
+in the global options section of PF command (see |perforce-global-options|),
+however for the client view mapping to work correctly, you need to have
+b:p4Client and b:clientRoot set. >
+
+        :let b:p4Options = '-H my_host'
+<
+All commands executed when this buffer is active will automatically inherit
+these global options.
+
+NOTE: Currently the b:clientRoot variable needs to be set manually.
+
+                                            *perforce-client-root*
+                                            *p4ClientRoot*
+The client root is required for certain commands (it is same as what you
+specify in the Root: property in the client settings), so if not specified and
+if the |p4EnableActiveStatus| setting is enabled, the plugin will run the "p4
+info" command to read the client root setting from the perforce server. But
+this will introduce a short delay in autoload time (especially if you are
+talking to a perforce server that is installed out side your network). To avoid
+this, use the following setting to specify the client root.
+>
+        :let g:p4ClientRoot = '<client_root_directory>'
+<
+    Example: >
+        :let g:p4ClientRoot = 'c:/dev'
+<
+If |p4EnableActiveStatus| is disabled, the current directory is used as the
+default.
+
+                                            *perforce-gui-dialogs*
+                                            *p4UseGUIDialogs*
+By default the plugin uses console dialogs for some of the prompts. This is
+convenient as you can then use the input-history and expression register
+|quote=|. But if you like, you can enable this setting to force using GUI
+dialogs for all the prompts.
+>
+        :let g:p4UseGUIDialogs = 1
+<
+
+                                            *perforce-automatic-checkout*
+                                            *p4PromptToCheckout*
+The plugin by default prompts you to checkout, when you start editing a
+readonly file under the client root. You can disable this behavior by using the
+following setting:
+>
+        :let g:p4PromptToCheckout = 0
+<
+Note that you can still manually checkout (or edit) the file even when this
+option is disabled by using "PE" or "PF edit" command.
+
+When the checkout prompt is given, you have the option saying Yes, No or Cancel.
+When accidental changes to the buffer bring up the checkout prompt, you can
+select Cancel to revert the state of the buffer as much as possible (you may
+still have to press <Esc> sometimes). One advantage of selecting Cancel in these
+cases is that the next time you start making a genuine change, you will get the
+checkout prompt as expected (otherwise, you will only get this prompt once).
+
+                                            *perforce-list-size*
+                                            *perforce-default-list-size*
+                                            *p4DefaultListSize*
+When you execute "changes", "jobs" and "filelog" perforce commands, the number
+of entries is limited to 100 to avoid generating a large volume of data. But you
+can change the value to whatever you like:
+>
+        :let g:p4DefaultListSize = 1000
+<
+To disabling it completely (show the entire list) set it to a negative number:
+>
+        :let g:p4DefaultListSize = -1
+<
+
+                                            *perforce-default-diff-options*
+                                            *p4DefaultDiffOptions*
+You can set the default diff options to be passed to all the "diff" and "diff2"
+operations (both direct and indirect execution of these commands), by using the
+following setting:
+>
+        :let g:p4DefaultDiffOptions = '-dwbu5'
+<
+For the options that you can set here, see the help for "diff" or "diff2" by
+running the "PH diff" or "PH diff2" command.
+
+Note, this setting can't be used to specify options to the external diff
+program.
+
+                                            *perforce-menu*
+                                            *p4EnableMenu*
+The distribution comes with an additional module called perforcemenu.vim to
+install a Perforce sub-menu on the main or PopUp menu.  By default the menu is
+not added as many people (including myself) don't use menus (I have the entire
+menu bar disabled). Use the following setting to enable the Perforce sub-menu:
+>
+        :let g:p4EnableMenu = 1
+<
+The above setting will create a very basic menu with the most needed commands.
+This makes it easy to use shortcut keys if you have the |winaltkeys| correctly
+configured. To enable a more full featured menu, see |p4UseExpandedMenu|.
+
+                                            *perforce-expanded-menu*
+                                            *p4UseExpandedMenu*
+By default the |p4EnableMenu| option creates a full-featured menu that is
+modelled closely after the p4Win utility, which comes with perforce. But you can
+disable this and have only a basic menu with the most commonly used set of
+commands (this was the default for older versions of the plugin). Use the
+following setting in your startup script:
+>
+        :let g:p4UseExpandedMenu = 0
+<
+If you want a basic menu on the main menu (for the ease of using the shortcut
+keys), then you can consider having the full-featured version on the popup
+menu, see |p4EnablePopupMenu| and |p4UseExpandedPopupMenu| settings.
+
+                                            *perforce-popup-menu*
+                                            *p4EnablePopupMenu*
+This is similar to |p4EnableMenu| except that enabling this option, adds a
+Perforce sub-menu on the PopUp menu instead of the main menu.
+>
+        :let g:p4EnablePopupMenu = 1
+<
+                                            *perforce-expanded-popup-menu*
+                                            *p4UseExpandedPopupMenu*
+This is similar to |p4UseExpandedMenu| except that enabling this option, adds a
+more full-featured Perforce sub-menu on the PopUp menu.
+>
+        :let g:p4UseExpandedPopupMenu = 1
+<
+                                            *loaded_perforcemenu*
+Note: If you never use the menu features of the plugin, consider setting
+"loaded_perforcemenu" to a non-zero value, to avoid getting this module sourced.
+
+                                            *perforce-max-lines-in-dialog*
+                                            *p4MaxLinesInDialog*
+Commands that use a dialog box to show the result (such as |PEdit|) assume
+that the messages generated by the perforce command are only a few lines. But
+depending on the arguments (e.g., "PEdit ..." and there are many files under
+the current directory ...), there can be too many lines to display in a dialog
+so the display mode is automatically switched to a new window instead of the
+dialog. Though the default limit is 1, which helps to draw your attention for
+the conditions that you normally expect a one line result (e.g., you checkout a
+file and someone else already checked out the file), you can change it by
+setting the following line:
+>
+            :let g:p4MaxLinesInDialog = <number of lines>
+<
+    Example:
+>
+            :let g:p4MaxLinesInDialog = 5
+<
+                                            *p4CheckOutDefault*
+When you start modifying a read-only file, the plugin prompts you to checkout
+the file from perforce. Set this option to 1 to make the default option to
+"Yes" and 2 for "No". The default is 2 to avoid accidentally checking out a
+file. >
+
+            :let g:p4CheckOutDefault = <option number>
+<
+    Example:
+>
+            :let g:p4CheckOutDefault = 1
+<
+                                            *p4SortSettings*
+The |PFSettings| command by default sorts the setting so that they are in
+alphabetical order. But this will alter the position of the settings as new
+settings get added, so if you want them to always appear in the same familiar
+order, then set this to 0 to disable sorting. >
+
+            :let p4SortSettings = 0
+<
+                                            *p4TempDir*
+This setting points to the directory which should be used by the plugin for
+creating any temporary files. This setting is used for vdiff and vdiff2
+commands, but currently these commands don't really create any files on the
+filesystem, as the directory is used to merely generate the filenames for
+temporary files (the filename is still such that it is valid on the filesystem,
+so that you can write the contents to it for any reason, but you will have to
+reset the 'buftype' option first). But this may change in future when new
+features/commands get added which require the temporary files to be on
+filesystem. >
+
+            :let p4TempDir = "c:/temp/vim-p4"
+<
+                                            *p4SplitCommand*
+When the plugin creates new windows as a result of issuing perforce commands, it
+by default uses |:split| command, which creates a horizontally split window above
+or below the current window depending on your 'splitbelow' setting. But if you
+would like to change the way the windows are created, you can set this setting
+to any split command that is valid (such as |:vsplit| or "topleft split"). For
+all possible commands see |:vertical|. >
+
+            :let p4SplitCommand = "vsplit"
+<
+                                            *p4UseVimDiff2*
+If this option is set, the plugin uses vdiff2 instead of diff2 in the filelog
+window. See |perforce-vim-diff|. >
+
+            :let p4UseVimDiff2 = 1
+<
+                                            *p4EnableFileChangedShell*
+The plugin normally listens to the |FileChangedShell| events and refreshes the
+ruler automatically (See |perforce-ruler|), keeping the status up to date. But
+because of the way this event works, the plugin has to emulate the |timestamp|
+warning messages that would be generated by Vim by default, when there is no
+listener for this event. If you don't like this feature for any reason, you can
+disable it by setting this option to 0. >
+
+            :let p4EnableFileChangedShell = 0
+<
+Note that the plugin no longer refreshes the ruler whenever vim detects that the
+file has been modified externally requiring a reload of the buffer (and this
+anyway happens only if the buffer is currently visible in a window, or if the
+'hidden' option is set). To manually refresh the ruler see
+|perforce-refresh-file-status|.
+
+                                            *p4BufHidden*
+This setting is useful if you do not normally set the 'hidden' option. The
+plugin normally sets the 'bufhidden' option for the perforce plugins to the
+value "wipe" such that they are automatically wiped out when they are unloaded.
+This keeps your buffer list clean and also conserves the vim resources as you
+can potentially end up creating a lot of buffers, one for each perforce command
+that you execute from with in Vim. But this would prevent you from switching
+back and forth between the perforce result buffers and other regular buffers.
+If you often find yourself working with perforce windows for a long time, you
+should consider setting this option to the value "hide" instead, avoiding unload
+of the buffers when they are hidden. When you set this value, an interesting and
+useful side effect is that you can use <C-O> and <Tab> to navigate the preview
+window, which can be very useful while viewing the description of list items,
+see |perforce-list-commands|. >
+
+        :let g:p4BufHidden = 'hide'
+
+Note: You have three other alternatives to essentially get the same
+functionality at different levels, avoiding the unload of the buffers:
+        - Set the 'hidden' global option to avoid unloading all buffers. This
+          essentially avoids unloading every buffer that is loaded/created in
+          Vim, not just perforce windows.
+        - Manually set 'bufhidden' local option to "hide" for any particular
+          perforce window that you are interested in keeping around. Once set,
+          this prevents the buffer from getting wipedout, until explicitly
+          requested to do so. >
+
+            :setlocal bufhidden=hide
+<
+        - Use the |:hide| command instead of quitting the buffer by other means,
+          such as the |:quit| command everytime. This again prevents the buffer
+          from getting unloaded even after it is hidden, but conveniently so
+          only when you use :hide command. If the buffer is later shown back
+          in another window and then hidden without having one of the other
+          settings appropriately set, then it will get unloaded and will get
+          wipedout.
+
+See |PFWipeoutBufs| command for a way to cleanup all the hidden perforce buffers
+that get accumulated, when you use one of the above techniques.
+
+Note: There are other values that the 'bufhidden' can take, and so does the
+g:p4BufHidden option, but they are not useful. They leave the perforce result
+buffers lying around, without any useful side effect.
+
+                                            *p4Depot*
+The plugin at any time can operate only on one preset depot, which by default is
+"depot". If your perforce server has multiple depots or if your depot name is
+not "depot", then you can use this setting:
+>
+        :let g:p4Depot = 'proj'
+>
+to switch to a different depot than "depot". You can also do this at any time by
+using the |PFSettings| command interactively.
+
+                                            *p4Autoread*
+By default, the plugin automatically reloads the file that get externally
+modified as a side effect of some perforce commands (such as get and edit), if
+the buffer inside vim is not yet modified. But you can disable this feature by
+using this setting: >
+
+        :let g:p4Autoread = -1
+>
+A value of -1 means, use the 'autoread' vim setting and a value of 0 means don't
+autoread and a value of 1 means autoread (the default).
+
+                                            *perforce-ruler*
+The below are some additional settings that are related to configuring the
+perforce ruler.
+
+                                            *p4EnableRuler*
+You can enable this setting to see additional information in the Vim ruler about
+the current file's status. If the status for the current file doesn't exist yet,
+nothing is shown. To make the status automatically available when you open a
+file, you can enable the active status option, see |p4EnableActiveStatus|. You
+can also manually obtain the status by executing the |PFRefreshFileStatus|
+command any time. See |p4RulerWidth| on how to adjust the width of the ruler. By
+default this setting is enabled, to disable it use, >
+
+        :let g:p4EnableRuler = 0
+<
+The plugin modifies the 'rulerformat' for this to work, so if you are also
+modifying this, make sure you do it before the plugin gets loaded (doing it in
+the vim startup file will ensure this.)
+
+Note that enabling this option alone is not sufficient, you should also have the
+|p4EnableActiveStatus| setting enabled or use the |PFRefreshFileStatus| command.
+Also see |p4OptimizeActiveStatus| setting.
+
+                                            *perforce-ruler-width*
+                                            *p4RulerWidth*
+By default the plugin uses an additional 25 columns to show the perforce file
+status. You might want to increase this value if you have long client names in
+your perforce setup:
+>
+        :let g:p4RulerWidth = 50
+<
+                                            *perforce-active-status*
+                                            *p4EnableActiveStatus*
+Enabling this option along with |p4EnableRuler| will provide you a quick look
+at the current file's status in the perforce depot, as soon as it is opened.
+By default this setting is enabled, but you can disable it if it introduces
+significant delay for every file you open (as it involves running an external
+command which in turn has to talk to a server). >
+
+        :let g:p4EnableActiveStatus = 0
+<
+If the response is slow and you would still like to have this feature, you can
+disable this option and and use the |PFRefreshFileStatus| command, for whichever
+file and whenever you want to see/update the ruler.
+
+Besides using the status for showing the ruler, if available, it is also used to
+make better decisions during some perforce operations.
+
+Note, you need to still have the |p4EnableRuler| setting enabled to actually see
+the status in the ruler. Also the default |p4OptimizeActiveStatus| setting
+optimises it such that the "fstat" is done only the first time the file is
+opened.
+
+                                        *perforce-active-status-ignore-patterns*
+                                        *p4ASIgnoreDefPattern*
+                                        *p4ASIgnoreUsrPattern*
+These are regular expression patterns matching the filenames for which the
+active status should be ignored. If the current filename matches the default
+ignore pattern or the user defined ignore pattern, then it is assumed that the
+file doesn't exist in the depot and no fstat is done on it. By default the
+user pattern is empty and the default pattern is set to all files under tmp
+and temp sub-directories and any file with log, dif, diff, out, buf and bak as
+extension (case insensitive), which is expressed by the following pattern: >
+
+  '\c\%(\<t\%(e\)\?mp\/.*\|^.*\.tmp$\|^.*\.log$\|^.*\.diff\?$\|^.*\.out$\|' .
+      \ '^.*\.buf$\|^.*\.bak$\)\C'
+<
+To add additional patterns, the "p4ASIgnoreUsrPattern" should be used. The
+"p4ASIgnoreDefPattern" can however be set to an empty value such that the
+default pattern is completely ignored. Setting the default pattern to empty
+string without defining the user pattern will completely disabled this feature,
+resulting in a 'p4 fstat' call to every file that is opened (the file still
+needs to be under the client root).
+
+                                            *perforce-optimize-filestatus*
+                                            *p4OptimizeActiveStatus*
+Enabling this option along with |p4EnableActiveStatus| and |p4EnableRuler| will
+allow the plugin to determine a brief status of the file in perforce and show it
+as part of the ruler, without loosing much of the responsiveness.  When you
+enable this option, the plugin determines the status only the first time you
+open the file and any other time there is a possibility for change in the status
+(after executing the |PEdit|, |PRevert| etc.) and when you manually ask the
+plugin to update the status using the |PFRefreshFileStatus| command. By default,
+this option is enabled, to disable it use, >
+
+        :let g:p4OptimizeActiveStatus = 0
+<
+When this option is set, the current file status shown in the ruler may not be
+the most up to date status, so when it is important (see
+|perforce-negative-revisions|) make sure you update it manually.
+
+                                            *perforce-file-launcher*
+                                            *p4FileLauncher*
+This setting is used to choose the launcher application for executing the
+|PFileLaunch| command in a filelist window. For non-windows platforms, this
+needs to be explicitly set before you can use the |PFileLaunch| command, but on
+windows, this by default is set to the following command: >
+
+       start rundll32 url.dll,FileProtocolHandler
+<
+This works almost the same as double clicking the file under explorer, so proper
+file associations are assumed. You can however change the command to whatever
+you like.
+
+                                            *perforce-dynamic-client*
+For those users who have a need to dynamically switch between different perforce
+clients, the plugin provides ability to set expressions that can be used to
+provide call back hooks and determine the appropriate client. This is especially
+useful when you have multiple clients mapped on to the same root and there is a
+way to deduce the client based on the usage patterns and or current file.
+
+                                            *p4CurPresetExpr*
+                                            *p4CurDirExpr*
+These by default are set to an empty string, but can be assigned any Vim
+expression that is valid on the RHS of a variable assignment. The expression is
+evaluated and the result is used as the corresponding setting.
+
+The "p4CurPresetExpr" setting is used only when using |P4CONFIG| setting. The
+"p4CurDirExpr" at any time can also be used to determine the working directory
+of the p4 operation, which in turn could impact which |P4CONFIG| file is picked
+up.
+
+For the more demanding users, these expressions provide the call-back hooks
+required to determine the client based on your environment.
+
+Note that when you specify an expression which returns a different current
+directory than the current Vim working directory (as returned by |getcwd()|),
+the plugin doesn't attempt to modify any relative filenames that you specify on
+the command-line to be relative to the new directory, so you need to make sure
+you specify a valid filename in this case (or specify full paths all the time).
+This could get tricky especially if you can't predict which directory your
+expression would return. If your logic to find the current directory is really
+that complex, then you could use the experimental API (see |perforce-API| that
+the plugin provides to manipulate the filename arguments yourself from with in
+the "p4CurDirExpr" before returning the directory name. However, while adding
+new filenames to arguments (such as the default filename for certain commands)
+the plugin automatically uses full filenames, when you specify a "-d" option on
+the command-line or through the return value of "p4CurDirExpr".
+
+It is recommended to return an empty string from "p4CurDirExpr", when the
+directory is same as Vim's current working directory.
+
+See also |perforce-switch-client|.
+
+See |perforce-tips| for an interesting and useful idea for using "p4CurDirExpr"
+setting.
+
+                                            *p4UseClientViewMap*
+This is an experimental feature, so defaults to 0.
+
+Use this setting to make the plugin look into your client View: mappings while
+translating depot paths to local paths and vice versa. The plugin translates the
+paths without needing to run "p4 where" command, by extracting the View: data
+from the client specification and building an internal representations for that.
+This is done the first time the translation is required and it is cached to
+avoid executing the "p4 client" command everytime. When the views in your client
+specification change, you need to manually update this mapping by running the
+|PFUpdateViews| command.
+
+Make sure you don't have any syntactic errors in your views (such as unmatched
+wildcards), as the plugin is not as forgiving as perforce itself in handling
+them (this is done for simplicity).
+==============================================================================
+
+                                            *perforce-syntax*
+The perforce plugin comes with a Vim syntax plugin for perforce filetype, and
+works even when the |perforce-filetype| plugin is not setup. Most of the
+output windows generated for perforce commands are set to the "perforce"
+filetype and results in automatically sourcing this syntax file. Like any syntax
+plugin, you can do further customizations and overrides from your vimrc or from
+an "after" plugin (as the case may be). To customize syntax colors, here is a
+complete list of all the syntax groups that the plugin defines (replace the tag
+on the right hand side with your own preferred highlight group such as Comment,
+Special etc.):
+>
+        hi link perforceSpecKey           <your_preferred_highlighting_group>
+        hi link perforceComment           <your_preferred_highlighting_group>
+        hi link perforceDate              <your_preferred_highlighting_group>
+        hi link perforceCommands          <your_preferred_highlighting_group>
+        hi link perforceHelpKeys          <your_preferred_highlighting_group>
+        hi link perforceClientRoot        <your_preferred_highlighting_group>
+        hi link perforceKeyName           <your_preferred_highlighting_group>
+        hi link perforceDepotFile         <your_preferred_highlighting_group>
+        hi link perforceLocalFile         <your_preferred_highlighting_group>
+        hi link perforceVerSep            <your_preferred_highlighting_group>
+        hi link perforceVersion           <your_preferred_highlighting_group>
+        hi link perforceSubmitType       <your_preferred_highlighting_group>
+        hi link perforceDefaultSubmitType <your_preferred_highlighting_group>
+        hi link perforceViewExclude       <your_preferred_highlighting_group>
+        hi link perforceDepotView         <your_preferred_highlighting_group>
+        hi link perforceClientView        <your_preferred_highlighting_group>
+<
+==============================================================================
+
+                                            *perforce-help*
+The plugin comes with a help browser to browse the perforce help from within
+vim window and move back and forth between different help pages. To start the
+help just type |PH| or "PF help" command or alternatively choose the appropriate
+menu entry. The plugin opens a new window that is positioned the way the vim
+built-in help does. Once you close the help window, all the other window sizes
+are restored, again the way the vim built-in help does.
+
+Once you are in the help window, you can see that the perforce help keywords
+are highlighted with a different color. To get additional perforce help on the
+keyword, you can just move on to the keyword and press |Enter| or |K|. You can
+also press double-click with your left mouse button.
+
+The |PH|, |PHelp| or "PF help" command takes a set of arguments that are passed
+to the perforce help command, which makes it easier to get to the help page if
+you know the keywords. The |PH| and |PHelp| commands also support command
+completion for help topics.
+
+You can use <BS> and <Tab> to navigate the help history. This makes it easy
+to view the perforce help and makes it feel like a hyper-text browser. Use "q"
+or any other vim command to quit the help window.
+==============================================================================
+
+                                            *perforce-commands*
+                                            *:PF*
+                                            *perforce-global-options*
+                                            *perforce-command-options*
+                                            *perforce-command*
+                                            *perforce-arguments*
+The plugin defines a set of new commands to interact with perforce. The most
+basic command is the "PF" command that is equivalent to the "p4" command on
+the shell. This command takes arguments that are processed and passed to the
+external p4 command and the output is collected back. Depending on the type of
+command and various user settings, the output is either displayed in a new
+window, a preview window or in a dialog box.
+
+The command syntax resembles that of p4 command. There are five different types
+of arguments that can be passed to PF command:
+>
+        :[range]PF [<p4 global options>] <p4 command>
+                [<p4 command options>] [<arguments>]
+<
+All the argument sections are optional except for the command name itself. It
+provides the flexibility to issue complex commands (essentially anything that is
+possible at the shell prompt) such as the below:
+>
+        :PF -c client -u user integrate -b branch -s source target1 target2
+<
+where, the "-c client -u user" are global options, "integrate" is the p4
+command, "-b branch -s source" are the command options and the "target1
+target2" are the arguments to the corresponding perforce command.
+
+Note: See |perforce-common-options| for informaton on providing global options
+transparently.
+
+All commands executed through "PF" go through a common internal function that
+does argument validations/modifications/customizations etc. in addition to any
+command specific operations. This results in a very consistent argument handling
+throughout all the perforce commands.
+
+    Example: >
+        " Run p4 diff on the current file and display the diff in a new window.
+        :PF diff
+
+        " Show all the opened files under the src directory (assuming you we are
+        "   currnetly above this directory) in a new window.
+        :PF opened src/...
+
+        " Open the client specification for editing.
+        :PF client
+<
+Most commands take variable number of arguments. Some commands that require a
+filename default to current file or ask you for an argument, if you didn't pass
+any.
+
+You can additionally pipe the output of p4 through external commands (filters)
+before the plugin sees the output. Anything after an unprotected bar ("|") is
+considered as the external filters and so is specially treated by the plugin,
+and the processing on such arguments is reduced to a minimum, which means that
+you need to take care of the shell specific issues (such as enclosing the
+arguments in quotes etc.) yourself. If you need to specify the bar symbol as
+part of the perforce arguments (not really as the shell pipe symbol), then you
+need to protect it with a back-slash, as discussed in the
+|perforce-special-chars| section.
+
+    Example (useful for older versions of p4 client that didn't support -u
+    argument): >
+        PChanges -s pending | grep hari
+<
+If you find yourself using a combination frequently, you can create a new
+command for it to make it easier to type. For the above combination, the
+following can be placed in .vimrc:
+>
+     command! MyPChanges PChanges -m -1 -s pending | grep hari
+<
+Note, If you want to by-pass additional command specific processing, then you
+can use the |PFRaw| command instead of the "PF" command.
+
+The :PF command also supports custom command completion, to complete partial
+perforce command names as well as filenames. Most filename expansions
+automatically happen (like, %, <cfile> etc.), and # followed by a revision
+specifier is treated specially when occurred at the end of the argument, and is
+prevented from getting expanding as a Vim buffer name.
+
+                                            *perforce-special-chars*
+Some of the characters in the arguments are treated specially by the plugin,
+unless they are protected by prefixing them with a back-slash. The characters
+that are treated specially are:
+
+    character       special meaning ~
+    <space>         argument separator.
+    &               codeline modifier. See
+                    |perforce-alternative-codeline-modifier|
+    |               pipe symbol.
+    \               protects other special characters, unless protected by
+                    itself.
+
+                                            *perforce-command-mode-specifier*
+                                            *perforce-filter* *perforce-pipe*
+You can also specify the run mode of the perforce command as one of the "run"
+(default), "filter" or "pipe" by specifying one of the '++r', '++f' or '++p'
+option respectively to the PF command in the <p4 global options> section as
+described in the |perforce-commands| section. These options are just used as
+directives to the command-processor and are not passed to the external p4
+command. The "filter" and "pipe" modes are most useful with the "-x -"
+perforce global option (see ":PH usage" for details) and "display" mode is used
+mainly for internal purposes.
+
+    option  details ~
+    ++p     Write (pipe) the lines specified by the given [range] to the p4
+            command on standard input. This works like the |:write_c| command.
+            This only pipes contents to p4, and doesn't read the output back, so
+            the contents are not effected.
+    ++f     Filter the lines specified by the given [range] through the p4
+            command. This works like the |filter| command. You can create a new
+            buffer or modify buffer containing the output of another perforce
+            command, and pass the contents as arguments to a perforce command
+            and get back the results. In fact the '++c' option of diff does
+            exactly this. It first obtains the list of opened files in the given
+            change list and passes them as arguments to the diff command. See
+            |perforce-extensions|.  The |PW| command is just an alias for this
+            feature so that you can skip the bang for convenience (you can't
+            specify -x option with this command, however).
+
+The default [range] for the ":PF" command is the whole buffer (1,$). There is no
+direct equivalent of Vim's |:read!| syntax here, but it can easily be done using
+the "filter" mode on an empty line created at the location where you want to
+read the output of p4 command.
+
+                                            *:PEdit*      *:PRevert*   *:PAdd*
+                                            *:PDiff*      *:PDiff2*    *:PPrint*
+                                            *:PGet*       *:PSync*
+                                            *:POpened*    *:PHelp*
+                                            *:PDelete*    *:PLock*
+                                            *:PSubmit*    *:PUnlock*
+                                            *:PClient*    *:PClients*  *:PUser*
+                                            *:PUsers*     *:PBranch*
+                                            *:PBranches*  *:PLabel*
+                                            *:PLabels*    *:PJob*      *:PJobs*
+                                            *:PJobspec*   *:PResolve*
+                                            *:PChange*    *:PChanges*
+                                            *:PDepot*     *:PDepots*   *:PHave*
+                                            *:PDescribe*  *:PFiles*    *:PFstat*
+                                            *:PGroup*     *:PGroups*
+                                            *:PLabelsync* *:PIntegrate*
+                                            *:PPasswd*
+
+While the perforce commands can be executed using the "PF xxx" syntax, some
+of them have an equivalent PXxx command. So the following two: >
+
+        :PF opened -c 123456
+<
+    and >
+
+        :POpened -c 123456
+<
+are identical. However, if you intend to pass some global arguments to p4,
+then you are forced to use the first syntax. E.g., if you want to change the
+user specification of another user, instead of changing the P4USER env.
+variable, or using the |PFSwitch| command, you can use the following approach:
+>
+        :PF -u other_user user
+<
+                                            *perforce-describe*
+The describe command by default adds the "-s" option to avoid generating the
+diff, unless a diff option is explicitly specified (implying that you would like
+to see the diff too).
+
+Note that the commands that normally prompt a confirmation message (such as
+revert) accept a "++y" argument to avoid the prompt.
+>
+        :PF ++y revert -c 12345
+<
+                                            *:PE*  *:PR*  *:PA*  *:PD*  *:PD2* *:PP*
+                                            *:PG*  *:PO*  *:PH*
+Some of the more frequently used commands have a shortcut to make it faster to
+type.  Following table gives the mapping:
+
+        Short-cut       Command ~
+        PA              add
+        PD              diff
+        PD2             diff2
+        PE              edit
+        PG              get/sync
+        PH              help    |perforce-help|
+        PO              opened
+        PP              print
+        PR              revert
+
+You can also define your own shortcuts easily, e.g., >
+
+        :command! -nargs=* PB :PF branch <args>
+<
+Place this command in your .vimrc so that it gets executed every time you
+start Vim.
+
+                                            *:PFRaw*
+PFRaw command is like |PF| command except that it bypasses all the processing
+that the |PF| command does. You should be able to pass most of the perforce
+arguments as they are to this command. The raw output from the p4 command is
+collected and placed in a new window
+
+Note that you can't use PFRaw to execute any p4 command that requires user
+interaction (such as "PFRaw client") unless you can pass in a "-o" options to
+it. In fact the |PF| command and the corresponding specialized commands (such
+as PClient) pass the "-o" argument internally to generate forms.
+
+                                            *perforce-initialize*
+                                            *perforce-reinitialize*
+                                            *:PFInitialize*
+Changing some settings may have impact on other plugin or Vim settings, so to
+propagate these changes, you should execute the :PFInitialize command.
+When you want to change a setting while within Vim, you can directly set the
+value of the corresponding global variable, but you should also call the
+:PFInitialize command. It is recommended to use the |:PFsettings| command, which
+not only makes it easier to find and enter values for these settings, it will
+also execute :PFInitialize for you.
+
+    Examples:
+        You can remove the Perforce sub-menu from the main menu by using
+        the following commands: >
+
+            :let g:p4EnableMenu = 0
+            :PFInitialize
+<
+        You can re-enable the menu, may be the full-featured one by setting: >
+
+            :let g:p4EnableMenu = 1
+            :let g:p4UseExpandedMenu = 1
+            :PFInitialize
+<
+                                            *:PFSettings*
+To make the above process easier, the PFSettings command prompts you with a
+list of settings to select from (without the common p4 prefix) and let you
+modify them. You can optionally pass in the setting name and value on the
+command line too. If passing the setting name, you can use Vim's completion
+mechanism to complete partially typed in name.
+>
+        :PFSettings [setting name] [new value]
+<
+The command reinitializes the plugin after making the modifications. A typical
+dialog to turn on perforce menu for a gvim window could look like this:
+>
+            :PFSettings
+            0   User                  1   Client                2   Password
+            3   Port                  4   Presets               5   ClientRoot
+            ...
+            9   EnableMenu            ...
+            ...
+            .
+            .
+            Select the setting: 9<Enter>
+            Current value for EnableMenu is: 0
+            Enter new value: 1<Enter>
+<
+You should see the menu turned on at the end of this process. You can quit the
+dialog at any time by just pressing <Enter> without typing anything.
+
+If you know the name of the setting (or use command-completion), you can specify
+the setting and its new value directly on the command-line, to avoid the
+dialogs.  E.g., to change the default diff options: >
+
+            :PFSettings DefaultDiffOptions -dwbu
+            Current value for DefaultDiffOptions: "-du" New value: "-dwbu"
+<
+Note, there is also an abbreviation defined for this command as "PFS".
+
+                                            *perforce-switch-client*
+                                            *:PFSwitchPortClientUser*
+                                            *:PFSwitch*
+If you are connecting to multiple perforce installations, the PFSwitch command
+can be used to quickly switch between them. For an explanation of how to
+store these configurations to avoid typing, or use the Settings menu, see
+|p4Presets|.
+
+When you want to switch to a different perforce server, or just switch to a
+different client or user, without leaving Vim or needing to change any
+environment variables, use the PFSwitch command in one of the following ways:
+
+    1. Prompt for the setting to choose from. Enter the index in the list of
+       settings.  >
+
+        :PFSwitch
+<
+    2. If you know the index, pass it as an argument.  >
+
+        :PFSwitch <index into p4Presets starting with 0>
+<
+    3. To switch to an arbitrary setting, pass in the arguments as below: >
+
+        :PFSwitch <port> [<client>] [<user>]
+<
+       As a special case, you can pass in P4CONFIG as the only argument to
+       switch to using the P4CONFIG feature of the external p4 command (see
+       also |perforce-dynamic-client| and |perforce-P4CONFIG|. You can use Vim
+       command completion mechanism to complete from the |p4Presets|.
+    4. You can also use PFSwitchPortClientUser which prompts you for the
+       individual values. >
+
+        :PFSwitchPortClientUser
+<
+See also |p4Presets|.
+
+Note, this command resets the cached file statuses of all the buffers such that
+their statuses are determined again based on the new client.
+
+                                            *:PFWipeoutBufs*
+This command can be used periodically to cleanup all the hidden perforce buffers
+that are not already wipedout because they are not yet unloaded (see
+|p4HideOnBufHidden| for ways to do this). By default, this command only prints
+the list of buffers that will be wipedout, so to actually wipeout the buffers,
+run the command with "++y" option. >
+
+        :PFWipeoutBufs ++y
+<
+                                            *perforce-update-views*
+                                            *:PFUpdateViews*
+Use this command to update the internal structures corresponding to the client
+view mapping. When run, it discards the local cache and reconstructs it by
+running "p4 client" command.
+==============================================================================
+
+                                            *perforce-revisions*
+For convenience most commands (such as sync and print) take in a revision (as
+specified by "help revisions") as the last argument and apply it to the
+current file. You need to however protect the '#'s with a backslash such that
+it is not substituted by vim with the current alternative-file |#|.
+    Examples: >
+
+        :PP \#1 - To see the revision 1 of the current file.
+        :PP @2002/01/01 - To see the current file as of a date.
+        :PP @65000 - To see the current file as of change 65000.
+<
+                                            *perforce-negative-revisions*
+In addition, you can also pass in a negative or positive number as a revision
+to specify an offset from the have revision. If you have the
+|perforce-active-status| feature enabled, the have revision value is available
+automatically, otherwise the plugin executes the file status such that it can
+generate the new revision after the offset.  >
+
+        :PP \#-2 " To see the (#head - 2)'th revision.
+        :PD2 \#-1 \#-0 " Diff between the have and the previous revisions.
+        :PD2 #\#have #\#head " Diff between the have and the head revisions for
+                            "   the alternate file..
+<
+Note: Observe the "-0" given as the revision number to mean the head revision.
+
+Note: PD2 (which is a shortcut for "PDiff2" or "PF diff2") also supports an
+interactive mode in which you can just type in PD2 by its own with no
+arguments, and the plugin will prompt you to specify the two revisions. You
+can specify any revision specifier that is normally supported on the
+command-line, in addition numbers are always treated as revisions so you don't
+have to prefix them with a \#. >
+
+        :PD2
+        Version 1? 10
+        Version 2? 2002/12/15
+<
+Note: To be able to specify revision offsets, you need to have all the resultant
+files already open in the current vim session.
+
+                                    *perforce-alternative-codeline-modifier*
+Another convenient feature supported by plugin is to allow specifying an
+alternative codeline in addition to the revision specifiers by using the '&'
+modifier. Suppose you want to diff between the revision 2 of the currently
+opened file with the head revision of the same file but from another codeline
+called 'other', then the following syntax makes it easy >
+
+        :PD2 \#head&other \#2 " Same as 'PD2 \#1 \#2' if 'other' is the parent
+                              "   codeline and if they are in sync.
+<
+You can pass multiple such modifiers too, though the main use is with the
+depot-modifier as described below.
+
+                                            *perforce-depot-modifier*
+The '&depot' modifier is treated specially. Instead of treating 'depot' (or the
+name of the current depot) as an alternative codeline, the filename is converted
+to its corresponding depot name. This is useful if the local file is not part of
+your client spec or is deleted from the depot. See also
+|perforce-edit-alternative-file| >
+        :PF filelog a-deleted-file&other&depot
+<
+
+                                            *perforce-local-modifier*
+The '&local' modifier is treated specially. Instead of treating 'local' as an
+alternative codeline, the filename is converted to its corresponding local name.
+
+==============================================================================
+
+                                            *perforce-forms*
+                                            *perforce-specifications*
+                                            *perforce-interactive-commands*
+Most of the perforce forms can be edited right from with in vim. The perforce
+command line normally invokes the external editor to edit the forms and when
+you save and quit the editor, the form is read back and the corresponding
+settings are updated. E.g., the following steps describe how you modify client
+specification using the p4 command:
+>
+        $ p4 client
+
+        # A Perforce Client Specification.
+        #
+        #  Client:      <so and so>.
+        .
+        .
+        :wq
+
+        Client <so and so> saved.
+<
+    The aim of the plugin is to be able to do most of such actions without
+needing to leave Vim. So when you execute a command that requires editing a
+form, the plugin automatically generates the form in a new window for you to
+edit. You can then modify it as you would normally in the external editor
+invoked by the p4 command, and to finally save it, use the normal |:write| or
+the special "W" (|perforce-W|) command. The plugin then tries to send the
+changes back to the server and generates the result also on the same window. The
+entire process would appear the same as above, except for
+
+        1. you would use "PF client" or |PClient| in Vim, instead of the
+           "p4 client" in the shell.
+        2. Edit the specification as you would otherwise.
+        3. The specification will be automatically written back to perforce when
+           you save it like any other file (using :w or :W).
+
+Note that you can also use :wq to save and quit the window at the same time, but
+because of a known issue in Vim as of the release version of 6.1, your changes
+could be lost if the specification has an error. There is a patch available for
+this that fixes the problem, so apply the patch or use the safer :WQ command.
+
+What is the advantage?
+
+        - You don't have to leave your Vim window or look for a command prompt.
+        - If you change your mind, it is easy to quit/leave or even postpone
+          (especially for submits, where you can convert it to a new
+          change list) the spec window.
+        - On errors you can just undo (by pressing 'u') and retry, as many
+          times as you need to.
+        - You have the option of opening the specification while viewing its
+          corresponding list. E.g., you can execute "PF changes -s pending"
+          and press "O" command on any change to open its change specification
+          and easily modify it.
+        - You can also view multiple specifications at the same time. You can
+          for example easily move files from one change list into another.
+        - You have additional commands defined local to the spec window, that
+          are specific to the type of spec being edited.
+        - Based on your working habits, you have other advantages that are
+          inherent in using the same Vim session for multiple things.
+        - It is much easier to use the Vim editing techniques to filter the
+          filelist or edit the description instead of using the GUI in p4Win.
+        - To me, it is also more fun to do it this way :)
+
+Unlike in earlier versions of the plugin, the specification buffers are now
+regular buffers, so they get marked as 'modified' when you start editing them.
+This prevents you from accidentally quitting the buffers, without writing them
+back to perforce. Also, Vim creates 'swapfile's for these buffers, so in case
+your session crashes, you can retrieve your changes (such as the description or
+your filelist) from the swapfile (see |crash-recover|). The swapfile will
+usually be created in the current directory or in a fixed directory specified by
+'directory' setting. When working with perforce specifications, the swapfiles
+could also be created in the temp directory, as the buffer names are often
+invalid on the file system (in which case the swap filename may not be that
+obvious).
+
+                                            *perforce-W*
+                                            *perforce-WQ*
+The W command accepts arguments that are in turn passed to the corresponding
+p4 command, so you can pass additional arguments such as "-r" while using
+submit form.
+
+There is also a WQ command which is same as the W command except that it also
+close the form window when there are no errors.
+
+The equivalent menu entries are "Save Current Spec" and "Save and Quit Current
+Spec".
+
+                                            *perforce-changelist*
+The change command accepts a set of perforce filename patterns that are
+passed directly to "opened" to filter the files that should be included in the
+changelist. Without specifying the patterns, the command would work exactly the
+way the native "p4" command works, which means the changelist will start with
+all the opened files that are in the "default" changelist.
+
+==============================================================================
+
+                                            *perforce-submit*
+The submit command is handled slightly differently than other interactive
+commands, as the "PF submit" or |PSubmit| internally runs the "PF change"
+command to generate a submission template. There are a few additional features
+implemented which are discussed below.
+
+PSubmit command accepts additional arguments which are passed in as they are
+to "opened" command to generate the list of files to submit, so it is possible
+to create the template with a set of files to submit such as, "PSubmit % #" to
+submit the current and alternate files only (which is more flexible than the
+native command). Of course you can always run with out arguments to generate a
+full list of files and then remove the ones that you don't want. You can also
+pass in the -c changelist# to submit the given changelist non-interactively (or
+submit it from "changes" list). You can convert a submission template into a
+changelist by simply using the PSubmitPostpone instead of the W or WQ command.
+Similarly, when you are in the change specification, you can use the
+PChangeSubmit to submit the current change instead of first saving it using W or
+WQ command and then submitting the change list using the "PSubmit -c
+changelist#".
+
+On partial errors during the submissions (such as those that require a resolve
+before submission), perforce sometimes automatically creates a changelist for
+the files in the submission template, in which case the changelist number is no
+longer "new" and the status is no longer "pending", so the plugin automatically
+detects this scenario and adjusts the template for these values. All you have to
+do in such cases is to 'undo' as you normally would, and fix the error before
+trying to submit again.
+
+Note, arguments such as '-r' are remembered when the PSubmit is first invoked
+and are used during the :W or :WQ command.
+
+==============================================================================
+
+                                            *perforce-list-commands*
+                                            *perforce-list-view*
+                                            *perforce-item-list*
+When executing a perforce command that generates a list of {something} such as
+changes, you have special commands defined that are local to the buffer and
+are specific to the list you are viewing. This allows you to visually perform
+some operations on the individual item without needing to type a separate
+command on the item.
+
+There are commands defined for both the commandline as well as key mappings
+and menu items for these operations. Note however that the same mapping can
+behave differently on different list views, depending on what is the list that
+you are viewing. E.g., pressing D normally means delete the specific item, but
+on a filelog window when you select two history entries (select all the lines
+between the two versions, inclusive) and press D, you get a diff between the two
+versions.
+
+There are some generic list commands that work in most of the list views with
+somewhat consistent behavior:
+
+Note that for commands that generate a list of files (such as opened), there
+is a different set of commands defined, see |perforce-filelist|.
+
+                                            *perforce-common-list-commands*
+These commands are available in all the listing windows.
+
+                                            *:PItemDescribe* *:PItemOpen*
+                                            *:PItemDelete*
+    Command             Key         Meaning ~
+    PItemDescribe       <Enter>     Describe the current item. This shows a
+                                    summary of the current item in a
+                                    |preview-window|.
+    PItemOpen           O           Open the current item for editing.
+    PItemDelete         D           Delete the current item. You will have be
+                                    prompted to confirm the deletion.
+
+                                            *perforce-client-list*
+You can use all the commands described in |perforce-common-list-commands|, as
+well as the below:
+
+                                            *:PClientsTemplate*
+    Command             Key         Meaning ~
+    PClientsTemplate    P           Using the current client as a template,
+                                    start creating a new client spec. You will
+                                    prompted for the name of the new client.
+
+                                            *perforce-labels-list*
+You can use all the commands described in |perforce-common-list-commands|, as
+well as the below:
+
+                                            *:PLabelsSyncClient*
+                                            *:PLabelsSyncLabel* *:PLabelsFiles*
+                                            *:PLabelsTemplate*
+    Command             Key         Meaning ~
+    PLabelsSyncClient   S           Sync the client to the current label.
+    PLabelsSyncLabel    C           Sync the the label to the current client.
+    PLabelsFiles        I           List the files associated with this label.
+    PLabelsTemplate     T           Using the current label as a template, start
+                                    editing a new label spec. You will be
+                                    prompted to enter the name of the new label.
+
+                                            *perforce-changes-list*
+You can use all the commands described in |perforce-common-list-commands| as
+well as the below:
+
+                                            *:PChangesSubmit* *:PChangesOpened*
+                                            *:PChangesDiff*
+    Command             Key         Meaning ~
+    PChangesSubmit      S           Submit the current change list. You will
+                                    be prompted to confirm.
+    PChangesOpened      o           List files associated with this
+                                    changelist.
+    PChangesDiff        d           Show diff for the current pending or
+                                    submitted changelist.
+
+                                            *perforce-filelog-list*
+You can only use the describe command described in
+|perforce-common-list-commands| however there are other convenience commands
+defined for this view:
+
+                                            *:PFilelogDiff* *:PFilelogDSync*
+                                            *:PFilelogDescribe* *:PFilelogPrint*
+    Command             Key         Meaning ~
+    PFilelogDiff        D           Show diff between two selected versions
+                                    (works only in the visual mode).
+    PFilelogDSync       S           Sync to the current version.
+    PFilelogDescribe    C           Describe the changelist for the current
+                                    change.
+    PFilelogPrint       p           Run print on the current version.
+
+You can generate the diff between two version while viewing the history. For
+this, you need to select all the lines between the two version, inclusive, and
+press the 'D' key. You can also use the PFilelogDiff with a range of lines as
+a prefix, without needing to select the lines.
+
+    Example:
+        You can mark the first version as 'a' using the 'ma' command and mart
+        the second line as 'b' using the 'mb' command and execute: >
+
+            :'a,'bPFilelogDiff
+<
+
+                                            *perforce-filelist*
+When you execute the commands |POpened|, |PHave|, |PFiles| and |PDescribe| that
+generate a list of files, you can use the file list window to do further
+operations on the files.
+
+                                        *:PFileDiff* *:PFileProps* *:PFileEdit*
+                                        *:PFileRevert* *:PFilePrint* *:PFileSync*
+                                        *:PFileChange* *:PFileLaunch* *:PFileLog*
+    Command             Key         Meaning ~
+    PFileDiff           D           Run "p4 diff" on the current file.
+    PFileProps          P           Print the properties (fstat) of the
+                                    current file.
+    PFileEdit           I           Edit (checkout) the current file.
+    PFileRevert         R           Revert the current file.
+    PFilePrint          P           Print the current file in the preview
+                                    window. This is mostly same as
+                                    PItemDescribe, but handles the deleted and
+                                    binary files correctly.
+    PFileGet                        Sync the current file to the revision
+                                    specified in the filelist.
+    PFileSync           S           Sync the current file to the head
+                                    revision.
+    PFileChange         C           Open change list for the current file.
+    PFileLaunch         A           Launch the current file. On windows, it
+                                    works almost the same way as double clicking
+                                    the file in explorer. On non-windows
+                                    platforms, you need to explicitly configure
+                                    a launcher command by using the
+                                    |p4FileLauncher| setting. You need to have
+                                    (correct revision of) the file already on
+                                    the filesystem, if not first do a sync as
+                                    described above.
+    PFileLog                        Run "filelog" on current file.
+
+In addition, the plugin sets up 'includeexpr' such that you can use |gf| and
+|<cfile>| on the depot files. Since |gf| would result in the current buffer
+getting hidden, the current perforce buffer showing the filelist could get
+wipedout (unless this is prevented using the techniques described in
+|p4BufHidden|). If you have any mappings using |<cfile>| they should work well.
+
+Note: There is no quick help available to see which commands are available and
+what the mappings are for any given perforce result buffer. However, the output
+of nmap command is pretty useful and sufficient for this purpose. Type the
+following command as it is to see the perforce commands with their mappings
+(among others) >
+        :nmap <buffer>
+<
+==============================================================================
+
+                                            *perforce-extensions*
+There are some useful extensions provided over the perforce commands to make
+your job easier.
+
+|perforce-pending-change-diff|      Restricting diff to an open changelist.
+|perforce-vim-diff|                 Diffing using vim's built-in diff feature.
+|perforce-external-diff|            Diffing using external diff tool.
+
+                                            *perforce-pending-change-diff*
+This provides an useful "++c" option to the PDiff command to specify a change
+number that is open on this client. The plugin internally queries the open files
+under this change list and restricts the diff to only these files. >
+
+        PF diff ++c 1111 //depot/branch/src/...
+<
+The above restricts changes to only the changelist '1111' and under the src
+directory.
+
+                                            *perforce-builtin-commands*
+                                            *perforce-vim-diff*
+The plugin provides two built-in commands vdiff and vdiff2 to view diff using
+the Vim's built-in diff features instead of using the perforce diff and diff2
+commands respectively. If you need to customize the view, then read help on
+|diff-options|. These commands do not accept any options and will ignore them if
+any are provided. Make sure you don't have any windows that have diff mode set
+before running these commands as otherwise they will participate in the diff
+too.
+
+                                            *perforce-vdiff*
+                                            *:PVDiff*
+                                            *perforce-vdiff2*
+                                            *:PVDiff2*
+These commands are an alternative to the perforce diff and diff2 commands that
+use the Vim's built-in |vdiff| feature to generate the diff. Both command work
+exactly same as both accept upto two arbitrary local or depot files as
+arguments, but while "vdiff" command works with the files as they are specified
+(local or depot paths), the "vdiff2" command tries to convert them to depot
+paths as much as possible. There is also difference in how they choose default
+arguments when the number of arguments is less than 2. The "vdiff" commands are
+also a lot more flexible than the "diff" commands in that you can mix any two
+filenames as arguments, including those from different codelines, local and
+depot files and even those that are not even related.
+
+With only one file (or the current file when no arguments) specified as
+argument, the "vdiff" command like the "diff" command, diffs the file against
+that of depot, where as the "vdiff2" command, like the "diff2" command prompts
+you to enter the two depot version that you would like to diff for the specified
+file.
+>
+        :PF vdiff " Diff the current file against depot.
+        :PVDiff % &altBranch " Diff the current file against the same from a
+                              " different branch.
+        :PVDiff2 \#1 \#2 " Diff the revisions 1 and 2 for the current file.
+<
+                                            *PFDiffOff*
+These commands always open new windows split vertically side-by-side to start
+diff settings (diff settings are local to windows), so that your existing
+windows are not disturbed. When you are done viewing the diff, you can just
+close the diff windows and be done. But in case you need to reset the diff
+settings, there is a command called "PFDiffOff" provided for convenience. The
+command is very flexible in the sense that, it can identify the diff windows
+that belong to one diff operation and when run from one of them, can turn off
+the diff settings on all the related windows.  This is useful to incrementally
+add/remove diff windows using PVDiff and PVDiff2 commands. If run outside of any
+perforce diff windows, it turns off diff for all perforce windows.
+
+                                            *perforce-default-diff-format*
+Pass the dummy option "-d" (with no diff flags) to perforce commands that
+produce diff output to force the format to be in the default diff format. This
+is useful in case the |p4DefaultDiffOptions| is used and you temporarily want
+the diff output to be of default type. This option is not really recognized by
+the p4 command, and so will be removed from the command string before seen by
+the p4 command (and so is merely used to avoid adding the default diff options).
+>
+        :PF describe -d 100
+<
+                                            *perforce-external-diff*
+                                            *perforce-GNU-diff*
+The "diff" command now supports running external GNU diff to generate the diff
+output. To use this feature, make sure you have GNU diff installed in the path
+and pass one or more of the valid GNU diff command options using the following
+syntax:
+>
+        +<short or long option>[=<optional argument>]
+<
+E.g., to generate diff output for the current file in unified format with the
+whole file in context (like the diff output in p4Win), you could use the
+following comand:
+>
+        :PDiff +U=99999
+<
+Just make sure that the number is large enough to include the whole file.
+Another example is:
+>
+        :PDiff +strip-trailing-cr +context=10 +W=120 +w
+<
+                                            *perforce-execute-direct*
+                                            *:PExec*
+This is a built-in command that executes arbitrary vim commands after
+processing the command-line the same way as it would if you executed any regular
+perforce command. This is useful to execute external perforce commands (though
+not limited to them) directly, when you find that you can't do some task using
+the features provided by the plugin alone. You typically want to execute
+perforce commands using Vim's |:!| feature, e.g.: >
+
+        :!p4 sync %
+<
+But if you want to take advantage of the various command-line shortcuts provided
+by the plugin, then you would just pass the command as it is to :PExec: >
+
+        :PExec !p4 sync %&altbranch#3
+<
+The above would sync the current file in a parallel branch called
+"altbranch" to the version 3. Using the command also has the other inherent
+advantages such as, you don't have to protect the filenanme special character
+such as "#". The plugin also performs the same escaping mechanism on the
+external command that it does on the regular plugin commands, however any
+filters specified are not currently escaped.
+
+Note, the above two commands are just examples that can easily be achieved using
+the following plugin commands: >
+
+        :PF sync
+        :PF sync &altbranch#3
+<
+==============================================================================
+
+                                            *perforce-misc-commands*
+These are some misc. commands that are provided by the plugin in addition to the
+commands that are already described.
+    |PFRefreshActivePane|, |PFRefreshFileStatus|,
+    |perforce-edit-alternative-file|, |PFSwitch|, |PW|, |PFToggleCkOut|,
+    |PFLastMessage|, |PDiffLink|, |PDiffPLink|
+
+                                            *perforce-refresh-active-pane*
+                                            *:PFRefreshActivePane*
+This command allows you to refresh the active perforce window (the window where
+the cursor currently is). The window should have been a result of a perforce
+command, to be able to refresh it. There is also a menu entry and a normal-mode
+mapping <Leader>prap to do the same.
+
+                                            *perforce-refresh-file-status*
+                                            *:PFRefreshFileStatus*
+Use this command to manually refresh the |perforce-ruler|. Useful when you
+have the automatic refresh disabled by setting the |p4EnableActiveStatus|
+to 0. You can also use the normal-mode mapping <Leader>prfs to do the same.
+
+                                            *perforce-edit-alternative-file*
+                                            *E*
+                                            *ES*
+These commands allow you to open the current file from an alternative codeline.
+The syntax of the command is: >
+
+    E [codeline] [files: default %]
+    ES [codeline] [files: default %]
+<
+The difference between the two is that ES opens the file by splitting the
+current window, where as E opens the file in the current window. You can specify
+more than one file in which case the first one is still opened, but the
+remaining files are just added to the buffer list so you can open them later
+using a buffer explorer, or using :e #<buf>. If no arguments are passed, (just
+type E or ES on its own), you will be prompted to enter the codeline.
+
+                                            *perforce-write-file-contents*
+                                            *:PW*
+The |PW| command is a special command that you can use to filter the current
+file contents through p4. You can specify the range of lines that need to be
+written to the command and the default range is the entire file (1,$). The "W"
+(|perforce-W|) or "WQ" (|perforce-WQ|) command described in |perforce-forms|
+internally uses this command to write the form back to the perforce command and
+read the result back. The command itself uses the |perforce-filter| feature to
+do its job.
+
+The following command can be used to revert the contents of the current file
+without using the perforce "revert" command.  You can save the file, but you can
+always do |:undo| to get back to your original contents (provided you haven't
+lost your undo history). So this can be used to temporarily revert contents to
+the depot version and then get back to your original version.
+>
+        :PW print -q
+        :w
+        :undo
+<
+Also see |perforce-command-mode-specifier| for alternatives to using this
+command.
+
+                                            *perforce-toggle-checkout-prompt*
+                                            *:PFToggleCkOut*
+This command can be used to disable/enable the automatic checkout prompt while
+editing the readonly files. This is useful while you are in a read-only vim
+(started with -R option), so even the files that are already checked out also
+appear as read-only files, in which case, you don't want to see the checkout
+prompt when you accidentally start modifying a file (or for that matter even
+when you deliberately modify a file).
+
+                                            *perforce-last-message*
+                                            *:PFLastMessage*
+Prints the last given message again.
+
+==============================================================================
+
+                                            *perforce-special-commands*
+
+                                            *:<pfitem>*
+You can use the special tag <pfitem> on the command-line to mean the current
+list item. This works very close to how |:<cword>| etc. work. The command-line
+parser would replace this with the value of the current item (which is dependent
+on the type of list view) at runtime. This is useful to create your own
+mappings/commands over what the plugin provides.
+
+                                            *perforce-special-mappings*
+There are some special mapping created for the command-line usage.
+
+While you are in a list view (such as list of labels), you can get the name of
+the current label on to the command-line by just typing <Ctrl-X><Ctrl-I>. This
+is useful to quickly and accurately execute commands on the items that have
+long names.
+
+On the same lines as the E command (|perforce-edit-alternative-file|), there
+is another command-line mapping created for inserting the name of an
+alternate file right at the command-line. You can do this by typing
+<Ctrl-X><Ctrl-P>. You will be prompted to enter the name of the alternative
+codeline.
+==============================================================================
+
+                                            *perforce-utils*
+These are available only when you install the perforce/perforceutils.vim plugin
+as described in the |perforce-installation| section.
+
+                                            *perforce-diff-mode*
+                                            *perforce-diff-hyperlink*
+                                            *:PDiffLink* *:PDiffPLink*
+Executing various commands such as |PDiff| and |PDescribe| produce output in the
+form of perforce diff (very similar to GNU diff output). The plugin provides two
+commands to make it easier to navigate from the diff output to the original
+source file like a hyperlink. The location of the original source file and the
+line number are extracted from the diff output (supports the default, context
+and unified formats). To open the source file and take the cursor to correct
+location, use :PDiffLink command or press "O". To open the source file and
+position the correct location in the preview window, use :PDiffPLink or press
+<CR>. These commands are defined for only those windows that contain perforce
+diff output.
+
+Note that the commands can be executed on both the old and new source lines, and
+the plugin either opens the appropriate file on the local filesystem or
+"print"s the correct version from Perforce to position the cursor. When diff
+refers to a depot file and the corresponding local file is already open in Vim
+and has the same revision as of the depot file that the diff refers to, the
+plugin opens the local file instead.
+
+Note these commands are capable of handling regular GNU diff output formats too,
+so you can use it on diff outputs generated using the "diff" command alone.
+
+                                            *:PFDiffLink*
+                                            *:PFDiffPLink*
+These commands provide the same functionality that |PDiffLink| and |PDiffPLink|
+provide in a diff windows, except that you can run them in any buffer that has
+diff style output. Mostly useful if you are viewing diff obtained in a patch.
+
+                                            *perforce-show-conflicts*
+                                            *:PFShowConflicts*
+This command is useful while resolving conflicts using an interactive
+"p4 resolve" command. When you choose the "e" option to edit files, perforce
+generates a single file containing changes from ORIGINAL, THEIRS and YOURS. It
+is usually hard (especially when the conflicting region is large) to figure out
+what others have changed and how to resolve them. Using a visual merge tool
+usually helps, but this commands provides an alternative by allowing you to use
+the |diff-mode| features in Vim. Once p4 invokes Vim as the EDITOR using a
+tempfile as the argument, you can run this command to generate three vertical
+windows each containing changes from ORIGINAL, THEIRS or YOURS only, and invokes
+the |diff-options| on them. Edit the YOURS file as usual and use the |:diffget|
+command from within YOURS or the |:diffput| command from the other two windows
+to move diff regions into the YOURS file, and finally write the changes back to
+the original temp file using the |:write| command.
+
+                                            *perforce-selectbuf-ftplugin*
+                                            *perforce-selectbuf-integration*
+The perforce plugin now comes with ftplugin that adds some perforce commands to
+the SelectBuf buffer browser. If you have the SelectBuf plugin installed, you
+don't need to do anything special to take advantage of this. When you are
+viewing the buffer list, you can execute the following commands directly on the
+current buffer in the list, or the current selection of buffers:
+
+        P4 Command      Map ~
+        add             <Leader>pfa
+        sync            <Leader>pfg
+        edit            <Leader>pfe
+        delete          <Leader>pft
+        revert          <Leader>pfr
+        submit          <Leader>pfs
+        lock            <Leader>pfl
+        unlock          <Leader>pfu
+        diff            <Leader>pfd
+        diff2           <Leader>pf2
+
+I also recommend also installing multiselect.vim plugin that you can download
+from www.vim.org that allows you to select multiple buffers that are
+non-adjacent to do operations on them. This allows you to e.g., submit a few
+files together that are spread out far from each other in the buffer list.
+==============================================================================
+
+                                            *perforce-API*
+The plugin comes with an experimental API that you can use to extend the
+functionality and provide some integration. Please send in your feedback to
+improve the API. You should also look at the perforce/perforcemenu.vim and
+perforce/perforcebugrep.vim for examples.
+>
+        " Return the value of the variable in the script context (so add
+        "   appropriate prefix).
+        " Ex:
+        "   let curClient = perforce#PFGet('s:p4Client')
+        String perforce#PFGet(String var)
+
+        " Set the value of the specified variable in the script context, to the
+        "   value given.
+        " Ex:
+        "   call perforce#PFSet('s:p4Client', 'xxx')
+        void perforce#PFSet(String var, String val)
+
+        " Call the specified function in the script context with the arguments
+        "   passed and return the result. Pass appropriate number of arguments
+        "   based on the function that you are calling.
+        " Ex:
+        "   echo perforce#PFCall('s:PFIF', '0', '4', 'info')
+        "   let client = perforce#PFCall('s:GetSettingValue', 'Client')
+        String perforce#PFCall(String func, ...)
+
+        " Evaluate the given expression in the script context and return the
+        "   result. The expression can be any Vim expression that is valid on
+        "   RHS of a variable assignment.
+        " Ex:
+        "   let client = perforce#PFEval('s:p4Client')
+        "   let client = perforce#PFEval('s:GetSettingValue("Client")')
+        String perforce#PFEval(String expr)
+<
+==============================================================================
+
+                                            *perforce-tips*
+- If you are new to Perforce, try the help browser using the |PH| command. You
+  can easily move back and forth in the help history using <BS> and <Tab> keys.
+
+- The :W and :WQ commands also accept arguments that are passed as they are to
+  the external p4 command. If you forgot to specify '-r' option to PSubmit,
+  you can still specify it to the :W or :WQ command.
+
+- How to quickly open the current file from a different codeline?
+>
+    :E <codeline>
+<
+- How to quickly and easily find and open a file, say x.y, which you know is
+  somewhere under the current directory?
+>
+    :PF files .../x.y   - List the files that match x.y in the codeline. This
+                          opens a new window with all the files that match x.y.
+                          You can move cursor to the file-line that you want to
+                          open.
+    O                   - Open the file that is displayed under the cursor.
+    ^Wo                 - Close all other windows (see |CTRL-W_o|).
+<
+  This technique can also be used with other wildcards that perforce supports.
+  E.g, you can find all the shell scripts that are checked in to a branch by the
+  following command (assuming they all have .sh extension):
+>
+>
+    :PF files //depot/branch/.../*.sh
+<
+- How to quickly and easily find and launch a file in its associated
+  application (needs configuration on non-windows platforms)?
+>
+    :PF files .../x.y   - List the files that match x.y in the codeline. This
+                          opens a new window with all the files that match x.y.
+                          You can move cursor to the file-line that you want to
+                          open.
+    A                   - LAunch the file.
+<
+- How to easily reach to a file that you know is currently checked out?
+>
+    :PO                 - This will create a new window with all the opened
+                          files. You can move cursor to the file-line that you
+                          want to open.
+    O                   - Open the file that is displayed under the cursor.
+    ^Wo                 - Close all other windows.
+<
+- You can temporarily disable the p4DefaultListSize (by default set to 100)
+  while running some list commands by using the -m -1 arguments, to see all
+  the results.
+>
+    :PChanges -s pending -m -1
+<
+- The plugin defines some long normal mode mappings, which could be hard to type
+  without making errors or pausing for a brief moment. To type in such long
+  mappings comfortably, you can download and install the execmap.vim plugin from
+  www.vim.org.
+- You can create aliases for most used command combinations using the Vim
+  |:command| feature (from your vimrc). E.g. the following gives my pending
+  change lists.
+>
+    :command! PendingChanges :PF changes -s pending -u hkrishna -m -1 <args>
+<
+- To quickly go to the lines that you have modified in the current file, open
+  the diff window against the depot, scroll/find the line that you are
+  interested in and use |perforce-diff-mode| features.
+>
+    :PD
+    /FIXME
+    O
+<
+- To quickly start over with the depot version, without needing to execute
+  "revert" followed by an "edit", use the |PW| command.
+>
+    :PW print -q
+
+- To insert the output of any perforce command at the current location, first
+  open a blank line and run the command using |PW.
+>
+    o<Esc>
+    :.PW describe -s 100
+<
+- Set "p4CurDirExpr" to the following:
+>
+    let g:p4CurDirExpr = "(isdirectory(expand('%')) ? substitute(expand('%:p'),
+            \ '\\\\$', '', '') : '')"
+<
+  to have the commands run from the directory that you are currently viewing,
+  instead of the current directory of Vim.
+
+- If your colleague sends you diff for review, you can make use of :PFDiffLink
+  and :PFDiffPLink commands to make it easier to reach the "before" file. In
+  addition, if your colleague's dev folder is accessible by you in the same m/c
+  (typically for a UNIX server), you should be able to look at the "after"
+  changes in context. Even if the dev folder is accessible only over the
+  network, you can convert the local paths in the diff to the network paths
+  (E.g., you could convert "c:\dev" to "\\tom\dev"), and browse the changes
+  comfortably.
+
+- Do you know that :PFDiffLink and :PFDiffPLink commands can be used on regular
+  "diff" command output too?
+
+- To quickly search for change descriptions, you can print the list of changes
+  matching a given restriction, with their descriptions and use Vim to search
+  for the pattern. You can then describe the change for more information. Ex: >
+
+    :PChanges -l //depot/branch/src/server/...
+    /socket
+<
+- Use VDiff if you want to revert only parts of your changes.
+                                            *perforce-explore-changed-lines*
+- Here is a technique I often use to track who and when someone made a
+  particular change. The technique assumes you have a branch which includes all
+  the releases (typically called "release" or "main"). You would first get the
+  annotations as below: >
+
+    :PF annotate -a &main&depot
+<
+  Locate the line that has been changed (you might find multiple lines due to
+  reformatting) and identify the version. Run the below command to identify the
+  change (can be executed from the annotate window): >
+
+    :PF filelog -i
+<
+  This gives you the change number and integration history for that version.
+  Sometimes this is not enough to find the origin branch, so you can continue to
+  execute "annotate" and "filelog" commands until you find the original branch
+  and change number.
+
+==============================================================================
+
+                                            *perforce-limitations*
+    - Interactive resolves can't be done using the plugin. You can however use
+      auto resolves by passing options such as -as to the 'resolve' command. See
+      "PH resolve" for help on auto-resolves. Also take a look at the
+      |PFShowConflicts| command.
+    - The plugin can work with only one depot at any time, but you can easily
+      switch between different depots by changing the |p4Depot| setting through
+      the |PFSettings| command.
+    - When executing commands that take a lot of time, such as syncing on the
+      entire branch, Vim waits for the command to complete and exit before
+      the plugin can display the result. So until Vim becomes more capable in
+      executing external commands in this regard, I recommend not to run such
+      commands using the plugin.
+    - Since 'q' is mapped to quit the perforce windows, it is hard to record
+      macros that involve dealing with perforce windows. A workaround is to
+      create a new mapping to the "q" command and use that to start and stop
+      recording instead, something like this:
+>
+        nnoremap <F12> q
+<
+                                            *perforce-troubleshooting*
+    - If none of the perforce operations work for you, then make sure you set
+      your |p4CmdPath| setting correctly. You can run PFS command and select
+      CmdPath setting to see what the current value is. On windows, if you have
+      back-slashes in the path, then make sure your 'shell' setting can honor
+      them. The 'shellslash' setting is also important if you use a UNIX-like
+      shells on windows. IF all seems to be well, then please report the problem
+      to me with your 'shell' setting and OS details.
+    - If you get E485 errors occassionally or most of the time, and your shell
+      related settings seem to be fine, then point your $TMP and $TEMP
+      environmental variables to some path that is shorter ("/tmp" instead of
+      "C:/DOCUME~1/HARI/LOCALS~1/Temp").
+    - If the online perforce help is not working (ie., you are reading this by
+      directly opening the file, instead of by typing :h
+      perforce-troubleshooting :), then make sure you ran |:helpt| command.
+    - If you are not getting automatic checkout prompt when you modify a
+      read-only file for the first time (and everything else seems to work
+      fine), then make sure you set your |p4ClientRoot| property correctly. The
+      plugin ignores any files that are not under your root from giving this
+      prompt.
+    - If vdiff and vdiff2 commands don't work for you, make sure the vimdiff
+      itself works for you. Try running the following command on any two files:
+      >
+            gvim -d file1 file2
+<
+      If the above command doesn't produce any diff though they are different,
+      or gives any error messages, then first go through the help on |vimdiff|
+      to get the standalone diff working.
+    - While editing perforce specifications from commandline, if you don't see
+      the perforce syntax or cursor is not positioned at a convenient position,
+      then make sure you added perforce filetype as described in
+      |perforce-filetype|.
+    - If |perforce-ruler| doesn't work for you, make sure you have 'ruler'
+      option set. Also make sure you didn't disable |p4EnableRuler| setting. It
+      is also possible that another plugin is overwriting the 'rulerformat'
+      setting (instead of appending to it, as done by the perforce plugin) after
+      the perforce plugin configures it. Also, if you are in the 'paste' mode,
+      Vim automatically disables ruler, so make sure you don't currently have
+      'paste' option set.
+    - If you observe a noticiable delay in Vim startup time after you installed
+      perforce plugin, it may be because, the plugin is trying to obtain the
+      value of |p4ClientRoot| setting by executing the "PF info" command. You
+      can avoid this by setting this property yourself in your vimrc.
+    - If piping a spec manually to a perforce command such as 'change' or
+      'submit' using ++p option is not working, make sure the command accepts a
+      '-i' option to read the spec from stdin and that you are passing in this
+      option.
+    - If you get a weird invalid option error, or if the command behaves
+      weirdly, make sure you don't have a typo in the command-name. The
+      command-line parser recognizes only the known perforce commands, which
+      makes an incorrect command name a global option, making it an invalid
+      syntax. E.g., if you mistype "opened" as "open", you get an error that
+      "-u" is an invalid option.
+
+                                            *perforce-version-changes*
+These are just a summary of the changes done in the current and previous
+versions. Though I have done a good attempt to list all, I could have very well
+missed some changes.
+
+                                            *perforce-changes-4.1*
+    - Fixed broken handling of <SHOW DIFF> in describe windows. But it is better
+      than before, as you can now describe multiple changelists and show diffs
+      selectively.
+    - For newer perforce servers, <Enter> on a pending change (in changelist)
+      showed the file list twice. Removed special handling for this, which means
+      for older perforce servers, you will see no file list.
+    - Force a file status update on auto checkout.
+    - File status handling has in general been improved.
+        - Now executing command on multiple files that result in changing the
+          file statuses (such as add, edit, revert etc.) will correctly result
+          in their file statuses getting reset.
+        - While create a new changelist or submitting a change, the Files:
+          section is examined and the file statuses for all of them will be
+          reset. This also works for most of the cases of modifying a changelist
+          to remove/add files.
+        - This will also solve a long standing issue that reload during submit
+          doesn't update its file status.
+    - All windows are getting navigatation commands mapped (like in help window)
+    - Workaround for one of the E788 errors (originating from the plugin) during
+      the auto-checkout. This part of the code has been cleaned up and
+      simplified. During the auto-checkout, if there are other users editing the
+      same file, it now results in the plugin echoing the output as a
+      |WarningMsg|. If you missed to read the output (because you pressed
+      <Enter> in advance), you can see it again using the |PFLastMessage|
+      command. The other E788 originating from Vim code can't be
+      fixed/workedaround, it has to be fixed by Bram.
+    - Fixed broken submit from changelist.
+    - When you create changelists, you can now safely undo to make any further
+      changes, and save them. This also works for submissions (to edit
+      description only), though you may have to remove the Files section before
+      saving the change description.
+
+                                            *perforce-changes-4.0*
+    - Using Vim7 features, so it is no longer backwards compatible with older
+      Vim releases. All the logic using multvals has been changed to take
+      advantage of the Vim7 Lists, so it should be a lot more cleaner and
+      flexible.
+    - No longer depends on multvals plugin.
+    - It is now autoloaded on demand, which means it will help your vim session
+      load faster. Read the impact on the installation due to this change,
+      |perforce-installation|.
+    - A new Cancel option for checkout prompt, see
+      |perforce-automatic-checkout|. The default for checkout prompt is now
+      "Cancel".
+    - The perforce/perforcemenu.vim needs to be loaded from your vimrc if you
+      want menu to be enabled. See |perforce-installation|.
+    - The plugin no longer removes the global user setting variables but you
+      still need to call |:PFInitialize| for effect of some settings to
+      propogate further. This should have no user visible impact (except in rare
+      cases).  This will only make it easier to deal with settings. You can
+      still use |:PFSettings| command conveniently for its prompting or
+      completion features.
+    - Setting a preset to the g:p4DefaultPreset directly now works fine.
+    - Most settings can now be overridden at the buffer/window/tab level.
+    - PFRefreshActivePane doesn't work well on the diff windows (especially when
+      the ++c option is used).
+
+                                            *perforce-changes-3.2*
+    - Fixed PVDiff to work with two filenames. The problem was only with PVDiff
+      command, as "PF vdiff" worked fine.
+    - PFDiffOff command is a lot more flexible now, see |PFDiffOff|.
+    - New command PPasswd for changing passwords.
+    - Don't confirm revert if -a or -n option is passed.
+    - Avoid accidentally loosing existing buffers while opening new ones from
+      diff and other windows.
+    - Recognize additional p4 commands as valid.
+    - You can now pass multiple codeline modifiers, see
+      |perforce-alternative-codeline-modifier|.
+    - Misc. tuneups for peforce diff hyperlinking feature.
+    - Misc. bugfixes in the menu.
+
+                                            *perforce-changes-3.1*
+
+    - This version introduces the concept of overriding settints at the
+      buffer/window level (an extension of the existing support for
+      b:p4Options). Makes it easier to work with multiple clients from a single
+      vim instance. Currently only the p4Client/p4Port/p4User/clientRoot can be
+      set at buffer/window level.
+    - Now view mappings are maintained separately for each client. This allows
+      us to easily work with multiple clients at once. Also see
+      |perforce-buffer-local-options|.
+    - For diff hyperlinking, avoid refreshing the depot file if it already
+      visible.
+    - Now supports <pfitem> tag to mean the current list item. See |:<pfite>|
+    - New :PExec command to make it easier to execute exeternal perforce
+      commands directly, when plugin can't do what you want. See |PExec|.
+    - PFDiffLink and PFDiffVLink commands couldn't handle "diff -r" output.
+    - If the current directory is not same as the directory of file being
+      resolved PFShowConflicts didn't work.
+    - PItemOpen in describe window now opens the local file, as PItemDescribe
+      can be used to open the depot file.
+    - Misc. bug fixes:
+        - Negative revisions are not working any more. E.g., PP #-1
+        - PW is not using the custom completion, so revision specifier (#1)
+          still needs to be escaped.
+        - While rerunning a command that opens up a new buffer (such as PD),
+          unexpected warning messages about matching an existing buffer.
+          Instead, it should silently refresh the output.
+        - Diff hyperlinking, prints the depot file everytime, this causes
+          unnecessary delays.
+        - PFRefreshActivePane would fail if there are filename special
+          characters in the command.
+    - PF command now implements a special completion mode to complete both the
+      perforce command as well as files. Now you no longer need to protect the
+      revision specifier (#<revision) as long as it appears at the end of the
+      argument.
+
+                                            *perforce-changes-3.0*
+    - Discard the diff output if P4DIFF env. var. is set (Denis Perelyubskiy).
+    - There is an experimental API to allow some modularization of the plugin.
+      See |perforce-API|. There are plugin modules available under perforce
+      directory that take advantage of this.
+    - New PFBugReport command to generate "perforcebugrep.txt" file. This is
+      implemented as a new module perforcebugrep.vim using the above API.
+    - The menu creation is now separated as a new module that is executed on
+      demand. Other than giving some modularity, this effectively reduces the
+      resources consumed by the plugin, especially when the menus are disabled.
+    - New settings and features that allow dynamic switching of perforce client
+      settings (Mark Brophy). See |perforce-dynamic-client|.
+    - The default options don't get saved with the perforce windows (Mark
+      Brophy).
+    - IsFileUnderDepot() should ignore case for windows (Mark Brophy).
+    - Some enhancements to the "diff-hyperlink" feature, see |PFDiffLink|.
+    - The plugin now translates the depot paths to local paths and vice versa
+      accurately by reading the client specification. This is an experimental
+      feature and so will very likely have bugs in it. While reporting bugs,
+      please include the "View" section in your client specification and the
+      path. See |p4UseClientViewMap| and |PFUpdateViews|.
+    - For consistency and to avoid potential conflict with the actual perforce
+      commands, most of the plugin commands that are not associated with any
+      external perforce command are prefixed with "PF". This involves renaming
+      of the following commands. If you would like to continue to use the old
+      command, you can add your own commands with the old names that in turn
+      call the new commands.
+            PWipeoutBufs          -> PFWipeoutBufs
+            PDiffOff              -> PFDiffOff
+            PToggleCkOut          -> PFToggleCkOut
+            PRefreshFileStatus    -> PFRefreshFileStatus
+            PRefreshActivePane    -> PFRefreshActivePane
+            PSwitch               -> PFSwitch
+            PSwitchPortClientUser -> PFSwitchPortClientUser
+            PLastMessage          -> PFLastMessage
+            PBugReport            -> PFBugReport
+    - :PFSettings command now optionally takes the name of the setting (as
+      displayed in the interactive session) and the value. The command also
+      supports completion, so you can type in a partial setting name and let Vim
+      complete it for you.
+    - I have changed the way the perforce client related settings are done.
+      Instead of using three different settings, "p4Port", "p4Client" and
+      "p4User", I have introduced a single setting for all the three called
+      |p4DefaultPreset|. The old settings are no longer recognized and these
+      will not appear when |PFSettings| is run. However, you can use |PSwitch|
+      and |PSwitchPortClientUser| commands to change to an arbitrary client at
+      runtime. Other than |p4DefaultPreset|, there are two new settings,
+      |p4CurPresetExpr| and |p4CurDirExpr|. Also, to be consistent, the
+      p4Password setting is no longer supported. Use "P4PASSWORD" environmental
+      variable for the same effect, or just let the plugin prompt you for one
+      when required.
+    - New command |PFileEdit| in filelist view.
+    - New plugin perforce/perforceutils.vim. Adds a useful |PFShowConflicts|
+      command. See |perforce-utils|. The |perforce-diff-hyperlink| feature is
+      also moved into this module with some enhancements.
+    - For consistency, some built-in options have been changed. Now all the
+      built-in options should be prefixed with "++". The options prefixed with a
+      single "+" are reserved for passing options to the external commands. The
+      following options have been modified to accommodate this:
+            Old option  New option  Scope ~
+            +y          ++y         PF, PWipeoutBufs
+            +c          ++c         PDiff
+    - New diff mode to execute external GNU diff command to generate the diff
+      output. See |perforce-external-diff|.
+    - New commands on filelist view, PFileLaunch, PFileLog. PFilePrint has been
+      enhanced to print previous version if the head action is "delete" and
+      avoid printing binary files.
+    - Many misc. bug fixes and enhancements and general toning down of the code.
+    - A new integration with SelectBuf plugin. If you install SelectBuf, you can
+      execute a bunch of commands right from the buffer list. See
+      |perforce-selectbuf-integration|.
+    - The default value for 'bufhidden' setting in the perforce result buffers
+      is now set to "wipe". This works much better than the earlier approach to
+      wiping out buffers as they get unloaded, and gives better control to
+      users. Consequently, the g:p4HideOnBufHidden option is now replaced with
+      the g:p4BufHidden option.
+    - The plugin no longer handles all the FileChangedShell events, instead only
+      the events generated during the execution of the perforce commands are
+      captured to refresh file-status. This is so that the default Vim mechanism
+      is least impacted. To manually refresh the ruler see
+      |perforce-refresh-file-status|.
+    - New :PChangesDiff command in changes window.
+    - Fixed the argument parser to recognize context size in the diff flags
+      (such as "-du10"), didn't actually know that you could pass context to the
+      diff flags. Also, the |perforce-default-diff-format| option has been
+      changed from "-dd" to simply "-d".
+    - All the perforce commands now implement a custom completion that completes
+      arguments in a context sensive manner. They can also complete depot paths
+      by running the "dirs" and "files" commands. In addition, |PHelp| supports
+      help topic completion, |PFSettings| supports setting name completion and
+      |PFSwitch| commands supports preset completion.
+
+                                            *perforce-changes-2.0*
+    - Renamed g:p4CodelineRoot to g:p4ClientRoot.
+    - Fixed a problem with choosing a default username on cygwin.
+    - Added support for showing ruler with the file status from "Tom Slee",
+      Perforce plugin with some enhancements.
+    - Added support to obtain the clientRoot from the perforce server, if it
+      is not already defined.
+    - More robust error handling. Now there is very less chance (or none) of
+      messing up the current window.
+    - Fixed to use setlocal instead of set command for changing some settings.
+    - Filelog also honors the defaultListSize option.
+    - Better formatting options for the form windows.
+    - The default option for checkout file dialog is changed from "Yes" to
+      "No". Use the |p4CheckOutDefault| option to get the old behavior.
+    - On Windows, allow execution of commands containing filename special
+      characters by replacing them with a [x] sequence. They are mapped as,
+>
+          '*' -> [S], 
+          ':' -> [C],
+          '?' -> [Q],
+          '"' -> [D],
+          '<' -> [L],
+          '>' -> [G],
+          '|' -> [P],
+<
+    - Now you can use -ve revisions to indicate previous revisions from the
+      head. You can also use branch specifiers to mean the same file from a
+      different branch. Also these enhanced revision specifiers are now
+      acceptable anywhere.
+    - Extended diff to take "++c" argument to specify change number. See
+      |perforce-pending-change-diff|.
+    - Added vdiff and vdiff2 commands. Added p4UseVimDiff
+      option. See |perforce-vim-diff|.
+    - Added PFileChange command for the filelist window.
+    - You can now pass "++y" option to revert to skip the prompt.
+    - New command |PFSettings| to interactively change the settings of a
+      session.
+    - The script now has a more robust and compact architecture, which actually
+      helped reducing the size of the plugin even after adding many more
+      features. I have also consolidated all the logic into one method described
+      by a set of metadata variables. Adding new features in the future should
+      be easier.
+    - The opened list is now better than ever. You can use it to quickly reach
+      to a file that you have already checked out of perforce. See
+      |perforce-filelist|
+    - You now have a way to preserve the perforce windows from getting wipedout
+      as soon as they are hidden. This feature can be used if you are intending
+      to keep them opened for a long time (such as "opened" list windows).
+    - You can now change the name of the depot from the default "depot". This is
+      useful if you have multiple depots in your system.
+    - I have extended the command syntax with the
+      |perforce-command-mode-specifier| for the more demanding users.
+    - The "change" command now takes perforce filename patterns to filter out
+      the initial list of files that should be included in the change. The
+      "submit" command already does this. See |perforce-changelist|.
+    - You can now press "q" to quit in read-only perforce windows (David
+      Fishburn).
+    - Improved the handling of back-slashes and spaces in filenames.
+    - The vim built-in :w and :wq commands work exactly like the :W and :WQ
+      commands respectively. For "submit", they now prompt for confirmation,
+      which can be suppressed by passing a "-y" option.
+    - Now the specification windows are regular buffers. For reasons on why this
+      is much better than the earlier approach, read towards the end of
+      |perforce-forms|.
+    - The plugin now uses try/finally blocks to avoid leaking any changes done
+      to global settings in case of unexpected errors.
+    - The submit command now detects partial errors that results in an
+      automatic changelist creation, and modify the template appropriately. This
+      also works while creating a new changelist, so that after saving the
+      changelist, you can just undo and continue making further changes.
+      See |perforce-submit|.
+    - Hyperlink the diff window to the source. Pressing O or <CR> on a line
+      takes you to the source line. See |perforce-diff-mode|.
+    - Fixed some bugs with passing special characters and whitespace to shell.
+    - A dummy option "-dd" to mean the default diff format. See
+      |perforce-default-diff-format|
+    - A new ftplugin for perforce forms which can be made to work even while
+      starting forms from p4 directly. See |perforce-ftplugin|
+    - This help file itself is new.
+
+                                            *perforce-todo*
+                                            *perforce-known-issues*
+Not in any particular order:
+    - PChangesDescribeCurrentItem doesn't work for pending changelists from
+      other clients.
+    - A deleted file is showing up as unopened.
+    - Consider #none also as revision in the syntax highlighting.
+    - I don't think the p4Depot setting is being handled correctly. We need to
+      investigate how depots created using "p4 depot" are used.
+    - There is a Syntax highlighting for: "<file>#1 - added as <file>". How is
+      this message generated?
+    - When you open a spec window with perforce wildcard patterns as arguments
+      (such as "P4 submit .../*") saving works as long as you don't change the
+      current working directory of Vim after creating the spec window, otherwise
+      because of some weird behavior in Vim, you get an E212.
+    - You can't login using the plugin.
+    - <pfitem> should be handled at a higher level for it to be more useful.
+    - During the auto-checkout, Vim will give error E788 if the ftplugin uses
+      :compiler command. This is a known issue with Vim7.0 release, and might
+      get fixed in a future patch.
+    - When PFSwitch fails because the perforce server is not up, you are forced
+      to switch to another setting and come back to it to select it again.
+    - When reverting files from files view, if you select No for the prompt, it
+      still refreshes the window.
+
+                                            *perforce-wishlist*
+Here is a list of changes that I think will be useful to do, in no particular
+order:
+    - PChangeSubmit etc. should have menu entries in change menu.
+    - Sort change lists and show those that are by the current client and
+      others separately.
+    - The "nmap <buffer>" output can probably be used to generate a simple help.
+    - A command to rename files (Raj). Invoke integrate and delete internally,
+      for convenience.
+    - It will be nice if the change number is automatically remembered.
+      Also, we should be able to set a change number which should be
+      automatically applied to all the edits, deletes etc.
+    - How can we support interactive resolves? Will it be worth doing it?
+    - In filelist view, allow visual select on the files to be operated upon
+      (for revert etc.).
+    - I should be able to parse the output of p4 resolve using this command:
+        sh -c "while true; do echo s ;done" | p4 resolve
+    - How can we avoid prompting for checkout when the current vim session is
+      in view mode (-R option)? For now just use PFToggleCkOut command in such
+      sessions.
+    - The script now has knowledge of client-view settings. There may still be
+      places that assume that the branch name and local directory name are same.
+    - The menus can be further improved.
+    - The list specific menus should be disabled unless you are in that
+      window.
+    - Backup/Restore commands for opened files will be useful. For now, just use
+      the included shell scripts.
+    - A simple p4win style explorer will be helpful for quickly browsing the
+      depot.
+    - Negative revisions for dates also? Is it possible using vim functions?
+    - Check for unsaved buffers during submit. Requires us to look into the
+      buffer list.
+    - There should be an option to show/hide deleted files in the filelists
+      (PFiles).
+    - The PPasswd can't be used when either the old or new password is empty.
+    - There should be an option to have a single perforce-log window where all
+      the output gets appended, instead of opening new windows for each command.
+    - We should be able to pass file patterns to be used as the View while
+      creating a new label command.
+    - We should have a feature to diff2 in labels and changes views when started
+      with a filename as an argument. Also diff from changes view.
+    - A command to refresh only the files that are in a label. Works well for
+      lables containing only a partial list of files.
+    - Supporting backquotes could be useful when the shell is unixy.
+    - In list views (and filelog view), we should be able to map the mouse
+      presses such a way that single mouse click results in selecting the entire
+      line and mouse drag automatically selects the entire line. We will then
+      need to make sure all the list view local commands will work with visual
+      mode.
+    - If the current buffer is a directory (i.e. opened with the vim
+      filebrowser), it would be nice if the plugin could simply append '...'
+      to the relative path, so something like PSync would make perforce sync
+      all files under the current directory (Mark). In otherwords, it would make
+      sense to append '...' to the directory name whenever a command that
+      defaults to current buffer is executed on a directory buffer.
+    - How about using v:dying to determine if the vim session is crashing and
+      preserve the spec buffers?
+    - Executing "admin" command shouldn't open up a new window.
+    - Better support for "monitor" command.
+    - There should be an option to position the current line in the diff (if
+      exists) (like view current line in the diff).
+    - While viewing a directory, it will be nice if the file completion
+      happens relative to the directory that is being viewed, rather than the
+      working directory of Vim. We probably should have an option to turn it
+      off.
+    - It is possible to have a command that starts p4win with options such as
+      -s.
+    - vdiff should print the have version not head version, unless this
+      information is for some reason not available.
+    - While doing custom expansion for files (:PF command), the spaces in the
+      filenames should be escaped.
+    - For diff hyper linking, there should be a way to always open the depot
+      file (even for the context lines).
+    - In DiffLink, detect the column position also. Also add a way to jump the
+      diff region containing the current line.
+    - There should be a way to force deletion from the users list (-f option).
+    - Need a "blame" built-in command to produce annotated output with change
+      numbers in them.
+
+                                            *perforce-bugreporting*
+Please read the entire plugin online help before you report any bugs, as it will
+help clear up scenarios which may not indeed be a bug. In addition,
+    - Refer the p4 user manual and online help to be clear about the right
+      behavior.
+    - Check if it is already mentioned in the |perforce-limitations| or
+      |perforce-known-issues| sections.
+    - Run :PFBugReport command to generate "perforcebugrep.txt" file in the
+      current directory and attach that with your email. Before sending this
+      out, check to make sure it doesn't contain any confidential information!
+
+Please send your reports to hari_vim at yahoo dot com.
+
+                                            *perforce-acknowledgements*
+- Tom Slee (tslee at ianywhere dot com) for his idea of creating a status
+  bar with the p4 fstat information (see
+  http://www.vim.org/script.php?script_id=167).
+- Leo L. Schwab (leo dot schwab at openwave dot com) for reporting problems and
+  helping me with debugging them.
+- David Fishburn (fishburn at ianywhere dot com) for his idea with mapping the
+  'q' key to quit non-editable perforce windows, and reporting various bugs.
+- Mark Brophy (mbrophy at esmertec dot com) for his ideas with using P4CONFIG
+  and others that allow dynamic configuration.
+- Various others for sending bugs, patches, ideas and feedback. Some of them
+  are:
+    - Denis Perelyubskiy (denisp at CS dot UCLA dot EDU)
+    - Kevin McCarthy (tunacat at yahoo dot com)
+    - Paul Wright (paul at noctua dot org dot uk)
+    - Reva Revadigar (reva dot revadigar at autodesk dot com)
+    - Peter Hutkins (PHUTKINS at altera dot com)
+    - Brett Humphreys (bretth at aiinet dot com)
+    - Craig Emery (craig dot emery at ntlworld dot com)
+
+ vim6:tw=80:ts=8:ft=help:ai:sw=4:et
diff --git a/.vim/ftplugin/perforce.vim b/.vim/ftplugin/perforce.vim
new file mode 100755 (executable)
index 0000000..e3bf7d4
--- /dev/null
@@ -0,0 +1,59 @@
+" Perforce spec filetype plugin file
+" Language:      Perforce Spec File
+" Maintainer:    Hari Krishna Dara <hari_vim at yahoo dot com>
+" Last Change:   13-Jan-2006 @ 17:38
+" Since Version:  1.4
+" Revision:      1.0.6
+" Plugin Version: 2.1
+" Download From:
+"     http://vim.sourceforge.net/scripts/script.php?script_id=240
+" TODO:
+
+" Only do this when not done yet for this buffer
+"if exists("b:did_ftplugin")
+"  finish
+"endif
+
+" Don't load another plugin for this buffer
+let b:did_ftplugin = 1
+
+" Set some options suitable for pure text editing.
+setlocal tabstop=8
+setlocal softtabstop=0
+setlocal shiftwidth=8
+setlocal noexpandtab
+setlocal autoindent
+setlocal formatoptions=tcqnl
+setlocal comments=:#,fb:-
+setlocal wrapmargin=0
+setlocal textwidth=80
+let b:undo_ftplugin = 'setl ts< sts< sw< et< ai< fo< com< wm< tw<'
+
+if !exists("loaded_perforce_ftplugin")
+let s:patterns{'Change'}   = '\%(^Description:\s*\_s\?\s*\)\zs\S\|^Description:'
+let s:patterns{'Branch'}   = '\%(^View:\s*\_s\?\s*\)\zs\S\|^View:'
+let s:patterns{'Label'}    = '\%(^View:\s*\_s\?\s*\)\zs\S\|^View:'
+let s:patterns{'Client'}   = '\%(^View:\s*\_s\?\s*\)\zs\S\|^View:'
+let s:patterns{'Job'}      = '\%(^Job:\s\+\)\@<=new\>\|\%(^Description:\s*\_s\?\s*\)\zs\S\|^Description:'
+let s:patterns{'Job_Spec'} = '^Fields:'
+let s:patterns{'User'}     = '^User:'
+let s:patterns{'Depot'}    = '\%(^Description:\s*\_s\?\s*\)\zs\S\|^Description:'
+let s:patterns{'Group'}    = '\%(^Users:\s*\_s\?\s*\)\zs\S\|^Users:'
+" Position cursor on the most appropriate line based on the type of spec being
+" edited.
+function! s:PositionLine()
+  let specPattern = '^# A Perforce \(.*\) Specification.$'
+  if getline(1) =~ specPattern
+    let spec = substitute(substitute(getline(1), specPattern, '\1', ''), ' ',
+         \ '_', 'g')
+    if spec != "" && exists('s:patterns'. spec) &&
+         \ search(s:patterns{spec}, 'w') != 0
+      let b:p4Pattern = s:patterns{spec}
+      normal! zz
+    endif
+  endif
+endfunction
+let loaded_perforce_ftplugin=1
+endif
+
+call s:PositionLine()
diff --git a/.vim/ftplugin/selectbuf_perforce.vim b/.vim/ftplugin/selectbuf_perforce.vim
new file mode 100755 (executable)
index 0000000..0836aa0
--- /dev/null
@@ -0,0 +1,15 @@
+
+if !exists('loaded_selectbuf') || loaded_selectbuf < 303
+  finish
+endif
+
+noremap <Leader>pfa :SBExec PF add<CR>
+noremap <Leader>pfg :SBExec PF sync<CR>
+noremap <Leader>pfe :SBExec PF edit<CR>
+noremap <Leader>pft :SBExec PF delete<CR>
+noremap <Leader>pfr :SBExec PF revert<CR>
+noremap <Leader>pfs :SBExec PF submit<CR>
+noremap <Leader>pfl :SBExec PF lock<CR>
+noremap <Leader>pfu :SBExec PF unlock<CR>
+noremap <Leader>pfd :SBExec PF diff<CR>
+noremap <Leader>pf2 :SBExec PF diff2<CR>
diff --git a/.vim/perforce/bakup.sh b/.vim/perforce/bakup.sh
new file mode 100755 (executable)
index 0000000..4b459c3
--- /dev/null
@@ -0,0 +1,396 @@
+#!/bin/bash\r
+# Author: Hari Krishna Dara ( hari_vim at yahoo dot com ) \r
+# Last Change: 17-Mar-2004 @ 18:47\r
+# Requires:\r
+#   - bash or ksh (tested on cygwin and MKS respectively).\r
+#   - Info Zip for the -z option to work (comes with linux/cygwin). Download for\r
+#     free from:\r
+#       http://www.info-zip.org/\r
+#   - GNU tar for the -a option to work (comes with linux/cygwin).\r
+# Version: 1.4.2\r
+# Licence: This program is free software; you can redistribute it and/or\r
+#          modify it under the terms of the GNU General Public License.\r
+#          See http://www.gnu.org/copyleft/gpl.txt \r
+# Description:\r
+#   Shell script to take backup of all the open files in perforce SCM.\r
+#\r
+#TODO:\r
+#   - Zip comment option -zz is no longer working?\r
+#   - To create cpio archives, I can use "cpio -o -H | gzip" command.\r
+#   - Option to generate a diff at the end.\r
+#   - Option to restrict by type (useful to avoid huge binary files).\r
+#   - Support to run zip from a different directory (to avoid certain path\r
+#     component from getting into zipfile).\r
+\r
+usage()\r
+{\r
+cat <<END\r
+$0 [<backup dir>] [<source dir>] ...\r
+$0 -h -n -q -v -z -zz [-c <change number>] [-t <backup dir>] [-r <backup root>] [[-s <source dir>] ...]\r
+    -t specify full path name to the target directory. If -z is specified this\r
+       becomes the path to the zip file name.\r
+    -r set the root for creating the default backup dir/zip file. Not used if\r
+       "backup dir" is specified. You can also set BAKUP_ROOT environmental\r
+       variable. Defaults to p: (p drive), because that is what it is on my\r
+       system :).\r
+    -i incremental backup, which makes the program use the previous backup\r
+       directory/archive for that day (if it exists) instead of creating a new\r
+       one. For tar (with "-a" option), you will have to provide one of the\r
+       relevant options to force reuse an existing archive ("-ar" e.g.) or the\r
+       existing archive will simply get overwritten. However this will not work\r
+       with compressed tar archives. You can also specify any existing backup\r
+       directory/archive path explicitly by using the "-t" option.\r
+    -a create a tar file instead of copying the files. You can specify the\r
+       tar file name using -t option.\r
+    -a[-|--]<taropt>\r
+       Pass -taropt to tar command.\r
+       Ex:\r
+        - Pass "-aC <dir>" or "-adirectory=<dir>" to cd to a directory before\r
+          creating the tar file (thus dropping file component).\r
+        - Pass "-az" to gzip the created tar file.\r
+    -z create a zip file instead of copying the files. You can specify the\r
+       zip file name using -t option.\r
+    -z[-]<zipopt>\r
+       Pass -zipopt to zip command.\r
+       Ex: Pass -zz for a prompt from zip to enter a comment.\r
+    -s limit to open files matching the given wildcard (local or depot). This\r
+       can be repeated multiple times to specify multiple source directories.\r
+       You can pass in anything that the 'p4 opened' command itself accepts.\r
+    -c limit the files to those specified in the change number, in addition to\r
+       the -s option. This can't be repeated multiple times though.\r
+    -n don't execute any commands, just show what is going to be done. Does not\r
+       currently work with -z option.\r
+    -q quite mode, no messages\r
+    -v verbose mode, print messages (the default).\r
+    -h print help message (this message) and exit\r
+  The first unspecified directory is treated as target directory, and the\r
+  remaining directories are treated as source directories. The '-n' option can\r
+  be used to generate a batch script that can be run later. The source\r
+  directory can be in depot or local format (NO BACKSLASHES PLEASE). Do not\r
+  combine multiple options into one argument.\r
+Examples:\r
+     bakup.sh\r
+         - Backup all the open files into a default generated backup dir. in p:\r
+           drive (p: driver is the default backup directory).\r
+     bakup.sh mybak c:/dev/branch/src\r
+         - Backup open files only under 'c:/dev/branch/src' into 'mybak'\r
+           directory.\r
+     bakup.sh -s //depot/branch/src -s //depot/branch/cfg\r
+         - Backup open files only under 'src' and 'cfg' into the default bakup\r
+           dir.\r
+\r
+     You could add -z option to all the above usages to create a zip file\r
+     instead of creating a directory. You need to have Info-Zip installed in the\r
+     path for this to work.\r
+\r
+     bakup.sh -n > mybackup.bat\r
+         - Generates a 'mybackup.bat' batch file that can be run at a later\r
+           time to take a backup. The files to be backed up are based on the\r
+           time the script was generated, so it should be regenerated if the\r
+           list has changed since then.\r
+END\r
+exit 1\r
+}\r
+\r
+generateTargetDirName()\r
+{\r
+    today=`date +"%d-%b-%Y"`\r
+    inc=1\r
+    #echo "---first time"\r
+    tDir="$targetRoot/bakup-$today"\r
+    prevDir=$tDir\r
+    while [ -d $tDir -o -f $tDir.* ]; do\r
+        inc=$[inc + 1]\r
+        prevDir=$tDir\r
+        tDir="$targetRoot/bakup-${today}_$inc"\r
+        #echo "---subsequent time inc=$inc tDir=$tDir"\r
+    done\r
+    if [ $incrementalMode -ne 0 ]; then\r
+        tDir=$prevDir # Backup one level to use an existing db.\r
+    fi\r
+    echo "$tDir"\r
+}\r
+\r
+getExtOpt()\r
+{\r
+    archiveOpt=${1/[-+]$2/}\r
+    case $archiveOpt in\r
+    --*)\r
+        ;;\r
+    -??*) # Mistyped long option\r
+        archiveOpt="-$archiveOpt"\r
+        ;;\r
+    -*)\r
+        ;;\r
+    ??*) # Long option.\r
+        archiveOpt="--$archiveOpt"\r
+        ;;\r
+    *)\r
+        archiveOpt="-$archiveOpt"\r
+        ;;\r
+    esac\r
+    echo $archiveOpt\r
+}\r
+\r
+#getExtOpt '-a--directory' 'a'\r
+#getExtOpt '-a-directory' 'a'\r
+#getExtOpt '-adirectory' 'a'\r
+#getExtOpt '-aC' 'a'\r
+#getExtOpt '-a-C' 'a'\r
+#exit\r
+\r
+checkOptArg='\r
+    shift;\r
+    if [ $# -eq 0 ]; then\r
+        usage;\r
+    fi\r
+'\r
+\r
+testMode=0\r
+archiveOpts=''\r
+chDirectory='' # If set, the listing is generated relative to this dir.\r
+archiveMode=0\r
+verboseMode=1\r
+targetDir=""\r
+targetRoot=""\r
+sourceDirs=""\r
+changeNumber=""\r
+compressedTar=0\r
+incrementalMode=0\r
+until [ $# -eq 0 ]; do\r
+    case $1 in\r
+    -h|-help|--help)\r
+        usage\r
+        ;;\r
+    -v)\r
+        verboseMode=1\r
+        ;;\r
+    -q)\r
+        verboseMode=0\r
+        ;;\r
+    -i)\r
+        incrementalMode=1\r
+        ;;\r
+    -a)\r
+        archiveMode=2 # Tar.\r
+        verboseMode=0 # Turn on quite mode, as zip will anyway show the files.\r
+        testMode=1 # Turn on test mode, so we won't copy files.\r
+        ;;\r
+    -a*)\r
+        # Need to take care of options with optional args.\r
+        extOpt=`getExtOpt $1 a`\r
+        if [ $extOpt = -z -o $extOpt = -Z -o $extOpt = -j ]; then\r
+            compressedTar=1\r
+            tarExt=`echo $extOpt | awk 'BEGIN{ext["-j"]="bz2";ext["-z"]="gz";ext["-Z"]="Z";}{print ext[$0];}'`\r
+        fi\r
+        archiveOpts="${archiveOpts} $extOpt"\r
+        case $extOpt in\r
+        --directory)\r
+            chDirectory=${extOpt/*=}\r
+            ;;\r
+        -C)\r
+            eval $checkOptArg\r
+            chDirectory=$1\r
+            #echo "---setting chDirectory=$chDirectory"\r
+            archiveOpts="${archiveOpts} $chDirectory"\r
+            ;;\r
+        esac\r
+        ;;\r
+    -z)\r
+        archiveMode=1 # Zip.\r
+        verboseMode=0 # Turn on quite mode, as zip will anyway show the files.\r
+        testMode=1 # Turn on test mode, so we won't copy files.\r
+        ;;\r
+    -z*)\r
+        # Need to take care of options with optional args.\r
+        archiveOpts="${archiveOpts} `getExtOpt $1 z`"\r
+        ;;\r
+    -n)\r
+        testMode=1\r
+        verboseMode=0\r
+        ;;\r
+    -c)\r
+        eval $checkOptArg\r
+        changeNumber=$1\r
+        ;;\r
+    -t)\r
+        eval $checkOptArg\r
+        targetDir=$1\r
+        ;;\r
+    -r)\r
+        eval $checkOptArg\r
+        targetRoot=$1\r
+        ;;\r
+    -s)\r
+        eval $checkOptArg\r
+        sourceDirs="$sourceDirs $1"\r
+        #echo "---setting sourceDirs=$sourceDirs"\r
+        ;;\r
+    -?)\r
+        usage\r
+        ;;\r
+    *)\r
+        if [ "$targetDir" = "" ]; then\r
+            #echo "---setting targetDir=$targetDir"\r
+            targetDir=$1\r
+        else\r
+            #echo "---appending sourceDirs=$1"\r
+            sourceDirs="$sourceDirs $1"\r
+        fi\r
+        ;;\r
+    esac\r
+    shift\r
+done\r
+\r
+# For tar, we can add -a option only if no other equivalent option is specified.\r
+if [ $archiveMode -eq 2 ]; then\r
+    case $archiveOpts in\r
+    *-[Acdtrux]*)\r
+        ;;\r
+    *)\r
+        archiveOpts="$archiveOpts -c"\r
+        ;;\r
+    esac\r
+fi\r
+\r
+if [ x${targetDir}x = xx -a x${targetRoot}x = xx ]; then\r
+    targetRoot=$BAKUP_ROOT\r
+    if [ "$targetRoot" = "" ]; then\r
+        targetRoot="p:"\r
+    fi\r
+fi\r
+\r
+if [ "$targetDir" = "" ]; then\r
+    targetDir=`generateTargetDirName`\r
+fi\r
+\r
+if [ "$sourceDirs" = "" ]; then\r
+    # By default backup all the open files.\r
+    sourceDirs="//..."\r
+fi\r
+\r
+\r
+# Create a dir if it doesn't exist, exit on error.\r
+createDir()\r
+{\r
+    if ! [ -d "$1" ]; then\r
+\r
+        if [ $testMode -eq 1 -o $verboseMode -eq 1 ]; then\r
+            echo "mkdir -p $1" 1>&4\r
+        fi\r
+\r
+        if [ $testMode -eq 0 ]; then\r
+            mkdir -p "$1"\r
+            if [ $? -ne 0 ]; then\r
+                echo "Error creating $1" 1>&2\r
+                exit 1\r
+            fi\r
+        fi\r
+    fi\r
+}\r
+\r
+\r
+#if [ $testMode -eq 1 ]; then\r
+#    echo "Running in test mode"\r
+#fi\r
+\r
+if [ $archiveMode -eq 0 ]; then\r
+    createDir $targetDir 4>&1\r
+fi\r
+\r
+if [ $verboseMode -eq 1 ]; then\r
+    echo "Copying to target directory: $targetDir"\r
+fi\r
+\r
+# Testing for $BASH will not work, if you happen to use the cygwin sh instead of\r
+#   bash.\r
+unset PWD\r
+codelineRoot=`p4 info | sed -n -e 's;\\\\;/;g' -e 's/Client root: //p'`\r
+#echo "---codelineRoot=$codelineRoot"\r
+rootDirLength=${#codelineRoot}\r
+\r
+if [ $archiveMode -eq 1 ]; then\r
+    fileExt=''\r
+    if [ ${targetDir%.zip} = $targetDir ]; then\r
+        fileExt='.zip'\r
+    fi\r
+    pipeCmd="zip ${archiveOpts} -@ ${targetDir}${fileExt}"\r
+    echo "Using: '${pipeCmd}' to create zip archive"\r
+elif [ $archiveMode -eq 2 ]; then\r
+    fileExt=''\r
+    if [ $compressedTar -eq 1 -a ${targetDir%.tar.$tarExt} = $targetDir ]; then\r
+        fileExt=".tar.$tarExt"\r
+    elif [ $compressedTar -ne 1 -a ${targetDir%.tar} = $targetDir ]; then\r
+        fileExt='.tar'\r
+    fi\r
+    pipeCmd="tar -vf ${targetDir}${fileExt} ${archiveOpts} -T -"\r
+    echo "Using: '${pipeCmd}' to create tar archive"\r
+else\r
+    pipeCmd="cat"\r
+fi\r
+\r
+exec 4>&1; {\r
+    for sourceDir in $sourceDirs; do\r
+        if [ ! -f $sourceDir ]; then\r
+            case $sourceDir in\r
+                *...)\r
+                    ;;\r
+                */)\r
+                    sourceDir="${sourceDir}..."\r
+                    ;;\r
+                *)\r
+                    sourceDir="${sourceDir}/..."\r
+                    ;;\r
+            esac\r
+        fi\r
+        if [ "$changeNumber" = "" ]; then\r
+            openedCmd="p4 opened $sourceDir"\r
+        else\r
+            openedCmd="p4 opened -c $changeNumber $sourceDir"\r
+        fi\r
+        if [ $verboseMode -eq 1 ]; then\r
+            echo "Collecing list of open files using: $openedCmd" 1>&4\r
+        fi\r
+\r
+        # FIXME: I couldn't get it working with the following IFS, don't know\r
+        # why. So as a temp. work-around, I am temporarily substituting spaces\r
+        # with '|' in sed and converting them back to spaces in the start of the\r
+        # loop.\r
+        #IFS="\n\r"\r
+        openedFiles=`$openedCmd | \\r
+                     sed -e '/ - delete \(default \)\?change /d' \\r
+                         -e 's/#.*//' |\r
+                     p4 -x - where | \\r
+                     sed -e 's;.\+/[^/]\+/\([^ /]\+\) //.*\1 \(.*\1\);\2;' \\r
+                         -e "s;^${chDirectory}/*;;" \\r
+                         -e 's/ /|/g' \\r
+                         -e 's;\\\\;/;g'`\r
+        for file in `echo $openedFiles`; do\r
+            file=${file//\|/ }\r
+            #echo "---file = $file" 1>&4\r
+            dir=`dirname "$file"`\r
+            # Relative to the codeline root.\r
+            tgtDir="${targetDir}/${dir:$rootDirLength}"\r
+            #echo "---tgtDir = $tgtDir" 1>&4\r
+\r
+            if [ $archiveMode -eq 0 ]; then\r
+                createDir "$tgtDir"\r
+            fi\r
+\r
+            if [ $archiveMode -ne 0 ]; then\r
+                echo $file 1>&3\r
+            elif [ $testMode -eq 1 -o $verboseMode -eq 1 ]; then\r
+                echo "cp \"$file\" \"$tgtDir\"" 1>&4\r
+            fi\r
+            if [ $testMode -eq 0 ]; then\r
+                cp "$file" "$tgtDir"\r
+                if [ $? -ne 0 ]; then\r
+                    echo "Error copying $1" 1>&2\r
+                    exit 1\r
+                fi\r
+            fi\r
+        done\r
+    done\r
+} 3>&1 | $pipeCmd\r
+\r
+# vim6: et sw=4\r
diff --git a/.vim/perforce/p4Utils.sh b/.vim/perforce/p4Utils.sh
new file mode 100755 (executable)
index 0000000..bcc92c6
--- /dev/null
@@ -0,0 +1,24 @@
+# Get the list of unopened files under the given $1/$2 directory.
+#   It should be possible to implement the logic by using temp files and diff,
+#   but this was more challenging to me :)
+# Ex:
+#   unOpenedFiles=`getUnOpenedFiles "$root" "src"`
+getUnOpenedFiles() {
+    path=$1
+    dir=$2
+    p4 opened $path/$dir/... \
+        | sed -e 's/.*'$dir'/'$dir'/' -e 's/#.*$//' \
+        | perl -n -e '
+          print;
+          END
+          {
+              # Merge the opened list with all the files under BaseClasses.
+              foreach $f (`find '$path/$dir' -type f | sed -e "s/.*'$dir'/'$dir'/"`)
+              {
+                  print $f;
+              }
+          }' \
+        | sort -f \
+        | uniq -c \
+        | sed -n -e 's/\s*1\s*//p'
+}
diff --git a/.vim/perforce/perforcebugrep.vim b/.vim/perforce/perforcebugrep.vim
new file mode 100755 (executable)
index 0000000..1bf1894
--- /dev/null
@@ -0,0 +1,97 @@
+" perforcebugrep.vim: Generate perforcebugrep.txt for perforce plugin.
+" Author: Hari Krishna (hari_vim at yahoo dot com)
+" Last Change: 29-Aug-2006 @ 17:57
+" Created:     07-Nov-2003
+" Requires:    Vim-7.0, perforce.vim(4.0)
+" Version:     2.1.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 
+
+if !exists("loaded_perforce")
+  runtime plugin/perforce.vim
+endif
+if !exists("loaded_perforce") || loaded_perforce < 400
+  echomsg "perforcebugrep: You need a newer version of perforce.vim plugin"
+  finish
+endif
+
+" Make sure line-continuations won't cause any problem. This will be restored
+"   at the end
+let s:save_cpo = &cpo
+set cpo&vim
+
+" Based on $VIM/bugreport.vim
+let _more = &more
+try
+  PFInitialize " Make sure it is autoloaded.
+
+  set nomore
+  call delete('perforcebugrep.txt')
+  if has("unix")
+    !echo "uname -a" >perforcebugrep.txt
+    !uname -a >>perforcebugrep.txt
+  endif
+
+  redir >>perforcebugrep.txt
+  version
+
+  echo "Perforce plugin version: " . loaded_perforce
+  echo "Genutils plugin version: " . loaded_genutils
+
+  echo "--- Perforce Plugin Settings ---"
+  for nextSetting in perforce#PFGet('s:settings')
+    let value = perforce#PFCall('s:_', nextSetting)
+    echo nextSetting.': '.value
+  endfor
+  echo "s:p4Contexts: " . string(perforce#PFCall('s:_', 'Contexts'))
+  echo "g:p4CurDirExpr: " . perforce#PFCall('s:_', 'CurDirExpr')
+  echo "g:p4CurPresetExpr: " . perforce#PFCall('s:_', 'CurPresetExpr')
+  echo "s:p4Client: " . perforce#PFCall('s:_', 'Client')
+  echo "s:p4User: " . perforce#PFCall('s:_', 'User')
+  echo "s:p4Port: " . perforce#PFCall('s:_', 'Port')
+
+  echo "--- Current Buffer ---"
+  echo "Current buffer: " . expand('%')
+  echo "Current directory: " . getcwd()
+  let tempDir = perforce#PFCall('s:_', 'TempDir')
+  if isdirectory(tempDir)
+    echo 'temp directory "' . tempDir . '" exists'
+  else
+    echo 'temp directory "' . tempDir . '" does NOT exist'
+  endif
+  if exists('b:p4OrgFileName')
+    echo 'b:p4OrgFileName: ' . b:p4OrgFileName
+  endif
+  if exists('b:p4Command')
+    echo 'b:p4Command: ' . b:p4Command
+  endif
+  if exists('b:p4Options')
+    echo 'b:p4Options: ' . b:p4Options
+  endif
+  if exists('b:p4FullCmd')
+    echo 'b:p4FullCmd: '. b:p4FullCmd
+  endif
+  if exists('g:p4FullCmd')
+    echo 'g:p4FullCmd: '. g:p4FullCmd
+  endif
+  setlocal
+
+  echo "--- p4 info ---"
+  let info = perforce#PFCall('perforce#PFIF', '1', '4', 'info')
+  " The above resets redir.
+  redir >>perforcebugrep.txt
+  echo info
+
+  set all
+finally
+  redir END
+  let &more = _more
+  sp perforcebugrep.txt
+endtry
+
+" Restore cpo.
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim6:fdm=marker et sw=2
diff --git a/.vim/perforce/perforcemenu.vim b/.vim/perforce/perforcemenu.vim
new file mode 100755 (executable)
index 0000000..6d0be2b
--- /dev/null
@@ -0,0 +1,335 @@
+" perforcemenu.vim: Create a menu for perforce plugin.
+" Author: Hari Krishna (hari_vim at yahoo dot com)
+" Last Change: 28-Aug-2006 @ 22:50
+" Created:     07-Nov-2003
+" Requires:    Vim-6.2, perforce.vim(4.0)
+" Version:     2.1.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 
+
+if !exists("loaded_perforce")
+  runtime plugin/perforce.vim
+endif
+if !exists("loaded_perforce") || loaded_perforce < 400
+  echomsg "perforcemenu: You need a newer version of perforce.vim plugin"
+  finish
+endif
+
+" Make sure line-continuations won't cause any problem. This will be restored
+"   at the end
+let s:save_cpo = &cpo
+set cpo&vim
+
+function! s:Get(setting, ...)
+  if exists('g:p4'.a:setting)
+    return g:p4{a:setting}
+  endif
+
+  if exists('*perforce#PFCall')
+    let val = perforce#PFCall('s:_', a:setting)
+    if val == '' && a:0 > 0
+      let val = a:1
+    endif
+    return val
+  endif
+  return 0
+endfunction
+
+function! s:PFExecCmd(cmd) " {{{
+  if exists(':'.a:cmd) == 2
+    exec a:cmd
+  else
+    call perforce#PFCall("s:EchoMessage", 'The command: ' . a:cmd .
+         \ ' is not defined for this buffer.', 'WarningMsg')
+  endif
+endfunction
+command! -nargs=1 PFExecCmd :call <SID>PFExecCmd(<q-args>) " }}}
+let s:loaded_perforcemenu = 1
+
+" CreateMenu {{{
+if s:Get('EnableMenu') || s:Get('EnablePopupMenu') " [-2f]
+function! s:CreateMenu(sub, expanded)
+  if ! a:expanded
+    let fileGroup = '.'
+  else
+    let fileGroup = '.&File.'
+  endif
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . '&Add :PAdd<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . 'S&ync :PSync<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . '&Edit :PEdit<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . '-Sep1- :'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup .
+        \ '&Delete :PDelete<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . '&Revert :PRevert<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . '-Sep2- :'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . 'Loc&k :PLock<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup .
+        \ 'U&nlock :PUnlock<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . '-Sep3- :'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . '&Diff :PDiff<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . 'Diff&2 :PDiff2<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup .
+        \ 'Revision\ &History :PFilelog<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . 'Propert&ies ' .
+        \ ':PFstat -C<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . '&Print :PPrint<CR>'
+  exec 'amenu <silent> ' . a:sub . '&Perforce' . fileGroup . '-Sep4- :'
+  if a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&File.' .
+          \ 'Resol&ve.Accept\ &Their\ Changes<Tab>resolve\ -at ' .
+          \ ':PResolve -at<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&File.' .
+          \ 'Resol&ve.Accept\ &Your\ Changes<Tab>resolve\ -ay :PResolve -ay<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&File.' .
+          \ 'Resol&ve.&Automatic\ Resolve<Tab>resolve\ -am :PResolve -am<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&File.' .
+          \ 'Resol&ve.&Safe\ Resolve<Tab>resolve\ -as :PResolve -as<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&File.' .
+          \ 'Resol&ve.&Force\ Resolve<Tab>resolve\ -af :PResolve -af<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&File.' .
+          \ 'Resol&ve.S&how\ Integrations<Tab>resolve\ -n :PResolve -n<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&File.-Sep5- :'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&File.Sa&ve\ Current\ Spec ' .
+         \':PFExecCmd W<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&File.Save\ and\ &Quit\ ' .
+         \'Current\ Spec :PFExecCmd WQ<CR>'
+  endif
+
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Opened\ Files :POpened<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Refresh\ Active\ Pane ' .
+          \ ':PRefreshActivePane<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.-Sep6- :'
+  else
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&View.&BranchSpecs :PBranches<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&View.&Changelist.' .
+          \ '&Pending\ Changelists :PChanges -s pending<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&View.&Changelist.' .
+          \ '&Submitted\ Changelists :PChanges -s submitted<CR>'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&View.Cl&ientSpecs :PClients<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&View.&Jobs :PJobs<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&View.&Labels :PLabels<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&View.&Users :PUsers<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&View.&Depots :PDepots<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&View.&Opened\ Files :POpened<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&View.&Refresh\ Active\ Pane ' .
+          \ ':PRefreshActivePane<CR>'
+  endif
+
+  if a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Settings.' .
+          \ '&Switch\ Port\ Client\ User '.
+          \ ':call perforce#PFCall("s:SwitchPortClientUser")<CR>'
+    let p4Presets = split(perforce#PFGet('s:p4Presets'), ',')
+    if len(p4Presets) > 0
+      let index = 0
+      while index < len(p4Presets)
+        exec 'amenu <silent>' a:sub.'&Perforce.&Settings.&'.index.'\ '
+              \ .escape(p4Presets[index], ' .') ':PFSwitch' index.'<CR>'
+        let index = index + 1
+      endwhile
+    endif
+  endif
+
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.New\ &Submission\ Template :PSubmit<CR>'
+  else
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Changelist.&New :PChange<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Changelist.' .
+          \ '&Edit\ Current\ Changelist :PFExecCmd PItemOpen<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Changelist.' .
+          \ 'Descri&be\ Current\ Changelist :PFExecCmd PItemDescribe<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Changelist.' .
+          \ '&Delete\ Current\ Changelist :PFExecCmd PItemDelete<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Changelist.' .
+          \ 'New\ &Submission\ Template :PSubmit<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Changelist.-Sep- :'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Changelist.' .
+          \ 'View\ &Pending\ Changelists :PChanges -s pending<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Changelist.' .
+          \ '&View\ Submitted\ Changelists :PChanges -s submitted<CR>'
+  endif
+
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Branch :PBranch<CR>'
+  else
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Branch.&New :PBranch<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Branch.' .
+          \ '&Edit\ Current\ BranchSpec :PFExecCmd PItemOpen<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Branch.' .
+          \ 'Descri&be\ Current\ BranchSpec :PFExecCmd PItemDescribe<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Branch.' .
+          \ '&Delete\ Current\ BranchSpec :PFExecCmd PItemDelete<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Branch.-Sep- :'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&Branch.&View\ BranchSpecs :PBranches<CR>'
+  endif
+
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Label :PLabel<CR>'
+  else
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Label.&New :PLabel<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Label.' .
+          \ '&Edit\ Current\ LabelSpec :PFExecCmd PItemOpen<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Label.' .
+          \ 'Descri&be\ Current\ LabelSpec :PFExecCmd PItemDescribe<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Label.' .
+          \ '&Delete\ Current\ LabelSpec :PFExecCmd PItemDelete<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Label.-Sep1- :'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Label.' .
+          \ '&Sync\ Client\ ' . s:Get('Client') . '\ to\ Current\ Label ' .
+          \ ':PFExecCmd PLabelsSyncClient<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Label.' .
+          \ '&Replace\ Files\ in\ Current\ Label\ with\ Client\ ' .
+          \ s:Get('Client') . '\ files ' . ':PFExecCmd PLabelsSyncLabel<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Label.-Sep2- :'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&Label.&View\ Labels :PLabels<CR>'
+  endif
+
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.Cl&ient :PClient<CR>'
+  else
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.Cl&ient.&New :PClient +P<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.Cl&ient.' .
+          \ '&Edit\ Current\ ClientSpec :PFExecCmd PItemOpen<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.Cl&ient.' .
+          \ 'Descri&be\ Current\ ClientSpec :PFExecCmd PItemDescribe<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.Cl&ient.' .
+          \ '&Delete\ Current\ ClientSpec :PFExecCmd PItemDelete<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.' .
+          \ 'Cl&ient.&Edit\ ' . escape(s:Get('Client', 'Current Client'), ' ') .
+         \ ' :PClient<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.Cl&ient.-Sep- :'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.Cl&ient.&Switch\ to\ Current' .
+          \ '\ Client :exec "PFSwitch ' . s:Get('Port') .
+          \ ' " . perforce#PFCall("s:GetCurrentItem")<CR>'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.Cl&ient.&View\ ClientSpecs :PClients<CR>'
+  endif
+
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&User :PUser<CR>'
+  else
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&User.&New :PUser +P<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&User.' .
+          \ '&Edit\ Current\ UserSpec :PFExecCmd PItemOpen<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&User.' .
+          \ 'Descri&be\ Current\ UserSpec :PFExecCmd PItemDescribe<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&User.' .
+          \ '&Delete\ Current\ UserSpec :PFExecCmd PItemDelete<CR>'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&User.&Edit\ ' .
+         \ escape(s:Get('User', 'Current User'), ' ') . ' :PUser<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&User.-Sep- :'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&User.&Switch\ to\ Current' .
+          \ '\ User :exec "PFSwitch ' . s:Get('Port') . ' ' .
+         \ s:Get('Client') . ' " . perforce#PFCall("s:GetCurrentItem")<CR>'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&User.&View\ Users :PUsers<CR>'
+  endif
+
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Job :PJob<CR>'
+  else
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Job.&New :PJob<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Job.' .
+          \ '&Edit\ Current\ JobSpec :PFExecCmd PItemOpen<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Job.' .
+          \ 'Descri&be\ Current\ JobSpec :PFExecCmd PItemDescribe<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Job.' .
+          \ '&Delete\ Current\ JobSpec :PFExecCmd PItemDelete<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Job.-Sep1- :'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Job.&Edit\ Job&Spec ' .
+         \ ':PJobspec<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Job.-Sep2- :'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Job.&View\ Jobs :PJobs<CR>'
+  endif
+
+  if a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Depot.&New :PDepot<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Depot.' .
+          \ '&Edit\ Current\ DepotSpec :PFExecCmd PItemOpen<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Depot.' .
+          \ 'Descri&be\ Current\ DepotSpec :PFExecCmd PItemDescribe<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Depot.' .
+          \ '&Delete\ Current\ DepotSpec :PFExecCmd PItemDelete<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Depot.-Sep- :'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&Depot.&View\ Depots :PDepots<CR>'
+  endif
+
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.Open\ Current\ File\ From\ A&nother\ Branch :E<CR>'
+  else
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&Tools.Open\ Current\ File\ From\ A&nother\ Branch ' .
+         \ ':E<CR>'
+  endif
+
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.-Sep7- :'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.Sa&ve\ Current\ Spec ' .
+         \':PFExecCmd W<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.Save\ and\ &Quit\ ' .
+         \'Current\ Spec :PFExecCmd WQ<CR>'
+  endif
+
+  exec 'amenu <silent> ' . a:sub . '&Perforce.-Sep8- :'
+  exec 'amenu <silent> ' . a:sub . '&Perforce.Re-Initial&ze :PFInitialize<CR>'
+  if ! a:expanded
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Help :PHelp<CR>'
+  else
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Help.&General :PHelp<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Help.&Simple :PHelp simple<CR>'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&Help.&Commands :PHelp commands<CR>'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&Help.&Environment :PHelp environment<CR>'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&Help.&Filetypes :PHelp filetypes<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Help.&Jobview :PHelp jobview<CR>'
+    exec 'amenu <silent> ' . a:sub .
+          \ '&Perforce.&Help.&Revisions :PHelp revisions<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Help.&Usage :PHelp usage<CR>'
+    exec 'amenu <silent> ' . a:sub . '&Perforce.&Help.&Views :PHelp views<CR>'
+  endif
+endfunction
+endif
+" }}}
+
+"
+" Add menu entries if user wants.
+"
+
+silent! unmenu Perforce
+silent! unmenu! Perforce
+if s:Get('EnableMenu')
+  call s:CreateMenu('', s:Get('UseExpandedMenu'))
+endif
+
+silent! unmenu PopUp.Perforce
+silent! unmenu! PopUp.Perforce
+if s:Get('EnablePopupMenu')
+  call s:CreateMenu('PopUp.', s:Get('UseExpandedPopupMenu'))
+endif
+
+" We no longer need this.
+silent! delf s:CreateMenu
+silent! delf s:Get
+
+let v:errmsg = ''
+
+" Make sure line-continuations won't cause any problem. This will be restored
+"   at the end
+let s:save_cpo = &cpo
+set cpo&vim
+
+" vim6:fdm=marker et sw=2
diff --git a/.vim/perforce/perforceutils.vim b/.vim/perforce/perforceutils.vim
new file mode 100755 (executable)
index 0000000..db093bb
--- /dev/null
@@ -0,0 +1,39 @@
+" perforceutils.vim: Add-On utilities for perforce plugin.
+" Author: Hari Krishna (hari_vim at yahoo dot com)
+" Last Change: 29-Aug-2006 @ 17:57
+" Created:     19-Apr-2004
+" Requires:    Vim-7.0
+" Version:     1.2.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 
+" NOTE:
+"   - This may not work well if there are multiple diff formats are mixed in
+"     the same file.
+
+" Make sure line-continuations won't cause any problem. This will be restored
+"   at the end
+let s:save_cpo = &cpo
+set cpo&vim
+
+" CAUTION: Don't assume the existence of plugin/perforce.vim (or any other
+"   plugins) at the time this file is sourced.
+
+command! -nargs=0 PFDiffLink :call perforceutils#DiffOpenSrc(0)
+command! -nargs=0 PFDiffPLink :call perforceutils#DiffOpenSrc(1)
+
+command! PFShowConflicts :call perforceutils#ShowConflicts()
+
+aug P4DiffLink
+  au!
+  au FileType * :if expand('<amatch>') ==# 'diff' && exists('b:p4OrgFileName') |
+        \   call perforceutils#SetupDiffLink() |
+        \ endif
+aug END
+
+" Restore cpo.
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim6:fdm=marker et sw=2
diff --git a/.vim/perforce/restor.sh b/.vim/perforce/restor.sh
new file mode 100755 (executable)
index 0000000..2da076d
--- /dev/null
@@ -0,0 +1,125 @@
+#!/bin/bash
+# Author: Hari Krishna Dara ( hari_vim at yahoo dot com ) 
+# Last Change: 06-Jan-2004 @ 19:07
+# Requires:
+#   - bash or ksh (tested on cygwin and MKS respectively).
+#   - Info Zip for the -z option to work (comes with linux/cygwin). Download for
+#     free from:
+#       http://www.info-zip.org/
+# Version: 1.1.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 
+usage()
+{
+cat <<END
+$0 <input package>
+  The input package is the name of the backup directory or the archive file(with
+  or without extension).
+END
+}
+
+inputType=''
+inputPackage=''
+verboseMode=1
+if [ -d $1 ]; then
+    inputType='dir'
+    inputPackage=$1
+elif [ -r $1.zip ]; then
+    inputType='zip'
+    inputPackage=$1.zip
+elif [ -r $1.tar.gz ]; then
+    inputType='tar'
+    inputPackage=$1.tar.gz
+    tarOpt='z'
+elif [ -r $1.tar.bz2 ]; then
+    inputType='tar'
+    inputPackage=$1.tar.bz2
+    tarOpt='j'
+elif [ -r $1.tar.Z ]; then
+    inputType='tar'
+    inputPackage=$1.tar.Z
+    tarOpt='Z'
+elif [ -r $1.tar ]; then
+    inputType='tar'
+    inputPackage=$1.tar
+    tarOpt=''
+elif [ -r $1 ]; then
+    case $1 in
+    *.zip)
+       inputType='zip'
+       ;;
+    *.tar.gz)
+       inputType='tar'
+       tarOpt='z'
+       ;;
+    *.tar.bz2)
+       inputType='tar'
+       tarOpt='j'
+       ;;
+    *.tar.Z)
+       inputType='tar'
+       tarOpt='Z'
+       ;;
+    *.tar)
+       inputType='tar'
+       tarOpt=''
+       ;;
+    *)
+       echo "$0: Unknown input package type."
+       exit 1
+       ;;
+    esac
+    inputPackage=$1
+else
+    echo "$0: No input package found for $1"
+    exit 1
+fi
+
+if [ $inputType = 'dir' ]; then
+    listCmd="find $inputPackage -type f -print | sed -e 's;^$1/*;;'"
+    copyCmd="cp"
+    if [ $verboseMode -ne 0 ]; then
+       copyCmd="$copyCmd -v"
+    fi
+    copyCmd="$copyCmd -r $inputPackage/* ."
+elif [ $inputType = 'zip' ]; then
+    listCmd="unzip -l -qq $inputPackage | awk 'BEGIN{OFS=\"\"}{\$1=\"\"; \$2=\"\"; \$3=\"\"; print \$0}'"
+    copyCmd="unzip"
+    if [ $verboseMode -ne 1 ]; then
+       copyCmd="$copyCmd -q"
+    fi
+    copyCmd="$copyCmd $inputPackage.zip"
+elif [ $inputType = 'tar' ]; then
+    listCmd="tar -t${tarOpt}f $inputPackage"
+    copyCmd="tar"
+    if [ $verboseMode -ne 0 ]; then
+       copyCmd="$copyCmd -v"
+    fi
+    copyCmd="$copyCmd -x${tarOpt}f $inputPackage"
+fi
+
+if [ $verboseMode -eq 1 ]; then
+    echo "Opening files in Perforce for edit."
+fi
+discardOutput=''
+if [ $verboseMode -eq 0 ]; then
+    discardOutput=' > /dev/null'
+fi
+#eval $listCmd | cat
+eval $listCmd | p4 -x - edit $discardOutput
+if [ $? -ne 0 ]; then
+    echo "$0: There was an error opening files in Perforce for edit."
+    echo "Make sure you are in the right directory and try again."
+    exit 1
+fi
+
+if [ $verboseMode -eq 1 ]; then
+    echo "$0: Copying files to the target directories."
+fi
+#echo $copyCmd
+eval $copyCmd
+if [ $? -ne 0 ]; then
+    echo "$0: Error copying files."
+    exit 1
+fi
diff --git a/.vim/plugin/genutils.vim b/.vim/plugin/genutils.vim
new file mode 100755 (executable)
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" <c928400@student.dtu.dk>
+"   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/.vim/plugin/perforce.vim b/.vim/plugin/perforce.vim
new file mode 100755 (executable)
index 0000000..35cf8f2
--- /dev/null
@@ -0,0 +1,375 @@
+" perforce.vim: Interface with perforce SCM through p4.
+" Author: Hari Krishna (hari_vim at yahoo dot com)
+" Last Change: 02-Sep-2006 @ 19:56
+" Created:     Sometime before 20-Apr-2001
+" Requires:    Vim-7.0, genutils.vim(2.3)
+" Version:     4.1.3
+" 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:
+"     See ":help perforce-acknowledgements".
+" Download From:
+"     http://www.vim.org//script.php?script_id=240
+" Usage:
+"     For detailed help, see ":help perforce" or read doc/perforce.txt. 
+"
+" TODO: {{{
+"   - Launch from describe window is not using the local path.
+"
+"   - I need a test suite to stop things from breaking.
+"   - Should the client returned by g:p4CurPresetExpr be made permanent?
+"   - curPresetExpr can't support password, so how is the expression going to
+"     change password?
+"   - If you actually use python to execute, you may be able to display the
+"     output incrementally.
+"   - There seems to be a problem with 'autoread' change leaking. Not sure if
+"     we explicitly set it somewhere, check if we are using try block.
+"   - Buffer local autocommads are pretty useful for perforce plugin, send
+"     feedback.
+"   - Verify that the buffers/autocommands are not leaking.
+" TODO }}}
+"
+" BEGIN NOTES {{{
+"   - Now that we increase the level of escaping in the ParseOptions(), we
+"     need to be careful in reparsing the options (by not using
+"     scriptOrigin=2). When you CreateArgString() using these escaped
+"     arguments as if they were typed in by user, they get sent to p4 as they
+"     are, with incorrect number of back-slashes.
+"   - When issuing sub-commands, we should remember to use the s:p4Options
+"     that was passed to the main command (unless the main command already
+"     generated a new window, in which case the original s:p4Options are
+"     remembered through b:p4Options and automatically reused for the
+"     subcommands), or the user will see incorrect behavior or at the worst,
+"     errors.
+"   - The p4FullCmd now can have double-quotes surrounding each of the
+"     individual arguments if the shell is cmd.exe or command.com, so while
+"     manipulating it directly, we need to use "\?.
+"   - With the new mode of scriptOrigin=2, the changes done to the s:p4*
+"     variables will not get reflected in the s:p4WinName, unless there is
+"     some other relevant processing done in PFIF.
+"   - With the new mode of scriptOrigin=2, there is no reason to use
+"     scriptOrigin=1 in most of the calls from handlers.
+"   - The s:PFSetupBufAutoCommand and its cousines expect the buffer name to
+"     be plain with no escaping, as they do their own escaping.
+"   - Wherever we normally expect a depot name, we should use the s:p4Depot
+"     instead of hardcoded 'depot'. We should also consider the client name
+"     here.
+"   - Eventhough DefFileChangedShell event handling is now localized, we still
+"     need to depend on s:currentCommand to determine the 'autoread' value,
+"     this is because some other plugin might have already installed a
+"     FileChangedShell event to DefFileChangedShell, resulting in us receiving
+"     callbacks anytime, so we need a variable that has a lifespace only for
+"     the duration of the execution of p4 commands?
+"   - We need to pass special characters such as <space>, *, ?, [, (, &, |, ', $
+"     and " to p4 without getting interpreted by the shell. We may have to use
+"     appropriate quotes around the characters when the shell treats them
+"     specially. Windows+native is the least bothersome of all as it doesn't
+"     treat most of the characters specially and the arguments can be
+"     sorrounded in double-quotes and embedded double-quotes can be easily
+"     passed in by just doubling them.
+"   - I am aware of the following unique ways in which external commands are
+"     executed (not sure if this is same for all of the variations possible: 
+"     ":[{range}][read|write]!cmd | filter" and "system()"):
+"     For :! command 
+"       On Windoze+native:
+"         cmd /c <command>
+"       On Windoze+sh:
+"         sh -c "<command>"
+"       On Unix+sh:
+"         sh -c (<command>) 
+"   - By the time we parse arguments, we protect all the back-slashes, which
+"     means that we would never see a single-back-slash.
+"   - Using back-slashes on Cygwin vim is unique and causes E303. This is
+"     because it thinks it is on UNIX where it is not a special character, but
+"     underlying Windows obviously treats it special and so it bails out.
+"   - Using back-slashes on Windows+sh also seems to be different. Somewhere in
+"     the execution line (most probably the path from CreateProcess() to sh,
+"     as it doesn't happen in all other types of interfaces) consumes one
+"     level of extra back-slashes. If it is even number it becomes half, and
+"     if it is odd then the last unpaired back-slash is left as it is.
+"   - Some test cases for special character handling:
+"     - PF fstat a\b
+"     - PF fstat a\ b
+"     - PF fstat a&b
+"     - PF fstat a\&b
+"     - PF fstat a\#b
+"     - PF fstat a\|b
+"   - Careful using s:PFIF(1) from within script, as it doesn't redirect the
+"     call to the corresponding handler (if any).
+"   - Careful using ":PF" command from within handlers, especially if you are
+"     executing the same s:p4Command again as it will result in a recursion.
+"   - The outputType's -2 and -1 are local to the s:PFrangeIF() interface, the
+"     actual s:PFImpl() or any other methods shouldn't know anything about it.
+"     Which is why this outputType should be used only for those commands that
+"     don't have a handler. Besides this scheme will not even work if a
+"     handler exists, as the outputType will get permanently set to 4 by the
+"     time it gets redirected back to s:PFrangeIF() through the handler. (If
+"     this should ever be a requirement, we will need another state variable
+"     called s:orgOutputType.)
+"   - Be careful to pass argument 0 to s:PopP4Context() whenever the logical
+"     p4 operation ends, to avoid getting the s:errCode carried over. This is
+"     currently taken care of for all the known recursive or ignorable error
+"     cases.
+"   - We need to use s:outputType as much as possible, not a:outputType, which
+"     is there only to pass it on to s:ParseOptions(). After calling s:PFIF()
+"     the outputType is established in s:outputType.
+"   - s:errCode is reset by ParseOptions(). For cases that Push and Pop context
+"     even before the first call to ParseOptions() (such as the
+"     s:GetClientInfo() function), we have to check for s:errCode before we
+"     pop context, or we will just carry on an error code from a previous bad
+"     run (applies to mostly utility functions).
+" END NOTES }}}
+
+if exists('loaded_perforce')
+  finish
+endif
+if v:version < 700
+  echomsg 'Perforce: You need at least Vim 7.0'
+  finish
+endif
+
+
+" We need these scripts at the time of initialization itself.
+if !exists('loaded_genutils')
+  runtime plugin/genutils.vim
+endif
+if !exists('loaded_genutils') || loaded_genutils < 203
+  echomsg 'perforce: You need a newer version of genutils.vim plugin'
+  finish
+endif
+let loaded_perforce=400
+
+" Make sure line-continuations won't cause any problem. This will be restored
+"   at the end
+let s:save_cpo = &cpo
+set cpo&vim
+
+" User option initialization {{{
+function! s:CondDefSetting(settingName, def)
+  if !exists(a:settingName)
+    let {a:settingName} = a:def
+  endif
+endfunction
+call s:CondDefSetting('g:p4CmdPath', 'p4')
+call s:CondDefSetting('g:p4ClientRoot', '')
+call s:CondDefSetting('g:p4DefaultListSize', '100')
+call s:CondDefSetting('g:p4DefaultDiffOptions', '')
+call s:CondDefSetting('g:p4DefaultPreset', -1)
+call s:CondDefSetting('g:p4Depot', 'depot')
+call s:CondDefSetting('g:p4Presets', '')
+call s:CondDefSetting('g:p4DefaultOptions', '')
+call s:CondDefSetting('g:p4UseGUIDialogs', 0)
+call s:CondDefSetting('g:p4PromptToCheckout', 1)
+call s:CondDefSetting('g:p4MaxLinesInDialog', 1)
+call s:CondDefSetting('g:p4EnableActiveStatus', 1)
+call s:CondDefSetting('g:p4ASIgnoreDefPattern',
+      \'\c\%(\<t\%(e\)\?mp\/.*\|^.*\.tmp$\|^.*\.log$\|^.*\.diff\?$\|^.*\.out$\|^.*\.buf$\|^.*\.bak$\)\C')
+call s:CondDefSetting('g:p4ASIgnoreUsrPattern', '')
+call s:CondDefSetting('g:p4OptimizeActiveStatus', 1)
+call s:CondDefSetting('g:p4EnableRuler', 1)
+call s:CondDefSetting('g:p4RulerWidth', 25)
+call s:CondDefSetting('g:p4EnableMenu', 0)
+call s:CondDefSetting('g:p4EnablePopupMenu', 0)
+call s:CondDefSetting('g:p4UseExpandedMenu', 1)
+call s:CondDefSetting('g:p4UseExpandedPopupMenu', 0)
+call s:CondDefSetting('g:p4CheckOutDefault', 3)
+call s:CondDefSetting('g:p4SortSettings', 1)
+" Probably safer than reading $TEMP.
+call s:CondDefSetting('g:p4TempDir', fnamemodify(tempname(), ':h'))
+call s:CondDefSetting('g:p4SplitCommand', 'split')
+call s:CondDefSetting('g:p4EnableFileChangedShell', 1)
+call s:CondDefSetting('g:p4UseVimDiff2', 0)
+call s:CondDefSetting('g:p4BufHidden', 'wipe')
+call s:CondDefSetting('g:p4Autoread', 1)
+call s:CondDefSetting('g:p4FileLauncher', '')
+call s:CondDefSetting('g:p4CurPresetExpr', '')
+call s:CondDefSetting('g:p4CurDirExpr', '')
+call s:CondDefSetting('g:p4UseClientViewMap', 1)
+delfunction s:CondDefSetting
+" }}}
+
+
+" Call this any time to reconfigure the environment. This re-performs the same
+"   initializations that the script does during the vim startup, without
+"   loosing what is already configured.
+command! -nargs=0 PFInitialize :call perforce#Initialize(0)
+
+""" The following are some shortcut commands. Some of them are enhanced such
+"""   as the help window or the filelog window.
+
+" Command definitions {{{
+
+command! -nargs=* -complete=custom,perforce#PFComplete PP
+      \ :call perforce#PFIF(0, 0, 'print', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PPrint
+      \ :call perforce#PFIF(0, 0, 'print', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PDiff
+      \ :call perforce#PFIF(0, 0, 'diff', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PD
+      \ :PDiff <args>
+command! -nargs=* -complete=custom,perforce#PFComplete PEdit
+      \ :call perforce#PFIF(0, 2, 'edit', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PE
+      \ :PEdit <args>
+command! -nargs=* -complete=custom,perforce#PFComplete PReopen
+      \ :call perforce#PFIF(0, 2, 'reopen', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PAdd
+      \ :call perforce#PFIF(0, 2, 'add', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PA
+      \ :PAdd <args>
+command! -nargs=* -complete=custom,perforce#PFComplete PDelete
+      \ :call perforce#PFIF(0, 2, 'delete', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PLock
+      \ :call perforce#PFIF(0, 2, 'lock', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PUnlock
+      \ :call perforce#PFIF(0, 2, 'unlock', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PRevert
+      \ :call perforce#PFIF(0, 2, 'revert', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PR
+      \ :PRevert <args>
+command! -nargs=* -complete=custom,perforce#PFComplete PSync
+      \ :call perforce#PFIF(0, 2, 'sync', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PG
+      \ :PSync <args>
+command! -nargs=* -complete=custom,perforce#PFComplete PGet
+      \ :call perforce#PFIF(0, 2, 'get', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete POpened
+      \ :call perforce#PFIF(0, 0, 'opened', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PO
+      \ :POpened <args>
+command! -nargs=* -complete=custom,perforce#PFComplete PHave
+      \ :call perforce#PFIF(0, 0, 'have', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PWhere
+      \ :call perforce#PFIF(0, 0, 'where', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PDescribe
+      \ :call perforce#PFIF(0, 0, 'describe', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PFiles
+      \ :call perforce#PFIF(0, 0, 'files', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PLabelsync
+      \ :call perforce#PFIF(0, 0, 'labelsync', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PFilelog
+      \ :call perforce#PFIF(0, 0, 'filelog', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PIntegrate
+      \ :call perforce#PFIF(0, 0, 'integrate', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PDiff2
+      \ :call perforce#PFIF(0, 0, 'diff2', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PD2
+      \ :PDiff2 <args>
+command! -nargs=* -complete=custom,perforce#PFComplete PFstat
+      \ :call perforce#PFIF(0, 0, 'fstat', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PHelp
+      \ :call perforce#PFIF(0, 0, 'help', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PH
+      \ :PHelp <args>
+command! -nargs=* PPasswd
+      \ :call perforce#PFIF(0, 2, 'passwd', <f-args>)
+
+
+""" Some list view commands.
+command! -nargs=* -complete=custom,perforce#PFComplete PChanges
+      \ :call perforce#PFIF(0, 0, 'changes', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PBranches
+      \ :call perforce#PFIF(0, 0, 'branches', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PLabels
+      \ :call perforce#PFIF(0, 0, 'labels', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PClients
+      \ :call perforce#PFIF(0, 0, 'clients', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PUsers
+      \ :call perforce#PFIF(0, 0, 'users', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PJobs
+      \ :call perforce#PFIF(0, 0, 'jobs', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PDepots
+      \ :call perforce#PFIF(0, 0, 'depots', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PGroups
+      \ :call perforce#PFIF(0, 0, 'groups', <f-args>)
+
+
+""" The following support some p4 operations that normally involve some
+"""   interaction with the user (they are more than just shortcuts).
+
+command! -nargs=* -complete=custom,perforce#PFComplete PChange
+      \ :call perforce#PFIF(0, 0, 'change', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PBranch
+      \ :call perforce#PFIF(0, 0, 'branch', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PLabel
+      \ :call perforce#PFIF(0, 0, 'label', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PClient
+      \ :call perforce#PFIF(0, 0, 'client', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PUser
+      \ :call perforce#PFIF(0, 0, 'user', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PJob
+      \ :call perforce#PFIF(0, 0, 'job', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PJobspec
+      \ :call perforce#PFIF(0, 0, 'jobspec', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PDepot
+      \ :call perforce#PFIF(0, 0, 'depot', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PGroup
+      \ :call perforce#PFIF(0, 0, 'group', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PSubmit
+      \ :call perforce#PFIF(0, 0, 'submit', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PResolve
+      \ :call perforce#PFIF(0, 0, 'resolve', <f-args>)
+
+" Some built-in commands.
+command! -nargs=* -complete=custom,perforce#PFComplete PVDiff
+      \ :call perforce#PFIF(0, 0, 'vdiff', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PVDiff2
+      \ :call perforce#PFIF(0, 0, 'vdiff2', <f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete PExec
+      \ :call perforce#PFIF(0, 5, 'exec', <f-args>)
+
+""" Other utility commands.
+
+command! -nargs=* -complete=file E :call perforce#PFOpenAltFile(0, <f-args>)
+command! -nargs=* -complete=file ES :call perforce#PFOpenAltFile(2, <f-args>)
+command! -nargs=* -complete=custom,perforce#PFSwitchComplete PFSwitch
+      \ :call perforce#PFSwitch(1, <f-args>)
+command! -nargs=* PFSwitchPortClientUser :call perforce#SwitchPortClientUser()
+command! -nargs=0 PFRefreshActivePane :call perforce#PFRefreshActivePane()
+command! -nargs=0 PFRefreshFileStatus :call perforce#GetFileStatus(0, 1)
+command! -nargs=0 PFToggleCkOut :call perforce#ToggleCheckOutPrompt(1)
+command! -nargs=* -complete=custom,perforce#PFSettingsComplete PFS
+      \ :PFSettings <args>
+command! -nargs=* -complete=custom,perforce#PFSettingsComplete PFSettings
+      \ :call perforce#PFSettings(<f-args>)
+command! -nargs=0 PFDiffOff :call perforce#PFDiffOff(
+      \ exists('w:p4VDiffWindow') ? w:p4VDiffWindow : -1)
+command! -nargs=? PFWipeoutBufs :call perforce#WipeoutP4Buffers(<f-args>)
+"command! -nargs=* -complete=file -range=% PF
+command! -nargs=* -complete=custom,perforce#PFComplete -range=% PF
+      \ :call perforce#PFrangeIF(<line1>, <line2>, 0, 0, <f-args>)
+command! -nargs=* -complete=file PFRaw :call perforce#PFRaw(<f-args>)
+command! -nargs=* -complete=custom,perforce#PFComplete -range=% PW
+      \ :call perforce#PW(<line1>, <line2>, 0, <f-args>)
+command! -nargs=0 PFLastMessage :call perforce#LastMessage()
+command! -nargs=0 PFBugReport :runtime perforce/perforcebugrep.vim
+command! -nargs=0 PFUpdateViews :call perforce#UpdateViewMappings()
+
+" New normal mode mappings.
+if (! exists('no_plugin_maps') || ! no_plugin_maps) &&
+      \ (! exists('no_perforce_maps') || ! no_execmap_maps)
+  nnoremap <silent> <Leader>prap :PFRefreshActivePane<cr>
+  nnoremap <silent> <Leader>prfs :PFRefreshFileStatus<cr>
+
+  " Some generic mappings.
+  if maparg('<C-X><C-P>', 'c') == ""
+    cnoremap <C-X><C-P> <C-R>=perforce#PFOpenAltFile(1)<CR>
+  endif
+endif
+
+" Command definitions }}}
+
+if exists('g:p4EnableActiveStatus') && g:p4EnableActiveStatus
+  aug P4Init
+    au!
+    au BufRead * exec 'au! P4Init' | exec 'PFInitialize' | PFRefreshFileStatus
+  aug END
+endif
+
+" Restore cpo.
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim6:fdm=marker et sw=2
diff --git a/.vim/syntax/perforce.vim b/.vim/syntax/perforce.vim
new file mode 100755 (executable)
index 0000000..ab3efa7
--- /dev/null
@@ -0,0 +1,184 @@
+" Vim syntax file
+" Language:      Perforce SCM Spec
+" Author:        Hari Krishna Dara (hari_vim at yahoo dot com)
+" Last Modified:  31-Mar-2004 @ 23:32
+" Plugin Version: 2.1
+" Revision:      1.0.4
+" Since Version:  1.4
+"
+" TODO:
+"   Filelog definition can be more complete.
+"   Don't know all the cases of resolve lines, so it may not be complete.
+
+" For version 5.x: Clear all syntax items
+" For version 6.x: Quit when a syntax file was already loaded
+if version < 600
+  syntax clear
+elseif exists("b:current_syntax")
+  finish
+endif
+
+" Generic interactive command:
+" View region:
+syn region perforceView start="^View:$" end="\%$" contains=perforceSpec,perforceViewLine
+syn region perforceFiles start="^Files:$" end="\%$" contains=perforceSpec,perforceChangeFilesLine,perforceSubmitFilesLine
+" Exclude View and Files alone, so that they can be matched by the regions.
+syn match perforceSpecline "^.*\%(\<View\|Files\)\@<!:.*$" contains=perforceSpec
+syn match perforceSpec "^\S\+:\@=" contains=perforceSpecKey contained
+syn match perforceSpecKey "^\S\+:\@=" contained
+syn match perforceViewLine "^\t-\?//[^/[:space:]]\+/.\+$" contains=perforceViewExclude,perforceDepotView,perforceClientView contained
+syn match perforceViewExclude "\%(^\t\)\@<=-" contained
+syn match perforceDepotView "\%(^\t-\?\)\@<=//[^/[:space:]]\+/.\+\%(//\)\@=" contained
+syn match perforceClientView "\%(\t-\?\)\@<!//[^/[:space:]]\+/.\+$" contained
+syn match perforceChangeFilesLine "^\t//[^/[:space:]]\+/\f\+\t\+#.*$" contains=perforceDepotFile,perforceSubmitType contained
+syn match perforceSubmitFilesLine "^\t//[^/[:space:]]\+/.*#\d\+ - .*$" contains=perforceDepotFileSpec,perforceSubmitType,perforceChangeNumber " opened, files
+
+
+" changes
+syn match perforceChangeItem "^Change \d\+ on \d\+/\d\+/\d\+ .*$" contains=perforceChangeNumber,perforceDate,perforceUserAtClient
+syn match perforceChangeNumber "\%(^Change \)\@<=\d\+ \@=" contained
+
+
+" clients
+syn match perforceClientItem "^Client \S\+ \d\+/\d\+/\d\+ .*$" contains=perforceClientName,perforceDate,perforceClientRoot
+syn match perforceClientName "\%(^Client \)\@<=\w\+ \@=" contained
+syn match perforceClientRoot "\%( root \)\@<=\f\+ \@=" contained
+
+
+" labels
+syn match perforceLabelItem "^Label \S\+ \d\+/\d\+/\d\+ .*$" contains=perforceLabelName,perforceDate,perforceUserName
+syn match perforceLabelName "\%(^Label \)\@<=\S\+ \@=" contained
+syn match perforceUserName "\%('Created by \)\@<=\w\+\.\@=" contained
+
+
+" branches
+syn match perforceBranchItem "^Branch \S\+ \d\+/\d\+/\d\+ .*$" contains=perforceBranchName,perforceDate
+syn match perforceBranchName "\%(^Branch \)\@<=\S\+ \@=" contained
+
+
+" depots
+syn match perforceDepotItem "^Depot \S\+ \d\+/\d\+/\d\+ .*$" contains=perforceDepotName,perforceDate
+syn match perforceDepotName "\%(^Depot \)\@<=\S\+ \@=" contained
+
+
+" users
+syn match perforceUserItem "^\w\+ <[^@]\+@[^>]\+> ([^)]\+) .*$" contains=perforceUserName,perforceDate
+syn match perforceUserName "^\w\+\%( <\)\@=" contained
+
+
+" jobs
+syn match perforceJobItem "^\S\+ on \d\+/\d\+/\d\+ by .*$" contains=perforceJobName,perforceDate,perforceClientName
+syn match perforceJobName "^\S\+\%( on\)\@=" contained
+syn match perforceClientName "\%( by \)\@<=[^@[:space:]]\+ \@=" contained
+
+
+" fixes
+syn match perforceFixItem "^\S\+ fixed by change \d\+.*$" contains=perforceJobName,perforceChangeNumber,perforceDate,perforceUserAtClient
+syn match perforceJobName "^\S\+\%( fixed \)\@=" contained
+syn match perforceChangeNumber "\%(by change \)\@<=\d\+ \@=" contained
+
+
+" opened, files, have etc.
+" The format of have is different because it contains the local file. 
+syn match perforceFilelistLine "^//[^/[:space:]]\+/.*#\d\+ - .*$" contains=perforceDepotFileSpec,perforceLocalFile " have
+syn match perforceFilelistLine "^//[^/[:space:]]\+/.*#\d\+ - \%(branch\|integrate\|edit\|delete\|add\).*$" contains=perforceDepotFileSpec,perforceSubmitType,perforceDefaultSubmitType,perforceChangeNumber " opened, files
+syn match perforceChangeNumber "\%( change \)\@<=\d\+ \@=" contained
+syn match perforceSubmitType "\%( - \)\@<=\S\+\%(\%( default\)\? change \)\@="  contained
+syn match perforceSubmitType "\%(# \)\@<=\S\+$" contained " change.
+syn match perforceDefaultSubmitType "\<default\%( change \)\@=" contained
+syn match perforceLocalFile "\%( - \)\@<=.\+$" contained
+
+
+" filelog
+syn match perforceFilelogLine "^//depot/\f\+$" contains=perforceDepotFileSpec
+syn match perforceFilelogLine "^\.\.\. #\d\+ change \d\+ .*$" contains=perforceVerStr,perforceChangeNumber,perforceSubmitType,perforceDate,perforceUserAtClient
+syn match perforceSubmitType " \@<=\S\+\%( on \)\@=" contained
+
+
+" resolve
+" What else can be there other than "merging" and "copy from" ?
+syn match perforceResolveLine "^\f\+ - \(merging\|copy from\) \f\+.*$" contains=perforceResolveTargetFile,perforceDepotFileSpec
+syn match perforceResolveLine "^Diff chunks:.*$" contains=perforceNumChunks
+" Strictly speaking, we should be able to distinguish between local and depot
+"   file names here, but I don't know how.
+syn match perforceResolveTargetFile "^\f\+" contained
+syn match perforceNumChunks "\d\+" contained
+syn match perforceConflicting "[1-9]\d* conflicting" contained
+syn match perforceResolveSkipped " - resolve skipped\."
+
+
+" help.
+syn region perforceHelp start=" \{4}\w\+ -- " end="\%$" contains=perforceCommands,perforceHelpKeys
+syn region perforceHelp start=" \{4}Most common Perforce client commands:" end="\%$" contains=perforceCommands,perforceHelpKeys
+syn region perforceHelp start=" \{4}Perforce client commands:" end="\%$" contains=perforceCommands,perforceHelpKeys
+syn region perforceHelp start=" \{4}Environment variables used by Perforce:" end="\%$" contains=perforceCommands,perforceHelpKeys
+syn region perforceHelp start=" \{4}File types supported by Perforce:" end="\%$" contains=perforceCommands,perforceHelpKeys
+syn region perforceHelp start=" \{3,4}Perforce job views:" end="\%$" contains=perforceCommands,perforceHelpKeys
+syn region perforceHelp start=" \{4}Specifying file revisions and revision ranges:" end="\%$" contains=perforceHelpVoid,perforceCommands,perforceHelpKeys
+syn region perforceHelp start=" \{4}Perforce client usage:" end="\%$" contains=perforceCommands,perforceHelpKeys
+syn region perforceHelp start=" \{4}Perforce views:" end="\%$" contains=perforceCommands,perforceHelpKeys
+syn keyword perforceHelpKeys contained simple commands environment filetypes
+syn keyword perforceHelpKeys contained jobview revisions usage views
+" Don't highlight these.
+syn match perforceHelpVoid "@change" contained
+syn match perforceHelpVoid "@client" contained
+syn match perforceHelpVoid "@label" contained
+syn match perforceHelpVoid "#have" contained
+" Needed for help to window to sync correctly.
+syn sync lines=100
+
+
+" Common.
+syn match perforceUserAtClient " by [^@[:space:]]\+@\S\+" contains=perforceUserName,perforceClientName contained
+syn match perforceClientName "@\@<=\w\+" contained
+syn match perforceUserName "\%( by \)\@<=[^@[:space:]]\+@\@=" contained
+syn match perforceDepotFileSpec "//[^/[:space:]]\+/\f\+\(#\d\+\)\?" contains=perforceDepotFile,perforceVerStr contained
+syn match perforceDepotFile "//[^#[:space:]]\+" contained
+syn match perforceComment "^\s*#.*$"
+syn match perforceDate "\<\@<=\d\+/\d\+/\d\+\>\@=" contained
+syn match perforceVerStr "#\d\+" contains=perforceVerSep,perforceVersion contained
+syn match perforceVerSep "#" contained
+syn match perforceVersion "\d\+" contained
+syn keyword perforceCommands contained add admin annotate branch branches change
+syn keyword perforceCommands contained changes client clients counter counters
+syn keyword perforceCommands contained delete depot dirs edit filelog files fix
+syn keyword perforceCommands contained fixes help info integrate integrated job
+syn keyword perforceCommands contained labelsync lock logger monitor obliterate
+syn keyword perforceCommands contained reopen resolve resolved revert review
+syn keyword perforceCommands contained triggers typemap unlock user users
+syn keyword perforceCommands contained verify where reviews set submit sync
+syn keyword perforceCommands contained opened passwd print protect rename
+syn keyword perforceCommands contained jobs jobspec label labels flush fstat
+syn keyword perforceCommands contained group groups have depots describe diff
+syn keyword perforceCommands contained diff2
+
+hi link perforceLabelName              perforceKeyName
+hi link perforceBranchName             perforceKeyName
+hi link perforceDepotName              perforceKeyName
+hi link perforceJobName                        perforceKeyName
+hi link perforceClientName             perforceKeyName
+hi link perforceUserName               perforceKeyName
+hi link perforceChangeNumber           perforceKeyName
+hi link perforceResolveTargetFile      perforceDepotFile
+
+hi def link perforceSpecKey           Label
+hi def link perforceComment           Comment
+hi def link perforceNumChunks         Constant
+hi def link perforceConflicting       Error
+hi def link perforceResolveSkipped    Error
+hi def link perforceDate              Constant
+hi def link perforceCommands          Identifier
+hi def link perforceHelpKeys          Identifier
+hi def link perforceClientRoot        Identifier
+hi def link perforceKeyName           Special
+hi def link perforceDepotFile         Directory
+hi def link perforceLocalFile         Identifier
+hi def link perforceVerSep            Operator
+hi def link perforceVersion           Constant
+hi def link perforceSubmitType       Type
+hi def link perforceDefaultSubmitType WarningMsg
+hi def link perforceViewExclude       WarningMsg
+hi def link perforceDepotView         Directory
+hi def link perforceClientView        Identifier
+
+let b:current_syntax='perforce'
diff --git a/.vimrc b/.vimrc
index 48a2cf2..62bd8d7 100644 (file)
--- a/.vimrc
+++ b/.vimrc
@@ -101,6 +101,9 @@ se t_WS=\e[8;%p1%d;%p2%dt
 " Highlight search results.
 se hlsearch
 
+" Set graphical window title.
+se titlestring=%{Show_TitleString()}
+
 " Syntax highlighting.  New versions will use syn enable instead.
 if version < 600
   syn on
@@ -174,6 +177,16 @@ fun! Show_Paste()
   endif
 endfun
 
+" Show the window title.
+fun! Show_TitleString()
+  if bufname("") == ""
+    let ts1='Vim'
+  else
+    let ts1=printf("%s [%2d:%s]", expand('%t'), bufnr(""), expand('%f'))
+  endif
+  return printf("%s (%s) %s", ts1, getcwd(), v:servername)
+endfun
+
 " Show the status line.
 fun! Show_StatusLine()
   call Iain_Vars()
@@ -224,7 +237,7 @@ call Show_StatusLine()
 
 " Function to create mappings with either a hardcoded \ or <Leader>.
 fun! Mapping(keysequence,mapping)
-  if version >= "600"
+  if version < "600"
     exec "map \\" . a:keysequence . " " . a:mapping
   else
     exec "map <Leader>" . a:keysequence . " " . a:mapping
@@ -442,4 +455,8 @@ se numberwidth=5
 
 " Add "previous tab" mapping as gb.
 map gb :tabPrev<CR>
+
+" Perforce.
+let g:p4EnableMenu=1
+let g:p4Presets='P4CONFIG'
 endif