Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimize C import block handle logic #131

Merged
merged 3 commits into from Nov 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -23,6 +23,8 @@ By default, blank and dot sections are not used and the corresponding lines end

All import blocks use one TAB(`\t`) as Indent.

Since v0.9.0, GCI always puts C import block as the first.

**Note**:

`nolint` is hard to handle at section level, GCI will consider it as a single comment.
Expand Down
2 changes: 1 addition & 1 deletion main.go
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/daixiang0/gci/cmd/gci"
)

var version = "0.8.5"
var version = "0.9.0"

func main() {
e := gci.NewExecutor(version)
Expand Down
8 changes: 7 additions & 1 deletion pkg/gci/gci.go
Expand Up @@ -121,7 +121,7 @@ func LoadFormatGoFile(file io.FileObj, cfg config.Config) (src, dist []byte, err
return src, src, nil
}

imports, headEnd, tailStart, err := parse.ParseFile(src, file.Path())
imports, headEnd, tailStart, cStart, cEnd, err := parse.ParseFile(src, file.Path())
if err != nil {
if errors.Is(err, parse.NoImportError{}) {
return src, src, nil
Expand Down Expand Up @@ -162,6 +162,12 @@ func LoadFormatGoFile(file io.FileObj, cfg config.Config) (src, dist []byte, err
copy(tail, src[tailStart:])

head = append(head, utils.Linebreak)
// ensure C
if cStart != 0 {
head = append(head, src[cStart:cEnd]...)
head = append(head, utils.Linebreak)
}

// add beginning of import block
head = append(head, `import (`...)
// add end of import block
Expand Down
1 change: 1 addition & 0 deletions pkg/gci/internal/testdata/cgo-block-after-import.cfg.yaml
14 changes: 14 additions & 0 deletions pkg/gci/internal/testdata/cgo-block-after-import.in.go
@@ -0,0 +1,14 @@
package main

import (
"fmt"

"github.com/daixiang0/gci"
g "github.com/golang"
)

// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"
15 changes: 15 additions & 0 deletions pkg/gci/internal/testdata/cgo-block-after-import.out.go
@@ -0,0 +1,15 @@
package main

// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"

import (
"fmt"

g "github.com/golang"

"github.com/daixiang0/gci"
)
4 changes: 0 additions & 4 deletions pkg/gci/internal/testdata/cgo-block-before-import.cfg.yaml

This file was deleted.

1 change: 1 addition & 0 deletions pkg/gci/internal/testdata/cgo-block-before-import.cfg.yaml
1 change: 1 addition & 0 deletions pkg/gci/internal/testdata/cgo-single.cfg.yaml
15 changes: 15 additions & 0 deletions pkg/gci/internal/testdata/cgo-single.in.go
@@ -0,0 +1,15 @@
package main

import (
"fmt"

"github.com/daixiang0/gci"
)

import "C"

import "github.com/golang"

import (
"github.com/daixiang0/gci"
)
11 changes: 11 additions & 0 deletions pkg/gci/internal/testdata/cgo-single.out.go
@@ -0,0 +1,11 @@
package main

import "C"

import (
"fmt"

"github.com/golang"

"github.com/daixiang0/gci"
)
90 changes: 59 additions & 31 deletions pkg/parse/parse.go
Expand Up @@ -70,31 +70,36 @@ func getImports(imp *ast.ImportSpec) (start, end int, name string) {
return
}

func ParseFile(src []byte, filename string) (ImportList, int, int, error) {
func ParseFile(src []byte, filename string) (ImportList, int, int, int, int, error) {
fileSet := token.NewFileSet()
f, err := parser.ParseFile(fileSet, filename, src, parser.ParseComments)
if err != nil {
return nil, 0, 0, err
return nil, 0, 0, 0, 0, err
}

if len(f.Imports) == 0 {
return nil, 0, 0, NoImportError{}
return nil, 0, 0, 0, 0, NoImportError{}
}

var (
// headEnd means the start of import block
headEnd int
// tailStart means the end + 1 of import block
tailStart int
data ImportList
// cStart means the start of C import block
cStart int
// cEnd means the end of C import block
cEnd int
data ImportList
)

for _, d := range f.Decls {
switch d.(type) {
for index, decl := range f.Decls {
switch decl.(type) {
// skip BadDecl and FuncDecl
case *ast.GenDecl:
dd := d.(*ast.GenDecl)
genDecl := decl.(*ast.GenDecl)

if dd.Tok == token.IMPORT {
if genDecl.Tok == token.IMPORT {
// there are two cases, both end with linebreak:
// 1.
// import (
Expand All @@ -103,35 +108,58 @@ func ParseFile(src []byte, filename string) (ImportList, int, int, error) {
// 2.
// import "xxx"
if headEnd == 0 {
headEnd = int(d.Pos()) - 1
headEnd = int(decl.Pos()) - 1
}
tailStart = int(decl.End())

for _, spec := range genDecl.Specs {
imp := spec.(*ast.ImportSpec)
// there are only one C import block
// ensure C import block is the first import block
if imp.Path.Value == C {
/*
common case:

// #include <png.h>
import "C"

notice that decl.Pos() == genDecl.Pos() > genDecl.Doc.Pos()
*/
if genDecl.Doc != nil {
cStart = int(genDecl.Doc.Pos()) - 1
// if C import block is the first, update headEnd
if index == 0 {
headEnd = cStart
}
} else {
/*
special case:

import "C"
*/
cStart = int(decl.Pos()) - 1
}

cEnd = int(decl.End())

continue
}

start, end, name := getImports(imp)

data = append(data, &GciImports{
Start: start,
End: end,
Name: name,
Path: strings.Trim(imp.Path.Value, `"`),
})
}
tailStart = int(d.End())
}
}
}

for _, imp := range f.Imports {
if imp.Path.Value == C {
if imp.Comment != nil {
headEnd = int(imp.Comment.End())
} else {
headEnd = int(imp.Path.End())
}
continue
}

start, end, name := getImports(imp)

data = append(data, &GciImports{
Start: start,
End: end,
Name: name,
Path: strings.Trim(imp.Path.Value, `"`),
})
}

sort.Sort(data)
return data, headEnd, tailStart, nil
return data, headEnd, tailStart, cStart, cEnd, nil
}

// IsGeneratedFileByComment reports whether the source file is generated code.
Expand Down