Skip to content

Commit

Permalink
cmd/shfmt: support editorconfig language sections
Browse files Browse the repository at this point in the history
That is, support `[[shell]]` EditorConfig sections when formatting
any shell file, and `[[bash]]` when formatting Bash files.

This is mainly helpful when formatting files with a shebang
but no extension, as then the formatter does use the right language
when parsing and formatting the code, but EditorConfig doesn't kick in
as it only knows how to match by filename per the spec.

This feature is out of the EditorConfig spec,
and upstream is tracking a feature like this at
editorconfig/editorconfig#404 since 2019.
Due to the lack of progress upstream, and how useful this feature
is for shfmt, go ahead and add it ourselves,
with a note that we may change or remove it at any time.

Fixes #664.
  • Loading branch information
mvdan committed Dec 28, 2023
1 parent 3384f3d commit 7f96e7d
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 5 deletions.
22 changes: 20 additions & 2 deletions cmd/shfmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,11 @@ func walkPath(path string, entry fs.DirEntry) error {
return filepath.SkipDir
}
if useEditorConfig {
props, err := ecQuery.Find(path)
// We don't know the language variant at this point yet, as we are walking directories
// and we first want to tell if we should skip a path entirely.
// TODO: Should the call to Find with the language name check "ignore" too, then?
// Otherwise a [[bash]] section with ignore=true is effectively never used.
props, err := ecQuery.Find(path, []string{"shell"})
if err != nil {
return err
}
Expand Down Expand Up @@ -420,9 +424,23 @@ func formatPath(path string, checkShebang bool) error {
return formatBytes(readBuf.Bytes(), path, fileLang)
}

func editorConfigLangs(l syntax.LangVariant) []string {
// All known shells match [[shell]].
// As a special case, bash and the bash-like bats also match [[bash]]
// We can later consider others like [[mksh]] or [[posix-shell]],
// just consider what list of languages the EditorConfig spec might eventually use.
switch l {
case syntax.LangBash, syntax.LangBats:
return []string{"shell", "bash"}
case syntax.LangPOSIX, syntax.LangMirBSDKorn, syntax.LangAuto:
return []string{"shell"}
}
return nil
}

func formatBytes(src []byte, path string, fileLang syntax.LangVariant) error {
if useEditorConfig {
props, err := ecQuery.Find(path)
props, err := ecQuery.Find(path, editorConfigLangs(fileLang))
if err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions cmd/shfmt/shfmt.1.scd
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ function_next_line = true
ignore = true
```

EditorConfig sections may also use `[[shell]]` or `[[bash]]` to match any shell or bash scripts,
which is particularly useful when scripts use a shebang but no extension.
Note that this feature is outside of the EditorConfig spec and may be changed in the future.

shfmt can also replace *bash -n* to check shell scripts for syntax errors. It is
more exhaustive, as it parses all syntax statically and requires valid UTF-8:

Expand Down
31 changes: 31 additions & 0 deletions cmd/shfmt/testdata/script/editorconfig.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ stdout 'regular\.sh'
! stdout 'ignored\.sh'
! stderr .

# Check EditorConfig [[language]] sections, used primarily for extension-less strings with shebangs.
exec shfmt -d shebang
! stdout .
! stderr .

-- .editorconfig --
root = true

Expand Down Expand Up @@ -156,3 +161,29 @@ echo foo
echo foo
-- ignored/3_regular/regular.sh --
echo foo
-- shebang/.editorconfig --
root = true

[*]
indent_style = space
indent_size = 1

[[shell]]
indent_size = 2

[[bash]]
indent_size = 4

-- shebang/binsh --
#!/bin/sh

{
indented
}
-- shebang/binbash --
#!/bin/bash

{
indented
}
array=(elem)
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
golang.org/x/sync v0.5.0
golang.org/x/sys v0.15.0
golang.org/x/term v0.15.0
mvdan.cc/editorconfig v0.2.0
mvdan.cc/editorconfig v0.2.1-0.20231228180347-1925077f8eb2
)

require (
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
mvdan.cc/editorconfig v0.2.0 h1:XL+7ys6ls/RKrkUNFQvEwIvNHh+JKx8Mj1pUV5wQxQE=
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
mvdan.cc/editorconfig v0.2.1-0.20231227223507-ad7477e883b4 h1:SUMDYCoRUx5kuz3pkNWvsvpO0b4gN4pxXll47TsWHe0=
mvdan.cc/editorconfig v0.2.1-0.20231227223507-ad7477e883b4/go.mod h1:r8RiQJRtzrPrZdcdEs5VCMqvRxAzYDUu9a4S9z7fKh8=
mvdan.cc/editorconfig v0.2.1-0.20231228180347-1925077f8eb2 h1:8nmqQGVnHUtHuT+yvuA49lQK0y5il5IOr2PtCBkDI2M=
mvdan.cc/editorconfig v0.2.1-0.20231228180347-1925077f8eb2/go.mod h1:r8RiQJRtzrPrZdcdEs5VCMqvRxAzYDUu9a4S9z7fKh8=

0 comments on commit 7f96e7d

Please sign in to comment.