From 0392f64a93aed99cd14092e93a1162b9e59c6fc1 Mon Sep 17 00:00:00 2001 From: Tim Pope Date: Sun, 11 Aug 2019 17:01:24 -0400 Subject: [PATCH] Make :Gblame a proper subcommand --- autoload/fugitive.vim | 156 ++++++++++++++++++++++++++++++------------ doc/fugitive.txt | 5 ++ 2 files changed, 116 insertions(+), 45 deletions(-) diff --git a/autoload/fugitive.vim b/autoload/fugitive.vim index 6532747..9f1f83b 100644 --- a/autoload/fugitive.vim +++ b/autoload/fugitive.vim @@ -4598,7 +4598,7 @@ function! s:BlameCommitFileLnum(...) abort let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)') let path = matchstr(line, '^\^\=\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s\+\%(\%( \d\+ \)\@ 0 && a:count > 0 && a:range != 1 + call extend(ranges, ['-L', a:line1 . ',' . a:count]) + endif while i < len(flags) let match = matchlist(flags[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)') if len(match) && len(match[2]) @@ -4643,9 +4647,21 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args) 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\)$' + if arg =~# '^-p$\|^--\%(help\|porcelain\|line-porcelain\|incremental\)$' + let raw = 1 + elseif arg ==# '--contents' && i + 1 < len(flags) + call extend(commits, remove(flags, i, i+1)) + continue + elseif arg ==# '-L' && i + 1 < len(flags) + call extend(ranges, remove(flags, i, i+1)) + continue + elseif arg =~# '^--contents=' + call add(commits, remove(flags, i)) + continue + elseif arg =~# '^-L.' + call add(ranges, remove(flags, i)) + continue + elseif arg =~# '^-[GLS]$\|^--\%(date\|encoding\|contents\)$' let i += 1 if i == len(flags) echohl ErrorMsg @@ -4654,32 +4670,58 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args) return '' endif elseif arg !~# '^-' - return 'echoerr ' . string("fugitive: '-' required for all blame options") - elseif arg ==# '--' - call remove(flags, i) + if index(flags, '--') >= 0 + call add(commits, remove(flags, i)) + continue + endif + if s:HasOpt(flags, '--reverse') && arg =~# '\.\.' && empty(commits) + call add(commits, remove(flags, i)) + continue + endif + try + let dcf = s:DirCommitFile(fugitive#Find(arg)) + if len(dcf[1]) && empty(dcf[2]) + call add(commits, remove(flags, i)) + continue + endif + catch /^fugitive:/ + endtry + call add(files, remove(flags, i)) continue + elseif arg ==# '--' + if i + 1 < len(flags) + call extend(files, remove(flags, i + 1, -1)) + endif + call remove(flags, i) + break endif let i += 1 endwhile + if empty(ranges + commits + files) && has_key(s:TempState(), 'blame_flags') + return substitute(s:BlameLeave(), '^$', 'bdelete', '') + endif + let file = substitute(get(files, 0, get(s:TempState(), 'blame_file', s:Relative('./'))), '^\.\%(/\|$\)', '', '') + if empty(commits) && len(files) > 1 + call add(commits, remove(files, 1)) + endif try - if empty(s:Relative('/')) - call s:throw('file or blob required') - endif - let commit = matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$') 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 && empty(ranges) let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))] endif - if len(commit) - let cmd += [commit] - elseif !s:HasOpt(flags, '--reverse') + call extend(cmd, ranges) + if len(commits) + let cmd += commits + elseif empty(files) && len(matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$')) + let cmd += [matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$')] + elseif empty(files) && !s:HasOpt(flags, '--reverse') let cmd += ['--contents', '-'] endif - let cmd += ['--', expand('%:p')] - let basecmd = escape(fugitive#Prepare(cmd), '!#%') - let error = tempname() - let temp = error.'.fugitiveblame' + let basecmd = escape(fugitive#Prepare(cmd) . ' -- ' . s:shellesc(len(files) ? files : file), '!#%') + let tempname = tempname() + let error = tempname . '.err' + let temp = tempname . (raw ? '' : '.fugitiveblame') if &shell =~# 'csh' silent! execute '%write !('.basecmd.' > '.temp.') >& '.error else @@ -4707,12 +4749,22 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args) endfor return '' endif - if a:count > 0 + if (a:line1 == 0 || a:range == 1) && 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('')) + return s:BlameCommit(edit, get(readfile(temp), 0, ''), file) else let temp = s:Resolve(temp) - let s:temp_files[s:cpath(temp)] = {'dir': s:Dir(), 'filetype': 'fugitiveblame', 'blame_flags': flags, 'modifiable': 0} + let s:temp_files[s:cpath(temp)] = {'dir': s:Dir(), 'filetype': (raw ? '' : 'fugitiveblame'), 'blame_flags': flags, 'blame_file': file, 'modifiable': 0} + if len(ranges + commits + files) || raw + if a:count != 0 + exe 'silent keepalt split ' . s:fnameescape(temp) + elseif !&modified || a:bang || &bufhidden ==# 'hide' || (empty(&bufhidden) && &hidden) + exe 'silent edit' . (a:bang ? '! ' : ' ') . s:fnameescape(temp) + else + return 'edit ' . s:fnameescape(temp) + endif + return '' + endif for winnr in range(winnr('$'),1,-1) if getwinvar(winnr, '&scrollbind') call setwinvar(winnr, '&scrollbind', 0) @@ -4742,7 +4794,7 @@ function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args) endif let top = line('w0') + &scrolloff let current = line('.') - exe 'keepalt' (a:bang ? 'split' : 'leftabove vsplit') s:fnameescape(temp) + exe 'silent keepalt' (a:bang ? 'split' : 'leftabove vsplit') s:fnameescape(temp) let w:fugitive_leave = restore execute top normal! zt @@ -4773,7 +4825,7 @@ function! s:BlameCommit(cmd, ...) abort if commit =~# '^0*$' return 'echoerr ' . string('fugitive: no commit') endif - let cmd = s:Open((&splitbelow ? "botright " : "topleft ") . a:cmd, 0, '', commit, [commit]) + let cmd = s:Open((s:BlameBufnr() < 0 ? '' : &splitbelow ? "botright " : "topleft ") . a:cmd, 0, '', commit, [commit]) if cmd =~# '^echoerr' return cmd endif @@ -4822,19 +4874,33 @@ function! s:BlameJump(suffix) abort let suffix = '' endif let offset = line('.') - line('w0') - let bufnr = bufnr('%') - let winnr = bufwinnr(s:BlameBufnr()) - if winnr > 0 - exe winnr.'wincmd w' - endif - execute 'Gedit' s:fnameescape(commit . suffix . ':' . path) - execute lnum - if winnr > 0 - exe bufnr.'bdelete' + let flags = get(s:TempState(), 'blame_flags', []) + let blame_bufnr = s:BlameBufnr() + if blame_bufnr > 0 + let bufnr = bufnr('') + let winnr = bufwinnr(blame_bufnr) + if winnr > 0 + exe winnr.'wincmd w' + endif + execute 'Gedit' s:fnameescape(commit . suffix . ':' . path) + execute lnum + if winnr > 0 + exe bufnr.'bdelete' + endif endif if exists(':Gblame') - let flags = get(s:TempState(), 'blame_flags', []) - execute 'Gblame ' . s:fnameescape(flags) + let my_bufnr = bufnr('') + if blame_bufnr < 0 + let blame_args = flags + [commit . suffix, '--', path] + let result = s:BlameSubcommand(0, 0, 0, 0, '', blame_args) + else + let blame_args = flags + let result = s:BlameSubcommand(-1, -1, 0, 0, '', blame_args) + endif + if bufnr('') == my_bufnr + return result + endif + execute result execute lnum let delta = line('.') - line('w0') - offset if delta > 0 @@ -4842,7 +4908,9 @@ function! s:BlameJump(suffix) abort elseif delta < 0 execute 'normal! '.(-delta)."\" endif - syncbind + keepjumps syncbind + redraw + echo ':Gblame' s:fnameescape(blame_args) endif return '' endfunction @@ -4951,14 +5019,12 @@ endfunction augroup fugitive_blame autocmd! autocmd FileType fugitiveblame call s:BlameFileType() - autocmd User Fugitive - \ if get(b:, 'fugitive_type') =~# '^\%(file\|blob\)$' || s:BlameBufnr() > 0 || filereadable(@%) | - \ exe "command! -buffer -bar -bang -range=-1 -nargs=* -complete=customlist,s:BlameComplete Gblame :execute s:BlameCommand(,,+'',,0,'',,,[])" | - \ endif autocmd ColorScheme,GUIEnter * call s:BlameRehighlight() autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('')), 'fugitive_leave') augroup END +call s:command('-buffer -bang -range=-1 -nargs=? -complete=customlist,s:BlameComplete Gblame', 'blame') + " Section: :Gbrowse call s:command("-bar -bang -range=-1 -nargs=* -complete=customlist,fugitive#CompleteObject Gbrowse", "Browse") diff --git a/doc/fugitive.txt b/doc/fugitive.txt index ee1d977..2b23b6d 100644 --- a/doc/fugitive.txt +++ b/doc/fugitive.txt @@ -211,6 +211,11 @@ that are part of Git repositories). ~ reblame at [count]th first grandparent P reblame at [count]th parent (like HEAD^[count]) +:[range]Gblame [flags] If a range is given, just that part of the file will +:Gblame [flags] {file} be blamed, and a horizontal split without + scrollbinding is used. You can also give an arbitrary + filename. + *fugitive-:Gbrowse* :Gbrowse Open the current file, blob, tree, commit, or tag in your browser at the upstream hosting provider.