Skip to content

Commit 024911c

Browse files
authored
Implement common helper functions for common formatting operations (#135)
Creates helper functions for the boilerplate of common operations in formatter implementations: `codefmt#formatterhelpers#Format({cmd})` and `codefmt#formatterhelpers#AttemptFakeRangeFormatting({startline}, {endline}, {cmd})`. These make conventions more obvious and help any intentional differences from the most common boilerplate version stand out more obviously.
1 parent 037707f commit 024911c

11 files changed

+133
-103
lines changed

autoload/codefmt/autopep8.vim

+8-19
Original file line numberDiff line numberDiff line change
@@ -67,29 +67,18 @@ function! codefmt#autopep8#GetFormatter() abort
6767

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

7271
if s:autopep8_supports_range
73-
let l:cmd = [l:executable, '--range', ''.a:startline, ''.a:endline, '-']
74-
let l:input = join(l:lines, "\n")
72+
call codefmt#formatterhelpers#Format([
73+
\ l:executable,
74+
\ '--range', string(a:startline), string(a:endline),
75+
\ '-'])
7576
else
76-
let l:cmd = [l:executable, '-']
77-
" Hack range formatting by formatting range individually, ignoring context.
78-
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
77+
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
78+
\ a:startline,
79+
\ a:endline,
80+
\ [l:executable, '-'])
7981
endif
80-
81-
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
82-
let l:formatted = split(l:result.stdout, "\n")
83-
84-
if s:autopep8_supports_range
85-
let l:full_formatted = l:formatted
86-
else
87-
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
88-
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
89-
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
90-
endif
91-
92-
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
9382
endfunction
9483

9584
return l:formatter

autoload/codefmt/buildifier.vim

+3-4
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,10 @@ function! codefmt#buildifier#GetFormatter() abort
4545
let l:cmd += ['-path', l:fname]
4646
endif
4747

48-
let l:input = join(getline(1, line('$')), "\n")
4948
try
50-
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
51-
let l:formatted = split(l:result.stdout, "\n")
52-
call maktaba#buffer#Overwrite(1, line('$'), l:formatted)
49+
" NOTE: Ignores any line ranges given and formats entire buffer.
50+
" buildifier does not support range formatting.
51+
call codefmt#formatterhelpers#Format(l:cmd)
5352
catch
5453
" Parse all the errors and stick them in the quickfix list.
5554
let l:errors = []

autoload/codefmt/dartfmt.vim

+6-15
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,12 @@ function! codefmt#dartfmt#GetFormatter() abort
3838
" @flag(dartfmt_executable}, only targetting the range from {startline} to
3939
" {endline}
4040
function l:formatter.FormatRange(startline, endline) abort
41-
" Hack range formatting by formatting range individually, ignoring context.
4241
let l:cmd = [ s:plugin.Flag('dartfmt_executable') ]
43-
" TODO When https://github.com/dart-lang/dart_style/issues/92 is implemented
44-
" use those options.
45-
call maktaba#ensure#IsNumber(a:startline)
46-
call maktaba#ensure#IsNumber(a:endline)
47-
let l:lines = getline(1, line('$'))
48-
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
4942
try
50-
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
51-
let l:formatted = split(l:result.stdout, "\n")
52-
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
53-
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
54-
55-
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
56-
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
43+
" dartfmt does not support range formatting yet:
44+
" https://github.com/dart-lang/dart_style/issues/92
45+
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
46+
\ a:startline, a:endline, l:cmd)
5747
catch /ERROR(ShellError):/
5848
" Parse all the errors and stick them in the quickfix list.
5949
let l:errors = []
@@ -71,7 +61,8 @@ function! codefmt#dartfmt#GetFormatter() abort
7161

7262
if empty(l:errors)
7363
" Couldn't parse dartfmt error format; display it all.
74-
call maktaba#error#Shout('Failed to format range; showing all errors: %s', v:exception)
64+
call maktaba#error#Shout(
65+
\ 'Failed to format range; showing all errors: %s', v:exception)
7566
else
7667
let l:errorHeaderLines = split(v:exception, "\n")[1 : 5]
7768
let l:errorHeader = join(l:errorHeaderLines, "\n")

