Show More
@@ -0,0 +1,97 b'' | |||||
|
1 | *patchreview.txt* Vim global plugin for doing single or multipatch code reviews | |||
|
2 | ||||
|
3 | Author: Manpreet Singh (junkblocker-CAT-yahoo-DOG-com) | |||
|
4 | (Replace -CAT- and -DOG- with @ and . first) | |||
|
5 | Copyright (C) 2006 by Manpreet Singh | |||
|
6 | License : This file is placed in the public domain. | |||
|
7 | ||||
|
8 | ============================================================================= | |||
|
9 | ||||
|
10 | CONTENTS *patchreview* *patchreview-contents* | |||
|
11 | ||||
|
12 | 1. Contents.........................................: |patchreview-contents| | |||
|
13 | 2. Introduction.....................................: |patchreview-intro| | |||
|
14 | 3. PatchReview options..............................: |patchreview-options| | |||
|
15 | 4. PatchReview Usage................................: |patchreview-usage| | |||
|
16 | 4.1 PatchReview Usage............................: |:PatchReview| | |||
|
17 | 4.2 PatchReview Usage............................: |:PatchReviewCleanup| | |||
|
18 | ||||
|
19 | ============================================================================= | |||
|
20 | ||||
|
21 | PatchReview Introduction *patchreview-intro* | |||
|
22 | ||||
|
23 | The Patch Review plugin allows single or multipatch code review to be done in | |||
|
24 | VIM. VIM provides the |:diffpatch| command to do single file reviews but can | |||
|
25 | not handle patch files containing multiple patches as is common with software | |||
|
26 | development projects. This plugin provides that missing functionality. It also | |||
|
27 | tries to improve on |:diffpatch|'s behaviour of creating the patched files in | |||
|
28 | the same directory as original file which can lead to project workspace | |||
|
29 | pollution. | |||
|
30 | ||||
|
31 | ============================================================================= | |||
|
32 | ||||
|
33 | PatchReview Options *patchreview-options* | |||
|
34 | ||||
|
35 | g:patchreview_filterdiff : Optional path to filterdiff binary. PatchReview | |||
|
36 | tries to locate filterdiff on system path | |||
|
37 | automatically. If the binary is not on system | |||
|
38 | path, this option tell PatchReview the full path | |||
|
39 | to the binary. This option, if specified, | |||
|
40 | overrides the default filterdiff binary on the | |||
|
41 | path. | |||
|
42 | ||||
|
43 | examples: | |||
|
44 | (On Windows with Cygwin) | |||
|
45 | ||||
|
46 | let g:patchreview_filterdiff = 'c:\\cygwin\\bin\\filterdiff.exe' | |||
|
47 | ||||
|
48 | (On *nix systems) | |||
|
49 | ||||
|
50 | let g:patchreview_filterdiff = '/usr/bin/filterdiff' | |||
|
51 | ||||
|
52 | g:patchreview_patch : Optional path to patch binary. PatchReview tries | |||
|
53 | to locate patch on system path automatically. If | |||
|
54 | the binary is not on system path, this option | |||
|
55 | tell PatchReview the full path to the binary. | |||
|
56 | This option, if specified, overrides the default | |||
|
57 | patch binary on the path. | |||
|
58 | ||||
|
59 | examples: | |||
|
60 | (On Windows with Cygwin) | |||
|
61 | ||||
|
62 | let g:patchreview_patch = 'c:\\cygwin\\bin\\patch.exe' | |||
|
63 | ||||
|
64 | (On *nix systems) | |||
|
65 | ||||
|
66 | let g:patchreview_patch = '/usr/bin/gpatch' | |||
|
67 | ||||
|
68 | ||||
|
69 | g:patchreview_tmpdir : Optional path where the plugin can save temporary | |||
|
70 | files. If this is not specified, the plugin tries to | |||
|
71 | use TMP, TEMP and TMPDIR environment variables in | |||
|
72 | succession. | |||
|
73 | ||||
|
74 | examples: | |||
|
75 | (On Windows) let g:patchreview_tmpdir = 'c:\\tmp' | |||
|
76 | (On *nix systems) let g:patchreview_tmpdir = '~/tmp' | |||
|
77 | ||||
|
78 | ============================================================================= | |||
|
79 | ||||
|
80 | PatchReview Usage *patchreview-usage* | |||
|
81 | *:PatchReview* | |||
|
82 | ||||
|
83 | :PatchReview patchfile_path [optional_source_directory] | |||
|
84 | ||||
|
85 | Perform a patch review in the current directory based on the supplied | |||
|
86 | patchfile_path. If optional_source_directory is specified, patchreview is | |||
|
87 | done on that directory. Othewise, the current directory is assumed to be | |||
|
88 | the source directory. | |||
|
89 | *:PatchReviewCleanup* | |||
|
90 | ||||
|
91 | :PatchReviewCleanup | |||
|
92 | ||||
|
93 | After you are done using the :PatchReview command, you can cleanup the | |||
|
94 | temporary files in the temporary directory using this command. | |||
|
95 | ||||
|
96 | ============================================================================= | |||
|
97 | vim: ft=help:ts=2:sts=2:sw=2:tw=78:tw=78 |
@@ -0,0 +1,332 b'' | |||||
|
1 | " Vim global plugin for doing single or multipatch code reviews"{{{ | |||
|
2 | ||||
|
3 | " Version : 0.1 "{{{ | |||
|
4 | " Last Modified : Thu 25 May 2006 10:15:11 PM PDT | |||
|
5 | " Author : Manpreet Singh (junkblocker AT yahoo DOT com) | |||
|
6 | " Copyright : 2006 by Manpreet Singh | |||
|
7 | " License : This file is placed in the public domain. | |||
|
8 | " | |||
|
9 | " History : 0.1 - First released | |||
|
10 | "}}} | |||
|
11 | " Documentation: "{{{ | |||
|
12 | " =========================================================================== | |||
|
13 | " This plugin allows single or multipatch code reviews to be done in VIM. Vim | |||
|
14 | " has :diffpatch command to do single file reviews but can not handle patch | |||
|
15 | " files containing multiple patches. This plugin provides that missing | |||
|
16 | " functionality and doesn't require the original file to be open. | |||
|
17 | " | |||
|
18 | " Installing: "{{{ | |||
|
19 | " | |||
|
20 | " For a quick start... | |||
|
21 | " | |||
|
22 | " Requirements: "{{{ | |||
|
23 | " | |||
|
24 | " 1) (g)vim 7.0 or higher built with +diff option. | |||
|
25 | " 2) patch and patchutils ( http://cyberelk.net/tim/patchutils/ ) installed | |||
|
26 | " for your OS. For windows it is availble from Cygwin ( | |||
|
27 | " http://www.cygwin.com ) or GnuWin32 ( http://gnuwin32.sourceforge.net/ | |||
|
28 | " ). | |||
|
29 | ""}}} | |||
|
30 | " Install: "{{{ | |||
|
31 | " | |||
|
32 | " 1) Extract this in your $VIM/vimfiles or $HOME/.vim directory and restart | |||
|
33 | " vim. | |||
|
34 | " | |||
|
35 | " 2) Make sure that you have filterdiff from patchutils and patch commands | |||
|
36 | " installed. | |||
|
37 | " | |||
|
38 | " 3) Optinally, specify the locations to filterdiff and patch commands and | |||
|
39 | " location of a temporary directory to use in your .vimrc. | |||
|
40 | " | |||
|
41 | " let g:patchreview_filterdiff = '/path/to/filterdiff' | |||
|
42 | " let g:patchreview_patch = '/path/to/patch' | |||
|
43 | " let g:patchreview_tmpdir = '/tmp/or/something' | |||
|
44 | " | |||
|
45 | " 4) Optionally, generate help tags to use help | |||
|
46 | " | |||
|
47 | " :helptags ~/.vim/doc | |||
|
48 | " or | |||
|
49 | " :helptags c:\vim\vimfiles\doc | |||
|
50 | ""}}} | |||
|
51 | ""}}} | |||
|
52 | " Usage: "{{{ | |||
|
53 | " | |||
|
54 | " :PatchReview path_to_submitted_patchfile [optional_source_directory] | |||
|
55 | " | |||
|
56 | " after review is done | |||
|
57 | " | |||
|
58 | " :PatchReviewCleanup | |||
|
59 | " | |||
|
60 | " See :help patchreview for details after you've created help tags. | |||
|
61 | ""}}} | |||
|
62 | "}}} | |||
|
63 | " Code "{{{ | |||
|
64 | ||||
|
65 | " Enabled only during development "{{{ | |||
|
66 | " unlet! g:loaded_patchreview " DEBUG | |||
|
67 | " unlet! g:patchreview_tmpdir " DEBUG | |||
|
68 | " unlet! g:patchreview_filterdiff " DEBUG | |||
|
69 | " unlet! g:patchreview_patch " DEBUG | |||
|
70 | "}}} | |||
|
71 | ||||
|
72 | " load only once "{{{ | |||
|
73 | if exists('g:loaded_patchreview') | |||
|
74 | finish | |||
|
75 | endif | |||
|
76 | let g:loaded_patchreview=1 | |||
|
77 | let s:msgbufname = 'Patch Review Messages' | |||
|
78 | "}}} | |||
|
79 | ||||
|
80 | function! <SID>PR_wipeMsgBuf() "{{{ | |||
|
81 | let s:winnum = bufwinnr(s:msgbufname) | |||
|
82 | if s:winnum != -1 " If the window is already open, jump to it | |||
|
83 | let s:cur_winnr = winnr() | |||
|
84 | if winnr() != s:winnum | |||
|
85 | exe s:winnum . 'wincmd w' | |||
|
86 | exe 'bw' | |||
|
87 | exe s:cur_winnr . 'wincmd w' | |||
|
88 | endif | |||
|
89 | endif | |||
|
90 | endfunction | |||
|
91 | "}}} | |||
|
92 | ||||
|
93 | function! <SID>PR_echo(...) "{{{ | |||
|
94 | " Usage: PR_echo(msg, [return_to_original_window_flag]) | |||
|
95 | " default return_to_original_window_flag = 0 | |||
|
96 | " | |||
|
97 | let s:cur_winnr = winnr() | |||
|
98 | let s:winnum = bufwinnr(s:msgbufname) | |||
|
99 | if s:winnum != -1 " If the window is already open, jump to it | |||
|
100 | if winnr() != s:winnum | |||
|
101 | exe s:winnum . 'wincmd w' | |||
|
102 | endif | |||
|
103 | else | |||
|
104 | let s:bufnum = bufnr(s:msgbufname) | |||
|
105 | if s:bufnum == -1 | |||
|
106 | let s:wcmd = s:msgbufname | |||
|
107 | else | |||
|
108 | let s:wcmd = '+buffer' . s:bufnum | |||
|
109 | endif | |||
|
110 | exe 'silent! botright 5split ' . s:wcmd | |||
|
111 | endif | |||
|
112 | setlocal modifiable | |||
|
113 | setlocal buftype=nofile | |||
|
114 | setlocal bufhidden=delete | |||
|
115 | setlocal noswapfile | |||
|
116 | setlocal nowrap | |||
|
117 | setlocal nobuflisted | |||
|
118 | if a:0 != 0 | |||
|
119 | silent! $put =a:1 | |||
|
120 | endif | |||
|
121 | exe ':$' | |||
|
122 | setlocal nomodifiable | |||
|
123 | if a:0 > 1 && a:2 | |||
|
124 | exe s:cur_winnr . 'wincmd w' | |||
|
125 | endif | |||
|
126 | endfunction | |||
|
127 | "}}} | |||
|
128 | ||||
|
129 | function! <SID>PR_checkBinary(BinaryName) "{{{ | |||
|
130 | " Verify that BinaryName is specified or available | |||
|
131 | if ! exists('g:patchreview_' . a:BinaryName) | |||
|
132 | if executable(a:BinaryName) | |||
|
133 | let g:patchreview_{a:BinaryName} = a:BinaryName | |||
|
134 | return 1 | |||
|
135 | else | |||
|
136 | call s:PR_echo('g:patchreview_' . a:BinaryName . ' is not defined and could not be found on path. Please define it in your .vimrc.') | |||
|
137 | return 0 | |||
|
138 | endif | |||
|
139 | elseif ! executable(g:patchreview_{a:BinaryName}) | |||
|
140 | call s:PR_echo('Specified g:patchreview_' . a:BinaryName . ' [' . g:patchreview_{a.BinaryName} . '] is not executable.') | |||
|
141 | return 0 | |||
|
142 | else | |||
|
143 | return 1 | |||
|
144 | endif | |||
|
145 | endfunction | |||
|
146 | "}}} | |||
|
147 | ||||
|
148 | function! <SID>PR_GetTempDirLocation(Quiet) "{{{ | |||
|
149 | if exists('g:patchreview_tmpdir') | |||
|
150 | if ! isdirectory(g:patchreview_tmpdir) || ! filewritable(g:patchreview_tmpdir) | |||
|
151 | if ! a:Quiet | |||
|
152 | call s:PR_echo('Temporary directory specified by g:patchreview_tmpdir [' . g:patchreview_tmpdir . '] is not accessible.') | |||
|
153 | return 0 | |||
|
154 | endif | |||
|
155 | endif | |||
|
156 | elseif exists("$TMP") && isdirectory($TMP) && filewritable($TMP) | |||
|
157 | let g:patchreview_tmpdir = $TMP | |||
|
158 | elseif exists("$TEMP") && isdirectory($TEMP) && filewritable($TEMP) | |||
|
159 | let g:patchreview_tmpdir = $TEMP | |||
|
160 | elseif exists("$TMPDIR") && isdirectory($TMPDIR) && filewritable($TMPDIR) | |||
|
161 | let g:patchreview_tmpdir = $TMPDIR | |||
|
162 | else | |||
|
163 | if ! a:Quiet | |||
|
164 | call s:PR_echo('Could not figure out a temporary directory to use. Please specify g:patchreview_tmpdir in your .vimrc.') | |||
|
165 | return 0 | |||
|
166 | endif | |||
|
167 | endif | |||
|
168 | let g:patchreview_tmpdir = g:patchreview_tmpdir . '/' | |||
|
169 | let g:patchreview_tmpdir = substitute(g:patchreview_tmpdir, '\\', '/', 'g') | |||
|
170 | let g:patchreview_tmpdir = substitute(g:patchreview_tmpdir, '/+$', '/', '') | |||
|
171 | if has('win32') | |||
|
172 | let g:patchreview_tmpdir = substitute(g:patchreview_tmpdir, '/', '\\', 'g') | |||
|
173 | endif | |||
|
174 | return 1 | |||
|
175 | endfunction | |||
|
176 | "}}} | |||
|
177 | ||||
|
178 | function! <SID>PatchReview(...) "{{{ | |||
|
179 | " VIM 7+ required"{{{ | |||
|
180 | if version < 700 | |||
|
181 | call s:PR_echo('This plugin needs VIM 7 or higher') | |||
|
182 | return | |||
|
183 | endif | |||
|
184 | "}}} | |||
|
185 | ||||
|
186 | let s:save_shortmess = &shortmess | |||
|
187 | set shortmess+=aW | |||
|
188 | call s:PR_wipeMsgBuf() | |||
|
189 | ||||
|
190 | " Check passed arguments "{{{ | |||
|
191 | if a:0 == 0 | |||
|
192 | call s:PR_echo('PatchReview command needs at least one argument specifying a patchfile path.') | |||
|
193 | let &shortmess = s:save_shortmess | |||
|
194 | return | |||
|
195 | endif | |||
|
196 | if a:0 >= 1 && a:0 <= 2 | |||
|
197 | let s:PatchFilePath = expand(a:1, ':p') | |||
|
198 | if ! filereadable(s:PatchFilePath) | |||
|
199 | call s:PR_echo('File [' . s:PatchFilePath . '] is not accessible.') | |||
|
200 | let &shortmess = s:save_shortmess | |||
|
201 | return | |||
|
202 | endif | |||
|
203 | if a:0 == 2 | |||
|
204 | let s:SrcDirectory = expand(a:2, ':p') | |||
|
205 | if ! isdirectory(s:SrcDirectory) | |||
|
206 | call s:PR_echo('[' . s:SrcDirectory . '] is not a directory') | |||
|
207 | let &shortmess = s:save_shortmess | |||
|
208 | return | |||
|
209 | endif | |||
|
210 | try | |||
|
211 | exe 'cd ' . s:SrcDirectory | |||
|
212 | catch /^.*E344.*/ | |||
|
213 | call s:PR_echo('Could not change to directory [' . s:SrcDirectory . ']') | |||
|
214 | let &shortmess = s:save_shortmess | |||
|
215 | return | |||
|
216 | endtry | |||
|
217 | endif | |||
|
218 | else | |||
|
219 | call s:PR_echo('PatchReview command needs at most two arguments: patchfile path and optional source directory path.') | |||
|
220 | let &shortmess = s:save_shortmess | |||
|
221 | return | |||
|
222 | endif | |||
|
223 | "}}} | |||
|
224 | ||||
|
225 | " Verify that filterdiff and patch are specified or available "{{{ | |||
|
226 | if ! s:PR_checkBinary('filterdiff') || ! s:PR_checkBinary('patch') | |||
|
227 | let &shortmess = s:save_shortmess | |||
|
228 | return | |||
|
229 | endif | |||
|
230 | ||||
|
231 | let s:retval = s:PR_GetTempDirLocation(0) | |||
|
232 | if ! s:retval | |||
|
233 | let &shortmess = s:save_shortmess | |||
|
234 | return | |||
|
235 | endif | |||
|
236 | "}}} | |||
|
237 | ||||
|
238 | " Requirements met, now execute "{{{ | |||
|
239 | let s:PatchFilePath = fnamemodify(s:PatchFilePath, ':p') | |||
|
240 | call s:PR_echo('Patch file : ' . s:PatchFilePath) | |||
|
241 | call s:PR_echo('Source directory: ' . getcwd()) | |||
|
242 | call s:PR_echo('------------------') | |||
|
243 | let s:theFilterDiffCommand = '' . g:patchreview_filterdiff . ' --list -s ' . s:PatchFilePath | |||
|
244 | let s:theFilesString = system(s:theFilterDiffCommand) | |||
|
245 | let s:theFilesList = split(s:theFilesString, '[\r\n]') | |||
|
246 | for s:filewithchangetype in s:theFilesList | |||
|
247 | if s:filewithchangetype !~ '^[!+-] ' | |||
|
248 | call s:PR_echo('*** Skipping review generation due to understood change for [' . s:filewithchangetype . ']', 1) | |||
|
249 | continue | |||
|
250 | endif | |||
|
251 | unlet! s:RelativeFilePath | |||
|
252 | let s:RelativeFilePath = substitute(s:filewithchangetype, '^. ', '', '') | |||
|
253 | let s:RelativeFilePath = substitute(s:RelativeFilePath, '^[a-z][^\\\/]*[\\\/]' , '' , '') | |||
|
254 | if s:filewithchangetype =~ '^! ' | |||
|
255 | let s:msgtype = 'Modification : ' | |||
|
256 | elseif s:filewithchangetype =~ '^+ ' | |||
|
257 | let s:msgtype = 'Addition : ' | |||
|
258 | elseif s:filewithchangetype =~ '^- ' | |||
|
259 | let s:msgtype = 'Deletion : ' | |||
|
260 | endif | |||
|
261 | let s:bufnum = bufnr(s:RelativeFilePath) | |||
|
262 | if buflisted(s:bufnum) && getbufvar(s:bufnum, '&mod') | |||
|
263 | call s:PR_echo('Old buffer for file [' . s:RelativeFilePath . '] exists in modified state. Skipping review.', 1) | |||
|
264 | continue | |||
|
265 | endif | |||
|
266 | let s:tmpname = substitute(s:RelativeFilePath, '/', '_', 'g') | |||
|
267 | let s:tmpname = substitute(s:tmpname, '\\', '_', 'g') | |||
|
268 | let s:tmpname = g:patchreview_tmpdir . 'PatchReview.' . s:tmpname . '.' . strftime('%Y%m%d%H%M%S') | |||
|
269 | if has('win32') | |||
|
270 | let s:tmpname = substitute(s:tmpname, '/', '\\', 'g') | |||
|
271 | endif | |||
|
272 | if ! exists('s:patchreview_tmpfiles') | |||
|
273 | let s:patchreview_tmpfiles = [] | |||
|
274 | endif | |||
|
275 | let s:patchreview_tmpfiles = s:patchreview_tmpfiles + [s:tmpname] | |||
|
276 | ||||
|
277 | let s:filterdiffcmd = '!' . g:patchreview_filterdiff . ' -i ' . s:RelativeFilePath . ' ' . s:PatchFilePath . ' > ' . s:tmpname | |||
|
278 | silent! exe s:filterdiffcmd | |||
|
279 | if s:filewithchangetype =~ '^+ ' | |||
|
280 | if has('win32') | |||
|
281 | let s:inputfile = 'nul' | |||
|
282 | else | |||
|
283 | let s:inputfile = '/dev/null' | |||
|
284 | endif | |||
|
285 | else | |||
|
286 | let s:inputfile = expand(s:RelativeFilePath, ':p') | |||
|
287 | endif | |||
|
288 | silent exe '!' . g:patchreview_patch . ' -o ' . s:tmpname . '.file ' . s:inputfile . ' < ' . s:tmpname | |||
|
289 | let s:origtabpagenr = tabpagenr() | |||
|
290 | silent! exe 'tabedit ' . s:RelativeFilePath | |||
|
291 | silent! exe 'vert diffsplit ' . s:tmpname . '.file' | |||
|
292 | if filereadable(s:tmpname . '.file.rej') | |||
|
293 | silent! exe 'topleft 5split ' . s:tmpname . '.file.rej' | |||
|
294 | call s:PR_echo(s:msgtype . '*** REJECTED *** ' . s:RelativeFilePath, 1) | |||
|
295 | else | |||
|
296 | call s:PR_echo(s:msgtype . ' ' . s:RelativeFilePath, 1) | |||
|
297 | endif | |||
|
298 | silent! exe 'tabn ' . s:origtabpagenr | |||
|
299 | endfor | |||
|
300 | call s:PR_echo('-----') | |||
|
301 | call s:PR_echo('Done.') | |||
|
302 | let &shortmess = s:save_shortmess | |||
|
303 | "}}} | |||
|
304 | endfunction | |||
|
305 | "}}} | |||
|
306 | ||||
|
307 | function! <SID>PatchReviewCleanup() "{{{ | |||
|
308 | let s:retval = s:PR_GetTempDirLocation(1) | |||
|
309 | if s:retval && exists('g:patchreview_tmpdir') && isdirectory(g:patchreview_tmpdir) && filewritable(g:patchreview_tmpdir) | |||
|
310 | let s:zefilestr = globpath(g:patchreview_tmpdir, 'PatchReview.*') | |||
|
311 | let s:theFilesList = split(s:zefilestr, '\m[\r\n]\+') | |||
|
312 | for s:thefile in s:theFilesList | |||
|
313 | call delete(s:thefile) | |||
|
314 | endfor | |||
|
315 | endif | |||
|
316 | endfunction | |||
|
317 | "}}} | |||
|
318 | ||||
|
319 | " Commands "{{{ | |||
|
320 | "============================================================================ | |||
|
321 | " :PatchReview | |||
|
322 | command! -nargs=* -complete=file PatchReview call s:PatchReview (<f-args>) | |||
|
323 | ||||
|
324 | ||||
|
325 | " :PatchReviewCleanup | |||
|
326 | command! -nargs=0 PatchReviewCleanup call s:PatchReviewCleanup () | |||
|
327 | "}}} | |||
|
328 | "}}} | |||
|
329 | ||||
|
330 | " vim: textwidth=78 nowrap tabstop=2 shiftwidth=2 softtabstop=2 expandtab | |||
|
331 | " vim: filetype=vim encoding=latin1 fileformat=unix foldlevel=0 foldmethod=marker | |||
|
332 | "}}} |
General Comments 0
You need to be logged in to leave comments.
Login now