diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7af6886 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,73 @@ +name: create-release + +on: + push: + branches: + - main # 监听 main 分支的 push 操作(编译和测试/代码检查) + tags: + - 'v*' # 监听以 'v' 开头的标签的 push 操作(发布 Release) + +jobs: + lint: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.23.x" + - uses: actions/checkout@v4 + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + + test: + runs-on: ubuntu-latest + strategy: + matrix: + go: [ "1.22.x", "1.23.x" ] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go }} + + - name: Run test + run: make test COVERAGE_DIR=/tmp/coverage + + - name: Send goveralls coverage + uses: shogo82148/actions-goveralls@v1 + with: + path-to-profile: /tmp/coverage/combined.txt + flag-name: Go-${{ matrix.go }} + parallel: true + + check-coverage: + name: Check coverage + needs: [ test ] + runs-on: ubuntu-latest + steps: + - uses: shogo82148/actions-goveralls@v1 + with: + parallel-finished: true + + # 发布 Release + release: + name: Release a new version + needs: [ lint, test ] + runs-on: ubuntu-latest + # 仅在推送标签时执行 + if: ${{ success() && startsWith(github.ref, 'refs/tags/v') }} + steps: + # 1. 检出代码 + - name: Checkout code + uses: actions/checkout@v4 + + # 2. 创建 Release 和上传源码包 + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7b2c0ba --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 yangyile-yyle88 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3c30a15 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +COVERAGE_DIR ?= .coverage + +# cp from: https://github.com/yyle88/erero/blob/aacef44379ac6c5e3c831d1df6b47de10d731a88/Makefile#L4 +test: + @-rm -r $(COVERAGE_DIR) + @mkdir $(COVERAGE_DIR) + make test-with-flags TEST_FLAGS='-v -race -covermode atomic -coverprofile $$(COVERAGE_DIR)/combined.txt -bench=. -benchmem -timeout 20m' + +test-with-flags: + @go test $(TEST_FLAGS) ./... diff --git a/README.md b/README.md index bdca598..1144375 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,114 @@ +[![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/yyle88/formatgo/release.yml?branch=main&label=BUILD)](https://github.com/yyle88/formatgo/actions/workflows/release.yml?query=branch%3Amain) +[![GoDoc](https://pkg.go.dev/badge/github.com/yyle88/formatgo)](https://pkg.go.dev/github.com/yyle88/formatgo) +[![Coverage Status](https://img.shields.io/coveralls/github/yyle88/formatgo/master.svg)](https://coveralls.io/github/yyle88/formatgo?branch=main) +![Supported Go Versions](https://img.shields.io/badge/Go-1.22%2C%201.23-lightgrey.svg) +[![GitHub Release](https://img.shields.io/github/release/yyle88/formatgo.svg)](https://github.com/yyle88/formatgo/releases) +[![Go Report Card](https://goreportcard.com/badge/github.com/yyle88/formatgo)](https://goreportcard.com/report/github.com/yyle88/formatgo) + # formatgo -格式化代码 gofmt 工具 format golang source code 的工具,当然顺带还能整理 import 的引用内容 -还是非常重要的 +`formatgo` is a Go package that provides utilities for formatting Go source code, whether it's in a byte slice, string, or a file, and even for entire directories containing Go files. + +## Installation + +To install the `formatgo` package, you can run the following command: + +```bash +go get github.com/yyle88/formatgo +``` + +## Usage + +The package provides several functions for formatting Go code. Below are the main functions that you can use: + +### `FormatBytes` + +Formats Go source code from a byte slice. + +```go +formattedCode, err := formatgo.FormatBytes(code []byte) +``` + +- `code`: The source code as a byte slice. +- Returns the formatted code as a byte slice or an error if something goes wrong. + +### `FormatCode` + +Formats Go source code from a string. + +```go +formattedCode, err := formatgo.FormatCode(code string) +``` + +- `code`: The source code as a string. +- Returns the formatted code as a string or an error if something goes wrong. + +### `FormatFile` + +Formats a Go source code file at the given path. + +```go +err := formatgo.FormatFile(path string) +``` + +- `path`: The path to the Go source code file. +- Returns an error if the formatting fails. + +### `FormatRoot` + +Formats all Go source files in the specified root directory and its subdirectories. + +```go +err := formatgo.FormatRoot(root string) +``` + +- `root`: The root directory to start formatting files from. +- Returns an error if something goes wrong during the formatting process. + +## Example + +Here’s a simple example of how to format Go code from a string: + +```go +package main + +import ( + "fmt" + "github.com/yyle88/formatgo" +) + +func main() { + code := `package main + +import "fmt" + +func main() {fmt.Println("Hello, world!")}` + + formattedCode, err := formatgo.FormatCode(code) + if err != nil { + fmt.Println("Error formatting code:", err) + return + } + + fmt.Println("Formatted Code:", formattedCode) +} +``` + +## License + +`formatgo` is open-source and released under the MIT License. See the LICENSE file for more information. + +--- + +## Support + +Welcome to contribute to this project by submitting pull requests or reporting issues. + +If you find this package helpful, give it a star on GitHub! + +**Thank you for your support!** + +**Happy Coding with `formatgo`!** 🎉 Give me stars. Thank you!!! diff --git a/README.zh.md b/README.zh.md new file mode 100644 index 0000000..e060a84 --- /dev/null +++ b/README.zh.md @@ -0,0 +1,104 @@ +# formatgo + +`formatgo` 是一个 Go 包,用于格式化 Go 源代码,无论是字节切片、字符串还是文件,甚至是包含 Go 文件的整个目录。 + +## 安装 + +你可以通过以下命令安装 `formatgo` 包: + +```bash +go get github.com/yyle88/formatgo +``` + +## 使用方法 + +该包提供了多个函数来格式化 Go 代码,下面是主要的函数及其用法: + +### `FormatBytes` + +从字节切片格式化 Go 源代码。 + +```go +formattedCode, err := formatgo.FormatBytes(code []byte) +``` + +- `code`: Go 源代码(字节切片)。 +- 返回值:格式化后的代码(字节切片)或者格式化出错时的错误。 + +### `FormatCode` + +从字符串格式化 Go 源代码。 + +```go +formattedCode, err := formatgo.FormatCode(code string) +``` + +- `code`: Go 源代码(字符串)。 +- 返回值:格式化后的代码(字符串)或者格式化出错时的错误。 + +### `FormatFile` + +格式化指定路径下的 Go 源代码文件。 + +```go +err := formatgo.FormatFile(path string) +``` + +- `path`: Go 源代码文件的路径。 +- 返回值:格式化失败时的错误。 + +### `FormatRoot` + +格式化指定根目录及其子目录下的所有 Go 源代码文件。 + +```go +err := formatgo.FormatRoot(root string) +``` + +- `root`: 要开始格式化的根目录路径。 +- 返回值:格式化过程中发生错误时的错误。 + +## 示例 + +以下是一个简单的例子,演示如何从字符串格式化 Go 代码: + +```go +package main + +import ( + "fmt" + "github.com/yyle88/formatgo" +) + +func main() { + code := `package main + +import "fmt" + +func main() {fmt.Println("Hello, world!")}` + + formattedCode, err := formatgo.FormatCode(code) + if err != nil { + fmt.Println("格式化代码时出错:", err) + return + } + + fmt.Println("格式化后的代码:", formattedCode) +} +``` + +## 许可证 + +`formatgo` 是开源项目,采用 MIT 许可证。详情请查看 LICENSE 文件。 + +## 贡献与支持 + +欢迎通过提交 pull request 或报告问题来贡献此项目。 + +如果你觉得这个包对你有帮助,请在 GitHub 上给个 ⭐,感谢支持!!! + +**感谢你的支持!** + +**祝编程愉快!** 🎉 + +Give me stars. Thank you!!! diff --git a/format.go b/format.go index 3312030..32a6080 100644 --- a/format.go +++ b/format.go @@ -13,47 +13,57 @@ import ( "golang.org/x/tools/imports" ) -// FormatBytesWithOptions 格式化golang的源代码,当出错时依然返回中间某个阶段的代码,这样出错时返回值也还是可以用的 +// FormatBytesWithOptions formats Go source code from a byte slice. +// Even if an error occurs during formatting, it returns the intermediate code. +// FormatBytesWithOptions 格式化 Go 的源代码(以字节切片形式提供)。 +// 即使在格式化期间发生错误,也会返回中间结果代码。 func FormatBytesWithOptions(code []byte, options *Options) ([]byte, error) { + // Step 1: Format source code + // 步骤 1:格式化源代码 if newSrc, err := format.Source(code); err != nil { - return code, erero.Wro(err) + return code, erero.Wro(err) // Return the original code if an error occurs // 如果发生错误,返回原始代码 } else { - code = newSrc // 存储前一阶段的正确结果 + code = newSrc // Save the successful intermediate result // 保存格式化后的中间结果 } - if options.IsCleanImportNewlines { // 清理引用语句中的空行,以便于整理引用语句 - if newSrc, err := CleanImportNewlines(code); err != nil { + // Step 2: Condense import statements if enabled + // 步骤 2:如果启用,合并导入语句 + if options.CondenseImport { + if newSrc, err := CleanCodeImportNewlines(code); err != nil { return code, erero.Wro(err) } else { - code = newSrc // 存储前一阶段的正确结果 + code = newSrc // Save the successful intermediate result // 保存格式化后的导入语句中间结果 } } - if options.IsProcessFormatImport { // 接下来顺带把 imports 整理整理 + // Step 3: Format imports if enabled + // 步骤 3:如果启用,格式化导入语句 + if options.IsFormatImport { if newSrc, err := imports.Process("", code, options.ImportsOptions); err != nil { return code, erero.Wro(err) } else { - code = newSrc // 存储前一阶段的正确结果 + code = newSrc // Save the successful intermediate result // 保存格式化后的导入语句中间结果 } } return code, nil } -// FormatCodeWithOptions 格式化源代码字符串,当出错时依然返回中间某个阶段的代码,这样出错时返回值也还是可以用的 +// FormatCodeWithOptions formats Go source code from a string. +// Even if an error occurs during formatting, it returns the intermediate code as a string. +// FormatCodeWithOptions 格式化 Go 的源代码(以字符串形式提供)。 +// 即使在格式化期间发生错误,也会返回中间结果代码(字符串形式)。 func FormatCodeWithOptions(code string, options *Options) (string, error) { newSrc, err := FormatBytesWithOptions([]byte(code), options) if err != nil { - return string(newSrc), erero.Wro(err) + return string(newSrc), erero.Wro(err) // Return intermediate result even on error // 即使发生错误,仍然返回中间结果 } return string(newSrc), nil } -// FormatSourceWithOptions 跟 FormatCodeWithOptions 完全是一样的,只是函数名称不同,看外部调用者喜欢哪个名称吧 -func FormatSourceWithOptions(code string, options *Options) (string, error) { - return FormatCodeWithOptions(code, options) -} - -// FormatFileWithOptions 格式化源代码文件 +// FormatFileWithOptions formats a Go source code file. +// It reads the file, formats it, and writes back the result if changes are made. +// FormatFileWithOptions 格式化一个 Go 源代码文件。 +// 它会读取文件内容,进行格式化,如果内容有变化则写回结果。 func FormatFileWithOptions(path string, options *Options) error { source, err := os.ReadFile(path) if err != nil { @@ -63,43 +73,55 @@ func FormatFileWithOptions(path string, options *Options) error { if err != nil { return erero.Wro(err) } + // Skip writing if no changes are detected + // 如果没有变化,跳过写入 if bytes.Equal(source, newSrc) { return nil } - return utils.WriteFileKeepFileMode(path, newSrc) + return utils.WriteFileKeepMode(path, newSrc) // Write the formatted content + // 写入格式化后的内容 } -// FormatRootWithOptions 格式化整个目录以及其子目录下的所有go文件 +// FormatRootWithOptions formats all Go files in a directory and its subdirectories. +// It recursively processes each directory and applies the provided options. +// FormatRootWithOptions 格式化指定目录及其子目录下的所有 Go 文件。 +// 它递归处理每个目录,并根据提供的选项进行格式化。 func FormatRootWithOptions(root string, options *RootOptions) error { - return formatRootWithOptions(root, 0, options) + return formatRootRecursive(root, 0, options) } -// FormatProjectWithOptions 格式化整个项目里所有的go文件 -func FormatProjectWithOptions(projectRoot string, options *RootOptions) error { - return formatRootWithOptions(projectRoot, 0, options) -} - -func formatRootWithOptions(root string, depth int, options *RootOptions) error { - mapNamePath, err := utils.LsMapName2Path(root) +// formatRootRecursive is a helper function for FormatRootWithOptions. +// It recursively processes directories and files based on the given options. +// formatRootRecursive 是 FormatRootWithOptions 的辅助函数。 +// 它根据给定的选项递归处理目录和文件。 +func formatRootRecursive(root string, depth int, options *RootOptions) error { + mapNamePath, err := utils.LsAsMap(root) if err != nil { return erero.Wro(err) } + for name, path := range mapNamePath { + // Skip hidden directories/files based on depth + // 根据深度跳过隐藏的目录/文件 if strings.HasPrefix(name, ".") { - if depth < options.MinSkipHiddenDepth { //在若干层以内跳过隐藏目录 - continue //跳过不可见的目录,比如.git目录和.idea目录 + if depth < options.SkipHiddenDepth { + continue // Skip hidden directories like .git or .idea // 跳过隐藏的目录,如 .git 或 .idea } } + // Process subdirectories + // 处理子目录 if done.VBE(utils.IsRootExists(path)).Done() { - if options.FilterRootFunction(depth, path, name) { - if err := formatRootWithOptions(path, depth+1, options); err != nil { + if options.FilterRoot(depth, path, name) { + if err := formatRootRecursive(path, depth+1, options); err != nil { return erero.Wro(err) } } } else if done.VBE(utils.IsFileExists(path)).Done() { - if filepath.Ext(name) == ".go" || utils.HasAnySuffix(name, options.FileNameSuffixes) { - if options.FilterFileFunction(depth, path, name) { + // Check if the file is a Go file or matches the specified suffixes + // 检查文件是否为 Go 文件,或是否与指定的后缀匹配 + if filepath.Ext(name) == ".go" || utils.HasAnySuffix(name, options.FileHasSuffixes) { + if options.FilterFile(depth, path, name) { if err := FormatFileWithOptions(path, options.FileOptions); err != nil { return erero.Wro(err) } diff --git a/format_test.go b/format_test.go index 5794032..c7713d0 100644 --- a/format_test.go +++ b/format_test.go @@ -16,14 +16,14 @@ func TestFormatBytesWithOptions(t *testing.T) { ) func TestGetImportsOptions(t *testing.T) { - _ ,_ = pretty.Println(GetImportsOptions()) + _ ,_ = pretty.Println(NewImportsOptions()) } ` - newSrc, err := FormatBytesWithOptions([]byte(code), NewOptions()) + source, err := FormatBytesWithOptions([]byte(code), NewOptions()) require.NoError(t, err) - t.Log(string(newSrc)) + t.Log(string(source)) const want = `package formatgo @@ -34,8 +34,8 @@ import ( ) func TestGetImportsOptions(t *testing.T) { - _, _ = pretty.Println(GetImportsOptions()) + _, _ = pretty.Println(NewImportsOptions()) } ` - require.Equal(t, want, string(newSrc)) + require.Equal(t, want, string(source)) } diff --git a/go.mod b/go.mod index 431652b..e353836 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/yyle88/done v1.0.18 github.com/yyle88/erero v1.0.14 + github.com/yyle88/runpath v1.0.21 github.com/yyle88/syntaxgo v0.0.35 github.com/yyle88/zaplog v0.0.16 go.uber.org/zap v1.27.0 diff --git a/import.go b/import.go index 34705a4..9464fe1 100644 --- a/import.go +++ b/import.go @@ -11,68 +11,92 @@ import ( "golang.org/x/tools/imports" ) -func GetImportsOptions() *imports.Options { +// NewImportsOptions returns a new imports.Options with default settings. +// NewImportsOptions 返回一个具有默认设置的 imports.Options。 +func NewImportsOptions() *imports.Options { return &imports.Options{ - TabWidth: 4, - TabIndent: true, - Comments: true, - Fragment: true, + TabWidth: 4, // 设置制表符宽度为 4 + TabIndent: true, // 启用制表符缩进 + Comments: true, // 保留注释 + Fragment: true, // 启用代码片段 } } -func CleanImportNewlinesInFile(path string) error { +// CleanFileImportNewlines reads a Go source file, condenses the import section +// by removing consecutive empty lines, and writes the result back to the file. +// CleanFileImportNewlines 读取 Go 源代码文件,压缩导入部分 +// 通过去除连续的空行并将结果写回文件。 +func CleanFileImportNewlines(path string) error { source, err := os.ReadFile(path) if err != nil { - return erero.Wro(err) + return erero.Wro(err) // 返回读取文件时的错误 } - newSrc, err := CleanImportNewlines(source) + + // Condense the import lines to remove empty lines + // 压缩导入语句以去除空行 + newSrc, err := CleanCodeImportNewlines(source) if err != nil { - return erero.Wro(err) + return erero.Wro(err) // 返回压缩导入行时的错误 } - err = utils.WriteFileKeepFileMode(path, newSrc) + + // Write the new source code back to the file + // 将新源代码写回文件 + err = utils.WriteFileKeepMode(path, newSrc) if err != nil { - return erero.Wro(err) + return erero.Wro(err) // 返回写入文件时的错误 } + return nil } -// CleanImportNewlines processes the provided source code in byte form, -// focusing on the import section of a Go file. It removes consecutive -// empty lines within the import statements, ensuring that there is at -// most one newline between them. -func CleanImportNewlines(source []byte) ([]byte, error) { +// CleanCodeImportNewlines processes the source code and condenses consecutive +// empty lines in the import section to a single newline. +// CleanCodeImportNewlines 处理源代码,并将导入部分中的连续空行压缩为单一的换行符。 +func CleanCodeImportNewlines(source []byte) ([]byte, error) { astBundle, err := syntaxgo_ast.NewAstBundleV1(source) if err != nil { - return nil, erero.Wro(err) + return nil, erero.Wro(err) // 返回解析 AST 时的错误 } + astFile, _ := astBundle.GetBundle() + // If no imports exist, return the source as it is + // 如果没有导入语句,则直接返回原始源代码 if len(astFile.Imports) == 0 { return source, nil } + // Create a new node that covers the range of import statements + // 创建一个新的节点,覆盖所有导入语句的范围 node := syntaxgo_astnode.NewNode( astFile.Imports[0].Pos(), astFile.Imports[len(astFile.Imports)-1].End(), ) + + // Get the text of the import section + // 获取导入部分的文本 oldImports := node.GetText(source) if oldImports == "" { return source, nil } - newImports := condenseNewlines(oldImports) + + // Condense consecutive newlines into a single newline in the import section + // 在导入部分将连续的换行符压缩为一个换行符 + newImports := condenseLines(oldImports) if oldImports == newImports { - return source, nil + return source, nil // No changes made, return the source as it is // 如果没有变化,则返回原始源代码 } + // Change the import section to the condensed version + // 将导入部分替换为压缩后的版本 return syntaxgo_astnode.ChangeNodeCode(source, node, []byte(newImports)), nil } -// condenseNewlines takes a string input and removes consecutive empty lines, -// condensing multiple newlines into a single newline. -// The function iteratively replaces instances of double newlines with a single -// newline until no more consecutive newlines remain, -// ensuring that the output string contains at most one newline between lines。 -func condenseNewlines(source string) string { +// condenseLines replaces multiple consecutive newlines with a single newline. +// It iteratively replaces instances of double newlines until only single newlines remain. +// condenseLines 将多个连续的换行符替换为单个换行符。 +// 它通过迭代地替换双换行符,直到只剩下单个换行符。 +func condenseLines(source string) string { for origin := ""; origin != source; source = strings.ReplaceAll(source, "\n\n", "\n") { origin = source } diff --git a/import_test.go b/import_test.go index 943df0a..9976e0f 100644 --- a/import_test.go +++ b/import_test.go @@ -7,5 +7,5 @@ import ( ) func TestGetImportsOptions(t *testing.T) { - _, _ = pretty.Println(GetImportsOptions()) + _, _ = pretty.Println(NewImportsOptions()) } diff --git a/internal/utils/utils.go b/internal/utils/utils.go index e3e20a0..9c94b63 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -10,11 +10,11 @@ import ( "go.uber.org/zap" ) -func WriteFileKeepFileMode(path string, data []byte) error { - return os.WriteFile(path, data, GetFileModePerm(path)) +func WriteFileKeepMode(path string, data []byte) error { + return os.WriteFile(path, data, GetFileMode(path)) } -func GetFileModePerm(path string) os.FileMode { +func GetFileMode(path string) os.FileMode { stat, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { @@ -49,7 +49,7 @@ func IsFileExists(path string) (bool, error) { return !info.IsDir(), nil } -func LsMapName2Path(root string) (map[string]string, error) { +func LsAsMap(root string) (map[string]string, error) { names, err := Ls(root) if err != nil { return nil, erero.WithMessage(err, "wrong") diff --git a/internal/utils/utils_test.go b/internal/utils/utils_test.go index dbd17e6..d38ecc6 100644 --- a/internal/utils/utils_test.go +++ b/internal/utils/utils_test.go @@ -1,16 +1,11 @@ package utils import ( - "runtime" "testing" - "github.com/stretchr/testify/require" + "github.com/yyle88/runpath" ) -func TestGetFileModePerm(t *testing.T) { - _, path, _, ok := runtime.Caller(0) - require.True(t, ok) - t.Log(path) - - t.Log(GetFileModePerm(path)) +func TestGetFileMode(t *testing.T) { + t.Log(GetFileMode(runpath.Path())) } diff --git a/option.go b/option.go index a7fe9fe..b098b4a 100644 --- a/option.go +++ b/option.go @@ -8,43 +8,56 @@ import ( "golang.org/x/tools/imports" ) +// Options holds configuration options for imports formatting. +// Options 结构体保存了导入格式化的配置选项。 type Options struct { - ImportsOptions *imports.Options - IsCleanImportNewlines bool - IsProcessFormatImport bool + ImportsOptions *imports.Options // Options for formatting imports // 导入格式化的选项 + CondenseImport bool // Whether to condense imports by removing empty lines // 是否压缩导入部分,去除空行 + IsFormatImport bool // Whether to format import statements // 是否格式化导入语句 } +// NewOptions creates and returns a new Options instance with default values. +// NewOptions 创建并返回一个带有默认值的 Options 实例。 func NewOptions() *Options { return &Options{ - ImportsOptions: GetImportsOptions(), - IsCleanImportNewlines: true, - IsProcessFormatImport: true, + ImportsOptions: NewImportsOptions(), // 使用默认的导入选项创建 + CondenseImport: true, // 默认开启导入部分压缩 + IsFormatImport: true, // 默认启用导入格式化 } } +// RootOptions holds configuration options for the root directory formatting. +// RootOptions 结构体保存了根目录格式化的配置选项。 type RootOptions struct { - FileOptions *Options - FilterRootFunction func(depth int, path string, name string) bool //只格式化目录名/路径符合的 - FilterFileFunction func(depth int, path string, name string) bool //只格式化文件名/路径符合的,当然前提是目录已经符合 - FileNameSuffixes []string //要格式化的文件名称的后缀范围,由于是go代码的format,即使是为空也必然包含".go"这个后缀 - MinSkipHiddenDepth int //在多少层深度内跳过隐藏目录,比如第一层的 ".git" ".idea" 目录等 + FileOptions *Options // File formatting options // 文件格式化选项 + FilterRoot func(depth int, path string, name string) bool // Filter to format only directories that match certain criteria // 用于过滤目录名/路径的条件,只有符合条件的目录才会被格式化 + FilterFile func(depth int, path string, name string) bool // Filter to format only files that match certain criteria // 用于过滤文件名/路径的条件,只有符合条件的文件才会被格式化 + FileHasSuffixes []string // Suffixes of files to be formatted // 要格式化的文件后缀列表 + SkipHiddenDepth int // Depth level to skip hidden directories (e.g., .git, .idea) // 在多少层深度内跳过隐藏目录(例如 .git,.idea) } +// NewRootOptions creates and returns a new RootOptions instance with default values. +// NewRootOptions 创建并返回一个带有默认值的 RootOptions 实例。 func NewRootOptions() *RootOptions { return &RootOptions{ - FileOptions: NewOptions(), - FilterRootFunction: func(depth int, path string, name string) bool { + FileOptions: NewOptions(), // 默认使用 NewOptions() 创建 FileOptions + FilterRoot: func(depth int, path string, name string) bool { + // Debug log for root filtering + // 输出根目录过滤的调试日志 zaplog.LOG.Debug("format_root", zap.Int("depth", depth), zap.String("path", path), zap.String("name", name)) - return true + return true // 默认允许所有根目录 }, - FilterFileFunction: func(depth int, path string, name string) bool { + FilterFile: func(depth int, path string, name string) bool { + // Debug log for file filtering + // 输出文件过滤的调试日志 zaplog.LOG.Debug("format_file", zap.Int("depth", depth), zap.String("path", path), zap.String("name", name)) - return true + return true // 默认允许所有文件 }, - FileNameSuffixes: []string{ //默认值设置为空也行,在逻辑里有只要是 ".go" 就算通过,剩下的才通过这个后缀筛选过滤 - ".go", - ".GO", + FileHasSuffixes: []string{ // Default file suffixes to format + ".go", // Go source files + ".GO", // Go source files with uppercase extension }, - MinSkipHiddenDepth: math.MaxInt, //直接设置为最大值,就是任何层的隐藏文件都不进行格式化,认为这才是符合99%场景的,当然实际上默认值设置为1也行,毕竟99%的项目除了第0层以外就没有别的隐藏文件啦 + SkipHiddenDepth: math.MaxInt, // Skip hidden directories at all depths (default behavior) + // 跳过所有层级的隐藏目录(默认行为) } } diff --git a/simple.go b/simple.go index 2e5cfa4..376bbb5 100644 --- a/simple.go +++ b/simple.go @@ -1,31 +1,25 @@ package formatgo +// FormatBytes formats Go source code from a byte slice. // FormatBytes 格式化golang的源代码 func FormatBytes(code []byte) ([]byte, error) { return FormatBytesWithOptions(code, NewOptions()) } +// FormatCode formats Go source code from a string. // FormatCode 格式化源代码字符串 func FormatCode(code string) (string, error) { return FormatCodeWithOptions(code, NewOptions()) } -// FormatSource 格式化源代码字符串 -func FormatSource(code string) (string, error) { - return FormatSourceWithOptions(code, NewOptions()) -} - +// FormatFile formats a Go source code file at the given path. // FormatFile 格式化源代码文件 func FormatFile(path string) error { return FormatFileWithOptions(path, NewOptions()) } +// FormatRoot formats all Go source files in the specified root directory and its subdirectories. // FormatRoot 格式化整个目录以及其子目录下的所有go文件 func FormatRoot(root string) error { return FormatRootWithOptions(root, NewRootOptions()) } - -// FormatProject 格式化整个项目里所有的go文件 -func FormatProject(projectRoot string) error { - return FormatProjectWithOptions(projectRoot, NewRootOptions()) -} diff --git a/simple_test.go b/simple_test.go index ebc20c1..7234ba4 100644 --- a/simple_test.go +++ b/simple_test.go @@ -52,5 +52,5 @@ func TestFormatProject(t *testing.T) { t.Log(root) require.True(t, strings.HasSuffix(root, "/formatgo")) //这样99.99%能够确保目录路径是正确的 - require.NoError(t, FormatProject(root)) //把本项目格式化 + require.NoError(t, FormatRoot(root)) //把本项目格式化 }