autoload/codefmt/formatterhelpers.vim

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
" Copyright 2020 Google Inc. All rights reserved.
2+
"
3+
" Licensed under the Apache License, Version 2.0 (the "License");
4+
" you may not use this file except in compliance with the License.
5+
" You may obtain a copy of the License at
6+
"
7+
" http://www.apache.org/licenses/LICENSE-2.0
8+
"
9+
" Unless required by applicable law or agreed to in writing, software
10+
" distributed under the License is distributed on an "AS IS" BASIS,
11+
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
" See the License for the specific language governing permissions and
13+
" limitations under the License.
14+
15+
16+
""
17+
" @public
18+
" Format lines in the current buffer via a formatter invoked by {cmd}, which
19+
" is a system call represented by either a |maktaba.Syscall| or any argument
20+
" accepted by |maktaba#syscall#Create()|. The command must include any
21+
" arguments for the explicit range line numbers to use, if any.
22+
"
23+
" @throws ShellError if the {cmd} system call fails
24+
function! codefmt#formatterhelpers#Format(cmd) abort
25+
let l:lines = getline(1, line('$'))
26+
let l:input = join(l:lines, "\n")
27+
28+
let l:result = maktaba#syscall#Create(a:cmd).WithStdin(l:input).Call()
29+
let l:formatted = split(l:result.stdout, "\n")
30+
31+
call maktaba#buffer#Overwrite(1, line('$'), l:formatted)
32+
endfunction
33+
34+
""
35+
" @public
36+
" Attempt to format a range of lines from {startline} to {endline} in the
37+
" current buffer via a formatter that doesn't natively support range
38+
" formatting, which is invoked via {cmd} (a system call represented by either
39+
" a |maktaba.Syscall| or any argument accepted by |maktaba#syscall#Create()|).
40+
" It uses a hacky strategy of sending those lines to the formatter in
41+
" isolation, which gives bad results if the code on those lines isn't
42+
" a self-contained block of syntax or is part of a larger indent.
43+
"
44+
" If invoking this hack, please make sure to file a feature request against
45+
" the tool for range formatting and post a URL for that feature request above
46+
" code that calls it.
47+
"
48+
" @throws ShellError if the {cmd} system call fails
49+
function! codefmt#formatterhelpers#AttemptFakeRangeFormatting(
50+
\ startline, endline, cmd) abort
51+
call maktaba#ensure#IsNumber(a:startline)
52+
call maktaba#ensure#IsNumber(a:endline)
53+
54+
let l:lines = getline(1, line('$'))
55+
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
56+
57+
let l:result = maktaba#syscall#Create(a:cmd).WithStdin(l:input).Call()
58+
let l:formatted = split(l:result.stdout, "\n")
59+
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
60+
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
61+
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
62+
63+
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
64+
endfunction

autoload/codefmt/gofmt.vim

+4-12
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,12 @@ function! codefmt#gofmt#GetFormatter() abort
3838
" @flag(gofmt_executable), only targeting the range between {startline} and
3939
" {endline}.
4040
function l:formatter.FormatRange(startline, endline) abort
41-
" Hack range formatting by formatting range individually, ignoring context.
4241
let l:cmd = [ s:plugin.Flag('gofmt_executable') ]
43-
call maktaba#ensure#IsNumber(a:startline)
44-
call maktaba#ensure#IsNumber(a:endline)
45-
let l:lines = getline(1, line('$'))
46-
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
4742
try
48-
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
49-
let l:formatted = split(l:result.stdout, "\n")
50-
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
51-
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
52-
53-
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
54-
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
43+
" gofmt does not support range formatting.
44+
" TODO: File a feature request with gofmt and link it here.
45+
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
46+
\ a:startline, a:endline, l:cmd)
5547
catch /ERROR(ShellError):/
5648
" Parse all the errors and stick them in the quickfix list.
5749
let l:errors = []

autoload/codefmt/googlejava.vim

+1-4
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,7 @@ function! codefmt#googlejava#GetFormatter() abort
5959
let l:ranges_str = join(map(copy(a:ranges), 'v:val[0] . ":" . v:val[1]'), ',')
6060
let l:cmd += ['--lines', l:ranges_str, '-']
6161

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

6865
return l:formatter

autoload/codefmt/jsbeautify.vim

+4-11
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,10 @@ function! codefmt#jsbeautify#GetFormatter() abort
5252
call maktaba#ensure#IsNumber(a:startline)
5353
call maktaba#ensure#IsNumber(a:endline)
5454

55-
let l:lines = getline(1, line('$'))
56-
" Hack range formatting by formatting range individually, ignoring context.
57-
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
58-
59-
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
60-
let l:formatted = split(l:result.stdout, "\n")
61-
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
62-
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
63-
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
64-
65-
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
55+
" js-beautify does not support range formatting yet:
56+
" https://github.com/beautify-web/js-beautify/issues/610
57+
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
58+
\ a:startline, a:endline, l:cmd)
6659
endfunction
6760

6861
return l:formatter

autoload/codefmt/rustfmt.vim

+2-5
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,10 @@ function! codefmt#rustfmt#GetFormatter() abort
5252

5353
call extend(l:cmd, l:rustfmt_options)
5454
try
55-
let l:lines = getline(1, line('$'))
56-
let l:input = join(l:lines, "\n")
57-
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
58-
let l:formatted = split(l:result.stdout, "\n")
55+
" NOTE: Ignores any line ranges given and formats entire buffer.
5956
" Even though rustfmt supports formatting ranges through the --file-lines
6057
" flag, it is not still enabled in the stable binaries.
61-
call maktaba#buffer#Overwrite(1, line('$'), l:formatted)
58+
call codefmt#formatterhelpers#Format(l:cmd)
6259
catch /ERROR(ShellError):/
6360
" Parse all the errors and stick them in the quickfix list.
6461
let l:errors = []

