file • x/tools/cmd/guru — query Go program in various ways • motemen/gofind — search for a specific usage of types % gofind -s golang.org/x/tools/cmd/stringer.Package.defs \ golang.org/x/tools/cmd/stringer stringer.go:262:6: pkg.defs = make(map[*ast.Ident]types.Object) stringer.go:265:13: Defs: pkg.defs, stringer.go:433:21: obj, ok := f.pkg.defs[name]
}; math.E • Does not require package math • Go is lexically scoped • Each ast.Ident has “Obj ast.Object”, named language entity • Find identifiers with Obj == nil
Insert blank lines to group imports • go/printer API to get []byte from ast.File • go/format.Source to re-format []byte • Same code is used inside gofmt • “This file and the file src/cmd/gofmt/internal.go are the same (but for this comment and the package name).”
GitHub, BitBucket, … • e.g. getGitHubDir() uses GitHub API • GET https://api.github.com/repos/{owner}/{repo}/contents/{dir} • Less-known ones can provide go-source meta tags
custom fs via fields like OpenFile, ReadDir, etc. type Context struct { // OpenFile opens a file (not a directory) for reading. // If OpenFile is nil, Import uses os.Open. OpenFile func(path string) (r io.ReadCloser, err error) … }
Then format by go/format.Source // Arguments to format are: // [1]: type name // [2]: size of index element (8 for uint8 etc.) // [3]: less than zero check (for signed types) const stringOneRun = `func (i %[1]s) String() string { if %[3]si >= %[1]s(len(_%[1]s_index)-1) { return fmt.Sprintf("%[1]s(%%d)", i) } return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]] } `