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