Repository: yqylovy/goimportdot
Branch: master
Commit: eb181a7eeabe
Files: 14
Total size: 16.9 KB
Directory structure:
gitextract_o0n_csr4/
├── README.md
├── core/
│ ├── file_filter.go
│ ├── imps.go
│ ├── imps_test.go
│ ├── pkg_filter.go
│ ├── pkg_filter_test.go
│ └── util.go
├── docs/
│ ├── examples-cn/
│ │ ├── cronsun.dot
│ │ ├── cronsun.node.core.dot
│ │ ├── cronsun.node.dot
│ │ └── goimportdot_guide.md
│ ├── zap.dot
│ └── zapcore.dot
└── goimportdot.go
================================================
FILE CONTENTS
================================================
================================================
FILE: README.md
================================================
# GoImportDot
## What is GoImportDot ?
GoImportDot is a tiny tool to generate a `dot` file (used for Graphviz) of imports of golang package.It has two purpose.
* Help people quickly understand how a package organization without going into details of code.
* Help people find out whether a package is too confusing and needs to be refactored.
## Quick Start
```
go get -u github.com/yqylovy/goimportdot
goimportdot -pkg=yourpackagename > pkg.dot
dot -Tsvg pkg.dot >pkg.svg
```
## Example
Install `go.uber.org/zap`
```
go get go.uber.org/zap
```
Get a graph of `go.uber.org/zap`
```
goimportdot -pkg=go.uber.org/zap > zap.dot
dot -Tpng zap.dot > zap.png
```

Only get a graph of zapcore in `go.uber.org/zap`
```
goimportdot -pkg=go.uber.org/zap -root=go.uber.org/zap/zapcore > zapcore.dot
dot -Tpng zapcore.dot > zapcore.png
```

