Support expansion flags

This commit is contained in:
Tim Pope 2018-08-28 00:35:06 -04:00
parent 1e3786734b
commit fd83fcaf90
2 changed files with 69 additions and 27 deletions

View File

@ -53,11 +53,12 @@ function! s:shellesc(arg) abort
endif endif
endfunction endfunction
let s:fnameescape = " \t\n*?[{`$\\%#'\"|!<"
function! s:fnameescape(file) abort function! s:fnameescape(file) abort
if exists('*fnameescape') if exists('*fnameescape')
return fnameescape(a:file) return fnameescape(a:file)
else else
return escape(a:file," \t\n*?[{`$\\%#'\"|!<") return escape(a:file, s:fnameescape)
endif endif
endfunction endfunction
@ -641,18 +642,13 @@ function! s:Generate(rev, ...) abort
return fugitive#Route(object, dir) return fugitive#Route(object, dir)
endfunction endfunction
function! s:RemoveDot(path, ...) abort function! s:DotRelative(path) abort
if a:path !~# '^\./' let cwd = getcwd()
return a:path let path = substitute(a:path, '^[~$]\i*', '\=expand(submatch(0))', '')
if s:cpath(cwd . '/', (path . '/')[0 : len(cwd)])
return '.' . strpart(path, len(cwd))
endif endif
let dir = a:0 ? a:1 : get(b:, 'git_dir', '') return a:path
let cdir = fugitive#CommonDir(dir)
if len(filter(['', '/tags', '/heads', '/remotes'], 'getftime(cdir . "/refs" . v:val . a:path[1:-1]) >= 0')) ||
\ a:path =~# 'HEAD$' && filereadable(dir . a:path[1:-1]) ||
\ a:path =~# '^\./refs/' && filereadable(cdir . a:path[1:-1])
return a:path
endif
return a:path[2:-1]
endfunction endfunction
function! fugitive#Object(...) abort function! fugitive#Object(...) abort
@ -681,6 +677,44 @@ function! fugitive#Object(...) abort
endif endif
endfunction endfunction
let s:var = '\%(%\|#<\=\d\+\|##\=\)'
let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)'
let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)'
function! s:BufName(var) abort
if a:var ==# '%'
return bufname(get(b:, 'fugitive_blamed_bufnr', ''))
elseif a:var =~# '^#\d*$'
let nr = getbufvar(+a:var[1:-1], 'fugitive_blamed_bufnr', '')
return bufname(nr ? nr : +a:var[1:-1])
else
return expand(a:var)
endif
endfunction
function! s:ExpandVar(other, var, flags, esc) abort
if a:other =~# '^\'
return a:other[1:-1]
elseif a:other =~# '^!'
let buffer = s:BufName(len(a:other) > 1 ? '#'. a:other[1:-1] : '%')
let owner = s:Owner(buffer)
return len(owner) ? owner : '@'
endif
let flags = a:flags
let file = s:DotRelative(fugitive#Real(s:BufName(a:var)))
while len(flags)
let flag = matchstr(flags, s:flag)
let flags = strpart(flags, len(flag))
if flag ==# ':.'
let file = s:DotRelative(file)
else
let file = fnamemodify(file, flag)
endif
endwhile
let file = s:Slash(file)
return (len(a:esc) ? shellescape(file) : file)
endfunction
function! s:Expand(rev) abort function! s:Expand(rev) abort
if a:rev =~# '^:[0-3]$' if a:rev =~# '^:[0-3]$'
let file = a:rev . s:Relative(':') let file = a:rev . s:Relative(':')
@ -688,19 +722,26 @@ function! s:Expand(rev) abort
let file = 'HEAD^{}' . a:rev[1:-1] . s:Relative(':') let file = 'HEAD^{}' . a:rev[1:-1] . s:Relative(':')
elseif a:rev =~# '^@{' elseif a:rev =~# '^@{'
let file = 'HEAD' . a:rev. s:Relative(':') let file = 'HEAD' . a:rev. s:Relative(':')
elseif a:rev =~# '^[~^]/\@!' elseif a:rev =~# '^\^[0-9~^{]\|^\~[0-9~^]'
let commit = substitute(s:DirCommitFile(@%)[1], '^\d\=$', 'HEAD', '') let commit = substitute(s:DirCommitFile(@%)[1], '^\d\=$', 'HEAD', '')
let file = commit . a:rev . s:Relative(':') let file = commit . a:rev . s:Relative(':')
else else
let file = a:rev let file = a:rev
endif endif
return s:sub(substitute(file, return substitute(file,
\ '\([%#]\)$\|\\\([[:punct:]]\)','\=len(submatch(2)) ? submatch(2) : fugitive#Path(expand(submatch(1)))','g'), \ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
\ '\.\@<=/$','') \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),"")', 'g')
endfunction
function! fugitive#Expand(object) abort
return substitute(a:object,
\ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5))', 'g')
endfunction endfunction
function! s:ShellExpand(cmd) abort function! s:ShellExpand(cmd) abort
return substitute(a:cmd, '\\\@<![%#]:\@!', '\=s:RemoveDot(fugitive#Path(expand(submatch(0)), "./"))', 'g') return substitute(a:cmd, '\(\\[!#%]\|!\d*\)\|' . s:expand,
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5))', 'g')
endfunction endfunction
let s:trees = {} let s:trees = {}

