diff --git a/autoload/codefmt/formatterhelpers.vim b/autoload/codefmt/formatterhelpers.vim index 43f06c9..a0a335d 100644 --- a/autoload/codefmt/formatterhelpers.vim +++ b/autoload/codefmt/formatterhelpers.vim @@ -13,6 +13,9 @@ " limitations under the License. +let s:plugin = maktaba#plugin#Get('codefmt') + + "" " @public " Format lines in the current buffer via a formatter invoked by {cmd}, which @@ -31,6 +34,7 @@ function! codefmt#formatterhelpers#Format(cmd) abort call maktaba#buffer#Overwrite(1, line('$'), l:formatted) endfunction + "" " @public " Attempt to format a range of lines from {startline} to {endline} in the @@ -62,3 +66,44 @@ function! codefmt#formatterhelpers#AttemptFakeRangeFormatting( call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted) endfunction + + +"" +" @public +" Resolve a flag (function, string or array) to a normalized array, with special +" handling to convert a spaceless string to a single-element array. This is the +" common case for executables, and more importantly, is backward-compatible for +" existing user settings. +" +" @throws WrongType if the flag doesn't resolve to a string or array +function! codefmt#formatterhelpers#ResolveFlagToArray(flag_name) abort + let l:FlagFn = s:plugin.Flag(a:flag_name) + if maktaba#value#IsFuncref(l:FlagFn) + let l:value = maktaba#function#Call(l:FlagFn) + else + let l:value = l:FlagFn + endif + + " After (conditionally) calling the function, the resulting value should be + " either a list that we can use directly, or a string that we can treat as + " a single-element list, mainly for backward compatibility. + if maktaba#value#IsString(l:value) + if l:value =~ '\s' + " Uh oh, there are spaces in the string. Rather than guessing user intent + " with shell quoting and word splitting, handle this (hopefully unusual) + " case by telling them to update their configuration. + throw maktaba#error#WrongType( + \ '%s flag is a string with spaces, please make it a list. ' . + \ 'Resolved value was: %s', + \ a:flag_name, l:value) + endif + " Convert spaceless string to single-element list. + let l:value = [l:value] + elseif !maktaba#value#IsList(l:value) + throw maktaba#error#WrongType( + \ '%s flag should be a list after calling. Found %s', + \ a:flag_name, maktaba#value#TypeName(l:value)) + endif + + return l:value +endfunction diff --git a/autoload/codefmt/prettier.vim b/autoload/codefmt/prettier.vim index 70399f0..dd25299 100644 --- a/autoload/codefmt/prettier.vim +++ b/autoload/codefmt/prettier.vim @@ -16,7 +16,7 @@ let s:plugin = maktaba#plugin#Get('codefmt') " See https://prettier.io for a list of supported file types. -let s:supported_filetypes = ['javascript', 'markdown', 'html', 'css', 'yaml', +let s:supported_filetypes = ['javascript', 'markdown', 'html', 'css', 'yaml', \ 'jsx', 'less', 'scss', 'mdx', 'vue'] @@ -30,7 +30,9 @@ function! codefmt#prettier#GetFormatter() abort \ 'and configure the prettier_executable flag'} function l:formatter.IsAvailable() abort - return executable(s:plugin.Flag('prettier_executable')) + let l:cmd = codefmt#formatterhelpers#ResolveFlagToArray( + \ 'prettier_executable') + return !empty(l:cmd) && executable(l:cmd[0]) endfunction function l:formatter.AppliesToBuffer() abort @@ -42,17 +44,8 @@ function! codefmt#prettier#GetFormatter() abort " @flag(prettier_executable), only targeting the range between {startline} and " {endline}. function l:formatter.FormatRange(startline, endline) abort - let l:Prettier_options = s:plugin.Flag('prettier_options') - if type(l:Prettier_options) is# type([]) - let l:prettier_options = l:Prettier_options - elseif maktaba#value#IsCallable(l:Prettier_options) - let l:prettier_options = maktaba#function#Call(l:Prettier_options) - else - throw maktaba#error#WrongType( - \ 'prettier_options flag must be list or callable. Found %s', - \ string(l:Prettier_options)) - endif - let l:cmd = [s:plugin.Flag('prettier_executable'), '--stdin', '--no-color'] + let l:cmd = codefmt#formatterhelpers#ResolveFlagToArray( + \ 'prettier_executable') + ['--stdin', '--no-color'] " prettier is able to automatically choose the best parser if the filepath " is provided. Otherwise, fall back to the previous default: babylon. @@ -74,7 +67,9 @@ function! codefmt#prettier#GetFormatter() abort let l:lines_end = join(l:lines[0 : a:endline - 1], "\n") call extend(l:cmd, ['--range-end', string(strchars(l:lines_end))]) - call extend(l:cmd, l:prettier_options) + call extend(l:cmd, codefmt#formatterhelpers#ResolveFlagToArray( + \ 'prettier_options')) + try let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call() let l:formatted = split(l:result.stdout, "\n") diff --git a/autoload/codefmt/zprint.vim b/autoload/codefmt/zprint.vim index bc0a287..3e60014 100644 --- a/autoload/codefmt/zprint.vim +++ b/autoload/codefmt/zprint.vim @@ -38,7 +38,9 @@ function! codefmt#zprint#GetFormatter() abort \ 'and configure the zprint_executable flag'} function l:formatter.IsAvailable() abort - return executable(s:plugin.Flag('zprint_executable')) + let l:cmd = codefmt#formatterhelpers#ResolveFlagToArray( + \ 'zprint_executable') + return !empty(l:cmd) && executable(l:cmd[0]) endfunction function l:formatter.AppliesToBuffer() abort @@ -50,25 +52,15 @@ function! codefmt#zprint#GetFormatter() abort " @flag(zprint_executable), only targeting the range between {startline} and " {endline}. function l:formatter.FormatRange(startline, endline) abort - " Must be upper-cased to call as a function - let l:ZprintOptions = s:plugin.Flag('zprint_options') - if type(l:ZprintOptions) is# type([]) - " Assign upper-case to lower-case - let l:zprint_options = l:ZprintOptions - elseif maktaba#value#IsCallable(l:ZprintOptions) - " Call upper-case to assign lower-case - let l:zprint_options = maktaba#function#Call(l:ZprintOptions) - else - throw maktaba#error#WrongType( - \ 'zprint_options flag must be list or callable. Found %s', - \ string(l:ZprintOptions)) - endif - let l:cmd_args = [s:plugin.Flag('zprint_executable')] - call extend(l:cmd_args, l:zprint_options) + let l:exe = codefmt#formatterhelpers#ResolveFlagToArray( + \ 'zprint_executable') + let l:opts = codefmt#formatterhelpers#ResolveFlagToArray( + \ 'zprint_options') " Prepare the syscall, changing to the containing directory in case the user " has configured {:search-config? true} in ~/.zprintrc - let l:cmd = maktaba#syscall#Create(l:cmd_args).WithCwd(expand('%:p:h')) + let l:cmd = maktaba#syscall#Create(l:exe + l:opts).WithCwd(expand('%:p:h')) + " zprint does not support range formatting yet: " https://github.com/kkinnear/zprint/issues/122 " This fake range formatting works well for top-level forms, although it's