Mercurial > vim-lawrencium
comparison autoload/lawrencium/diff.vim @ 139:065625e1bb31
Split plugin file into multiple extensions.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Mon, 13 Jun 2016 09:32:34 -0700 |
parents | |
children | 4d5f4233b04e |
comparison
equal
deleted
inserted
replaced
138:a2d823c82e5f | 139:065625e1bb31 |
---|---|
1 | |
2 function! lawrencium#diff#init() abort | |
3 call lawrencium#add_command("-nargs=* Hgdiff :call lawrencium#diff#HgDiff('%:p', 0, <f-args>)") | |
4 call lawrencium#add_command("-nargs=* Hgvdiff :call lawrencium#diff#HgDiff('%:p', 1, <f-args>)") | |
5 call lawrencium#add_command("-nargs=* Hgtabdiff :call lawrencium#diff#HgDiff('%:p', 2, <f-args>)") | |
6 | |
7 call lawrencium#add_command("-nargs=* Hgdiffsum :call lawrencium#diff#HgDiffSummary('%:p', 0, <f-args>)") | |
8 call lawrencium#add_command("-nargs=* Hgdiffsumsplit :call lawrencium#diff#HgDiffSummary('%:p', 1, <f-args>)") | |
9 call lawrencium#add_command("-nargs=* Hgvdiffsumsplit :call lawrencium#diff#HgDiffSummary('%:p', 2, <f-args>)") | |
10 call lawrencium#add_command("-nargs=* Hgtabdiffsum :call lawrencium#diff#HgDiffSummary('%:p', 3, <f-args>)") | |
11 | |
12 call lawrencium#add_reader('diff', 'lawrencium#diff#read') | |
13 endfunction | |
14 | |
15 function! lawrencium#diff#read(repo, path_parts, full_path) abort | |
16 let l:diffargs = [] | |
17 let l:commaidx = stridx(a:path_parts['value'], ',') | |
18 if l:commaidx > 0 | |
19 let l:rev1 = strpart(a:path_parts['value'], 0, l:commaidx) | |
20 let l:rev2 = strpart(a:path_parts['value'], l:commaidx + 1) | |
21 if l:rev1 == '-' | |
22 let l:diffargs = [ '-r', l:rev2 ] | |
23 elseif l:rev2 == '-' | |
24 let l:diffargs = [ '-r', l:rev1 ] | |
25 else | |
26 let l:diffargs = [ '-r', l:rev1, '-r', l:rev2 ] | |
27 endif | |
28 elseif a:path_parts['value'] != '' | |
29 let l:diffargs = [ '-c', a:path_parts['value'] ] | |
30 else | |
31 let l:diffargs = [] | |
32 endif | |
33 if a:path_parts['path'] != '' && a:path_parts['path'] != '.' | |
34 call add(l:diffargs, a:full_path) | |
35 endif | |
36 call a:repo.ReadCommandOutput('diff', l:diffargs) | |
37 setlocal filetype=diff | |
38 setlocal nofoldenable | |
39 endfunction | |
40 | |
41 function! lawrencium#diff#HgDiff(filename, split, ...) abort | |
42 " Default revisions to diff: the working directory (null string) | |
43 " and the parent of the working directory (using Mercurial's revsets syntax). | |
44 " Otherwise, use the 1 or 2 revisions specified as extra parameters. | |
45 let l:rev1 = 'p1()' | |
46 let l:rev2 = '' | |
47 if a:0 == 1 | |
48 if type(a:1) == type([]) | |
49 if len(a:1) >= 2 | |
50 let l:rev1 = a:1[0] | |
51 let l:rev2 = a:1[1] | |
52 elseif len(a:1) == 1 | |
53 let l:rev1 = a:1[0] | |
54 endif | |
55 else | |
56 let l:rev1 = a:1 | |
57 endif | |
58 elseif a:0 == 2 | |
59 let l:rev1 = a:1 | |
60 let l:rev2 = a:2 | |
61 endif | |
62 | |
63 " Get the current repo, and expand the given filename in case it contains | |
64 " fancy filename modifiers. | |
65 let l:repo = lawrencium#hg_repo() | |
66 let l:path = expand(a:filename) | |
67 let l:diff_id = localtime() | |
68 call lawrencium#trace("Diff'ing '".l:rev1."' and '".l:rev2."' on file: ".l:path) | |
69 | |
70 " Get the first file and open it. | |
71 let l:cleanupbufnr = -1 | |
72 if l:rev1 == '' | |
73 if a:split == 2 | |
74 " Don't use `tabedit` here because if `l:path` is the same as | |
75 " the current path, it will also reload the buffer in the current | |
76 " tab/window for some reason, which causes all state to be lost | |
77 " (all folds get collapsed again, cursor is moved to start, etc.) | |
78 tabnew | |
79 let l:cleanupbufnr = bufnr('%') | |
80 execute 'edit ' . fnameescape(l:path) | |
81 else | |
82 if bufexists(l:path) | |
83 execute 'buffer ' . fnameescape(l:path) | |
84 else | |
85 execute 'edit ' . fnameescape(l:path) | |
86 endif | |
87 endif | |
88 " Make it part of the diff group. | |
89 call s:HgDiff_DiffThis(l:diff_id) | |
90 else | |
91 let l:rev_path = l:repo.GetLawrenciumPath(l:path, 'rev', l:rev1) | |
92 if a:split == 2 | |
93 " See comments above about avoiding `tabedit`. | |
94 tabnew | |
95 let l:cleanupbufnr = bufnr('%') | |
96 endif | |
97 execute 'edit ' . fnameescape(l:rev_path) | |
98 " Make it part of the diff group. | |
99 call s:HgDiff_DiffThis(l:diff_id) | |
100 endif | |
101 if l:cleanupbufnr >= 0 && bufloaded(l:cleanupbufnr) | |
102 execute 'bdelete ' . l:cleanupbufnr | |
103 endif | |
104 | |
105 " Get the second file and open it too. | |
106 " Don't use `diffsplit` because it will set `&diff` before we get a chance | |
107 " to save a bunch of local settings that we will want to restore later. | |
108 let l:diffsplit = 'split' | |
109 if a:split >= 1 | |
110 let l:diffsplit = 'vsplit' | |
111 endif | |
112 if l:rev2 == '' | |
113 execute l:diffsplit . ' ' . fnameescape(l:path) | |
114 else | |
115 let l:rev_path = l:repo.GetLawrenciumPath(l:path, 'rev', l:rev2) | |
116 execute l:diffsplit . ' ' . fnameescape(l:rev_path) | |
117 endif | |
118 call s:HgDiff_DiffThis(l:diff_id) | |
119 endfunction | |
120 | |
121 function! lawrencium#diff#HgDiffThis(diff_id) | |
122 call s:HgDiff_DiffThis(a:diff_id) | |
123 endfunction | |
124 | |
125 function! s:HgDiff_DiffThis(diff_id) abort | |
126 " Store some commands to run when we exit diff mode. | |
127 " It's needed because `diffoff` reverts those settings to their default | |
128 " values, instead of their previous ones. | |
129 if &diff | |
130 call lawrencium#throw("Calling diffthis too late on a buffer!") | |
131 return | |
132 endif | |
133 call lawrencium#trace('Enabling diff mode on ' . bufname('%')) | |
134 let w:lawrencium_diffoff = {} | |
135 let w:lawrencium_diffoff['&diff'] = 0 | |
136 let w:lawrencium_diffoff['&wrap'] = &l:wrap | |
137 let w:lawrencium_diffoff['&scrollopt'] = &l:scrollopt | |
138 let w:lawrencium_diffoff['&scrollbind'] = &l:scrollbind | |
139 let w:lawrencium_diffoff['&cursorbind'] = &l:cursorbind | |
140 let w:lawrencium_diffoff['&foldmethod'] = &l:foldmethod | |
141 let w:lawrencium_diffoff['&foldcolumn'] = &l:foldcolumn | |
142 let w:lawrencium_diffoff['&foldenable'] = &l:foldenable | |
143 let w:lawrencium_diff_id = a:diff_id | |
144 diffthis | |
145 autocmd BufWinLeave <buffer> call s:HgDiff_CleanUp() | |
146 endfunction | |
147 | |
148 function! s:HgDiff_DiffOff(...) abort | |
149 " Get the window name (given as a paramter, or current window). | |
150 let l:nr = a:0 ? a:1 : winnr() | |
151 | |
152 " Run the commands we saved in `HgDiff_DiffThis`, or just run `diffoff`. | |
153 let l:backup = getwinvar(l:nr, 'lawrencium_diffoff') | |
154 if type(l:backup) == type({}) && len(l:backup) > 0 | |
155 call lawrencium#trace('Disabling diff mode on ' . l:nr) | |
156 for key in keys(l:backup) | |
157 call setwinvar(l:nr, key, l:backup[key]) | |
158 endfor | |
159 call setwinvar(l:nr, 'lawrencium_diffoff', {}) | |
160 else | |
161 call lawrencium#trace('Disabling diff mode on ' . l:nr . ' (but no true restore)') | |
162 diffoff | |
163 endif | |
164 endfunction | |
165 | |
166 function! s:HgDiff_GetDiffWindows(diff_id) abort | |
167 let l:result = [] | |
168 for nr in range(1, winnr('$')) | |
169 if getwinvar(nr, '&diff') && getwinvar(nr, 'lawrencium_diff_id') == a:diff_id | |
170 call add(l:result, nr) | |
171 endif | |
172 endfor | |
173 return l:result | |
174 endfunction | |
175 | |
176 function! s:HgDiff_CleanUp() abort | |
177 " If we're not leaving one of our diff window, do nothing. | |
178 if !&diff || !exists('w:lawrencium_diff_id') | |
179 return | |
180 endif | |
181 | |
182 " If there will be only one diff window left (plus the one we're leaving), | |
183 " turn off diff in it and restore its local settings. | |
184 let l:nrs = s:HgDiff_GetDiffWindows(w:lawrencium_diff_id) | |
185 if len(l:nrs) <= 2 | |
186 call lawrencium#trace('Disabling diff mode in ' . len(l:nrs) . ' windows.') | |
187 for nr in l:nrs | |
188 if getwinvar(nr, '&diff') | |
189 call s:HgDiff_DiffOff(nr) | |
190 endif | |
191 endfor | |
192 else | |
193 call lawrencium#trace('Still ' . len(l:nrs) . ' diff windows open.') | |
194 endif | |
195 endfunction | |
196 | |
197 function! lawrencium#diff#HgDiffSummary(filename, present_args, ...) abort | |
198 " Default revisions to diff: the working directory (null string) | |
199 " and the parent of the working directory (using Mercurial's revsets syntax). | |
200 " Otherwise, use the 1 or 2 revisions specified as extra parameters. | |
201 let l:revs = '' | |
202 if a:0 == 1 | |
203 if type(a:1) == type([]) | |
204 if len(a:1) >= 2 | |
205 let l:revs = a:1[0] . ',' . a:1[1] | |
206 elseif len(a:1) == 1 | |
207 let l:revs = a:1[0] | |
208 endif | |
209 else | |
210 let l:revs = a:1 | |
211 endif | |
212 elseif a:0 >= 2 | |
213 let l:revs = a:1 . ',' . a:2 | |
214 endif | |
215 | |
216 " Get the current repo, and expand the given filename in case it contains | |
217 " fancy filename modifiers. | |
218 let l:repo = lawrencium#hg_repo() | |
219 let l:path = expand(a:filename) | |
220 call lawrencium#trace("Diff'ing revisions: '".l:revs."' on file: ".l:path) | |
221 let l:special = l:repo.GetLawrenciumPath(l:path, 'diff', l:revs) | |
222 | |
223 " Build the correct edit command, and switch to the correct window, based | |
224 " on the presentation arguments we got. | |
225 if type(a:present_args) == type(0) | |
226 " Just got a split mode. | |
227 let l:valid_args = {'split_mode': a:present_args} | |
228 else | |
229 " Got complex args. | |
230 let l:valid_args = a:present_args | |
231 endif | |
232 | |
233 " First, see if we should reuse an existing window based on some buffer | |
234 " variable. | |
235 let l:target_winnr = -1 | |
236 let l:split = get(l:valid_args, 'split_mode', 0) | |
237 let l:reuse_id = get(l:valid_args, 'reuse_id', '') | |
238 let l:avoid_id = get(l:valid_args, 'avoid_win', -1) | |
239 if l:reuse_id != '' | |
240 let l:target_winnr = lawrencium#find_buffer_window(l:reuse_id, 1) | |
241 if l:target_winnr > 0 && l:split != 3 | |
242 " Unless we'll be opening in a new tab, don't split anymore, since | |
243 " we found the exact window we wanted. | |
244 let l:split = 0 | |
245 endif | |
246 call lawrencium#trace("Looking for window with '".l:reuse_id."', found: ".l:target_winnr) | |
247 endif | |
248 " If we didn't find anything, see if we should use the current or previous | |
249 " window. | |
250 if l:target_winnr <= 0 | |
251 let l:use_prev_win = get(l:valid_args, 'use_prev_win', 0) | |
252 if l:use_prev_win | |
253 let l:target_winnr = winnr('#') | |
254 call lawrencium#trace("Will use previous window: ".l:target_winnr) | |
255 endif | |
256 endif | |
257 " And let's see if we have a window we should actually avoid. | |
258 if l:avoid_id >= 0 && | |
259 \(l:target_winnr == l:avoid_id || | |
260 \(l:target_winnr <= 0 && winnr() == l:avoid_id)) | |
261 for wnr in range(1, winnr('$')) | |
262 if wnr != l:avoid_id | |
263 call lawrencium#trace("Avoiding using window ".l:avoid_id. | |
264 \", now using: ".wnr) | |
265 let l:target_winnr = wnr | |
266 break | |
267 endif | |
268 endfor | |
269 endif | |
270 " Now let's see what kind of split we want to use, if any. | |
271 let l:cmd = 'edit ' | |
272 if l:split == 1 | |
273 let l:cmd = 'rightbelow split ' | |
274 elseif l:split == 2 | |
275 let l:cmd = 'rightbelow vsplit ' | |
276 elseif l:split == 3 | |
277 let l:cmd = 'tabedit ' | |
278 endif | |
279 | |
280 " All good now, proceed. | |
281 if l:target_winnr > 0 | |
282 execute l:target_winnr . "wincmd w" | |
283 endif | |
284 execute 'keepalt ' . l:cmd . fnameescape(l:special) | |
285 | |
286 " Set the reuse ID if we had one. | |
287 if l:reuse_id != '' | |
288 call lawrencium#trace("Setting reuse ID '".l:reuse_id."' on buffer: ".bufnr('%')) | |
289 call setbufvar('%', l:reuse_id, 1) | |
290 endif | |
291 endfunction | |
292 |