@@ -2,84 +2,81 @@ import ArgumentParser
22import Foundation
33import SwiftDoc
44import SwiftSemantics
5+ import GraphViz
6+ import DOT
57
68extension SwiftDoc {
79 struct Diagram : ParsableCommand {
810 struct Options : ParsableArguments {
911 @Argument ( help: " One or more paths to Swift files " )
1012 var inputs : [ String ]
1113 }
12-
14+
1315 static var configuration = CommandConfiguration ( abstract: " Generates diagram of Swift symbol relationships " )
14-
16+
1517 @OptionGroup ( )
1618 var options : Options
17-
19+
1820 func run( ) throws {
1921 let module = try Module ( paths: options. inputs)
20- print ( GraphViz . diagram ( of: module) , to: & standardOutput)
22+ print ( diagram ( of: module) , to: & standardOutput)
2123 }
2224 }
2325}
2426
2527// MARK: -
2628
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: " _ " ) ) " )
3034
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
3438
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
4041 }
41- }
4242
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)
5748
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)
6451 }
65-
66- lines. append ( contentsOf: clusterLines)
6752 }
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+
6862
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)
7066
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)
7573 }
74+ }
7675
77- lines = [ " digraph \( module. name) { " ] +
78- lines. map { $0. indented ( ) } +
79- [ " } " ]
76+ let encoder = DOTEncoder ( )
77+ let dot = encoder. encode ( graph)
8078
81- return lines. joined ( separator: " \n " )
82- }
79+ return dot
8380}
8481
8582// MARK: -
0 commit comments