" 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 xx xx let s:sid = maparg("xx") unmap 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, '\') != -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("")' 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 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", '']) 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("\$", '%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('W', \ [0]+b:p4Options+['submit']+split(, '\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('W', \ [0]+b:p4Options+['change']+split(, '\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 D silent! delcommand PItemDelete command! -range -buffer -nargs=0 PFilelogDiff \ :call s:FilelogDiff2(, ) vnoremap D :PFilelogDiff command! -buffer -nargs=0 PFilelogPrint :call perforce#PFIF(0, 0, 'print', \ GetCurrentItem()) nnoremap p :PFilelogPrint command! -buffer -nargs=0 PFilelogSync :call FilelogSyncToCurrentItem() nnoremap S :PFilelogSync command! -buffer -nargs=0 PFilelogDescribe \ :call FilelogDescribeChange() nnoremap C :PFilelogDescribe 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', GetCurrentItem()) nnoremap P :PClientsTemplate 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 PChangesDescribeCurrentItem() command! -buffer -nargs=0 PChangesSubmit \ :call ChangesSubmitChangeList() nnoremap S :PChangesSubmit command! -buffer -nargs=0 PChangesOpened \ :if getline('.') =~# " \\*pending\\* '" | \ call perforce#PFIF(1, 0, 'opened', '-c', GetCurrentItem()) | \ endif nnoremap o :PChangesOpened command! -buffer -nargs=0 PChangesDiff \ :if getline('.') =~# " \\*pending\\* '" | \ call perforce#PFIF(0, 0, 'diff', '++c', GetCurrentItem()) | \ else | \ call perforce#PFIF(0, 0, 'describe', (('DefaultDiffOptions') \ =~ '^\s*$' ? '-dd' : ('DefaultDiffOptions')), \ GetCurrentItem()) | \ endif nnoremap d :PChangesDiff command! -buffer -nargs=0 PItemOpen \ :if getline('.') =~# " \\*pending\\* '" | \ call perforce#PFIF(0, 0, 'change', GetCurrentItem()) | \ else | \ call perforce#PFIF(0, 0, 'describe', '-dd', 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 LabelsSyncClientToLabel() nnoremap S :PLabelsSyncClient command! -buffer -nargs=0 PLabelsSyncLabel \ :call LabelsSyncLabelToClient() nnoremap C :PLabelsSyncLabel command! -buffer -nargs=0 PLabelsFiles :call perforce#PFIF(0, 0, '++n', 'files', \ '//...@'. GetCurrentItem()) nnoremap I :PLabelsFiles command! -buffer -nargs=0 PLabelsTemplate :call perforce#PFIF(0, 0, '++A', \ 'label', '-t', GetCurrentItem()) nnoremap P :PLabelsTemplate 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("")) nnoremap :PHelpSelect nnoremap K :PHelpSelect nnoremap <2-LeftMouse> :PHelpSelect 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 /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" 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 D silent! delcommand PItemDelete command! -buffer -nargs=0 PFileDiff :call perforce#PFIF(0, 1, 'diff', \ GetCurrentDepotFile(line("."))) nnoremap D :PFileDiff command! -buffer -nargs=0 PFileProps :call perforce#PFIF(1, 0, 'fstat', '-C', \ GetCurrentDepotFile(line("."))) nnoremap P :PFileProps command! -buffer -nargs=0 PFileLog :call perforce#PFIF(1, 0, 'filelog', \ GetCurrentDepotFile(line("."))) command! -buffer -nargs=0 PFileEdit :call perforce#PFIF(1, 2, 'edit', \ GetCurrentItem()) nnoremap I :PFileEdit command! -buffer -bar -nargs=0 PFileRevert :call perforce#PFIF(1, 2, 'revert', \ GetCurrentItem()) nnoremap R :PFileRevert \| PFRefreshActivePane command! -buffer -nargs=0 PFilePrint \ :if getline('.') !~# '(\%(u\|ux\)binary)$' | \ call perforce#PFIF(0, 0, 'print', \ substitute(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 p :PFilePrint command! -buffer -nargs=0 PFileGet :call perforce#PFIF(1, 2, 'sync', \ GetCurrentDepotFile(line("."))) command! -buffer -nargs=0 PFileSync :call perforce#PFIF(1, 2, 'sync', \ GetCurrentItem()) nnoremap S :PFileSync command! -buffer -nargs=0 PFileChange :call perforce#PFIF(0, 0, 'change', \ GetCurrentChangeNumber(line("."))) nnoremap C :PFileChange command! -buffer -nargs=0 PFileLaunch :call LaunchCurrentFile() nnoremap A :PFileLaunch endfunction " }}} function! s:SetupDiff() " {{{ setlocal ft=diff endfunction " }}} function! s:SetupSelectItem() " {{{ nnoremap D :PItemDelete nnoremap O :PItemOpen nnoremap :PItemDescribe nnoremap <2-LeftMouse> :PItemDescribe command! -buffer -nargs=0 PItemDescribe :call OpenCurrentItem(1) command! -buffer -nargs=0 PItemOpen :call OpenCurrentItem(0) command! -buffer -nargs=0 PItemDelete :call DeleteCurrentItem() cnoremap =GetCurrentItem() 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("\") 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 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 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: " (?:) " " (?:) 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('W', \ [0]+b:p4Options+[b:p4Command]+b:p4CmdOptions+split(, \ '\s')) command! -buffer -nargs=* WQ :call call('W', \ [1]+b:p4Options+[b:p4Command]+b:p4CmdOptions+split(, \ '\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 q q endif endif if index(s:navigateCmds, s:p4Command) != -1 nnoremap :call NavigateBack() nnoremap :call NavigateBack() nnoremap :call NavigateForward() 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., &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! \e'".cmdLine."'\" " 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', "") return '' endif finally call s:PopP4Context(0) endtry if match(fileStatusStr, ' - file(s) not in client view\.') >= 0 call setbufvar(bufNr, 'p4RulerErr', "") " Required for optimizing out in future runs. call setbufvar(bufNr, 'p4HeadRev', '') return '' elseif match(fileStatusStr, ' - no such file(s).') >= 0 call setbufvar(bufNr, 'p4RulerErr', "") " 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', '') 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 == '' 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('') + 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 nested :call CancelEdit(1) au CursorMoved nested :call CancelEdit(1) elseif a:stage == 1 stopinsert silent undo setl readonly endif aug END endfunction function! perforce#FileChangedShell() let bufNr = expand("") + 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\)\@$", '%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 | " Ex: PF -c hari integrate -b branch -s 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, '\\\@' 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, '^ \+\|\%(\\\@ or // 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, \ '\(\*\|\%(\.\)\@ 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, '\\\@ 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) =~# '\TestParseOptions() 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('') + 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 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