Full Code of cuonglm/gocmt for AI

main 515d346459b3 cached
20 files
23.4 KB
7.4k tokens
57 symbols
1 requests
Download .txt
Repository: cuonglm/gocmt
Branch: main
Commit: 515d346459b3
Files: 20
Total size: 23.4 KB

Directory structure:
gitextract_p96zk_1u/

├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── LICENSE
├── README.md
├── doc.go
├── go.mod
├── go.sum
├── helper.go
├── helper_test.go
├── main.go
├── parse.go
├── parse_test.go
└── testdata/
    ├── empty_var_block.go
    ├── existed.go
    ├── generic.go
    ├── invalid_file.go
    ├── issue7.go
    ├── main.go
    ├── parenthesis.go
    └── vendor/
        └── main.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/ci.yml
================================================
name: Test

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: 1.19
      - name: Cache
        uses: actions/cache@v2
        with:
          path: |
            ~/go/bin
            ~/go/src
            ~/go/pkg/mod
            ~/.cache/go-build
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-
      - run: go version
      - run: go test -race -v ./...
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: golangci/golangci-lint-action@v2


================================================
FILE: .gitignore
================================================
.idea


================================================
FILE: LICENSE
================================================
Author:: LE Manh Cuong <cuong.manhle.vn@gmail.com>
Copyright:: Copyright (c) 2016, Cuong Manh Le
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.

    * Neither the name of the @organization@ nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LE MANH CUONG
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# gocmt - Add missing comment on exported function, method, type, constant, variable in go file

