Skip to content

Commit 5a198e2

Browse files
authored
Merge pull request #107 from infokiller/master
Add shfmt.
2 parents 18fe8fb + f40a68f commit 5a198e2

File tree

5 files changed

+209
-0
lines changed

5 files changed

+209
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ helpfiles in the `doc/` directory. The helpfiles are also available via
2424
* Proto (clang-format)
2525
* Python (Autopep8 or YAPF)
2626
* TypeScript (clang-format)
27+
* Shell (shfmt)
2728

2829
# Commands
2930

autoload/codefmt/shfmt.vim

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
" Copyright 2017 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+
let s:plugin = maktaba#plugin#Get('codefmt')
17+
18+
19+
""
20+
" @private
21+
" Formatter: shfmt
22+
function! codefmt#shfmt#GetFormatter() abort
23+
let l:formatter = {
24+
\ 'name': 'shfmt',
25+
\ 'setup_instructions': 'Install shfmt (https://github.com/mvdan/sh) ' .
26+
\ 'and configure the shfmt_executable flag'}
27+
28+
function l:formatter.IsAvailable() abort
29+
return executable(s:plugin.Flag('shfmt_executable'))
30+
endfunction
31+
32+
function l:formatter.AppliesToBuffer() abort
33+
return &filetype is# 'sh'
34+
endfunction
35+
36+
""
37+
" Reformat the current buffer with shfmt or the binary named in
38+
" @flag(shfmt_executable), only targeting the range between {startline} and
39+
" {endline}.
40+
function l:formatter.FormatRange(startline, endline) abort
41+
let l:Shfmt_options = s:plugin.Flag('shfmt_options')
42+
if type(l:Shfmt_options) is# type([])
43+
let l:shfmt_options = l:Shfmt_options
44+
elseif maktaba#value#IsCallable(l:Shfmt_options)
45+
let l:shfmt_options = maktaba#function#Call(l:Shfmt_options)
46+
else
47+
throw maktaba#error#WrongType(
48+
\ 'shfmt_options flag must be list or callable. Found %s',
49+
\ string(l:Shfmt_options))
50+
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
54+
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")
59+
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)
67+
catch /ERROR(ShellError):/
68+
" Parse all the errors and stick them in the quickfix list.
69+
let l:errors = []
70+
for l:line in split(v:exception, "\n")
71+
let l:tokens = matchlist(l:line,
72+
\ '\C\v^\<standard input\>:(\d+):(\d+):\s*(.*)')
73+
if !empty(l:tokens)
74+
call add(l:errors, {
75+
\ 'filename': @%,
76+
\ 'lnum': l:tokens[1] + a:startline - 1,
77+
\ 'col': l:tokens[2],
78+
\ 'text': l:tokens[3]})
79+
endif
80+
endfor
81+
82+
if empty(l:errors)
83+
" Couldn't parse shfmt error format; display it all.
84+
call maktaba#error#Shout('Error formatting file: %s', v:exception)
85+
else
86+
call setqflist(l:errors, 'r')
87+
cc 1
88+
endif
89+
endtry
90+
endfunction
91+
92+
return l:formatter
93+
endfunction

instant/flags.vim

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,14 @@ call s:plugin.Flag('buildifier_executable', 'buildifier')
9393
" form:
9494
" `java -jar /path/to/google-java`
9595
call s:plugin.Flag('google_java_executable', 'google-java-format')
96+
97+
""
98+
" Command line arguments to to feed shfmt. Either a list or callable that
99+
" takes no args and returns a list with command line arguments. By default, uses
100+
" the Google's style.
101+
" See https://github.com/mvdan/sh for details.
102+
call s:plugin.Flag('shfmt_options', ['-i', '2', '-sr', '-ci'])
103+
104+
""
105+
" The path to the shfmt executable.
106+
call s:plugin.Flag('shfmt_executable', 'shfmt')