================================================
FILE: core/file_filter.go
================================================
package core
import (
"os"
"strings"
)
type FileFilter struct {
IsBlack bool
Func func(fp string, info os.FileInfo, err error) bool
}
func NameContains(isblack bool, str string) FileFilter {
return FileFilter{
IsBlack: isblack,
Func: func(fp string, _ os.FileInfo, _ error) bool {
return strings.Contains(fp, str)
},
}
}
func HasSuffix(isblack bool, suffixs ...string) FileFilter {
return FileFilter{
IsBlack: isblack,
Func: func(fp string, _ os.FileInfo, _ error) bool {
for _, sf := range suffixs {
if sf == "" {
continue
}
if sf[0] != '.' {
sf = "." + sf
}
if strings.HasSuffix(fp, sf) {
return true
}
}
return false
},
}
}
================================================
FILE: core/imps.go
================================================
package core
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
func GetImports(pkg string, filters ...FileFilter) (pkgimports map[string]StrSet, err error) {
fullpath := ""
goPath := os.Getenv("GOPATH")
gopaths := strings.Split(goPath, ":")
for _, gp := range gopaths {
fp := filepath.Join(gp, "src", pkg)
if _, err := os.Stat(fp); err == nil {
fullpath = fp
}
}
if fullpath == "" {
err = fmt.Errorf("Can not find package [%s] in GOPATH [%s]", pkg, goPath)
return
}
pkgimports = make(map[string]StrSet)
filepath.Walk(fullpath, func(fp string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
for _, filter := range filters {
if !filter.IsBlack {
continue
}
if filter.Func(fp, info, err) {
return nil
}
}
for _, filter := range filters {
if filter.IsBlack {
continue
}
if !filter.Func(fp, info, err) {
return nil
}
}
pkg := PkgOfFile(fp)
if _, ok := pkgimports[pkg]; !ok {
pkgimports[pkg] = NewStrSet()
}
ss, err := ParseGoImport(fp)
if err != nil {
// TODO: better err
panic(err)
}
pkgimports[pkg].Merge(ss)
return nil
})
return
}
func WriteDot(pkgimports map[string]StrSet, writer io.Writer) (err error) {
nodes := NewStrSet()
edges := [][2]string{}
for pkg, imps := range pkgimports {
nodes.Put(pkg)
for imp := range imps {
nodes.Put(imp)
edges = append(edges, [2]string{pkg, imp})
}
}
buf := bytes.NewBuffer([]byte{})
buf.WriteString("digraph G {\n")
for _, edge := range edges {
buf.WriteString(fmt.Sprintf(`"%s"->"%s";`, edge[0], edge[1]))
buf.WriteByte('\n')
}
for pkg, _ := range nodes {
buf.WriteString(fmt.Sprintf(`"%s";`, pkg))
buf.WriteByte('\n')
}
buf.WriteString("}\n")
_, err = writer.Write(buf.Bytes())
return
}
================================================
FILE: core/imps_test.go
================================================
package core
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestGetImports(t *testing.T) {
Convey("Test GetImports", t, func() {
_, err := GetImports("go.uber.org/zap",
NameContains(true, ".git"),
NameContains(true, "_test.go"),
HasSuffix(false, ".go"))
So(err, ShouldBeNil)
})
}
================================================
FILE: core/pkg_filter.go
================================================
package core
import (
"regexp"
"strings"
)
type PkgFilter func(map[string]StrSet) map[string]StrSet
func RootFilter(root string) PkgFilter {
return func(imps map[string]StrSet) (ret map[string]StrSet) {
ret = make(map[string]StrSet)
cur := []string{root}
for len(cur) > 0 {
newcur := NewStrSet()
for _, pkg := range cur {
if pkgimp, ok := imps[pkg]; ok {
ret[pkg] = pkgimp
newcur.Merge(pkgimp)
}
}
cur = newcur.Array()
}
return ret
}
}
func PkgWildcardFilter(isBlack bool, pkgs ...string) PkgFilter {
regs := []*regexp.Regexp{}
for _, pkg := range pkgs {
rgp := regexp.MustCompile("^" + strings.Replace(pkg, "*", ".*", -1) + "$")
regs = append(regs, rgp)
}
return func(imps map[string]StrSet) (ret map[string]StrSet) {
ret = make(map[string]StrSet)
BIG:
for pkg, imps := range imps {
for _, rgp := range regs {
if isBlack == rgp.MatchString(pkg) {
continue BIG
}
}
for k := range imps {
for _, rgp := range regs {
if isBlack == rgp.MatchString(k) {
imps.Del(k)
}
}
}
ret[pkg] = imps
}
return ret
}
}
func ParsePkgWildcardStr(str string) (fs []PkgFilter, err error) {
if str == "" {
return
}
strArr := strings.Split(str, ";")
for _, str := range strArr {
str = strings.TrimSpace(str)
wb_pkgs := strings.SplitN(str, ":", 2)
fs = append(fs, PkgWildcardFilter(wb_pkgs[0] == "b", strings.Split(wb_pkgs[1], ",")...))
}
return
}
func PkgLevelFilter(level int) PkgFilter {
return func(imps map[string]StrSet) (ret map[string]StrSet) {
if level < 0 {
return imps
}
// find the root
// which are not pointed to
allTarget := NewStrSet()
for _, targets := range imps {
allTarget.Merge(targets)
}
levelMap := map[string]int{}
for pkg := range imps {
if !allTarget.Contains(pkg) {
levelMap[pkg] = 0
}
}
for i := 0; i < level; i++ {
nextLevel := NewStrSet()
for pkg, pkgLevel := range levelMap {
if pkgLevel != i {
continue
}
for target := range imps[pkg] {
nextLevel.Put(target)
}
}
for next := range nextLevel {
levelMap[next] = i + 1
}
}
ret = make(map[string]StrSet, len(levelMap))
for pkg, lvl := range levelMap {
ret[pkg] = imps[pkg]
if lvl == level {
continue
}
}
return ret
}
}
================================================
FILE: core/pkg_filter_test.go
================================================
package core
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestPkgRegex(t *testing.T) {
Convey("Test PkgRegex", t, func() {
imps := map[string]StrSet{
"a/b/c": NewStrSet(
"test/subt",
),
}
filter := PkgWildcardFilter(true, "test*")
imps = filter(imps)
So(imps["a/b/c"], ShouldBeEmpty)
})
}
func TestParsePkgWildcardStr(t *testing.T) {
Convey("Test ParsePkgWildcardStr", t, func() {
str := "w:a*,*b;b:c"
fs, err := ParsePkgWildcardStr(str)
So(err, ShouldBeNil)
So(len(fs), ShouldEqual, 2)
})
}
================================================
FILE: core/util.go
================================================
package core
import (
"go/parser"
"go/token"
"path/filepath"
"strings"
)
func ParseGoImport(gofile string) (ss StrSet, err error) {
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, gofile, nil, parser.ImportsOnly)
if err != nil {
return
}
ss = NewStrSet()
for _, s := range f.Imports {
ss.Put(strings.Trim(s.Path.Value, `"`))
}
return
}
func PkgOfFile(gofile string) (pkg string) {
return strings.SplitN(filepath.Dir(gofile), "/src/", 2)[1]
}
type StrSet map[string]bool
func NewStrSet(strs ...string) StrSet {
ss := StrSet(make(map[string]bool))
for _, str := range strs {
ss.Put(str)
}
return ss
}
func (this StrSet) Put(str string) { this[str] = true }
func (this StrSet) Del(str string) { delete(this, str) }
func (this StrSet) Contains(str string) (ok bool) { _, ok = this[str]; return ok }
func (this StrSet) Merge(that StrSet) {
for str := range that {
this[str] = true
}
}
func (this StrSet) Array() []string {
ret := make([]string, 0, len(this))
for str := range this {
ret = append(ret, str)
}
return ret
}
================================================
FILE: docs/examples-cn/cronsun.dot
================================================
digraph G {
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/node/cron";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/utils";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun";
"github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun/event";
"github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/bin/web"->"github.com/shunfei/cronsun/web";
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/node/cron";
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/db";
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/web"->"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun/web"->"github.com/shunfei/cronsun";
"github.com/shunfei/cronsun/web"->"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun";
"github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun/event";
"github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/bin/node"->"github.com/shunfei/cronsun/node";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/db";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/utils";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/event";
"github.com/shunfei/cronsun";
"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/bin/web";
"github.com/shunfei/cronsun/bin/node";
"github.com/shunfei/cronsun/db/mid";
"github.com/shunfei/cronsun/node";
"github.com/shunfei/cronsun/node/cron";
"github.com/shunfei/cronsun/utils";
"github.com/shunfei/cronsun/event";
"github.com/shunfei/cronsun/web";
"github.com/shunfei/cronsun/db";
}
================================================
FILE: docs/examples-cn/cronsun.node.core.dot
================================================
digraph G {
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/db";
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/node/cron";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/db";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/event";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/node/cron";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun";
"github.com/shunfei/cronsun/node/cron";
"github.com/shunfei/cronsun";
"github.com/shunfei/cronsun/db";
"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun/event";
"github.com/shunfei/cronsun/node";
}
================================================
FILE: docs/examples-cn/cronsun.node.dot
================================================
digraph G {
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/db";
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/node/cron";
"github.com/shunfei/cronsun"->"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/db";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/event";
"github.com/shunfei/cronsun/conf"->"github.com/shunfei/cronsun/utils";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/utils";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/conf";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/node"->"github.com/shunfei/cronsun/node/cron";
"github.com/shunfei/cronsun/node/cron";
"github.com/shunfei/cronsun/log";
"github.com/shunfei/cronsun/event";
"github.com/shunfei/cronsun/utils";
"github.com/shunfei/cronsun/node";
"github.com/shunfei/cronsun";
"github.com/shunfei/cronsun/db";
"github.com/shunfei/cronsun/conf";
}
================================================
FILE: docs/examples-cn/goimportdot_guide.md
================================================
# goimportdot : 一个帮你迅速了解 golang 项目结构的工具
## 简介
很多时候,当我们想熟悉一个 `golang` 项目时,都需要能快速地对代码的整体结构有个宏观了解,初步明白项目是如何组织构成的。在有了大体的概念后,再选择适当的切入点,专注于代码的核心部分进行研究,达到熟悉项目的目的。 `goimportdot` 就是一个根据 `golang` 中 `import` 生成调用关系,再配合 `Graphviz` 生成调用图的工具。
## 安装
```
go get -u github.com/yqylovy/goimportdot
```
在安装后会在 `$GOPATH/bin` 路径中生成 `goimportdot` 文件。
---
## 使用示例
以 `github.com/shunfei/cronsun` 作为示例,这是一个分布式任务系统,类似于 `crontab`。首先我们把代码下载下来。
```
go get github.com/shunfei/cronsun
```
使用 `goimportdot` 对项目结构进行解析。再通过 dot 把 解析结构转化为 `png` 图片。
```
goimportdot -pkg=github.com/shunfei/cronsun > cronsun.dot
dot -Tpng cronsun.dot > cronsun.png
```
打开 `cronsun.png`,图片如下:

可以看到现在项目整体结构一目了然,存在两个入口 `github.com/shunfei/cronsun/bin/node` 和 `github.com/shunfei/cronsun/bin/web`。
`cronsun` 还是个轻量级、整洁的项目,可以一目了然。作者在分析更复杂的项目的时候,发现生成的调用图非常复杂,看上去像一团乱麻,难以入手。这时候需要减少信息量,逐步分析。依旧以 `cronsun` 为例。如果我们只想分析 `cronsun` 的 `node` 部分:
```
goimportdot -pkg=github.com/shunfei/cronsun -root=github.com/shunfei/cronsun/node > cronsun.node.dot
dot -Tpng cronsun.node.dot > cronsun.node.png
```
图片结果:

项目中通常会存在一些辅助工具,如 `log` 包,被大量地引用,在分析时可以通过指定黑名单来达到忽略某些包的目的:
```
# 忽略其中的 log、utils
goimportdot -pkg=github.com/shunfei/cronsun -root=github.com/shunfei/cronsun/node -filter=b:*utils,*log > cronsun.node.core.dot
dot -Tpng cronsun.node.core.dot > cronsun.node.core.png
```
图片结果:

通过指定 `root` 和 `filter`,可以有效地减少输出,快速地把握核心。
## 结语
`goimportdot`是个刚推出的小工具,还有很多不成熟的地方,欢迎提出建议。
项目地址: [https://github.com/yqylovy/goimportdot](https://github.com/yqylovy/goimportdot)
================================================
FILE: docs/zap.dot
================================================
digraph G {
"go.uber.org/zap/internal/multierror"->"go.uber.org/zap/internal/bufferpool";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/bufferpool";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/exit";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/color";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/buffer";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/multierror";
"go.uber.org/zap/internal/bufferpool"->"go.uber.org/zap/buffer";
"go.uber.org/zap/zapgrpc"->"go.uber.org/zap";
"go.uber.org/zap/zaptest/observer"->"go.uber.org/zap/zapcore";
"go.uber.org/zap"->"go.uber.org/zap/zapcore";
"go.uber.org/zap"->"go.uber.org/zap/internal/bufferpool";
"go.uber.org/zap"->"go.uber.org/zap/internal/multierror";
"go.uber.org/zap/internal/readme";
"go.uber.org/zap/buffer";
"go.uber.org/zap";
"go.uber.org/zap/zaptest/observer";
"go.uber.org/zap/benchmarks";
"go.uber.org/zap/internal/multierror";
"go.uber.org/zap/zaptest";
"go.uber.org/zap/internal/exit";
"go.uber.org/zap/zapgrpc";
"go.uber.org/zap/internal/bufferpool";
"go.uber.org/zap/internal/color";
"go.uber.org/zap/zapcore";
}
================================================
FILE: docs/zapcore.dot
================================================
digraph G {
"go.uber.org/zap/internal/multierror"->"go.uber.org/zap/internal/bufferpool";
"go.uber.org/zap/internal/bufferpool"->"go.uber.org/zap/buffer";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/buffer";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/multierror";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/exit";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/bufferpool";
"go.uber.org/zap/zapcore"->"go.uber.org/zap/internal/color";
"go.uber.org/zap/internal/exit";
"go.uber.org/zap/zapcore";
"go.uber.org/zap/internal/color";
"go.uber.org/zap/buffer";
"go.uber.org/zap/internal/multierror";
"go.uber.org/zap/internal/bufferpool";
}
================================================
FILE: goimportdot.go
================================================
package main
import (
"flag"
"fmt"
"os"
"github.com/yqylovy/goimportdot/core"
)
func main() {
var ignoreGit = true
var ignoreTest = true
var onlySelfPkg = true
var packageName = ""
var root = ""
var filters = ""
var level = -1
flag.BoolVar(&ignoreGit, "ignoregit", ignoreGit, "ignore files in git")
flag.BoolVar(&ignoreTest, "ignoretest", ignoreTest, "ignore test files")
flag.BoolVar(&onlySelfPkg, "only", onlySelfPkg, "only to draw the input package")
flag.StringVar(&filters, "filter", "", "filter to (ignore/only include) package match wildcard,example: -filter=w:a*,*b;b:c means only include package start with a and ends with b, ignore package named c")
flag.StringVar(&root, "root", root, "only draw package with the graph start from root")
flag.IntVar(&level, "level", level, "show how many level , -1 for all")
flag.StringVar(&packageName, "pkg", packageName, "the package to draw")
flag.Parse()
if packageName == "" {
fmt.Println("You must specify the packge name with -pkg ")
return
}
fileFilter := []core.FileFilter{
core.HasSuffix(false, ".go"),
}
if ignoreGit {
fileFilter = append(fileFilter, core.NameContains(true, ".git"))
}
if ignoreTest {
fileFilter = append(fileFilter, core.NameContains(true, "_test.go"))
}
pkgAndImports, err := core.GetImports(packageName, fileFilter...)
if err != nil {
panic(err)
}
pkgFilters := []core.PkgFilter{}
if onlySelfPkg {
pkgFilters = append(pkgFilters, core.PkgWildcardFilter(false, packageName+"*"))
}
if root != "" {
pkgFilters = append(pkgFilters, core.RootFilter(root))
}
moreFilters, err := core.ParsePkgWildcardStr(filters)
if err != nil {
fmt.Printf("No right filter [%s], please check!", filters)
return
}
pkgFilters = append(pkgFilters, moreFilters...)
if level >= 0 {
pkgFilters = append(pkgFilters, core.PkgLevelFilter(level))
}
for _, f := range pkgFilters {
pkgAndImports = f(pkgAndImports)
}
core.WriteDot(pkgAndImports, os.Stdout)
}
gitextract_o0n_csr4/ ├── README.md ├── core/ │ ├── file_filter.go │ ├── imps.go │ ├── imps_test.go │ ├── pkg_filter.go │ ├── pkg_filter_test.go │ └── util.go ├── docs/ │ ├── examples-cn/ │ │ ├── cronsun.dot │ │ ├── cronsun.node.core.dot │ │ ├── cronsun.node.dot │ │ └── goimportdot_guide.md │ ├── zap.dot │ └── zapcore.dot └── goimportdot.go
SYMBOL INDEX (23 symbols across 7 files)
FILE: core/file_filter.go
type FileFilter (line 8) | type FileFilter struct
function NameContains (line 13) | func NameContains(isblack bool, str string) FileFilter {
function HasSuffix (line 21) | func HasSuffix(isblack bool, suffixs ...string) FileFilter {
FILE: core/imps.go
function GetImports (line 12) | func GetImports(pkg string, filters ...FileFilter) (pkgimports map[strin...
function WriteDot (line 63) | func WriteDot(pkgimports map[string]StrSet, writer io.Writer) (err error) {
FILE: core/imps_test.go
function TestGetImports (line 9) | func TestGetImports(t *testing.T) {
FILE: core/pkg_filter.go
type PkgFilter (line 9) | type PkgFilter
function RootFilter (line 11) | func RootFilter(root string) PkgFilter {
function PkgWildcardFilter (line 29) | func PkgWildcardFilter(isBlack bool, pkgs ...string) PkgFilter {
function ParsePkgWildcardStr (line 57) | func ParsePkgWildcardStr(str string) (fs []PkgFilter, err error) {
function PkgLevelFilter (line 70) | func PkgLevelFilter(level int) PkgFilter {
FILE: core/pkg_filter_test.go
function TestPkgRegex (line 9) | func TestPkgRegex(t *testing.T) {
function TestParsePkgWildcardStr (line 22) | func TestParsePkgWildcardStr(t *testing.T) {
FILE: core/util.go
function ParseGoImport (line 10) | func ParseGoImport(gofile string) (ss StrSet, err error) {
function PkgOfFile (line 22) | func PkgOfFile(gofile string) (pkg string) {
type StrSet (line 26) | type StrSet
method Put (line 35) | func (this StrSet) Put(str string) { this[str] = true }
method Del (line 36) | func (this StrSet) Del(str string) { delete(this, str) }
method Contains (line 37) | func (this StrSet) Contains(str string) (ok bool) { _, ok = this[str];...
method Merge (line 38) | func (this StrSet) Merge(that StrSet) {
method Array (line 43) | func (this StrSet) Array() []string {
function NewStrSet (line 28) | func NewStrSet(strs ...string) StrSet {
FILE: goimportdot.go
function main (line 11) | func main() {
Condensed preview — 14 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (21K chars).
[
{
"path": "README.md",
"chars": 892,
"preview": "# GoImportDot\n\n## What is GoImportDot ?\n\nGoImportDot is a tiny tool to generate a `dot` file (used for Graphviz) of impo"
},
{
"path": "core/file_filter.go",
"chars": 704,
"preview": "package core\n\nimport (\n\t\"os\"\n\t\"strings\"\n)\n\ntype FileFilter struct {\n\tIsBlack bool\n\tFunc func(fp string, info os.FileI"
},
{
"path": "core/imps.go",
"chars": 1804,
"preview": "package core\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nfunc GetImports(pkg string, filters .."
},
{
"path": "core/imps_test.go",
"chars": 324,
"preview": "package core\n\nimport (\n\t\"testing\"\n\n\t. \"github.com/smartystreets/goconvey/convey\"\n)\n\nfunc TestGetImports(t *testing.T) {\n"
},
{
"path": "core/pkg_filter.go",
"chars": 2315,
"preview": "package core\n\nimport (\n\t\"regexp\"\n\n\t\"strings\"\n)\n\ntype PkgFilter func(map[string]StrSet) map[string]StrSet\n\nfunc RootFilte"
},
{
"path": "core/pkg_filter_test.go",
"chars": 556,
"preview": "package core\n\nimport (\n\t\"testing\"\n\n\t. \"github.com/smartystreets/goconvey/convey\"\n)\n\nfunc TestPkgRegex(t *testing.T) {\n\tC"
},
{
"path": "core/util.go",
"chars": 1129,
"preview": "package core\n\nimport (\n\t\"go/parser\"\n\t\"go/token\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nfunc ParseGoImport(gofile string) (ss Str"
},
{
"path": "docs/examples-cn/cronsun.dot",
"chars": 2257,
"preview": "digraph G {\n\"github.com/shunfei/cronsun/node\"->\"github.com/shunfei/cronsun\";\n\"github.com/shunfei/cronsun/node\"->\"github."
},
{
"path": "docs/examples-cn/cronsun.node.core.dot",
"chars": 770,
"preview": "digraph G {\n\"github.com/shunfei/cronsun\"->\"github.com/shunfei/cronsun/db\";\n\"github.com/shunfei/cronsun\"->\"github.com/shu"
},
{
"path": "docs/examples-cn/cronsun.node.dot",
"chars": 1184,
"preview": "digraph G {\n\"github.com/shunfei/cronsun\"->\"github.com/shunfei/cronsun/db\";\n\"github.com/shunfei/cronsun\"->\"github.com/shu"
},
{
"path": "docs/examples-cn/goimportdot_guide.md",
"chars": 1629,
"preview": "# goimportdot : 一个帮你迅速了解 golang 项目结构的工具\n\n## 简介\n\n很多时候,当我们想熟悉一个 `golang` 项目时,都需要能快速地对代码的整体结构有个宏观了解,初步明白项目是如何组织构成的。在有了大体的概念"
},
{
"path": "docs/zap.dot",
"chars": 1106,
"preview": "digraph G {\n\"go.uber.org/zap/internal/multierror\"->\"go.uber.org/zap/internal/bufferpool\";\n\"go.uber.org/zap/zapcore\"->\"go"
},
{
"path": "docs/zapcore.dot",
"chars": 661,
"preview": "digraph G {\n\"go.uber.org/zap/internal/multierror\"->\"go.uber.org/zap/internal/bufferpool\";\n\"go.uber.org/zap/internal/buff"
},
{
"path": "goimportdot.go",
"chars": 1982,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/yqylovy/goimportdot/core\"\n)\n\nfunc main() {\n\tvar ignoreGit = tr"
}
]
About this extraction
This page contains the full source code of the yqylovy/goimportdot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 14 files (16.9 KB), approximately 6.0k tokens, and a symbol index with 23 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.