1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-03 23:50:05 +08:00
SpaceVim/bundle/gina.vim/autoload/gina/core/buffer.vim

224 lines
6.6 KiB
VimL
Raw Normal View History

let s:Buffer = vital#gina#import('Vim.Buffer')
let s:String = vital#gina#import('Data.String')
let s:Guard = vital#gina#import('Vim.Guard')
let s:Opener = vital#gina#import('Vim.Buffer.Opener')
let s:Path = vital#gina#import('System.Filepath')
let s:Window = vital#gina#import('Vim.Window')
let s:NOAUTOCMD_SUFFIX = ':$'
let s:DEFAULT_PARAMS_ATTRIBUTES = {
\ 'repo': '',
\ 'scheme': '',
\ 'params': [],
\ 'rev': '',
\ 'path': '',
\ 'treeish': '',
\}
function! gina#core#buffer#bufname(git, scheme, ...) abort
let options = get(a:000, 0, {})
let params = filter(gina#util#get(options, 'params', []), '!empty(v:val)')
let treeish = gina#util#get(options, 'treeish', printf('%s:%s',
\ gina#util#get(options, 'rev'),
\ gina#util#get(options, 'path'),
\))
return s:normalize_bufname(printf(
\ 'gina://%s:%s%s/%s%s',
\ a:git.refname,
\ a:scheme,
\ empty(params) ? '' : ':' . join(params, ':'),
\ s:Path.unixpath(substitute(treeish, '^:0', '', '')),
\ gina#util#get(options, 'noautocmd', 0) ? s:NOAUTOCMD_SUFFIX : '',
\))
endfunction
function! gina#core#buffer#parse(expr) abort
let path = expand(a:expr)
let m = matchlist(path, printf(
\ '\v^gina://([^:]+):([^:\/]+)([^\/]*)[\/]?(%%(:[0-3]|[^:]*)%%(:[^:]*)?)%%(\m%s\v)?$',
\ s:String.escape_pattern(s:NOAUTOCMD_SUFFIX),
\))
if empty(m)
return {}
endif
let treeish = m[4]
let [rev, path] = gina#core#treeish#parse(treeish)
let params = {
\ 'repo': m[1],
\ 'scheme': m[2],
\ 'params': filter(split(m[3], ':'), '!empty(v:val)'),
\ 'rev': rev,
\ 'treeish': treeish,
\}
if path isnot# v:null
let params.path = substitute(
\ path,
\ s:String.escape_pattern(s:NOAUTOCMD_SUFFIX) . '$',
\ '',
\ '',
\)
endif
return params
endfunction
function! gina#core#buffer#param(expr, attr, ...) abort
if !has_key(s:DEFAULT_PARAMS_ATTRIBUTES, a:attr)
throw gina#core#revelator#critical(printf(
\ 'Unknown attribute "%s" has specified',
\ a:attr,
\))
endif
let default = get(a:000, 0, s:DEFAULT_PARAMS_ATTRIBUTES[a:attr])
let params = gina#core#buffer#parse(a:expr)
return gina#util#get(params, a:attr, default)
endfunction
function! gina#core#buffer#open(bufname, ...) abort
let options = extend({
\ 'mods': '',
\ 'group': '',
\ 'opener': '',
\ 'origin': v:true,
\ 'range': 'tabpage',
\ 'cmdarg': '',
\ 'width': v:null,
\ 'height': v:null,
\ 'line': v:null,
\ 'col': v:null,
\ 'callback': v:null,
\}, get(a:000, 0, {}),
\)
let bufname = s:normalize_bufname(a:bufname)
" Move focus to an origin window if necessary
if options.origin isnot# v:null && s:is_locator_available(options.opener)
let origin = options.origin is# v:true ? winnr() : options.origin
call gina#core#locator#focus(origin)
endif
" Open a buffer
if options.callback is# v:null
let context = s:open_without_callback(bufname, options)
else
let context = s:open_with_callback(bufname, options)
endif
" Resize width/height if necessary
if options.width && options.width != winwidth(0)
execute printf('vertical resize %d', options.width)
endif
if options.height && options.height != winheight(0)
execute printf('resize %d', options.height)
endif
" Move cursor if necessary
call setpos('.', [
\ 0,
\ options.line is# v:null ? line('.') : options.line,
\ options.col is# v:null ? col('.') : options.col,
\ 0,
\])
normal! zvzz
" Finalize
call context.end()
return context
endfunction
function! gina#core#buffer#focus(expr) abort
return s:Window.focus_buffer(a:expr)
endfunction
function! gina#core#buffer#assign_cmdarg(...) abort
let guard = s:Guard.store(['&l:modifiable'])
try
setlocal modifiable
let cmdarg = a:0 == 0 ? v:cmdarg : a:1
let fileencoding = matchstr(cmdarg, '++enc=\zs\S\+')
if !empty(fileencoding)
let &l:fileencoding = fileencoding
endif
let fileformat = matchstr(cmdarg, '++ff=\zs\S\+')
if !empty(fileformat)
let &l:fileformat = fileformat
endif
finally
call guard.restore()
endtry
endfunction
" Private --------------------------------------------------------------------
function! s:normalize_bufname(bufname) abort
" The {bufname}
" 1. Could not be started/ended with whitespaces
" 2. Could not ends with ':' in Windows
" 3. Should not ends with '/' in Vim 8 (opening a buffer fail randomly)
let oldname = ''
let newname = a:bufname
while oldname !=# newname
let oldname = newname
let newname = substitute(newname, '\%(^\s\+\|\s\+$\)', '', 'g')
let newname = substitute(newname, '\:\+$', '', '')
let newname = substitute(newname, '[\\/]$', '', '')
endwhile
return newname
endfunction
function! s:open_without_callback(bufname, options) abort
let context = s:Opener.open(a:bufname, {
\ 'mods': a:options.mods,
\ 'cmdarg': a:options.cmdarg,
\ 'group': a:options.group,
\ 'opener': a:options.opener,
\ 'range': a:options.range,
\})
return context
endfunction
function! s:open_with_callback(bufname, options) abort
" Open a buffer without BufReadCmd
let guard = s:Guard.store(['&eventignore'])
try
set eventignore+=BufReadCmd
let context = s:Opener.open(a:bufname, {
\ 'mods': a:options.mods,
\ 'group': a:options.group,
\ 'opener': a:options.opener,
\ 'range': a:options.range,
\})
finally
call guard.restore()
endtry
" NOTE:
" The content of the buffer MUST NOT be modified by callback while 'edit'
" command will be called to override the content later.
let content = getline(1, '$')
call call(
\ a:options.callback.fn,
\ get(a:options.callback, 'args', []),
\ a:options.callback
\)
if content != getline(1, '$')
throw gina#core#revelator#critical(
\ 'A buffer content could not be modified by callback'
\)
endif
" Update content
if !&modified
execute 'keepjumps edit' a:options.cmdarg
endif
return context
endfunction
function! s:is_locator_available(opener) abort
if a:opener =~# '\<p\%[tag]!\?\>'
return 0
elseif a:opener =~# '\<ped\%[it]!\?\>'
return 0
elseif a:opener =~# '\<ps\%[earch]!\?\>'
return 0
elseif a:opener =~# '\<\%(tabe\%[dit]\|tabnew\)\>'
return 0
elseif a:opener =~# '\<tabf\%[ind]\>'
return 0
endif
return 1
endfunction