Skip to content

Commit 8f32207

Browse files
committed
extra rules by ast: add or delete empty lines
1 parent b5af61a commit 8f32207

26 files changed

+1469
-65
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010

1111
# Output of the go coverage tool, specifically when used with LiteIDE
1212
*.out
13+
*.got

README.md

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,7 @@
77
* 支持将多行的 copyright 注释修改为单行格式(默认不调整)
88
* 简化代码
99
* struct 赋值表达式,自动补齐 key
10-
11-
12-
<details><summary><i>Example</i></summary>
13-
14-
```
15-
type User struct {
16-
Name string
17-
Age int
18-
}
19-
- u2 := User{"hello", 12}
20-
21-
+ u2 := User{Name: "hello", Age: 12}
22-
```
23-
</details>
10+
* 补充空行、移除多余的空行
2411

2512
对于 import 部分:
2613
> 1.可使用`-mi`参数来控制是否将多段import合并为一段(默认否)。
@@ -32,6 +19,79 @@ type User struct {
3219
会忽略当前目录以及子目录下的 `testdata``vendor` 目录。
3320
若需要可进入其目录里执行该命令。
3421

22+
23+
<details><summary><i>Example 1:补齐 struct key</i></summary>
24+
25+
```
26+
- u2 := User{"hello", 12}
27+
+ u2 := User{Name: "hello", Age: 12}
28+
```
29+
</details>
30+
31+
<details><summary><i>Example 2:注释格式化</i></summary>
32+
33+
```
34+
- //User 注释内容
35+
- type User struct{
36+
37+
+ // User 注释内容
38+
+ type User struct{
39+
```
40+
</details>
41+
42+
<details><summary><i>Example 3:简化代码</i></summary>
43+
44+
```
45+
- s[a:len(s)]
46+
+ s[a:]
47+
48+
- for x, _ = range v {...}
49+
+ for x = range v {...}
50+
51+
- for _ = range v {...}
52+
+ for range v {...}
53+
```
54+
</details>
55+
56+
<details><summary><i>Example 4:移除多余的空行</i></summary>
57+
58+
1. 移除 struct 内部前后多余的空行:
59+
```
60+
- type userfn91 struct{
61+
-
62+
- name string
63+
-
64+
- }
65+
66+
+ type userfn91 struct{
67+
+ name string
68+
+ }
69+
```
70+
71+
2. 移除 func 内部前后多余的空行:
72+
```
73+
- fn1() {
74+
-
75+
- println("hello")
76+
-
77+
- }
78+
79+
+ fn1() {
80+
+ println("hello")
81+
+
82+
+ }
83+
```
84+
85+
3. 空 func 变为一行:
86+
```
87+
- fn1() {
88+
- }
89+
90+
+ fn1() {}
91+
```
92+
93+
</details>
94+
3595
## 2.安装/更新
3696
```bash
3797
export GO111MODULE=on
@@ -53,6 +113,7 @@ usage: go_fmt [flags] [path ...]
53113
-d display diffs instead of rewriting files
54114
-df string
55115
display diffs format, support: text, json (default "text")
116+
-e enable extra rules (default true)
56117
-ig string
57118
import group sort rule,
58119
stc: Go Standard pkgs, Third Party pkgs, Current ModuleByFile pkg
@@ -68,6 +129,8 @@ usage: go_fmt [flags] [path ...]
68129
rewrite with build in rules:
69130
a[b:len(a)] -> a[b:]
70131
interface{} -> any
132+
a == "" -> len(a) == 0
133+
a != "" -> len(a) != 0
71134
-s simplify code (default true)
72135
-slcr
73136
multiline copyright to single-line

internal/common/debug.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright(C) 2022 github.com/fsgo All Rights Reserved.
2+
// Author: hidu <[email protected]>
3+
// Date: 2022/10/6
4+
5+
package common
6+
7+
import (
8+
"fmt"
9+
"log"
10+
"os"
11+
)
12+
13+
// Debug 程序内部调试
14+
var Debug = os.Getenv("go_fmt_debug") == "1"
15+
16+
var debugLogger = log.New(os.Stderr, "[Debug] ", log.Lshortfile)
17+
18+
// DebugPrintln 打印调试日志
19+
func DebugPrintln(depth int, v ...any) {
20+
if !Debug {
21+
return
22+
}
23+
_ = debugLogger.Output(2+depth, fmt.Sprintln(v...))
24+
}

internal/common/options.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ type Options struct {
8383

8484
// 是否使用内置的 rewrite 规则简化代码,可选,默认 false
8585
RewriteWithBuildIn bool
86+
87+
// Extra 更多额外的、高级的格式化规则
88+
Extra bool
8689
}
8790

8891
// ImportGroupType import 分组类型
@@ -138,6 +141,7 @@ func NewDefaultOptions() *Options {
138141
Write: true,
139142
MergeImports: true,
140143
Simplify: true,
144+
Extra: false,
141145
}
142146
}
143147

@@ -175,8 +179,7 @@ func (opt *Options) AllGoFiles() ([]string, error) {
175179
}
176180
if info.IsDir() {
177181
tmpList, err = allGoFiles(name)
178-
} else {
179-
// 若属实传入 文件名 可以不用检查是否是.go文件
182+
} else { // 若属实传入 文件名 可以不用检查是否是.go文件
180183
// 在一些特殊场景可能会有用
181184
if len(opt.Files) == 1 || isGoFileName(name) {
182185
tmpList = []string{name}
@@ -206,6 +209,7 @@ func (opt *Options) BindFlags() {
206209
commandLine.BoolVar(&opt.Simplify, "s", true, "simplify code")
207210
commandLine.StringVar(&opt.LocalModule, "local", "auto", "put imports beginning with this string as 3rd-party packages")
208211
commandLine.BoolVar(&opt.Trace, "trace", false, "show trace infos")
212+
commandLine.BoolVar(&opt.Extra, "e", true, "enable extra rules")
209213
commandLine.BoolVar(&opt.MergeImports, "mi", false, "merge imports into one")
210214
commandLine.BoolVar(&opt.SingleLineCopyright, "slcr", false, "multiline copyright to single-line")
211215
commandLine.StringVar(&opt.ImportGroupRule, "ig", defaultImportGroupRule, `import group sort rule,

internal/common/parser.go

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"go/ast"
99
"go/parser"
1010
"go/token"
11-
"path/filepath"
1211
)
1312