plugin/register.vim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ call s:registry.AddExtension(codefmt#autopep8#GetFormatter())
3030
call s:registry.AddExtension(codefmt#gn#GetFormatter())
3131
call s:registry.AddExtension(codefmt#buildifier#GetFormatter())
3232
call s:registry.AddExtension(codefmt#googlejava#GetFormatter())
33+
call s:registry.AddExtension(codefmt#shfmt#GetFormatter())

vroom/shfmt.vroom

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
The built-in shfmt formatter knows how to format shell code (POSIX/Bash/mksh).
2+
If you aren't familiar with basic codefmt usage yet, see main.vroom first.
3+
4+
We'll set up codefmt and configure the vroom environment, then jump into some
5+
examples.
6+
7+
:source $VROOMDIR/setupvroom.vim
8+
9+
:let g:repeat_calls = []
10+
:function FakeRepeat(...)<CR>
11+
| call add(g:repeat_calls, a:000)<CR>
12+
:endfunction
13+
:call maktaba#test#Override('repeat#set', 'FakeRepeat')
14+
15+
:call codefmt#SetWhetherToPerformIsAvailableChecksForTesting(0)
16+
17+
18+
The shfmt formatter expects the shfmt executable to be installed on your system.
19+
20+
% f()
21+
:FormatCode shfmt
22+
! shfmt .*
23+
$ f()
24+
25+
The name or path of the shfmt executable can be configured via the
26+
shfmt_executable flag if the default of "shfmt" doesn't work.
27+
28+
:Glaive codefmt shfmt_executable='myshfmt'
29+
:FormatCode shfmt
30+
! myshfmt .*
31+
$ f()
32+
:Glaive codefmt shfmt_executable='shfmt'
33+
34+
35+
You can format any buffer with shfmt specifying the formatter explicitly.
36+
37+
@clear
38+
% if [ $print_hello_world -eq 1 ]; then echo "hello"; echo "world"; fi
39+
40+
:FormatCode shfmt
41+
! shfmt .*2>.*
42+
$ if [ $print_hello_world -eq 1 ]; then
43+
$ echo "hello"
44+
$ echo "world"
45+
$ fi
46+
if [ $print_hello_world -eq 1 ]; then
47+
echo "hello"
48+
echo "world"
49+
fi
50+
@end
51+
52+
Errors are reported using the quickfix list.
53+
54+
@clear
55+
% foo() {
56+
57+
:FormatCode shfmt
58+
! shfmt .*2> (.*)
59+
$ echo '<standard input>:1:7: reached EOF without matching { with }' >\1 (command)
60+
$ 1 (status)
61+
foo() {
62+
@end
63+
:echomsg line('.') . ',' . col('.')
64+
~ 1,7
65+
:echomsg string(map(getqflist(),
66+
|'v:val.lnum . "," . v:val.col . "," . v:val.text'))
67+
~ ['1,7,reached EOF without matching { with }']
68+
69+
70+
The sh filetype will use the shfmt formatter by default.
71+
72+
@clear
73+
% f()
74+
75+
:set filetype=sh
76+
:FormatCode
77+
! shfmt .*
78+
$ f()
79+
80+
:set filetype=
81+
82+
It can format specific line ranges of code using :FormatLines.
83+
84+
@clear
85+
% foo() { echo "my name is:"; echo "foo"; }<CR>
86+
|bar() { echo "my name is:"; echo "bar"; }
87+
88+
:1,2FormatLines shfmt
89+
! shfmt .*2>.*
90+
$ foo() {
91+
$ echo "my name is:"
92+
$ echo "foo"
93+
$ }
94+
$ bar() { echo "my name is:"; echo "bar"; }
95+
foo() {
96+
echo "my name is:"
97+
echo "foo"
98+
}
99+
bar() { echo "my name is:"; echo "bar"; }
100+
@end
101+
102+
NOTE: the shfmt formatter does not natively support range formatting, so there
103+
are certain limitations like not being able to format misaligned braces.

0 commit comments

Comments
 (0)