Skip to content

fsgo/go_fmt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go 代码格式化

1 功能说明

  • 格式化 import 部分:分3段式,依默认为 标准库第三方库项目自身库
  • 格式化单行注释:若为 //注释内容,调整为 //{空格}注释内容
  • 默认只对 git 项目库里有修改的进行格式化
  • 支持将多行的 copyright 注释修改为单行格式(默认不调整)
  • 简化代码
  • struct 赋值表达式,自动补齐 key
  • 补充空行、移除多余的空行

对于 import 部分:

1.可使用-mi参数来控制是否将多段import合并为一段(默认否)。
2.对于注释的import path,会按照其实际路径参与分组和排序。
3.对于非末行的注释的位置会和其下面紧挨的import path绑定在一起。
4.末行的注释则会放入import的头部。
5.import path 不要使用相对路径(如./../)。

会忽略当前目录以及子目录下的 testdatavendor 目录。
若需要可进入其目录里执行该命令。

Example 1:补齐 struct key
- u2 := User{"hello", 12}
+ u2 := User{Name: "hello", Age: 12}
Example 2:注释格式化
- //User 注释内容
+ // User 注释内容
type User struct{
Example 3:简化代码

1.简化循环逻辑:

- s[a:len(s)]
+ s[a:]

- for x, _ = range v {...}
+ for x = range v {...}

- for _ = range v {...}
+ for range v {...}

- for {
+ for ok{
-   if !ok {
-     break
-   }
   // do something
 }

2.简化判断逻辑:

- if b == true {
+ if b { 

- if b == false {
+ if !b {

- if b != true {
+ if !b {

- if b != false {
+ if b {

- for b == true {
+ for b {

- _ = 1 == index
+ _ = index == 1

- _ = 1 < index
+ _ = index > 1

 func ok() bool {
- 	if a > b {
- 		return true
- 	}else{
- 		return false
+ 	return a > b
} 

func ok() bool {
- 	if a > b {
- 		return true
- 	return false
+ 	return a > b
} 

- if val!=nil && len(val)!=0 {
+ if len(val)!=0 {
   // do something
}

3.使用 strings.Contains 替换 strings.Countstrings.Index

- strings.Count(s, "a") == 0
+ !strings.Contains(s, "a")

- strings.Count(s, "a") > 0
+ strings.Contains(s, "a")

- strings.Count(s, "a") != 0
+ strings.Contains(s, "a")

bytes.Count 具有和 strings.Count 一样的规则。

- strings.Index(s, "a") == -1
+ !strings.Contains(s, "a")

- strings.Index(s, "a") != -1
+ strings.Contains(s, "a")

bytes.Index 具有和 strings.Index 一样的规则。

4.字符串的比较:

使用 bytes.Equal 替换 bytes.Compare

- bytes.Compare(s,a) == 0
+ bytes.Equal(s, a)

- bytes.Compare(s,a) != 0
+ !bytes.Equal(s, a)

使用 == 替换 strings.Compare

- strings.Compare("abc","a") == 0
+ "abc" == "a"

- strings.Compare("abc","a") != 0
+ "abc" != "a"

5.递增 1、递减 1:

- i += 1
+ i++

- i -= 1
+ i--

6.time.Since 和 time.Until time.Since 替换 time.Now().Sub:

- time.Now().Sub( t1 )
+ time.Since( t1 )

time.Until 替换 t.Sub( time.Now() ):

- t1.Sub( time.Now() )
+ time.Until( t1 )

7.channel:

- _ = <-chan
+ <-done

8.map:

- x, _ := someMap["key"]
+ x := someMap["key"]

9.fmt:

- fmt.Errorf("hello")
+ errors.New("hello")

- fmt.Printf("abc")
+ fmt.Print("abc")

- log.Printf("abc")
+ log.Print("abc")

- _ = errors.New(fmt.Sprintf("hello"))
+ _ = errors.New("hello")

- _ = errors.New(fmt.Sprintf("hello %s", "world"))
+ _ = fmt.Errorf("hello %s", "world")

- bf.Write([]byte(fmt.Sprintf("hello %d", 1)))
+ fmt.Fprintf(bf,"hello %d",1)

- fmt.Sprintf("%d",123)  // 性能  1
+ strconv.Atoi(123)      // 性能  3

- fmt.Sprintf("%v",123)
+ strconv.Atoi(123)   

- fmt.Sprintf("%d",int32Num)
+ strconv.FormatInt(int64(int32Num), 10)

- fmt.Sprintf("%d",uint32Num)
+ strconv.FormatUint(uint64(uint32Num), 10)

10.raw string :

- regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")
+ regexp.Compile(`\A(\w+) profile: total \d+\n\z`)

11.sort :

- sort.Sort(sort.StringSlice(x))
+ sort.Strings(x)
Example 4:基于表达式规则,重写代码 使用 `-rr=false` 可以使用默认内置规则不生效。
  1. 替换废弃的 ioutil 的函数调用:
import (
-	"io/ioutil"
+	"io
)

- buf, err := ioutil.ReadAll(f)
+ buf, err := io.ReadAll(f)
Example 5:移除多余的空行
  1. 移除 struct 内部前后多余的空行:
type userfn91 struct{
-				
	name string
-				
}
  1. 移除 func 内部前后多余的空行:
fn1() {
-				
	println("hello")
-				
}
  1. 空 func 变为一行:
- fn1() {
- }
+ fn1() {}
Example 6:补充空行 在适当的位置添加空行可以增加代码的可读性。
  1. struct 有文档的字段前后添加换行:
type User1 struct {
-				
	// on Name
	Name string
+				
	// on Age
	Age int
+				
	Grade int
	Class int

	Address string // 前面有空行,会保持
}
  1. interface 有文档的方法前后添加换行:
type Group1 interface {
-				
	// Register 注册延迟函数
	Register(fn func())
+				
	Add()
+				
	// on Delete
	Delete()
+				
	Fn1()

	Fn2() // 前面有空行,会保持
-				
}
  1. 多个定义之间添加空行:
type (
	User1 struct {
		name string
	}
+				
	User1 struct {
-				
		name string
	}
)
  1. 全局的,不同类型定义之间添加空行:
var a="hello"
var b="world" // after b
+				
const c01="say"
+ 				
var a0 = "a0"
Example 7:Array / Slice 格式化
- var _ = []int{
-	1, 2, 
-	3, 4, 5}
// 当代码是如上这种 3 行格式的时候(这 3 行内不能有注释),会格式化为下面这样。
// 上面第一行是 2 个元素,所以按照每行 2 个元素格式化对齐。

+	var _ = []int{
+	 1, 2,
+	 3, 4,
+	 5,
+	 }

2 安装/更新

export GO111MODULE=on
go env GOPROXY=https://goproxy.cn,direct

go install github.com/fsgo/go_fmt/cmd/gorgeous@latest

升级 Go 版本后,请用最新版本 go 重新安装/更新 gorgeous
最低 Go 版本:go1.22

3 使用

3.0 help

gorgeous -help

usage: gorgeous [flags] [path ...]
  -d	display diffs instead of rewriting files
  -df string
    	display diffs format, support: text, json (default "text")
  -e	enable extra rules (default true)
  -ig string
    	import groups sort rule,
    	stc: Go Standard package, Third Party package, Current package
    	sct: Go Standard package, Current package, Third Party package
    	 (default "stc")
  -local string
    	current package path, will put imports beginning with this string as 3rd-party packages.
    	by default, it will got from 'go.mod' file.
    	 (default "auto")
  -mi
    	merge imports into one section.
    	with env 'GORGEOUS_MI=false' to set default value as false (default true)
  -r value
    	rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')
    	or a file path for rewrite rules (like -rr)

  -rr
    	rewrite with build in rules:
    	a[b:len(a)] -> a[b:]
    	interface{} -> any                    // go1.18
    	io/#ioutil.NopCloser -> io.NopCloser  // go1.16
    	io/#ioutil.ReadAll   -> io.ReadAll    // go1.16
    	io/#ioutil.ReadFile  -> os.ReadFile   // go1.16
    	io/#ioutil.TempFile  -> os.CreateTemp // go1.16
    	io/#ioutil.TempDir   -> os.MkdirTemp  // go1.16
    	io/#ioutil.WriteFile -> os.WriteFile  // go1.16
    	io/#ioutil.Discard   -> io.Discard    // go1.16

    	with env 'GORGEOUS_RR=false' to set default value as false
    	 (default true)
  -s	simplify code (default true)
  -slcr
    	multiline copyright to single-line
    	with env 'GORGEOUS_SLCR=true' to set default value as true (default true)
  -trace
    	show trace messages
  -w	write result to (source) file instead of stdout (default true)

3.1 格式化 git 项目里有修改的.go文件

$ gorgeous

3.2 对当前目录所有 .go 文件格式化

$ gorgeous ./...

3.3 对指定 .go 文件格式化

$ gorgeous abc.go

3.4 从 STDIN 读取代码并输出到 STDOUT

$ cat code.go|gorgeous stdin

4 git hooks

在执行 git 命令时自动格式化或者检查是否已经完成格式化。

4.1 使用 bin-auto-switcher 添加全局 Hooks

bin-auto-switcher 可以很方便的对任意命令添加前置和后置 Hooks。

  1. 安装 git 替换命令(安装后,执行 git 命令时,实际会执行此命令,并执行配置的 Hooks):
    go install github.com/fsgo/bin-auto-switcher/git@latest
  2. 编辑配置文件 ~/.config/bas/git.toml
[[Rules]]
Cmd = "/usr/local/bin/git" # 替换为实际 git 命令的地址

[[Rules.Pre]]
Match = "^add\\s"  # 在执行 git add 命令前执行
Trace = true       # 打印日志
#Cond  = ["in_dir /home/work/goapps"]
Cond  = ["go_module"]
Cmd   = "gorgeous"

# 在 go.mod 文件所在目录下执行 gorgeous 命令
#Cmd   = "inner:find-exec"
#Args  = ["-name","go.mod","-dir_not","testdata","gorgeous"]

4.2 使用 git hooks

编辑项目的 .git/hooks/pre-commit文件,将gorgeous命令加入。

# 检查是否格式化
echo -e '\n gorgeous -d \n' >> $(git rev-parse --git-dir)/hooks/pre-commit

chmod 777 $(git rev-parse --git-dir)/hooks/pre-commit

# 或者:自动格式化
echo -e '\n gorgeous \n git add . \n' >> $(git rev-parse --git-dir)/hooks/pre-commit

还可以配置到全局 Hooks:

该方式会导致项目自身的 hooks 失效。
若项目有自己的 hooks,请不要配置全局而要配置到单个项目。

mkdir -p ~/.git_config/hooks/
git config --global core.hooksPath ~/.git_config/hooks/

echo -e '\n gorgeous -d\n' >>  ~/.git_config/hooks/pre-commit
chmod 777 ~/.git_config/hooks/pre-commit

5 GitHub Actions

- name: Set up Go
  uses: actions/setup-go@v4
  with:
    go-version: 1.20

- name: gorgeous style check
  run: go install github.com/fsgo/go_fmt/cmd/gorgeous@latest && gorgeous -d ./...

6 Visual Studio Code

6.1 As goformat

  1. Install as goformat:
go install github.com/fsgo/go_fmt/cmd/goformat@latest
  1. 配置的 Go: Format Tool,设置为 "goformat":
  "go.formatTool": "goformat"

6.2 Run on Save

  1. 先安装插件 Run on Save

  2. 配置插件,在保存文件的时候执行格式化命令:

  "runOnSave.commands": [
    {
        "match": "\\.go$",
        "command": "cd ${fileDirname} && gorgeous -rr ${fileBasename}",
        "runIn":"terminal"
    }
 ]

3.配置的 Go: Format Tool,设置为 "default":

  "go.formatTool": "default"