mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-03 21:50:05 +08:00
424 lines
13 KiB
VimL
424 lines
13 KiB
VimL
|
" ___vital___
|
||
|
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
|
||
|
" Do not mofidify the code nor insert new lines before '" ___vital___'
|
||
|
if v:version > 703 || v:version == 703 && has('patch1170')
|
||
|
function! vital#_grammarous#OptionParser#import() abort
|
||
|
return map({'_vital_depends': '', 'new': '', '_vital_loaded': ''}, 'function("s:" . v:key)')
|
||
|
endfunction
|
||
|
else
|
||
|
function! s:_SID() abort
|
||
|
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||
|
endfunction
|
||
|
execute join(['function! vital#_grammarous#OptionParser#import() abort', printf("return map({'_vital_depends': '', 'new': '', '_vital_loaded': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||
|
delfunction s:_SID
|
||
|
endif
|
||
|
" ___vital___
|
||
|
let s:save_cpo = &cpo
|
||
|
set cpo&vim
|
||
|
|
||
|
let s:_STRING_TYPE = type('')
|
||
|
let s:_LIST_TYPE = type([])
|
||
|
let s:_DICT_TYPE = type({})
|
||
|
let s:_NUM_TYPE = type(0)
|
||
|
|
||
|
function! s:_vital_loaded(V) abort
|
||
|
let s:L = a:V.import('Data.List')
|
||
|
endfunction
|
||
|
|
||
|
function! s:_vital_depends() abort
|
||
|
return ['Data.List']
|
||
|
endfunction
|
||
|
|
||
|
let s:_PRESET_COMPLETER = {}
|
||
|
function! s:_PRESET_COMPLETER.file(optlead, cmdline, cursorpos) abort
|
||
|
let candidates = glob(a:optlead . '*', 0, 1)
|
||
|
if a:optlead =~# '^\~'
|
||
|
let home_matcher = '^' . expand('~') . '/'
|
||
|
call map(candidates, "substitute(v:val, home_matcher, '~/', '')")
|
||
|
endif
|
||
|
call map(candidates, "escape(isdirectory(v:val) ? v:val.'/' : v:val, ' \\')")
|
||
|
return candidates
|
||
|
endfunction
|
||
|
|
||
|
function! s:_make_option_description_for_help(opt) abort
|
||
|
let extra = ''
|
||
|
if has_key(a:opt, 'default_value')
|
||
|
let extra .= 'DEFAULT: ' . string(a:opt.default_value) . ', '
|
||
|
endif
|
||
|
if get(a:opt, 'required_option', 0)
|
||
|
let extra .= 'REQUIRED, '
|
||
|
endif
|
||
|
if has_key(a:opt, 'pattern_option')
|
||
|
let extra .= 'PATTERN: ' . string(a:opt.pattern_option) . ', '
|
||
|
endif
|
||
|
let extra = substitute(extra, ', $', '', '')
|
||
|
if extra !=# ''
|
||
|
let extra = ' (' . extra . ')'
|
||
|
endif
|
||
|
return a:opt.description . extra
|
||
|
endfunction
|
||
|
|
||
|
function! s:_make_option_definition_for_help(opt) abort
|
||
|
let key = a:opt.definition
|
||
|
if has_key(a:opt, 'short_option_definition')
|
||
|
let key .= ', ' . a:opt.short_option_definition
|
||
|
endif
|
||
|
return key
|
||
|
endfunction
|
||
|
|
||
|
function! s:_extract_special_opts(argc, argv) abort
|
||
|
let ret = {'specials' : {}}
|
||
|
if a:argc <= 0
|
||
|
return ret
|
||
|
endif
|
||
|
|
||
|
let ret.q_args = a:argv[0]
|
||
|
for arg in a:argv[1:]
|
||
|
let arg_type = type(arg)
|
||
|
if arg_type == s:_LIST_TYPE
|
||
|
let ret.specials.__range__ = arg
|
||
|
elseif arg_type == type(0)
|
||
|
let ret.specials.__count__ = arg
|
||
|
elseif arg_type == s:_STRING_TYPE
|
||
|
if arg ==# '!'
|
||
|
let ret.specials.__bang__ = arg
|
||
|
elseif arg !=# ''
|
||
|
let ret.specials.__reg__ = arg
|
||
|
endif
|
||
|
endif
|
||
|
unlet arg
|
||
|
endfor
|
||
|
return ret
|
||
|
endfunction
|
||
|
|
||
|
function! s:_make_args(cmd_args) abort
|
||
|
let type = type(a:cmd_args)
|
||
|
if type == s:_STRING_TYPE
|
||
|
return split(a:cmd_args)
|
||
|
elseif type == s:_LIST_TYPE
|
||
|
return map(copy(a:cmd_args), 'type(v:val) == s:_STRING_TYPE ? v:val : string(v:val)')
|
||
|
else
|
||
|
throw 'vital: OptionParser: Invalid type: first argument of parse() should be string or list of string'
|
||
|
endif
|
||
|
endfunction
|
||
|
|
||
|
function! s:_expand_short_option(arg, options) abort
|
||
|
let short_opt = matchstr(a:arg, '^-[^- =]\>')
|
||
|
for [name, value] in items(a:options)
|
||
|
if get(value, 'short_option_definition', '') ==# short_opt
|
||
|
return substitute(a:arg, short_opt, '--' . name, '')
|
||
|
endif
|
||
|
endfor
|
||
|
return a:arg
|
||
|
endfunction
|
||
|
|
||
|
function! s:_check_extra_option(parsed_args, options) abort
|
||
|
for [name, option] in items(a:options)
|
||
|
if has_key(option, 'default_value') && ! has_key(a:parsed_args, name)
|
||
|
let a:parsed_args[name] = option.default_value
|
||
|
endif
|
||
|
if get(option, 'required_option', 0) && ! has_key(a:parsed_args, name)
|
||
|
throw 'vital: OptionParser: parameter is required: ' . name
|
||
|
endif
|
||
|
if has_key(option, 'pattern_option') && has_key(a:parsed_args, name) && a:parsed_args[name] !~# option.pattern_option
|
||
|
throw 'vital: OptionParser: parameter doesn''t match pattern: ' . name . ' ' . option.pattern_option
|
||
|
endif
|
||
|
endfor
|
||
|
endfunction
|
||
|
|
||
|
function! s:_parse_arg(arg, options) abort
|
||
|
" if --no-hoge pattern
|
||
|
if a:arg =~# '^--no-[^= ]\+'
|
||
|
" get hoge from --no-hoge
|
||
|
let key = matchstr(a:arg, '^--no-\zs[^= ]\+')
|
||
|
if has_key(a:options, key) && has_key(a:options[key], 'no')
|
||
|
return [key, 0]
|
||
|
endif
|
||
|
|
||
|
" if --hoge pattern
|
||
|
elseif a:arg =~# '^--[^= ]\+$'
|
||
|
" get hoge from --hoge
|
||
|
let key = matchstr(a:arg, '^--\zs[^= ]\+')
|
||
|
if has_key(a:options, key)
|
||
|
if has_key(a:options[key], 'has_value')
|
||
|
throw 'vital: OptionParser: Must specify value for option: ' . key
|
||
|
endif
|
||
|
return [key, 1]
|
||
|
endif
|
||
|
|
||
|
" if --hoge=poyo pattern
|
||
|
else
|
||
|
" get hoge from --hoge=poyo
|
||
|
let key = matchstr(a:arg, '^--\zs[^= ]\+')
|
||
|
if has_key(a:options, key)
|
||
|
" get poyo from --hoge=poyo
|
||
|
return [key, matchstr(a:arg, '^--[^= ]\+=\zs\S\+$')]
|
||
|
endif
|
||
|
endif
|
||
|
|
||
|
return a:arg
|
||
|
endfunction
|
||
|
|
||
|
function! s:_parse_args(cmd_args, options) abort
|
||
|
let parsed_args = {}
|
||
|
let unknown_args = []
|
||
|
let args = s:_make_args(a:cmd_args)
|
||
|
|
||
|
for arg in args
|
||
|
|
||
|
" replace short option with long option if short option is available
|
||
|
if arg =~# '^-[^- =]\>'
|
||
|
let arg = s:_expand_short_option(arg, a:options)
|
||
|
endif
|
||
|
|
||
|
" check if arg is --[no-]hoge[=VALUE]
|
||
|
if arg !~# '^--\%(no-\)\=[^= ]\+\%(=\S\+\)\=$'
|
||
|
call add(unknown_args, arg)
|
||
|
continue
|
||
|
endif
|
||
|
|
||
|
let parsed_arg = s:_parse_arg(arg, a:options)
|
||
|
if type(parsed_arg) == s:_LIST_TYPE
|
||
|
let parsed_args[parsed_arg[0]] = parsed_arg[1]
|
||
|
else
|
||
|
call add(unknown_args, parsed_arg)
|
||
|
endif
|
||
|
unlet parsed_arg
|
||
|
endfor
|
||
|
|
||
|
return [parsed_args, unknown_args]
|
||
|
endfunction
|
||
|
|
||
|
let s:_DEFAULT_PARSER = {'options' : {}}
|
||
|
|
||
|
function! s:_DEFAULT_PARSER.help() abort
|
||
|
let definitions = map(values(self.options), '[s:_make_option_definition_for_help(v:val), s:_make_option_description_for_help(v:val)]')
|
||
|
let key_width = len(s:L.max_by(definitions, 'len(v:val[0])')[0])
|
||
|
return "Options:\n" .
|
||
|
\ join(map(definitions, '
|
||
|
\ " " . v:val[0] .
|
||
|
\ repeat(" ", key_width - len(v:val[0])) . " : " .
|
||
|
\ v:val[1]
|
||
|
\ '), "\n")
|
||
|
endfunction
|
||
|
|
||
|
function! s:_DEFAULT_PARSER.parse(...) abort
|
||
|
let opts = s:_extract_special_opts(a:0, a:000)
|
||
|
if ! has_key(opts, 'q_args')
|
||
|
return opts.specials
|
||
|
endif
|
||
|
|
||
|
if ! get(self, 'disable_auto_help', 0)
|
||
|
\ && opts.q_args ==# '--help'
|
||
|
\ && ! has_key(self.options, 'help')
|
||
|
echo self.help()
|
||
|
return extend(opts.specials, {'help' : 1, '__unknown_args__' : []})
|
||
|
endif
|
||
|
|
||
|
let parsed_args = s:_parse_args(opts.q_args, self.options)
|
||
|
|
||
|
let ret = parsed_args[0]
|
||
|
call s:_check_extra_option(ret, self.options)
|
||
|
call extend(ret, opts.specials)
|
||
|
let ret.__unknown_args__ = parsed_args[1]
|
||
|
return ret
|
||
|
endfunction
|
||
|
|
||
|
function! s:_DEFAULT_PARSER.on(def, desc, ...) abort
|
||
|
if a:0 > 1
|
||
|
throw 'vital: OptionParser: Wrong number of arguments: ' . a:0 + 2 . ' for 2 or 3'
|
||
|
endif
|
||
|
|
||
|
" get hoge and huga from --hoge=huga
|
||
|
let matched = matchlist(a:def, '^--\([^= ]\+\)\(=\S\+\)\=$')[1:2]
|
||
|
if len(matched) != 2
|
||
|
throw 'vital: OptionParser: Invalid option "' . a:def . '"'
|
||
|
endif
|
||
|
let [name, value] = matched
|
||
|
let has_value = value !=# ''
|
||
|
|
||
|
let no = name =~# '^\[no-]'
|
||
|
if no
|
||
|
let name = matchstr(name, '^\[no-]\zs.\+')
|
||
|
endif
|
||
|
|
||
|
if name ==# ''
|
||
|
throw 'vital: OptionParser: Option of key is invalid: ' . a:def
|
||
|
endif
|
||
|
|
||
|
let self.options[name] = {'definition' : a:def, 'description' : a:desc}
|
||
|
if no
|
||
|
let self.options[name].no = 1
|
||
|
endif
|
||
|
if has_value
|
||
|
let self.options[name].has_value = 1
|
||
|
endif
|
||
|
|
||
|
" if short option is specified
|
||
|
if a:0 == 1
|
||
|
if type(a:1) == type({})
|
||
|
if has_key(a:1, 'short')
|
||
|
if (a:1.short !~# '^-[[:alnum:]]\>')
|
||
|
throw 'vital: OptionParser: Invalid short option: ' . a:1.short
|
||
|
endif
|
||
|
let self.options[name].short_option_definition = a:1.short
|
||
|
endif
|
||
|
if has_key(a:1, 'default')
|
||
|
let self.options[name].default_value = a:1.default
|
||
|
endif
|
||
|
if has_key(a:1, 'completion')
|
||
|
if type(a:1.completion) == s:_STRING_TYPE
|
||
|
let self.options[name].completion = s:_PRESET_COMPLETER[a:1.completion]
|
||
|
else
|
||
|
let self.options[name].completion = a:1.completion
|
||
|
endif
|
||
|
endif
|
||
|
if has_key(a:1, 'required')
|
||
|
if a:1.required isnot 0 && a:1.required isnot 1
|
||
|
throw 'vital: OptionParser: Invalid required option: ' . string(a:1.required)
|
||
|
endif
|
||
|
let self.options[name].required_option = a:1.required
|
||
|
endif
|
||
|
if has_key(a:1, 'pattern')
|
||
|
try
|
||
|
call match('', a:1.pattern)
|
||
|
catch
|
||
|
throw printf('vital: OptionParser: Invalid pattern option: exception="%s" pattern="%s"', v:exception, a:1.pattern)
|
||
|
endtry
|
||
|
let self.options[name].pattern_option = a:1.pattern
|
||
|
endif
|
||
|
else
|
||
|
let self.options[name].default_value = a:1
|
||
|
endif
|
||
|
endif
|
||
|
|
||
|
return self
|
||
|
endfunction
|
||
|
|
||
|
function! s:_complete_long_option(arglead, options) abort
|
||
|
let candidates = []
|
||
|
for [name, option] in items(a:options)
|
||
|
let has_value = get(option, 'has_value', 0)
|
||
|
call add(candidates, '--' . name . (has_value ? '=' : ''))
|
||
|
if get(option, 'no', 0)
|
||
|
call add(candidates, '--no-' . name . (has_value ? '=' : ''))
|
||
|
endif
|
||
|
endfor
|
||
|
let lead_pattern = '^' . a:arglead
|
||
|
return filter(candidates, 'v:val =~# lead_pattern')
|
||
|
endfunction
|
||
|
|
||
|
function! s:_complete_short_option(arglead, options) abort
|
||
|
let candidates = []
|
||
|
for option in values(a:options)
|
||
|
let has_value = get(option, 'has_value', 0)
|
||
|
if has_key(option, 'short_option_definition')
|
||
|
call add(candidates, option.short_option_definition . (has_value ? '=' : ''))
|
||
|
if get(option, 'no', 0)
|
||
|
call add(candidates, '-no' . option.short_option_definition . (has_value ? '=' : ''))
|
||
|
endif
|
||
|
endif
|
||
|
endfor
|
||
|
let lead_pattern = '^' . a:arglead
|
||
|
return filter(candidates, 'v:val =~# lead_pattern')
|
||
|
endfunction
|
||
|
|
||
|
function! s:_complete_user_specified_option(options, arglead, cmdline, cursorpos) abort
|
||
|
let lead = matchstr(a:arglead, '=\zs.*$')
|
||
|
let name = matchstr(a:arglead, '^--\zs[^=]\+')
|
||
|
if ! has_key(a:options, name) || ! has_key(a:options[name], 'completion')
|
||
|
return []
|
||
|
endif
|
||
|
return a:options[name].completion(lead, a:cmdline, a:cursorpos)
|
||
|
endfunction
|
||
|
|
||
|
function! s:_complete_user_specified_short_option(options, arglead, cmdline, cursorpos) abort
|
||
|
let lead = matchstr(a:arglead, '=\zs.*$')
|
||
|
let def = matchstr(a:arglead, '^-[^-=]')
|
||
|
for option in values(a:options)
|
||
|
if has_key(option, 'short_option_definition')
|
||
|
\ && option.short_option_definition ==# def
|
||
|
\ && has_key(option, 'completion')
|
||
|
return option.completion(lead, a:cmdline, a:cursorpos)
|
||
|
endif
|
||
|
endfor
|
||
|
return []
|
||
|
endfunction
|
||
|
|
||
|
function! s:_complete_unknown_option(Completer, arglead, cmdline, cursorpos) abort
|
||
|
if type(a:Completer) == s:_STRING_TYPE
|
||
|
return s:_PRESET_COMPLETER[a:Completer](a:arglead, a:cmdline, a:cursorpos)
|
||
|
else
|
||
|
return a:Completer(a:arglead, a:cmdline, a:cursorpos)
|
||
|
endif
|
||
|
endfunction
|
||
|
|
||
|
function! s:_DEFAULT_PARSER.complete(arglead, cmdline, cursorpos) abort
|
||
|
if a:arglead =~# '^--[^=]*$'
|
||
|
" when long option
|
||
|
return s:_complete_long_option(a:arglead, self.options)
|
||
|
|
||
|
elseif a:arglead =~# '^-[^-=]\?$'
|
||
|
" when short option
|
||
|
return s:_complete_short_option(a:arglead, self.options)
|
||
|
|
||
|
elseif a:arglead =~# '^--.\+=.*$'
|
||
|
let prefix = matchstr(a:arglead, '^.\+=')
|
||
|
return map(
|
||
|
\ s:_complete_user_specified_option(self.options, a:arglead, a:cmdline, a:cursorpos),
|
||
|
\ 'prefix . v:val'
|
||
|
\ )
|
||
|
|
||
|
elseif a:arglead =~# '^-[^-=]=.*$'
|
||
|
let prefix = matchstr(a:arglead, '^-[^-=]=')
|
||
|
return map(
|
||
|
\ s:_complete_user_specified_short_option(self.options, a:arglead, a:cmdline, a:cursorpos),
|
||
|
\ 'prefix . v:val'
|
||
|
\ )
|
||
|
|
||
|
elseif has_key(self, 'unknown_options_completion')
|
||
|
return s:_complete_unknown_option(self.unknown_options_completion, a:arglead, a:cmdline, a:cursorpos)
|
||
|
endif
|
||
|
|
||
|
return []
|
||
|
endfunction
|
||
|
|
||
|
function! s:_DEFAULT_PARSER.complete_greedily(arglead, cmdline, cursorpos) abort
|
||
|
|
||
|
if a:arglead =~# '^--.\+=.*$'
|
||
|
let prefix = matchstr(a:arglead, '^.\+=')
|
||
|
return map(
|
||
|
\ s:_complete_user_specified_option(self.options, a:arglead, a:cmdline, a:cursorpos),
|
||
|
\ 'prefix . v:val'
|
||
|
\ )
|
||
|
|
||
|
elseif a:arglead =~# '^-[^-=]=.*$'
|
||
|
let prefix = matchstr(a:arglead, '^-[^-=]=')
|
||
|
return map(
|
||
|
\ s:_complete_user_specified_short_option(self.options, a:arglead, a:cmdline, a:cursorpos),
|
||
|
\ 'prefix . v:val'
|
||
|
\ )
|
||
|
endif
|
||
|
|
||
|
let long_opts = s:_complete_long_option(a:arglead, self.options)
|
||
|
if has_key(self, 'unknown_options_completion')
|
||
|
return long_opts + s:_complete_unknown_option(
|
||
|
\ self.unknown_options_completion,
|
||
|
\ a:arglead,
|
||
|
\ a:cmdline,
|
||
|
\ a:cursorpos
|
||
|
\ )
|
||
|
else
|
||
|
return long_opts
|
||
|
endif
|
||
|
endfunction
|
||
|
|
||
|
function! s:new() abort
|
||
|
return deepcopy(s:_DEFAULT_PARSER)
|
||
|
endfunction
|
||
|
|
||
|
let &cpo = s:save_cpo
|
||
|
unlet s:save_cpo
|
||
|
|
||
|
" vim:set et ts=2 sts=2 sw=2 tw=0:
|