View File

@ -312,14 +312,14 @@ a Show the current tag, commit, or tree in an alternate
SPECIFYING OBJECTS *fugitive-object* *fugitive-revision* SPECIFYING OBJECTS *fugitive-object* *fugitive-revision*
Fugitive objects are either work tree files or Git revisions as defined in the Fugitive objects are either work tree files or Git revisions as defined in the
"SPECIFYING REVISIONS" section in the git-rev-parse man page, with a few "SPECIFYING REVISIONS" section in the git-rev-parse man page, with expansions
convenience notations thrown in for good measure. For commands that accept an inspired by |cmdline-special| layered on top. For commands that accept an
optional object, the default is the file in the index for work tree files and optional object, the default is the file in the index for work tree files and
the work tree file for everything else. Example objects follow. the work tree file for everything else. Example objects follow.
Object Meaning ~ Object Meaning ~
HEAD .git/HEAD HEAD .git/HEAD
refs/heads/x .git/refs/heads/x refs/heads/x .git/refs/heads/x (in "common dir" if present)
@ The commit referenced by @ aka HEAD @ The commit referenced by @ aka HEAD
master^ The parent of the commit referenced by master master^ The parent of the commit referenced by master
master: The tree referenced by master master: The tree referenced by master
@ -327,15 +327,16 @@ master: The tree referenced by master
Makefile The file named Makefile in the work tree Makefile The file named Makefile in the work tree
@^:Makefile The file named Makefile in the parent of HEAD @^:Makefile The file named Makefile in the parent of HEAD
:Makefile The file named Makefile in the index (writable) :Makefile The file named Makefile in the index (writable)
@:% The current file in HEAD @~2:% The current file in the grandparent of HEAD
- The current file in HEAD
-^ The current file in the previous commit
-~3 The current file 3 commits ago
: .git/index (Same as |:Gstatus|)
:% The current file in the index :% The current file in the index
:1:% The current file's common ancestor during a conflict :1:% The current file's common ancestor during a conflict
:2:% The current file in the target branch during a conflict :2:# The alternate file in the target branch during a conflict
:3:% The current file in the merged branch during a conflict :3:#5 The file from buffer #5 in the merged branch during a conflict
! The commit owning the current file
!:Makefile The file named Makefile in the commit owning the current file
!3^2 The second parent of the commit owning buffer #3
.git/config The repo config file
: Same as |:Gstatus|
STATUSLINE *fugitive-statusline* STATUSLINE *fugitive-statusline*