Skip to content

Commit

Permalink
Add option to separate named imports (#159)
Browse files Browse the repository at this point in the history
This addresses #116

Add a `-separate-named` boolean option.  When activated, named/aliased
imports are split off into a separate block below the relevant group,
e.g. named std below std, named project below project, etc.
  • Loading branch information
n-oden authored Nov 21, 2024
1 parent 37bb521 commit 3423e46
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 24 deletions.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ Usage of goimports-reviser:
Apply rules recursively if target is a directory. In case of ./... execution will be recursively applied by default. Optional parameter.
-rm-unused
Remove unused imports. Optional parameter.
-separate-named
Separate named imports from their group with a new line. Optional parameter.
-set-alias
Set alias for versioned package names, like 'github.com/go-pg/pg/v9'. In this case import will be set as 'pg "github.com/go-pg/pg/v9"'. Optional parameter.
-set-exit-status
Expand Down Expand Up @@ -238,6 +240,36 @@ func additionalTest(){
}
```

### Example with `-separate-named`-option

Before usage:

```go
package testdata // goimports-reviser/testdata

import (
"fmt"
"github.com/incu6us/goimports-reviser/pkg"
extpkg "google.com/golang/pkg"
"golang.org/x/exp/slices"
extslice "github.com/PeterRK/slices"
)
```

After usage:
```go
package testdata // goimports-reviser/testdata

import (
"fmt"

"github.com/incu6us/goimports-reviser/pkg"
"golang.org/x/exp/slices"

extpkg "google.com/golang/pkg"
extslice "github.com/PeterRK/slices"
)
```
---
## Contributors

Expand Down
14 changes: 13 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const (
applyToGeneratedFiles = "apply-to-generated-files"
excludesArg = "excludes"
// using a regex here so that this will work with forked repos (at least on github.com)
modulePathRegex = `^github.com/[\w-]+/goimports-reviser(/v\d+)?@?`
modulePathRegex = `^github.com/[\w-]+/goimports-reviser(/v\d+)?@?`
separateNamedArg = "separate-named"

// Deprecated options
localArg = "local"
Expand All @@ -56,6 +57,7 @@ var (
shouldSetAlias *bool
shouldFormat *bool
shouldApplyToGeneratedFiles *bool
shouldSeparateNamedImports *bool
listFileName *bool
setExitStatus *bool
isRecursive *bool
Expand Down Expand Up @@ -158,6 +160,12 @@ Optional parameter.`,
"Option will perform additional formatting. Optional parameter.",
)

shouldSeparateNamedImports = flag.Bool(
separateNamedArg,
false,
"Option will separate named imports from the rest of the imports, per group. Optional parameter.",
)

