" ___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(''), '\zs\d\+\ze__SID$') endfunction execute join(['function! vital#_grammarous#OptionParser#import() abort', printf("return map({'_vital_depends': '', 'new': '', '_vital_loaded': ''}, \"function('%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: