Skip to content

Commit 6902c24

Browse files
author
win5do
committed
cmd/goimports: add regroup flag support regroup imports
Updates golang/go#64271
1 parent 1b796a9 commit 6902c24

File tree

3 files changed

+123
-1
lines changed

3 files changed

+123
-1
lines changed

cmd/goimports/goimports.go

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func init() {
5353
flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
5454
flag.StringVar(&options.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
5555
flag.BoolVar(&options.FormatOnly, "format-only", false, "if true, don't fix imports and only format. In this mode, goimports is effectively gofmt, with the addition that imports are grouped into sections.")
56+
flag.BoolVar(&options.RegroupImports, "regroup", false, "remove blank line and regroup imports")
5657
}
5758

5859
func report(err error) {

internal/imports/fix_test.go

+67
Original file line numberDiff line numberDiff line change
@@ -3085,3 +3085,70 @@ func BenchmarkMatchesPath(b *testing.B) {
30853085
})
30863086
}
30873087
}
3088+
3089+
func TestRegroupImports(t *testing.T) {
3090+
const input = `package foo
3091+
3092+
import (
3093+
"fmt"
3094+
3095+
"github.com/foo/a"
3096+
"github.com/foo/b"
3097+
3098+
"context"
3099+
"go.pkg.com/bar/x"
3100+
"go.pkg.com/bar/y"
3101+
3102+
"github.com/foo/c"
3103+
"go.pkg.com/bar/z"
3104+
)
3105+
3106+
var (
3107+
ctx context.Context
3108+
fmt1 fmt.Formatter
3109+
a1 a.A
3110+
b1 b.A
3111+
c1 c.A
3112+
x1 x.A
3113+
y1 y.A
3114+
z1 z.A
3115+
)
3116+
`
3117+
3118+
const want = `package foo
3119+
3120+
import (
3121+
"context"
3122+
"fmt"
3123+
3124+
"github.com/foo/a"
3125+
"github.com/foo/b"
3126+
"github.com/foo/c"
3127+
"go.pkg.com/bar/x"
3128+
"go.pkg.com/bar/y"
3129+
"go.pkg.com/bar/z"
3130+
)
3131+
3132+
var (
3133+
ctx context.Context
3134+
fmt1 fmt.Formatter
3135+
a1 a.A
3136+
b1 b.A
3137+
c1 c.A
3138+
x1 x.A
3139+
y1 y.A
3140+
z1 z.A
3141+
)
3142+
`
3143+
3144+
testConfig{
3145+
module: packagestest.Module{
3146+
Name: "foo.com",
3147+
Files: fm{
3148+
"p/test.go": input,
3149+
},
3150+
},
3151+
}.processTest(t, "foo.com", "p/test.go", nil, &Options{
3152+
RegroupImports: true,
3153+
}, want)
3154+
}

internal/imports/imports.go

+55-1
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,19 @@ type Options struct {
4141
TabIndent bool // Use tabs for indent (true if nil *Options provided)
4242
TabWidth int // Tab width (8 if nil *Options provided)
4343

44-
FormatOnly bool // Disable the insertion and deletion of imports
44+
FormatOnly bool // Disable the insertion and deletion of imports
45+
RegroupImports bool // Remove blank line in imports.
4546
}
4647

4748
// Process implements golang.org/x/tools/imports.Process with explicit context in opt.Env.
4849
func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) {
50+
if opt.RegroupImports {
51+
src, err = removeBlankLineInImport(filename, src)
52+
if err != nil {
53+
return nil, err
54+
}
55+
}
56+
4957
fileSet := token.NewFileSet()
5058
var parserMode parser.Mode
5159
if opt.Comments {
@@ -357,3 +365,49 @@ func addImportSpaces(r io.Reader, breaks []string) ([]byte, error) {
357365
}
358366
return out.Bytes(), nil
359367
}
368+
369+
// removeBlankLineInImport remove blank line in import block.
370+
func removeBlankLineInImport(filename string, src []byte) (out []byte, rerr error) {
371+
tokenSet := token.NewFileSet()
372+
astFile, err := parser.ParseFile(tokenSet, filename, src, parser.ParseComments)
373+
if err != nil {
374+
return src, err
375+
}
376+
377+
if len(astFile.Decls) <= 1 {
378+
return src, nil
379+
}
380+
tokenFile := tokenSet.File(astFile.Pos())
381+
382+
for i := 0; i < len(astFile.Decls); i++ {
383+
decl := astFile.Decls[i]
384+
gen, ok := decl.(*ast.GenDecl)
385+
if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") {
386+
continue
387+
}
388+
389+
if !gen.Lparen.IsValid() {
390+
// Not a block: sorted by default.
391+
continue
392+
}
393+
394+
lpLine := tokenFile.Line(gen.Lparen)
395+
rpLine := tokenFile.Line(gen.Rparen)
396+
src = removeEmptyLine(src, lpLine, rpLine)
397+
}
398+
399+
return src, nil
400+
}
401+
402+
func removeEmptyLine(src []byte, l, r int) []byte {
403+
lines := bytes.Split(src, []byte("\n"))
404+
for i := l; i < r; i++ {
405+
if i < len(lines) && len(bytes.TrimSpace(lines[i])) == 0 {
406+
lines = append(lines[:i], lines[i+1:]...)
407+
i--
408+
r--
409+
}
410+
}
411+
412+
return bytes.Join(lines, []byte("\n"))
413+
}

0 commit comments

Comments
 (0)