Skip to content

Commit

Permalink
Render images with Go library instead of Graphviz's dot (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrajz authored Aug 5, 2020
1 parent 6b289a9 commit d9b7b59
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 14 deletions.
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ the code much higher or when you are just simply trying to understand code of so
### Features

- 🆕 **support for Go modules!** :boom:
- interactive view allowing quick switching between focused packages in web browser
- focus specific package in the program
- click on package to quickly switch the focus using [interactive viewer](#interactive-viewer)
- group functions by package and/or methods by type
- filter packages to specific import path prefixes
- ignore funcs from standard library
- omit various types of function calls

### Output preview
Expand Down Expand Up @@ -74,8 +75,8 @@ Here you can find descriptions for various types of output.

#### Requirements

- [Go](https://golang.org/dl/) 1.12+
- [Graphviz](http://www.graphviz.org/download/)
- [Go](https://golang.org/dl/) 1.13+
- [Graphviz](http://www.graphviz.org/download/) (optional, required only with `-graphviz` flag)

### Installation

Expand All @@ -88,13 +89,19 @@ cd go-callvis && make install

### Usage

#### Interactive viewer

To use the interactive view provided by a web server that serves SVG images of focused packages, you can simply run:

`go-callvis [OPTIONS] <main package>`
`go-callvis <target package>`

HTTP server is listening on [http://localhost:7878/](http://localhost:7878/) by default, use option `-http="ADDR:PORT"` to change HTTP server address.

#### Render static output

> HTTP server is listening on [http://localhost:7878/](http://localhost:7878/) by default.
To generate a single output file use option `-file=<file path>` to choose output file destination.

To generate a single output file use option `-file=<file path>` to choose output file destination. The output format defaults to `svg`, use option `-format=<svg|png|jpg|...>` to pick a different output format.
The output format defaults to `svg`, use option `-format=<svg|png|jpg|...>` to pick a different output format.

#### Options

Expand All @@ -108,8 +115,10 @@ Usage of go-callvis:
Focus specific package using name or import path. (default "main")
-format string
output file format [svg | png | jpg | ...] (default "svg")
-graphviz
Use Graphviz's dot program to render images.
-group string
Grouping functions by packages and/or types [pkg, type] (separated by comma)
Grouping functions by packages and/or types [pkg, type] (separated by comma) (default "pkg")
-http string
HTTP service address. (default ":7878")
-ignore string
Expand Down
36 changes: 34 additions & 2 deletions dot.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"path/filepath"
"strings"
"text/template"

"github.com/goccy/go-graphviz"
)

var (
Expand All @@ -21,8 +23,8 @@ var (
// it's usually at: /usr/bin/dot
var dotExe string

// dotToImage generates a SVG using the 'dot' utility, returning the filepath
func dotToImage(outfname string, format string, dot []byte) (string, error) {
// dotToImageGraphviz generates a SVG using the 'dot' utility, returning the filepath
func dotToImageGraphviz(outfname string, format string, dot []byte) (string, error) {
if dotExe == "" {
dot, err := exec.LookPath("dot")
if err != nil {
Expand All @@ -39,7 +41,37 @@ func dotToImage(outfname string, format string, dot []byte) (string, error) {
}
cmd := exec.Command(dotExe, fmt.Sprintf("-T%s", format), "-o", img)
cmd.Stdin = bytes.NewReader(dot)
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return "", fmt.Errorf("command '%v': %v\n%v", cmd, err, stderr.String())
}
return img, nil
}

func dotToImage(outfname string, format string, dot []byte) (string, error) {
if *graphvizFlag {
return dotToImageGraphviz(outfname, format, dot)
}

g := graphviz.New()
graph, err := graphviz.ParseBytes(dot)
if err != nil {
return "", err
}
defer func() {
if err := graph.Close(); err != nil {
log.Fatal(err)
}
g.Close()
}()
var img string
if outfname == "" {
img = filepath.Join(os.TempDir(), fmt.Sprintf("go-callvis_export.%s", format))
} else {
img = fmt.Sprintf("%s.%s", outfname, format)
}
if err := g.RenderFilename(graph, graphviz.Format(format), img); err != nil {
return "", err
}
return img, nil
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/ofabry/go-callvis
go 1.12

require (
github.com/goccy/go-graphviz v0.0.6
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
golang.org/x/tools v0.0.0-20200305224536-de023d59a5d1
)
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
github.com/corona10/goimagehash v1.0.2 h1:pUfB0LnsJASMPGEZLj7tGY251vF+qLGqOgEP4rUs6kA=
github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/goccy/go-graphviz v0.0.6 h1:sCT69fmH2KKsObVfsozYyKXxrqmIfo3SyHZs72xkgxs=
github.com/goccy/go-graphviz v0.0.6/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg=
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand Down
3 changes: 2 additions & 1 deletion handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func handler(w http.ResponseWriter, r *http.Request) {

// Convert list-style args to []string
if e := processListArgs(&opts); e != nil {
http.Error(w, "invalid group option", http.StatusInternalServerError)
http.Error(w, "invalid parameters", http.StatusBadRequest)
return
}

Expand All @@ -123,6 +123,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
}

log.Printf("converting dot to %s..\n", *outputFormat)

img, err := dotToImage("", *outputFormat, output)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand Down
8 changes: 5 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,21 @@ import (

var (
focusFlag = flag.String("focus", "main", "Focus specific package using name or import path.")
groupFlag = flag.String("group", "", "Grouping functions by packages and/or types [pkg, type] (separated by comma)")
groupFlag = flag.String("group", "pkg", "Grouping functions by packages and/or types [pkg, type] (separated by comma)")
limitFlag = flag.String("limit", "", "Limit package paths to given prefixes (separated by comma)")
ignoreFlag = flag.String("ignore", "", "Ignore package paths containing given prefixes (separated by comma)")
includeFlag = flag.String("include", "", "Include package paths with given prefixes (separated by comma)")
nostdFlag = flag.Bool("nostd", false, "Omit calls to/from packages in standard library.")
nointerFlag = flag.Bool("nointer", false, "Omit calls to unexported functions.")
testFlag = flag.Bool("tests", false, "Include test code.")
debugFlag = flag.Bool("debug", false, "Enable verbose log.")
versionFlag = flag.Bool("version", false, "Show version and exit.")
graphvizFlag = flag.Bool("graphviz", false, "Use Graphviz's dot program to render images.")
httpFlag = flag.String("http", ":7878", "HTTP service address.")
skipBrowser = flag.Bool("skipbrowser", false, "Skip opening browser.")
outputFile = flag.String("file", "", "output filename - omit to use server mode")
outputFormat = flag.String("format", "svg", "output file format [svg | png | jpg | ...]")

debugFlag = flag.Bool("debug", false, "Enable verbose log.")
versionFlag = flag.Bool("version", false, "Show version and exit.")
)

func init() {
Expand Down
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

var (
version = "v0.6.0"
version = "v0.6.1"
commit = "(unknown)"
)

Expand Down

0 comments on commit d9b7b59

Please sign in to comment.