1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-10 13:45:48 +08:00
SpaceVim/bundle/gina.vim/autoload/gina/command/browse.vim

218 lines
7.5 KiB
VimL

let s:Formatter = vital#gina#import('Data.String.Formatter')
let s:Git = vital#gina#import('Git')
let s:Path = vital#gina#import('System.Filepath')
let s:SCHEME = gina#command#scheme(expand('<sfile>'))
let s:FORMAT_MAP = {
\ 'pt': 'path',
\ 'ls': 'line_start',
\ 'le': 'line_end',
\ 'c0': 'commit0',
\ 'c1': 'commit1',
\ 'c2': 'commit2',
\ 'h0': 'hash0',
\ 'h1': 'hash1',
\ 'h2': 'hash2',
\ 'r0': 'rev0',
\ 'r1': 'rev1',
\ 'r2': 'rev2',
\}
function! gina#command#browse#call(range, args, mods) abort
call gina#core#options#help_if_necessary(a:args, s:get_options())
call gina#process#register(s:SCHEME, 1)
try
call s:call(a:range, a:args, a:mods)
finally
call gina#process#unregister(s:SCHEME, 1)
endtry
endfunction
function! gina#command#browse#complete(arglead, cmdline, cursorpos) abort
let args = gina#core#args#new(matchstr(a:cmdline, '^.*\ze .*'))
if a:arglead[0] ==# '-' || !empty(args.get(1))
let options = s:get_options()
return options.complete(a:arglead, a:cmdline, a:cursorpos)
endif
return gina#complete#common#treeish(a:arglead, a:cmdline, a:cursorpos)
endfunction
" Private --------------------------------------------------------------------
function! s:get_options() abort
let options = gina#core#options#new()
call options.define(
\ '-h|--help',
\ 'Show this help.',
\)
call options.define(
\ '--scheme=',
\ 'Specify a URL scheme to open.',
\ ['_', 'root', 'blame', 'compare'],
\)
call options.define(
\ '--exact',
\ 'Use a sha1 instead of a branch name.',
\)
call options.define(
\ '--yank',
\ 'Yank a URL instead of opening.',
\)
return options
endfunction
function! s:build_args(git, args, range) abort
let args = a:args.clone()
let args.params.yank = args.pop('--yank')
let args.params.exact = args.pop('--exact')
let args.params.range = a:range == [1, line('$')] ? [] : a:range
let args.params.scheme = args.pop('--scheme', v:null)
call gina#core#args#extend_treeish(a:git, args, args.pop(1))
return args.lock()
endfunction
function! s:call(range, args, mods) abort
let git = gina#core#get_or_fail()
let args = s:build_args(git, a:args, a:range)
let rev = gina#util#get(args.params, 'rev')
let path = gina#util#get(args.params, 'path')
let revinfo = s:parse_rev(git, rev)
let base_url = s:build_base_url(
\ s:get_remote_url(git, revinfo.commit1, revinfo.commit2),
\ args.params.scheme is# v:null
\ ? empty(path) ? 'root' : '_'
\ : args.params.scheme,
\)
let url = s:Formatter.format(base_url, s:FORMAT_MAP, {
\ 'path': substitute(path, ' ', '%20', 'g'),
\ 'line_start': get(args.params.range, 0, ''),
\ 'line_end': get(args.params.range, 1, ''),
\ 'commit0': revinfo.commit0,
\ 'commit1': revinfo.commit1,
\ 'commit2': revinfo.commit2,
\ 'hash0': revinfo.hash0,
\ 'hash1': revinfo.hash1,
\ 'hash2': revinfo.hash2,
\ 'rev0': args.params.exact ? revinfo.hash0 : revinfo.commit0,
\ 'rev1': args.params.exact ? revinfo.hash1 : revinfo.commit1,
\ 'rev2': args.params.exact ? revinfo.hash2 : revinfo.commit2,
\})
if empty(url)
throw gina#core#revelator#warning(printf(
\ 'No url translation pattern for "%s" is found.',
\ rev,
\))
endif
if args.params.yank
call gina#util#yank(url)
else
call gina#util#open(url)
endif
call gina#core#emitter#emit('command:called', s:SCHEME)
endfunction
function! s:parse_rev(git, rev) abort
let [commit1, commit2] = gina#core#treeish#split(a:rev)
let commit0 = empty(a:rev) ? 'HEAD' : a:rev
let commit1 = empty(commit1) ? 'HEAD' : commit1
let commit2 = empty(commit2) ? 'HEAD' : commit2
let hash0 = gina#core#treeish#sha1(a:git, commit0)
let hash1 = gina#core#treeish#sha1(a:git, commit1)
let hash2 = gina#core#treeish#sha1(a:git, commit2)
return {
\ 'commit0': gina#core#treeish#resolve(a:git, commit0, 1),
\ 'commit1': gina#core#treeish#resolve(a:git, commit1, 1),
\ 'commit2': gina#core#treeish#resolve(a:git, commit2, 1),
\ 'hash0': hash0,
\ 'hash1': hash1,
\ 'hash2': hash2,
\}
endfunction
function! s:get_remote_url(git, commit1, commit2) abort
let config = gina#core#repo#config(a:git)
" Find a corresponding 'remote'
let candidates = [a:commit1, a:commit2, 'master']
for candidate in candidates
let remote_name = get(config, printf('branch.%s.remote', candidate), '')
if !empty(remote_name) && remote_name !=# '.'
break
endif
endfor
let remote_name = empty(remote_name) ? 'origin' : remote_name
let result = gina#process#call(a:git, ['remote', 'get-url', remote_name])
if result.status
throw gina#process#errormsg(result)
endif
return result.content[0]
endfunction
function! s:build_base_url(remote_url, scheme) abort
for [domain, info] in items(g:gina#command#browse#translation_patterns)
for pattern in info[0]
let pattern = substitute(pattern, '\C' . '%domain', domain, 'g')
if a:remote_url =~# pattern
let repl = get(info[1], a:scheme, a:remote_url)
let repl = escape(repl, '&')
return substitute(a:remote_url, '\C' . pattern, repl, 'g')
endif
endfor
endfor
return ''
endfunction
" Config ---------------------------------------------------------------------
call gina#config(expand('<sfile>'), {
\ 'translation_patterns': {
\ 'github\.com': [
\ [
\ '\vhttps?://(%domain)/(.{-})/(.{-})%(\.git)?$',
\ '\vgit://(%domain)/(.{-})/(.{-})%(\.git)?$',
\ '\vgit\@(%domain):(.{-})/(.{-})%(\.git)?$',
\ '\vssh://git\@(%domain)/(.{-})/(.{-})%(\.git)?$',
\ ], {
\ '_': 'https://\1/\2/\3/blob/%r0/%pt%{#L|}ls%{-L|}le',
\ 'root': 'https://\1/\2/\3/tree/%r0/',
\ 'blame': 'https://\1/\2/\3/blame/%r0/%pt%{#L|}ls%{-L|}le',
\ 'compare': 'https://\1/\2/\3/compare/%h1...%h2',
\ },
\ ],
\ 'gitlab\.com': [
\ [
\ '\vhttps?://(%domain)/(.{-})/(.{-})%(\.git)?$',
\ '\vgit://(%domain)/(.{-})/(.{-})%(\.git)?$',
\ '\vgit\@(%domain):(.{-})/(.{-})%(\.git)?$',
\ '\vssh://git\@(%domain)/(.{-})/(.{-})%(\.git)?$',
\ ], {
\ '_': 'https://\1/\2/\3/blob/%r0/%pt%{#L|}ls%{-L|}le',
\ 'root': 'https://\1/\2/\3/tree/%r0/',
\ 'blame': 'https://\1/\2/\3/blame/%r0/%pt%{#L|}ls%{-L|}le',
\ 'compare': 'https://\1/\2/\3/compare/%h1...%h2',
\ },
\ ],
\ 'bitbucket\.org': [
\ [
\ '\vhttps?://(%domain)/(.{-})/(.{-})%(\.git)?$',
\ '\vgit://(%domain)/(.{-})/(.{-})%(\.git)?$',
\ '\vgit\@(%domain):(.{-})/(.{-})%(\.git)?$',
\ '\vssh://git\@(%domain)/(.{-})/(.{-})%(\.git)?$',
\ ], {
\ '_': 'https://\1/\2/\3/src/%r0/%pt%{#cl-|}ls',
\ 'root': 'https://\1/\2/\3/commits/%r0',
\ 'blame': 'https://\1/\2/\3/annotate/%r0/%pt',
\ 'compare': 'https://\1/\2/\3/diff/%pt?diff1=%h1&diff2=%h2',
\ },
\ ],
\ '.*\.visualstudio\.com': [
\ [
\ '\vhttps?://(%domain)/(.{-})/_git/(.{-})$',
\ ], {
\ '_': 'https://\1/\2/_git/\3/?path=%pt&version=GB%r0%{&line=|}ls%{&lineEnd=|}le',
\ 'root': 'https://\1/\2/_git/\3/?version=GB%r0',
\ },
\ ],
\ },
\})