aboutsummaryrefslogtreecommitdiff
path: root/cmd/digraph/digraph.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/digraph/digraph.go')
-rw-r--r--cmd/digraph/digraph.go49
1 files changed, 34 insertions, 15 deletions
diff --git a/cmd/digraph/digraph.go b/cmd/digraph/digraph.go
index 88eb05bf1..0e50ad18d 100644
--- a/cmd/digraph/digraph.go
+++ b/cmd/digraph/digraph.go
@@ -34,7 +34,7 @@ The support commands are:
sccs
all strongly connected components (one per line)
scc <node>
- the set of nodes nodes strongly connected to the specified one
+ the set of nodes strongly connected to the specified one
focus <node>
the subgraph containing all directed paths that pass through the specified node
@@ -69,11 +69,12 @@ Using digraph with existing Go tools:
$ go list -m all | digraph nodes # Operate on the Go package graph.
Show the transitive closure of imports of the digraph tool itself:
+
$ go list -f '{{.ImportPath}} {{join .Imports " "}}' ... | digraph forward golang.org/x/tools/cmd/digraph
Show which clothes (see above) must be donned before a jacket:
- $ digraph reverse jacket
+ $ digraph reverse jacket
*/
package main // import "golang.org/x/tools/cmd/digraph"
@@ -121,7 +122,8 @@ The support commands are:
allpaths <node> <node>
the set of nodes on all paths from the first node to the second
sccs
- all strongly connected components (one per line)
+ all non-trivial strongly connected components, one per line
+ (single-node components are only printed for nodes with self-loops)
scc <node>
the set of nodes nodes strongly connected to the specified one
focus <node>
@@ -157,7 +159,7 @@ func (l nodelist) println(sep string) {
fmt.Fprintln(stdout)
}
-type nodeset map[string]bool // TODO(deklerk): change bool to struct to reduce memory footprint
+type nodeset map[string]bool
func (s nodeset) sort() nodelist {
nodes := make(nodelist, len(s))
@@ -265,6 +267,9 @@ func (g graph) sccs() []nodeset {
if !seen[top] {
scc = make(nodeset)
rvisit(top)
+ if len(scc) == 1 && !g[top][top] {
+ continue
+ }
sccs = append(sccs, scc)
}
}
@@ -346,25 +351,34 @@ func parse(rd io.Reader) (graph, error) {
g := make(graph)
var linenum int
- in := bufio.NewScanner(rd)
- for in.Scan() {
+ // We avoid bufio.Scanner as it imposes a (configurable) limit
+ // on line length, whereas Reader.ReadString does not.
+ in := bufio.NewReader(rd)
+ for {
linenum++
+ line, err := in.ReadString('\n')
+ eof := false
+ if err == io.EOF {
+ eof = true
+ } else if err != nil {
+ return nil, err
+ }
// Split into words, honoring double-quotes per Go spec.
- words, err := split(in.Text())
+ words, err := split(line)
if err != nil {
return nil, fmt.Errorf("at line %d: %v", linenum, err)
}
if len(words) > 0 {
g.addEdges(words[0], words[1:]...)
}
- }
- if err := in.Err(); err != nil {
- return nil, err
+ if eof {
+ break
+ }
}
return g, nil
}
-// Overridable for testing purposes.
+// Overridable for redirection.
var stdin io.Reader = os.Stdin
var stdout io.Writer = os.Stdout
@@ -484,9 +498,16 @@ func digraph(cmd string, args []string) error {
if len(args) != 0 {
return fmt.Errorf("usage: digraph sccs")
}
+ buf := new(bytes.Buffer)
+ oldStdout := stdout
+ stdout = buf
for _, scc := range g.sccs() {
scc.sort().println(" ")
}
+ lines := strings.SplitAfter(buf.String(), "\n")
+ sort.Strings(lines)
+ stdout = oldStdout
+ io.WriteString(stdout, strings.Join(lines, ""))
case "scc":
if len(args) != 1 {
@@ -546,9 +567,8 @@ func digraph(cmd string, args []string) error {
// spaces, but Go-style double-quoted string literals are also supported.
// (This approximates the behaviour of the Bourne shell.)
//
-// `one "two three"` -> ["one" "two three"]
-// `a"\n"b` -> ["a\nb"]
-//
+// `one "two three"` -> ["one" "two three"]
+// `a"\n"b` -> ["a\nb"]
func split(line string) ([]string, error) {
var (
words []string
@@ -605,7 +625,6 @@ func split(line string) ([]string, error) {
// its length is returned.
//
// TODO(adonovan): move this into a strconv-like utility package.
-//
func quotedLength(input string) (n int, ok bool) {
var offset int