@@ -2,84 +2,81 @@ import ArgumentParser
2
2
import Foundation
3
3
import SwiftDoc
4
4
import SwiftSemantics
5
+ import GraphViz
6
+ import DOT
5
7
6
8
extension SwiftDoc {
7
9
struct Diagram : ParsableCommand {
8
10
struct Options : ParsableArguments {
9
11
@Argument ( help: " One or more paths to Swift files " )
10
12
var inputs : [ String ]
11
13
}
12
-
14
+
13
15
static var configuration = CommandConfiguration ( abstract: " Generates diagram of Swift symbol relationships " )
14
-
16
+
15
17
@OptionGroup ( )
16
18
var options : Options
17
-
19
+
18
20
func run( ) throws {
19
21
let module = try Module ( paths: options. inputs)
20
- print ( GraphViz . diagram ( of: module) , to: & standardOutput)
22
+ print ( diagram ( of: module) , to: & standardOutput)
21
23
}
22
24
}
23
25
}
24
26
25
27
// MARK: -
26
28
27
- enum GraphViz {
28
- static func diagram( of module: Module ) -> String {
29
- var lines : [ String ] = [ ]
29
+ fileprivate func diagram( of module: Module ) -> String {
30
+ var graph = Graph ( directed: true )
31
+
32
+ for (baseClass, subclasses) in module. interface. classHierarchies {
33
+ var subgraph = Subgraph ( id: " cluster_ \( baseClass. id. description. replacingOccurrences ( of: " . " , with: " _ " ) ) " )
30
34
31
- var classClusters : [ Symbol : Set < Symbol > ] = [ : ]
32
- for baseClass in module . interface . baseClasses {
33
- var superclasses = Set ( CollectionOfOne ( baseClass ) )
35
+ for subclass in subclasses {
36
+ var subclassNode = Node ( " \( subclass . id ) " )
37
+ subclassNode . shape = . box
34
38
35
- while !superclasses. isEmpty {
36
- let subclasses = Set ( superclasses. flatMap { module. interface. typesInheriting ( from: $0) }
37
- . filter { $0. isPublic } )
38
- defer { superclasses = subclasses }
39
- classClusters [ baseClass, default: [ ] ] . formUnion ( subclasses)
39
+ if subclass. declaration. modifiers. contains ( where: { $0. name == " final " } ) {
40
+ subclassNode. strokeWidth = 2.0
40
41
}
41
- }
42
42
43
- for (baseClass, cluster) in classClusters {
44
- var clusterLines : [ String ] = [ ]
45
-
46
- for subclass in cluster {
47
- if subclass. declaration. modifiers. contains ( where: { $0. name == " final " } ) {
48
- clusterLines. append ( #"" \#( subclass. id) " [shape=box,peripheries=2];"# )
49
- } else {
50
- clusterLines. append ( #"" \#( subclass. id) " [shape=box];"# )
51
- }
52
-
53
- for superclass in module. interface. typesInherited ( by: subclass) {
54
- clusterLines. append ( #"" \#( subclass. id) " -> " \#( superclass. id) ";"# )
55
- }
56
- }
43
+ subgraph. append ( subclassNode)
44
+
45
+ for superclass in module. interface. typesInherited ( by: subclass) {
46
+ let superclassNode = Node ( " \( superclass. id) " )
47
+ subgraph. append ( superclassNode)
57
48
58
- if cluster. count > 1 {
59
- clusterLines = (
60
- [ " " , " subgraph cluster_ \( baseClass. id. description. replacingOccurrences ( of: " . " , with: " _ " ) ) { " ] +
61
- clusterLines. map { $0. indented ( ) } +
62
- [ " } " , " " ]
63
- )
49
+ let edge = Edge ( from: subclassNode, to: superclassNode)
50
+ subgraph. append ( edge)
64
51
}
65
-
66
- lines. append ( contentsOf: clusterLines)
67
52
}
53
+
54
+ if subclasses. count > 1 {
55
+ graph. append ( subgraph)
56
+ } else {
57
+ subgraph. nodes. forEach { graph. append ( $0) }
58
+ subgraph. edges. forEach { graph. append ( $0) }
59
+ }
60
+ }
61
+
68
62
69
- lines. append ( " " )
63
+ for symbol in ( module. interface. symbols. filter { $0. isPublic && $0. declaration is Type } ) {
64
+ let symbolNode = Node ( " \( symbol. id) " )
65
+ graph. append ( symbolNode)
70
66
71
- for symbol in ( module. interface. symbols. filter { $0. isPublic && $0. declaration is Type } ) {
72
- for inherited in module. interface. typesConformed ( by: symbol) {
73
- lines. append ( #"" \#( symbol. id) " -> " \#( inherited. id) ";"# )
74
- }
67
+ for inherited in module. interface. typesConformed ( by: symbol) {
68
+ let inheritedNode = Node ( " \( inherited. id. description) " )
69
+ let edge = Edge ( from: symbolNode, to: inheritedNode)
70
+
71
+ graph. append ( inheritedNode)
72
+ graph. append ( edge)
75
73
}
74
+ }
76
75
77
- lines = [ " digraph \( module. name) { " ] +
78
- lines. map { $0. indented ( ) } +
79
- [ " } " ]
76
+ let encoder = DOTEncoder ( )
77
+ let dot = encoder. encode ( graph)
80
78
81
- return lines. joined ( separator: " \n " )
82
- }
79
+ return dot
83
80
}
84
81
85
82
// MARK: -
0 commit comments