Retool diffing

Support mods, rename to match the built-in :diffsplit, and (perhaps
controversially) move three-way diffing to the bang variant, so that the
default behavior always focuses the other window.

Closes https://github.com/tpope/vim-fugitive/pull/715
This commit is contained in:
Tim Pope 2019-07-07 10:34:04 -04:00
parent e49b9a9303
commit cd78a08543
2 changed files with 73 additions and 50 deletions

View File

@ -76,11 +76,12 @@ function! s:DirCheck(...) abort
endfunction
function! s:Mods(mods, ...) abort
let mods = a:mods ==# '<mods>' || empty(a:mods) ? '' : a:mods . ' '
if a:0 && mods !~# 'aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright\|tab'
let mods = substitute(a:mods, '\C<mods>', '', '')
let mods = mods =~# '\S$' ? a:mods . ' ' : a:mods
if a:0 && mods !~# '\<\%(aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright\|tab\)\>'
let mods = a:1 . ' ' . mods
endif
return mods
return substitute(mods, '\s\+', ' ', 'g')
endfunction
function! s:Slash(path) abort
@ -1710,12 +1711,12 @@ function! fugitive#BufReadStatus() abort
exe 'xnoremap <buffer> <silent>' nowait "= :<C-U>execute <SID>StageInline('toggle',line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>"
exe 'xnoremap <buffer> <silent>' nowait "< :<C-U>execute <SID>StageInline('show', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>"
exe 'xnoremap <buffer> <silent>' nowait "> :<C-U>execute <SID>StageInline('hide', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>"
nnoremap <buffer> <silent> D :<C-U>execute <SID>StageDiff('Gdiff')<CR>
nnoremap <buffer> <silent> dd :<C-U>execute <SID>StageDiff('Gdiff')<CR>
nnoremap <buffer> <silent> dh :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
nnoremap <buffer> <silent> ds :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
nnoremap <buffer> <silent> D :<C-U>execute <SID>StageDiff('Gdiffsplit')<CR>
nnoremap <buffer> <silent> dd :<C-U>execute <SID>StageDiff('Gdiffsplit')<CR>
nnoremap <buffer> <silent> dh :<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>
nnoremap <buffer> <silent> ds :<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>
nnoremap <buffer> <silent> dp :<C-U>execute <SID>StageDiffEdit()<CR>
nnoremap <buffer> <silent> dv :<C-U>execute <SID>StageDiff('Gvdiff')<CR>
nnoremap <buffer> <silent> dv :<C-U>execute <SID>StageDiff('Gvdiffsplit')<CR>
nnoremap <buffer> <silent> J :<C-U>execute <SID>StageNext(v:count1)<CR>
nnoremap <buffer> <silent> K :<C-U>execute <SID>StagePrevious(v:count1)<CR>
nnoremap <buffer> <silent> P :<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>
@ -3739,9 +3740,9 @@ endfunction
" Section: :Gdiff
call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gdiff :execute s:Diff('',<bang>0,<f-args>)")
call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gvdiff :execute s:Diff('keepalt vert ',<bang>0,<f-args>)")
call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gsdiff :execute s:Diff('keepalt ',<bang>0,<f-args>)")
call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gdiffsplit :execute s:Diff(1, <bang>0, '<mods>', <f-args>)")
call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gvdiffsplit :execute s:Diff(0, <bang>0, 'vertical <mods>', <f-args>)")
call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Ghdiffsplit :execute s:Diff(0, <bang>0, '<mods>', <f-args>)")
augroup fugitive_diff
autocmd!
@ -3768,13 +3769,13 @@ endfunction
function! s:diff_modifier(count) abort
let fdc = matchstr(&diffopt, 'foldcolumn:\zs\d\+')
if &diffopt =~# 'horizontal' && &diffopt !~# 'vertical'
return 'keepalt '
return ''
elseif &diffopt =~# 'vertical'
return 'keepalt vert '
return 'vertical '
elseif winwidth(0) <= a:count * ((&tw ? &tw : 80) + (empty(fdc) ? 2 : fdc))
return 'keepalt '
return ''
else
return 'keepalt vert '
return 'vertical '
endif
endfunction
@ -3855,26 +3856,37 @@ function! s:CompareAge(mine, theirs) abort
return my_time < their_time ? -1 : my_time != their_time
endfunction
function! s:Diff(vert,keepfocus,...) abort
function! s:IsConflicted() abort
return !empty(s:TreeChomp('ls-files', '--unmerged', '--', expand('%:p')))
endfunction
function! s:Diff(autodir, keepfocus, mods, ...) abort
if exists(':DiffGitCached') && !a:0
return s:Mods(a:mods) . 'DiffGitCached'
endif
let args = copy(a:000)
let post = ''
if get(args, 0) =~# '^+'
let post = remove(args, 0)[1:-1]
endif
let vert = empty(a:vert) ? s:diff_modifier(2) : a:vert
let commit = s:DirCommitFile(@%)[1]
if a:mods =~# '\<tab\>'
let mods = substitute(a:mods, '\<tab\>', '', 'g')
tab split
else
let mods = 'keepalt ' . a:mods
endif
let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p'
if exists(':DiffGitCached')
return 'DiffGitCached'
elseif (empty(args) || args[0] ==# ':') && commit =~# '^[0-1]\=$' && !empty(s:TreeChomp('ls-files', '--unmerged', '--', expand('%:p')))
let vert = empty(a:vert) ? s:diff_modifier(3) : a:vert
if (empty(args) || args[0] ==# ':') && commit =~# '^[0-1]\=$' && a:keepfocus && s:IsConflicted()
let mods = (a:autodir ? s:diff_modifier(3) : '') . s:Mods(mods, 'leftabove')
let nr = bufnr('')
execute 'leftabove '.vert.'split' s:fnameescape(s:Generate(s:Relative(':2:')))
execute mods 'split' s:fnameescape(s:Generate(s:Relative(':2:')))
execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
let nr2 = bufnr('')
call s:diffthis()
exe back
execute 'rightbelow '.vert.'split' s:fnameescape(s:Generate(s:Relative(':3:')))
let mods = substitute(mods, '\Cleftabove\|rightbelow\|aboveleft\|belowright', '\=submatch(0) =~# "f" ? "rightbelow" : "leftabove"', '')
execute mods 'split' s:fnameescape(s:Generate(s:Relative(':3:')))
execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
let nr3 = bufnr('')
call s:diffthis()
@ -3903,8 +3915,13 @@ function! s:Diff(vert,keepfocus,...) abort
if file !~# ':' && file !~# '^/' && s:TreeChomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$'
let file = file.s:Relative(':')
endif
elseif len(commit)
let file = s:Relative()
elseif s:IsConflicted()
let file = s:Relative(':1:')
let post = 'echohl WarningMsg|echo "Use :Gdiffsplit! for 3 way diff"|echohl NONE|' . post
else
let file = empty(commit) ? s:Relative(':0:') : s:Relative()
let file = s:Relative(':0:')
endif
try
let spec = s:Generate(file)
@ -3914,10 +3931,12 @@ function! s:Diff(vert,keepfocus,...) abort
endif
let w:fugitive_diff_restore = restore
if s:CompareAge(commit, s:DirCommitFile(spec)[1]) < 0
execute 'rightbelow '.vert.'diffsplit '.s:fnameescape(spec)
let mods = s:Mods(mods, 'rightbelow')
else
execute 'leftabove '.vert.'diffsplit '.s:fnameescape(spec)
let mods = s:Mods(mods, 'leftabove')
endif
let mods = (a:autodir ? s:diff_modifier(2) : '') . mods
execute mods 'diffsplit' s:fnameescape(spec)
let &l:readonly = &l:readonly
redraw
let w:fugitive_diff_restore = restore
@ -4789,18 +4808,18 @@ function! s:cfile() abort
let dref = matchstr(diff, '\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
let ref = matchstr(diff, '\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
let dcmd = 'Gdiff! +'.offset
let dcmd = 'Gdiffsplit! +'.offset
elseif getline('.') =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
let dref = matchstr(getline('.'),'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
let ref = matchstr(getline('.'),'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
let dcmd = 'Gdiff!'
let dcmd = 'Gdiffsplit!'
elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
let line = getline(line('.')-1)
let dref = matchstr(line,'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
let ref = matchstr(line,'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
let dcmd = 'Gdiff!'
let dcmd = 'Gdiffsplit!'
elseif line('$') == 1 && getline('.') =~ '^\x\{40,\}$'
let ref = getline('.')

View File

@ -143,25 +143,28 @@ that are part of Git repositories).
:Gwq! [path] Like |:Gwrite|! followed by |:quit|! if the write
succeeded.
*fugitive-:Gdiff*
:Gdiff [object] Perform a |vimdiff| against the given file, or if a
*fugitive-:Gdiffsplit*
:Gdiffsplit [object] Perform a |vimdiff| against the given file, or if a
commit is given, the current file in that commit.
With no argument, the version in the index is used
(which means a three-way diff during a merge conflict,
making it a git-mergetool alternative). The newer of
the two files is placed to the right or bottom,
depending on 'diffopt', and the width of the window
relative to 'textwidth'. Use |do| and |dp| and write
to the index file to simulate "git add --patch". For
the three-way diff, there is also d2o and d3o pulling
the hunk to the middle from the left or the right
window, respectively.
With no argument, the version in the index or work
tree is used. The newer of the two files is placed to
the right or bottom, depending on 'diffopt' and the
width of the window relative to 'textwidth'. Use
Vim's |do| and |dp| to stage and unstage changes.
*fugitive-:Gsdiff*
:Gsdiff [object] Like |:Gdiff|, but always split horizontally.
*fugitive-:Gdiffsplit!*
:Gdiffsplit! [object] During a merge conflict, do a three-way diff against
both ancestors. Additional d2o and d3o maps are
provided to to obtain the hunk from the "ours" or
"theirs" ancestor, respectively. If the file is not
conflicted, behaves the same as if no bang was given,
but keeps focus in the original window.
*fugitive-:Gvdiff*
:Gvdiff [object] Like |:Gdiff|, but always split vertically.
*fugitive-:Gvdiffsplit*
:Gvdiffsplit [object] Like |:Gdiffsplit|, but always split vertically.
*fugitive-:Ghdiffsplit* *fugitive-:Gsdiff*
:Ghdiffsplit [object] Like |:Gdiffsplit|, but always split horizontally.
*fugitive-:Gmove*
:Gmove {destination} Wrapper around git-mv that renames the buffer
@ -276,13 +279,14 @@ i On untracked files, call |:Git| add --intent-to-add.
automatically.
*fugitive_dd*
dd Perform a |:Gdiff| on the file under the cursor.
*fugitive_ds*
ds Perform a |:Gsdiff| on the file under the cursor.
dd Perform a |:Gdiffsplit| on the file under the cursor.
*fugitive_dv*
dv Perform a |:Gvdiff| on the file under the cursor.
dv Perform a |:Gvdiffsplit| on the file under the cursor.
*fugitive_ds* *fugitive_dh*
ds Perform a |:Ghdiffsplit| on the file under the cursor.
dh
*fugitive_dp*
dp Invoke |:Git!| diff on the file under the cursor.