##// END OF EJS Templates
spelling: implementing
timeless@mozdev.org -
r17496:223328c6 default
parent child Browse files
Show More
@@ -1,868 +1,868 b''
1 " VIM plugin for doing single, multi-patch or diff code reviews {{{
1 " VIM plugin for doing single, multi-patch or diff code reviews {{{
2 " Home: http://www.vim.org/scripts/script.php?script_id=1563
2 " Home: http://www.vim.org/scripts/script.php?script_id=1563
3
3
4 " Version : 0.2.2 "{{{
4 " Version : 0.2.2 "{{{
5 " Author : Manpreet Singh < junkblocker@yahoo.com >
5 " Author : Manpreet Singh < junkblocker@yahoo.com >
6 " Copyright : 2006-2010 by Manpreet Singh
6 " Copyright : 2006-2010 by Manpreet Singh
7 " License : This file is placed in the public domain.
7 " License : This file is placed in the public domain.
8 " No warranties express or implied. Use at your own risk.
8 " No warranties express or implied. Use at your own risk.
9 "
9 "
10 " Changelog :
10 " Changelog :
11 "
11 "
12 " 0.2.2 - Security fixes by removing custom tempfile creation
12 " 0.2.2 - Security fixes by removing custom tempfile creation
13 " - Removed need for DiffReviewCleanup/PatchReviewCleanup
13 " - Removed need for DiffReviewCleanup/PatchReviewCleanup
14 " - Better command execution error detection and display
14 " - Better command execution error detection and display
15 " - Improved diff view and folding by ignoring modelines
15 " - Improved diff view and folding by ignoring modelines
16 " - Improved tab labels display
16 " - Improved tab labels display
17 "
17 "
18 " 0.2.1 - Minor temp directory autodetection logic and cleanup
18 " 0.2.1 - Minor temp directory autodetection logic and cleanup
19 "
19 "
20 " 0.2 - Removed the need for filterdiff by implemeting it in pure vim script
20 " 0.2 - Removed the need for filterdiff by implementing it in pure vim script
21 " - Added DiffReview command for reverse (changed repository to
21 " - Added DiffReview command for reverse (changed repository to
22 " pristine state) reviews.
22 " pristine state) reviews.
23 " (PatchReview does pristine repository to patch review)
23 " (PatchReview does pristine repository to patch review)
24 " - DiffReview does automatic detection and generation of diffs for
24 " - DiffReview does automatic detection and generation of diffs for
25 " various Source Control systems
25 " various Source Control systems
26 " - Skip load if VIM 7.0 or higher unavailable
26 " - Skip load if VIM 7.0 or higher unavailable
27 "
27 "
28 " 0.1 - First released
28 " 0.1 - First released
29 "}}}
29 "}}}
30
30
31 " Documentation: "{{{
31 " Documentation: "{{{
32 " ===========================================================================
32 " ===========================================================================
33 " This plugin allows single or multiple, patch or diff based code reviews to
33 " This plugin allows single or multiple, patch or diff based code reviews to
34 " be easily done in VIM. VIM has :diffpatch command to do single file reviews
34 " be easily done in VIM. VIM has :diffpatch command to do single file reviews
35 " but a) can not handle patch files containing multiple patches or b) do
35 " but a) can not handle patch files containing multiple patches or b) do
36 " automated diff generation for various version control systems. This plugin
36 " automated diff generation for various version control systems. This plugin
37 " attempts to provide those functionalities. It opens each changed / added or
37 " attempts to provide those functionalities. It opens each changed / added or
38 " removed file diff in new tabs.
38 " removed file diff in new tabs.
39 "
39 "
40 " Installing:
40 " Installing:
41 "
41 "
42 " For a quick start, unzip patchreview.zip into your ~/.vim directory and
42 " For a quick start, unzip patchreview.zip into your ~/.vim directory and
43 " restart Vim.
43 " restart Vim.
44 "
44 "
45 " Details:
45 " Details:
46 "
46 "
47 " Requirements:
47 " Requirements:
48 "
48 "
49 " 1) VIM 7.0 or higher built with +diff option.
49 " 1) VIM 7.0 or higher built with +diff option.
50 "
50 "
51 " 2) A gnu compatible patch command installed. This is the standard patch
51 " 2) A gnu compatible patch command installed. This is the standard patch
52 " command on Linux, Mac OS X, *BSD, Cygwin or /usr/bin/gpatch on newer
52 " command on Linux, Mac OS X, *BSD, Cygwin or /usr/bin/gpatch on newer
53 " Solaris.
53 " Solaris.
54 "
54 "
55 " 3) Optional (but recommended for speed)
55 " 3) Optional (but recommended for speed)
56 "
56 "
57 " Install patchutils ( http://cyberelk.net/tim/patchutils/ ) for your
57 " Install patchutils ( http://cyberelk.net/tim/patchutils/ ) for your
58 " OS. For windows it is availble from Cygwin
58 " OS. For windows it is availble from Cygwin
59 "
59 "
60 " http://www.cygwin.com
60 " http://www.cygwin.com
61 "
61 "
62 " or GnuWin32
62 " or GnuWin32
63 "
63 "
64 " http://gnuwin32.sourceforge.net/
64 " http://gnuwin32.sourceforge.net/
65 "
65 "
66 " Install:
66 " Install:
67 "
67 "
68 " 1) Extract the zip in your $HOME/.vim or $VIM/vimfiles directory and
68 " 1) Extract the zip in your $HOME/.vim or $VIM/vimfiles directory and
69 " restart vim. The directory location relevant to your platform can be
69 " restart vim. The directory location relevant to your platform can be
70 " seen by running :help add-global-plugin in vim.
70 " seen by running :help add-global-plugin in vim.
71 "
71 "
72 " 2) Restart vim.
72 " 2) Restart vim.
73 "
73 "
74 " Configuration:
74 " Configuration:
75 "
75 "
76 " Optionally, specify the locations to these filterdiff and patch commands
76 " Optionally, specify the locations to these filterdiff and patch commands
77 " and location of a temporary directory to use in your .vimrc.
77 " and location of a temporary directory to use in your .vimrc.
78 "
78 "
79 " let g:patchreview_patch = '/path/to/gnu/patch'
79 " let g:patchreview_patch = '/path/to/gnu/patch'
80 "
80 "
81 " " If you are using filterdiff
81 " " If you are using filterdiff
82 " let g:patchreview_filterdiff = '/path/to/filterdiff'
82 " let g:patchreview_filterdiff = '/path/to/filterdiff'
83 "
83 "
84 "
84 "
85 " Usage:
85 " Usage:
86 "
86 "
87 " Please see :help patchreview or :help diffreview for details.
87 " Please see :help patchreview or :help diffreview for details.
88 "
88 "
89 ""}}}
89 ""}}}
90
90
91 " Enabled only during development
91 " Enabled only during development
92 " unlet! g:loaded_patchreview " DEBUG
92 " unlet! g:loaded_patchreview " DEBUG
93 " unlet! g:patchreview_patch " DEBUG
93 " unlet! g:patchreview_patch " DEBUG
94 " unlet! g:patchreview_filterdiff " DEBUG
94 " unlet! g:patchreview_filterdiff " DEBUG
95 " let g:patchreview_patch = 'patch' " DEBUG
95 " let g:patchreview_patch = 'patch' " DEBUG
96
96
97 if v:version < 700
97 if v:version < 700
98 finish
98 finish
99 endif
99 endif
100 if ! has('diff')
100 if ! has('diff')
101 call confirm('patchreview.vim plugin needs (G)VIM built with +diff support to work.')
101 call confirm('patchreview.vim plugin needs (G)VIM built with +diff support to work.')
102 finish
102 finish
103 endif
103 endif
104
104
105 " load only once
105 " load only once
106 if (! exists('g:patchreview_debug') && exists('g:loaded_patchreview')) || &compatible
106 if (! exists('g:patchreview_debug') && exists('g:loaded_patchreview')) || &compatible
107 finish
107 finish
108 endif
108 endif
109 let g:loaded_patchreview="0.2.2"
109 let g:loaded_patchreview="0.2.2"
110
110
111 let s:msgbufname = '-PatchReviewMessages-'
111 let s:msgbufname = '-PatchReviewMessages-'
112
112
113 function! <SID>Debug(str) "{{{
113 function! <SID>Debug(str) "{{{
114 if exists('g:patchreview_debug')
114 if exists('g:patchreview_debug')
115 Pecho 'DEBUG: ' . a:str
115 Pecho 'DEBUG: ' . a:str
116 endif
116 endif
117 endfunction
117 endfunction
118 command! -nargs=+ -complete=expression Debug call s:Debug(<args>)
118 command! -nargs=+ -complete=expression Debug call s:Debug(<args>)
119 "}}}
119 "}}}
120
120
121 function! <SID>PR_wipeMsgBuf() "{{{
121 function! <SID>PR_wipeMsgBuf() "{{{
122 let winnum = bufwinnr(s:msgbufname)
122 let winnum = bufwinnr(s:msgbufname)
123 if winnum != -1 " If the window is already open, jump to it
123 if winnum != -1 " If the window is already open, jump to it
124 let cur_winnr = winnr()
124 let cur_winnr = winnr()
125 if winnr() != winnum
125 if winnr() != winnum
126 exe winnum . 'wincmd w'
126 exe winnum . 'wincmd w'
127 exe 'bw'
127 exe 'bw'
128 exe cur_winnr . 'wincmd w'
128 exe cur_winnr . 'wincmd w'
129 endif
129 endif
130 endif
130 endif
131 endfunction
131 endfunction
132 "}}}
132 "}}}
133
133
134 function! <SID>Pecho(...) "{{{
134 function! <SID>Pecho(...) "{{{
135 " Usage: Pecho(msg, [return_to_original_window_flag])
135 " Usage: Pecho(msg, [return_to_original_window_flag])
136 " default return_to_original_window_flag = 0
136 " default return_to_original_window_flag = 0
137 "
137 "
138 let cur_winnr = winnr()
138 let cur_winnr = winnr()
139 let winnum = bufwinnr(s:msgbufname)
139 let winnum = bufwinnr(s:msgbufname)
140 if winnum != -1 " If the window is already open, jump to it
140 if winnum != -1 " If the window is already open, jump to it
141 if winnr() != winnum
141 if winnr() != winnum
142 exe winnum . 'wincmd w'
142 exe winnum . 'wincmd w'
143 endif
143 endif
144 else
144 else
145 let bufnum = bufnr(s:msgbufname)
145 let bufnum = bufnr(s:msgbufname)
146 if bufnum == -1
146 if bufnum == -1
147 let wcmd = s:msgbufname
147 let wcmd = s:msgbufname
148 else
148 else
149 let wcmd = '+buffer' . bufnum
149 let wcmd = '+buffer' . bufnum
150 endif
150 endif
151 exe 'silent! botright 5split ' . wcmd
151 exe 'silent! botright 5split ' . wcmd
152 endif
152 endif
153 setlocal modifiable
153 setlocal modifiable
154 setlocal buftype=nofile
154 setlocal buftype=nofile
155 setlocal bufhidden=delete
155 setlocal bufhidden=delete
156 setlocal noswapfile
156 setlocal noswapfile
157 setlocal nowrap
157 setlocal nowrap
158 setlocal nobuflisted
158 setlocal nobuflisted
159 if a:0 != 0
159 if a:0 != 0
160 silent! $put =a:1
160 silent! $put =a:1
161 endif
161 endif
162 exe ':$'
162 exe ':$'
163 setlocal nomodifiable
163 setlocal nomodifiable
164 if a:0 > 1 && a:2
164 if a:0 > 1 && a:2
165 exe cur_winnr . 'wincmd w'
165 exe cur_winnr . 'wincmd w'
166 endif
166 endif
167 endfunction
167 endfunction
168
168
169 command! -nargs=+ -complete=expression Pecho call s:Pecho(<args>)
169 command! -nargs=+ -complete=expression Pecho call s:Pecho(<args>)
170 "}}}
170 "}}}
171
171
172 function! <SID>PR_checkBinary(BinaryName) "{{{
172 function! <SID>PR_checkBinary(BinaryName) "{{{
173 " Verify that BinaryName is specified or available
173 " Verify that BinaryName is specified or available
174 if ! exists('g:patchreview_' . a:BinaryName)
174 if ! exists('g:patchreview_' . a:BinaryName)
175 if executable(a:BinaryName)
175 if executable(a:BinaryName)
176 let g:patchreview_{a:BinaryName} = a:BinaryName
176 let g:patchreview_{a:BinaryName} = a:BinaryName
177 return 1
177 return 1
178 else
178 else
179 Pecho 'g:patchreview_' . a:BinaryName . ' is not defined and ' . a:BinaryName . ' command could not be found on path.'
179 Pecho 'g:patchreview_' . a:BinaryName . ' is not defined and ' . a:BinaryName . ' command could not be found on path.'
180 Pecho 'Please define it in your .vimrc.'
180 Pecho 'Please define it in your .vimrc.'
181 return 0
181 return 0
182 endif
182 endif
183 elseif ! executable(g:patchreview_{a:BinaryName})
183 elseif ! executable(g:patchreview_{a:BinaryName})
184 Pecho 'Specified g:patchreview_' . a:BinaryName . ' [' . g:patchreview_{a:BinaryName} . '] is not executable.'
184 Pecho 'Specified g:patchreview_' . a:BinaryName . ' [' . g:patchreview_{a:BinaryName} . '] is not executable.'
185 return 0
185 return 0
186 else
186 else
187 return 1
187 return 1
188 endif
188 endif
189 endfunction
189 endfunction
190 "}}}
190 "}}}
191
191
192 function! <SID>ExtractDiffsNative(...) "{{{
192 function! <SID>ExtractDiffsNative(...) "{{{
193 " Sets g:patches = {'reason':'', 'patch':[
193 " Sets g:patches = {'reason':'', 'patch':[
194 " {
194 " {
195 " 'filename': filepath
195 " 'filename': filepath
196 " 'type' : '+' | '-' | '!'
196 " 'type' : '+' | '-' | '!'
197 " 'content' : patch text for this file
197 " 'content' : patch text for this file
198 " },
198 " },
199 " ...
199 " ...
200 " ]}
200 " ]}
201 let g:patches = {'reason' : '', 'patch' : []}
201 let g:patches = {'reason' : '', 'patch' : []}
202 " TODO : User pointers into lines list rather then use collect
202 " TODO : User pointers into lines list rather then use collect
203 if a:0 == 0
203 if a:0 == 0
204 let g:patches['reason'] = "ExtractDiffsNative expects at least a patchfile argument"
204 let g:patches['reason'] = "ExtractDiffsNative expects at least a patchfile argument"
205 return
205 return
206 endif
206 endif
207 let patchfile = expand(a:1, ':p')
207 let patchfile = expand(a:1, ':p')
208 if a:0 > 1
208 if a:0 > 1
209 let patch = a:2
209 let patch = a:2
210 endif
210 endif
211 if ! filereadable(patchfile)
211 if ! filereadable(patchfile)
212 let g:patches['reason'] = "File " . patchfile . " is not readable"
212 let g:patches['reason'] = "File " . patchfile . " is not readable"
213 return
213 return
214 endif
214 endif
215 unlet! filterdiffcmd
215 unlet! filterdiffcmd
216 let filterdiffcmd = '' . g:patchreview_filterdiff . ' --list -s ' . patchfile
216 let filterdiffcmd = '' . g:patchreview_filterdiff . ' --list -s ' . patchfile
217 let fileslist = split(system(filterdiffcmd), '[\r\n]')
217 let fileslist = split(system(filterdiffcmd), '[\r\n]')
218 for filewithchangetype in fileslist
218 for filewithchangetype in fileslist
219 if filewithchangetype !~ '^[!+-] '
219 if filewithchangetype !~ '^[!+-] '
220 Pecho '*** Skipping review generation due to unknown change for [' . filewithchangetype . ']'
220 Pecho '*** Skipping review generation due to unknown change for [' . filewithchangetype . ']'
221 continue
221 continue
222 endif
222 endif
223
223
224 unlet! this_patch
224 unlet! this_patch
225 let this_patch = {}
225 let this_patch = {}
226
226
227 unlet! relpath
227 unlet! relpath
228 let relpath = substitute(filewithchangetype, '^. ', '', '')
228 let relpath = substitute(filewithchangetype, '^. ', '', '')
229
229
230 let this_patch['filename'] = relpath
230 let this_patch['filename'] = relpath
231
231
232 if filewithchangetype =~ '^! '
232 if filewithchangetype =~ '^! '
233 let this_patch['type'] = '!'
233 let this_patch['type'] = '!'
234 elseif filewithchangetype =~ '^+ '
234 elseif filewithchangetype =~ '^+ '
235 let this_patch['type'] = '+'
235 let this_patch['type'] = '+'
236 elseif filewithchangetype =~ '^- '
236 elseif filewithchangetype =~ '^- '
237 let this_patch['type'] = '-'
237 let this_patch['type'] = '-'
238 endif
238 endif
239
239
240 unlet! filterdiffcmd
240 unlet! filterdiffcmd
241 let filterdiffcmd = '' . g:patchreview_filterdiff . ' -i ' . relpath . ' ' . patchfile
241 let filterdiffcmd = '' . g:patchreview_filterdiff . ' -i ' . relpath . ' ' . patchfile
242 let this_patch['content'] = split(system(filterdiffcmd), '[\n\r]')
242 let this_patch['content'] = split(system(filterdiffcmd), '[\n\r]')
243 let g:patches['patch'] += [this_patch]
243 let g:patches['patch'] += [this_patch]
244 Debug "Patch collected for " . relpath
244 Debug "Patch collected for " . relpath
245 endfor
245 endfor
246 endfunction
246 endfunction
247 "}}}
247 "}}}
248
248
249 function! <SID>ExtractDiffsPureVim(...) "{{{
249 function! <SID>ExtractDiffsPureVim(...) "{{{
250 " Sets g:patches = {'reason':'', 'patch':[
250 " Sets g:patches = {'reason':'', 'patch':[
251 " {
251 " {
252 " 'filename': filepath
252 " 'filename': filepath
253 " 'type' : '+' | '-' | '!'
253 " 'type' : '+' | '-' | '!'
254 " 'content' : patch text for this file
254 " 'content' : patch text for this file
255 " },
255 " },
256 " ...
256 " ...
257 " ]}
257 " ]}
258 let g:patches = {'reason' : '', 'patch' : []}
258 let g:patches = {'reason' : '', 'patch' : []}
259 " TODO : User pointers into lines list rather then use collect
259 " TODO : User pointers into lines list rather then use collect
260 if a:0 == 0
260 if a:0 == 0
261 let g:patches['reason'] = "ExtractDiffsPureVim expects at least a patchfile argument"
261 let g:patches['reason'] = "ExtractDiffsPureVim expects at least a patchfile argument"
262 return
262 return
263 endif
263 endif
264 let patchfile = expand(a:1, ':p')
264 let patchfile = expand(a:1, ':p')
265 if a:0 > 1
265 if a:0 > 1
266 let patch = a:2
266 let patch = a:2
267 endif
267 endif
268 if ! filereadable(patchfile)
268 if ! filereadable(patchfile)
269 let g:patches['reason'] = "File " . patchfile . " is not readable"
269 let g:patches['reason'] = "File " . patchfile . " is not readable"
270 return
270 return
271 endif
271 endif
272 call s:PR_wipeMsgBuf()
272 call s:PR_wipeMsgBuf()
273 let collect = []
273 let collect = []
274 let linum = 0
274 let linum = 0
275 let lines = readfile(patchfile)
275 let lines = readfile(patchfile)
276 let linescount = len(lines)
276 let linescount = len(lines)
277 State 'START'
277 State 'START'
278 while linum < linescount
278 while linum < linescount
279 let line = lines[linum]
279 let line = lines[linum]
280 let linum += 1
280 let linum += 1
281 if State() == 'START'
281 if State() == 'START'
282 let mat = matchlist(line, '^--- \([^\t]\+\).*$')
282 let mat = matchlist(line, '^--- \([^\t]\+\).*$')
283 if ! empty(mat) && mat[1] != ''
283 if ! empty(mat) && mat[1] != ''
284 State 'MAYBE_UNIFIED_DIFF'
284 State 'MAYBE_UNIFIED_DIFF'
285 let p_first_file = mat[1]
285 let p_first_file = mat[1]
286 let collect = [line]
286 let collect = [line]
287 Debug line . State()
287 Debug line . State()
288 continue
288 continue
289 endif
289 endif
290 let mat = matchlist(line, '^\*\*\* \([^\t]\+\).*$')
290 let mat = matchlist(line, '^\*\*\* \([^\t]\+\).*$')
291 if ! empty(mat) && mat[1] != ''
291 if ! empty(mat) && mat[1] != ''
292 State 'MAYBE_CONTEXT_DIFF'
292 State 'MAYBE_CONTEXT_DIFF'
293 let p_first_file = mat[1]
293 let p_first_file = mat[1]
294 let collect = [line]
294 let collect = [line]
295 Debug line . State()
295 Debug line . State()
296 continue
296 continue
297 endif
297 endif
298 continue
298 continue
299 elseif State() == 'MAYBE_CONTEXT_DIFF'
299 elseif State() == 'MAYBE_CONTEXT_DIFF'
300 let mat = matchlist(line, '^--- \([^\t]\+\).*$')
300 let mat = matchlist(line, '^--- \([^\t]\+\).*$')
301 if empty(mat) || mat[1] == ''
301 if empty(mat) || mat[1] == ''
302 State 'START'
302 State 'START'
303 let linum -= 1
303 let linum -= 1
304 continue
304 continue
305 Debug 'Back to square one ' . line()
305 Debug 'Back to square one ' . line()
306 endif
306 endif
307 let p_second_file = mat[1]
307 let p_second_file = mat[1]
308 if p_first_file == '/dev/null'
308 if p_first_file == '/dev/null'
309 if p_second_file == '/dev/null'
309 if p_second_file == '/dev/null'
310 let g:patches['reason'] = "Malformed diff found at line " . linum
310 let g:patches['reason'] = "Malformed diff found at line " . linum
311 return
311 return
312 endif
312 endif
313 let p_type = '+'
313 let p_type = '+'
314 let filepath = p_second_file
314 let filepath = p_second_file
315 else
315 else
316 if p_second_file == '/dev/null'
316 if p_second_file == '/dev/null'
317 let p_type = '-'
317 let p_type = '-'
318 let filepath = p_first_file
318 let filepath = p_first_file
319 else
319 else
320 let p_type = '!'
320 let p_type = '!'
321 let filepath = p_first_file
321 let filepath = p_first_file
322 endif
322 endif
323 endif
323 endif
324 State 'EXPECT_15_STARS'
324 State 'EXPECT_15_STARS'
325 let collect += [line]
325 let collect += [line]
326 Debug line . State()
326 Debug line . State()
327 elseif State() == 'EXPECT_15_STARS'
327 elseif State() == 'EXPECT_15_STARS'
328 if line !~ '^*\{15}$'
328 if line !~ '^*\{15}$'
329 State 'START'
329 State 'START'
330 let linum -= 1
330 let linum -= 1
331 Debug line . State()
331 Debug line . State()
332 continue
332 continue
333 endif
333 endif
334 State 'EXPECT_CONTEXT_CHUNK_HEADER_1'
334 State 'EXPECT_CONTEXT_CHUNK_HEADER_1'
335 let collect += [line]
335 let collect += [line]
336 Debug line . State()
336 Debug line . State()
337 elseif State() == 'EXPECT_CONTEXT_CHUNK_HEADER_1'
337 elseif State() == 'EXPECT_CONTEXT_CHUNK_HEADER_1'
338 let mat = matchlist(line, '^\*\*\* \(\d\+,\)\?\(\d\+\) \*\*\*\*$')
338 let mat = matchlist(line, '^\*\*\* \(\d\+,\)\?\(\d\+\) \*\*\*\*$')
339 if empty(mat) || mat[1] == ''
339 if empty(mat) || mat[1] == ''
340 State 'START'
340 State 'START'
341 let linum -= 1
341 let linum -= 1
342 Debug line . State()
342 Debug line . State()
343 continue
343 continue
344 endif
344 endif
345 let collect += [line]
345 let collect += [line]
346 State 'SKIP_CONTEXT_STUFF_1'
346 State 'SKIP_CONTEXT_STUFF_1'
347 Debug line . State()
347 Debug line . State()
348 continue
348 continue
349 elseif State() == 'SKIP_CONTEXT_STUFF_1'
349 elseif State() == 'SKIP_CONTEXT_STUFF_1'
350 if line !~ '^[ !+].*$'
350 if line !~ '^[ !+].*$'
351 let mat = matchlist(line, '^--- \(\d\+\),\(\d\+\) ----$')
351 let mat = matchlist(line, '^--- \(\d\+\),\(\d\+\) ----$')
352 if ! empty(mat) && mat[1] != '' && mat[2] != ''
352 if ! empty(mat) && mat[1] != '' && mat[2] != ''
353 let goal_count = mat[2] - mat[1] + 1
353 let goal_count = mat[2] - mat[1] + 1
354 let c_count = 0
354 let c_count = 0
355 State 'READ_CONTEXT_CHUNK'
355 State 'READ_CONTEXT_CHUNK'
356 let collect += [line]
356 let collect += [line]
357 Debug line . State() . " Goal count set to " . goal_count
357 Debug line . State() . " Goal count set to " . goal_count
358 continue
358 continue
359 endif
359 endif
360 State 'START'
360 State 'START'
361 let linum -= 1
361 let linum -= 1
362 Debug line . State()
362 Debug line . State()
363 continue
363 continue
364 endif
364 endif
365 let collect += [line]
365 let collect += [line]
366 continue
366 continue
367 elseif State() == 'READ_CONTEXT_CHUNK'
367 elseif State() == 'READ_CONTEXT_CHUNK'
368 let c_count += 1
368 let c_count += 1
369 if c_count == goal_count
369 if c_count == goal_count
370 let collect += [line]
370 let collect += [line]
371 State 'BACKSLASH_OR_CRANGE_EOF'
371 State 'BACKSLASH_OR_CRANGE_EOF'
372 continue
372 continue
373 else " goal not met yet
373 else " goal not met yet
374 let mat = matchlist(line, '^\([\\!+ ]\).*$')
374 let mat = matchlist(line, '^\([\\!+ ]\).*$')
375 if empty(mat) || mat[1] == ''
375 if empty(mat) || mat[1] == ''
376 let linum -= 1
376 let linum -= 1
377 State 'START'
377 State 'START'
378 Debug line . State()
378 Debug line . State()
379 continue
379 continue
380 endif
380 endif
381 let collect += [line]
381 let collect += [line]
382 continue
382 continue
383 endif
383 endif
384 elseif State() == 'BACKSLASH_OR_CRANGE_EOF'
384 elseif State() == 'BACKSLASH_OR_CRANGE_EOF'
385 if line =~ '^\\ No newline.*$' " XXX: Can we go to another chunk from here??
385 if line =~ '^\\ No newline.*$' " XXX: Can we go to another chunk from here??
386 let collect += [line]
386 let collect += [line]
387 let this_patch = {}
387 let this_patch = {}
388 let this_patch['filename'] = filepath
388 let this_patch['filename'] = filepath
389 let this_patch['type'] = p_type
389 let this_patch['type'] = p_type
390 let this_patch['content'] = collect
390 let this_patch['content'] = collect
391 let g:patches['patch'] += [this_patch]
391 let g:patches['patch'] += [this_patch]
392 Debug "Patch collected for " . filepath
392 Debug "Patch collected for " . filepath
393 State 'START'
393 State 'START'
394 continue
394 continue
395 endif
395 endif
396 if line =~ '^\*\{15}$'
396 if line =~ '^\*\{15}$'
397 let collect += [line]
397 let collect += [line]
398 State 'EXPECT_CONTEXT_CHUNK_HEADER_1'
398 State 'EXPECT_CONTEXT_CHUNK_HEADER_1'
399 Debug line . State()
399 Debug line . State()
400 continue
400 continue
401 endif
401 endif
402 let this_patch = {}
402 let this_patch = {}
403 let this_patch['filename'] = filepath
403 let this_patch['filename'] = filepath
404 let this_patch['type'] = p_type
404 let this_patch['type'] = p_type
405 let this_patch['content'] = collect
405 let this_patch['content'] = collect
406 let g:patches['patch'] += [this_patch]
406 let g:patches['patch'] += [this_patch]
407 let linum -= 1
407 let linum -= 1
408 State 'START'
408 State 'START'
409 Debug "Patch collected for " . filepath
409 Debug "Patch collected for " . filepath
410 Debug line . State()
410 Debug line . State()
411 continue
411 continue
412 elseif State() == 'MAYBE_UNIFIED_DIFF'
412 elseif State() == 'MAYBE_UNIFIED_DIFF'
413 let mat = matchlist(line, '^+++ \([^\t]\+\).*$')
413 let mat = matchlist(line, '^+++ \([^\t]\+\).*$')
414 if empty(mat) || mat[1] == ''
414 if empty(mat) || mat[1] == ''
415 State 'START'
415 State 'START'
416 let linum -= 1
416 let linum -= 1
417 Debug line . State()
417 Debug line . State()
418 continue
418 continue
419 endif
419 endif
420 let p_second_file = mat[1]
420 let p_second_file = mat[1]
421 if p_first_file == '/dev/null'
421 if p_first_file == '/dev/null'
422 if p_second_file == '/dev/null'
422 if p_second_file == '/dev/null'
423 let g:patches['reason'] = "Malformed diff found at line " . linum
423 let g:patches['reason'] = "Malformed diff found at line " . linum
424 return
424 return
425 endif
425 endif
426 let p_type = '+'
426 let p_type = '+'
427 let filepath = p_second_file
427 let filepath = p_second_file
428 else
428 else
429 if p_second_file == '/dev/null'
429 if p_second_file == '/dev/null'
430 let p_type = '-'
430 let p_type = '-'
431 let filepath = p_first_file
431 let filepath = p_first_file
432 else
432 else
433 let p_type = '!'
433 let p_type = '!'
434 let filepath = p_first_file
434 let filepath = p_first_file
435 endif
435 endif
436 endif
436 endif
437 State 'EXPECT_UNIFIED_RANGE_CHUNK'
437 State 'EXPECT_UNIFIED_RANGE_CHUNK'
438 let collect += [line]
438 let collect += [line]
439 Debug line . State()
439 Debug line . State()
440 continue
440 continue
441 elseif State() == 'EXPECT_UNIFIED_RANGE_CHUNK'
441 elseif State() == 'EXPECT_UNIFIED_RANGE_CHUNK'
442 let mat = matchlist(line, '^@@ -\(\d\+,\)\?\(\d\+\) +\(\d\+,\)\?\(\d\+\) @@$')
442 let mat = matchlist(line, '^@@ -\(\d\+,\)\?\(\d\+\) +\(\d\+,\)\?\(\d\+\) @@$')
443 if ! empty(mat)
443 if ! empty(mat)
444 let old_goal_count = mat[2]
444 let old_goal_count = mat[2]
445 let new_goal_count = mat[4]
445 let new_goal_count = mat[4]
446 let o_count = 0
446 let o_count = 0
447 let n_count = 0
447 let n_count = 0
448 Debug "Goal count set to " . old_goal_count . ', ' . new_goal_count
448 Debug "Goal count set to " . old_goal_count . ', ' . new_goal_count
449 State 'READ_UNIFIED_CHUNK'
449 State 'READ_UNIFIED_CHUNK'
450 let collect += [line]
450 let collect += [line]
451 Debug line . State()
451 Debug line . State()
452 continue
452 continue
453 endif
453 endif
454 State 'START'
454 State 'START'
455 Debug line . State()
455 Debug line . State()
456 continue
456 continue
457 elseif State() == 'READ_UNIFIED_CHUNK'
457 elseif State() == 'READ_UNIFIED_CHUNK'
458 if o_count == old_goal_count && n_count == new_goal_count
458 if o_count == old_goal_count && n_count == new_goal_count
459 if line =~ '^\\.*$' " XXX: Can we go to another chunk from here??
459 if line =~ '^\\.*$' " XXX: Can we go to another chunk from here??
460 let collect += [line]
460 let collect += [line]
461 let this_patch = {}
461 let this_patch = {}
462 let this_patch['filename'] = filepath
462 let this_patch['filename'] = filepath
463 let this_patch['type'] = p_type
463 let this_patch['type'] = p_type
464 let this_patch['content'] = collect
464 let this_patch['content'] = collect
465 let g:patches['patch'] += [this_patch]
465 let g:patches['patch'] += [this_patch]
466 Debug "Patch collected for " . filepath
466 Debug "Patch collected for " . filepath
467 State 'START'
467 State 'START'
468 continue
468 continue
469 endif
469 endif
470 let mat = matchlist(line, '^@@ -\(\d\+,\)\?\(\d\+\) +\(\d\+,\)\?\(\d\+\) @@$')
470 let mat = matchlist(line, '^@@ -\(\d\+,\)\?\(\d\+\) +\(\d\+,\)\?\(\d\+\) @@$')
471 if ! empty(mat)
471 if ! empty(mat)
472 let old_goal_count = mat[2]
472 let old_goal_count = mat[2]
473 let new_goal_count = mat[4]
473 let new_goal_count = mat[4]
474 let o_count = 0
474 let o_count = 0
475 let n_count = 0
475 let n_count = 0
476 Debug "Goal count set to " . old_goal_count . ', ' . new_goal_count
476 Debug "Goal count set to " . old_goal_count . ', ' . new_goal_count
477 let collect += [line]
477 let collect += [line]
478 Debug line . State()
478 Debug line . State()
479 continue
479 continue
480 endif
480 endif
481 let this_patch = {}
481 let this_patch = {}
482 let this_patch['filename'] = filepath
482 let this_patch['filename'] = filepath
483 let this_patch['type'] = p_type
483 let this_patch['type'] = p_type
484 let this_patch['content'] = collect
484 let this_patch['content'] = collect
485 let g:patches['patch'] += [this_patch]
485 let g:patches['patch'] += [this_patch]
486 Debug "Patch collected for " . filepath
486 Debug "Patch collected for " . filepath
487 let linum -= 1
487 let linum -= 1
488 State 'START'
488 State 'START'
489 Debug line . State()
489 Debug line . State()
490 continue
490 continue
491 else " goal not met yet
491 else " goal not met yet
492 let mat = matchlist(line, '^\([\\+ -]\).*$')
492 let mat = matchlist(line, '^\([\\+ -]\).*$')
493 if empty(mat) || mat[1] == ''
493 if empty(mat) || mat[1] == ''
494 let linum -= 1
494 let linum -= 1
495 State 'START'
495 State 'START'
496 continue
496 continue
497 endif
497 endif
498 let chr = mat[1]
498 let chr = mat[1]
499 if chr == '+'
499 if chr == '+'
500 let n_count += 1
500 let n_count += 1
501 endif
501 endif
502 if chr == ' '
502 if chr == ' '
503 let o_count += 1
503 let o_count += 1
504 let n_count += 1
504 let n_count += 1
505 endif
505 endif
506 if chr == '-'
506 if chr == '-'
507 let o_count += 1
507 let o_count += 1
508 endif
508 endif
509 let collect += [line]
509 let collect += [line]
510 Debug line . State()
510 Debug line . State()
511 continue
511 continue
512 endif
512 endif
513 else
513 else
514 let g:patches['reason'] = "Internal error: Do not use the plugin anymore and if possible please send the diff or patch file you tried it with to Manpreet Singh <junkblocker@yahoo.com>"
514 let g:patches['reason'] = "Internal error: Do not use the plugin anymore and if possible please send the diff or patch file you tried it with to Manpreet Singh <junkblocker@yahoo.com>"
515 return
515 return
516 endif
516 endif
517 endwhile
517 endwhile
518 "Pecho State()
518 "Pecho State()
519 if (State() == 'READ_CONTEXT_CHUNK' && c_count == goal_count) || (State() == 'READ_UNIFIED_CHUNK' && n_count == new_goal_count && o_count == old_goal_count)
519 if (State() == 'READ_CONTEXT_CHUNK' && c_count == goal_count) || (State() == 'READ_UNIFIED_CHUNK' && n_count == new_goal_count && o_count == old_goal_count)
520 let this_patch = {}
520 let this_patch = {}
521 let this_patch['filename'] = filepath
521 let this_patch['filename'] = filepath
522 let this_patch['type'] = p_type
522 let this_patch['type'] = p_type
523 let this_patch['content'] = collect
523 let this_patch['content'] = collect
524 let g:patches['patch'] += [this_patch]
524 let g:patches['patch'] += [this_patch]
525 Debug "Patch collected for " . filepath
525 Debug "Patch collected for " . filepath
526 endif
526 endif
527 return
527 return
528 endfunction
528 endfunction
529 "}}}
529 "}}}
530
530
531 function! State(...) " For easy manipulation of diff extraction state "{{{
531 function! State(...) " For easy manipulation of diff extraction state "{{{
532 if a:0 != 0
532 if a:0 != 0
533 let s:STATE = a:1
533 let s:STATE = a:1
534 else
534 else
535 if ! exists('s:STATE')
535 if ! exists('s:STATE')
536 let s:STATE = 'START'
536 let s:STATE = 'START'
537 endif
537 endif
538 return s:STATE
538 return s:STATE
539 endif
539 endif
540 endfunction
540 endfunction
541 com! -nargs=+ -complete=expression State call State(<args>)
541 com! -nargs=+ -complete=expression State call State(<args>)
542 "}}}
542 "}}}
543
543
544 function! <SID>PatchReview(...) "{{{
544 function! <SID>PatchReview(...) "{{{
545 let s:save_shortmess = &shortmess
545 let s:save_shortmess = &shortmess
546 let s:save_aw = &autowrite
546 let s:save_aw = &autowrite
547 let s:save_awa = &autowriteall
547 let s:save_awa = &autowriteall
548 set shortmess=aW
548 set shortmess=aW
549 call s:PR_wipeMsgBuf()
549 call s:PR_wipeMsgBuf()
550 let s:reviewmode = 'patch'
550 let s:reviewmode = 'patch'
551 call s:_GenericReview(a:000)
551 call s:_GenericReview(a:000)
552 let &autowriteall = s:save_awa
552 let &autowriteall = s:save_awa
553 let &autowrite = s:save_aw
553 let &autowrite = s:save_aw
554 let &shortmess = s:save_shortmess
554 let &shortmess = s:save_shortmess
555 endfunction
555 endfunction
556 "}}}
556 "}}}
557
557
558 function! <SID>_GenericReview(argslist) "{{{
558 function! <SID>_GenericReview(argslist) "{{{
559 " diff mode:
559 " diff mode:
560 " arg1 = patchfile
560 " arg1 = patchfile
561 " arg2 = strip count
561 " arg2 = strip count
562 " patch mode:
562 " patch mode:
563 " arg1 = patchfile
563 " arg1 = patchfile
564 " arg2 = strip count
564 " arg2 = strip count
565 " arg3 = directory
565 " arg3 = directory
566
566
567 " VIM 7+ required
567 " VIM 7+ required
568 if version < 700
568 if version < 700
569 Pecho 'This plugin needs VIM 7 or higher'
569 Pecho 'This plugin needs VIM 7 or higher'
570 return
570 return
571 endif
571 endif
572
572
573 " +diff required
573 " +diff required
574 if ! has('diff')
574 if ! has('diff')
575 Pecho 'This plugin needs VIM built with +diff feature.'
575 Pecho 'This plugin needs VIM built with +diff feature.'
576 return
576 return
577 endif
577 endif
578
578
579
579
580 if s:reviewmode == 'diff'
580 if s:reviewmode == 'diff'
581 let patch_R_option = ' -t -R '
581 let patch_R_option = ' -t -R '
582 elseif s:reviewmode == 'patch'
582 elseif s:reviewmode == 'patch'
583 let patch_R_option = ''
583 let patch_R_option = ''
584 else
584 else
585 Pecho 'Fatal internal error in patchreview.vim plugin'
585 Pecho 'Fatal internal error in patchreview.vim plugin'
586 return
586 return
587 endif
587 endif
588
588
589 " Check passed arguments
589 " Check passed arguments
590 if len(a:argslist) == 0
590 if len(a:argslist) == 0
591 Pecho 'PatchReview command needs at least one argument specifying a patchfile path.'
591 Pecho 'PatchReview command needs at least one argument specifying a patchfile path.'
592 return
592 return
593 endif
593 endif
594 let StripCount = 0
594 let StripCount = 0
595 if len(a:argslist) >= 1 && ((s:reviewmode == 'patch' && len(a:argslist) <= 3) || (s:reviewmode == 'diff' && len(a:argslist) == 2))
595 if len(a:argslist) >= 1 && ((s:reviewmode == 'patch' && len(a:argslist) <= 3) || (s:reviewmode == 'diff' && len(a:argslist) == 2))
596 let PatchFilePath = expand(a:argslist[0], ':p')
596 let PatchFilePath = expand(a:argslist[0], ':p')
597 if ! filereadable(PatchFilePath)
597 if ! filereadable(PatchFilePath)
598 Pecho 'File [' . PatchFilePath . '] is not accessible.'
598 Pecho 'File [' . PatchFilePath . '] is not accessible.'
599 return
599 return
600 endif
600 endif
601 if len(a:argslist) >= 2 && s:reviewmode == 'patch'
601 if len(a:argslist) >= 2 && s:reviewmode == 'patch'
602 let s:SrcDirectory = expand(a:argslist[1], ':p')
602 let s:SrcDirectory = expand(a:argslist[1], ':p')
603 if ! isdirectory(s:SrcDirectory)
603 if ! isdirectory(s:SrcDirectory)
604 Pecho '[' . s:SrcDirectory . '] is not a directory'
604 Pecho '[' . s:SrcDirectory . '] is not a directory'
605 return
605 return
606 endif
606 endif
607 try
607 try
608 " Command line has already escaped the path
608 " Command line has already escaped the path
609 exe 'cd ' . s:SrcDirectory
609 exe 'cd ' . s:SrcDirectory
610 catch /^.*E344.*/
610 catch /^.*E344.*/
611 Pecho 'Could not change to directory [' . s:SrcDirectory . ']'
611 Pecho 'Could not change to directory [' . s:SrcDirectory . ']'
612 return
612 return
613 endtry
613 endtry
614 endif
614 endif
615 if s:reviewmode == 'diff'
615 if s:reviewmode == 'diff'
616 " passed in by default
616 " passed in by default
617 let StripCount = eval(a:argslist[1])
617 let StripCount = eval(a:argslist[1])
618 elseif s:reviewmode == 'patch'
618 elseif s:reviewmode == 'patch'
619 let StripCount = 1
619 let StripCount = 1
620 " optional strip count
620 " optional strip count
621 if len(a:argslist) == 3
621 if len(a:argslist) == 3
622 let StripCount = eval(a:argslist[2])
622 let StripCount = eval(a:argslist[2])
623 endif
623 endif
624 endif
624 endif
625 else
625 else
626 if s:reviewmode == 'patch'
626 if s:reviewmode == 'patch'
627 Pecho 'PatchReview command needs at most three arguments: patchfile path, optional source directory path and optional strip count.'
627 Pecho 'PatchReview command needs at most three arguments: patchfile path, optional source directory path and optional strip count.'
628 elseif s:reviewmode == 'diff'
628 elseif s:reviewmode == 'diff'
629 Pecho 'DiffReview command accepts no arguments.'
629 Pecho 'DiffReview command accepts no arguments.'
630 endif
630 endif
631 return
631 return
632 endif
632 endif
633
633
634 " Verify that patch command and temporary directory are available or specified
634 " Verify that patch command and temporary directory are available or specified
635 if ! s:PR_checkBinary('patch')
635 if ! s:PR_checkBinary('patch')
636 return
636 return
637 endif
637 endif
638
638
639 " Requirements met, now execute
639 " Requirements met, now execute
640 let PatchFilePath = fnamemodify(PatchFilePath, ':p')
640 let PatchFilePath = fnamemodify(PatchFilePath, ':p')
641 if s:reviewmode == 'patch'
641 if s:reviewmode == 'patch'
642 Pecho 'Patch file : ' . PatchFilePath
642 Pecho 'Patch file : ' . PatchFilePath
643 endif
643 endif
644 Pecho 'Source directory: ' . getcwd()
644 Pecho 'Source directory: ' . getcwd()
645 Pecho '------------------'
645 Pecho '------------------'
646 if s:PR_checkBinary('filterdiff')
646 if s:PR_checkBinary('filterdiff')
647 Debug "Using filterdiff"
647 Debug "Using filterdiff"
648 call s:ExtractDiffsNative(PatchFilePath)
648 call s:ExtractDiffsNative(PatchFilePath)
649 else
649 else
650 Debug "Using own diff extraction (slower)"
650 Debug "Using own diff extraction (slower)"
651 call s:ExtractDiffsPureVim(PatchFilePath)
651 call s:ExtractDiffsPureVim(PatchFilePath)
652 endif
652 endif
653 for patch in g:patches['patch']
653 for patch in g:patches['patch']
654 if patch.type !~ '^[!+-]$'
654 if patch.type !~ '^[!+-]$'
655 Pecho '*** Skipping review generation due to unknown change [' . patch.type . ']', 1
655 Pecho '*** Skipping review generation due to unknown change [' . patch.type . ']', 1
656 continue
656 continue
657 endif
657 endif
658 unlet! relpath
658 unlet! relpath
659 let relpath = patch.filename
659 let relpath = patch.filename
660 " XXX: svn diff and hg diff produce different kind of outputs, one requires
660 " XXX: svn diff and hg diff produce different kind of outputs, one requires
661 " XXX: stripping but the other doesn't. We need to take care of that
661 " XXX: stripping but the other doesn't. We need to take care of that
662 let stripmore = StripCount
662 let stripmore = StripCount
663 let StrippedRelativeFilePath = relpath
663 let StrippedRelativeFilePath = relpath
664 while stripmore > 0
664 while stripmore > 0
665 " strip one
665 " strip one
666 let StrippedRelativeFilePath = substitute(StrippedRelativeFilePath, '^[^\\\/]\+[^\\\/]*[\\\/]' , '' , '')
666 let StrippedRelativeFilePath = substitute(StrippedRelativeFilePath, '^[^\\\/]\+[^\\\/]*[\\\/]' , '' , '')
667 let stripmore -= 1
667 let stripmore -= 1
668 endwhile
668 endwhile
669 if patch.type == '!'
669 if patch.type == '!'
670 if s:reviewmode == 'patch'
670 if s:reviewmode == 'patch'
671 let msgtype = 'Patch modifies file: '
671 let msgtype = 'Patch modifies file: '
672 elseif s:reviewmode == 'diff'
672 elseif s:reviewmode == 'diff'
673 let msgtype = 'File has changes: '
673 let msgtype = 'File has changes: '
674 endif
674 endif
675 elseif patch.type == '+'
675 elseif patch.type == '+'
676 if s:reviewmode == 'patch'
676 if s:reviewmode == 'patch'
677 let msgtype = 'Patch adds file : '
677 let msgtype = 'Patch adds file : '
678 elseif s:reviewmode == 'diff'
678 elseif s:reviewmode == 'diff'
679 let msgtype = 'New file : '
679 let msgtype = 'New file : '
680 endif
680 endif
681 elseif patch.type == '-'
681 elseif patch.type == '-'
682 if s:reviewmode == 'patch'
682 if s:reviewmode == 'patch'
683 let msgtype = 'Patch removes file : '
683 let msgtype = 'Patch removes file : '
684 elseif s:reviewmode == 'diff'
684 elseif s:reviewmode == 'diff'
685 let msgtype = 'Removed file : '
685 let msgtype = 'Removed file : '
686 endif
686 endif
687 endif
687 endif
688 let bufnum = bufnr(relpath)
688 let bufnum = bufnr(relpath)
689 if buflisted(bufnum) && getbufvar(bufnum, '&mod')
689 if buflisted(bufnum) && getbufvar(bufnum, '&mod')
690 Pecho 'Old buffer for file [' . relpath . '] exists in modified state. Skipping review.', 1
690 Pecho 'Old buffer for file [' . relpath . '] exists in modified state. Skipping review.', 1
691 continue
691 continue
692 endif
692 endif
693 let tmpname = tempname()
693 let tmpname = tempname()
694
694
695 " write patch for patch.filename into tmpname
695 " write patch for patch.filename into tmpname
696 call writefile(patch.content, tmpname)
696 call writefile(patch.content, tmpname)
697 if patch.type == '+' && s:reviewmode == 'patch'
697 if patch.type == '+' && s:reviewmode == 'patch'
698 let inputfile = ''
698 let inputfile = ''
699 let patchcmd = '!' . g:patchreview_patch . patch_R_option . ' -o "' . tmpname . '.file" "' . inputfile . '" < "' . tmpname . '"'
699 let patchcmd = '!' . g:patchreview_patch . patch_R_option . ' -o "' . tmpname . '.file" "' . inputfile . '" < "' . tmpname . '"'
700 elseif patch.type == '+' && s:reviewmode == 'diff'
700 elseif patch.type == '+' && s:reviewmode == 'diff'
701 let inputfile = ''
701 let inputfile = ''
702 unlet! patchcmd
702 unlet! patchcmd
703 else
703 else
704 let inputfile = expand(StrippedRelativeFilePath, ':p')
704 let inputfile = expand(StrippedRelativeFilePath, ':p')
705 let patchcmd = '!' . g:patchreview_patch . patch_R_option . ' -o "' . tmpname . '.file" "' . inputfile . '" < "' . tmpname . '"'
705 let patchcmd = '!' . g:patchreview_patch . patch_R_option . ' -o "' . tmpname . '.file" "' . inputfile . '" < "' . tmpname . '"'
706 endif
706 endif
707 if exists('patchcmd')
707 if exists('patchcmd')
708 let v:errmsg = ''
708 let v:errmsg = ''
709 Debug patchcmd
709 Debug patchcmd
710 silent exe patchcmd
710 silent exe patchcmd
711 if v:errmsg != '' || v:shell_error
711 if v:errmsg != '' || v:shell_error
712 Pecho 'ERROR: Could not execute patch command.'
712 Pecho 'ERROR: Could not execute patch command.'
713 Pecho 'ERROR: ' . patchcmd
713 Pecho 'ERROR: ' . patchcmd
714 Pecho 'ERROR: ' . v:errmsg
714 Pecho 'ERROR: ' . v:errmsg
715 Pecho 'ERROR: Diff skipped.'
715 Pecho 'ERROR: Diff skipped.'
716 continue
716 continue
717 endif
717 endif
718 endif
718 endif
719 call delete(tmpname)
719 call delete(tmpname)
720 let s:origtabpagenr = tabpagenr()
720 let s:origtabpagenr = tabpagenr()
721 silent! exe 'tabedit ' . StrippedRelativeFilePath
721 silent! exe 'tabedit ' . StrippedRelativeFilePath
722 if exists('patchcmd')
722 if exists('patchcmd')
723 " modelines in loaded files mess with diff comparision
723 " modelines in loaded files mess with diff comparision
724 let s:keep_modeline=&modeline
724 let s:keep_modeline=&modeline
725 let &modeline=0
725 let &modeline=0
726 silent! exe 'vert diffsplit ' . tmpname . '.file'
726 silent! exe 'vert diffsplit ' . tmpname . '.file'
727 setlocal buftype=nofile
727 setlocal buftype=nofile
728 setlocal noswapfile
728 setlocal noswapfile
729 setlocal syntax=none
729 setlocal syntax=none
730 setlocal bufhidden=delete
730 setlocal bufhidden=delete
731 setlocal nobuflisted
731 setlocal nobuflisted
732 setlocal modifiable
732 setlocal modifiable
733 setlocal nowrap
733 setlocal nowrap
734 " Remove buffer name
734 " Remove buffer name
735 silent! 0f
735 silent! 0f
736 " Switch to original to get a nice tab title
736 " Switch to original to get a nice tab title
737 silent! wincmd p
737 silent! wincmd p
738 let &modeline=s:keep_modeline
738 let &modeline=s:keep_modeline
739 else
739 else
740 silent! exe 'vnew'
740 silent! exe 'vnew'
741 endif
741 endif
742 if filereadable(tmpname . '.file.rej')
742 if filereadable(tmpname . '.file.rej')
743 silent! exe 'topleft 5split ' . tmpname . '.file.rej'
743 silent! exe 'topleft 5split ' . tmpname . '.file.rej'
744 Pecho msgtype . '*** REJECTED *** ' . relpath, 1
744 Pecho msgtype . '*** REJECTED *** ' . relpath, 1
745 else
745 else
746 Pecho msgtype . ' ' . relpath, 1
746 Pecho msgtype . ' ' . relpath, 1
747 endif
747 endif
748 silent! exe 'tabn ' . s:origtabpagenr
748 silent! exe 'tabn ' . s:origtabpagenr
749 endfor
749 endfor
750 Pecho '-----'
750 Pecho '-----'
751 Pecho 'Done.'
751 Pecho 'Done.'
752
752
753 endfunction
753 endfunction
754 "}}}
754 "}}}
755
755
756 function! <SID>DiffReview(...) "{{{
756 function! <SID>DiffReview(...) "{{{
757 let s:save_shortmess = &shortmess
757 let s:save_shortmess = &shortmess
758 set shortmess=aW
758 set shortmess=aW
759 call s:PR_wipeMsgBuf()
759 call s:PR_wipeMsgBuf()
760
760
761 let vcsdict = {
761 let vcsdict = {
762 \'Mercurial' : {'dir' : '.hg', 'binary' : 'hg', 'diffargs' : 'diff' , 'strip' : 1},
762 \'Mercurial' : {'dir' : '.hg', 'binary' : 'hg', 'diffargs' : 'diff' , 'strip' : 1},
763 \'Bazaar-NG' : {'dir' : '.bzr', 'binary' : 'bzr', 'diffargs' : 'diff' , 'strip' : 0},
763 \'Bazaar-NG' : {'dir' : '.bzr', 'binary' : 'bzr', 'diffargs' : 'diff' , 'strip' : 0},
764 \'monotone' : {'dir' : '_MTN', 'binary' : 'mtn', 'diffargs' : 'diff --unified', 'strip' : 0},
764 \'monotone' : {'dir' : '_MTN', 'binary' : 'mtn', 'diffargs' : 'diff --unified', 'strip' : 0},
765 \'Subversion' : {'dir' : '.svn', 'binary' : 'svn', 'diffargs' : 'diff' , 'strip' : 0},
765 \'Subversion' : {'dir' : '.svn', 'binary' : 'svn', 'diffargs' : 'diff' , 'strip' : 0},
766 \'cvs' : {'dir' : 'CVS', 'binary' : 'cvs', 'diffargs' : '-q diff -u' , 'strip' : 0},
766 \'cvs' : {'dir' : 'CVS', 'binary' : 'cvs', 'diffargs' : '-q diff -u' , 'strip' : 0},
767 \}
767 \}
768
768
769 unlet! s:theDiffCmd
769 unlet! s:theDiffCmd
770 unlet! l:vcs
770 unlet! l:vcs
771 if ! exists('g:patchreview_diffcmd')
771 if ! exists('g:patchreview_diffcmd')
772 for key in keys(vcsdict)
772 for key in keys(vcsdict)
773 if isdirectory(vcsdict[key]['dir'])
773 if isdirectory(vcsdict[key]['dir'])
774 if ! s:PR_checkBinary(vcsdict[key]['binary'])
774 if ! s:PR_checkBinary(vcsdict[key]['binary'])
775 Pecho 'Current directory looks like a ' . vcsdict[key] . ' repository but ' . vcsdist[key]['binary'] . ' command was not found on path.'
775 Pecho 'Current directory looks like a ' . vcsdict[key] . ' repository but ' . vcsdist[key]['binary'] . ' command was not found on path.'
776 let &shortmess = s:save_shortmess
776 let &shortmess = s:save_shortmess
777 return
777 return
778 else
778 else
779 let s:theDiffCmd = vcsdict[key]['binary'] . ' ' . vcsdict[key]['diffargs']
779 let s:theDiffCmd = vcsdict[key]['binary'] . ' ' . vcsdict[key]['diffargs']
780 let strip = vcsdict[key]['strip']
780 let strip = vcsdict[key]['strip']
781
781
782 Pecho 'Using [' . s:theDiffCmd . '] to generate diffs for this ' . key . ' review.'
782 Pecho 'Using [' . s:theDiffCmd . '] to generate diffs for this ' . key . ' review.'
783 let &shortmess = s:save_shortmess
783 let &shortmess = s:save_shortmess
784 let l:vcs = vcsdict[key]['binary']
784 let l:vcs = vcsdict[key]['binary']
785 break
785 break
786 endif
786 endif
787 else
787 else
788 continue
788 continue
789 endif
789 endif
790 endfor
790 endfor
791 else
791 else
792 let s:theDiffCmd = g:patchreview_diffcmd
792 let s:theDiffCmd = g:patchreview_diffcmd
793 let strip = 0
793 let strip = 0
794 endif
794 endif
795 if ! exists('s:theDiffCmd')
795 if ! exists('s:theDiffCmd')
796 Pecho 'Please define g:patchreview_diffcmd and make sure you are in a VCS controlled top directory.'
796 Pecho 'Please define g:patchreview_diffcmd and make sure you are in a VCS controlled top directory.'
797 let &shortmess = s:save_shortmess
797 let &shortmess = s:save_shortmess
798 return
798 return
799 endif
799 endif
800
800
801 let outfile = tempname()
801 let outfile = tempname()
802 let cmd = s:theDiffCmd . ' > "' . outfile . '"'
802 let cmd = s:theDiffCmd . ' > "' . outfile . '"'
803 let v:errmsg = ''
803 let v:errmsg = ''
804 let cout = system(cmd)
804 let cout = system(cmd)
805 if v:errmsg == '' && exists('l:vcs') && l:vcs == 'cvs' && v:shell_error == 1
805 if v:errmsg == '' && exists('l:vcs') && l:vcs == 'cvs' && v:shell_error == 1
806 " Ignoring CVS non-error
806 " Ignoring CVS non-error
807 elseif v:errmsg != '' || v:shell_error
807 elseif v:errmsg != '' || v:shell_error
808 Pecho v:errmsg
808 Pecho v:errmsg
809 Pecho 'Could not execute [' . s:theDiffCmd . ']'
809 Pecho 'Could not execute [' . s:theDiffCmd . ']'
810 Pecho 'Error code: ' . v:shell_error
810 Pecho 'Error code: ' . v:shell_error
811 Pecho cout
811 Pecho cout
812 Pecho 'Diff review aborted.'
812 Pecho 'Diff review aborted.'
813 let &shortmess = s:save_shortmess
813 let &shortmess = s:save_shortmess
814 return
814 return
815 endif
815 endif
816 let s:reviewmode = 'diff'
816 let s:reviewmode = 'diff'
817 call s:_GenericReview([outfile, strip])
817 call s:_GenericReview([outfile, strip])
818 let &shortmess = s:save_shortmess
818 let &shortmess = s:save_shortmess
819 endfunction
819 endfunction
820 "}}}
820 "}}}
821
821
822 " End user commands "{{{
822 " End user commands "{{{
823 "============================================================================
823 "============================================================================
824 " :PatchReview
824 " :PatchReview
825 command! -nargs=* -complete=file PatchReview call s:PatchReview (<f-args>)
825 command! -nargs=* -complete=file PatchReview call s:PatchReview (<f-args>)
826
826
827 " :DiffReview
827 " :DiffReview
828 command! -nargs=0 DiffReview call s:DiffReview()
828 command! -nargs=0 DiffReview call s:DiffReview()
829 "}}}
829 "}}}
830
830
831 " Development "{{{
831 " Development "{{{
832 if exists('g:patchreview_debug')
832 if exists('g:patchreview_debug')
833 " Tests
833 " Tests
834 function! <SID>PRExtractTestNative(...)
834 function! <SID>PRExtractTestNative(...)
835 "let patchfiles = glob(expand(a:1) . '/?*')
835 "let patchfiles = glob(expand(a:1) . '/?*')
836 "for fname in split(patchfiles)
836 "for fname in split(patchfiles)
837 call s:PR_wipeMsgBuf()
837 call s:PR_wipeMsgBuf()
838 let fname = a:1
838 let fname = a:1
839 call s:ExtractDiffsNative(fname)
839 call s:ExtractDiffsNative(fname)
840 for patch in g:patches['patch']
840 for patch in g:patches['patch']
841 for line in patch.content
841 for line in patch.content
842 Pecho line
842 Pecho line
843 endfor
843 endfor
844 endfor
844 endfor
845 "endfor
845 "endfor
846 endfunction
846 endfunction
847
847
848 function! <SID>PRExtractTestVim(...)
848 function! <SID>PRExtractTestVim(...)
849 "let patchfiles = glob(expand(a:1) . '/?*')
849 "let patchfiles = glob(expand(a:1) . '/?*')
850 "for fname in split(patchfiles)
850 "for fname in split(patchfiles)
851 call s:PR_wipeMsgBuf()
851 call s:PR_wipeMsgBuf()
852 let fname = a:1
852 let fname = a:1
853 call s:ExtractDiffsPureVim(fname)
853 call s:ExtractDiffsPureVim(fname)
854 for patch in g:patches['patch']
854 for patch in g:patches['patch']
855 for line in patch.content
855 for line in patch.content
856 Pecho line
856 Pecho line
857 endfor
857 endfor
858 endfor
858 endfor
859 "endfor
859 "endfor
860 endfunction
860 endfunction
861
861
862 command! -nargs=+ -complete=file PRTestVim call s:PRExtractTestVim(<f-args>)
862 command! -nargs=+ -complete=file PRTestVim call s:PRExtractTestVim(<f-args>)
863 command! -nargs=+ -complete=file PRTestNative call s:PRExtractTestNative(<f-args>)
863 command! -nargs=+ -complete=file PRTestNative call s:PRExtractTestNative(<f-args>)
864 endif
864 endif
865 "}}}
865 "}}}
866
866
867 " modeline
867 " modeline
868 " vim: set et fdl=0 fdm=marker fenc=latin ff=unix ft=vim sw=2 sts=0 ts=2 textwidth=78 nowrap :
868 " vim: set et fdl=0 fdm=marker fenc=latin ff=unix ft=vim sw=2 sts=0 ts=2 textwidth=78 nowrap :
General Comments 0
You need to be logged in to leave comments. Login now