From 0597380f6b22f43a3ea6ff8364d5c239bb2504ea Mon Sep 17 00:00:00 2001 From: Andy Stewart Date: Thu, 18 Oct 2018 15:39:55 +0100 Subject: [PATCH] Prompt user to choose hunk when overlapping. See #556. --- autoload/gitgutter/diff.vim | 14 +++++++++++++- autoload/gitgutter/hunk.vim | 24 ++++++++++++++++++++++++ test/test_gitgutter.vim | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/autoload/gitgutter/diff.vim b/autoload/gitgutter/diff.vim index 4e1ec66..68b2117 100644 --- a/autoload/gitgutter/diff.vim +++ b/autoload/gitgutter/diff.vim @@ -332,8 +332,14 @@ endfunction " Returns a diff for the current hunk. -function! gitgutter#diff#hunk_diff(bufnr, full_diff) +" Assumes there is only 1 current hunk unless the optional argument is given, +" in which case the cursor is in two hunks and the argument specifies the one +" to choose. +" +" Optional argument: 0 (to use the first hunk) or 1 (to use the second). +function! gitgutter#diff#hunk_diff(bufnr, full_diff, ...) let modified_diff = [] + let hunk_index = 0 let keep_line = 1 " Don't keepempty when splitting because the diff we want may not be the " final one. Instead add trailing NL at end of function. @@ -341,6 +347,12 @@ function! gitgutter#diff#hunk_diff(bufnr, full_diff) let hunk_info = gitgutter#diff#parse_hunk(line) if len(hunk_info) == 4 " start of new hunk let keep_line = gitgutter#hunk#cursor_in_hunk(hunk_info) + + if a:0 && hunk_index != a:1 + let keep_line = 0 + endif + + let hunk_index += 1 endif if keep_line call add(modified_diff, line) diff --git a/autoload/gitgutter/hunk.vim b/autoload/gitgutter/hunk.vim index c779b59..3c886dc 100644 --- a/autoload/gitgutter/hunk.vim +++ b/autoload/gitgutter/hunk.vim @@ -93,6 +93,19 @@ function! s:current_hunk() abort return current_hunk endfunction +" Returns truthy if the cursor is in two hunks (which can only happen if the +" cursor is on the first line and lines above have been deleted and lines +" immediately below have been deleted) or falsey otherwise. +function! s:cursor_in_two_hunks() + let hunks = gitgutter#hunk#hunks(bufnr('')) + + if line('.') == 1 && len(hunks) > 1 && hunks[0][2:3] == [0, 0] && hunks[1][2:3] == [1, 0] + return 1 + endif + + return 0 +endfunction + " A line can be in 0 or 1 hunks, with the following exception: when the first " line(s) of a file has been deleted, and the new second line (and " optionally below) has been deleted, the new first line is in two hunks. @@ -161,6 +174,17 @@ function! s:hunk_op(op) if empty(s:current_hunk()) call gitgutter#utility#warn('cursor is not in a hunk') + elseif s:cursor_in_two_hunks() + let choice = input('Choose hunk: upper or lower (u/l)? ') + " Clear input + normal! : + if choice =~ 'u' + call a:op(gitgutter#diff#hunk_diff(bufnr, diff, 0)) + elseif choice =~ 'l' + call a:op(gitgutter#diff#hunk_diff(bufnr, diff, 1)) + else + call gitgutter#utility#warn('did not recognise your choice') + endif else call a:op(gitgutter#diff#hunk_diff(bufnr, diff)) endif diff --git a/test/test_gitgutter.vim b/test/test_gitgutter.vim index 96a2d2c..1e13cc6 100644 --- a/test/test_gitgutter.vim +++ b/test/test_gitgutter.vim @@ -455,6 +455,42 @@ function Test_undo_nearby_hunk() endfunction +function Test_overlapping_hunk_op() + func Answer(char) + call feedkeys(a:char."\") + endfunc + + " Undo upper + + execute '3d' + execute '1d' + call s:trigger_gitgutter() + normal gg + call timer_start(100, {-> Answer('u')} ) + GitGutterUndoHunk + call s:trigger_gitgutter() + + let expected = [ + \ 'line=2 id=3000 name=GitGutterLineRemoved', + \ ] + call assert_equal(expected, s:signs('fixture.txt')) + + " Undo lower + + execute '1d' + call s:trigger_gitgutter() + normal gg + call timer_start(100, {-> Answer('l')} ) + GitGutterUndoHunk + call s:trigger_gitgutter() + + let expected = [ + \ 'line=1 id=3000 name=GitGutterLineRemovedFirstLine', + \ ] + call assert_equal(expected, s:signs('fixture.txt')) +endfunction + + function Test_write_option() set nowrite