![Build status](https://github.com/cuonglm/gocmt/actions/workflows/ci.yml/badge.svg?branch=main)
[![Go Reference](https://pkg.go.dev/badge/github.com/cuonglm/gocmt.svg)](https://pkg.go.dev/github.com/cuonglm/gocmt)
[![Go Report Card](https://goreportcard.com/badge/github.com/cuonglm/gocmt)](https://goreportcard.com/report/github.com/cuonglm/gocmt)

# Installation

For go1.15 and below:

```sh
go get -u github.com/cuonglm/gocmt
```

For go1.16 and above:

```sh
go install github.com/cuonglm/gocmt@latest
```

# Why gocmt

Some of my projects have many files with exported fields, variables, functions missing comment, so lint tools will complain.

I find a way to auto add missing comment to them, just to pass the lint tools but nothing existed.

So `gocmt` comes in.

# Usage
```sh
$ gocmt -h
usage: gocmt [flags] [file ...]
  -d string
    	Directory to process
  -i	Make in-place editing
  -t string
    	Comment template (default "...")
```

# Example
```sh
$ cat testdata/main.go
package p

var i = 0

var I = 1

var c = "constant un-exported"

const C = "constant exported"

type t struct{}

type T struct{}

func main() {
}

func unexport(s string) {
}
func Export(s string) {
}

func ExportWithComment(s string) {
}
```

Using `gocmt` give you:
```sh
$ gocmt testdata/main.go
package p

var i = 0

// I ...
var I = 1

var c = "constant un-exported"

// C ...
const C = "constant exported"

type t struct{}

// T ...
type T struct{}

func main() {
}

func unexport(s string) {
}
// Export ...
func Export(s string) {
}

// ExportWithComment ...
func ExportWithComment(s string) {
}
```

Default template is `...`, you can change it using `-t` option.

# Author

Cuong Manh Le <cuong.manhle.vn@gmail.com>

# License

See [LICENSE](https://github.com/cuonglm/gocmt/blob/main/LICENSE)


================================================
FILE: doc.go
================================================
// gocmt adds missing comments on exported identifiers in Go source files.
//
// Usage:
//
//  gocmt [-i] [-p] [-t "comment template"] [-d dir]
//
// This tools exists because I have to work with some existed code base, which
// is lack of documentation for many exported identifiers. Iterating over them
// is time consuming and maybe not suitable at a stage of the project. So I
// wrote this tool to quickly bypassing CI system. Once thing is settle, we can
// lookback and fix missing comments.
//
// You SHOULD always write documentation for all your exported identifiers.
package main


================================================
FILE: go.mod
================================================
module github.com/cuonglm/gocmt

go 1.19

require github.com/stretchr/testify v1.6.1

require (
	github.com/davecgh/go-spew v1.1.0 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)


================================================
FILE: go.sum
================================================
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: helper.go
================================================
package main

import (
	"go/ast"
	"go/scanner"
	"os"
	"strings"
)

func isGoFile(f os.FileInfo) bool {
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}

func printError(err error) {
	scanner.PrintError(os.Stderr, err)
}

func walkFunc(path string, fi os.FileInfo, err error) error {
	if err == nil && isGoFile(fi) {
		err = processFile(path, *template, *inPlace)
	}

	if err != nil {
		return err
	}

	return nil
}

func isLineComment(comment *ast.CommentGroup) bool {
	if comment == nil {
		return false
	}
	if len(comment.List) == 0 {
		return false
	}
	head := comment.List[0].Text
	head = strings.TrimSpace(head)
	return strings.HasPrefix(head, "//")
}

func hasCommentPrefix(comment *ast.CommentGroup, prefix string) bool {
	return strings.HasPrefix(strings.TrimSpace(comment.Text()), prefix)
}

func appendCommentGroup(list []*ast.CommentGroup, item *ast.CommentGroup) []*ast.CommentGroup {
	ret := []*ast.CommentGroup{}
	hasInsert := false
	for _, group := range list {
		if group.Pos() < item.Pos() {
			ret = append(ret, group)
			continue
		}
		if group.Pos() == item.Pos() {
			ret = append(ret, item)
			hasInsert = true
			continue
		}
		if group.Pos() > item.Pos() {
			if !hasInsert {
				ret = append(ret, item)
				hasInsert = true
			}
			ret = append(ret, group)
			continue
		}
	}
	if !hasInsert {
		ret = append(ret, item)
	}
	return ret
}


================================================
FILE: helper_test.go
================================================
package main

import (
	"os"
	"testing"
)

func Test_isGoFile(t *testing.T) {
	isGoFileTest := []struct {
		path     string
		expected bool
	}{
		{"main.go", true},
		{"README.md", false},
	}

	for _, tt := range isGoFileTest {
		fi, err := os.Stat(tt.path)
		if err != nil {
			t.Fatal(err)
		}

		if got := isGoFile(fi); got != tt.expected {
			t.Fatalf("isGoFile(%+v): expected %v, got %v", fi, tt.expected, got)
		}
	}
}


================================================
FILE: main.go
================================================
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"go/token"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

var (
	// ensure that the comment starts on a newline (without the \n, sometimes it starts on the previous }
	commentBase = "\n// %s "
	// if it's in an indented block, this makes sure that the indentation is correct
	commentIndentedBase = "// %s "
	fset                = token.NewFileSet()
	defaultMode         = os.FileMode(0644)
	tralingWsRegex      = regexp.MustCompile(`(?m)[\t ]+$`)
	newlinesRegex       = regexp.MustCompile(`(?m)\n{3,}`)
)

var (
	inPlace      = flag.Bool("i", false, "Make in-place editing")
	template     = flag.String("t", "...", "Comment template")
	dir          = flag.String("d", "", "Directory to process")
	parenComment = flag.Bool("p", false, "Add comments to all const inside the parens if true")
)

func main() {
	os.Exit(gocmtRun())
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: gocmt [flags] [file ...]\n")
	flag.PrintDefaults()
}

func gocmtRun() int {
	flag.Parse()

	if *dir != "" {
		if err := filepath.Walk(*dir, walkFunc); err != nil {
			printError(err)
			return 1
		}
		return 0
	}

	if flag.NArg() == 0 {
		usage()
	}

	for i := 0; i < flag.NArg(); i++ {
		path := flag.Arg(i)
		switch fi, err := os.Stat(path); {
		case err != nil:
			printError(err)
		case fi.IsDir():
			printError(fmt.Errorf("%s is a directory", path))
		default:
			if err := processFile(path, *template, *inPlace); err != nil {
				printError(err)
				return 1
			}
		}
	}

	return 0
}

func processFile(filename, template string, inPlace bool) error {
	// skip test files and files in vendor/
	if strings.HasSuffix(filename, "_test.go") || strings.Contains(filename, "/vendor/") {
		return nil
	}

	af, modified, err := parseFile(fset, filename, template)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	if err := format.Node(&buf, fset, af); err != nil {
		panic(err)
	}

	newBuf := buf.Bytes()
	if modified {
		newBuf = tralingWsRegex.ReplaceAll(newBuf, []byte(""))
		newBuf = newlinesRegex.ReplaceAll(newBuf, []byte("\n\n"))
		if inPlace {
			return os.WriteFile(filename, newBuf, defaultMode)
		}

		fmt.Fprintf(os.Stdout, "%s", newBuf)
		return nil
	}

	fmt.Fprintf(os.Stderr, "%s no changes\n", filename)

	return nil
}


================================================
FILE: parse.go
================================================
package main

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"strings"
)

// parseFile parses and modifies the input file if necessary. Returns AST represents of (new) source, a boolean
// to report whether the source file was modified, and any error if occurred.
func parseFile(fset *token.FileSet, filePath, template string) (af *ast.File, modified bool, err error) {
	af, err = parser.ParseFile(fset, filePath, nil, parser.ParseComments|parser.AllErrors)
	if err != nil {
		return
	}

	// Inject first comment to prevent nil comment map
	if len(af.Comments) == 0 {
		af.Comments = []*ast.CommentGroup{{List: []*ast.Comment{{Slash: -1, Text: "// gocmt"}}}}
		defer func() {
			// Remove the injected comment
			af.Comments = af.Comments[1:]
		}()
	}

	commentTemplate := commentBase + template

	originalCommentSign := ""
	for _, c := range af.Comments {
		originalCommentSign += c.Text()
	}

	cmap := ast.NewCommentMap(fset, af, af.Comments)

	skipped := make(map[ast.Node]bool)
	ast.Inspect(af, func(n ast.Node) bool {
		switch typ := n.(type) {
		case *ast.FuncDecl:
			if skipped[typ] || !typ.Name.IsExported() {
				return true
			}
			addFuncDeclComment(typ, commentTemplate)
			cmap[typ] = appendCommentGroup(cmap[typ], typ.Doc)

		case *ast.DeclStmt:
			skipped[typ.Decl] = true

		case *ast.GenDecl:
			switch typ.Tok {
			case token.CONST, token.VAR:
				if !(typ.Lparen == token.NoPos && typ.Rparen == token.NoPos) {
					// if there's a () and parenComment is true, add comment for each sub entry
					if *parenComment {
						for _, spec := range typ.Specs {
							vs := spec.(*ast.ValueSpec)
							if !vs.Names[0].IsExported() {
								continue
							}
							addParenValueSpecComment(vs, commentTemplate)
							cmap[vs] = appendCommentGroup(cmap[vs], vs.Doc)
						}
						return true
					}
				}

				// empty var block
				if len(typ.Specs) == 0 {
					return true
				}

				vs := typ.Specs[0].(*ast.ValueSpec)
				if skipped[typ] || !vs.Names[0].IsExported() {
					return true
				}
				addValueSpecComment(typ, vs, commentTemplate)

			case token.TYPE:
				ts := typ.Specs[0].(*ast.TypeSpec)
				if skipped[typ] || !ts.Name.IsExported() {
					return true
				}
				addTypeSpecComment(typ, ts, commentTemplate)
			default:
				return true
			}
			cmap[typ] = appendCommentGroup(cmap[typ], typ.Doc)
		}
		return true
	})

	// Rebuild comments
	af.Comments = cmap.Filter(af).Comments()

	currentCommentSign := ""
	for _, c := range af.Comments {
		currentCommentSign += c.Text()
	}

	modified = currentCommentSign != originalCommentSign
	return
}

func addFuncDeclComment(fd *ast.FuncDecl, commentTemplate string) {
	if fd.Doc == nil || strings.TrimSpace(fd.Doc.Text()) == fd.Name.Name {
		text := fmt.Sprintf(commentTemplate, fd.Name)
		pos := fd.Pos() - token.Pos(1)
		if fd.Doc != nil {
			pos = fd.Doc.Pos()
		}
		fd.Doc = &ast.CommentGroup{List: []*ast.Comment{{Slash: pos, Text: text}}}
		return
	}
	if fd.Doc != nil && isLineComment(fd.Doc) && !hasCommentPrefix(fd.Doc, fd.Name.Name) {
		modifyComment(fd.Doc, fd.Name.Name)
		return
	}
}

func addValueSpecComment(gd *ast.GenDecl, vs *ast.ValueSpec, commentTemplate string) {
	if gd.Doc == nil || strings.TrimSpace(gd.Doc.Text()) == vs.Names[0].Name {
		text := fmt.Sprintf(commentTemplate, vs.Names[0].Name)
		pos := gd.Pos() - token.Pos(1)
		if gd.Doc != nil {
			pos = gd.Doc.Pos()
		}
		gd.Doc = &ast.CommentGroup{List: []*ast.Comment{{Slash: pos, Text: text}}}
		return
	}
	if gd.Doc != nil && isLineComment(gd.Doc) && !hasCommentPrefix(gd.Doc, vs.Names[0].Name) {
		modifyComment(gd.Doc, vs.Names[0].Name)
		return
	}
}

func addParenValueSpecComment(vs *ast.ValueSpec, commentTemplate string) {
	if vs.Doc == nil || strings.TrimSpace(vs.Doc.Text()) == vs.Names[0].Name {
		commentTemplate = strings.Replace(commentTemplate, commentBase, commentIndentedBase, 1)
		text := fmt.Sprintf(commentTemplate, vs.Names[0].Name)
		pos := vs.Pos() - token.Pos(1)
		if vs.Doc != nil {
			pos = vs.Doc.Pos()
		}
		vs.Doc = &ast.CommentGroup{List: []*ast.Comment{{Slash: pos, Text: text}}}
		return
	}
	if vs.Doc != nil && isLineComment(vs.Doc) && !hasCommentPrefix(vs.Doc, vs.Names[0].Name) {
		modifyComment(vs.Doc, vs.Names[0].Name)
		return
	}
}

func addTypeSpecComment(gd *ast.GenDecl, ts *ast.TypeSpec, commentTemplate string) {
	if gd.Doc == nil || strings.TrimSpace(gd.Doc.Text()) == ts.Name.Name {
		text := fmt.Sprintf(commentTemplate, ts.Name.Name)
		pos := gd.Pos() - token.Pos(1)
		if gd.Doc != nil {
			pos = gd.Doc.Pos()
		}
		gd.Doc = &ast.CommentGroup{List: []*ast.Comment{{Slash: pos, Text: text}}}
		return
	}
	if gd.Doc != nil && isLineComment(gd.Doc) && !hasCommentPrefix(gd.Doc, ts.Name.Name) {
		modifyComment(gd.Doc, ts.Name.Name)
		return
	}
}

func modifyComment(comment *ast.CommentGroup, prefix string) {
	commentTemplate := commentBase + *template
	first := comment.List[0].Text
	if strings.HasPrefix(first, "//") && !strings.HasPrefix(first, "// ") {
		text := fmt.Sprintf(commentTemplate, prefix)
		comment.List = append([]*ast.Comment{{Text: text, Slash: comment.Pos()}}, comment.List...)
		return
	}
	first = strings.TrimPrefix(first, "// ")
	first = fmt.Sprintf(commentBase+"%s", prefix, first)
	comment.List[0].Text = first
}


================================================
FILE: parse_test.go
================================================
package main

import (
	"bytes"
	"go/format"
	"go/token"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

const baseSrc = `package p

var i = 0

// I ...
var I = 1

var c = "constant un-exported"

// C ...
const C = "constant exported"

type t struct{}

// T ...
type T struct{}

func main() {
}

func unexport(s string) {
}
// Export ...
func Export(s string) {
}

// ExportWithComment ...
func ExportWithComment(s string) {
}

// ExistedComment ...
func ExistedComment() {}
`

const parenSrc = `package p

// Summon ...
type Summon string

// DarkOmega ...
const (
	DarkOmega Summon = "celeste"
	// LightOmega best summon
	LightOmega Summon = "luminineria"
	// WindOmega
	WindOmega Summon = "tiamat"
)

// FireUtility ...
const FireUtility Summon = "the sun"

// Light ...
const (
	// Light best summon
	Light Summon = "lucifer"
)

// Light2 best summon
const (
	Light2 Summon = "lucifer"
)
`
const parenSrc2 = `package p

// Summon ...
type Summon string

const (
	// DarkOmega ...
	DarkOmega Summon = "celeste"
	// LightOmega best summon
	LightOmega Summon = "luminineria"
	// WindOmega ...
	WindOmega Summon = "tiamat"
)

// FireUtility ...
const FireUtility Summon = "the sun"

const (
	// Light best summon
	Light Summon = "lucifer"
)

// Light2 best summon
const (
	// Light2 ...
	Light2 Summon = "lucifer"
)
`

const issue7 = `package p

import "log"

// I ...
var I = 1

func a() {

        // LogAll ...
	var LogAll map[string]struct{}
	log.Println(LogAll)
}`

const existed = `package p

// global comments should never be deleted
// something

import "embed"

// global comment 1

// global comment 2
// global comment 3

// ============= function =============

// FuncWithExistedComment1 ...
func FuncWithExistedComment1() {
}

// FuncWithExistedComment2 ...
func FuncWithExistedComment2() {
	// this comments should never be deleted
}

// FuncWithExistedComment3 something
func FuncWithExistedComment3() {
}

// FuncWithExistedComment4 multi-line comments
// something
func FuncWithExistedComment4() {
}

/*
something
*/
func FuncWithExistedComment5() {
}

// ============= value =============

// ValueWithExistedComment1 existed comment
var ValueWithExistedComment1 = 1

// ValueWithExistedComment2 existed comment with spaces
var ValueWithExistedComment2 = 1

// ValueWithExistedComment3 multi-line comments
// something
var ValueWithExistedComment3 = 1

/*
should't change C style comment
*/
var ValueWithExistedComment4 = 1

// ============= paren value =============

// ParenValueWithExistedComment1 existed comment
const (
	ParenValueWithExistedComment1 = 1
	// ParenValueWithExistedComment2 something
	ParenValueWithExistedComment2 = 1
)

// ParenValueWithExistedComment3 multi-line comments
// something
const (
	ParenValueWithExistedComment3 = 1
	// ParenValueWithExistedComment2 something
	ParenValueWithExistedComment4 = 1
)

// ============= type =============

// TypeWithExistedComment1 existed comment
type TypeWithExistedComment1 int

// TypeWithExistedComment2 existed comment with spaces
type TypeWithExistedComment2 int

// TypeWithExistedComment3 multi-line comments
// something
type TypeWithExistedComment3 int

/*
should't change C style comment
*/
type TypeWithExistedComment4 int

// ============= marker comment =============

// Embed ...
//go:embed dont_modify_this_comment.txt
//go:embed image/*
var Embed embed.FS

// Embed something
//go:embed dont_modify_this_comment.txt
var Embed embed.FS

// Generate ...
//go:generate goyacc -o gopher.go -p parser gopher.y
func Generate() {
}

// ============= end =============
`

const generic = `package p

// G ...
type G[T any] struct{}
`

func Test_parseFile(t *testing.T) {
	parseFileTests := []struct {
		path        string
		expectedSrc string
		modified    bool
		wantErr     bool
	}{
		{"testdata/main.go", baseSrc, true, false},
		{"testdata/parenthesis.go", parenSrc, true, false},
		{"testdata/invalid_file.go", "", false, true},
		{"testdata/issue7.go", issue7, false, false},
		{"testdata/existed.go", existed, true, false},
		{"testdata/generic.go", generic, true, false},
	}

	for _, tc := range parseFileTests {
		tc := tc
		t.Run(tc.path, func(t *testing.T) {
			t.Parallel()
			fset := token.NewFileSet()
			af, modified, err := parseFile(fset, tc.path, "...")
			assert.True(t, tc.wantErr == (err != nil))
			assert.Equal(t, tc.modified, modified)

			if tc.modified {
				buf := new(bytes.Buffer)
				assert.NoError(t, format.Node(buf, fset, af))
				newBuf := buf.Bytes()
				newBuf = tralingWsRegex.ReplaceAll(newBuf, []byte(""))
				newBuf = newlinesRegex.ReplaceAll(newBuf, []byte("\n\n"))
				assert.Equal(t, tc.expectedSrc, string(newBuf))
			}
		})

	}
}
func Test_parseFileWithParenComment(t *testing.T) {
	*parenComment = true
	parseFileTests := []struct {
		path        string
		expectedSrc string
		modified    bool
		wantErr     bool
	}{
		{"testdata/parenthesis.go", parenSrc2, true, false},
	}

	for _, tc := range parseFileTests {
		tc := tc
		t.Run(tc.path, func(t *testing.T) {
			t.Parallel()
			fset := token.NewFileSet()
			af, modified, err := parseFile(fset, tc.path, "...")
			assert.True(t, tc.wantErr == (err != nil))
			assert.Equal(t, tc.modified, modified)

			if tc.modified {
				buf := new(bytes.Buffer)
				assert.NoError(t, format.Node(buf, fset, af))
				newBuf := buf.Bytes()
				newBuf = tralingWsRegex.ReplaceAll(newBuf, []byte(""))
				newBuf = newlinesRegex.ReplaceAll(newBuf, []byte("\n\n"))
				assert.Equal(t, tc.expectedSrc, string(newBuf))
			}
		})

	}
}

func TestSkipVendor(t *testing.T) {
	filePath := "testdata/vendor/main.go"
	origBuf, err := os.ReadFile(filePath)
	if err != nil {
		t.Fatal(err)
	}
	if err := processFile(filePath, "...", true); err != nil {
		t.Fatal(err)
	}
	buf, err := os.ReadFile(filePath)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(buf, origBuf) {
		t.Fatal("file in vendor/ directory was edited")
	}
}


================================================
FILE: testdata/empty_var_block.go
================================================
package p

var (
	// Error
)


================================================
FILE: testdata/existed.go
================================================
package p

// global comments should never be deleted
// something

import "embed"

// global comment 1

// global comment 2
// global comment 3

// ============= function =============

// FuncWithExistedComment1
func FuncWithExistedComment1() {
}

func FuncWithExistedComment2() {
	// this comments should never be deleted
}

// something
func FuncWithExistedComment3() {
}

// multi-line comments
// something
func FuncWithExistedComment4() {
}

/*
something
*/
func FuncWithExistedComment5() {
}

// ============= value =============

// existed comment
var ValueWithExistedComment1 = 1

// existed comment with spaces     
var ValueWithExistedComment2 = 1

// multi-line comments
// something
var ValueWithExistedComment3 = 1

/*
should't change C style comment
*/
var ValueWithExistedComment4 = 1

// ============= paren value =============

// existed comment
const (
	ParenValueWithExistedComment1 = 1
	// ParenValueWithExistedComment2 something
	ParenValueWithExistedComment2 = 1
)

// multi-line comments
// something
const (
	ParenValueWithExistedComment3 = 1
	// ParenValueWithExistedComment2 something
	ParenValueWithExistedComment4 = 1
)

// ============= type =============

// existed comment
type TypeWithExistedComment1 int

// existed comment with spaces     
type TypeWithExistedComment2 int

// multi-line comments
// something
type TypeWithExistedComment3 int

/*
should't change C style comment
*/
type TypeWithExistedComment4 int

// ============= marker comment =============

//go:embed dont_modify_this_comment.txt
//go:embed image/*
var Embed embed.FS

// something
//go:embed dont_modify_this_comment.txt
var Embed embed.FS

//go:generate goyacc -o gopher.go -p parser gopher.y
func Generate() {
}

// ============= end =============


================================================
FILE: testdata/generic.go
================================================
package p

type G[T any] struct{}


================================================
FILE: testdata/invalid_file.go
================================================
package p

var i := 1


================================================
FILE: testdata/issue7.go
================================================
package p

import "log"

// I ...
var I = 1

func a() {

	// LogAll ...
	var LogAll map[string]struct{}
	log.Println(LogAll)
}


================================================
FILE: testdata/main.go
================================================
package p

var i = 0

var I = 1

var c = "constant un-exported"

const C = "constant exported"

type t struct{}

type T struct{}

func main() {
}

func unexport(s string) {
}
func Export(s string) {
}

func ExportWithComment(s string) {
}

// ExistedComment
func ExistedComment() {}


================================================
FILE: testdata/parenthesis.go
================================================
package p

type Summon string

const (
	DarkOmega Summon = "celeste"
	// LightOmega best summon
	LightOmega Summon = "luminineria"
	// WindOmega
	WindOmega Summon = "tiamat"
)

const FireUtility Summon = "the sun"

const (
	// Light best summon
	Light Summon = "lucifer"
)

// Light2 best summon
const (
	Light2 Summon = "lucifer"
)


================================================
FILE: testdata/vendor/main.go
================================================
package p

var I = 1
Download .txt
gitextract_p96zk_1u/

├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── LICENSE
├── README.md
├── doc.go
├── go.mod
├── go.sum
├── helper.go
├── helper_test.go
├── main.go
├── parse.go
├── parse_test.go
└── testdata/
    ├── empty_var_block.go
    ├── existed.go
    ├── generic.go
    ├── invalid_file.go
    ├── issue7.go
    ├── main.go
    ├── parenthesis.go
    └── vendor/
        └── main.go
Download .txt
SYMBOL INDEX (57 symbols across 10 files)

FILE: helper.go
  function isGoFile (line 10) | func isGoFile(f os.FileInfo) bool {
  function printError (line 15) | func printError(err error) {
  function walkFunc (line 19) | func walkFunc(path string, fi os.FileInfo, err error) error {
  function isLineComment (line 31) | func isLineComment(comment *ast.CommentGroup) bool {
  function hasCommentPrefix (line 43) | func hasCommentPrefix(comment *ast.CommentGroup, prefix string) bool {
  function appendCommentGroup (line 47) | func appendCommentGroup(list []*ast.CommentGroup, item *ast.CommentGroup...

FILE: helper_test.go
  function Test_isGoFile (line 8) | func Test_isGoFile(t *testing.T) {

FILE: main.go
  function main (line 33) | func main() {
  function usage (line 37) | func usage() {
  function gocmtRun (line 42) | func gocmtRun() int {
  function processFile (line 75) | func processFile(filename, template string, inPlace bool) error {

FILE: parse.go
  function parseFile (line 13) | func parseFile(fset *token.FileSet, filePath, template string) (af *ast....
  function addFuncDeclComment (line 105) | func addFuncDeclComment(fd *ast.FuncDecl, commentTemplate string) {
  function addValueSpecComment (line 121) | func addValueSpecComment(gd *ast.GenDecl, vs *ast.ValueSpec, commentTemp...
  function addParenValueSpecComment (line 137) | func addParenValueSpecComment(vs *ast.ValueSpec, commentTemplate string) {
  function addTypeSpecComment (line 154) | func addTypeSpecComment(gd *ast.GenDecl, ts *ast.TypeSpec, commentTempla...
  function modifyComment (line 170) | func modifyComment(comment *ast.CommentGroup, prefix string) {

FILE: parse_test.go
  constant baseSrc (line 13) | baseSrc = `package p
  constant parenSrc (line 47) | parenSrc = `package p
  constant parenSrc2 (line 75) | parenSrc2 = `package p
  constant issue7 (line 104) | issue7 = `package p
  constant existed (line 118) | existed = `package p
  constant generic (line 226) | generic = `package p
  function Test_parseFile (line 232) | func Test_parseFile(t *testing.T) {
  function Test_parseFileWithParenComment (line 268) | func Test_parseFileWithParenComment(t *testing.T) {
  function TestSkipVendor (line 301) | func TestSkipVendor(t *testing.T) {

FILE: testdata/existed.go
  function FuncWithExistedComment1 (line 16) | func FuncWithExistedComment1() {
  function FuncWithExistedComment2 (line 19) | func FuncWithExistedComment2() {
  function FuncWithExistedComment3 (line 24) | func FuncWithExistedComment3() {
  function FuncWithExistedComment4 (line 29) | func FuncWithExistedComment4() {
  function FuncWithExistedComment5 (line 35) | func FuncWithExistedComment5() {
  constant ParenValueWithExistedComment1 (line 59) | ParenValueWithExistedComment1 = 1
  constant ParenValueWithExistedComment2 (line 61) | ParenValueWithExistedComment2 = 1
  constant ParenValueWithExistedComment3 (line 67) | ParenValueWithExistedComment3 = 1
  constant ParenValueWithExistedComment4 (line 69) | ParenValueWithExistedComment4 = 1
  type TypeWithExistedComment1 (line 75) | type TypeWithExistedComment1
  type TypeWithExistedComment2 (line 78) | type TypeWithExistedComment2
  type TypeWithExistedComment3 (line 82) | type TypeWithExistedComment3
  type TypeWithExistedComment4 (line 87) | type TypeWithExistedComment4
  function Generate (line 100) | func Generate() {

FILE: testdata/generic.go
  type G (line 3) | type G struct

FILE: testdata/issue7.go
  function a (line 8) | func a() {

FILE: testdata/main.go
  constant C (line 9) | C = "constant exported"
  type t (line 11) | type t struct
  type T (line 13) | type T struct
  function main (line 15) | func main() {
  function unexport (line 18) | func unexport(s string) {
  function Export (line 20) | func Export(s string) {
  function ExportWithComment (line 23) | func ExportWithComment(s string) {
  function ExistedComment (line 27) | func ExistedComment() {}

FILE: testdata/parenthesis.go
  type Summon (line 3) | type Summon
  constant DarkOmega (line 6) | DarkOmega Summon = "celeste"
  constant LightOmega (line 8) | LightOmega Summon = "luminineria"
  constant WindOmega (line 10) | WindOmega Summon = "tiamat"
  constant FireUtility (line 13) | FireUtility Summon = "the sun"
  constant Light (line 17) | Light Summon = "lucifer"
  constant Light2 (line 22) | Light2 Summon = "lucifer"
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (27K chars).
[
  {
    "path": ".github/workflows/ci.yml",
    "chars": 750,
    "preview": "name: Test\n\non:\n  pull_request:\n  push:\n    branches:\n      - main\n\njobs:\n  test:\n    name: Test\n    runs-on: ubuntu-lat"
  },
  {
    "path": ".gitignore",
    "chars": 6,
    "preview": ".idea\n"
  },
  {
    "path": "LICENSE",
    "chars": 1567,
    "preview": "Author:: LE Manh Cuong <cuong.manhle.vn@gmail.com>\nCopyright:: Copyright (c) 2016, Cuong Manh Le\nAll rights reserved.\n\nR"
  },
  {
    "path": "README.md",
    "chars": 1891,
    "preview": "# gocmt - Add missing comment on exported function, method, type, constant, variable in go file\n\n![Build status](https:/"
  },
  {
    "path": "doc.go",
    "chars": 591,
    "preview": "// gocmt adds missing comments on exported identifiers in Go source files.\n//\n// Usage:\n//\n//  gocmt [-i] [-p] [-t \"comm"
  },
  {
    "path": "go.mod",
    "chars": 232,
    "preview": "module github.com/cuonglm/gocmt\n\ngo 1.19\n\nrequire github.com/stretchr/testify v1.6.1\n\nrequire (\n\tgithub.com/davecgh/go-s"
  },
  {
    "path": "go.sum",
    "chars": 1075,
    "preview": "github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=\ngithub.com/davecgh/go-spew v1.1.0/go.m"
  },
  {
    "path": "helper.go",
    "chars": 1418,
    "preview": "package main\n\nimport (\n\t\"go/ast\"\n\t\"go/scanner\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc isGoFile(f os.FileInfo) bool {\n\tname := f.Name()"
  },
  {
    "path": "helper_test.go",
    "chars": 425,
    "preview": "package main\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc Test_isGoFile(t *testing.T) {\n\tisGoFileTest := []struct {\n\t\tpath     str"
  },
  {
    "path": "main.go",
    "chars": 2279,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"go/format\"\n\t\"go/token\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nv"
  },
  {
    "path": "parse.go",
    "chars": 5243,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"strings\"\n)\n\n// parseFile parses and modifies the inpu"
  },
  {
    "path": "parse_test.go",
    "chars": 5907,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"go/format\"\n\t\"go/token\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\ncons"
  },
  {
    "path": "testdata/empty_var_block.go",
    "chars": 29,
    "preview": "package p\n\nvar (\n\t// Error\n)\n"
  },
  {
    "path": "testdata/existed.go",
    "chars": 1763,
    "preview": "package p\n\n// global comments should never be deleted\n// something\n\nimport \"embed\"\n\n// global comment 1\n\n// global comme"
  },
  {
    "path": "testdata/generic.go",
    "chars": 34,
    "preview": "package p\n\ntype G[T any] struct{}\n"
  },
  {
    "path": "testdata/invalid_file.go",
    "chars": 22,
    "preview": "package p\n\nvar i := 1\n"
  },
  {
    "path": "testdata/issue7.go",
    "chars": 127,
    "preview": "package p\n\nimport \"log\"\n\n// I ...\nvar I = 1\n\nfunc a() {\n\n\t// LogAll ...\n\tvar LogAll map[string]struct{}\n\tlog.Println(Log"
  },
  {
    "path": "testdata/main.go",
    "chars": 283,
    "preview": "package p\n\nvar i = 0\n\nvar I = 1\n\nvar c = \"constant un-exported\"\n\nconst C = \"constant exported\"\n\ntype t struct{}\n\ntype T "
  },
  {
    "path": "testdata/parenthesis.go",
    "chars": 333,
    "preview": "package p\n\ntype Summon string\n\nconst (\n\tDarkOmega Summon = \"celeste\"\n\t// LightOmega best summon\n\tLightOmega Summon = \"lu"
  },
  {
    "path": "testdata/vendor/main.go",
    "chars": 21,
    "preview": "package p\n\nvar I = 1\n"
  }
]

About this extraction

This page contains the full source code of the cuonglm/gocmt GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (23.4 KB), approximately 7.4k tokens, and a symbol index with 57 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!