Skip to content

Commit 30a50b8

Browse files
authored
Support grouping dot and blank imports at the end (#88)
* feat: Support grouping dot and blank imports at the end Signed-off-by: Marco Nenciarini <[email protected]> * doc: Document blank and dot groups Signed-off-by: Marco Nenciarini <[email protected]>
1 parent 21c5505 commit 30a50b8

File tree

12 files changed

+184
-21
lines changed

12 files changed

+184
-21
lines changed

README.md

+53-9
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ Name Path Comment
1111
```
1212
All comments will keep as they were, except the independent comment blocks(line breaks before and after).
1313

14-
GCI splits all import blocks into different sections, now support three section type:
14+
GCI splits all import blocks into different sections, now support five section type:
1515
- standard: Golang official imports, like "fmt"
16-
- custom: Custom section, use full and the longest match(match full string first, if multiple matches, use the longest one)
16+
- custom: Custom section, use full and the longest match (match full string first, if multiple matches, use the longest one)
1717
- default: All rest import blocks
18+
- blank: Put blank imports together in a separate group
19+
- dot: Put dot imports together in a separate group
1820

19-
The priority is standard > default > custom, all sections sort alphabetically inside.
21+
The priority is standard > default > custom > blank > dot, all sections sort alphabetically inside.
22+
By default, blank and dot sections are not used and the corresponding lines end up in the other groups.
2023

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

@@ -50,11 +53,13 @@ Aliases:
5053
Flags:
5154
-d, --debug Enables debug output from the formatter
5255
-h, --help help for write
53-
-s, --section strings Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom. The default value is [standard,default].
56+
-s, --section strings Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot. The default value is [standard,default].
5457
standard - standard section that Golang provides officially, like "fmt"
5558
Prefix(github.com/daixiang0) - custom section, groups all imports with the specified Prefix. Imports will be matched to the longest Prefix.
56-
default - default section, contains all rest imports (default [standard,default])
59+
default - default section, contains all rest imports
60+
blank - blank section, contains all blank imports. This section is not presed unless explicitly enabled. (default [standard,default])
5761
--skip-generated Skip generated files
62+
5863
```
5964

6065
```shell
@@ -70,11 +75,14 @@ Aliases:
7075
Flags:
7176
-d, --debug Enables debug output from the formatter
7277
-h, --help help for write
73-
-s, --section strings Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom. The default value is [standard,default].
78+
-s, --section strings Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot. The default value is [standard,default].
7479
standard - standard section thatolang provides officially, like "fmt"
7580
Prefix(github.com/daixiang0) - custom section, groups all imports with the specified Prefix. Imports will be matched to the longest Prefix.
76-
default - default section, contains all rest imports (default [standard,default])
81+
default - default section, contains all rest imports
82+
blank - blank section, contains all blank imports. This section is not presed unless explicitly enabled.
83+
dot - dot section, contains all dot imports. This section is not presed unless explicitly enabled. (default [standard,default])
7784
--skip-generated Skip generated files
85+
7886
```
7987

8088
```shell
@@ -87,10 +95,12 @@ Usage:
8795
Flags:
8896
-d, --debug Enables debug output from the formatter
8997
-h, --help help for write
90-
-s, --section strings Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom. The default value is [standard,default].
98+
-s, --section strings Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot. The default value is [standard,default].
9199
standard - standard section thatolang provides officially, like "fmt"
92100
Prefix(github.com/daixiang0) - custom section, groups all imports with the specified Prefix. Imports will be matched to the longest Prefix.
93-
default - default section, contains all rest imports (default [standard,default])
101+
default - default section, contains all rest imports
102+
blank - blank section, contains all blank imports. This section is not presed unless explicitly enabled.
103+
dot - dot section, contains all dot imports. This section is not presed unless explicitly enabled. (default [standard,default])
94104
--skip-generated Skip generated files
95105

96106
```
@@ -168,6 +178,40 @@ import (
168178
)
169179
```
170180

181+
### with blank and dot grouping enabled
182+
183+
```go
184+
package main
185+
import (
186+
"fmt"
187+
go "github.com/golang"
188+
_ "github.com/golang/blank"
189+
. "github.com/golang/dot"
190+
"github.com/daixiang0/gci"
191+
_ "github.com/daixiang0/gci/blank"
192+
. "github.com/daixiang0/gci/dot"
193+
)
194+
```
195+
196+
to
197+
198+
```go
199+
package main
200+
import (
201+
"fmt"
202+
203+
go "github.com/golang"
204+
205+
"github.com/daixiang0/gci"
206+
207+
_ "github.com/daixiang0/gci/blank"
208+
_ "github.com/golang/blank"
209+
210+
. "github.com/daixiang0/gci/dot"
211+
. "github.com/golang/dot"
212+
)
213+
```
214+
171215
## TODO
172216

173217
- Ensure only one blank between `Name` and `Path` in an import block

cmd/gci/gcicommand.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,12 @@ func (e *Executor) newGciCommand(use, short, long string, aliases []string, stdI
4646

4747
debug = cmd.Flags().BoolP("debug", "d", false, "Enables debug output from the formatter")
4848

49-
sectionHelp := `Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom. The default value is [standard,default].
49+
sectionHelp := `Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot. The default value is [standard,default].
5050
standard - standard section that Golang provides officially, like "fmt"
5151
Prefix(github.com/daixiang0) - custom section, groups all imports with the specified Prefix. Imports will be matched to the longest Prefix.
52-
default - default section, contains all rest imports`
52+
default - default section, contains all rest imports
53+
blank - blank section, contains all blank imports. This section is not presed unless explicitly enabled.
54+
dot - dot section, contains all dot imports. This section is not presed unless explicitly enabled.`
5355

5456
skipGenerated = cmd.Flags().Bool("skip-generated", false, "Skip generated files")
5557

pkg/gci/gci.go

+28-9
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,8 @@ func LoadFormatGoFile(file io.FileObj, cfg config.Config) (src, dist []byte, err
144144
tail := src[tailStart:]
145145

146146
// sort for custom sections
147-
allKeys := make([]string, 0, len(result))
148147
customKeys := make([]string, 0, len(result))
149148
for k := range result {
150-
allKeys = append(allKeys, k)
151149
if strings.HasPrefix(k, "prefix(") {
152150
customKeys = append(customKeys, k)
153151
}
@@ -162,9 +160,8 @@ func LoadFormatGoFile(file io.FileObj, cfg config.Config) (src, dist []byte, err
162160
AddIndent(&body, &firstWithIndex)
163161
body = append(body, src[d.Start:d.End]...)
164162
}
165-
if len(allKeys) > 1 {
166-
body = append(body, utils.Linebreak)
167-
}
163+
164+
body = append(body, utils.Linebreak)
168165
}
169166

170167
if len(result["default"]) > 0 {
@@ -173,9 +170,7 @@ func LoadFormatGoFile(file io.FileObj, cfg config.Config) (src, dist []byte, err
173170
body = append(body, src[d.Start:d.End]...)
174171
}
175172

176-
if len(customKeys) > 0 {
177-
body = append(body, utils.Linebreak)
178-
}
173+
body = append(body, utils.Linebreak)
179174
}
180175

181176
if len(customKeys) > 0 {
@@ -189,13 +184,37 @@ func LoadFormatGoFile(file io.FileObj, cfg config.Config) (src, dist []byte, err
189184
body = append(body, utils.Linebreak)
190185
}
191186
}
187+
188+
body = append(body, utils.Linebreak)
189+
}
190+
191+
if len(result["blank"]) > 0 {
192+
for _, d := range result["blank"] {
193+
AddIndent(&body, &firstWithIndex)
194+
body = append(body, src[d.Start:d.End]...)
195+
}
196+
197+
body = append(body, utils.Linebreak)
198+
}
199+
200+
if len(result["dot"]) > 0 {
201+
for _, d := range result["dot"] {
202+
AddIndent(&body, &firstWithIndex)
203+
body = append(body, src[d.Start:d.End]...)
204+
}
205+
206+
body = append(body, utils.Linebreak)
192207
}
193208

194209
// remove breakline in the end
195-
if body[len(body)-1] == utils.Linebreak && tail[0] == utils.Linebreak {
210+
for body[len(body)-1] == utils.Linebreak {
196211
body = body[:len(body)-1]
197212
}
198213

214+
if tail[0] != utils.Linebreak {
215+
body = append(body, utils.Linebreak)
216+
}
217+
199218
var totalLen int
200219
slices := [][]byte{head, body, tail}
201220
for _, s := range slices {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
sections:
2+
- Standard
3+
- Default
4+
- Prefix(github.com/daixiang0)
5+
- Blank
6+
- Dot
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package main
2+
import (
3+
"fmt"
4+
5+
g "github.com/golang"
6+
. "github.com/golang/dot"
7+
_ "github.com/golang/blank"
8+
9+
"github.com/daixiang0/a"
10+
"github.com/daixiang0/gci"
11+
"github.com/daixiang0/gci/subtest"
12+
. "github.com/daixiang0/gci/dot"
13+
_ "github.com/daixiang0/gci/blank"
14+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package main
2+
import (
3+
"fmt"
4+
5+
g "github.com/golang"
6+
7+
"github.com/daixiang0/a"
8+
"github.com/daixiang0/gci"
9+
"github.com/daixiang0/gci/subtest"
10+
11+
_ "github.com/daixiang0/gci/blank"
12+
_ "github.com/golang/blank"
13+
14+
. "github.com/daixiang0/gci/dot"
15+
. "github.com/golang/dot"
16+
)

pkg/section/blank.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package section
2+
3+
import (
4+
"github.com/daixiang0/gci/pkg/parse"
5+
"github.com/daixiang0/gci/pkg/specificity"
6+
)
7+
8+
type Blank struct{}
9+
10+
func (b Blank) MatchSpecificity(spec *parse.GciImports) specificity.MatchSpecificity {
11+
if spec.Name == "_" {
12+
return specificity.NameMatch{}
13+
}
14+
return specificity.MisMatch{}
15+
}
16+
17+
func (b Blank) String() string {
18+
return "blank"
19+
}

pkg/section/dot.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package section
2+
3+
import (
4+
"github.com/daixiang0/gci/pkg/parse"
5+
"github.com/daixiang0/gci/pkg/specificity"
6+
)
7+
8+
type Dot struct{}
9+
10+
func (d Dot) MatchSpecificity(spec *parse.GciImports) specificity.MatchSpecificity {
11+
if spec.Name == "." {
12+
return specificity.NameMatch{}
13+
}
14+
return specificity.MisMatch{}
15+
}
16+
17+
func (d Dot) String() string {
18+
return "dot"
19+
}

pkg/section/parser.go

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ func Parse(data []string) (SectionList, error) {
2929
list = append(list, Custom{d[7 : len(d)-1]})
3030
} else if strings.HasPrefix(s, "commentline(") && len(d) > 13 {
3131
list = append(list, Custom{d[12 : len(d)-1]})
32+
} else if s == "dot" {
33+
list = append(list, Dot{})
34+
} else if s == "blank" {
35+
list = append(list, Blank{})
3236
} else {
3337
errString += fmt.Sprintf(" %s", s)
3438
}

pkg/section/standard_list.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/specificity/name.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package specificity
2+
3+
type NameMatch struct{}
4+
5+
func (n NameMatch) IsMoreSpecific(than MatchSpecificity) bool {
6+
return isMoreSpecific(n, than)
7+
}
8+
9+
func (n NameMatch) Equal(to MatchSpecificity) bool {
10+
return equalSpecificity(n, to)
11+
}
12+
13+
func (n NameMatch) class() specificityClass {
14+
return NameClass
15+
}
16+
17+
func (n NameMatch) String() string {
18+
return "Name"
19+
}

pkg/specificity/specificity.go

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const (
77
DefaultClass = 10
88
StandardClass = 20
99
MatchClass = 30
10+
NameClass = 40
1011
)
1112

1213
// MatchSpecificity is used to determine which section matches an import best

0 commit comments

Comments
 (0)