Skip to content

Commit

Permalink
imports: support separate grouping of local prefix packages
Browse files Browse the repository at this point in the history
Apply abandoned https://go.dev/cl/376315 CL.

Original message:

Add "separate-local" flag which is separate grouping of local prefix packages.

Signed-off-by: Koichi Shiraishi <zchee.io@gmail.com>
  • Loading branch information
zchee committed Feb 13, 2022
1 parent 61ed4ba commit c10fb95
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 51 deletions.
1 change: 1 addition & 0 deletions cmd/goimportz/goimportz.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ var (
func init() {
flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
flag.StringVar(&options.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
flag.BoolVar(&options.SepareteLocal, "separate-local", false, "separate local package per comma-separated list")
flag.BoolVar(&options.FormatOnly, "format-only", false, "if true, don't fix imports and only format. In this mode, goimportz is effectively gofmt, with the addition that imports are grouped into sections.")
}

Expand Down
16 changes: 8 additions & 8 deletions internal/imports/fix.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,28 @@ import (

// importToGroup is a list of functions which map from an import path to
// a group number.
var importToGroup = []func(localPrefix, importPath string) (num int, ok bool){
func(localPrefix, importPath string) (num int, ok bool) {
var importToGroup = []func(localPrefix, importPath string, separateLocal bool) (num int, ok bool){
func(localPrefix, importPath string, separateLocal bool) (num int, ok bool) {
if localPrefix == "" {
return
}
for i, p := range strings.Split(localPrefix, ",") {
if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath {
if i == 0 {
return 4, true
if separateLocal {
return i + 2, true // +2 for stdlib and 3rd-party package groups
}
return 3, true
}
}
return
},
func(_, importPath string) (num int, ok bool) {
func(_, importPath string, _ bool) (num int, ok bool) {
if strings.HasPrefix(importPath, "appengine") {
return 2, true
}
return
},
func(_, importPath string) (num int, ok bool) {
func(_, importPath string, _ bool) (num int, ok bool) {
firstComponent := strings.Split(importPath, "/")[0]
if strings.Contains(firstComponent, ".") {
return 1, true
Expand All @@ -63,9 +63,9 @@ var importToGroup = []func(localPrefix, importPath string) (num int, ok bool){
},
}

func importGroup(localPrefix, importPath string) int {
func importGroup(localPrefix, importPath string, separateLocal bool) int {
for _, fn := range importToGroup {
if n, ok := fn(localPrefix, importPath); ok {
if n, ok := fn(localPrefix, importPath, separateLocal); ok {
return n
}
}
Expand Down
66 changes: 56 additions & 10 deletions internal/imports/fix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1780,11 +1780,12 @@ var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong}
// to be added into a later group (num=3).
func TestLocalPrefix(t *testing.T) {
tests := []struct {
name string
modules []packagestest.Module
localPrefix string
src string
want string
name string
modules []packagestest.Module
localPrefix string
separeteLocal bool
src string
want string
}{
{
name: "one_local",
Expand All @@ -1808,6 +1809,50 @@ import (
const Y = bar.X
const _ = runtime.GOOS
`,
},
{
name: "three_prefixes_with_separate",
modules: []packagestest.Module{
{
Name: "example.org/pkg",
Files: fm{"pkg.go": "package pkg \n const A = 1"},
},
{
Name: "foo.com",
Files: fm{"bar/bar.go": "package bar \n const B = 1"},
},
{
Name: "code.org/r/p",
Files: fm{"expproj/expproj.go": "package expproj \n const C = 1"},
},
{
Name: "test.org/localprefix",
Files: fm{"localprefix.go": "package localprefix \n const D = 1"},
},
},
localPrefix: "example.org/pkg,foo.com/,code.org",
separeteLocal: true,
src: "package main \n const W = pkg.A \n const X = bar.B \n const Y = expproj.C \n const Z = localprefix.D \n const _ = runtime.GOOS",
want: `package main
import (
"runtime"
"test.org/localprefix"
"example.org/pkg"
"foo.com/bar"
"code.org/r/p/expproj"
)
const W = pkg.A
const X = bar.B
const Y = expproj.C
const Z = localprefix.D
const _ = runtime.GOOS
`,
},
{
Expand Down Expand Up @@ -1883,11 +1928,12 @@ const _ = runtime.GOOS
}}, tt.modules...),
}.test(t, func(t *goimportTest) {
options := &Options{
LocalPrefix: tt.localPrefix,
TabWidth: 8,
TabIndent: true,
Comments: true,
Fragment: true,
LocalPrefix: tt.localPrefix,
SepareteLocal: tt.separeteLocal,
TabWidth: 8,
TabIndent: true,
Comments: true,
Fragment: true,
}
t.assertProcessEquals("test.com", "t.go", nil, options, tt.want)
})
Expand Down
53 changes: 22 additions & 31 deletions internal/imports/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"go/printer"
"go/token"
"io"
"regexp"
"strconv"
"strings"

Expand All @@ -33,6 +34,9 @@ type Options struct {
// into another group after 3rd-party packages.
LocalPrefix string

// SepareteLocal wthether the separates local prefix packages
SepareteLocal bool

Fragment bool // Accept fragment of a source file (no package statement)
AllErrors bool // Report all errors (not just the first 10 on different lines)

Expand Down Expand Up @@ -106,7 +110,6 @@ func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(
mergeImports(fileSet, file)
sortImports(opt.LocalPrefix, fileSet, file)
imps := astutil.Imports(fileSet, file)
impsByGroup := make(map[int][]*ast.ImportSpec)
var spacesBefore []string // import paths we need spaces before
for _, impSection := range imps {
// Within each block of contiguous imports, see if any
Expand All @@ -116,8 +119,7 @@ func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(
lastGroup := -1
for _, importSpec := range impSection {
importPath, _ := strconv.Unquote(importSpec.Path.Value)
groupNum := importGroup(opt.LocalPrefix, importPath)
impsByGroup[groupNum] = append(impsByGroup[groupNum], importSpec)
groupNum := importGroup(opt.LocalPrefix, importPath, opt.SepareteLocal)
if groupNum != lastGroup && lastGroup != -1 {
spacesBefore = append(spacesBefore, importPath)
}
Expand All @@ -142,7 +144,7 @@ func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(
out = adjust(src, out)
}
if len(spacesBefore) > 0 {
out, err = separateImportsIntoGroups(bytes.NewReader(out), impsByGroup)
out, err = addImportSpaces(bytes.NewReader(out), spacesBefore)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -307,12 +309,13 @@ func matchSpace(orig []byte, src []byte) []byte {
return b.Bytes()
}

func separateImportsIntoGroups(r io.Reader, impsByGroup map[int][]*ast.ImportSpec) ([]byte, error) {
var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+)"`)

func addImportSpaces(r io.Reader, breaks []string) ([]byte, error) {
var out bytes.Buffer
in := bufio.NewReader(r)
inImports := false
done := false
impInserted := false
for {
s, err := in.ReadString('\n')
if err == io.EOF {
Expand All @@ -321,38 +324,26 @@ func separateImportsIntoGroups(r io.Reader, impsByGroup map[int][]*ast.ImportSpe
return nil, err
}

if !inImports && !done && strings.HasPrefix(s, "import") && strings.Contains(s, "(") {
if !inImports && !done && strings.HasPrefix(s, "import") {
inImports = true
fmt.Fprint(&out, s)
continue
}
if inImports && !impInserted {
for i := 0; i <= 4; i++ {
for _, imp := range impsByGroup[i] {
if imp.Name != nil {
fmt.Fprint(&out, imp.Name.Name, " ")
}
fmt.Fprint(&out, imp.Path.Value)
if imp.Comment != nil {
for _, comment := range imp.Comment.List {
fmt.Fprint(&out, " ", comment.Text)
}
}
if inImports && (strings.HasPrefix(s, "var") ||
strings.HasPrefix(s, "func") ||
strings.HasPrefix(s, "const") ||
strings.HasPrefix(s, "type")) {
done = true
inImports = false
}
if inImports && len(breaks) > 0 {
if m := impLine.FindStringSubmatch(s); m != nil {
if m[1] == breaks[0] {
out.WriteByte('\n')
breaks = breaks[1:]
}
out.WriteByte('\n')
}
impInserted = true
continue
}
if inImports && strings.Contains(s, ")") {
done = true
inImports = false
}

if !inImports {
fmt.Fprint(&out, s)
}
fmt.Fprint(&out, s)
}
return out.Bytes(), nil
}
5 changes: 3 additions & 2 deletions internal/imports/sortimports.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,9 @@ func (x byImportSpec) Less(i, j int) bool {
ipath := importPath(x.specs[i])
jpath := importPath(x.specs[j])

igroup := importGroup(x.localPrefix, ipath)
jgroup := importGroup(x.localPrefix, jpath)
// force true seprateLocal for sort local package groups
igroup := importGroup(x.localPrefix, ipath, true)
jgroup := importGroup(x.localPrefix, jpath, true)
if igroup != jgroup {
return igroup < jgroup
}
Expand Down

0 comments on commit c10fb95

Please sign in to comment.