Mercurial > vim-lawrencium
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/autoload/lawrencium/diff.vim Mon Jun 13 09:32:34 2016 -0700 @@ -0,0 +1,292 @@ + +function! lawrencium#diff#init() abort + call lawrencium#add_command("-nargs=* Hgdiff :call lawrencium#diff#HgDiff('%:p', 0, <f-args>)") + call lawrencium#add_command("-nargs=* Hgvdiff :call lawrencium#diff#HgDiff('%:p', 1, <f-args>)") + call lawrencium#add_command("-nargs=* Hgtabdiff :call lawrencium#diff#HgDiff('%:p', 2, <f-args>)") + + call lawrencium#add_command("-nargs=* Hgdiffsum :call lawrencium#diff#HgDiffSummary('%:p', 0, <f-args>)") + call lawrencium#add_command("-nargs=* Hgdiffsumsplit :call lawrencium#diff#HgDiffSummary('%:p', 1, <f-args>)") + call lawrencium#add_command("-nargs=* Hgvdiffsumsplit :call lawrencium#diff#HgDiffSummary('%:p', 2, <f-args>)") + call lawrencium#add_command("-nargs=* Hgtabdiffsum :call lawrencium#diff#HgDiffSummary('%:p', 3, <f-args>)") + + call lawrencium#add_reader('diff', 'lawrencium#diff#read') +endfunction + +function! lawrencium#diff#read(repo, path_parts, full_path) abort + let l:diffargs = [] + let l:commaidx = stridx(a:path_parts['value'], ',') + if l:commaidx > 0 + let l:rev1 = strpart(a:path_parts['value'], 0, l:commaidx) + let l:rev2 = strpart(a:path_parts['value'], l:commaidx + 1) + if l:rev1 == '-' + let l:diffargs = [ '-r', l:rev2 ] + elseif l:rev2 == '-' + let l:diffargs = [ '-r', l:rev1 ] + else + let l:diffargs = [ '-r', l:rev1, '-r', l:rev2 ] + endif + elseif a:path_parts['value'] != '' + let l:diffargs = [ '-c', a:path_parts['value'] ] + else + let l:diffargs = [] + endif + if a:path_parts['path'] != '' && a:path_parts['path'] != '.' + call add(l:diffargs, a:full_path) + endif + call a:repo.ReadCommandOutput('diff', l:diffargs) + setlocal filetype=diff + setlocal nofoldenable +endfunction + +function! lawrencium#diff#HgDiff(filename, split, ...) abort + " Default revisions to diff: the working directory (null string) + " and the parent of the working directory (using Mercurial's revsets syntax). + " Otherwise, use the 1 or 2 revisions specified as extra parameters. + let l:rev1 = 'p1()' + let l:rev2 = '' + if a:0 == 1 + if type(a:1) == type([]) + if len(a:1) >= 2 + let l:rev1 = a:1[0] + let l:rev2 = a:1[1] + elseif len(a:1) == 1 + let l:rev1 = a:1[0] + endif + else + let l:rev1 = a:1 + endif + elseif a:0 == 2 + let l:rev1 = a:1 + let l:rev2 = a:2 + endif + + " Get the current repo, and expand the given filename in case it contains + " fancy filename modifiers. + let l:repo = lawrencium#hg_repo() + let l:path = expand(a:filename) + let l:diff_id = localtime() + call lawrencium#trace("Diff'ing '".l:rev1."' and '".l:rev2."' on file: ".l:path) + + " Get the first file and open it. + let l:cleanupbufnr = -1 + if l:rev1 == '' + if a:split == 2 + " Don't use `tabedit` here because if `l:path` is the same as + " the current path, it will also reload the buffer in the current + " tab/window for some reason, which causes all state to be lost + " (all folds get collapsed again, cursor is moved to start, etc.) + tabnew + let l:cleanupbufnr = bufnr('%') + execute 'edit ' . fnameescape(l:path) + else + if bufexists(l:path) + execute 'buffer ' . fnameescape(l:path) + else + execute 'edit ' . fnameescape(l:path) + endif + endif + " Make it part of the diff group. + call s:HgDiff_DiffThis(l:diff_id) + else + let l:rev_path = l:repo.GetLawrenciumPath(l:path, 'rev', l:rev1) + if a:split == 2 + " See comments above about avoiding `tabedit`. + tabnew + let l:cleanupbufnr = bufnr('%') + endif + execute 'edit ' . fnameescape(l:rev_path) + " Make it part of the diff group. + call s:HgDiff_DiffThis(l:diff_id) + endif + if l:cleanupbufnr >= 0 && bufloaded(l:cleanupbufnr) + execute 'bdelete ' . l:cleanupbufnr + endif + + " Get the second file and open it too. + " Don't use `diffsplit` because it will set `&diff` before we get a chance + " to save a bunch of local settings that we will want to restore later. + let l:diffsplit = 'split' + if a:split >= 1 + let l:diffsplit = 'vsplit' + endif + if l:rev2 == '' + execute l:diffsplit . ' ' . fnameescape(l:path) + else + let l:rev_path = l:repo.GetLawrenciumPath(l:path, 'rev', l:rev2) + execute l:diffsplit . ' ' . fnameescape(l:rev_path) + endif + call s:HgDiff_DiffThis(l:diff_id) +endfunction + +function! lawrencium#diff#HgDiffThis(diff_id) + call s:HgDiff_DiffThis(a:diff_id) +endfunction + +function! s:HgDiff_DiffThis(diff_id) abort + " Store some commands to run when we exit diff mode. + " It's needed because `diffoff` reverts those settings to their default + " values, instead of their previous ones. + if &diff + call lawrencium#throw("Calling diffthis too late on a buffer!") + return + endif + call lawrencium#trace('Enabling diff mode on ' . bufname('%')) + let w:lawrencium_diffoff = {} + let w:lawrencium_diffoff['&diff'] = 0 + let w:lawrencium_diffoff['&wrap'] = &l:wrap + let w:lawrencium_diffoff['&scrollopt'] = &l:scrollopt + let w:lawrencium_diffoff['&scrollbind'] = &l:scrollbind + let w:lawrencium_diffoff['&cursorbind'] = &l:cursorbind + let w:lawrencium_diffoff['&foldmethod'] = &l:foldmethod + let w:lawrencium_diffoff['&foldcolumn'] = &l:foldcolumn + let w:lawrencium_diffoff['&foldenable'] = &l:foldenable + let w:lawrencium_diff_id = a:diff_id + diffthis + autocmd BufWinLeave <buffer> call s:HgDiff_CleanUp() +endfunction + +function! s:HgDiff_DiffOff(...) abort + " Get the window name (given as a paramter, or current window). + let l:nr = a:0 ? a:1 : winnr() + + " Run the commands we saved in `HgDiff_DiffThis`, or just run `diffoff`. + let l:backup = getwinvar(l:nr, 'lawrencium_diffoff') + if type(l:backup) == type({}) && len(l:backup) > 0 + call lawrencium#trace('Disabling diff mode on ' . l:nr) + for key in keys(l:backup) + call setwinvar(l:nr, key, l:backup[key]) + endfor + call setwinvar(l:nr, 'lawrencium_diffoff', {}) + else + call lawrencium#trace('Disabling diff mode on ' . l:nr . ' (but no true restore)') + diffoff + endif +endfunction + +function! s:HgDiff_GetDiffWindows(diff_id) abort + let l:result = [] + for nr in range(1, winnr('$')) + if getwinvar(nr, '&diff') && getwinvar(nr, 'lawrencium_diff_id') == a:diff_id + call add(l:result, nr) + endif + endfor + return l:result +endfunction + +function! s:HgDiff_CleanUp() abort + " If we're not leaving one of our diff window, do nothing. + if !&diff || !exists('w:lawrencium_diff_id') + return + endif + + " If there will be only one diff window left (plus the one we're leaving), + " turn off diff in it and restore its local settings. + let l:nrs = s:HgDiff_GetDiffWindows(w:lawrencium_diff_id) + if len(l:nrs) <= 2 + call lawrencium#trace('Disabling diff mode in ' . len(l:nrs) . ' windows.') + for nr in l:nrs + if getwinvar(nr, '&diff') + call s:HgDiff_DiffOff(nr) + endif + endfor + else + call lawrencium#trace('Still ' . len(l:nrs) . ' diff windows open.') + endif +endfunction + +function! lawrencium#diff#HgDiffSummary(filename, present_args, ...) abort + " Default revisions to diff: the working directory (null string) + " and the parent of the working directory (using Mercurial's revsets syntax). + " Otherwise, use the 1 or 2 revisions specified as extra parameters. + let l:revs = '' + if a:0 == 1 + if type(a:1) == type([]) + if len(a:1) >= 2 + let l:revs = a:1[0] . ',' . a:1[1] + elseif len(a:1) == 1 + let l:revs = a:1[0] + endif + else + let l:revs = a:1 + endif + elseif a:0 >= 2 + let l:revs = a:1 . ',' . a:2 + endif + + " Get the current repo, and expand the given filename in case it contains + " fancy filename modifiers. + let l:repo = lawrencium#hg_repo() + let l:path = expand(a:filename) + call lawrencium#trace("Diff'ing revisions: '".l:revs."' on file: ".l:path) + let l:special = l:repo.GetLawrenciumPath(l:path, 'diff', l:revs) + + " Build the correct edit command, and switch to the correct window, based + " on the presentation arguments we got. + if type(a:present_args) == type(0) + " Just got a split mode. + let l:valid_args = {'split_mode': a:present_args} + else + " Got complex args. + let l:valid_args = a:present_args + endif + + " First, see if we should reuse an existing window based on some buffer + " variable. + let l:target_winnr = -1 + let l:split = get(l:valid_args, 'split_mode', 0) + let l:reuse_id = get(l:valid_args, 'reuse_id', '') + let l:avoid_id = get(l:valid_args, 'avoid_win', -1) + if l:reuse_id != '' + let l:target_winnr = lawrencium#find_buffer_window(l:reuse_id, 1) + if l:target_winnr > 0 && l:split != 3 + " Unless we'll be opening in a new tab, don't split anymore, since + " we found the exact window we wanted. + let l:split = 0 + endif + call lawrencium#trace("Looking for window with '".l:reuse_id."', found: ".l:target_winnr) + endif + " If we didn't find anything, see if we should use the current or previous + " window. + if l:target_winnr <= 0 + let l:use_prev_win = get(l:valid_args, 'use_prev_win', 0) + if l:use_prev_win + let l:target_winnr = winnr('#') + call lawrencium#trace("Will use previous window: ".l:target_winnr) + endif + endif + " And let's see if we have a window we should actually avoid. + if l:avoid_id >= 0 && + \(l:target_winnr == l:avoid_id || + \(l:target_winnr <= 0 && winnr() == l:avoid_id)) + for wnr in range(1, winnr('$')) + if wnr != l:avoid_id + call lawrencium#trace("Avoiding using window ".l:avoid_id. + \", now using: ".wnr) + let l:target_winnr = wnr + break + endif + endfor + endif + " Now let's see what kind of split we want to use, if any. + let l:cmd = 'edit ' + if l:split == 1 + let l:cmd = 'rightbelow split ' + elseif l:split == 2 + let l:cmd = 'rightbelow vsplit ' + elseif l:split == 3 + let l:cmd = 'tabedit ' + endif + + " All good now, proceed. + if l:target_winnr > 0 + execute l:target_winnr . "wincmd w" + endif + execute 'keepalt ' . l:cmd . fnameescape(l:special) + + " Set the reuse ID if we had one. + if l:reuse_id != '' + call lawrencium#trace("Setting reuse ID '".l:reuse_id."' on buffer: ".bufnr('%')) + call setbufvar('%', l:reuse_id, 1) + endif +endfunction +