1 " perforce.vim: Please see plugin/perforce.vim
3 " Make sure line-continuations won't cause any problem. This will be restored
9 """ BEGIN: Initializations {{{
11 " Determine the script id.
12 function! s:MyScriptId()
14 let s:sid = maparg("<SID>xx")
16 return substitute(s:sid, "xx$", "", "")
18 let s:myScriptId = s:MyScriptId()
19 delfunction s:MyScriptId " This is not needed anymore.
21 """ BEGIN: One-time initialization of some script variables {{{
23 let s:lastMsgGrp = 'None'
24 " Indicates the current recursion level for executing p4 commands.
27 if genutils#OnMS() && match(&shell, '\<bash\>') != -1
28 " When using cygwin bash with native vim, p4 gets confused by the PWD, which
30 let s:p4CommandPrefix = "unset PWD && "
32 let s:p4CommandPrefix = ""
35 " Special characters in a filename that are not acceptable in a filename (as a
36 " window title) on windows.
37 let s:specialChars = '\([*:?"<>|]\)'
38 let s:specialCharsMap = {
49 " A lot of metadata on perforce command syntax and handling.
52 let s:p4KnownCmds = split('add,admin,annotate,branch,branches,change,changes,' .
53 \ 'client,clients,counter,counters,delete,depot,depots,describe,diff,' .
54 \ 'diff2,dirs,edit,filelog,files,fix,fixes,flush,fstat,get,group,' .
55 \ 'groups,have,help,info,integ,integrate,integrated,job,jobs,jobspec,' .
56 \ 'label,labels,labelsync,lock,logger,login,monitor,obliterate,opened,' .
57 \ 'passwd,print,protect,rename,reopen,resolve,resolved,revert,review,' .
58 \ 'reviews,set,submit,sync,triggers,typemap,unlock,user,users,verify,' .
59 \ 'where,workspaces,', ',')
60 " Add some built-in commands to this list.
61 let s:builtinCmds = split('vdiff,vdiff2,exec,', ',')
62 let s:allCommands = s:p4KnownCmds + s:builtinCmds
63 let s:p4KnownCmdsCompStr = ''
65 " Map between the option and the commands that reqire us to pass an argument
67 let s:p4OptCmdMap = {}
68 let s:p4OptCmdMap['b'] = split('diff2,integrate', ',')
69 let s:p4OptCmdMap['c'] = split('add,delete,edit,fix,fstat,integrate,lock,' .
70 \ 'opened,reopen,r[ver],review,reviews,submit,unlock', ',')
71 let s:p4OptCmdMap['e'] = ['jobs']
72 let s:p4OptCmdMap['j'] = ['fixes']
73 let s:p4OptCmdMap['l'] = ['labelsync']
74 let s:p4OptCmdMap['m'] = split('changes,filelog,jobs', ',')
75 let s:p4OptCmdMap['o'] = ['print']
76 let s:p4OptCmdMap['s'] = split('changes,integrate', ',')
77 let s:p4OptCmdMap['t'] = split('add,client,edit,label,reopen', ',')
78 let s:p4OptCmdMap['O'] = ['passwd']
79 let s:p4OptCmdMap['P'] = ['passwd']
80 let s:p4OptCmdMap['S'] = ['set']
82 " These built-in options require us to pass an argument. These options start
84 let s:biOptCmdMap = {}
85 let s:biOptCmdMap['c'] = ['diff']
87 " Map the commands with short name to their long versions.
88 let s:shortCmdMap = {}
89 let s:shortCmdMap['p'] = 'print'
90 let s:shortCmdMap['d'] = 'diff'
91 let s:shortCmdMap['e'] = 'edit'
92 let s:shortCmdMap['a'] = 'add'
93 let s:shortCmdMap['r'] = 'revert'
94 let s:shortCmdMap['g'] = 'get'
95 let s:shortCmdMap['o'] = 'open'
96 let s:shortCmdMap['d2'] = 'diff2'
97 let s:shortCmdMap['h'] = 'help'
100 " NOTE: The current file is used as the default argument, only when the
101 " command is not one of the s:askUserCmds and it is not one of
102 " s:curFileNotDefCmds or s:nofileArgsCmds.
103 " For these commands, we don't need to default to the current file, as these
104 " commands can work without any arguments.
105 let s:curFileNotDefCmds = split('change,changes,client,files,integrate,job,' .
106 \ 'jobs,jobspec,labels,labelsync,opened,resolve,submit,user,', ',')
107 " For these commands, we need to ask user for the argument, as we can't assume
108 " the current file is the default.
109 let s:askUserCmds = split('admin,branch,counter,depot,fix,group,label,', ',')
110 " A subset of askUserCmds, that should use a more generic prompt.
111 let s:genericPromptCmds = split('admin,counter,fix,', ',')
112 " Commands that essentially display a list of files.
113 let s:filelistCmds = split('files,have,integrate,opened,', ',')
114 " Commands that work with a spec.
115 let s:specCmds = split('branch,change,client,depot,group,job,jobspec,label,' .
116 \ 'protect,submit,triggers,typemap,user,', ',')
117 " Out of the above specCmds, these are the only commands that don't
118 " support '-o' option. Consequently we have to have our own template.
119 let s:noOutputCmds = ['submit']
120 " The following are used only to create a specification, not to view them.
121 " Consequently, they don't accept a '-d' option to delete the spec.
122 let s:specOnlyCmds = split('jobspec,submit,', ',')
123 " These commands might change the fstat of files, requiring an update on some
124 " or all the buffers loaded into vim.
125 "let s:statusUpdReqCmds = 'add,delete,edit,get,lock,reopen,revert,sync,unlock,'
126 "" For these commands we need to call :checktime, as the command might have
127 "" changed the state of the file.
128 "let s:checktimeReqCmds = 'edit,get,reopen,revert,sync,'
129 " For these commands, we can even set 'autoread' along with doing a :checktime.
130 let s:autoreadCmds = split('edit,get,reopen,revert,sync,', ',')
131 " These commands don't expect filename arguments, so no special processing for
133 let s:nofileArgsCmds = split('branch,branches,change,client,clients,counters,' .
134 \ 'depot,depots,describe,dirs,group,groups,help,info,job,jobspec,label,' .
135 \ 'logger,passwd,protect,rename,review,triggers,typemap,user,users,', ',')
136 " For these commands, the output should not be set to perforce type.
137 let s:ftNotPerforceCmds = split('diff,diff2,print,vdiff,vdiff2', ',')
138 " Allows navigation keys in the command window.
139 let s:navigateCmds = ['help']
140 " These commands accept a '-m' argument to limit the list size.
141 let s:limitListCmds = split('filelog,jobs,changes,', ',')
142 " These commands take the diff option -dx.
143 let s:diffCmds = split('describe,diff,diff2,', ',')
144 " The following commands prefer dialog output. If the output exceeds
145 " g:p4MaxLinesInDialog, we should switch to showing the output in a window.
146 let s:dlgOutputCmds =
147 \ split('add,delete,edit,get,lock,reopen,revert,sync,unlock,', ',')
149 " If there is a confirm message, then PFIF() will prompt user before
150 " continuing with the run.
151 let s:confirmMsgs{'revert'} = "Reverting file(s) will overwrite any edits to " .
152 \ "the files(s)\n Do you want to continue?"
153 let s:confirmMsgs{'submit'} = "This will commit the changelist to the depot." .
154 \ "\n Do you want to continue?"
156 " Settings that are not directly exposed to the user. These can be accessed
157 " using the public API.
158 " Refresh the contents of perforce windows, even if the window is already open.
159 let s:refreshWindowsAlways = 1
161 " List of the global variable names of the user configurable settings.
162 let s:settings = split('ClientRoot,CmdPath,Presets,' .
163 \ 'DefaultOptions,DefaultDiffOptions,EnableMenu,EnablePopupMenu,' .
164 \ 'UseExpandedMenu,UseExpandedPopupMenu,EnableRuler,RulerWidth,' .
165 \ 'DefaultListSize,EnableActiveStatus,OptimizeActiveStatus,' .
166 \ 'ASIgnoreDefPattern,ASIgnoreUsrPattern,PromptToCheckout,' .
167 \ 'CheckOutDefault,UseGUIDialogs,MaxLinesInDialog,SortSettings,' .
168 \ 'TempDir,SplitCommand,UseVimDiff2,EnableFileChangedShell,' .
169 \ 'BufHidden,Depot,Autoread,UseClientViewMap,DefaultPreset', ',')
170 let s:settingsCompStr = ''
172 let s:helpWinName = 'P4\ help'
175 let s:SPACE_AS_SEP = genutils#CrUnProtectedCharsPattern(' ')
176 let s:EMPTY_STR = '^\_s*$'
178 if !exists('s:p4Client') || s:p4Client =~# s:EMPTY_STR
179 let s:p4Client = $P4CLIENT
181 if !exists('s:p4User') || s:p4User =~# s:EMPTY_STR
182 if exists("$P4USER") && $P4USER !~# s:EMPTY_STR
183 let s:p4User = $P4USER
184 elseif genutils#OnMS() && exists("$USERNAME")
185 let s:p4User = $USERNAME
186 elseif exists("$LOGNAME")
187 let s:p4User = $LOGNAME
188 elseif exists("$USERNAME") " Happens if you are on cygwin too.
189 let s:p4User = $USERNAME
194 if !exists('s:p4Port') || s:p4Port =~# s:EMPTY_STR
195 let s:p4Port = $P4PORT
197 let s:p4Password = $P4PASSWD
199 let s:CM_RUN = 'run' | let s:CM_FILTER = 'filter' | let s:CM_DISPLAY = 'display'
200 let s:CM_PIPE = 'pipe'
202 let s:changesExpr = "matchstr(getline(\".\"), '" .
203 \ '^Change \zs\d\+\ze ' . "')"
204 let s:branchesExpr = "matchstr(getline(\".\"), '" .
205 \ '^Branch \zs[^ ]\+\ze ' . "')"
206 let s:labelsExpr = "matchstr(getline(\".\"), '" .
207 \ '^Label \zs[^ ]\+\ze ' . "')"
208 let s:clientsExpr = "matchstr(getline(\".\"), '" .
209 \ '^Client \zs[^ ]\+\ze ' . "')"
210 let s:usersExpr = "matchstr(getline(\".\"), '" .
211 \ '^[^ ]\+\ze <[^@>]\+@[^>]\+> ([^)]\+)' . "')"
212 let s:jobsExpr = "matchstr(getline(\".\"), '" .
213 \ '^[^ ]\+\ze on ' . "')"
214 let s:depotsExpr = "matchstr(getline(\".\"), '" .
215 \ '^Depot \zs[^ ]\+\ze ' . "')"
216 let s:describeExpr = 's:DescribeGetCurrentItem()'
217 let s:filelogExpr = 's:GetCurrentDepotFile(line("."))'
218 let s:groupsExpr = 'expand("<cword>")'
220 let s:fileBrowseExpr = 's:ConvertToLocalPath(s:GetCurrentDepotFile(line(".")))'
221 let s:openedExpr = s:fileBrowseExpr
222 let s:filesExpr = s:fileBrowseExpr
223 let s:haveExpr = s:fileBrowseExpr
224 let s:integrateExpr = s:fileBrowseExpr
225 " Open in describe window should open the local file.
226 let s:describeOpenItemExpr = s:fileBrowseExpr
228 " If an explicit handler is defined, then it will override the default rule of
229 " finding the command with the singular form.
230 let s:filelogItemHandler = "s:printHdlr"
231 let s:changesItemHandler = "s:changeHdlr"
232 let s:openedItemHandler = "s:OpenFile"
233 let s:describeItemHandler = "s:OpenFile"
234 let s:filesItemHandler = "s:OpenFile"
235 let s:haveItemHandler = "s:OpenFile"
237 " Define handlers for built-in commands. These have no arguments, they will
238 " use the existing parsed command-line vars. Set s:errCode on errors.
239 let s:builtinCmdHandler{'vdiff'} = 's:VDiffHandler'
240 let s:builtinCmdHandler{'vdiff2'} = 's:VDiff2Handler'
241 let s:builtinCmdHandler{'exec'} = 's:ExecHandler'
243 let s:vdiffCounter = 0
245 " A stack of contexts.
246 let s:p4Contexts = []
248 " Cache of client view mappings, with client name as the key.
249 let s:fromDepotMapping = {}
250 let s:toDepotMapping = {}
252 aug Perforce | aug END " Define autocommand group.
253 call genutils#AddToFCShellPre('perforce#FileChangedShell')
255 """ END: One-time initialization of some script variables }}}
257 """ END: Initializations }}}
260 """ BEGIN: Command specific functions {{{
262 function! s:printHdlr(scriptOrigin, outputType, ...)
263 let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'print']
268 " The first line printed by p4 for non-q operation causes vim to misjudge
270 if getline(1) =~# '//[^#]\+#\d\+ - '
272 let firstLine = getline(1)
277 doautocmd filetypedetect BufNewFile
278 " If automatic detection doesn't work...
280 let &ft=s:GuessFileTypeForCurrentWindow()
283 if exists('firstLine')
284 silent! 1put! =firstLine
285 setlocal nomodifiable
293 function! s:describeHdlr(scriptOrigin, outputType, ...)
295 call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'describe']+a:000)
297 " If -s doesn't exist, and user doesn't intent to see a diff, then let us
298 " add -s option. In any case he can press enter on the <SHOW DIFFS> to see
300 if index(s:p4CmdOptions, '-s') == -1 &&
301 \ s:indexMatching(s:p4CmdOptions, '^-d.\+$') == -1
302 call add(s:p4CmdOptions, '-s')
303 let s:p4WinName = s:MakeWindowName() " Adjust window name.
306 let retVal = perforce#PFIF(2, a:outputType, 'describe')
307 if s:StartBufSetup() && getline(1) !~# ' - no such changelist'
308 call s:SetupFileBrowse()
309 if index(s:p4CmdOptions, '-s') != -1
311 silent! 2,$g/^Change \d\+ by \|\%$/
312 \ call append(line('.')-1, ['', "\t<SHOW DIFFS>", ''])
313 setlocal nomodifiable
323 function! s:diffHdlr(scriptOrigin, outputType, ...)
325 call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'diff']+a:000)
328 " If a change number is specified in the diff, we need to handle it
329 " ourselves, as p4 doesn't understand this.
330 let changeOptIdx = index(s:p4CmdOptions, '++c')
332 if changeOptIdx != -1 " If a change no. is specified.
333 let changeNo = s:p4CmdOptions[changeOptIdx+1]
334 call s:PushP4Context()
336 call extend(s:p4Options, ['++T', '++N'], 0) " Testmode.
337 let retVal = perforce#PFIF(2, a:outputType, 'diff') " Opens window.
340 exec '%PF ++f opened -c' changeNo
343 let cntxtStr = s:PopP4Context()
346 " Any + option is treated like a signal to run external diff.
347 let externalDiffOptExists = (s:indexMatching(s:p4CmdOptions, '^+\S\+$') != -1)
348 if externalDiffOptExists
349 if len(s:p4Arguments) > 1
350 return s:SyntaxError('External diff options can not be used with multiple files.')
354 let _p4Options = copy(s:p4Options)
355 call insert(s:p4Options, '++T', 0) " Testmode, just open the window.
356 let retVal = perforce#PFIF(2, 0, 'diff')
357 let s:p4Options = _p4Options
361 call s:PushP4Context() | let needsPop = 1
365 let fileName = s:ConvertToLocalPath(s:p4Arguments[0])
366 call s:PeekP4Context()
367 " Gather and process only external options.
369 " '-x +width=10 -du -y +U=20 -z -a -db +tabsize=4'
371 " '--width=10 -U 20 --tabsize=4'
373 for opt in s:p4CmdOptions
375 call add(diffOpts, substitute(opt, '^+\([^= ]\+\)=\(.*\)$',
376 \ '\=(strlen(submatch(1)) > 1 ? '.
377 \ '("--".submatch(1).'.
378 \ '(submatch(2) != "" ? "=".submatch(2) : "")) : '.
379 \ '("-".submatch(1).'.
380 \ '(submatch(2) != "" ? " ".submatch(2) : "")))',
384 if getbufvar(bufnr('#'), '&ff') ==# 'dos'
388 \ genutils#EscapeCommand('diff', diffOpts+['--', '-', fileName],
391 call s:EchoMessage('Error executing external diff command. '.
392 \ 'Verify that GNU (or a compatible) diff is in your path.',
396 call genutils#SilentSubstitute("\<CR>$", '%s///')
397 call genutils#SilentSubstitute('^--- -', '1s;;--- '.
398 \ s:ConvertToDepotPath(fileName))
402 setlocal nomodifiable
404 call s:PopP4Context()
408 let retVal = perforce#PFIF(2, exists('$P4DIFF') ? 5 : a:outputType, 'diff')
415 if changeNo != '' && getline(1) !~# 'ile(s) not opened on this client\.'
417 call genutils#SilentSubstitute('#.*', '%s///e')
418 call s:SetP4ContextVars(cntxtStr) " Restore original diff context.
419 call perforce#PFIF(1, 0, '-x', '-', '++f', '++n', 'diff')
428 function! s:diff2Hdlr(scriptOrigin, outputType, ...)
430 call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'diff2']+a:000)
433 let s:p4Arguments = s:GetDiff2Args()
435 let retVal = perforce#PFIF(2, exists('$P4DIFF') ? 5 : a:outputType, 'diff2')
444 function! s:passwdHdlr(scriptOrigin, outputType, ...)
446 call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'passwd']+a:000)
450 if index(s:p4CmdOptions, '-O') == -1
451 let oldPasswd = input('Enter old password: ')
452 " FIXME: Handle empty passwords.
453 call add(add(s:p4CmdOptions, '-O'), oldPasswd)
456 if index(s:p4CmdOptions, '-P') == -1
458 let newPasswd = input('Enter new password: ')
459 if (input('Re-enter new password: ') != newPasswd)
460 call s:EchoMessage("Passwords don't match", 'Error')
462 " FIXME: Handle empty passwords.
463 call add(add(s:p4CmdOptions, '-P'), newPasswd)
468 let retVal = perforce#PFIF(2, a:outputType, 'passwd')
472 " Only to avoid confirming for -n and -a options.
473 function! s:revertHdlr(scriptOrigin, outputType, ...)
475 call call('s:ParseOptionsIF', [1, line('$'), 1,
476 \ a:outputType, 'passwd']+a:000)
479 if index(s:p4CmdOptions, '-n') != -1 || index(s:p4CmdOptions, '-a') != -1
480 call add(s:p4Options, '++y')
482 let retVal = perforce#PFIF(2, a:outputType, 'revert')
486 function! s:changeHdlrImpl(outputType)
487 let _p4Arguments = s:p4Arguments
488 " If argument(s) is not a number...
489 if len(s:p4Arguments) != 0 && s:indexMatching(s:p4Arguments, '^\d\+$') == -1
490 let s:p4Arguments = [] " Let a new changelist be created.
492 let retVal = perforce#PFIF(2, a:outputType, 'change')
493 let s:p4Arguments = _p4Arguments
494 if s:errCode == 0 && s:indexMatching(s:p4Arguments, '^\d\+$') == -1
495 \ && (s:StartBufSetup() || s:commandMode ==# s:CM_FILTER)
496 if len(s:p4Arguments) != 0
497 if search('^Files:\s*$', 'w') && line('.') != line('$')
499 call s:PushP4Context()
501 call call('perforce#PFrangeIF', [line("."), line("$"), 1, 0]+
502 \ s:p4Options+['++f', 'opened', '-c', 'default']+
505 call s:PopP4Context()
509 call genutils#SilentSubstitute('^', '.,$s//\t/e')
510 call genutils#SilentSubstitute('#\d\+ - \(\S\+\) .*$',
518 if len(s:p4Arguments) != 0 && &cmdheight > 1
519 " The message about W and WQ must have gone by now.
520 redraw | call perforce#LastMessage()
523 " Save the filelist in this changelist so that we can update their status
525 if search('Files:\s*$', 'w')
526 let b:p4OrgFilelist = getline(line('.')+1, line('$'))
532 function! s:changeHdlr(scriptOrigin, outputType, ...)
534 call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'change']+a:000)
536 let retVal = s:changeHdlrImpl(a:outputType)
538 command! -buffer -nargs=* PChangeSubmit :call call('<SID>W',
539 \ [0]+b:p4Options+['submit']+split(<q-args>, '\s'))
546 " Create a template for submit.
547 function! s:submitHdlr(scriptOrigin, outputType, ...)
549 call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'submit']+a:000)
552 if index(s:p4CmdOptions, '-c') != -1
554 let retVal = perforce#PFIF(2, a:outputType, 'submit')
556 call s:PushP4Context()
558 " This is done just to get the :W and :WQ commands defined properly and
559 " open the window with a proper name. The actual job is done by the call
560 " to s:changeHdlrImpl() which is then run in filter mode to avoid the
561 " side-effects (such as :W and :WQ getting overwritten etc.)
562 call extend(s:p4Options, ['++y', '++T'], 0) " Don't confirm, and testmode.
563 call perforce#PFIF(2, 0, 'submit')
565 call s:PeekP4Context()
566 let s:p4CmdOptions = [] " These must be specific to 'submit'.
567 let s:p4Command = 'change'
568 let s:commandMode = s:CM_FILTER | let s:filterRange = '.'
569 let retVal = s:changeHdlrImpl(a:outputType)
576 call s:PopP4Context()
580 command! -buffer -nargs=* PSubmitPostpone :call call('<SID>W',
581 \ [0]+b:p4Options+['change']+split(<q-args>, '\s'))
582 set ft=perforce " Just to get the cursor placement right.
587 call s:EchoMessage("Error creating submission template.", 'Error')
593 function! s:resolveHdlr(scriptOrigin, outputType, ...)
595 call call('s:ParseOptionsIF', [1, line('$'), 1, a:outputType, 'resolve']+a:000)
598 if (s:indexMatching(s:p4CmdOptions, '^-a[fmsty]$') == -1) &&
599 \ (index(s:p4CmdOptions, '-n') == -1)
600 return s:SyntaxError("Interactive resolve not implemented (yet).")
602 let retVal = perforce#PFIF(2, a:outputType, 'resolve')
606 function! s:filelogHdlr(scriptOrigin, outputType, ...)
607 let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'filelog']+a:000)
610 " No meaning for delete.
611 silent! nunmap <buffer> D
612 silent! delcommand PItemDelete
613 command! -range -buffer -nargs=0 PFilelogDiff
614 \ :call s:FilelogDiff2(<line1>, <line2>)
615 vnoremap <silent> <buffer> D :PFilelogDiff<CR>
616 command! -buffer -nargs=0 PFilelogPrint :call perforce#PFIF(0, 0, 'print',
617 \ <SID>GetCurrentItem())
618 nnoremap <silent> <buffer> p :PFilelogPrint<CR>
619 command! -buffer -nargs=0 PFilelogSync :call <SID>FilelogSyncToCurrentItem()
620 nnoremap <silent> <buffer> S :PFilelogSync<CR>
621 command! -buffer -nargs=0 PFilelogDescribe
622 \ :call <SID>FilelogDescribeChange()
623 nnoremap <silent> <buffer> C :PFilelogDescribe<CR>
629 function! s:clientsHdlr(scriptOrigin, outputType, ...)
630 let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'clients']+a:000)
633 command! -buffer -nargs=0 PClientsTemplate
634 \ :call perforce#PFIF(0, 0, '++A', 'client', '-t', <SID>GetCurrentItem())
635 nnoremap <silent> <buffer> P :PClientsTemplate<CR>
642 function! s:changesHdlr(scriptOrigin, outputType, ...)
643 let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'changes']+a:000)
646 command! -buffer -nargs=0 PItemDescribe
647 \ :call <SID>PChangesDescribeCurrentItem()
648 command! -buffer -nargs=0 PChangesSubmit
649 \ :call <SID>ChangesSubmitChangeList()
650 nnoremap <silent> <buffer> S :PChangesSubmit<CR>
651 command! -buffer -nargs=0 PChangesOpened
652 \ :if getline('.') =~# " \\*pending\\* '" |
653 \ call perforce#PFIF(1, 0, 'opened', '-c', <SID>GetCurrentItem()) |
655 nnoremap <silent> <buffer> o :PChangesOpened<CR>
656 command! -buffer -nargs=0 PChangesDiff
657 \ :if getline('.') =~# " \\*pending\\* '" |
658 \ call perforce#PFIF(0, 0, 'diff', '++c', <SID>GetCurrentItem()) |
660 \ call perforce#PFIF(0, 0, 'describe', (<SID>('DefaultDiffOptions')
661 \ =~ '^\s*$' ? '-dd' : <SID>('DefaultDiffOptions')),
662 \ <SID>GetCurrentItem()) |
664 nnoremap <silent> <buffer> d :PChangesDiff<CR>
665 command! -buffer -nargs=0 PItemOpen
666 \ :if getline('.') =~# " \\*pending\\* '" |
667 \ call perforce#PFIF(0, 0, 'change', <SID>GetCurrentItem()) |
669 \ call perforce#PFIF(0, 0, 'describe', '-dd', <SID>GetCurrentItem()) |
676 function! s:labelsHdlr(scriptOrigin, outputType, ...)
677 let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'labels']+a:000)
680 command! -buffer -nargs=0 PLabelsSyncClient
681 \ :call <SID>LabelsSyncClientToLabel()
682 nnoremap <silent> <buffer> S :PLabelsSyncClient<CR>
683 command! -buffer -nargs=0 PLabelsSyncLabel
684 \ :call <SID>LabelsSyncLabelToClient()
685 nnoremap <silent> <buffer> C :PLabelsSyncLabel<CR>
686 command! -buffer -nargs=0 PLabelsFiles :call perforce#PFIF(0, 0, '++n', 'files',
687 \ '//...@'. <SID>GetCurrentItem())
688 nnoremap <silent> <buffer> I :PLabelsFiles<CR>
689 command! -buffer -nargs=0 PLabelsTemplate :call perforce#PFIF(0, 0, '++A',
690 \ 'label', '-t', <SID>GetCurrentItem())
691 nnoremap <silent> <buffer> P :PLabelsTemplate<CR>
698 function! s:helpHdlr(scriptOrigin, outputType, ...)
699 call genutils#SaveWindowSettings2("PerforceHelp", 1)
700 " If there is a help window already open, then we need to reuse it.
701 let helpWin = bufwinnr(s:helpWinName)
702 let retVal = call('perforce#PFIF', [a:scriptOrigin + 1, a:outputType, 'help']+a:000)
705 command! -buffer -nargs=0 PHelpSelect
706 \ :call perforce#PFIF(0, 0, 'help', expand("<cword>"))
707 nnoremap <silent> <buffer> <CR> :PHelpSelect<CR>
708 nnoremap <silent> <buffer> K :PHelpSelect<CR>
709 nnoremap <silent> <buffer> <2-LeftMouse> :PHelpSelect<CR>
710 call genutils#AddNotifyWindowClose(s:helpWinName, s:myScriptId .
712 if helpWin == -1 " Resize only when it was not already visible.
716 \ "Press <CR>/K/<2-LeftMouse> to drilldown on perforce help keywords."
723 " Built-in command handlers {{{
724 function! s:VDiffHandler()
725 let nArgs = len(s:p4Arguments)
727 return s:SyntaxError("vdiff: Too many arguments.")
733 let firstFile = s:p4Arguments[0]
734 let secondFile = s:p4Arguments[1]
736 let secondFile = s:p4Arguments[0]
738 let secondFile = s:EscapeFileName(s:GetCurFileName())
741 let firstFile = s:ConvertToDepotPath(secondFile)
743 call s:VDiffImpl(firstFile, secondFile, 0)
746 function! s:VDiff2Handler()
747 if len(s:p4Arguments) > 2
748 return s:SyntaxError("vdiff2: Too many arguments")
751 let s:p4Arguments = s:GetDiff2Args()
752 call s:VDiffImpl(s:p4Arguments[0], s:p4Arguments[1], 1)
755 function! s:VDiffImpl(firstFile, secondFile, preferDepotPaths)
756 let firstFile = a:firstFile
757 let secondFile = a:secondFile
759 if a:preferDepotPaths || s:PathRefersToDepot(firstFile)
760 let firstFile = s:ConvertToDepotPath(firstFile)
761 let tempFile1 = s:MakeTempName(firstFile)
763 let tempFile1 = firstFile
765 if a:preferDepotPaths || s:PathRefersToDepot(secondFile)
766 let secondFile = s:ConvertToDepotPath(secondFile)
767 let tempFile2 = s:MakeTempName(secondFile)
769 let tempFile2 = secondFile
771 if firstFile =~# s:EMPTY_STR || secondFile =~# s:EMPTY_STR ||
772 \ (tempFile1 ==# tempFile2)
773 return s:SyntaxError("diff requires two distinct files as arguments.")
776 let s:vdiffCounter = s:vdiffCounter + 1
778 if s:IsDepotPath(firstFile)
779 let s:p4Command = 'print'
780 let s:p4CmdOptions = ['-q']
781 let s:p4WinName = tempFile1
782 let s:p4Arguments = [firstFile]
783 call perforce#PFIF(2, 0, 'print')
789 silent! exec 'split' firstFile
791 return s:ShowVimError("Error opening file: ".firstFile."\n".v:errmsg, '')
795 let w:p4VDiffWindow = s:vdiffCounter
798 " CAUTION: If there is a buffer or window local value, then this will get
799 " overridden, but it is OK.
800 if exists('t:p4SplitCommand')
801 let _splitCommand = t:p4SplitCommand
803 let t:p4SplitCommand = 'vsplit'
804 let _splitright = &splitright
807 if s:IsDepotPath(secondFile)
808 let s:p4Command = 'print'
809 let s:p4CmdOptions = ['-q']
810 let s:p4WinName = tempFile2
811 let s:p4Arguments = [secondFile]
812 call perforce#PFIF(2, 0, 'print')
818 silent! exec 'vsplit' secondFile
820 return s:ShowVimError("Error opening file: ".secondFile."\n".v:errmsg, '')
824 if exists('_splitCommand')
825 let t:p4SplitCommand = _splitCommand
827 unlet t:p4SplitCommand
829 let &splitright = _splitright
832 let w:p4VDiffWindow = s:vdiffCounter
836 " Returns a fileName in the temp directory that is unique for the branch and
837 " revision specified in the fileName.
838 function! s:MakeTempName(filePath)
839 let depotPath = s:ConvertToDepotPath(a:filePath)
840 if depotPath =~# s:EMPTY_STR
843 let tmpName = s:_('TempDir') . '/'
844 let branch = s:GetBranchName(depotPath)
845 if branch !~# s:EMPTY_STR
846 let tmpName = tmpName . branch . '-'
848 let revSpec = s:GetRevisionSpecifier(depotPath)
849 if revSpec !~# s:EMPTY_STR
850 let tmpName = tmpName . substitute(strpart(revSpec, 1), '/', '_', 'g') . '-'
852 return tmpName . fnamemodify(substitute(a:filePath, '\\*#\d\+$', '', ''),
856 function! s:ExecHandler()
857 if len(s:p4Arguments) != 0
858 echo join(s:p4Arguments, ' ')
860 if s:p4Arguments[0] =~# '^!'
862 " FIXME: Pipe itself needs to be escaped, and they could be chained.
863 let cmd = genutils#EscapeCommand(substitute(s:p4Arguments[0], '^!', '',
864 \ ''), s:p4Arguments[1:], s:p4Pipe)
866 let cmd = join(s:p4Arguments, ' ')
868 let cmd = genutils#Escape(cmd, '#%!')
870 exec (cmdHasBang ? '!' : '').cmd
872 let v:errmsg = substitute(v:exception, '^[^:]\+:', '', '')
873 call s:ShowVimError(v:errmsg, v:throwpoint)
878 " Built-in command handlers }}}
880 """ END: Command specific functions }}}
883 """ BEGIN: Helper functions {{{
885 " Open a file from an alternative codeline.
886 " If mode == 0, first file is opened and all other files are added to buffer
888 " If mode == 1, the files are not really opened, the list is just returned.
889 " If mode == 2, it behaves the same as mode == 0, except that the file is
891 " If there are no arguments passed, user is prompted to enter. He can then
892 " enter a codeline followed by a list of filenames.
893 " If only one argument is passed, it is assumed to be the codeline and the
894 " current filename is assumed (user is not prompted).
895 function! perforce#PFOpenAltFile(mode, ...) " {{{
896 let argList = copy(a:000)
899 " Prompt for codeline string (codeline optionally followed by filenames).
900 let codelineStr = s:PromptFor(0, s:_('UseGUIDialogs'),
901 \ "Enter the alternative codeline string: ", '')
902 if codelineStr =~# s:EMPTY_STR
905 let argList = split(codelineStr, s:SPACE_AS_SEP)
908 call add(argList, s:EscapeFileName(s:GetCurFileName()))
912 let altFileNames = call('s:PFGetAltFiles', ['']+argList)
913 if a:mode == 0 || a:mode == 2
915 for altFileName in altFileNames
917 execute ((a:mode == 0) ? ":edit " : ":split ") . altFileName
920 execute ":badd " . altFileName
924 return join(altFileNames, ' ')
928 " Interactively change the port/client/user. {{{
929 function! perforce#SwitchPortClientUser()
930 let p4Port = s:PromptFor(0, s:_('UseGUIDialogs'), "Port: ", s:_('p4Port'))
931 let p4Client = s:PromptFor(0, s:_('UseGUIDialogs'), "Client: ", s:_('p4Client'))
932 let p4User = s:PromptFor(0, s:_('UseGUIDialogs'), "User: ", s:_('p4User'))
933 call perforce#PFSwitch(1, p4Port, p4Client, p4User)
936 " No args: Print presets and prompt user to select a preset.
937 " Number: Select that numbered preset.
938 " port [client] [user]: Set the specified settings.
939 function! perforce#PFSwitch(updateClientRoot, ...)
940 if a:0 == 0 || match(a:1, '^\d\+$') == 0
942 let presets = split(s:_('Presets'), ',')
945 call s:EchoMessage("No presets to select from.", 'Error')
949 let selPreset = genutils#PromptForElement(presets, -1,
950 \ "Select the setting: ", -1, s:_('UseGUIDialogs'), 1)
953 if index >= len(presets)
954 call s:EchoMessage("Not that many presets.", 'Error')
957 let selPreset = presets[index]
962 let argList = split(selPreset, s:SPACE_AS_SEP)
965 let argList = split(a:1, ' ')
970 call call('s:PSwitchHelper', [a:updateClientRoot]+argList)
972 " Loop through all the buffers and invalidate the filestatuses.
973 let lastBufNr = bufnr('$')
976 if bufexists(i) && getbufvar(i, '&buftype') == ''
977 call s:ResetFileStatusForBuffer(i)
983 function! s:PSwitchHelper(updateClientRoot, ...)
985 let p4Client = s:_('p4Client')
986 let p4User = s:_('p4User')
993 if ! s:SetPortClientUser(p4Port, p4Client, p4User)
997 if a:updateClientRoot
998 if s:p4Port !=# 'P4CONFIG'
999 call s:GetClientInfo()
1001 let g:p4ClientRoot = '' " Since the client is chosen dynamically.
1006 function! s:SetPortClientUser(port, client, user)
1007 if s:p4Port ==# a:port && s:p4Client ==# a:client && s:p4User ==# a:user
1011 let s:p4Port = a:port
1012 let s:p4Client = a:client
1013 let s:p4User = a:user
1014 let s:p4Password = ''
1018 function! perforce#PFSwitchComplete(ArgLead, CmdLine, CursorPos)
1019 return substitute(s:_('Presets'), ',', "\n", 'g')
1021 " port/client/user }}}
1023 function! s:PHelpComplete(ArgLead, CmdLine, CursorPos)
1024 if s:p4KnownCmdsCompStr == ''
1025 let s:p4KnownCmdsCompStr = join(s:p4KnownCmds, "\n")
1027 return s:p4KnownCmdsCompStr.
1028 \ "simple\ncommands\nenvironment\nfiletypes\njobview\nrevisions\n".
1032 " Handler for opened command.
1033 function! s:OpenFile(scriptOrigin, outputType, fileName) " {{{
1034 if filereadable(a:fileName)
1035 if a:outputType == 0
1036 let curWin = winnr()
1037 let bufNr = genutils#FindBufferForName(a:fileName)
1038 let winnr = bufwinnr(bufNr)
1040 exec winnr.'wincmd w'
1044 if curWin != winnr() && &previewwindow
1045 wincmd p " Don't use preview window.
1047 " Avoid loosing temporary buffers accidentally.
1048 if winnr() == curWin || getbufvar('%', '&bufhidden') != ''
1051 if winbufnr(winnr()) != bufNr
1053 exec "buffer" bufNr | " Preserves cursor position.
1055 exec "edit " . a:fileName
1059 exec "pedit " . a:fileName
1062 call perforce#PFIF(0, a:outputType, 'print', a:fileName)
1066 function! s:DescribeGetCurrentItem() " {{{
1067 if getline(".") ==# "\t<SHOW DIFFS>"
1068 let [changeHdrLine, col] = searchpos('^Change \zs\d\+ by ', 'bnW')
1069 if changeHdrLine != 0
1070 let changeNo = matchstr(getline(changeHdrLine), '\d\+', col-1)
1071 let _modifiable = &l:modifiable
1074 call genutils#SaveHardPosition('DescribeGetCurrentItem')
1075 exec changeHdrLine.',.PF ++f describe' s:_('DefaultDiffOptions') changeNo
1076 call genutils#RestoreHardPosition('DescribeGetCurrentItem')
1077 call genutils#ResetHardPosition('DescribeGetCurrentItem')
1079 let &l:modifiable = _modifiable
1085 return s:GetCurrentDepotFile(line('.'))
1088 function! s:getCommandItemHandler(outputType, command, args) " {{{
1089 let itemHandler = ""
1090 if exists("s:{a:command}ItemHandler")
1091 let itemHandler = s:{a:command}ItemHandler
1092 elseif match(a:command, 'e\?s$') != -1
1093 let handlerCmd = substitute(a:command, 'e\?s$', '', '')
1094 if exists('*s:{handlerCmd}Hdlr')
1095 let itemHandler = 's:' . handlerCmd . 'Hdlr'
1097 let itemHandler = 'perforce#PFIF'
1100 if itemHandler ==# 'perforce#PFIF'
1101 return "call perforce#PFIF(1, " . a:outputType . ", '" . handlerCmd . "', " .
1103 elseif itemHandler !~# s:EMPTY_STR
1104 return 'call ' . itemHandler . '(0, ' . a:outputType . ', ' . a:args . ')'
1109 function! s:OpenCurrentItem(outputType) " {{{
1110 let curItem = s:GetOpenItem(a:outputType)
1111 if curItem !~# s:EMPTY_STR
1112 let commandHandler = s:getCommandItemHandler(a:outputType, b:p4Command,
1113 \ "'" . curItem . "'")
1114 if commandHandler !~# s:EMPTY_STR
1120 function! s:GetCurrentItem() " {{{
1121 if exists("b:p4Command") && exists("s:{b:p4Command}Expr")
1122 exec "return " s:{b:p4Command}Expr
1127 function! s:GetOpenItem(outputType) " {{{
1128 " For non-preview open.
1129 if exists("b:p4Command") && a:outputType == 0 &&
1130 \ exists("s:{b:p4Command}OpenItemExpr")
1131 exec "return " s:{b:p4Command}OpenItemExpr
1133 return s:GetCurrentItem()
1136 function! s:DeleteCurrentItem() " {{{
1137 let curItem = s:GetCurrentItem()
1138 if curItem !~# s:EMPTY_STR
1139 let answer = s:ConfirmMessage("Are you sure you want to delete " .
1140 \ curItem . "?", "&Yes\n&No", 2, "Question")
1142 let commandHandler = s:getCommandItemHandler(2, b:p4Command,
1143 \ "'-d', '" . curItem . "'")
1144 if commandHandler !~# s:EMPTY_STR
1147 if v:shell_error == ""
1148 call perforce#PFRefreshActivePane()
1154 function! s:LaunchCurrentFile() " {{{
1155 if g:p4FileLauncher =~# s:EMPTY_STR
1156 call s:ConfirmMessage("There was no launcher command configured to launch ".
1157 \ "this item, use g:p4FileLauncher to configure." , "OK", 1, "Error")
1160 let curItem = s:GetCurrentItem()
1161 if curItem !~# s:EMPTY_STR
1162 exec 'silent! !'.g:p4FileLauncher curItem
1166 function! s:FilelogDiff2(line1, line2) " {{{
1170 if line2 < line("$")
1171 let line2 = line2 + 1
1173 let line1 = line1 - 1
1179 let file1 = s:GetCurrentDepotFile(line1)
1180 if file1 !~# s:EMPTY_STR
1181 let file2 = s:GetCurrentDepotFile(line2)
1182 if file2 !~# s:EMPTY_STR && file2 != file1
1183 " file2 will be older than file1.
1184 exec "call perforce#PFIF(0, 0, \"" . (s:_('UseVimDiff2') ? 'vdiff2' : 'diff2') .
1185 \ "\", file2, file1)"
1190 function! s:FilelogSyncToCurrentItem() " {{{
1191 let curItem = s:GetCurrentItem()
1192 if curItem !~# s:EMPTY_STR
1193 let answer = s:ConfirmMessage("Do you want to sync to: " . curItem . " ?",
1194 \ "&Yes\n&No", 2, "Question")
1196 call perforce#PFIF(1, 2, 'sync', curItem)
1201 function! s:ChangesSubmitChangeList() " {{{
1202 let curItem = s:GetCurrentItem()
1203 if curItem !~# s:EMPTY_STR
1204 let answer = s:ConfirmMessage("Do you want to submit change list: " .
1205 \ curItem . " ?", "&Yes\n&No", 2, "Question")
1207 call perforce#PFIF(0, 0, '++y', 'submit', '-c', curItem)
1212 function! s:LabelsSyncClientToLabel() " {{{
1213 let curItem = s:GetCurrentItem()
1214 if curItem !~# s:EMPTY_STR
1215 let answer = s:ConfirmMessage("Do you want to sync client to the label: " .
1216 \ curItem . " ?", "&Yes\n&No", 2, "Question")
1218 let retVal = call('perforce#PFIF', [1, 1, 'sync',
1219 \ '//".s:_('Depot')."/...@'.curItem])
1225 function! s:LabelsSyncLabelToClient() " {{{
1226 let curItem = s:GetCurrentItem()
1227 if curItem !~# s:EMPTY_STR
1228 let answer = s:ConfirmMessage("Do you want to sync label: " . curItem .
1229 \ " to client " . s:_('p4Client') . " ?", "&Yes\n&No", 2, "Question")
1231 let retVal = perforce#PFIF(1, 1, 'labelsync', '-l', curItem)
1237 function! s:FilelogDescribeChange() " {{{
1238 let changeNo = matchstr(getline("."), ' change \zs\d\+\ze ')
1239 if changeNo !~# s:EMPTY_STR
1240 exec "call perforce#PFIF(0, 1, 'describe', changeNo)"
1244 function! s:SetupFileBrowse() " {{{
1245 " For now, assume that a new window is created and we are in the new window.
1246 exec "setlocal includeexpr=P4IncludeExpr(v:fname)"
1248 " No meaning for delete.
1249 silent! nunmap <buffer> D
1250 silent! delcommand PItemDelete
1251 command! -buffer -nargs=0 PFileDiff :call perforce#PFIF(0, 1, 'diff',
1252 \ <SID>GetCurrentDepotFile(line(".")))
1253 nnoremap <silent> <buffer> D :PFileDiff<CR>
1254 command! -buffer -nargs=0 PFileProps :call perforce#PFIF(1, 0, 'fstat', '-C',
1255 \ <SID>GetCurrentDepotFile(line(".")))
1256 nnoremap <silent> <buffer> P :PFileProps<CR>
1257 command! -buffer -nargs=0 PFileLog :call perforce#PFIF(1, 0, 'filelog',
1258 \ <SID>GetCurrentDepotFile(line(".")))
1259 command! -buffer -nargs=0 PFileEdit :call perforce#PFIF(1, 2, 'edit',
1260 \ <SID>GetCurrentItem())
1261 nnoremap <silent> <buffer> I :PFileEdit<CR>
1262 command! -buffer -bar -nargs=0 PFileRevert :call perforce#PFIF(1, 2, 'revert',
1263 \ <SID>GetCurrentItem())
1264 nnoremap <silent> <buffer> R :PFileRevert \| PFRefreshActivePane<CR>
1265 command! -buffer -nargs=0 PFilePrint
1266 \ :if getline('.') !~# '(\%(u\|ux\)binary)$' |
1267 \ call perforce#PFIF(0, 0, 'print',
1268 \ substitute(<SID>GetCurrentDepotFile(line('.')), '#[^#]\+$', '', '').
1270 \ ((getline(".") =~# '#\d\+ - delete change') ?
1271 \ matchstr(getline('.'), '#\zs\d\+\ze - ') - 1 :
1272 \ matchstr(getline('.'), '#\zs\d\+\ze - '))
1275 \ echo 'PFilePrint: Binary file... ignored.' |
1277 nnoremap <silent> <buffer> p :PFilePrint<CR>
1278 command! -buffer -nargs=0 PFileGet :call perforce#PFIF(1, 2, 'sync',
1279 \ <SID>GetCurrentDepotFile(line(".")))
1280 command! -buffer -nargs=0 PFileSync :call perforce#PFIF(1, 2, 'sync',
1281 \ <SID>GetCurrentItem())
1282 nnoremap <silent> <buffer> S :PFileSync<CR>
1283 command! -buffer -nargs=0 PFileChange :call perforce#PFIF(0, 0, 'change',
1284 \ <SID>GetCurrentChangeNumber(line(".")))
1285 nnoremap <silent> <buffer> C :PFileChange<CR>
1286 command! -buffer -nargs=0 PFileLaunch :call <SID>LaunchCurrentFile()
1287 nnoremap <silent> <buffer> A :PFileLaunch<CR>
1290 function! s:SetupDiff() " {{{
1294 function! s:SetupSelectItem() " {{{
1295 nnoremap <buffer> <silent> D :PItemDelete<CR>
1296 nnoremap <buffer> <silent> O :PItemOpen<CR>
1297 nnoremap <buffer> <silent> <CR> :PItemDescribe<CR>
1298 nnoremap <buffer> <silent> <2-LeftMouse> :PItemDescribe<CR>
1299 command! -buffer -nargs=0 PItemDescribe :call <SID>OpenCurrentItem(1)
1300 command! -buffer -nargs=0 PItemOpen :call <SID>OpenCurrentItem(0)
1301 command! -buffer -nargs=0 PItemDelete :call <SID>DeleteCurrentItem()
1302 cnoremap <buffer> <C-X><C-I> <C-R>=<SID>GetCurrentItem()<CR>
1305 function! s:RestoreWindows(dummy) " {{{
1306 call genutils#RestoreWindowSettings2("PerforceHelp")
1309 function! s:NavigateBack() " {{{
1310 call s:Navigate('u')
1311 if line('$') == 1 && getline(1) == ''
1312 call s:NavigateForward()
1316 function! s:NavigateForward() " {{{
1317 call s:Navigate("\<C-R>")
1320 function! s:Navigate(key) " {{{
1321 let _modifiable = &l:modifiable
1324 " Use built-in markers as Vim takes care of remembering and restoring them
1325 " during the undo/redo.
1328 silent! exec "normal" a:key
1330 if line("'t") > 0 && line("'t") <= line('$')
1334 let &l:modifiable = _modifiable
1338 function! s:GetCurrentChangeNumber(lineNo) " {{{
1339 let line = getline(a:lineNo)
1340 let changeNo = matchstr(line, ' - \S\+ change \zs\S\+\ze (')
1341 if changeNo ==# 'default'
1347 function! s:PChangesDescribeCurrentItem() " {{{
1348 let currentChangeNo = s:GetCurrentItem()
1349 if currentChangeNo !~# s:EMPTY_STR
1350 call perforce#PFIF(0, 1, 'describe', '-s', currentChangeNo)
1355 function! perforce#PFSettings(...)
1356 if s:_('SortSettings ')
1357 if exists("s:sortedSettings")
1358 let settings = s:sortedSettings
1360 let settings = sort(s:settings)
1361 let s:sortedSettings = settings
1364 let settings = s:settings
1367 let selectedSetting = a:1
1369 let selectedSetting = genutils#PromptForElement(settings, -1,
1370 \ "Select the setting: ", -1, 0, 3)
1372 if selectedSetting !~# s:EMPTY_STR
1373 let oldVal = s:_(selectedSetting)
1376 echo 'Current value for' selectedSetting.': "'.oldVal.'" New value: "'.
1379 let newVal = input('Current value for ' . selectedSetting . ' is: ' .
1380 \ oldVal . "\nEnter new value: ", oldVal)
1383 let g:p4{selectedSetting} = newVal
1384 call perforce#Initialize(1)
1389 function! perforce#PFSettingsComplete(ArgLead, CmdLine, CursorPos)
1390 if s:settingsCompStr == ''
1391 let s:settingsCompStr = join(s:settings, "\n")
1393 return s:settingsCompStr
1397 function! s:MakeRevStr(ver) " {{{
1399 if a:ver =~# '^[#@&]'
1401 elseif a:ver =~# '^[-+]\?\d\+\>\|^none\>\|^head\>\|^have\>'
1402 let verStr = '#' . a:ver
1403 elseif a:ver !~# s:EMPTY_STR
1404 let verStr = '@' . a:ver
1409 function! s:GetBranchName(fileName) " {{{
1410 if s:IsFileUnderDepot(a:fileName)
1411 " TODO: Need to run where command at this phase.
1412 elseif stridx(a:fileName, '//') == 0
1413 return matchstr(a:fileName, '^//[^/]\+/\zs[^/]\+\ze')
1419 function! s:GetDiff2Args()
1420 let p4Arguments = s:p4Arguments
1421 if len(p4Arguments) < 2
1422 if len(p4Arguments) == 0
1423 let file = s:EscapeFileName(s:GetCurFileName())
1425 let file = p4Arguments[0]
1427 let ver1 = s:PromptFor(0, s:_('UseGUIDialogs'), "Version1? ", '')
1428 let ver2 = s:PromptFor(0, s:_('UseGUIDialogs'), "Version2? ", '')
1429 let p4Arguments = [file.s:MakeRevStr(ver1), file.s:MakeRevStr(ver2)]
1434 function! perforce#ToggleCheckOutPrompt(interactive)
1437 if g:p4PromptToCheckout
1438 let g:p4PromptToCheckout = 0
1440 let g:p4PromptToCheckout = 1
1441 au FileChangedRO * nested :call <SID>CheckOutFile()
1445 echomsg "PromptToCheckout is now " . ((g:p4PromptToCheckout) ? "enabled." :
1450 function! perforce#PFDiffOff(diffCounter)
1451 " Cycle through all windows and turn off diff options for the specified diff
1452 " run, or all, if none specified.
1453 let curWinNr = winnr()
1454 let eventignore = &eventignore
1458 while winbufnr(i) != -1
1461 if ! exists('w:p4VDiffWindow')
1465 if a:diffCounter == -1 || w:p4VDiffWindow == a:diffCounter
1466 call genutils#CleanDiffOptions()
1467 unlet w:p4VDiffWindow
1474 " Return to the original window.
1475 exec curWinNr 'wincmd w'
1476 let &eventignore = eventignore
1479 """ END: Helper functions }}}
1482 """ BEGIN: Middleware functions {{{
1484 " Filter contents through p4.
1485 function! perforce#PW(fline, lline, scriptOrigin, ...) range
1486 if a:scriptOrigin != 2
1487 call call('s:ParseOptions', [a:fline, a:lline, 0, '++f'] + a:000)
1489 let s:commandMode = s:CM_FILTER
1492 let retVal = perforce#PFIF(2, 5, s:p4Command)
1496 " Generate raw output into a new window.
1497 function! perforce#PFRaw(...)
1498 call call('s:ParseOptions', [1, line('$'), 0] + a:000)
1500 let retVal = s:PFImpl(1, 0)
1504 function! s:W(quitWhenDone, commandName, ...)
1505 call call('s:ParseOptionsIF', [1, line('$'), 0, 5, a:commandName]+a:000)
1506 if index(s:p4CmdOptions, '-i') == -1
1507 call insert(s:p4CmdOptions, '-i', 0)
1509 let retVal = perforce#PW(1, line('$'), 2)
1515 if s:p4Command ==# 'change' || s:p4Command ==# 'submit'
1516 " Change number fixed, or files added/removed.
1517 if s:FixChangeNo() || search('^Change \d\+ updated, ', 'w')
1519 if search('^Files:\s*$', 'w')
1520 let b:p4NewFilelist = getline(line('.')+1, line('$'))
1521 if !exists('b:p4OrgFilelist')
1522 let filelist = b:p4NewFilelist
1524 " Find outersection.
1525 let filelist = copy(b:p4NewFilelist)
1526 call filter(filelist, 'index(b:p4OrgFilelist, v:val)==-1')
1527 call extend(filelist, filter(copy(b:p4OrgFilelist), 'index(b:p4NewFilelist, v:val)==-1'))
1529 let files = map(filelist, 's:ConvertToLocalPath(v:val)')
1530 call s:ResetFileStatusForFiles(files)
1537 call s:FixChangeNo()
1541 function! s:FixChangeNo()
1542 if search('^Change \d\+ created', 'w') ||
1543 \ search('^Change \d\+ renamed change \d\+ and submitted', 'w')
1544 let newChangeNo = matchstr(getline('.'), '\d\+\ze\%(created\|and\)')
1545 let _undolevels=&undolevels
1547 let allLines = getline(1, line('$'))
1549 " Make the below changes such a way that they can't be undo. This in a
1550 " way, forces Vim to create an undo point, so that user can later
1551 " undo and see these changes, with proper change number and status
1552 " in place. This has the side effect of loosing the previous undo
1553 " history, which can be considered desirable, as otherwise the user
1554 " can undo this change and back to the new state.
1556 if search("^Change:\t\%(new\|\d\+\)$")
1557 silent! keepjumps call setline('.', "Change:\t" . newChangeNo)
1558 " If no Date is present (happens for new changelists)
1559 if !search("^Date:\t", 'w')
1560 call append('.', ['', "Date:\t".strftime('%Y/%m/%d %H:%M:%S')])
1563 if search("^Status:\tnew$")
1564 silent! keepjumps call setline('.', "Status:\tpending")
1567 let &undolevels=_undolevels
1569 call setline(1, allLines)
1571 call s:PFSetupForSpec()
1573 let &undolevels=_undolevels
1575 let b:p4Command = 'change'
1576 let b:p4CmdOptions = ['-i']
1583 function! s:ParseOptionsIF(fline, lline, scriptOrigin, outputType, commandName,
1585 " There are multiple possibilities here:
1586 " - scriptOrigin, in which case the commandName contains the name of the
1587 " command, but the varArgs also may contain it.
1588 " - commandOrigin, in which case the commandName may actually be the
1589 " name of the command, or it may be the first argument to p4 itself, in
1590 " any case we will let p4 handle the error cases.
1591 if index(s:allCommands, a:commandName) != -1 && a:scriptOrigin
1592 call call('s:ParseOptions', [a:fline, a:lline, a:outputType] + a:000)
1593 " Add a:commandName only if it doesn't already exist in the var args.
1594 " Handles cases like "PF help submit" and "PF -c <client> change changeno#",
1595 " where the commandName need not be at the starting and there could be
1596 " more than one valid commandNames (help and submit).
1597 if s:p4Command != a:commandName
1598 call call('s:ParseOptions',
1599 \ [a:fline, a:lline, a:outputType, a:commandName] + a:000)
1602 call call('s:ParseOptions',
1603 \ [a:fline, a:lline, a:outputType, a:commandName] + a:000)
1608 " The commandName may not be the perforce command when it is not of script
1609 " origin (called directly from a command), but it should be always command
1610 " name, when it is script origin.
1611 " scriptOrigin: An integer indicating the origin of the call.
1612 " 0 - Originated directly from the user, so should redirect to the specific
1613 " command handler (if exists), after some basic processing.
1614 " 1 - Originated from the script, continue with the full processing (makes
1615 " difference in some command parsing).
1616 " 2 - Same as 1 but, avoid processing arguments (they are already processing
1618 function! perforce#PFIF(scriptOrigin, outputType, commandName, ...)
1619 return call('perforce#PFrangeIF', [1, line('$'), a:scriptOrigin, a:outputType,
1620 \ a:commandName]+a:000)
1623 function! perforce#PFrangeIF(fline, lline, scriptOrigin, outputType,
1625 if a:scriptOrigin != 2
1626 call call('s:ParseOptionsIF', [a:fline, a:lline,
1627 \ a:scriptOrigin, a:outputType, a:commandName]+a:000)
1630 " Save a copy of the arguments so that the refresh can invoke this exactly
1632 let s:userArgs = [a:fline, a:lline, a:scriptOrigin, a:outputType, a:commandName]
1637 let outputOptIdx = index(s:p4Options, '++o')
1638 if outputOptIdx != -1
1639 " Get the argument followed by ++o.
1640 let s:outputType = get(s:p4Options, outputIdx + 1) + 0
1642 " If this command prefers a dialog output, then just take care of
1644 if index(s:dlgOutputCmds, s:p4Command) != -1
1645 let s:outputType = 2
1648 if exists('*s:{s:p4Command}Hdlr')
1649 return s:{s:p4Command}Hdlr(1, s:outputType, a:commandName)
1653 let modifyWindowName = 0
1654 let dontProcess = (index(s:p4Options, '++n') != -1)
1655 let noDefaultArg = (index(s:p4Options, '++N') != -1)
1656 " If there is a confirm message for this command, then first prompt user.
1657 let dontConfirm = (index(s:p4Options, '++y') != -1)
1658 if exists('s:confirmMsgs{s:p4Command}') && ! dontConfirm
1659 let option = s:ConfirmMessage(s:confirmMsgs{s:p4Command}, "&Yes\n&No", 2,
1667 if index(s:limitListCmds, s:p4Command) != -1 &&
1668 \ index(s:p4CmdOptions, '-m') == -1 && s:_('DefaultListSize') > -1
1669 call extend(s:p4CmdOptions, ['-m', s:_('DefaultListSize')], 0)
1670 let modifyWindowName = 1
1672 if index(s:diffCmds, s:p4Command) != -1 &&
1673 \ s:indexMatching(s:p4CmdOptions, '^-d.*$') == -1
1674 \ && s:_('DefaultDiffOptions') !~# s:EMPTY_STR
1675 " FIXME: Avoid split().
1676 call extend(s:p4CmdOptions, split(s:_('DefaultDiffOptions'), '\s'), 0)
1677 let modifyWindowName = 1
1680 " Process p4Arguments, unless explicitly not requested to do so, or the '-x'
1681 " option to read arguments from a file is given.
1682 let unprotectedSpecPat = genutils#CrUnProtectedCharsPattern('&#@')
1683 if ! dontProcess && ! noDefaultArg && len(s:p4Arguments) == 0 &&
1684 \ index(s:p4Options, '-x') == -1
1685 if (index(s:askUserCmds, s:p4Command) != -1 &&
1686 \ index(s:p4CmdOptions, '-i') == -1) ||
1687 \ index(s:p4Options, '++A') != -1
1688 if index(s:genericPromptCmds, s:p4Command) != -1
1689 let prompt = 'Enter arguments for ' . s:p4Command . ': '
1691 let prompt = "Enter the " . s:p4Command . " name: "
1693 let additionalArg = s:PromptFor(0, s:_('UseGUIDialogs'), prompt, '')
1694 if additionalArg =~# s:EMPTY_STR
1695 if index(s:genericPromptCmds, s:p4Command) != -1
1696 call s:EchoMessage('Arguments required for '. s:p4Command, 'Error')
1698 call s:EchoMessage(substitute(s:p4Command, "^.", '\U&', '') .
1699 \ " name required.", 'Error')
1704 let s:p4Arguments = [additionalArg]
1705 elseif ! dontProcess &&
1706 \ index(s:curFileNotDefCmds, s:p4Command) == -1 &&
1707 \ index(s:nofileArgsCmds, s:p4Command) == -1
1708 let s:p4Arguments = [s:EscapeFileName(s:GetCurFileName())]
1709 let modifyWindowName = 1
1711 elseif ! dontProcess && s:indexMatching(s:p4Arguments, unprotectedSpecPat) != -1
1712 let branchModifierSpecified = 0
1713 let unprotectedAmp = genutils#CrUnProtectedCharsPattern('&')
1714 if s:indexMatching(s:p4Arguments, unprotectedAmp) != -1
1715 let branchModifierSpecified = 1
1716 " CAUTION: We make sure the view mappings are generated before
1717 " s:PFGetAltFiles() gets invoked, otherwise the call results in a
1718 " recursive |sub-replace-special| and corrupts the mappings.
1719 call s:CondUpdateViewMappings()
1722 " This is like running substitute(v:val, 'pat', '\=expr', 'g') on each
1724 " Pattern is (the start of line or series of non-space chars) followed by
1725 " an unprotected [#@&] with a revision/codeline specifier.
1726 " Expression is a concatenation of each of:
1727 " (<no filename specified>?<current file>:<specified file>)
1728 " <revision specifier>
1729 " (<offset specified>?<adjusted revision>:<specified revision>)
1730 call map(s:p4Arguments, "substitute(v:val, '".
1731 \ '\(^\|\%(\S\)\+\)\('.unprotectedSpecPat.'\)\%(\2\)\@!\([-+]\?\d\+\|\S\+\)'."', ".
1732 \ "'\\=s:ExpandRevision(submatch(1), submatch(2), submatch(3))', 'g')")
1737 if branchModifierSpecified
1738 call map(s:p4Arguments,
1739 \ '(v:val =~ unprotectedAmp ? s:ApplyBranchSpecs(v:val, unprotectedAmp) : v:val)')
1741 " Unescape them, as user is required to escape them to avoid the above
1743 call map(s:p4Arguments, "genutils#UnEscape(v:val, '&@')")
1745 let modifyWindowName = 1
1749 if index(s:p4Options, '++T') != -1
1750 let testMode = 1 " Dry run, opens the window.
1751 elseif index(s:p4Options, '++D') != -1
1752 let testMode = 2 " Debug. Opens the window and displays the command.
1755 let oldOptLen = len(s:p4Options)
1756 " Remove all the built-in options.
1757 call filter(s:p4Options, "v:val !~ '".'++\S\+\%(\s\+[^-+]\+\|\s\+\)\?'."'")
1758 if len(s:p4Options) != oldOptLen
1759 let modifyWindowName = 1
1761 if index(s:diffCmds, s:p4Command) != -1
1762 " Remove the dummy option, if exists (see |perforce-default-diff-format|).
1763 call filter(s:p4CmdOptions, 'v:val != "-d"')
1764 let modifyWindowName = 1
1767 if s:p4Command ==# 'help'
1768 " Use simple window name for all the help commands.
1769 let s:p4WinName = s:helpWinName
1770 elseif modifyWindowName
1771 let s:p4WinName = s:MakeWindowName()
1774 " If the command is a built-in command, then don't pass it to external p4.
1775 if exists('s:builtinCmdHandler{s:p4Command}')
1777 return {s:builtinCmdHandler{s:p4Command}}()
1781 if index(s:specCmds, s:p4Command) != -1
1782 if index(s:p4CmdOptions, '-d') == -1
1783 \ && index(s:p4CmdOptions, '-o') == -1
1784 \ && index(s:noOutputCmds, s:p4Command) == -1
1785 call add(s:p4CmdOptions, '-o')
1788 " Go into specification mode only if the user intends to edit the output.
1789 if ((s:p4Command ==# 'submit' && index(s:p4CmdOptions, '-c') == -1) ||
1790 \ (index(s:specOnlyCmds, s:p4Command) == -1 &&
1791 \ index(s:p4CmdOptions, '-d') == -1)) &&
1798 if index(s:navigateCmds, s:p4Command) != -1
1804 " FIXME: When is "not clearing" (value 2) useful at all?
1805 let clearBuffer = (testMode != 2 ? ! navigateCmd : 2)
1806 " CAUTION: This is like a do..while loop, but not exactly the same, be
1807 " careful using continue, the counter will not get incremented.
1809 let retVal = s:PFImpl(clearBuffer, testMode)
1811 " Everything else in this loop is for password support.
1815 if retVal =~# s:EMPTY_STR
1816 let retVal = getline(1)
1818 " FIXME: Works only with English as the language.
1819 if retVal =~# 'Perforce password (P4PASSWD) invalid or unset.'
1820 let p4Password = inputsecret("Password required for user " .
1821 \ s:_('p4User') . ": ", s:p4Password)
1822 if p4Password ==# s:p4Password
1825 let s:p4Password = p4Password
1830 let retryCount = retryCount + 1
1836 if s:errCode == 0 && index(s:autoreadCmds, s:p4Command) != -1
1837 call s:ResetFileStatusForFiles(s:p4Arguments)
1844 call s:SetupWindow(specMode)
1849 function! s:SetupWindow(specMode)
1850 if s:StartBufSetup()
1851 " If this command has a handler for the individual items, then enable the
1852 " item selection commands.
1853 if s:getCommandItemHandler(0, s:p4Command, '') !~# s:EMPTY_STR
1854 call s:SetupSelectItem()
1857 if index(s:ftNotPerforceCmds, s:p4Command) == -1
1858 setlocal ft=perforce
1861 if index(s:filelistCmds, s:p4Command) != -1
1862 call s:SetupFileBrowse()
1865 if s:NewWindowCreated()
1867 " It is not possible to have an s:p4Command which is in s:allCommands
1868 " and still not be the actual intended command.
1869 command! -buffer -nargs=* W :call call('<SID>W',
1870 \ [0]+b:p4Options+[b:p4Command]+b:p4CmdOptions+split(<q-args>,
1872 command! -buffer -nargs=* WQ :call call('<SID>W',
1873 \ [1]+b:p4Options+[b:p4Command]+b:p4CmdOptions+split(<q-args>,
1875 call s:EchoMessage("When done, save " . s:p4Command .
1876 \ " spec by using :W or :WQ command. Undo on errors.", 'None')
1877 call s:PFSetupForSpec()
1878 else " Define q to quit the read-only perforce windows (David Fishburn)
1879 nnoremap <buffer> q <C-W>q
1883 if index(s:navigateCmds, s:p4Command) != -1
1884 nnoremap <silent> <buffer> <C-O> :call <SID>NavigateBack()<CR>
1885 nnoremap <silent> <buffer> <BS> :call <SID>NavigateBack()<CR>
1886 nnoremap <silent> <buffer> <Tab> :call <SID>NavigateForward()<CR>
1889 call s:EndBufSetup()
1893 function! s:ExpandRevision(fName, revType, revSpec)
1894 return (a:fName==''?s:EscapeFileName(s:GetCurFileName()):a:fName).
1896 \ (a:revSpec=~'^[-+]'?s:AdjustRevision(a:fName, a:revSpec):a:revSpec)
1899 function! s:ApplyBranchSpecs(arg, unprotectedAmp)
1900 " The first arg will be filename, followed by multiple specifiers.
1901 let toks = split(a:arg, a:unprotectedAmp)
1902 " FIXME: Handle "&" in the filename.
1903 " FIXME: Handle other revision specifiers occuring in any order
1904 " (e.g., <file>&branch#3).
1905 " Ex: c:/dev/docs/Triage/Tools/Machine\ Configurations\ &\ Codeline\ Requirements.xls
1906 if len(toks) == 0 || toks[0] == ''
1909 let fname = remove(toks, 0)
1910 " Reduce filename according to the branch tokens.
1911 for altBranch in toks
1912 let fname = s:PFGetAltFiles("&", altBranch, fname)[0]
1917 function! perforce#PFComplete(ArgLead, CmdLine, CursorPos)
1918 if s:p4KnownCmdsCompStr == ''
1919 let s:p4KnownCmdsCompStr = join(s:p4KnownCmds, "\n")
1921 if a:CmdLine =~ '^\s*P[FW] '
1922 let argStr = strpart(a:CmdLine, matchend(a:CmdLine, '^\s*PF '))
1923 let s:p4Command = ''
1924 if argStr !~# s:EMPTY_STR
1925 if exists('g:p4EnableUserFileExpand')
1926 let _p4EnableUserFileExpand = g:p4EnableUserFileExpand
1929 " WORKAROUND for :redir broken, when called from here.
1930 let g:p4EnableUserFileExpand = 0
1931 exec 'call s:ParseOptionsIF(-1, -1, 0, 0, ' .
1932 \ genutils#CreateArgString(argStr, s:SPACE_AS_SEP).')'
1934 if exists('_p4EnableUserFileExpand')
1935 let g:p4EnableUserFileExpand = _p4EnableUserFileExpand
1937 unlet g:p4EnableUserFileExpand
1941 if s:p4Command ==# '' || s:p4Command ==# a:ArgLead
1942 return s:p4KnownCmdsCompStr."\n".join(s:builtinCmds, "\n")
1945 let userCmd = substitute(a:CmdLine, '^\s*P\(.\)\(\w*\).*', '\l\1\2', '')
1946 if strlen(userCmd) < 3
1947 if !has_key(s:shortCmdMap, userCmd)
1948 throw "Perforce internal error: no map found for short command: ".
1951 let userCmd = s:shortCmdMap[userCmd]
1953 let s:p4Command = userCmd
1955 if s:p4Command ==# 'help'
1956 return s:PHelpComplete(a:ArgLead, a:CmdLine, a:CursorPos)
1958 if index(s:nofileArgsCmds, s:p4Command) != -1
1962 " FIXME: Can't set command-line from user function.
1963 "let argLead = genutils#UserFileExpand(a:ArgLead)
1964 "if argLead !=# a:ArgLead
1965 " let cmdLine = strpart(a:CmdLine, 0, a:CursorPos-strlen(a:ArgLead)) .
1966 " \ argLead . strpart(a:CmdLine, a:CursorPos)
1967 " exec "normal! \<C-\>e'".cmdLine."'\<CR>"
1968 " call setcmdpos(a:CursorPos+(strlen(argLead) - strlen(a:ArgLead)))
1971 if a:ArgLead =~ '^//'.s:_('Depot').'/'
1972 " Get directory matches.
1973 let dirMatches = s:GetOutput('dirs', a:ArgLead, "\n", '/&')
1975 let fileMatches = s:GetOutput('files', a:ArgLead, '#\d\+[^'."\n".']\+', '')
1976 if dirMatches !~ s:EMPTY_STR || fileMatches !~ s:EMPTY_STR
1977 return dirMatches.fileMatches
1982 return genutils#UserFileComplete(a:ArgLead, a:CmdLine, a:CursorPos, 1, '')
1985 function! s:GetOutput(p4Cmd, arg, pat, repl)
1986 let matches = perforce#PFIF(0, 4, a:p4Cmd, a:arg.'*')
1988 if matches =~ 'no such file(s)'
1991 let matches = substitute(substitute(matches, a:pat, a:repl, 'g'),
1992 \ "\n\n", "\n", 'g')
1999 """ START: Adopted from Tom's perforce plugin. {{{
2001 "---------------------------------------------------------------------------
2002 " Produce string for ruler output
2003 function! perforce#RulerStatus()
2004 if exists('b:p4RulerStr') && b:p4RulerStr !~# s:EMPTY_STR
2007 if !exists('b:p4FStatDone') || !b:p4FStatDone
2011 "let b:p4RulerStr = '[p4 '
2012 let b:p4RulerStr = '['
2013 if exists('b:p4RulerErr') && b:p4RulerErr !~# s:EMPTY_STR
2014 let b:p4RulerStr = b:p4RulerStr . b:p4RulerErr
2015 elseif !exists('b:p4HaveRev')
2016 let b:p4RulerStr = ''
2017 elseif b:p4Action =~# s:EMPTY_STR
2018 if b:p4OtherOpen =~# s:EMPTY_STR
2019 let b:p4RulerStr = b:p4RulerStr . 'unopened'
2021 let b:p4RulerStr = b:p4RulerStr . b:p4OtherOpen . ':' . b:p4OtherAction
2024 if b:p4Change ==# 'default' || b:p4Change =~# s:EMPTY_STR
2025 let b:p4RulerStr = b:p4RulerStr . b:p4Action
2027 let b:p4RulerStr = b:p4RulerStr . b:p4Action . ':' . b:p4Change
2030 if exists('b:p4HaveRev') && b:p4HaveRev !~# s:EMPTY_STR
2031 let b:p4RulerStr = b:p4RulerStr . ' #' . b:p4HaveRev . '/' . b:p4HeadRev
2034 if b:p4RulerStr !~# s:EMPTY_STR
2035 let b:p4RulerStr = b:p4RulerStr . ']'
2040 function! s:GetClientInfo()
2042 call s:PushP4Context()
2044 let infoStr = perforce#PFIF(0, 4, 'info')
2046 return s:ConfirmMessage((v:errmsg != '') ? v:errmsg : infoStr, 'OK', 1,
2050 call s:PopP4Context(0)
2052 let g:p4ClientRoot = genutils#CleanupFileName(s:StrExtract(infoStr,
2053 \ '\CClient root: [^'."\n".']\+', 13))
2054 let s:p4Client = s:StrExtract(infoStr, '\CClient name: [^'."\n".']\+', 13)
2055 let s:p4User = s:StrExtract(infoStr, '\CUser name: [^'."\n".']\+', 11)
2058 " Get/refresh filestatus for the specified buffer with optimizations.
2059 function! perforce#GetFileStatus(buf, refresh)
2060 if ! type(a:buf) " If number.
2061 let bufNr = (a:buf == 0) ? bufnr('%') : a:buf
2063 let bufNr = bufnr(a:buf)
2066 " If it is not a normal buffer, then ignore it.
2067 if getbufvar(bufNr, '&buftype') != '' || bufname(bufNr) == ''
2070 if bufNr == -1 || (!a:refresh && s:_('OptimizeActiveStatus') &&
2071 \ getbufvar(bufNr, "p4FStatDone"))
2075 " This is an optimization by restricting status to the files under the
2077 if !s:IsFileUnderDepot(expand('#'.bufNr.':p'))
2081 return s:GetFileStatusImpl(bufNr)
2084 function! s:ResetFileStatusForFiles(files)
2086 let bufNr = genutils#FindBufferForName(file)
2088 " FIXME: Check for other tabs also.
2089 if bufwinnr(bufNr) != -1 " If currently visible.
2090 call perforce#GetFileStatus(bufNr, 1)
2092 call s:ResetFileStatusForBuffer(bufNr)
2098 function! s:ResetFileStatusForBuffer(bufNr)
2099 " Avoid proliferating this buffer variable.
2100 if getbufvar(a:bufNr, 'p4FStatDone') != 0
2101 call setbufvar(a:bufNr, 'p4FStatDone', 0)
2105 "---------------------------------------------------------------------------
2106 " Obtain file status information
2107 function! s:GetFileStatusImpl(bufNr)
2108 if bufname(a:bufNr) == ""
2111 let fileName = fnamemodify(bufname(a:bufNr), ':p')
2113 " If the filename matches with one of the ignore patterns, then don't do
2115 if s:_('ASIgnoreDefPattern') !~# s:EMPTY_STR &&
2116 \ match(fileName, s:_('ASIgnoreDefPattern')) != -1
2119 if s:_('ASIgnoreUsrPattern') !~# s:EMPTY_STR &&
2120 \ match(fileName, s:_('ASIgnoreUsrPattern')) != -1
2124 call setbufvar(bufNr, 'p4RulerStr', '') " Let this be reconstructed.
2126 " This could very well be a recursive call, so we should save the current
2128 call s:PushP4Context()
2130 let fileStatusStr = perforce#PFIF(1, 4, 'fstat', fileName)
2131 call setbufvar(bufNr, 'p4FStatDone', '1')
2134 call setbufvar(bufNr, 'p4RulerErr', "<ERROR>")
2138 call s:PopP4Context(0)
2141 if match(fileStatusStr, ' - file(s) not in client view\.') >= 0
2142 call setbufvar(bufNr, 'p4RulerErr', "<Not In View>")
2143 " Required for optimizing out in future runs.
2144 call setbufvar(bufNr, 'p4HeadRev', '')
2146 elseif match(fileStatusStr, ' - no such file(s).') >= 0
2147 call setbufvar(bufNr, 'p4RulerErr', "<Not In Depot>")
2148 " Required for optimizing out in future runs.
2149 call setbufvar(bufNr, 'p4HeadRev', '')
2152 call setbufvar(bufNr, 'p4RulerErr', '')
2155 call setbufvar(bufNr, 'p4HeadRev',
2156 \ s:StrExtract(fileStatusStr, '\CheadRev [0-9]\+', 8))
2157 "call setbufvar(bufNr, 'p4DepotFile',
2158 " \ s:StrExtract(fileStatusStr, '\CdepotFile [^'."\n".']\+', 10))
2159 "call setbufvar(bufNr, 'p4ClientFile',
2160 " \ s:StrExtract(fileStatusStr, '\CclientFile [^'."\n".']\+', 11))
2161 call setbufvar(bufNr, 'p4HaveRev',
2162 \ s:StrExtract(fileStatusStr, '\ChaveRev [0-9]\+', 8))
2163 let headAction = s:StrExtract(fileStatusStr, '\CheadAction [^[:space:]]\+',
2165 if headAction ==# 'delete'
2166 call setbufvar(bufNr, 'p4Action', '<Deleted>')
2167 call setbufvar(bufNr, 'p4Change', '')
2169 call setbufvar(bufNr, 'p4Action',
2170 \ s:StrExtract(fileStatusStr, '\Caction [^[:space:]]\+', 7))
2171 call setbufvar(bufNr, 'p4OtherOpen',
2172 \ s:StrExtract(fileStatusStr, '\CotherOpen0 [^[:space:]@]\+', 11))
2173 call setbufvar(bufNr, 'p4OtherAction',
2174 \ s:StrExtract(fileStatusStr, '\CotherAction0 [^[:space:]@]\+', 13))
2175 call setbufvar(bufNr, 'p4Change',
2176 \ s:StrExtract(fileStatusStr, '\Cchange [^[:space:]]\+', 7))
2179 return fileStatusStr
2182 function! s:StrExtract(str, pat, pos)
2183 let part = matchstr(a:str, a:pat)
2184 let part = strpart(part, a:pos)
2188 function! s:AdjustRevision(file, adjustment)
2190 let revNum = a:adjustment
2191 if revNum =~# '[-+]\d\+'
2192 let revNum = substitute(revNum, '^+', '', '')
2193 if getbufvar(a:file, 'p4HeadRev') =~# s:EMPTY_STR
2194 " If fstat is not done yet, do it now.
2195 call perforce#GetFileStatus(a:file, 1)
2196 if getbufvar(a:file, 'p4HeadRev') =~# s:EMPTY_STR
2197 call s:EchoMessage("Current revision is not available. " .
2198 \ "To be able to use negative revisions, see help on " .
2199 \ "'perforce-active-status'.", 'Error')
2204 let revNum = getbufvar(a:file, 'p4HaveRev') + revNum
2206 call s:EchoMessage("Not that many revisions available. Try again " .
2207 \ "after running PFRefreshFileStatus command.", 'Error')
2215 "---------------------------------------------------------------------------
2216 " One of a set of functions that returns fields from the p4 fstat command
2217 function! s:IsCurrent()
2218 let revdiff = b:p4HeadRev - b:p4HaveRev
2226 function! s:CheckOutFile()
2227 if ! g:p4PromptToCheckout || ! s:IsFileUnderDepot(expand('%:p'))
2230 " If we know that the file is deleted from the depot, don't prompt.
2231 if exists('b:p4Action') && b:p4Action == '<Deleted>'
2235 if filereadable(expand("%")) && ! filewritable(expand("%"))
2236 let option = s:ConfirmMessage("Readonly file, do you want to checkout " .
2237 \ "from perforce?", "&Yes\n&No\n&Cancel", s:_('CheckOutDefault'),
2240 call perforce#PFIF(1, 2, 'edit')
2243 call perforce#GetFileStatus(expand('<abuf>') + 0, 1)
2246 call s:CancelEdit(0)
2251 function! s:CancelEdit(stage)
2255 au CursorMovedI <buffer> nested :call <SID>CancelEdit(1)
2256 au CursorMoved <buffer> nested :call <SID>CancelEdit(1)
2265 function! perforce#FileChangedShell()
2266 let bufNr = expand("<abuf>") + 0
2267 if s:_('EnableActiveStatus')
2268 call s:ResetFileStatusForBuffer(bufNr)
2271 if index(s:autoreadCmds, s:currentCommand) != -1
2272 let autoread = s:_('Autoread')
2274 call setbufvar(bufNr, '&readonly', 0)
2279 """ END: Adapted from Tom's perforce plugin. }}}
2281 """ END: Middleware functions }}}
2284 """ BEGIN: Infrastructure {{{
2286 " Assumes that the arguments are already parsed and are ready to be used in
2287 " the script variables.
2288 " Low level interface with the p4 command.
2289 " clearBuffer: If the buffer contents should be cleared before
2290 " adding the new output (See s:GotoWindow).
2291 " testMode (number):
2293 " 1 - testing, ignore.
2294 " 2 - debugging, display the command-line instead of the actual output..
2295 " Returns the output if available. If there is any error, the error code will
2296 " be available in s:errCode variable.
2297 function! s:PFImpl(clearBuffer, testMode) " {{{
2301 let p4Options = s:GetP4Options()
2302 let fullCmd = s:CreateFullCmd(s:MakeP4ArgList(p4Options, 0))
2303 " Save the name of the current file.
2304 let p4OrgFileName = s:GetCurFileName()
2306 let s:currentCommand = ''
2307 " Make sure all the already existing changes are detected. We don't have
2308 " s:currentCommand set here, so the user will get an appropriate prompt.
2312 " FIXME: Ignore error for now.
2315 " If the output has to be shown in a window, position cursor appropriately,
2316 " creating a new window if required.
2318 " Ignore outputType in this case.
2319 if s:commandMode != s:CM_PIPE && s:commandMode != s:CM_FILTER
2320 if s:outputType == 0 || s:outputType == 1
2321 " Only when "clear with undo" is selected, we optimize out the call.
2322 if s:GotoWindow(s:outputType,
2323 \ (!s:refreshWindowsAlways && (a:clearBuffer == 1)) ?
2324 \ 2 : a:clearBuffer, p4OrgFileName, 0) != 0
2333 let s:currentCommand = s:p4Command
2334 if s:_('EnableFileChangedShell')
2335 call genutils#DefFCShellInstall()
2339 if s:commandMode ==# s:CM_RUN
2340 " Only when "clear with undo" is selected, we optimize out the call.
2341 if s:refreshWindowsAlways ||
2342 \ ((!s:refreshWindowsAlways && (a:clearBuffer == 1)) &&
2343 \ (line('$') == 1 && getline(1) =~ '^\s*$'))
2344 " If we are placing the output in a new window, then we should
2345 " avoid system() for performance reasons, imagine doing a
2346 " 'print' on a huge file.
2347 " These two outputType's correspond to placing the output in a
2349 if s:outputType != 0 && s:outputType != 1
2350 let output = s:System(fullCmd)
2352 exec '.call s:Filter(fullCmd, 1)'
2356 elseif s:commandMode ==# s:CM_FILTER
2357 exec s:filterRange . 'call s:Filter(fullCmd, 1)'
2358 elseif s:commandMode ==# s:CM_PIPE
2359 exec s:filterRange . 'call s:Filter(fullCmd, 2)'
2361 " Detect any new changes to the loaded buffers.
2362 " CAUTION: This actually results in a reentrant call back to this
2363 " function, but our Push/Pop mechanism for the context should take
2368 " FIXME: Ignore error for now.
2371 if s:_('EnableFileChangedShell')
2372 call genutils#DefFCShellUninstall()
2374 let s:currentCommand = ''
2376 elseif a:testMode != 1
2377 let output = fullCmd
2381 " If we have non-null output, then handling it is still pending.
2382 if output !~# s:EMPTY_STR
2383 let echoGrp = 'NONE' " The default
2384 let maxLinesInDlg = s:_('MaxLinesInDialog')
2385 if s:outputType == 2 && maxLinesInDlg != -1
2390 let nlIdx = stridx(output, "\n", nlIdx+1)
2393 if nNLs > maxLinesInDlg
2400 if nNLs > maxLinesInDlg
2401 " NOTE: Keep in sync with that at the start of the function.
2402 if s:GotoWindow(s:outputType,
2403 \ (!s:refreshWindowsAlways && (a:clearBuffer == 1)) ?
2404 \ 2 : a:clearBuffer, p4OrgFileName, 0) == 0
2405 let s:outputType = 0
2407 let s:outputType = 3
2408 let echoGrp = 'WarningMsg'
2412 " If the output has to be shown in a dialog, bring up a dialog with the
2413 " output, otherwise show it in the current window.
2414 if s:outputType == 0 || s:outputType == 1
2415 silent! put! =output
2417 elseif s:outputType == 2
2418 call s:ConfirmMessage(output, "OK", 1, "Info")
2419 elseif s:outputType == 3
2420 call s:EchoMessage(output, echoGrp)
2421 elseif s:outputType == 4
2422 " Do nothing we will just return it.
2429 call s:InitWindow(p4Options)
2433 function! s:NewWindowCreated()
2434 if (s:outputType == 0 || s:outputType == 1) && s:errCode == 0 &&
2435 \ (s:commandMode ==# s:CM_RUN)
2442 function! s:setBufSetting(opt, set)
2443 let optArg = matchstr(b:p4Options, '\%(\S\)\@<!-'.a:opt.'\s\+\S\+')
2444 if optArg !~# s:EMPTY_STR
2445 let b:p4Options = substitute(b:p4Options, '\V'.optArg, '', '')
2446 let b:{a:set} = matchstr(optArg, '-'.a:opt.'\s\+\zs.*')
2450 " These p4Options are frozen according to the current s:p4Options.
2451 function! s:InitWindow(p4Options)
2452 if s:NewWindowCreated()
2453 let b:p4Command = s:p4Command
2454 let b:p4Options = a:p4Options
2455 let b:p4CmdOptions = s:p4CmdOptions
2456 let b:p4Arguments = s:p4Arguments
2457 let b:p4UserArgs = s:userArgs
2458 " Separate -p port -c client -u user options and set them individually.
2459 " Leave the rest in the b:p4Options variable.
2460 call s:setBufSetting('c', 'p4Client')
2461 call s:setBufSetting('p', 'p4Port')
2462 call s:setBufSetting('u', 'p4User')
2463 " Remove any ^M's at the end (for windows), without corrupting the search
2464 " register or its history.
2465 call genutils#SilentSubstitute("\<CR>$", '%s///e')
2466 setlocal nomodifiable
2469 if s:outputType == 1
2475 " External command execution {{{
2477 function! s:System(fullCmd)
2478 return s:ExecCmd(a:fullCmd, 0)
2481 function! s:Filter(fullCmd, mode) range
2482 " For command-line, we need to protect '%', '#' and '!' chars, even if they
2483 " are in quotes, to avoid getting expanded by Vim before invoking external
2485 let fullCmd = genutils#Escape(a:fullCmd, '%#!')
2486 exec a:firstline.",".a:lastline.
2487 \ "call s:ExecCmd(fullCmd, a:mode)"
2490 function! s:ExecCmd(fullCmd, mode) range
2494 " Assume the shellredir is set correctly to capture the error messages.
2496 let output = system(a:fullCmd)
2498 silent! exec a:firstline.",".a:lastline."!".a:fullCmd
2500 silent! exec a:firstline.",".a:lastline."write !".a:fullCmd
2503 call s:CheckShellError(output, s:outputType)
2505 catch /^Vim\%((\a\+)\)\=:E/ " 48[2-5]
2506 let v:errmsg = substitute(v:exception, '^[^:]\+:', '', '')
2507 call s:CheckShellError(output, s:outputType)
2508 catch /^Vim:Interrupt$/
2510 let v:errmsg = 'Interrupted'
2515 function! s:EvalExpr(expr, def)
2517 if a:expr !~# s:EMPTY_STR
2518 exec "let result = " . a:expr
2523 function! s:GetP4Options()
2526 " If there are duplicates, perfore takes the first option, so let
2527 " s:p4Options or b:p4Options come before g:p4DefaultOptions.
2528 " LIMITATATION: We choose either s:p4Options or b:p4Options only. But this
2529 " shouldn't be a big issue as this feature is meant for executing more
2530 " commands on the p4 result windows only.
2531 if len(s:p4Options) != 0
2532 call extend(addOptions, s:p4Options)
2533 elseif exists('b:p4Options') && len(b:p4Options) != 0
2534 call extend(addOptions, b:p4Options)
2537 " FIXME: avoid split here.
2538 call extend(addOptions, split(s:_('DefaultOptions'), ' '))
2540 let p4Client = s:p4Client
2541 let p4User = s:p4User
2542 let p4Port = s:p4Port
2544 if s:p4Port !=# 'P4CONFIG'
2545 if s:_('CurPresetExpr') !~# s:EMPTY_STR
2546 let preset = s:EvalExpr(s:_('CurPresetExpr'), '')
2547 if preset ~= s:EMPTY_STR
2548 call perforce#PFSwitch(0, preset)
2552 if s:_('p4Client') !~# s:EMPTY_STR && index(addOptions, '-c') == -1
2553 call add(add(addOptions, '-c'), s:_('p4Client'))
2555 if s:_('p4User') !~# s:EMPTY_STR && index(addOptions, '-u') == -1
2556 call add(add(addOptions, '-u'), s:_('p4User'))
2558 if s:_('p4Port') !~# s:EMPTY_STR && index(addOptions, '-p') == -1
2559 call add(add(addOptions, '-p'), s:_('p4Port'))
2561 " Don't pass password with '-P' option, it will be too open (ps will show
2563 let $P4PASSWD = s:p4Password
2567 let s:p4Client = p4Client
2568 let s:p4User = p4User
2569 let s:p4Port = p4Port
2575 function! s:CreateFullCmd(argList)
2576 let fullCmd = genutils#EscapeCommand(s:p4CommandPrefix.s:_('CmdPath'), a:argList,
2578 let g:p4FullCmd = fullCmd
2582 " Generates a command string as the user typed, using the script variables.
2583 function! s:MakeP4ArgList(p4Options, useBufLocal)
2584 if a:useBufLocal && exists('b:p4Command')
2585 let p4Command = b:p4Command
2587 let p4Command = s:p4Command
2589 if a:useBufLocal && exists('b:p4CmdOptions')
2590 let p4CmdOptions = b:p4CmdOptions
2592 let p4CmdOptions = s:p4CmdOptions
2594 if a:useBufLocal && exists('b:p4Arguments')
2595 let p4Arguments = b:p4Arguments
2597 let p4Arguments = s:p4Arguments
2599 let cmdList = a:p4Options+[p4Command]+p4CmdOptions+p4Arguments
2600 " Remove the protection from the characters that we treat specially (Note: #
2601 " and % are treated specially by Vim command-line itself, and the
2602 " back-slashes are removed even before we see them.)
2603 call map(cmdList, "genutils#UnEscape(v:val, '&')")
2607 " In case of outputType == 4, it assumes the caller wants to see the output as
2608 " it is, so no error message is given. The caller is expected to check for
2609 " error code, though.
2610 function! s:CheckShellError(output, outputType)
2611 if (v:shell_error != 0 || v:errmsg != '') && a:outputType != 4
2612 let output = "There was an error executing external p4 command.\n"
2614 let output = output . "\n" . "errmsg = " . v:errmsg
2616 " When commandMode ==# s:CM_RUN, the error message may already be there in
2617 " the current window.
2618 if a:output !~# s:EMPTY_STR
2619 let output = output . "\n" . a:output
2620 elseif a:output =~# s:EMPTY_STR &&
2621 \ (s:commandMode ==# s:CM_RUN && line('$') == 1 && col('$') == 1)
2622 let output = output . "\n\n" .
2623 \ "Check if your 'shellredir' option captures error messages."
2625 call s:ConfirmMessage(output, "OK", 1, "Error")
2627 let s:errCode = v:shell_error
2628 return v:shell_error
2631 " External command execution }}}
2633 " Push/Pop/Peek context {{{
2634 function! s:PushP4Context()
2635 call add(s:p4Contexts, s:GetP4ContextVars())
2638 function! s:PeekP4Context()
2639 return s:PopP4ContextImpl(1, 1)
2642 function! s:PopP4Context(...)
2643 " By default carry forward error.
2644 return s:PopP4ContextImpl(0, (a:0 ? a:1 : 1))
2647 function! s:NumP4Contexts()
2648 return len(s:p4Contexts)
2651 function! s:PopP4ContextImpl(peek, carryFwdErr)
2652 let nContexts = len(s:p4Contexts)
2654 echoerr "PopP4Context: Contexts stack is empty"
2657 let context = s:p4Contexts[-1]
2659 call remove(s:p4Contexts, nContexts-1)
2662 call s:SetP4ContextVars(context, a:carryFwdErr)
2666 " Serialize p4 context variables.
2667 function! s:GetP4ContextVars()
2668 return [s:p4Options , s:p4Command , s:p4CmdOptions , s:p4Arguments ,
2669 \ s:p4Pipe , s:p4WinName , s:commandMode , s:filterRange ,
2670 \ s:outputType , s:errCode, s:userArgs]
2673 " De-serialize p4 context variables.
2674 function! s:SetP4ContextVars(context, ...)
2677 let carryFwdErr = s:errCode
2680 let [s:p4Options, s:p4Command, s:p4CmdOptions, s:p4Arguments, s:p4Pipe,
2681 \ s:p4WinName, s:commandMode, s:filterRange, s:outputType, s:errCode,
2682 \ s:userArgs] = a:context
2683 let s:errCode = s:errCode + carryFwdErr
2685 " Push/Pop/Peek context }}}
2687 """ BEGIN: Argument parsing {{{
2688 function! s:ResetP4ContextVars()
2690 " PF <p4Options> <p4Command> <p4CmdOptions> <p4Arguments> | <p4Pipe>
2691 " Ex: PF -c hari integrate -b branch -s <fromFile> <toFile>
2692 let s:p4Options = []
2693 let s:p4Command = ""
2694 let s:p4CmdOptions = []
2695 let s:p4Arguments = []
2697 let s:p4WinName = ""
2699 " run - Execute p4 using system() or its equivalent.
2700 " filter - Execute p4 as a filter for the current window contents. Use
2701 " commandPrefix to restrict the filter range.
2702 " display - Don't execute p4. The output is already passed in.
2703 let s:commandMode = "run"
2704 let s:filterRange = ""
2705 let s:outputType = 0
2708 " Special variable to keep track of full user arguments.
2711 call s:ResetP4ContextVars() " Let them get initialized the first time.
2713 " Parses the arguments into 4 parts, "options to p4", "p4 command",
2714 " "options to p4 command", "actual arguments". Also generates the window name.
2715 " outputType (string):
2716 " 0 - Execute p4 and place the output in a new window.
2717 " 1 - Same as above, but use preview window.
2718 " 2 - Execute p4 and show the output in a dialog for confirmation.
2719 " 3 - Execute p4 and echo the output.
2720 " 4 - Execute p4 and return the output.
2721 " 5 - Execute p4 no output expected. Essentially same as 4 when the current
2722 " commandMode doesn't produce any output, just for clarification.
2723 function! s:ParseOptions(fline, lline, outputType, ...) " range
2724 call s:ResetP4ContextVars()
2725 let s:outputType = a:outputType
2730 let s:filterRange = a:fline . ',' . a:lline
2734 let s:pendingPipeArg = ''
2736 try " Just for the sake of loop variables. [-2f]
2738 if s:pendingPipeArg !~# s:EMPTY_STR
2739 let curArg = s:pendingPipeArg
2740 let s:pendingPipeArg = ''
2741 elseif len(s:p4Pipe) == 0
2742 let curArg = a:000[i-1]
2743 " The user can't specify a null string on the command-line, this is an
2744 " argument originating from the script, so just ignore it (just for
2745 " the sake of convenience, see PChangesDiff for a possibility).
2749 let pipeIndex = match(curArg, '\\\@<!\%(\\\\\)*\zs|')
2751 let pipePart = strpart(curArg, pipeIndex)
2752 let p4Part = strpart(curArg, 0, pipeIndex)
2753 if p4Part !~# s:EMPTY_STR
2755 let s:pendingPipeArg = pipePart
2757 let curArg = pipePart
2761 let curArg = a:000[i-1]
2764 if curArg ==# '<pfitem>'
2765 let curItem = s:GetCurrentItem()
2766 if curItem !~# s:EMPTY_STR
2767 let curArg = curItem
2771 " As we use custom completion mode, the filename meta-sequences in the
2772 " arguments will not be expanded by Vim automatically, so we need to
2773 " expand them manually here. On the other hand, this provides us control
2774 " on what to expand, so we can avoid expanding perforce file revision
2775 " numbers as buffernames (escaping is no longer required by the user on
2778 let fileRevIndex = match(curArg, '#\(-\?\d\+\|none\|head\|have\)$')
2779 if fileRevIndex != -1
2780 let fileRev = strpart(curArg, fileRevIndex)
2781 let curArg = strpart(curArg, 0, fileRevIndex)
2783 if curArg != '' && (!exists('g:p4EnableUserFileExpand') ||
2784 \ g:p4EnableUserFileExpand)
2785 let curArg = genutils#UserFileExpand(curArg)
2788 let curArg = curArg.fileRev
2791 if curArg =~# '^|' || len(s:p4Pipe) != 0
2792 call add(s:p4Pipe, curArg)
2796 if ! s:IsAnOption(curArg) " If not an option.
2797 if s:p4Command =~# s:EMPTY_STR &&
2798 \ index(s:allCommands, curArg) != -1
2799 " If the previous one was an option to p4 that takes in an argument.
2800 if prevArg =~# '^-[cCdHLpPux]$' || prevArg =~# '^++o$' " See :PH usage.
2801 call add(s:p4Options, curArg)
2802 if prevArg ==# '++o' && (curArg == '0' || curArg == 1)
2803 let s:outputType = curArg
2806 let s:p4Command = curArg
2808 else " Argument is not a perforce command.
2809 if s:p4Command =~# s:EMPTY_STR
2810 call add(s:p4Options, curArg)
2813 " Look for options that have an argument, so we can collect this
2814 " into p4CmdOptions instead of p4Arguments.
2815 if len(s:p4Arguments) == 0 && s:IsAnOption(prevArg)
2816 " We could as well just check for the option here, but combining
2817 " this with the command name will increase the accuracy of finding
2818 " the starting point for p4Arguments.
2819 if (prevArg[0] ==# '-' && has_key(s:p4OptCmdMap, prevArg[1]) &&
2820 \ index(s:p4OptCmdMap[prevArg[1]], s:p4Command) != -1) ||
2821 \ (prevArg =~# '^++' && has_key(s:biOptCmdMap, prevArg[2]) &&
2822 \ index(s:biOptCmdMap[prevArg[2]], s:p4Command) != -1)
2828 call add(s:p4CmdOptions, curArg)
2830 call add(s:p4Arguments, curArg)
2835 if len(s:p4Arguments) == 0
2836 if s:p4Command =~# s:EMPTY_STR
2837 if curArg =~# '^++[pfdr]$'
2839 let s:commandMode = s:CM_PIPE
2840 elseif curArg ==# '++f'
2841 let s:commandMode = s:CM_FILTER
2842 elseif curArg ==# '++r'
2843 let s:commandMode = s:CM_RUN
2847 call add(s:p4Options, curArg)
2849 call add(s:p4CmdOptions, curArg)
2852 call add(s:p4Arguments, curArg)
2855 " The "-x -" option requires it to act like a filter.
2856 if s:p4Command =~# s:EMPTY_STR && prevArg ==# '-x' && curArg ==# '-'
2857 let s:commandMode = s:CM_FILTER
2861 if s:pendingPipeArg =~# s:EMPTY_STR
2864 let prevArg = curArg
2868 if index(s:p4Options, '-d') == -1
2869 let curDir = s:EvalExpr(s:_('CurDirExpr'), '')
2871 call add(add(s:p4Options, '-d'), s:EscapeFileName(curDir))
2874 let s:p4WinName = s:MakeWindowName()
2877 function! s:IsAnOption(arg)
2878 if a:arg =~# '^-.$' || a:arg =~# '^-d\%([cnsubw]\|\d\+\)*$' ||
2879 \ a:arg =~# '^-a[fmsty]$' || a:arg =~# '^-s[ader]$' ||
2880 \ a:arg =~# '^-qu$' || a:arg =~# '^+'
2887 function! s:CleanSpaces(str)
2888 " Though not complete, it is enough to just say,
2889 " "spaces that are not preceded by \'s".
2890 return substitute(substitute(a:str, '^ \+\|\%(\\\@<! \)\+$', '', 'g'),
2891 \ '\%(\\\@<! \)\+', ' ', 'g')
2901 elseif exists('w:'.set)
2903 elseif exists('t:'.set)
2905 elseif exists('s:'.set)
2907 elseif exists('g:'.set)
2910 echoerr 'No setting found for: ' set
2914 function! s:indexMatching(list, pat)
2924 """ END: Argument parsing }}}
2926 """ BEGIN: Messages and dialogs {{{
2927 function! s:SyntaxError(msg)
2929 call s:ConfirmMessage("Syntax Error:\n".a:msg, "OK", 1, "Error")
2933 function! s:ShowVimError(errmsg, stack)
2934 call s:ConfirmMessage("There was an error executing a Vim command.\n\t" .
2935 \ a:errmsg.(a:stack != '' ? "\nCurrent stack: ".a:stack : ''), "OK", 1,
2937 echohl ErrorMsg | echomsg a:errmsg | echohl None
2939 echomsg "Current stack:" a:stack
2941 redraw " Cls, such that it is only available in the message list.
2946 function! s:EchoMessage(msg, type)
2947 let s:lastMsg = a:msg
2948 let s:lastMsgGrp = a:type
2949 redraw | exec 'echohl' a:type | echo a:msg | echohl NONE
2952 function! s:ConfirmMessage(msg, opts, def, type)
2953 let s:lastMsg = a:msg
2954 let s:lastMsgGrp = 'None'
2955 if a:type ==# 'Error'
2956 let s:lastMsgGrp = 'Error'
2958 return confirm(a:msg, a:opts, a:def, a:type)
2961 function! s:PromptFor(loop, useDialogs, msg, default)
2963 while result =~# s:EMPTY_STR
2965 let result = inputdialog(a:msg, a:default)
2967 let result = input(a:msg, a:default)
2976 function! perforce#LastMessage()
2977 call s:EchoMessage(s:lastMsg, s:lastMsgGrp)
2979 """ END: Messages and dialogs }}}
2981 """ BEGIN: Filename handling {{{
2982 " Escape all the special characters (as the user would if he typed the name
2984 function! s:EscapeFileName(fName)
2985 " If there is a -d option existing, then it is better to use the full path
2987 if index(s:p4Options, '-d') != -1
2988 let fName = fnamemodify(a:fName, ':p')
2992 return genutils#Escape(fName, ' &|')
2995 function! s:GetCurFileName()
2996 " When the current window itself is a perforce window, then carry over the
2998 return (exists('b:p4OrgFileName') &&
2999 \ b:p4OrgFileName !~# s:EMPTY_STR) ?
3000 \ b:p4OrgFileName : expand('%:p')
3003 function! s:GuessFileTypeForCurrentWindow()
3004 let fileExt = s:GuessFileType(b:p4OrgFileName)
3005 if fileExt =~# s:EMPTY_STR
3006 let fileExt = s:GuessFileType(expand("%"))
3011 function! s:GuessFileType(name)
3012 let fileExt = fnamemodify(a:name, ":e")
3013 return matchstr(fileExt, '\w\+')
3016 function! s:IsDepotPath(path)
3017 if match(a:path, '^//'.s:_('Depot').'/') == 0
3018 " \ || match(a:path, '^//'. s:_('p4Client') . '/') == 0
3025 function! s:PathRefersToDepot(path)
3026 if s:IsDepotPath(a:path) || s:GetRevisionSpecifier(a:path) !~# s:EMPTY_STR
3033 function! s:GetRevisionSpecifier(fileName)
3034 return matchstr(a:fileName,
3035 \ '^\(\%(\S\|\\\@<!\%(\\\\\)*\\ \)\+\)[\\]*\zs[#@].*$')
3038 " Removes the //<depot> or //<client> prefix from fileName.
3039 function! s:StripRemotePath(fileName)
3040 "return substitute(a:fileName, '//\%('.s:_('Depot').'\|'.s:_('p4Client').'\)', '', '')
3041 return substitute(a:fileName, '//\%('.s:_('Depot').'\)', '', '')
3044 " Client view translation {{{
3045 " Convert perforce file wildcards ("*", "..." and "%[1-9]") to a Vim string
3046 " regex (see |pattern.txt|). Returns patterns that work when "very nomagic"
3049 function! s:TranlsateP4Wild(p4Wild, rhsView)
3052 if a:p4Wild[0] ==# '%'
3053 let pos = s:p4WildMap[a:p4Wild[1]]
3055 let pos = s:p4WildCount
3057 let strRegex = '\'.pos
3060 let strRegex = '\(\[^/]\*\)'
3061 elseif a:p4Wild ==# '...'
3062 let strRegex = '\(\.\*\)'
3063 elseif a:p4Wild[0] ==# '%'
3064 let strRegex = '\(\[^/]\*\)'
3065 let s:p4WildMap[a:p4Wild[1]] = s:p4WildCount
3068 let s:p4WildCount = s:p4WildCount + 1
3072 " Convert perforce file regex (containing "*", "..." and "%[1-9]") to a Vim
3073 " string regex. No error checks for now, for simplicity.
3074 function! s:TranslateP4FileRegex(p4Regex, rhsView)
3075 let s:p4WildCount = 1
3076 " Note: We don't expect backslashes in the views, so no special handling.
3077 return substitute(a:p4Regex,
3078 \ '\(\*\|\%(\.\)\@<!\.\.\.\%(\.\)\@!\|%\([1-9]\)\)',
3079 \ '\=s:TranlsateP4Wild(submatch(1), a:rhsView)', 'g')
3082 function! s:CondUpdateViewMappings()
3083 if s:_('UseClientViewMap') &&
3084 \ (!has_key(s:toDepotMapping, s:_("p4Client")) ||
3085 \ (len(s:toDepotMapping[s:_('p4Client')]) < 0))
3086 call perforce#UpdateViewMappings()
3090 function! perforce#UpdateViewMappings()
3091 if s:_('p4Client') =~# s:EMPTY_STR
3095 call s:PushP4Context()
3097 let view = substitute(perforce#PFIF(1, 4, '-c', s:_('p4Client'), 'client'),
3098 \ "\\_.*\nView:\\ze\n", '', 'g')
3103 call s:PopP4Context(0)
3105 let fromDepotMapping = []
3106 let toDepotMapping = []
3107 for nextMap in reverse(split(view, "\n"))
3108 " We need to inverse the order of mapping such that the mappings that come
3109 " later in the view take more priority.
3110 " Also, don't care about exclusionary mappings for simplicity (this could
3111 " be considered a feature too).
3112 exec substitute(nextMap,
3113 \ '\s*-\?//'.s:_('Depot').'/\([^ ]\+\)\s*//'.s:_("p4Client").'/\(.\+\)',
3114 \ 'call add(fromDepotMapping, [s:TranslateP4FileRegex('.
3115 \ "'".'\1'."'".', 0), s:TranslateP4FileRegex('."'".'\2'."'".
3117 exec substitute(nextMap,
3118 \ '\s*-\?//'.s:_('Depot').'/\([^ ]\+\)\s*//'.s:_("p4Client").'/\(.\+\)',
3119 \ 'call add(toDepotMapping, [s:TranslateP4FileRegex('.
3120 \ "'".'\2'."'".', 0), s:TranslateP4FileRegex('."'".'\1'."'".
3123 let s:fromDepotMapping[s:_('p4Client')] = fromDepotMapping
3124 let s:toDepotMapping[s:_('p4Client')] = toDepotMapping
3127 function! P4IncludeExpr(path)
3128 return s:ConvertToLocalPath(a:path)
3131 function! s:ConvertToLocalPath(path)
3132 let fileName = substitute(a:path, '^\s\+\|\s*#[^#]\+$', '', 'g')
3133 if s:IsDepotPath(fileName)
3134 if s:_('UseClientViewMap')
3135 call s:CondUpdateViewMappings()
3136 for nextMap in s:fromDepotMapping[s:_('p4Client')]
3137 let [lhs, rhs] = nextMap
3138 if fileName =~# '\V'.lhs
3139 let fileName = substitute(fileName, '\V'.lhs, rhs, '')
3144 if s:IsDepotPath(fileName)
3145 let fileName = s:_('ClientRoot') . s:StripRemotePath(fileName)
3151 function! s:ConvertToDepotPath(path)
3152 " If already a depot path, just return it without any changes.
3153 if s:IsDepotPath(a:path)
3154 let fileName = a:path
3156 let fileName = genutils#CleanupFileName(a:path)
3157 if s:IsFileUnderDepot(fileName)
3158 if s:_('UseClientViewMap')
3159 call s:CondUpdateViewMappings()
3160 for nextMap in s:toDepotMapping[s:_('p4Client')]
3161 let [lhs, rhs] = nextMap
3162 if fileName =~# '\V'.lhs
3163 let fileName = substitute(fileName, '\V'.lhs, rhs, '')
3168 if ! s:IsDepotPath(fileName)
3169 let fileName = substitute(fileName, '^'.s:_('ClientRoot'),
3170 \ '//'.s:_('Depot'), '')
3176 " Client view translation }}}
3178 " Requires at least 2 arguments.
3179 " Returns a List of alternative filenames.
3180 function! s:PFGetAltFiles(protectedChars, codeline, ...)
3185 let altCodeLine = a:codeline
3190 let fileName = a:000[i-1]
3191 let fileName = genutils#CleanupFileName2(fileName, a:protectedChars)
3193 if altCodeLine ==# 'local' && s:IsDepotPath(fileName)
3194 let altFile = s:ConvertToLocalPath(fileName)
3195 elseif ! s:IsDepotPath(fileName)
3196 let fileName = s:ConvertToDepotPath(fileName)
3198 if altCodeLine ==# s:_('Depot')
3199 " We do nothing, it is already converted to depot path.
3200 let altFile = fileName
3202 " FIXME: Assumes that the current branch name has single path component.
3203 let altFile = substitute(fileName, '//'.s:_('Depot').'/[^/]\+',
3204 \ '//'.s:_('Depot').'/' . altCodeLine, "")
3205 let altFile = s:ConvertToLocalPath(altFile)
3208 call add(altFiles, altFile)
3214 function! s:IsFileUnderDepot(fileName)
3215 let fileName = genutils#CleanupFileName(a:fileName)
3216 if fileName =~? '^\V'.s:_('ClientRoot')
3223 " This better take the line as argument, but I need the context of current
3224 " buffer contents anyway...
3225 " I don't need to handle other revision specifiers here, as I won't expect to
3226 " see them here (perforce converts them to the appropriate revision number).
3227 function! s:GetCurrentDepotFile(lineNo)
3228 " Local submissions.
3230 let line = getline(a:lineNo)
3231 if match(line, '//'.s:_('Depot').'/.*\(#\d\+\)\?') != -1
3232 " \ || match(line, '^//'. s:_('p4Client') . '/.*\(#\d\+\)\?') != -1
3233 let fileName = matchstr(line, '//[^/]\+/[^#]*\(#\d\+\)\?')
3234 elseif match(line, '\.\.\. #\d\+ .*') != -1
3235 " Branches, integrations etc.
3236 let fileVer = matchstr(line, '\d\+')
3237 call genutils#SaveHardPosition('Perforce')
3239 if search('^//'.s:_('Depot').'/', 'bW') == 0
3242 let fileName = substitute(s:GetCurrentDepotFile(line(".")), '#\d\+$', '',
3244 let fileName = fileName . "#" . fileVer
3246 call genutils#RestoreHardPosition('Perforce')
3247 call genutils#ResetHardPosition('Perforce')
3251 """ END: Filename handling }}}
3253 """ BEGIN: Buffer management, etc. {{{
3254 " Must be followed by a call to s:EndBufSetup()
3255 function! s:StartBufSetup()
3256 " If the command created a new window, then only do setup.
3258 if s:NewWindowCreated()
3259 if s:outputType == 1
3269 function! s:EndBufSetup()
3270 if s:NewWindowCreated()
3271 if s:outputType == 1
3277 " Goto/Open window for the current command.
3278 " clearBuffer (number):
3279 " 0 - clear with undo.
3280 " 1 - clear with no undo.
3282 function! s:GotoWindow(outputType, clearBuffer, p4OrgFileName, cmdCompleted)
3283 let bufNr = genutils#FindBufferForName(s:p4WinName)
3284 " NOTE: Precautionary measure to avoid accidentally matching an existing
3285 " buffer and thus overwriting the contents.
3286 if bufNr != -1 && getbufvar(bufNr, '&buftype') == ''
3287 return s:BufConflictError(a:cmdCompleted)
3290 " If there is a window for this buffer already, then we will just move
3292 let curBufnr = bufnr('%')
3293 let maxBufNr = bufnr('$')
3294 let bufWinnr = bufwinnr(bufNr)
3295 let nWindows = genutils#NumberOfWindows()
3296 let _eventignore = &eventignore
3298 "set eventignore=BufRead,BufReadPre,BufEnter,BufNewFile
3300 if a:outputType == 1 " Preview
3304 " No exception, meaning preview window is already open.
3305 if winnr() == bufWinnr
3306 " The buffer is already visible in the preview window. We don't have
3307 " to do anything in this case.
3310 catch /^Vim\%((\a\+)\)\=:E441/
3314 call s:EditP4WinName(1, nWindows)
3317 elseif bufWinnr != -1
3318 call genutils#MoveCursorToWindow(bufWinnr)
3320 exec s:_('SplitCommand')
3321 call s:EditP4WinName(0, nWindows)
3324 " FIXME: If the name didn't originally match with a buffer, we expect
3325 " the s:EditP4WinName() to create a new buffer, but there is a bug in
3326 " Vim, that treats "..." in filenames as ".." resulting in multiple
3327 " names matching the same buffer ( "p4 diff ../.../*.java" and
3328 " "p4 submit ../.../*.java" e.g.). Though I worked around this
3329 " particular bug by avoiding "..." in filenames, this is a good check
3331 if bufNr == -1 && bufnr('%') <= maxBufNr
3332 return s:BufConflictError(a:cmdCompleted)
3337 catch /^Vim\%((\a\+)\)\=:E788/ " Happens during FileChangedRO.
3340 return s:ShowVimError("Exception while opening new window.\n" . v:exception,
3343 let &eventignore = _eventignore
3345 " We now have a new window created, but may be with errors.
3349 if s:commandMode ==# s:CM_RUN
3350 if a:clearBuffer == 1
3351 call genutils#OptClearBuffer()
3352 elseif a:clearBuffer == 0
3357 let b:p4OrgFileName = a:p4OrgFileName
3358 call s:PFSetupBuf(expand('%'))
3360 " Window is created but with an error. We might actually miss the cases
3361 " where a preview operation when the preview window is already open
3362 " fails, and so no additional windows are created, but detecting such
3363 " cases could be error prone, so it is better to leave the buffer in
3364 " this case, rather than making a mistake.
3365 if genutils#NumberOfWindows() > nWindows
3366 if winbufnr(winnr()) == curBufnr " Error creating buffer itself.
3368 elseif bufname('%') == s:p4WinName
3369 " This should even close the window.
3370 silent! exec "bwipeout " . bufnr('%')
3377 function! s:BufConflictError(cmdCompleted)
3378 return s:ShowVimError('This perforce command resulted in matching an '.
3379 \ 'existing buffer. To prevent any demage this could cause '.
3380 \ 'the command will be aborted at this point.'.
3381 \ (a:cmdCompleted ? ("\nHowever the command completed ".
3382 \ (s:errCode ? 'un' : ''). 'successfully.') : ''), '')
3385 function! s:EditP4WinName(preview, nWindows)
3389 let pWindowWasOpen = (genutils#GetPreviewWinnr() != -1)
3390 " Some patterns can cause problems.
3391 let _wildignore = &wildignore
3394 exec (a:preview?'p':'').'edit' s:p4WinName
3395 catch /^Vim\%((\a\+)\)\=:E303/
3396 " This is a non-fatal error.
3397 let bug = 1 | let exception = v:exception
3398 let stack = v:throwpoint
3399 catch /^Vim\%((\a\+)\)\=:\%(E77\|E480\)/
3400 let bug = 1 | let exception = v:exception | let fatal = 1
3401 let stack = v:throwpoint
3403 let exception = v:exception | let fatal = 1
3404 let stack = v:throwpoint
3406 let &wildignore = _wildignore
3409 call s:ShowVimError(exception, '')
3413 echomsg "Please report this error message:"
3414 echomsg "\t".exception
3416 echomsg "with the following information:"
3417 echomsg "\ts:p4WinName:" s:p4WinName
3418 echomsg "\tCurrent stack:" stack
3421 " For non preview operation, or for preview window operation when the preview
3422 " window is not already visible, we expect the number of windows to go up.
3423 if !a:preview || (a:preview && !pWindowWasOpen)
3424 if a:nWindows >= genutils#NumberOfWindows()
3430 function! s:MakeWindowName(...)
3431 " Let only the options that are explicitly specified appear in the window
3436 let cmdStr = 'p4 '.join(s:MakeP4ArgList(s:p4Options, 0), ' ')
3438 let winName = cmdStr
3439 "let winName = genutils#DeEscape(winName)
3440 " HACK: Work-around for some weird handling of buffer names that have "..."
3441 " (the perforce wildcard) at the end of the filename or in the middle
3442 " followed by a space. The autocommand is not getting triggered to clean
3443 " the buffer. If we append another character to this, I observed that the
3444 " autocommand gets triggered. Using "/" instead of "'" would probably be
3445 " more appropriate, but this is causing unexpected FileChangedShell
3446 " autocommands on certain filenames (try "PF submit ../..." e.g.). There
3447 " is also another issue with "..." (anywhere) getting treated as ".."
3448 " resulting in two names matching the same buffer(
3449 " "p4 diff ../.../*.java" and "p4 submit ../.../*.java" e.g.). This
3450 " could also change the name of the buffer during the :cd operations
3451 " (though applies only to spec buffers).
3452 "let winName = substitute(winName, '\.\.\%( \|$\)\@=', '&/', 'g')
3453 "let winName = substitute(winName, '\.\.\%( \|$\)\@=', "&'", 'g')
3454 let winName = substitute(winName, '\.\.\.', '..,', 'g')
3455 " The intention is to do the substitute only on systems like windoze that
3456 " don't allow all characters in the filename, but I can't generalize it
3457 " enough, so as a workaround I a just assuming any system supporting
3458 " 'shellslash' option to be a windoze like system. In addition, cygwin
3459 " vim thinks that it is on Unix and tries to allow all characters, but
3460 " since the underlying OS doesn't support it, we need the same treatment
3462 if exists('+shellslash') || has('win32unix')
3463 " Some characters are not allowed in a filename on windows so substitute
3464 " them with something else.
3465 let winName = substitute(winName, s:specialChars,
3466 \ '\="[" . s:specialCharsMap[submatch(1)] . "]"', 'g')
3467 "let winName = substitute(winName, s:specialChars, '\\\1', 'g')
3469 " Finally escape some characters again.
3470 let winName = genutils#Escape(winName, " #%\t")
3471 if ! exists('+shellslash') " Assuming UNIX environment.
3472 let winName = substitute(winName, '\\\@<!\(\%(\\\\\)*\\[^ ]\)', '\\\1', 'g')
3473 let winName = escape(winName, "'~$`{\"")
3478 function! s:PFSetupBuf(bufName)
3479 call genutils#SetupScratchBuffer()
3480 let &l:bufhidden=s:_('BufHidden')
3483 function! s:PFSetupForSpec()
3486 exec 'aug Perforce'.bufnr('%')
3488 au BufWriteCmd <buffer> nested :W
3492 function! perforce#WipeoutP4Buffers(...)
3494 if a:0 > 0 && a:1 ==# '++y'
3498 let lastBuf = bufnr('$')
3499 let cleanedBufs = ''
3501 if bufexists(i) && expand('#'.i) =~# '\<p4 ' && bufwinnr(i) == -1
3503 let cleanedBufs = cleanedBufs . ', ' . expand('#'.i)
3505 silent! exec 'bwipeout' i
3506 let cleanedBufs = cleanedBufs + 1
3512 echo "Buffers that will be wipedout (Use ++y to perform action):" .
3515 echo "Total Perforce buffers wipedout (start with 'p4 '): " . cleanedBufs
3519 function! perforce#PFRefreshActivePane()
3520 if exists("b:p4UserArgs")
3521 call genutils#SaveSoftPosition('Perforce')
3525 call call('perforce#PFrangeIF', b:p4UserArgs)
3527 call s:ShowVimError(v:exception, v:throwpoint)
3530 call genutils#RestoreSoftPosition('Perforce')
3531 call genutils#ResetSoftPosition('Perforce')
3534 """ END: Buffer management, etc. }}}
3536 """ BEGIN: Testing {{{
3537 " Ex: PFTestCmdParse -c client -u user integrate -b branch -s source target1 target2
3538 command! -nargs=* -range=% -complete=file PFTestCmdParse
3539 \ :call <SID>TestParseOptions(<f-args>)
3540 function! s:TestParseOptions(commandName, ...) range
3541 call call('s:ParseOptionsIF', [a:firstline, a:lastline, 0, 0, a:commandName]+
3543 call s:DebugP4Status()
3546 function! s:DebugP4Status()
3547 echo "p4Options :" . join(s:p4Options, ' ') . ":"
3548 echo "p4Command :" . s:p4Command . ":"
3549 echo "p4CmdOptions :" . join(s:p4CmdOptions, ' ') . ":"
3550 echo "p4Arguments :" . join(s:p4Arguments, ' ') . ":"
3551 echo "p4Pipe :" . join(s:p4Pipe, ' ') . ":"
3552 echo "p4WinName :" . s:p4WinName . ":"
3553 echo "outputType :" . s:outputType . ":"
3554 echo "errCode :" . s:errCode . ":"
3555 echo "commandMode :" . s:commandMode . ":"
3556 echo "filterRange :" . s:filterRange . ":"
3557 echo "Cmd :" . s:CreateFullCmd(s:MakeP4ArgList([''], 0)) . ":"
3560 "function! s:TestPushPopContexts()
3561 " let s:p4Options = ["options1"]
3562 " let s:p4Command = "command1"
3563 " let s:p4CmdOptions = ["cmdOptions1"]
3564 " let s:p4Arguments = ["arguments1"]
3565 " let s:p4WinName = "winname1"
3566 " call s:PushP4Context()
3568 " let s:p4Options = ["options2"]
3569 " let s:p4Command = "command2"
3570 " let s:p4CmdOptions = ["cmdOptions2"]
3571 " let s:p4Arguments = ["arguments2"]
3572 " let s:p4WinName = "winname2"
3573 " call s:PushP4Context()
3575 " call s:ResetP4ContextVars()
3576 " echo "After reset: " . s:CreateFullCmd(s:MakeP4ArgList([''], 0))
3577 " call s:PopP4Context()
3578 " echo "After pop1: " . s:CreateFullCmd(s:MakeP4ArgList([''], 0))
3579 " call s:PopP4Context()
3580 " echo "After pop2: " . s:CreateFullCmd(s:MakeP4ArgList([''], 0))
3583 """ END: Testing }}}
3585 """ BEGIN: Experimental API {{{
3587 function! perforce#PFGet(var)
3591 function! perforce#PFSet(var, val)
3595 function! perforce#PFCall(func, ...)
3596 let result = call(a:func, a:000)
3600 function! perforce#PFEval(expr)
3601 exec "let result = ".a:expr
3605 """ END: Experimental API }}}
3607 function! perforce#Initialize(initMenu) " {{{
3611 if g:p4ClientRoot != ''
3612 let g:p4ClientRoot = genutils#CleanupFileName(g:p4ClientRoot)
3614 if type(g:p4DefaultListSize) == 0
3615 let g:p4DefaultListSize = string(g:p4DefaultListSize)
3617 if g:p4FileLauncher == '' && genutils#OnMS()
3618 let g:p4FileLauncher = "start rundll32 SHELL32.DLL,ShellExec_RunDLL"
3620 if g:p4DefaultPreset != -1 &&
3621 \ g:p4DefaultPreset.'' !~# s:EMPTY_STR
3622 call perforce#PFSwitch(1, g:p4DefaultPreset)
3626 " Assume the user already has the preferred rulerformat set (which is anyway
3627 " going to be done through the .vimrc file which should have been sourced by
3630 " Take care of rerunning this code, as the reinitialization can happen any
3632 if !exists("s:orgRulerFormat")
3633 let s:orgRulerFormat = &rulerformat
3635 let &rulerformat = s:orgRulerFormat
3638 if &rulerformat != ""
3639 if match(&rulerformat, '^%\d\+') == 0
3640 let orgWidth = substitute(&rulerformat, '^%\(\d\+\)(.*$',
3642 let orgRuler = substitute(&rulerformat, '^%\d\+(\(.*\)%)$', '\1', '')
3644 let orgWidth = strlen(&rulerformat) " Approximate.
3645 let orgRuler = &rulerformat
3649 let orgRuler = '%l,%c%V%=%5(%p%%%)'
3651 let &rulerformat = '%' . (orgWidth + g:p4RulerWidth) . '(%{' .
3652 \ 'perforce#RulerStatus()}%=' . orgRuler . '%)'
3654 if exists("s:orgRulerFormat")
3655 let &rulerformat = s:orgRulerFormat
3663 if g:p4EnableActiveStatus
3664 au BufRead * call perforce#GetFileStatus(expand('<abuf>') + 0, 0)
3671 runtime! perforce/perforcemenu.vim
3675 let g:p4PromptToCheckout = ! g:p4PromptToCheckout
3676 call perforce#ToggleCheckOutPrompt(0)
3678 endfunction " s:Initialize }}}
3680 """ END: Infrastructure }}}
3682 " Do some initializations.
3683 if g:p4DefaultPreset != -1 &&
3684 \ g:p4DefaultPreset.'' !~# s:EMPTY_STR
3685 call perforce#PFSwitch(0, g:p4DefaultPreset)
3690 if g:p4ClientRoot =~# s:EMPTY_STR || s:p4Client =~# s:EMPTY_STR ||
3691 \ s:p4User =~# s:EMPTY_STR
3692 if s:_('EnableActiveStatus')
3693 " If Vim is still starting up (construct suggested by Eric Arnold).
3694 if bufnr("$") == 1 && !bufloaded(1)
3695 au VimEnter * call <SID>GetClientInfo() | au! P4ClientRoot
3697 call s:GetClientInfo()
3700 let g:p4ClientRoot = fnamemodify(".", ":p")
3705 call perforce#Initialize(0)
3707 " WORKAROUND for :redir broken, when called from completion function... just
3708 " make sure this is initialized early.
3709 call genutils#MakeArgumentString()
3712 let &cpo = s:save_cpo
3715 " vim6:fdm=marker et sw=2