Added Perforce plugin.
[profile.git] / .vim / autoload / perforceutils.vim
1 " perforceutils.vim: Please see perforce/perforceutils.vim
2
3 " Make sure line-continuations won't cause any problem. This will be restored
4 "   at the end
5 let s:save_cpo = &cpo
6 set cpo&vim
7
8 " Determine the script id.
9 function! s:MyScriptId()
10   map <SID>xx <SID>xx
11   let s:sid = maparg("<SID>xx")
12   unmap <SID>xx
13   return substitute(s:sid, "xx$", "", "")
14 endfunction
15 let s:myScriptId = s:MyScriptId()
16 delfunction s:MyScriptId " This is not needed anymore.
17
18 " CAUTION: Don't assume the existence of plugin/perforce.vim (or any other
19 "   plugins) at the time this file is sourced.
20
21 " DiffLink {{{
22  
23 " Open the source line for the current line from the diff.
24 function! perforceutils#DiffOpenSrc(preview) " {{{
25   let s:EMPTY_STR = perforce#PFEval('s:EMPTY_STR')
26   if perforce#PFEval('s:GetCurrentItem()') !~# s:EMPTY_STR
27     PItemOpen
28   endif
29   call genutils#SaveHardPosition('DiffOpenSrc')
30   " Move to the end of next line (if possible), so that the search will work
31   " correctly when the cursor is ON the header (should find the current line).
32   normal $
33   let filePat = '\zs[^#]\+\%(#\d\+\)\=\ze\%( ([^)]\+)\)\='
34   let diffHdr = '^diff \%(-\S\+\s\+\)*'
35   " Search backwards to find the header for this diff (could contain two
36   " depot files or one depot file with or without a local file).
37   if search('^==== '.filePat.'\%( - '.filePat.'\)\= ====', 'bW')
38     let firstFile = matchstr(getline('.'), '^==== \zs'.filePat.
39           \ '\%( - \| ====\)')
40     let secondFile = matchstr(getline('.'), ' - '.filePat.' ====',
41           \ strlen(firstFile)+5)
42     let foundHeader = 1
43
44   " GNU diff header.
45   elseif search('^--- '.filePat.'.*\n\_^+++ '.filePat, 'bW')
46     let firstFile = matchstr(getline(line('.')-1), '^--- \zs.\{-}\ze\t')
47     let secondFile = matchstr(getline('.'), '^+++ \zs.\{-}\ze\t')
48     let foundHeader = 1
49
50   " Another GNU diff header, for default output (typically for -r option).
51   elseif search(diffHdr.filePat.' '.filePat, 'bW')
52     exec substitute(substitute(getline('.'),
53           \ diffHdr.'\('.filePat.'\) \('.filePat.'\)',
54           \ ":::let firstFile = '\\1' | let secondFile = '\\2'", ''),
55           \ '^.*:::', '', '')
56     let foundHeader = 1
57   else
58     let foundHeader = 0
59   endif
60   if foundHeader
61     call genutils#RestoreHardPosition('DiffOpenSrc')
62     if firstFile =~# s:EMPTY_STR
63       return
64     elseif secondFile =~# s:EMPTY_STR
65       " When there is only one file, then it is treated as the secondFile.
66       let secondFile = firstFile
67       let firstFile = ''
68     endif
69
70     " Search for the start of the diff segment. We could be in default,
71     " context or unified mode. Determine context, stLine and offset.
72     if search('^\d\+\%(,\d\+\)\=[adc]\d\+\%(,\d\+\)\=$', 'bW') " default.
73       let segStLine = line('.')
74       let segHeader = getline('.')
75       call genutils#RestoreHardPosition('DiffOpenSrc')
76       let context = 'depot'
77       let regPre = '^'
78       if getline('.') =~# '^>'
79         let context = 'local'
80         let regPre = '[cad]'
81         if search('^---$', 'bW') && line('.') > segStLine
82           let segStLine = line('.')
83         endif
84       endif
85       let stLine = matchstr(segHeader, regPre.'\zs\d\+\ze')
86       call genutils#RestoreHardPosition('DiffOpenSrc')
87       let offset = line('.') - segStLine - 1
88     elseif search('\([*-]\)\1\1 \d\+,\d\+ \1\{4}', 'bW') " context.
89       if getline('.') =~# '^-'
90         let context = 'local'
91       else
92         let context = 'depot'
93       endif
94       let stLine = matchstr(getline('.'), '^[*-]\{3} \zs\d\+\ze,')
95       let segStLine = line('.')
96       call genutils#RestoreHardPosition('DiffOpenSrc')
97       let offset = line('.') - segStLine - 1
98     elseif search('^@@ -\=\d\+,\d\+ +\=\d\+,\d\+ @@$', 'bW') " unified
99       let segStLine = line('.')
100       let segHeader = getline('.')
101       call genutils#RestoreHardPosition('DiffOpenSrc')
102       let context = 'local'
103       let sign = '+'
104       if getline('.') =~# '^-'
105         let context = 'depot'
106         let sign = '-'
107       endif
108       let stLine = matchstr(segHeader, ' '.sign.'\zs\d\+\ze,\d\+')
109       let _ma = &l:modifiable
110       try
111         setl modifiable
112         " Count the number of lines that come from the other side (those lines
113         "   that start with an opposite sign).
114         let _ss = @/ | let @/ = '^'.substitute('-+', sign, '', '') |
115               \ let offOffset = matchstr(genutils#GetVimCmdOutput( segStLine.',.s//&/'),
116               \ '\d\+\ze substitutions\? on \d\+ lines\?') + 0 | let @/ = _ss
117         if offOffset > 0
118           silent! undo
119           call genutils#RestoreHardPosition('DiffOpenSrc')
120         endif
121         let offset = line('.') - segStLine - 1 - offOffset
122       finally
123         let &l:modifiable = _ma
124       endtry
125     else " Not inside a diff context, just use 1.
126       let context = 'local'
127       let stLine = 1
128       let offset = 0
129     endif
130
131     try
132       if context ==# 'depot' && firstFile =~# s:EMPTY_STR
133         " Assume previous revision as the "before" file if none specified.
134         if perforce#PFCall('s:IsDepotPath', secondFile) && secondFile =~# '#\d\+'
135           let depotRev = s:GetFileRevision(secondFile)
136           if depotRev == ''
137             return
138           endif
139           let firstFile = substitute(secondFile, '#\d\+', '', '').'#'.(depotRev-1)
140         else
141           return
142         endif
143       endif
144       if context ==# 'local'
145         let file = secondFile
146       else
147         let file = firstFile
148       endif
149       " If the path refers to a depot file, check if the local file is currently
150       " open in Vim and if so has the same version number as the depot file.
151       if context ==# 'local' && perforce#PFCall('s:IsDepotPath', file)
152         let localFile = perforce#PFCall('s:ConvertToLocalPath', file)
153         let bufNr = bufnr(localFile) + 0
154         if bufNr != -1
155           let haveRev = getbufvar(bufNr, 'p4HaveRev')
156           let depotRev = s:GetFileRevision(file)
157           if haveRev == depotRev
158             let file = localFile
159           endif
160           " else " We could also try to run 'fstat' command and open up the file.
161         endif
162       endif
163       if perforce#PFCall('s:IsDepotPath', file)
164         let refresh = perforce#PFGet('s:refreshWindowsAlways')
165         try
166           call perforce#PFSet('s:refreshWindowsAlways', 0)
167           call perforce#PFIF(1, a:preview, 'print', file)
168         finally
169           call perforce#PFSet('s:refreshWindowsAlways', refresh)
170         endtry
171         let offset = offset + 1 " For print header.
172       else
173         call perforce#PFCall('s:OpenFile', 1, a:preview, genutils#CleanupFileName(file))
174       endif
175       if perforce#PFEval('s:errCode') == 0
176         if a:preview
177           wincmd P
178         endif
179         mark '
180         exec (stLine + offset)
181         if a:preview
182           " Also works as a work-around for the buffer not getting scrolled.
183           normal! z.
184           wincmd p
185         endif
186       endif
187     catch
188       call perforce#PFCall('s:EchoMessage', v:exception, 'Error')
189     endtry
190   endif
191 endfunction " }}}
192
193 function! perforceutils#SetupDiffLink()
194   command! -buffer -nargs=0 PDiffLink :PFDiffLink
195   command! -buffer -nargs=0 PDiffPLink :PFDiffPLink
196   nnoremap <buffer> <silent> O :PDiffLink<CR>
197   nnoremap <buffer> <silent> <CR> :PDiffPLink<CR>
198 endfunction
199
200 function! s:GetFileRevision(depotPath)
201   let rev = matchstr(a:depotPath, '#\zs\d\+$')
202   return (rev !~# s:EMPTY_STR) ? rev + 0 : ''
203 endfunction
204
205 " DiffLink }}}
206
207 " ShowConflicts {{{
208 function! perforceutils#ShowConflicts()
209   let _splitright = &splitright
210   set splitright
211   try
212     let curFile = expand('%:p')
213     "exec 'split' curFile.'.Original'
214     exec 'edit' curFile.'.Original'
215     silent! exec 'read' curFile
216     silent! 1delete _
217     call genutils#SilentSubstitute('^==== THEIRS \_.\{-}\%(^<<<<$\)\@=', '%s///e')
218     call genutils#SilentDelete('\%(^>>>> ORIGINAL \|^==== THEIRS\|^==== YOURS \|^<<<<$\)')
219     call genutils#SetupScratchBuffer()
220     setlocal nomodifiable
221     diffthis
222
223     exec 'vsplit' curFile.'.Theirs'
224     silent! exec 'read' curFile
225     1delete _
226     call genutils#SilentSubstitute('^>>>> ORIGINAL \_.\{-}\%(^==== THEIRS \)\@=', '%s///e')
227     call genutils#SilentSubstitute('^==== YOURS \_.\{-}\%(^<<<<$\)\@=', '%s///e')
228     call genutils#SilentDelete('\%(^>>>> ORIGINAL \|^==== THEIRS\|^==== YOURS \|^<<<<$\)')
229     call genutils#SetupScratchBuffer()
230     setlocal nomodifiable
231     diffthis
232
233     exec 'vsplit' curFile.'.Yours'
234     silent! exec 'read' curFile
235     1delete _
236     call genutils#SilentSubstitute('^>>>> ORIGINAL \_.\{-}\%(^==== YOURS \)\@=', '%s///e')
237     call genutils#SilentDelete('\%(^>>>> ORIGINAL \|^==== THEIRS\|^==== YOURS \|^<<<<$\)')
238     call genutils#SetupScratchBuffer()
239     setlocal buftype=
240     setlocal nomodified
241     exec "au Perforce BufWriteCmd <buffer> :call <SID>SaveYours('".curFile."')"
242     diffthis
243   finally
244     let &splitright = _splitright
245   endtry
246 endfunction
247
248 function! s:SaveYours(orgFile)
249   if confirm('Do you want to accept the changes in "'.expand("%:p:t").'"?',
250         \ "&Yes\n&No", 2, "Question") == 1
251     exec 'w!' a:orgFile
252   endif
253 endfunction
254 " ShowConflicts }}}
255
256 " Restore cpo.
257 let &cpo = s:save_cpo
258 unlet s:save_cpo
259
260 " vim6:fdm=marker et sw=2