Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
21 changes: 14 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ var (
shortenComments = kingpin.Flag(
"shorten-comments",
"Shorten single-line comments").Default("false").Bool()
funcParamThreshold = kingpin.Flag(
"func-param-threshold",
"Expand function signatures to one-param-per-line when they have this many or more parameters "+
"(0 to disable)",
).
Default("0").Int()
tabLen = kingpin.Flag(
"tab-len",
"Length of a tab").Short('t').Default("4").Int()
Expand Down Expand Up @@ -151,13 +157,14 @@ type Runner struct {

func NewRunner() *Runner {
config := &shorten.Config{
MaxLen: deref(maxLen),
TabLen: deref(tabLen),
KeepAnnotations: deref(keepAnnotations),
ShortenComments: deref(shortenComments),
ReformatTags: deref(reformatTags),
DotFile: deref(dotFile),
ChainSplitDots: deref(chainSplitDots),
MaxLen: deref(maxLen),
TabLen: deref(tabLen),
KeepAnnotations: deref(keepAnnotations),
ShortenComments: deref(shortenComments),
ReformatTags: deref(reformatTags),
DotFile: deref(dotFile),
ChainSplitDots: deref(chainSplitDots),
FuncParamThreshold: deref(funcParamThreshold),
}

return &Runner{
Expand Down
12 changes: 9 additions & 3 deletions shorten/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ func (s *Shortener) formatNode(node dst.Node) {
func (s *Shortener) formatDecl(decl dst.Decl) {
switch d := decl.(type) {
case *dst.FuncDecl:
if d.Type != nil && d.Type.Params != nil && annotation.HasRecursive(d) {
if d.Type != nil && d.Type.Params != nil &&
(annotation.HasRecursive(d) || s.shouldExpandParams(d.Type.Params)) {
s.formatFieldList(d.Type.Params)
}

Expand Down Expand Up @@ -233,16 +234,21 @@ func (s *Shortener) formatExpr(expr dst.Expr, force, isChain bool) {
s.formatExprs(e.Elts, false, isChain)

case *dst.FuncLit:
if e.Type != nil && s.shouldExpandParams(e.Type.Params) {
s.formatFieldList(e.Type.Params)
}

s.formatStmt(e.Body, false)

case *dst.FuncType:
if shouldShorten {
if shouldShorten || s.shouldExpandParams(e.Params) {
s.formatFieldList(e.Params)
}

case *dst.InterfaceType:
for _, method := range e.Methods.List {
if annotation.Has(method) {
funcType, isFuncType := method.Type.(*dst.FuncType)
if annotation.Has(method) || (isFuncType && s.shouldExpandParams(funcType.Params)) {
s.formatExpr(method.Type, true, isChain)
}
}
Expand Down
33 changes: 31 additions & 2 deletions shorten/shortener.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ type Config struct {

// ChainSplitDots Whether to split chain methods by putting dots at the ends of lines
ChainSplitDots bool

// FuncParamThreshold triggers multi-line expansion of function signatures
// with this many or more parameters, regardless of line length.
// Set to 0 (default) to disable.
FuncParamThreshold int
}

// NewDefaultConfig returns a [Config] with default values.
Expand Down Expand Up @@ -186,10 +191,34 @@ func (s *Shortener) Process(content []byte) ([]byte, error) {
// if there are lines to shorten,
// or if this is the first round (0),
// and the option to reformat struct tags is enabled,
// and there are struct tags with multiple entries.
// and there are struct tags with multiple entries,
// or if this is the first round (0),
// and func param threshold expansion is enabled.
func (s *Shortener) shouldContinue(nbLinesToShorten, round int, lines []string) bool {
return nbLinesToShorten > 0 ||
round == 0 && s.config.ReformatTags && tags.HasMultipleEntries(lines)
round == 0 && s.config.ReformatTags && tags.HasMultipleEntries(lines) ||
round == 0 && s.config.FuncParamThreshold > 0
}

// shouldExpandParams reports whether a field list has enough parameters
// to trigger multi-line expansion based on the configured threshold.
// Named parameters with shared types (e.g. a, b int) count individually.
func (s *Shortener) shouldExpandParams(params *dst.FieldList) bool {
if s.config.FuncParamThreshold <= 0 || params == nil {
return false
}

count := 0

for _, field := range params.List {
if len(field.Names) == 0 {
count++ // unnamed parameter: func(int, string)
} else {
count += len(field.Names) // named: func(a, b int) counts as 2
}
}

return count >= s.config.FuncParamThreshold
}

func (s *Shortener) createDot(result dst.Node) error {
Expand Down
106 changes: 106 additions & 0 deletions shorten/testdata/func_param_threshold/func_param_threshold.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package fixtures

// Functions below threshold: unchanged.

func zeroParams() error {
return nil
}

func oneParam(a int) error {
return nil
}

func twoParams(a int, b string) error {
return nil
}

// Functions at or above threshold: expand.

func threeParams(a int, b string, c bool) error {
return nil
}

func fourParams(a int, b string, c bool, d float64) error {
return nil
}

// Methods: receiver is not counted toward the threshold.

type Receiver struct{}

func (r *Receiver) methodTwoParams(a int, b string) error {
return nil
}

func (r *Receiver) methodThreeParams(a int, b string, c bool) error {
return nil
}

// Value receiver on a named basic type: receiver still not counted.

type MyString string

func (s MyString) basicTypeTwoParams(a int, b string) error {
return nil
}

func (s MyString) basicTypeThreeParams(a int, b string, c bool) error {
return nil
}

// Named params with shared types: count names, not fields.
// a, b int, c string = 3 logical params.

func sharedTypes(a, b int, c string) error {
return nil
}

// Unnamed params.

func unnamed(int, string, bool) error {
return nil
}

// Variadic: a + b = 2 logical params, unchanged.

func variadic(a int, b ...string) error {
return nil
}

// Already multi-line: idempotent, no change.

func alreadyMultiLine(
a int,
b string,
c bool,
) error {
return nil
}

// Return values are not counted toward the threshold.

func returnValuesNotCounted(a int) (int, string, bool) {
return 0, "", false
}

// Interface methods.

type MyInterface interface {
interfaceMethodTwo(a int, b string) error
interfaceMethodThree(a int, b string, c bool) error
}

// Function type declarations.

type MyFunc func(a int, b string, c bool) error

// Function literals.

func funcLiterals() {
_ = func(a int, b string) error {
return nil
}
_ = func(a int, b string, c bool) error {
return nil
}
}
142 changes: 142 additions & 0 deletions shorten/testdata/func_param_threshold/func_param_threshold.go.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package fixtures

// Functions below threshold: unchanged.

func zeroParams() error {
return nil
}

func oneParam(a int) error {
return nil
}

func twoParams(a int, b string) error {
return nil
}

// Functions at or above threshold: expand.

func threeParams(
a int,
b string,
c bool,
) error {
return nil
}

func fourParams(
a int,
b string,
c bool,
d float64,
) error {
return nil
}

// Methods: receiver is not counted toward the threshold.

type Receiver struct{}

func (r *Receiver) methodTwoParams(a int, b string) error {
return nil
}

func (r *Receiver) methodThreeParams(
a int,
b string,
c bool,
) error {
return nil
}

// Value receiver on a named basic type: receiver still not counted.

type MyString string

func (s MyString) basicTypeTwoParams(a int, b string) error {
return nil
}

func (s MyString) basicTypeThreeParams(
a int,
b string,
c bool,
) error {
return nil
}

// Named params with shared types: count names, not fields.
// a, b int, c string = 3 logical params.

func sharedTypes(
a, b int,
c string,
) error {
return nil
}

// Unnamed params.

func unnamed(
int,
string,
bool,
) error {
return nil
}

// Variadic: a + b = 2 logical params, unchanged.

func variadic(a int, b ...string) error {
return nil
}

// Already multi-line: idempotent, no change.

func alreadyMultiLine(
a int,
b string,
c bool,
) error {
return nil
}

// Return values are not counted toward the threshold.

func returnValuesNotCounted(a int) (int, string, bool) {
return 0, "", false
}

// Interface methods.

type MyInterface interface {
interfaceMethodTwo(a int, b string) error
interfaceMethodThree(
a int,
b string,
c bool,
) error
}

// Function type declarations.

type MyFunc func(
a int,
b string,
c bool,
) error

// Function literals.

func funcLiterals() {
_ = func(a int, b string) error {
return nil
}
_ = func(
a int,
b string,
c bool,
) error {
return nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"MaxLen": 100,
"TabLen": 4,
"KeepAnnotations": false,
"ShortenComments": false,
"ReformatTags": true,
"ChainSplitDots": true,
"FuncParamThreshold": 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fixtures

func threeParams(a int, b string, c bool) error {
return nil
}

func fourParams(a int, b string, c bool, d float64) error {
return nil
}

func fiveParams(a int, b string, c bool, d float64, e []byte) error {
return nil
}
Loading