1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-03 19:00:06 +08:00
SpaceVim/bundle/vim-grammarous/autoload/vital/_grammarous/OptionParser.vim
2023-03-28 23:43:55 +08:00

424 lines
13 KiB
VimL
Vendored

" ___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: