Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement common helper functions for common formatting operations #135

Merged
merged 5 commits into from
Jan 8, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 8 additions & 19 deletions autoload/codefmt/autopep8.vim
Original file line number Diff line number Diff line change
Expand Up @@ -67,29 +67,18 @@ function! codefmt#autopep8#GetFormatter() abort

call maktaba#ensure#IsNumber(a:startline)
call maktaba#ensure#IsNumber(a:endline)
let l:lines = getline(1, line('$'))

if s:autopep8_supports_range
let l:cmd = [l:executable, '--range', ''.a:startline, ''.a:endline, '-']
let l:input = join(l:lines, "\n")
call codefmt#formatterhelpers#Format(maktaba#syscall#Create([
\ l:executable,
\ '--range', string(a:startline), string(a:endline),
\ '-']))
else
let l:cmd = [l:executable, '-']
" Hack range formatting by formatting range individually, ignoring context.
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
\ a:startline,
\ a:endline,
\ maktaba#syscall#Create([l:executable, '-']))
endif

let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")

if s:autopep8_supports_range
let l:full_formatted = l:formatted
else
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
endif

call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
endfunction

return l:formatter
Expand Down
7 changes: 3 additions & 4 deletions autoload/codefmt/buildifier.vim
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,10 @@ function! codefmt#buildifier#GetFormatter() abort
let l:cmd += ['-path', l:fname]
endif

let l:input = join(getline(1, line('$')), "\n")
try
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")
call maktaba#buffer#Overwrite(1, line('$'), l:formatted)
" NOTE: Ignores any line ranges given and formats entire buffer.
" buildifier does not support range formatting.
call codefmt#formatterhelpers#Format(maktaba#syscall#Create(l:cmd))
catch
" Parse all the errors and stick them in the quickfix list.
let l:errors = []
Expand Down
21 changes: 6 additions & 15 deletions autoload/codefmt/dartfmt.vim
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,12 @@ function! codefmt#dartfmt#GetFormatter() abort
" @flag(dartfmt_executable}, only targetting the range from {startline} to
" {endline}
function l:formatter.FormatRange(startline, endline) abort
" Hack range formatting by formatting range individually, ignoring context.
let l:cmd = [ s:plugin.Flag('dartfmt_executable') ]
" TODO When https://github.com/dart-lang/dart_style/issues/92 is implemented
" use those options.
call maktaba#ensure#IsNumber(a:startline)
call maktaba#ensure#IsNumber(a:endline)
let l:lines = getline(1, line('$'))
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
try
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []

let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
" dartfmt does not support range formatting yet:
" https://github.com/dart-lang/dart_style/issues/92
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
\ a:startline, a:endline, maktaba#syscall#Create(l:cmd))
catch /ERROR(ShellError):/
" Parse all the errors and stick them in the quickfix list.
let l:errors = []
Expand All @@ -71,7 +61,8 @@ function! codefmt#dartfmt#GetFormatter() abort

if empty(l:errors)
" Couldn't parse dartfmt error format; display it all.
call maktaba#error#Shout('Failed to format range; showing all errors: %s', v:exception)
call maktaba#error#Shout(
\ 'Failed to format range; showing all errors: %s', v:exception)
else
let l:errorHeaderLines = split(v:exception, "\n")[1 : 5]
let l:errorHeader = join(l:errorHeaderLines, "\n")
Expand Down
74 changes: 74 additions & 0 deletions autoload/codefmt/formatterhelpers.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
" Copyright 2020 Google Inc. All rights reserved.
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
" http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.


function! s:EnsureIsSyscall(Value) abort
if type(a:Value) == type({}) &&
\ has_key(a:Value, 'Call') &&
\ maktaba#function#HasSameName(
\ a:Value.Call, function('maktaba#syscall#Call'))
return a:Value
endif
throw maktaba#error#BadValue(
\ 'Not a valid matkaba.Syscall: %s', string(a:Value))
endfunction


""
" @public
" Format lines in the current buffer via a formatter invoked by {cmd} (a
" |maktaba.Syscall|). The command includes the explicit range line numbers to
" use, if any.
"
" @throws ShellError if the {cmd} system call fails
function! codefmt#formatterhelpers#Format(cmd) abort
call s:EnsureIsSyscall(a:cmd)
let l:lines = getline(1, line('$'))
let l:input = join(l:lines, "\n")

let l:result = maktaba#syscall#Create(a:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")

call maktaba#buffer#Overwrite(1, line('$'), l:formatted)
endfunction

""
" @public
" Attempt to format a range of lines from {startline} to {endline} in the
" current buffer via a formatter that doesn't natively support range
" formatting (invoked by {cmd}, a |maktaba.Syscall|), using a hacky strategy
" of sending those lines to the formatter in isolation.
"
" If invoking this hack, please make sure to file a feature request against
" the tool for range formatting and post a URL for that feature request above
" code that calls it.
"
" @throws ShellError if the {cmd} system call fails
function! codefmt#formatterhelpers#AttemptFakeRangeFormatting(
\ startline, endline, cmd) abort
call maktaba#ensure#IsNumber(a:startline)
call maktaba#ensure#IsNumber(a:endline)
call s:EnsureIsSyscall(a:cmd)

let l:lines = getline(1, line('$'))
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")

let l:result = a:cmd.WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]

call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
endfunction
16 changes: 4 additions & 12 deletions autoload/codefmt/gofmt.vim
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,12 @@ function! codefmt#gofmt#GetFormatter() abort
" @flag(gofmt_executable), only targeting the range between {startline} and
" {endline}.
function l:formatter.FormatRange(startline, endline) abort
" Hack range formatting by formatting range individually, ignoring context.
let l:cmd = [ s:plugin.Flag('gofmt_executable') ]
call maktaba#ensure#IsNumber(a:startline)
call maktaba#ensure#IsNumber(a:endline)
let l:lines = getline(1, line('$'))
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
try
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []

let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
" gofmt does not support range formatting.
" TODO: File a feature request with gofmt and link it here.
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
\ a:startline, a:endline, maktaba#syscall#Create(l:cmd))
catch /ERROR(ShellError):/
" Parse all the errors and stick them in the quickfix list.
let l:errors = []
Expand Down
5 changes: 1 addition & 4 deletions autoload/codefmt/googlejava.vim
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ function! codefmt#googlejava#GetFormatter() abort
let l:ranges_str = join(map(copy(a:ranges), 'v:val[0] . ":" . v:val[1]'), ',')
let l:cmd += ['--lines', l:ranges_str, '-']

let l:input = join(getline(1, line('$')), "\n")
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")
call maktaba#buffer#Overwrite(1, line('$'), l:formatted)
call codefmt#formatterhelpers#Format(maktaba#syscall#Create(l:cmd))
endfunction

return l:formatter
Expand Down
15 changes: 4 additions & 11 deletions autoload/codefmt/jsbeautify.vim
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,10 @@ function! codefmt#jsbeautify#GetFormatter() abort
call maktaba#ensure#IsNumber(a:startline)
call maktaba#ensure#IsNumber(a:endline)

let l:lines = getline(1, line('$'))
" Hack range formatting by formatting range individually, ignoring context.
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")

let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]

call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
" js-beautify does not support range formatting yet:
" https://github.com/beautify-web/js-beautify/issues/610
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
\ a:startline, a:endline, maktaba#syscall#Create(l:cmd))
endfunction

return l:formatter
Expand Down
7 changes: 2 additions & 5 deletions autoload/codefmt/rustfmt.vim
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,10 @@ function! codefmt#rustfmt#GetFormatter() abort

call extend(l:cmd, l:rustfmt_options)
try
let l:lines = getline(1, line('$'))
let l:input = join(l:lines, "\n")
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")
" NOTE: Ignores any line ranges given and formats entire buffer.
" Even though rustfmt supports formatting ranges through the --file-lines
" flag, it is not still enabled in the stable binaries.
call maktaba#buffer#Overwrite(1, line('$'), l:formatted)
call codefmt#formatterhelpers#Format(maktaba#syscall#Create(l:cmd))
catch /ERROR(ShellError):/
" Parse all the errors and stick them in the quickfix list.
let l:errors = []
Expand Down
20 changes: 6 additions & 14 deletions autoload/codefmt/shfmt.vim
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,14 @@ function! codefmt#shfmt#GetFormatter() abort
\ 'shfmt_options flag must be list or callable. Found %s',
\ string(l:Shfmt_options))
endif
" Hack range formatting by formatting range individually, ignoring context.
" Feature request for range formatting:
" https://github.com/mvdan/sh/issues/333
let l:cmd = [ s:plugin.Flag('shfmt_executable') ] + l:shfmt_options
call maktaba#ensure#IsNumber(a:startline)
call maktaba#ensure#IsNumber(a:endline)
let l:lines = getline(1, line('$'))
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
try
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []

let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
" Feature request for range formatting:
" https://github.com/mvdan/sh/issues/333
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
\ a:startline,
\ a:endline,
\ maktaba#syscall#Create(l:cmd))
catch /ERROR(ShellError):/
" Parse all the errors and stick them in the quickfix list.
let l:errors = []
Expand Down
30 changes: 11 additions & 19 deletions autoload/codefmt/zprint.vim
Original file line number Diff line number Diff line change
Expand Up @@ -63,28 +63,20 @@ function! codefmt#zprint#GetFormatter() abort
\ 'zprint_options flag must be list or callable. Found %s',
\ string(l:ZprintOptions))
endif
let l:cmd = [s:plugin.Flag('zprint_executable')]
call extend(l:cmd, l:zprint_options)

call maktaba#ensure#IsNumber(a:startline)
call maktaba#ensure#IsNumber(a:endline)
let l:lines = getline(1, line('$'))

" zprint doesn't support formatting a range of lines, so format the range
" individually, ignoring context. This works well for top-level forms, although it's
" not ideal for inner forms because it loses the indentation.
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
let l:cmd_args = [s:plugin.Flag('zprint_executable')]
call extend(l:cmd_args, l:zprint_options)

" Prepare the syscall, changing to the containing directory in case the user
" has configured {:search-config? true} in ~/.zprintrc
let l:result = maktaba#syscall#Create(l:cmd).WithCwd(expand('%:p:h')).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")

" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]

call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
let l:cmd = maktaba#syscall#Create(l:cmd_args).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
" not ideal for inner forms because it loses the indentation.
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
\ a:startline,
\ a:endline,
\ l:cmd)
endfunction

return l:formatter
Expand Down
20 changes: 20 additions & 0 deletions doc/codefmt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,26 @@ codefmt#FormatMap({type}) *codefmt#FormatMap()*
Suitable for use as 'operatorfunc'; see |g@| for details. The type is
ignored since formatting only works on complete lines.

codefmt#formatterhelpers#Format({cmd}) *codefmt#formatterhelpers#Format()*
Format lines in the current buffer via a formatter invoked by {cmd} (a
|maktaba.Syscall|). The command includes the explicit range line numbers to
use, if any.

Throws ERROR(ShellError) if the {cmd} system call fails

codefmt#formatterhelpers#AttemptFakeRangeFormatting({startline}, {endline},
{cmd}) *codefmt#formatterhelpers#AttemptFakeRangeFormatting()*
Attempt to format a range of lines from {startline} to {endline} in the
current buffer via a formatter that doesn't natively support range
formatting (invoked by {cmd}, a |maktaba.Syscall|), using a hacky strategy
of sending those lines to the formatter in isolation.

If invoking this hack, please make sure to file a feature request against
the tool for range formatting and post a URL for that feature request above
code that calls it.

Throws ERROR(ShellError) if the {cmd} system call fails

==============================================================================
MAPPINGS *codefmt-mappings*

Expand Down