isRecursive = flag.Bool(
recursiveArg,
false,
Expand Down Expand Up @@ -311,6 +319,10 @@ func main() {
options = append(options, reviser.WithSkipGeneratedFile)
}

if shouldSeparateNamedImports != nil && *shouldSeparateNamedImports {
options = append(options, reviser.WithSeparatedNamedImports)
}

if localPkgPrefixes != "" {
if companyPkgPrefixes != "" {
companyPkgPrefixes = localPkgPrefixes
Expand Down
68 changes: 58 additions & 10 deletions reviser/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type SourceFile struct {
shouldUseAliasForVersionSuffix bool
shouldFormatCode bool
shouldSkipAutoGenerated bool
shouldSeparateNamedImports bool
hasSeparateSideEffectGroup bool
companyPackagePrefixes []string
importsOrders ImportsOrders
Expand Down Expand Up @@ -165,12 +166,16 @@ func (f *SourceFile) groupImports(
importsWithMetadata map[string]*commentsMetadata,
) *groupsImports {
var (
stdImports []string
projectImports []string
projectLocalPkgs []string
generalImports []string
blankedImports []string
dottedImports []string
stdImports []string
projectImports []string
projectLocalPkgs []string
generalImports []string
namedStdImports []string
namedProjectImports []string
namedProjectLocalPkgs []string
namedGeneralImports []string
blankedImports []string
dottedImports []string
)

for imprt := range importsWithMetadata {
Expand All @@ -185,14 +190,33 @@ func (f *SourceFile) groupImports(
}

pkgWithoutAlias := skipPackageAlias(imprt)
values := strings.Split(imprt, " ")

if _, ok := std.StdPackages[pkgWithoutAlias]; ok {
if f.shouldSeparateNamedImports {
if len(values) > 1 {
namedStdImports = append(namedStdImports, imprt)
} else {
stdImports = append(stdImports, imprt)
}
continue
}
stdImports = append(stdImports, imprt)
continue
}

var isLocalPackageFound bool
for _, localPackagePrefix := range localPkgPrefixes {
if strings.HasPrefix(pkgWithoutAlias, localPackagePrefix) && !strings.HasPrefix(pkgWithoutAlias, projectName) {
if f.shouldSeparateNamedImports {
if len(values) > 1 {
namedProjectLocalPkgs = append(namedProjectLocalPkgs, imprt)
} else {
projectLocalPkgs = append(projectLocalPkgs, imprt)
}
isLocalPackageFound = true
break
}
projectLocalPkgs = append(projectLocalPkgs, imprt)
isLocalPackageFound = true
break
Expand All @@ -204,10 +228,26 @@ func (f *SourceFile) groupImports(
}

if strings.Contains(pkgWithoutAlias, projectName) {
if f.shouldSeparateNamedImports {
if len(values) > 1 {
namedProjectImports = append(namedProjectImports, imprt)
} else {
projectImports = append(projectImports, imprt)
}
continue
}
projectImports = append(projectImports, imprt)
continue
}

if f.shouldSeparateNamedImports {
if len(values) > 1 {
namedGeneralImports = append(namedGeneralImports, imprt)
} else {
generalImports = append(generalImports, imprt)
}
continue
}
generalImports = append(generalImports, imprt)
}

Expand All @@ -217,13 +257,21 @@ func (f *SourceFile) groupImports(
sort.Strings(projectImports)
sort.Strings(blankedImports)
sort.Strings(dottedImports)
sort.Strings(namedStdImports)
sort.Strings(namedGeneralImports)
sort.Strings(namedProjectLocalPkgs)
sort.Strings(namedProjectImports)

result := &groupsImports{
common: &common{
std: stdImports,
general: generalImports,
company: projectLocalPkgs,
project: projectImports,
std: stdImports,
namedStd: namedStdImports,
general: generalImports,
namedGeneral: namedGeneralImports,
company: projectLocalPkgs,
namedCompany: namedProjectLocalPkgs,
project: projectImports,
namedProject: namedProjectImports,
},
blanked: blankedImports,
dotted: dottedImports,
Expand Down
5 changes: 5 additions & 0 deletions reviser/file_option.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,8 @@ func WithSkipGeneratedFile(f *SourceFile) error {
f.shouldSkipAutoGenerated = true
return nil
}

func WithSeparatedNamedImports(f *SourceFile) error {
f.shouldSeparateNamedImports = true
return nil
}
136 changes: 136 additions & 0 deletions reviser/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1999,3 +1999,139 @@ import (
})
}
}

func TestSourceFile_Fix_WithSeparatedNamedImports(t *testing.T) {
type args struct {
projectName string
filePath string
fileContent string
}
tests := []struct {
name string
args args
want string
wantChange bool
wantErr bool
}{
{
name: "simple",
args: args{
projectName: "github.com/incu6us/goimports-reviser",
filePath: "./testdata/example.go",
fileContent: `package testdata
import (
"fmt"
"github.com/incu6us/goimports-reviser/testdata/innderpkg"
"bytes"
"golang.org/x/exp/slices"
)
`,
},
want: `package testdata
import (
"bytes"
"fmt"
"golang.org/x/exp/slices"
"github.com/incu6us/goimports-reviser/testdata/innderpkg"
)
`,
wantChange: true,
wantErr: false,
},
{
name: "named",
args: args{
projectName: "github.com/incu6us/goimports-reviser",
filePath: "./testdata/example.go",
fileContent: `package testdata
import (
"fmt"
second "github.com/incu6us/goimports-reviser/testdata/secondpkg"
by "bytes"
js "encoding/json"
"golang.org/x/exp/slices"
er "golang.org/x/errors"
"github.com/incu6us/goimports-reviser/testdata/innderpkg"
)
`,
},
want: `package testdata
import (
"fmt"
by "bytes"
js "encoding/json"
"golang.org/x/exp/slices"
er "golang.org/x/errors"
"github.com/incu6us/goimports-reviser/testdata/innderpkg"
second "github.com/incu6us/goimports-reviser/testdata/secondpkg"
)
`,
wantChange: true,
wantErr: false,
},
{
name: "named with comments",
args: args{
projectName: "github.com/incu6us/goimports-reviser",
filePath: "./testdata/example.go",
fileContent: `package testdata
import (
"fmt" //fmt package
second "github.com/incu6us/goimports-reviser/testdata/secondpkg" //secondpkg package
by "bytes"
js "encoding/json"
"golang.org/x/exp/slices" //slices package
er "golang.org/x/errors"
"github.com/incu6us/goimports-reviser/testdata/innderpkg"
)
`,
},
want: `package testdata
import (
"fmt" //fmt package
by "bytes"
js "encoding/json"
"golang.org/x/exp/slices" //slices package
er "golang.org/x/errors"
"github.com/incu6us/goimports-reviser/testdata/innderpkg"
second "github.com/incu6us/goimports-reviser/testdata/secondpkg" //secondpkg package
)
`,
wantChange: true,
wantErr: false,
},
}

for _, tt := range tests {
if tt.args.filePath != StandardInput && !strings.Contains(tt.args.filePath, "does-not-exist") {
require.NoError(t, os.WriteFile(tt.args.filePath, []byte(tt.args.fileContent), 0o644))
}

t.Run(tt.name, func(t *testing.T) {
got, _, hasChange, err := NewSourceFile(tt.args.projectName, tt.args.filePath).Fix(WithSeparatedNamedImports)
if tt.wantErr {
assert.Error(t, err)
return
}

assert.NoError(t, err)
assert.Equal(t, tt.wantChange, hasChange)
assert.Equal(t, tt.want, string(got))
})
}
}
Loading

0 comments on commit 3423e46

Please sign in to comment.