Support nearly all git-blame flags

This commit is contained in:
Tim Pope 2019-08-11 00:36:31 -04:00
parent 7686b7dc5c
commit 4daa0c558c
2 changed files with 95 additions and 51 deletions

View File

@ -4558,7 +4558,7 @@ augroup fugitive_blame
autocmd FileType fugitiveblame setlocal nomodeline | if len(s:Dir()) | let &l:keywordprg = s:Keywordprg() | endif
autocmd User Fugitive
\ if get(b:, 'fugitive_type') =~# '^\%(file\|blob\)$' || s:BlameBufnr() > 0 || filereadable(@%) |
\ exe "command! -buffer -bar -bang -range=-1 -nargs=* Gblame :execute s:BlameCommand(<line1>,<line2>,+'<range>',<count>,<bang>0,'<mods>',<q-reg>,<q-args>,[<f-args>])" |
\ exe "command! -buffer -bar -bang -range=-1 -nargs=* -complete=customlist,s:BlameComplete Gblame :execute s:BlameCommand(<line1>,<line2>,+'<range>',<count>,<bang>0,'<mods>',<q-reg>,<q-args>,[<f-args>])" |
\ endif
autocmd ColorScheme,GUIEnter * call s:RehighlightBlame()
autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
@ -4583,6 +4583,20 @@ function! s:BlameBufnr(...) abort
endif
endfunction
function! s:BlameCommitFileLnum(...) abort
let line = a:0 ? a:1 : getline('.')
let commit = matchstr(line, '^\^\=\zs\x\+')
if commit =~# '^0\+$'
let commit = ''
endif
let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)')
let path = matchstr(line, '^\^\=\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s\+\%(\%( \d\+ \)\@<!([^()]*\w \d\+)\|\d\+ \)')
if empty(path) && lnum
let path = fugitive#Path(bufname(a:0 ? a:2 : s:BlameBufnr()), '')
endif
return [commit, path, lnum]
endfunction
function! s:BlameLeave() abort
let bufwinnr = bufwinnr(s:BlameBufnr())
if bufwinnr > 0
@ -4604,29 +4618,56 @@ function! s:BlameQuit() abort
endif
endfunction
function! s:BlameComplete(A, L, P) abort
return s:CompleteSub('blame', a:A, a:L, a:P, [])
endfunction
function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
if exists('b:fugitive_blame_arguments')
if has_key(s:TempState(), 'blame_flags')
return substitute(s:BlameLeave(), '^$', 'bdelete', '')
endif
exe s:DirCheck()
let flags = copy(a:args)
let i = 0
while i < len(flags)
let match = matchlist(flags[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
if len(match) && len(match[2])
call insert(flags, match[1])
let flags[i+1] = '-' . match[2]
continue
endif
let arg = flags[i]
if arg =~# '^-[Lp]$\|^--\%(help\|porcelain\|line-porcelain\|incremental\|contents\)$'
return 'echoerr ' . string('fugitive: blame ' . arg . ' unsupported')
elseif arg =~# '^-[GLS]$\|^--\%(date\|encoding\)$'
let i += 1
if i == len(flags)
echohl ErrorMsg
echo s:ChompError(['blame', arg])[0]
echohl NONE
return ''
endif
elseif arg !~# '^-'
return 'echoerr ' . string("fugitive: '-' required for all blame options")
elseif arg ==# '--'
call remove(flags, i)
continue
endif
let i += 1
endwhile
try
if empty(s:Relative('/'))
call s:throw('file or blob required')
endif
let commit = matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$')
if filter(copy(a:args),'v:val !~# "^-"') != []
call s:throw("'-' required for all options")
elseif filter(copy(a:args),'v:val !~# "^\\%(--abbrev=\\d*\\|--relative-date\\|--first-parent\\|--root\\|--show-name' . (len(commit) ? '\\|--reverse' : '') . '\\|-\\%([ltfnsew]\\|[MC]\\d*\\)\\+\\)$"') != []
call s:throw('unsupported option' . commit)
endif
let cmd = ['--no-pager', '-c', 'blame.coloring=none', '-c', 'blame.blankBoundary=false', 'blame', '--show-number']
call extend(cmd, filter(copy(flags), 'v:val !~# "\\v^%(-b|--%(no-)=color-.*|--progress)$"'))
if a:count > 0
let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))]
endif
let cmd += a:args
if len(commit)
let cmd += [commit]
else
elseif !s:HasOpt(flags, '--reverse')
let cmd += ['--contents', '-']
endif
let cmd += ['--', expand('%:p')]
@ -4638,16 +4679,34 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args)
else
silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
endif
redraw
try
if v:shell_error
call s:throw(join(readfile(error),"\n"))
let lines = readfile(error)
if empty(lines)
let lines = readfile(temp)
endif
for i in range(len(lines))
if lines[i] =~# '^error: \|^fatal: '
echohl ErrorMsg
echon lines[i]
echohl NONE
break
else
echon lines[i]
endif
if i != len(lines) - 1
echon "\n"
endif
endfor
return ''
endif
if a:count > 0
let edit = s:Mods(a:mods) . get(['edit', 'split', 'pedit', 'vsplit', 'tabedit'], a:count - (a:line1 ? a:line1 : 1), 'split')
return s:BlameCommit(edit, get(readfile(temp), 0, ''), bufnr(''))
else
let temp = s:Resolve(temp)
let s:temp_files[s:cpath(temp)] = {'dir': s:Dir(), 'filetype': 'fugitiveblame', 'args': cmd, 'modifiable': 0}
let s:temp_files[s:cpath(temp)] = {'dir': s:Dir(), 'filetype': 'fugitiveblame', 'blame_flags': flags, 'modifiable': 0}
for winnr in range(winnr('$'),1,-1)
if getwinvar(winnr, '&scrollbind')
call setwinvar(winnr, '&scrollbind', 0)
@ -4679,7 +4738,6 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args)
let current = line('.')
exe 'keepalt' (a:bang ? 'split' : 'leftabove vsplit') s:fnameescape(temp)
let w:fugitive_leave = restore
let b:fugitive_blame_arguments = join(a:args,' ')
execute top
normal! zt
execute current
@ -4705,9 +4763,9 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args)
nnoremap <buffer> <silent> P :<C-U>exe <SID>BlameJump('^'.v:count1)<CR>
nnoremap <buffer> <silent> ~ :<C-U>exe <SID>BlameJump('~'.v:count1)<CR>
nnoremap <buffer> <silent> i :<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>
nnoremap <buffer> <silent> o :<C-U>exe <SID>BlameCommit((&splitbelow ? "botright" : "topleft")." split")<CR>
nnoremap <buffer> <silent> o :<C-U>exe <SID>BlameCommit("split")<CR>
nnoremap <buffer> <silent> O :<C-U>exe <SID>BlameCommit("tabedit")<CR>
nnoremap <buffer> <silent> p :<C-U>exe <SID>Open((&splitbelow ? "botright" : "topleft").' pedit', 0, '', matchstr(getline('.'), '\x\+'), [matchstr(getline('.'), '\x\+')])<CR>
nnoremap <buffer> <silent> p :<C-U>exe <SID>BlameCommit("pedit")<CR>
nnoremap <buffer> <silent> A :<C-u>exe "vertical resize ".(<SID>linechars('.\{-\}\ze [0-9:/+-][0-9:/+ -]* \d\+)')+1+v:count)<CR>
nnoremap <buffer> <silent> C :<C-u>exe "vertical resize ".(<SID>linechars('^\S\+')+1+v:count)<CR>
nnoremap <buffer> <silent> D :<C-u>exe "vertical resize ".(<SID>linechars('.\{-\}\ze\d\ze\s\+\d\+)')+1-v:count)<CR>
@ -4722,24 +4780,16 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args)
endfunction
function! s:BlameCommit(cmd, ...) abort
let line = a:0 ? a:1 : getline('.')
if line =~# '^0\{4,\} '
return 'echoerr ' . string('Not Committed Yet')
let [commit, path, lnum] = call('s:BlameCommitFileLnum', a:000)
if commit =~# '^0*$'
return 'echoerr ' . string('fugitive: no commit')
endif
let cmd = s:Open(a:cmd, 0, '', matchstr(line, '\x\+'), [matchstr(line, '\x\+')])
let cmd = s:Open((&splitbelow ? "botright " : "topleft ") . a:cmd, 0, '', commit, [commit])
if cmd =~# '^echoerr'
return cmd
endif
let lnum = matchstr(line, ' \zs\d\+\ze\s\+[([:digit:]]')
let path = matchstr(line, '^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ')
if empty(path)
let path = fugitive#Path(bufname(a:0 ? a:2 : s:BlameBufnr()), '')
endif
if empty(path)
return 'echoerr ' . string('fugitive: could not determine filename for blame')
endif
execute cmd
if a:cmd ==# 'pedit'
if a:cmd ==# 'pedit' || empty(path)
return ''
endif
if search('^diff .* b/\M'.escape(path,'\').'$','W')
@ -4773,25 +4823,18 @@ function! s:BlameCommit(cmd, ...) abort
endfunction
function! s:BlameJump(suffix) abort
let commit = matchstr(getline('.'),'^\^\=\zs\x\+')
let suffix = a:suffix
if commit =~# '^0\+$'
let commit = 'HEAD'
let suffix = ''
endif
let lnum = matchstr(getline('.'),' \zs\d\+\ze\s\+[([:digit:]]')
let path = matchstr(getline('.'),'^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ')
let original_bufnr = s:BlameBufnr()
if empty(path)
let path = fugitive#Path(bufname(original_bufnr), '')
endif
let [commit, path, lnum] = s:BlameCommitFileLnum()
if empty(path)
return 'echoerr ' . string('fugitive: could not determine filename for blame')
endif
let args = get(b:, 'fugitive_blame_arguments', '')
if commit =~# '^0*$'
let commit = 'HEAD'
let suffix = ''
endif
let offset = line('.') - line('w0')
let bufnr = bufnr('%')
let winnr = bufwinnr(original_bufnr)
let winnr = bufwinnr(s:BlameBufnr())
if winnr > 0
exe winnr.'wincmd w'
endif
@ -4801,7 +4844,8 @@ function! s:BlameJump(suffix) abort
exe bufnr.'bdelete'
endif
if exists(':Gblame')
execute 'Gblame '.args
let flags = get(s:TempState(), 'blame_flags', [])
execute 'Gblame ' . s:fnameescape(flags)
execute lnum
let delta = line('.') - line('w0') - offset
if delta > 0
@ -4819,10 +4863,11 @@ let s:hash_colors = {}
function! fugitive#BlameSyntax() abort
let conceal = has('conceal') ? ' conceal' : ''
let config = fugitive#Config()
let flags = get(s:TempState(), 'blame_flags', [])
syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
syn match FugitiveblameHash "\%(^\^\=\)\@<=\<\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=\<0\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
if get(get(config, 'blame.blankboundary', ['x']), 0, 'x') =~# '^$\|^true$'
if get(get(config, 'blame.blankboundary', ['x']), 0, 'x') =~# '^$\|^true$' || s:HasOpt(flags, '-b')
syn match FugitiveblameBoundaryIgnore "^\^\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
else
syn match FugitiveblameBoundary "^\^"
@ -4831,9 +4876,9 @@ function! fugitive#BlameSyntax() abort
syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%(\s\d\+\)\@<=)" contained keepend oneline
syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%(\s\+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation
exec 'syn match FugitiveblameLineNumber "\s*\d\+)\@=" contained containedin=FugitiveblameAnnotation' conceal
exec 'syn match FugitiveblameOriginalFile "\s\%(\f\+\D\@<=\|\D\@=\f\+\)\%(\%(\s\+\d\+\)\=\s\%((\|\s*\d\+)\)\)\@=" contained nextgroup=FugitiveblameOriginalLineNumber,FugitiveblameAnnotation skipwhite' conceal
exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite' conceal
exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite' conceal
exec 'syn match FugitiveblameOriginalFile "\s\%(\f\+\D\@<=\|\D\@=\f\+\)\%(\%(\s\+\d\+\)\=\s\%((\|\s*\d\+)\)\)\@=" contained nextgroup=FugitiveblameOriginalLineNumber,FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-name', '-f') ? '' : conceal)
exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
syn match FugitiveblameShort " \d\+)" contained contains=FugitiveblameLineNumber
syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
hi def link FugitiveblameBoundary Keyword
@ -4848,7 +4893,7 @@ function! fugitive#BlameSyntax() abort
hi def link FugitiveblameShort FugitiveblameDelimiter
hi def link FugitiveblameDelimiter Delimiter
hi def link FugitiveblameNotCommittedYet Comment
if !get(g:, 'fugitive_dynamic_colors', 1)
if !get(g:, 'fugitive_dynamic_colors', 1) && !s:HasOpt(flags, '--color-lines') || s:HasOpt(flags, '--no-color-lines')
return
endif
let seen = {}

View File

@ -193,11 +193,10 @@ that are part of Git repositories).
:Gremove Like :Gdelete, but keep the (now empty) buffer around.
*fugitive-:Gblame*
:Gblame [flags] Run git-blame on the file and open the results in a
scroll-bound vertical split. You can give any of
-ltsewMC as flags, and they will be passed along to
git-blame. The following maps, which work on the
cursor line commit where sensible, are provided:
:Gblame [flags] Run git-blame [flags] on the current file and open the
results in a scroll-bound vertical split. The
following maps, which work on the cursor line commit
where sensible, are provided:
g? show this help
A resize to end of author column