Skip to content

Commit

Permalink
optimize C import block handle logic (#131)
Browse files Browse the repository at this point in the history
* optimize C import block handle logic

Signed-off-by: Loong <loong.dai@intel.com>

* update comments

Signed-off-by: Loong <loong.dai@intel.com>

* bump up

Signed-off-by: Loong <loong.dai@intel.com>
  • Loading branch information
daixiang0 committed Nov 25, 2022
1 parent 666f229 commit 0a02b8b
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 37 deletions.
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

0 comments on commit 0a02b8b

Please sign in to comment.