autoload/codefmt/shfmt.vim

+6-14
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,14 @@ function! codefmt#shfmt#GetFormatter() abort
4848
\ 'shfmt_options flag must be list or callable. Found %s',
4949
\ string(l:Shfmt_options))
5050
endif
51-
" Hack range formatting by formatting range individually, ignoring context.
52-
" Feature request for range formatting:
53-
" https://github.com/mvdan/sh/issues/333
5451
let l:cmd = [ s:plugin.Flag('shfmt_executable') ] + l:shfmt_options
55-
call maktaba#ensure#IsNumber(a:startline)
56-
call maktaba#ensure#IsNumber(a:endline)
57-
let l:lines = getline(1, line('$'))
58-
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
5952
try
60-
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
61-
let l:formatted = split(l:result.stdout, "\n")
62-
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
63-
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
64-
65-
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
66-
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
53+
" Feature request for range formatting:
54+
" https://github.com/mvdan/sh/issues/333
55+
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
56+
\ a:startline,
57+
\ a:endline,
58+
\ l:cmd)
6759
catch /ERROR(ShellError):/
6860
" Parse all the errors and stick them in the quickfix list.
6961
let l:errors = []

autoload/codefmt/zprint.vim

+11-19
Original file line numberDiff line numberDiff line change
@@ -63,28 +63,20 @@ function! codefmt#zprint#GetFormatter() abort
6363
\ 'zprint_options flag must be list or callable. Found %s',
6464
\ string(l:ZprintOptions))
6565
endif
66-
let l:cmd = [s:plugin.Flag('zprint_executable')]
67-
call extend(l:cmd, l:zprint_options)
68-
69-
call maktaba#ensure#IsNumber(a:startline)
70-
call maktaba#ensure#IsNumber(a:endline)
71-
let l:lines = getline(1, line('$'))
72-
73-
" zprint doesn't support formatting a range of lines, so format the range
74-
" individually, ignoring context. This works well for top-level forms, although it's
75-
" not ideal for inner forms because it loses the indentation.
76-
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
66+
let l:cmd_args = [s:plugin.Flag('zprint_executable')]
67+
call extend(l:cmd_args, l:zprint_options)
7768

7869
" Prepare the syscall, changing to the containing directory in case the user
7970
" has configured {:search-config? true} in ~/.zprintrc
80-
let l:result = maktaba#syscall#Create(l:cmd).WithCwd(expand('%:p:h')).WithStdin(l:input).Call()
81-
let l:formatted = split(l:result.stdout, "\n")
82-
83-
" Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right.
84-
let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : []
85-
let l:full_formatted = l:before + l:formatted + l:lines[a:endline :]
86-
87-
call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted)
71+
let l:cmd = maktaba#syscall#Create(l:cmd_args).WithCwd(expand('%:p:h'))
72+
" zprint does not support range formatting yet:
73+
" https://github.com/kkinnear/zprint/issues/122
74+
" This fake range formatting works well for top-level forms, although it's
75+
" not ideal for inner forms because it loses the indentation.
76+
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
77+
\ a:startline,
78+
\ a:endline,
79+
\ l:cmd)
8880
endfunction
8981

9082
return l:formatter

doc/codefmt.txt

+24
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,30 @@ codefmt#FormatMap({type}) *codefmt#FormatMap()*
225225
Suitable for use as 'operatorfunc'; see |g@| for details. The type is
226226
ignored since formatting only works on complete lines.
227227

228+
codefmt#formatterhelpers#Format({cmd}) *codefmt#formatterhelpers#Format()*
229+
Format lines in the current buffer via a formatter invoked by {cmd}, which
230+
is a system call represented by either a |maktaba.Syscall| or any argument
231+
accepted by |maktaba#syscall#Create()|. The command must include any
232+
arguments for the explicit range line numbers to use, if any.
233+
234+
Throws ERROR(ShellError) if the {cmd} system call fails
235+
236+
codefmt#formatterhelpers#AttemptFakeRangeFormatting({startline}, {endline},
237+
{cmd}) *codefmt#formatterhelpers#AttemptFakeRangeFormatting()*
238+
Attempt to format a range of lines from {startline} to {endline} in the
239+
current buffer via a formatter that doesn't natively support range
240+
formatting, which is invoked via {cmd} (a system call represented by either
241+
a |maktaba.Syscall| or any argument accepted by |maktaba#syscall#Create()|).
242+
It uses a hacky strategy of sending those lines to the formatter in
243+
isolation, which gives bad results if the code on those lines isn't a
244+
self-contained block of syntax or is part of a larger indent.
245+
246+
If invoking this hack, please make sure to file a feature request against
247+
the tool for range formatting and post a URL for that feature request above
248+
code that calls it.
249+
250+
Throws ERROR(ShellError) if the {cmd} system call fails
251+
228252
==============================================================================
229253
MAPPINGS *codefmt-mappings*
230254

0 commit comments

Comments
 (0)