Skip to content

Linter checks formatting of paired brackets

License

Notifications You must be signed in to change notification settings

maratori/pairedbrackets

Repository files navigation

pairedbrackets
go minimal version go tested version CI Codecov Codebeat Maintainability Go Report Card License Go Reference

Linter checks formatting of paired brackets (inspired by this article).

Rule

According to the original notation, "a bracket should either start/end a line or be paired on the same line".
With modification for multiline items, the following cases are allowed:

  1. Both brackets and all items are in one line.
    fmt.Printf("%s, %s!", "Hello", "world")
  2. Left (opening) bracket is the last character on a line and right (closing) bracket starts a new line.
    fmt.Printf( // comments and whitespaces are ignored
    	"%s, %s!", "Hello", "world",
    )
  3. If the last item is multiline, it can start on the same line with the left bracket.
    In this case, the right bracket should be on the same line where the last item ends.
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    	...
    })

Linter reports (wordings):

x.go:1:16: left parenthesis should either be the last character on a line or be on the same line with the last argument

http.HandleFunc("/",
	func(w http.ResponseWriter, r *http.Request) {
		...
	},
)

x.go:4:1: right parenthesis should be on the previous line

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	...
},
)
⬆

x.go:5:3: right parenthesis should be on the next line

http.HandleFunc(
	"/",
	func(w http.ResponseWriter, r *http.Request) {
		...
	})
	 ⬆

Examples

Function/method call
BadGood
fmt.Printf("%s, %s!",
	"Hello", "world")
fmt.Printf("%s, %s!",
	"Hello", "world",
)
fmt.Printf(
	"%s, %s!",
	"Hello", "world")
fmt.Printf("%s %s", "Last", `item
is multiline`,
)
fmt.Printf("%s, %s!", "Hello", "world")
fmt.Printf(
	"%s, %s!", "Hello", "world",
)
fmt.Printf(
	"%s, %s!",
	"Hello", "world",
)
fmt.Printf("%s %s", "Last", `item
is multiline`)
Composite literal
BadGood
foo := []int{1,
	2, 3}
foo := []int{1,
	2, 3,
}
foo := []int{
	1,
	2,
	3}
foo := []string{"Last", "item", `is
multiline`,
}
bar := []int{1, 2, 3}
bar := []int{
	1,
	2,
	3,
}
bar := []int{
	1, 2, 3,
}
bar := []string{"Last", "item", `is
multiline`}
Function parameters
BadGood
func Foo(a int,
	b string, c bool) {
	...
}
func Foo(a int,
	b string, c bool,
) {
	...
}
func Foo(
	a int,
	b string,
	c bool) {
	...
}
func Foo(a int, b string,
) {
	...
}
func Foo(a int, b struct {
	X int
	Y string
},
) {
	...
}
func Bar(a int, b string) {
	...
}
func Bar(
	a int,
	b string,
	c bool,
) {
	...
}
func Bar(
	a int, b string, c bool,
) {
	...
}
func Bar(a int, b struct {
	X int
	Y string
}) {
	...
}
Function type parameters (generics)
BadGood
func Foo[T int,
	V string]() {
	...
}
func Foo[T int,
	V string,
]() {
	...
}
func Foo[
	T int,
	V string]() {
	...
}
func Foo[T int, V string,
]() {
	...
}
func Foo[T int, V interface {
	int | string
},
]() {
	...
}
func Bar[T int, V string]() {
	...
}
func Bar[
	T int,
	V string,
]() {
	...
}
func Bar[
	T int, V string,
]() {
	...
}
func Bar[T int, V interface {
	int | string
}]() {
	...
}
Function returns (output parameters)
BadGood
func Foo() (int,
	error) {
	...
}
func Foo() (int,
	error,
) {
	...
}
func Foo() (
	int,
	error) {
	...
}
func Foo() (int, error,
) {
	...
}
func Foo() (int, interface {
	Error()
},
) {
	...
}
func Bar() (int, error) {
	...
}
func Bar() (
	int,
	error,
) {
	...
}
func Bar() (
	int, error,
) {
	...
}
func Bar() (int, interface {
	Error()
}) {
	...
}

Other tools

gofmt

gofmt fixes many cases, which pairedbrackets complains about. But not all of them. All examples above formatted correctly according to gofmt.

gofumpt

gofumpt is just a slightly better than gofmt. It fixes some composite literal examples above. But not all of them, and it doesn't fix other examples.

Usage

You can use golangci-lint.
Unfortunately, v1 was rejected to be a built-in linter (hopefully v2 will be accepted). You can configure pairedbrackets as a plugin.

Install golangci-lint

Prebuilt binaries doesn't support plugins (see discussion), so you have to build golangci-lint:

go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

Install pairedbrackets

go install github.com/maratori/pairedbrackets@latest

Build plugin

pairedbrackets -build-golangci-lint-plugin

pairedbrackets.so will be created in current working directory. You can change the output path with flag -plugin-output (see other flags in help as well).

Config

pairedbrackets is disabled by default.
To enable it, add the following to your .golangci.yml:

linters-settings:
  custom:
     pairedbrackets:
        path: /path/to/plugin/pairedbrackets.so
        description: The linter checks formatting of paired brackets
        original-url: github.com/maratori/pairedbrackets
linters:
  enable:
     pairedbrackets

Run

golangci-lint run

Usage as standalone linter

Install

go install github.com/maratori/pairedbrackets@latest

Run

pairedbrackets ./...

License

MIT License