1413
// ParseOneFile 解析为 astFile
@@ -18,25 +17,3 @@ func ParseOneFile(fileName string, src []byte) (*token.FileSet, *ast.File, error
1817
file, err := parser.ParseFile(fileSet, fileName, src, parserMode)
1918
return fileSet, file, err
2019
}
21-
22-
// ParseFile 解析文件,优先尝试使用 ParseDir
23-
func ParseFile(fileName string, src []byte) (*token.FileSet, *ast.File, error) {
24-
fileSet := token.NewFileSet()
25-
var file *ast.File
26-
parserMode := parser.Mode(0) | parser.ParseComments
27-
pkgs, err := parser.ParseDir(fileSet, filepath.Dir(fileName), nil, parserMode)
28-
if err == nil {
29-
for _, pkg := range pkgs {
30-
for name, f := range pkg.Files {
31-
if name == fileName {
32-
file = f
33-
}
34-
}
35-
}
36-
}
37-
if file == nil {
38-
fileSet = token.NewFileSet()
39-
file, err = parser.ParseFile(fileSet, fileName, src, parserMode)
40-
}
41-
return fileSet, file, err
42-
}

internal/common/request.go

Lines changed: 150 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,173 @@
55
package common
66

77
import (
8+
"fmt"
89
"go/ast"
910
"go/token"
11+
"os"
12+
"reflect"
13+
"sort"
1014
)
1115

1216
// Request 一次格式化的请求
1317
type Request struct {
14-
FileName string
15-
FSet *token.FileSet
16-
AstFile *ast.File
17-
Opt Options
18+
FileName string
19+
FSet *token.FileSet
20+
AstFile *ast.File
21+
Opt Options
22+
tokenLine *TokenLine
23+
}
24+
25+
// TokenLine 获取 TokenLine
26+
func (req *Request) TokenLine() *TokenLine {
27+
if req.tokenLine == nil {
28+
req.tokenLine = &TokenLine{
29+
file: req.FSet.File(req.AstFile.Pos()),
30+
}
31+
}
32+
return req.tokenLine
1833
}
1934

2035
// FormatFile 将 AstFile 格式化、得到源码
2136
func (req *Request) FormatFile() ([]byte, error) {
2237
return req.Opt.Source(req.FSet, req.AstFile)
2338
}
2439

40+
// Save 保存文件
41+
func (req *Request) Save(name string) error {
42+
code, err := req.FormatFile()
43+
if err != nil {
44+
return err
45+
}
46+
return os.WriteFile(name, code, 0644)
47+
}
48+
2549
// ReParse 重新解析
26-
func (req *Request) ReParse() (*token.FileSet, *ast.File, error) {
50+
func (req *Request) ReParse() error {
51+
fs, f, err := req.reParse()
52+
if err == nil {
53+
req.FSet = fs
54+
req.AstFile = f
55+
}
56+
return err
57+
}
58+
59+
// MustReParse 重新解析,若失败会 panic
60+
func (req *Request) MustReParse() {
61+
if err := req.ReParse(); err != nil {
62+
panic(fmt.Errorf("reParse %q failed: %w", req.FileName, err))
63+
}
64+
}
65+
66+
func (req *Request) reParse() (*token.FileSet, *ast.File, error) {
67+
req.TokenLine().Execute()
2768
code, err := req.FormatFile()
2869
if err != nil {
2970
return nil, nil, err
3071
}
72+
req.tokenLine = nil
3173
return ParseOneFile(req.FileName, code)
3274
}
75+
76+
// Clone reParser it and return a new Request
77+
func (req *Request) Clone() *Request {
78+
c := &Request{
79+
FileName: req.FileName,
80+
Opt: req.Opt,
81+
}
82+
fs, f, err := req.reParse()
83+
if err != nil {
84+
panic(fmt.Sprintf("reParser %q failed: %v", req.FileName, err))
85+
}
86+
c.FSet = fs
87+
c.AstFile = f
88+
return c
89+
}
90+
91+
// TokenLine 记录对文件的换行的处理
92+
type TokenLine struct {
93+
addPos []int
94+
deleteLine []int
95+
file *token.File
96+
}
97+
98+
// AddLine 在指定位置添加新行
99+
func (tf *TokenLine) AddLine(depth int, at token.Pos) {
100+
if Debug {
101+
DebugPrintln(depth+1, "AddLine", tf.file.Position(at), "atPos=", at)
102+
}
103+
tf.addPos = append(tf.addPos, tf.file.Offset(at))
104+
}
105+
106+
// DeleteLine 删除指定位置的新行
107+
func (tf *TokenLine) DeleteLine(depth int, line int) {
108+
if line < 1 {
109+
panic(fmt.Sprintf("invalid line number %d (should be >= 1)", line))
110+
}
111+
max := tf.file.LineCount()
112+
if line > max {
113+
panic(fmt.Sprintf("invalid line number %d (should be < %d)", line, max))
114+
}
115+
if Debug {
116+
DebugPrintln(depth+1, "DeleteLine=", line, "lineStart=", tf.file.LineStart(line))
117+
}
118+
tf.deleteLine = append(tf.deleteLine, line)
119+
}
120+
121+
// Execute 将 Add、 Delete 的结果生效
122+
func (tf *TokenLine) Execute() {
123+
if len(tf.addPos) == 0 && len(tf.deleteLine) == 0 {
124+
return
125+
}
126+
defer func() {
127+
tf.addPos = nil
128+
tf.deleteLine = nil
129+
}()
130+
lines := tokenFileLines(tf.file)
131+
lines = intSliceDelete(lines, tf.deleteLine...)
132+
133+
lm := make(map[int]struct{}, len(lines))
134+
for _, v := range lines {
135+
lm[v] = struct{}{}
136+
}
137+
for _, offset := range tf.addPos {
138+
lm[offset] = struct{}{}
139+
}
140+
141+
result := make([]int, 0, len(lm))
142+
for k := range lm {
143+
if k >= tf.file.Size() {
144+
continue
145+
}
146+
result = append(result, k)
147+
}
148+
sort.Ints(result)
149+
if !tf.file.SetLines(result) {
150+
panic(fmt.Sprintf("SetLines failed, size=%d, lines=%v", tf.file.Size(), result))
151+
}
152+
}
153+
154+
func tokenFileLines(f *token.File) []int {
155+
field := reflect.ValueOf(f).Elem().FieldByName("lines")
156+
total := field.Len()
157+
lines := make([]int, 0, total)
158+
for i := 0; i < total; i++ {
159+
cur := int(field.Index(i).Int())
160+
lines = append(lines, cur)
161+
}
162+
return lines
163+
}
164+
165+
func intSliceDelete(lines []int, delete ...int) []int {
166+
dm := make(map[int]bool, len(delete))
167+
for _, v := range delete {
168+
dm[v] = true
169+
}
170+
result := make([]int, 0, len(lines))
171+
for line, v := range lines {
172+
if !dm[line] {
173+
result = append(result, v)
174+
}
175+
}
176+
return result
177+
}

0 commit comments

Comments
 (0)