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 FileType fugitiveblame setlocal nomodeline | if len(s:Dir()) | let &l:keywordprg = s:Keywordprg() | endif
autocmd User Fugitive autocmd User Fugitive
\ if get(b:, 'fugitive_type') =~# '^\%(file\|blob\)$' || s:BlameBufnr() > 0 || filereadable(@%) | \ 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 \ endif
autocmd ColorScheme,GUIEnter * call s:RehighlightBlame() autocmd ColorScheme,GUIEnter * call s:RehighlightBlame()
autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave') autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
@ -4583,6 +4583,20 @@ function! s:BlameBufnr(...) abort
endif endif
endfunction 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 function! s:BlameLeave() abort
let bufwinnr = bufwinnr(s:BlameBufnr()) let bufwinnr = bufwinnr(s:BlameBufnr())
if bufwinnr > 0 if bufwinnr > 0
@ -4604,29 +4618,56 @@ function! s:BlameQuit() abort
endif endif
endfunction 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 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', '') return substitute(s:BlameLeave(), '^$', 'bdelete', '')
endif endif
exe s:DirCheck() 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 try
if empty(s:Relative('/')) if empty(s:Relative('/'))
call s:throw('file or blob required') call s:throw('file or blob required')
endif endif
let commit = matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$') 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'] 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 if a:count > 0
let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))] let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))]
endif endif
let cmd += a:args
if len(commit) if len(commit)
let cmd += [commit] let cmd += [commit]
else elseif !s:HasOpt(flags, '--reverse')
let cmd += ['--contents', '-'] let cmd += ['--contents', '-']
endif endif
let cmd += ['--', expand('%:p')] let cmd += ['--', expand('%:p')]
@ -4638,16 +4679,34 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args)
else else
silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
endif endif
redraw
try try
if v:shell_error 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 endif
if a:count > 0 if a:count > 0
let edit = s:Mods(a:mods) . get(['edit', 'split', 'pedit', 'vsplit', 'tabedit'], a:count - (a:line1 ? a:line1 : 1), 'split') 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('')) return s:BlameCommit(edit, get(readfile(temp), 0, ''), bufnr(''))
else else
let temp = s:Resolve(temp) 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) for winnr in range(winnr('$'),1,-1)
if getwinvar(winnr, '&scrollbind') if getwinvar(winnr, '&scrollbind')
call setwinvar(winnr, '&scrollbind', 0) call setwinvar(winnr, '&scrollbind', 0)
@ -4679,7 +4738,6 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args)
let current = line('.') let current = line('.')
exe 'keepalt' (a:bang ? 'split' : 'leftabove vsplit') s:fnameescape(temp) exe 'keepalt' (a:bang ? 'split' : 'leftabove vsplit') s:fnameescape(temp)
let w:fugitive_leave = restore let w:fugitive_leave = restore
let b:fugitive_blame_arguments = join(a:args,' ')
execute top execute top
normal! zt normal! zt
execute current 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> P :<C-U>exe <SID>BlameJump('^'.v:count1)<CR>
nnoremap <buffer> <silent> ~ :<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> 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> 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> 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> 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> 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 endfunction
function! s:BlameCommit(cmd, ...) abort function! s:BlameCommit(cmd, ...) abort
let line = a:0 ? a:1 : getline('.') let [commit, path, lnum] = call('s:BlameCommitFileLnum', a:000)
if line =~# '^0\{4,\} ' if commit =~# '^0*$'
return 'echoerr ' . string('Not Committed Yet') return 'echoerr ' . string('fugitive: no commit')
endif 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' if cmd =~# '^echoerr'
return cmd return cmd
endif 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 execute cmd
if a:cmd ==# 'pedit' if a:cmd ==# 'pedit' || empty(path)
return '' return ''
endif endif
if search('^diff .* b/\M'.escape(path,'\').'$','W') if search('^diff .* b/\M'.escape(path,'\').'$','W')
@ -4773,25 +4823,18 @@ function! s:BlameCommit(cmd, ...) abort
endfunction endfunction
function! s:BlameJump(suffix) abort function! s:BlameJump(suffix) abort
let commit = matchstr(getline('.'),'^\^\=\zs\x\+')
let suffix = a:suffix let suffix = a:suffix
if commit =~# '^0\+$' let [commit, path, lnum] = s:BlameCommitFileLnum()
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
if empty(path) if empty(path)
return 'echoerr ' . string('fugitive: could not determine filename for blame') return 'echoerr ' . string('fugitive: could not determine filename for blame')
endif endif
let args = get(b:, 'fugitive_blame_arguments', '') if commit =~# '^0*$'
let commit = 'HEAD'
let suffix = ''
endif
let offset = line('.') - line('w0') let offset = line('.') - line('w0')
let bufnr = bufnr('%') let bufnr = bufnr('%')
let winnr = bufwinnr(original_bufnr) let winnr = bufwinnr(s:BlameBufnr())
if winnr > 0 if winnr > 0
exe winnr.'wincmd w' exe winnr.'wincmd w'
endif endif
@ -4801,7 +4844,8 @@ function! s:BlameJump(suffix) abort
exe bufnr.'bdelete' exe bufnr.'bdelete'
endif endif
if exists(':Gblame') if exists(':Gblame')
execute 'Gblame '.args let flags = get(s:TempState(), 'blame_flags', [])
execute 'Gblame ' . s:fnameescape(flags)
execute lnum execute lnum
let delta = line('.') - line('w0') - offset let delta = line('.') - line('w0') - offset
if delta > 0 if delta > 0
@ -4819,10 +4863,11 @@ let s:hash_colors = {}
function! fugitive#BlameSyntax() abort function! fugitive#BlameSyntax() abort
let conceal = has('conceal') ? ' conceal' : '' let conceal = has('conceal') ? ' conceal' : ''
let config = fugitive#Config() let config = fugitive#Config()
let flags = get(s:TempState(), 'blame_flags', [])
syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
syn match FugitiveblameHash "\%(^\^\=\)\@<=\<\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite syn match FugitiveblameHash "\%(^\^\=\)\@<=\<\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=\<0\{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 syn match FugitiveblameBoundaryIgnore "^\^\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
else else
syn match FugitiveblameBoundary "^\^" syn match FugitiveblameBoundary "^\^"
@ -4831,9 +4876,9 @@ function! fugitive#BlameSyntax() abort
syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%(\s\d\+\)\@<=)" contained keepend oneline 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 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 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 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' 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' 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 FugitiveblameShort " \d\+)" contained contains=FugitiveblameLineNumber
syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
hi def link FugitiveblameBoundary Keyword hi def link FugitiveblameBoundary Keyword
@ -4848,7 +4893,7 @@ function! fugitive#BlameSyntax() abort
hi def link FugitiveblameShort FugitiveblameDelimiter hi def link FugitiveblameShort FugitiveblameDelimiter
hi def link FugitiveblameDelimiter Delimiter hi def link FugitiveblameDelimiter Delimiter
hi def link FugitiveblameNotCommittedYet Comment 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 return
endif endif
let seen = {} let seen = {}

View File

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