Created
August 2, 2013 22:15
-
-
Save naquad/6143913 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"" Option parser. | |
"" | |
"" Arguments | |
"" [ | |
"" { | |
"" \ 'short': 'X', | |
"" \ 'long': '--xxx', | |
"" \ 'output': 'name', | |
"" \ 'has_value': 1, | |
"" \ 'default': 'some_value', | |
"" \ 'validator': funcref, | |
"" \ }, | |
"" " ... | |
"" ] | |
"" | |
"" short - short option name | |
"" long - long optio name | |
"" output - name in output dictionary. by default it is either long or short | |
"" whatever is available | |
"" has_value - if 0 (default) option is treated as flag | |
"" default - default value. used only if has_value = 1 | |
"" validator - funcref to function(option_name, option_value, other_options) | |
"" where | |
"" option_name - same as output, | |
"" option_value - passed value or 1 if flag | |
"" other_options - dictionary with options and arguments | |
"" ALL VALIDATOR FUNCTIONS ARE CALLED AFTER PARSING IS FINISHED | |
"" Invoked only if has_value = 1. | |
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | |
if &cp || exists('g:loaded_command_opts') | |
finish | |
endif | |
let s:allowed_keys = [ | |
\ 'short', | |
\ 'long', | |
\ 'output', | |
\ 'has_value', | |
\ 'default', | |
\ 'validator' | |
\ ] | |
let command#opts#Parser = {} | |
" Constructor take 1 parameter: list of options | |
" returns command#opts#Parser object | |
function! command#opts#Parser.new(options) dict abort | |
let this = copy(self) | |
let this.options = a:options | |
call this.setupOptions() | |
return this | |
endfunction | |
" Internal method for validating and setting defaults for options | |
function! command#opts#Parser.setupOptions() dict abort | |
if type(self.options) != 3 | |
throw 'command#opts#Parser options should be a list of dictionaries' | |
endif | |
" Because options will be modified to avoid unpleasant surprises | |
" passed options are copied | |
let self.options = deepcopy(self.options) | |
let self.keys = {'short': {}, 'long': {}, 'output': {}} | |
let i = 1 | |
for o in self.options | |
if type(o) != 4 | |
throw 'command#opts#Parser option #' . i . ' is not a dictionary' | |
endif | |
if !has_key(o, 'short') && !has_key(o, 'long') | |
throw 'command#opts#Parser option #' . i . ' has no short / long options' | |
endif | |
if !has_key(o, 'output') | |
let o.output = get(o, 'long', get(o, 'short')) | |
endif | |
for t in ['short', 'long', 'output'] | |
if has_key(o, t) | |
if has_key(self.keys[t], o[t]) | |
throw 'command#opts#Parser option #' . i . ' has duplicate ' . t . ' option' | |
endif | |
if o[t] == '' | |
throw 'command#opts#Parser option #' . i . ' has empty ' . t . ' option' | |
endif | |
let self.keys[t][o[t]] = o | |
endif | |
endfor | |
if has_key(o, 'short') && strlen(o.short) > 1 | |
throw 'command#opts#Parser option #' . i . ' has short name longet than 1 character' | |
endif | |
if !has_key(o, 'has_value') | |
let o.has_value = 0 | |
endif | |
let unknown_keys = filter(keys(o), 'index(s:allowed_keys, v:val) == -1') | |
if !empty(unknown_keys) | |
throw 'Unknown keys found in #' . i . ': ' . join(unknown_keys, ', ') | |
endif | |
let i = i + 1 | |
endfor | |
endfunction | |
" Just a helper for making error result | |
function! command#opts#Parser.error(msg) dict abort | |
return {'success': 0, 'message': a:msg} | |
endfunction | |
" Parser itself. Takes a list of arguments. | |
" Should be a list of strings. | |
function! command#opts#Parser.parse(params) dict abort | |
let result = {'success': 1, 'arguments': [], 'options': {}} | |
for opt in self.options | |
if has_key(opt, 'default') | |
let result.options[opt.output] = opt.default | |
endif | |
endfor | |
let i = 0 | |
let pl = len(a:params) | |
let skip = 0 | |
while i < pl | |
if a:params[i][0] == '-' && a:params[i] != '-' && !skip | |
if a:params[i] == '--' | |
let skip = 1 | |
let i = i + 1 | |
continue | |
endif | |
if a:params[i][1] == '-' " long option | |
let eq = stridx(a:params[i], '=') | |
let option = a:params[i][2 : (eq == -1 ? eq : eq - 1)] | |
if !has_key(self.keys.long, option) | |
return self.error('Unknown option: ' . a:params[i]) | |
endif | |
let spec = self.keys.long[option] | |
if spec.has_value | |
if eq == -1 | |
if i == pl - 1 | |
return self.error('Option ' . option . ' requres argument') | |
endif | |
let result.options[spec.output] = a:params[i + 1] | |
let i += 1 | |
else | |
let result.options[spec.output] = a:params[i][eq + 1 :] | |
endif | |
elseif eq != -1 | |
return self.error('Option ' . option . ' doesnt take any arguments') | |
else | |
let result.options[spec.output] = 1 | |
endif | |
else " short option | |
let j = 1 | |
let al = strlen(a:params[i]) | |
while j < al | |
let option = a:params[i][j] | |
if !has_key(self.keys.short, option) | |
return self.error('Unknown option -' . option) | |
endif | |
let spec = self.keys.short[option] | |
if spec.has_value | |
if j == al - 1 | |
if i == pl - 1 | |
return self.error('Option -' . option . ' requires argument') | |
else | |
let result.options[spec.output] = a:params[i + 1] | |
let i = i + 1 | |
endif | |
break | |
else | |
let result.options[spec.output] = a:params[i][j + 1 :] | |
break | |
endif | |
else | |
let result.options[spec.output] = 1 | |
endif | |
let j = j + 1 | |
endwhile | |
endif | |
else | |
call add(result.arguments, a:params[i]) | |
endif | |
let i += 1 | |
endwhile | |
for [o, v] in items(result.options) | |
let spec = self.keys.output[o] | |
if spec.has_value && has_key(spec, 'validator') | |
let ret = spec.validator(o, v, result) | |
if type(ret) == 1 && ret != '' | |
return self.error(ret) | |
endif | |
endif | |
endfor | |
return result | |
endfunction | |
let g:loaded_command_opts = 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment