Browse handler API

Taking experimental out of the name, but small tweaks may occur before
then next release.

For future compatibility, any third party handlers should bail and
return an empty string if any of the following are true:

* More than 2 arguments are given.
* The second argument isn't a dictionary.
* The dictionary doesn't contain a "remote" key.

Closes #445.
This commit is contained in:
Tim Pope 2014-07-22 00:17:43 -04:00
parent 5d1c219ee5
commit 5aaa65736d

View File

@ -2175,8 +2175,15 @@ function! s:Browse(bang,line1,count,...) abort
let raw = remote let raw = remote
endif endif
for Handler in g:fugitive_experimental_browse_handlers for Handler in g:fugitive_browse_handlers
let url = call(Handler, [s:repo(),raw,rev,commit,path,type,a:line1,a:count]) let url = call(Handler, [s:repo(), {
\ 'remote': raw,
\ 'revision': rev,
\ 'commit': commit,
\ 'path': path,
\ 'type': type,
\ 'line1': a:line1,
\ 'line2': a:count}])
if !empty(url) if !empty(url)
break break
endif endif
@ -2199,17 +2206,20 @@ function! s:Browse(bang,line1,count,...) abort
endtry endtry
endfunction endfunction
function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort function! s:github_url(repo, opts, ...) abort
let path = a:path if a:0 || type(a:opts) != type({})
return ''
endif
let domain_pattern = 'github\.com' let domain_pattern = 'github\.com'
let domains = exists('g:fugitive_github_domains') ? g:fugitive_github_domains : [] let domains = exists('g:fugitive_github_domains') ? g:fugitive_github_domains : []
for domain in domains for domain in domains
let domain_pattern .= '\|' . escape(split(domain, '://')[-1], '.') let domain_pattern .= '\|' . escape(split(domain, '://')[-1], '.')
endfor endfor
let repo = matchstr(a:url,'^\%(https\=://\|git://\|git@\)\=\zs\('.domain_pattern.'\)[/:].\{-\}\ze\%(\.git\)\=$') let repo = matchstr(get(a:opts, 'remote'), '^\%(https\=://\|git://\|git@\)\=\zs\('.domain_pattern.'\)[/:].\{-\}\ze\%(\.git\)\=$')
if repo ==# '' if repo ==# ''
return '' return ''
endif endif
let path = a:opts.path
if index(domains, 'http://' . matchstr(repo, '^[^:/]*')) >= 0 if index(domains, 'http://' . matchstr(repo, '^[^:/]*')) >= 0
let root = 'http://' . s:sub(repo,':','/') let root = 'http://' . s:sub(repo,':','/')
else else
@ -2229,27 +2239,27 @@ function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort
elseif path =~# '^\.git\>' elseif path =~# '^\.git\>'
return root return root
endif endif
if a:rev =~# '^[[:alnum:]._-]\+:' if a:opts.revision =~# '^[[:alnum:]._-]\+:'
let commit = matchstr(a:rev,'^[^:]*') let commit = matchstr(a:opts.revision,'^[^:]*')
elseif a:commit =~# '^\d\=$' elseif a:opts.commit =~# '^\d\=$'
let local = matchstr(a:repo.head_ref(),'\<refs/heads/\zs.*') let local = matchstr(a:repo.head_ref(),'\<refs/heads/\zs.*')
let commit = a:repo.git_chomp('config','branch.'.local.'.merge')[11:-1] let commit = a:repo.git_chomp('config','branch.'.local.'.merge')[11:-1]
if commit ==# '' if commit ==# ''
let commit = local let commit = local
endif endif
else else
let commit = a:commit let commit = a:opts.commit
endif endif
if a:type == 'tree' if a:opts.type == 'tree'
let url = s:sub(root . '/tree/' . commit . '/' . path,'/$','') let url = s:sub(root . '/tree/' . commit . '/' . path,'/$','')
elseif a:type == 'blob' elseif a:opts.type == 'blob'
let url = root . '/blob/' . commit . '/' . path let url = root . '/blob/' . commit . '/' . path
if a:line2 > 0 && a:line1 == a:line2 if get(a:opts, 'line2') && a:opts.line1 == a:opts.line2
let url .= '#L' . a:line1 let url .= '#L' . a:opts.line1
elseif a:line2 > 0 elseif get(a:opts, 'line2')
let url .= '#L' . a:line1 . '-' . a:line2 let url .= '#L' . a:opts.line1 . '-' . a:opts.line2
endif endif
elseif a:type == 'tag' elseif a:opts.type == 'tag'
let commit = matchstr(getline(3),'^tag \zs.*') let commit = matchstr(getline(3),'^tag \zs.*')
let url = root . '/tree/' . commit let url = root . '/tree/' . commit
else else
@ -2258,52 +2268,52 @@ function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort
return url return url
endfunction endfunction
function! s:instaweb_url(repo,rev,commit,path,type,...) abort function! s:instaweb_url(repo, opts) abort
let output = a:repo.git_chomp('instaweb','-b','unknown') let output = a:repo.git_chomp('instaweb','-b','unknown')
if output =~# 'http://' if output =~# 'http://'
let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:repo.dir(),':t') let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:repo.dir(),':t')
else else
return '' return ''
endif endif
if a:path =~# '^\.git/refs/.' if a:opts.path =~# '^\.git/refs/.'
return root . ';a=shortlog;h=' . matchstr(a:path,'^\.git/\zs.*') return root . ';a=shortlog;h=' . matchstr(a:opts.path,'^\.git/\zs.*')
elseif a:path =~# '^\.git\>' elseif a:opts.path =~# '^\.git\>'
return root return root
endif endif
let url = root let url = root
if a:commit =~# '^\x\{40\}$' if a:opts.commit =~# '^\x\{40\}$'
if a:type ==# 'commit' if a:opts.type ==# 'commit'
let url .= ';a=commit' let url .= ';a=commit'
endif endif
let url .= ';h=' . a:repo.rev_parse(a:commit . (a:path == '' ? '' : ':' . a:path)) let url .= ';h=' . a:repo.rev_parse(a:opts.commit . (a:opts.path == '' ? '' : ':' . a:opts.path))
else else
if a:type ==# 'blob' if a:opts.type ==# 'blob'
let tmp = tempname() let tmp = tempname()
silent execute 'write !'.a:repo.git_command('hash-object','-w','--stdin').' > '.tmp silent execute 'write !'.a:repo.git_command('hash-object','-w','--stdin').' > '.tmp
let url .= ';h=' . readfile(tmp)[0] let url .= ';h=' . readfile(tmp)[0]
else else
try try
let url .= ';h=' . a:repo.rev_parse((a:commit == '' ? 'HEAD' : ':' . a:commit) . ':' . a:path) let url .= ';h=' . a:repo.rev_parse((a:opts.commit == '' ? 'HEAD' : ':' . a:opts.commit) . ':' . a:opts.path)
catch /^fugitive:/ catch /^fugitive:/
call s:throw('fugitive: cannot browse uncommitted file') call s:throw('fugitive: cannot browse uncommitted file')
endtry endtry
endif endif
let root .= ';hb=' . matchstr(a:repo.head_ref(),'[^ ]\+$') let root .= ';hb=' . matchstr(a:repo.head_ref(),'[^ ]\+$')
endif endif
if a:path !=# '' if a:opts.path !=# ''
let url .= ';f=' . a:path let url .= ';f=' . a:opts.path
endif endif
if a:0 && a:1 if get(a:opts, 'line1')
let url .= '#l' . a:1 let url .= '#l' . a:opts.line1
endif endif
return url return url
endfunction endfunction
if !exists('g:fugitive_experimental_browse_handlers') if !exists('g:fugitive_browse_handlers')
let g:fugitive_experimental_browse_handlers = [] let g:fugitive_browse_handlers = []
endif endif
call extend(g:fugitive_experimental_browse_handlers, call extend(g:fugitive_browse_handlers,
\ [s:function('s:github_url'), s:function('s:instaweb_url')]) \ [s:function('s:github_url'), s:function('s:instaweb_url')])
" Section: File access " Section: File access