Skip to content
This repository was archived by the owner on Jun 1, 2023. It is now read-only.

Commit e1b8d78

Browse files
committed
Initial implementation of HTML support
1 parent 868ded0 commit e1b8d78

31 files changed

+2035
-219
lines changed

Package.resolved

Lines changed: 41 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,19 @@ let package = Package(
99
.package(url: "https://github.com/apple/swift-syntax.git", .revision("swift-5.2-DEVELOPMENT-SNAPSHOT-2020-03-09-a")),
1010
.package(url: "https://github.com/SwiftDocOrg/SwiftSemantics.git", .branch("swift-5.2")),
1111
.package(url: "https://github.com/SwiftDocOrg/CommonMark.git", .branch("master")),
12-
.package(url: "https://github.com/SwiftDocOrg/SwiftMarkup.git", .upToNextMinor(from: "0.0.4")),
13-
.package(url: "https://github.com/SwiftDocOrg/GraphViz.git", .upToNextMinor(from: "0.0.1")),
14-
.package(url: "https://github.com/apple/swift-argument-parser", .upToNextMinor(from: "0.0.2")),
12+
.package(url: "https://github.com/SwiftDocOrg/SwiftMarkup.git", .upToNextMinor(from: "0.0.5")),
13+
.package(url: "https://github.com/SwiftDocOrg/GraphViz.git", .revision("ce0327a62c4b5a5ddd4741406ea54f164f228a54")),
14+
.package(url: "https://github.com/NSHipster/HypertextLiteral.git", .upToNextMinor(from: "0.0.2")),
15+
.package(url: "https://github.com/SwiftDocOrg/Markup.git", .revision("bcc9bff98749f8ed92221375591a1afd61b02f1a")),
16+
.package(url: "https://github.com/NSHipster/SwiftSyntaxHighlighter.git", .revision("fe39b4ec07e1e37872adf4b506d223ab27cf8cea")),
17+
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "0.0.2")),
1518
],
1619
targets: [
1720
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
1821
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
1922
.target(
2023
name: "swift-doc",
21-
dependencies: ["ArgumentParser", "SwiftDoc", "SwiftSemantics", "SwiftMarkup", "CommonMarkBuilder", "DCOV", "GraphViz"]
24+
dependencies: ["ArgumentParser", "SwiftDoc", "SwiftSemantics", "SwiftMarkup", "CommonMarkBuilder", "HypertextLiteral", "Markup", "DCOV", "GraphViz", "SwiftSyntaxHighlighter"]
2225
),
2326
.target(
2427
name: "DCOV",

Sources/SwiftDoc/Extensions/Array+Parallel.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import Dispatch
22

3-
extension Array {
3+
public extension RandomAccessCollection {
44
func parallelMap<T>(_ transform: (Element) throws -> T) throws -> [T] {
55
guard count > 1 else {
66
return try map(transform)
77
}
88

9-
var results = [(index: Int, result: Result<T, Error>)]()
9+
let indices = Array(self.indices)
10+
11+
var results = [(index: Index, result: Result<T, Error>)]()
1012
results.reserveCapacity(count)
1113

12-
let queue = DispatchQueue(label: "org.swiftdoc.swift-doc.parallelMap")
14+
let queue = DispatchQueue(label: #function)
1315
withoutActuallyEscaping(transform) { escapingtransform in
14-
DispatchQueue.concurrentPerform(iterations: count) { (index) in
16+
DispatchQueue.concurrentPerform(iterations: count) { (iteration) in
17+
let index = indices[iteration]
18+
1519
do {
1620
let transformed = try escapingtransform(self[index])
1721
queue.sync {
@@ -36,4 +40,8 @@ extension Array {
3640
func parallelFlatMap<T>(transform: (Element) throws -> [T]) throws -> [T] {
3741
return try parallelMap(transform).flatMap { $0 }
3842
}
43+
44+
func parallelForEach(_ body: (Element) throws -> Void) throws {
45+
_ = try parallelMap(body)
46+
}
3947
}

Sources/SwiftDoc/Interface.swift

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,20 @@ public final class Interface: Codable {
1313

1414
// MARK: -
1515

16-
private lazy var symbolsByIdentifier: [Symbol.ID: [Symbol]] = {
16+
public lazy var symbolsGroupedByIdentifier: [Symbol.ID: [Symbol]] = {
1717
return Dictionary(grouping: symbols, by: { $0.id })
1818
}()
1919

20+
public lazy var symbolsGroupedByName: [String: [Symbol]] = {
21+
return Dictionary(grouping: symbols, by: { $0.name })
22+
}()
23+
2024
public private(set) lazy var topLevelSymbols: [Symbol] = {
21-
return symbols.filter { $0.declaration is Type || $0.id.pathComponents.isEmpty }
25+
return symbols.filter { $0.api is Type || $0.id.pathComponents.isEmpty }
2226
}()
2327

2428
public private(set) lazy var baseClasses: [Symbol] = {
25-
return symbols.filter { $0.declaration is Class &&
29+
return symbols.filter { $0.api is Class &&
2630
typesInherited(by: $0).isEmpty }
2731
}()
2832

@@ -42,6 +46,12 @@ public final class Interface: Codable {
4246
return classClusters
4347
}()
4448

49+
private lazy var extensionsByExtendedType: [String: [Extension]] = {
50+
return Dictionary(grouping: symbols.flatMap { $0.context.compactMap { $0 as? Extension } }) {
51+
$0.extendedType
52+
}
53+
}()
54+
4555
public private(set) lazy var relationships: [Relationship] = {
4656
var relationships: Set<Relationship> = []
4757
for symbol in symbols {
@@ -50,9 +60,9 @@ public final class Interface: Codable {
5060
if let container = symbol.context.compactMap({ $0 as? Symbol }).last {
5161
let predicate: Relationship.Predicate
5262

53-
switch container.declaration {
63+
switch container.api {
5464
case is Protocol:
55-
if symbol.declaration.modifiers.contains(where: { $0.name == "optional" }) {
65+
if symbol.api.modifiers.contains(where: { $0.name == "optional" }) {
5666
predicate = .optionalRequirementOf
5767
} else {
5868
predicate = .requirementOf
@@ -65,9 +75,10 @@ public final class Interface: Codable {
6575
}
6676

6777
if let `extension` = `extension` {
68-
for extended in symbols.filter({ $0.declaration is Type && $0.id.matches(`extension`.extendedType) }) {
78+
if let extended = symbols.first(where: { $0.api is Type && $0.id.matches(`extension`.extendedType) }) {
79+
6980
let predicate: Relationship.Predicate
70-
switch extended.declaration {
81+
switch extended.api {
7182
case is Protocol:
7283
predicate = .defaultImplementationOf
7384
default:
@@ -78,17 +89,26 @@ public final class Interface: Codable {
7889
}
7990
}
8091

81-
if let type = symbol.declaration as? Type {
82-
let inheritance = Set((type.inheritance + (`extension`?.inheritance ?? [])).flatMap { $0.split(separator: "&").map { $0.trimmingCharacters(in: .whitespaces) } })
83-
for name in inheritance {
84-
let inheritedTypes = symbols.filter({ ($0.declaration is Class || $0.declaration is Protocol) && $0.id.matches(name) })
92+
if let type = symbol.api as? Type {
93+
var inheritedTypeNames: Set<String> = []
94+
inheritedTypeNames.formUnion(type.inheritance.flatMap { $0.split(separator: "&").map { $0.trimmingCharacters(in: .whitespaces) }
95+
})
96+
97+
for `extension` in extensionsByExtendedType[symbol.id.description] ?? [] {
98+
inheritedTypeNames.formUnion(`extension`.inheritance)
99+
}
100+
101+
inheritedTypeNames = Set(inheritedTypeNames.flatMap { $0.split(separator: "&").map { $0.trimmingCharacters(in: .whitespaces) } })
102+
103+
for name in inheritedTypeNames {
104+
let inheritedTypes = symbols.filter({ ($0.api is Class || $0.api is Protocol) && $0.id.matches(name) })
85105
if inheritedTypes.isEmpty {
86-
let inherited = Symbol(declaration: Unknown(name: name), context: [], documentation: nil, sourceLocation: nil)
106+
let inherited = Symbol(api: Unknown(name: name), context: [], declaration: nil, documentation: nil, sourceLocation: nil)
87107
relationships.insert(Relationship(subject: symbol, predicate: .inheritsFrom, object: inherited))
88108
} else {
89109
for inherited in inheritedTypes {
90110
let predicate: Relationship.Predicate
91-
if symbol.declaration is Class, inherited.declaration is Class {
111+
if symbol.api is Class, inherited.api is Class {
92112
predicate = .inheritsFrom
93113
} else {
94114
predicate = .conformsTo
@@ -104,26 +124,26 @@ public final class Interface: Codable {
104124
return Array(relationships)
105125
}()
106126

107-
private lazy var relationshipsBySubject: [Symbol.ID: [Relationship]] = {
127+
public private(set) lazy var relationshipsBySubject: [Symbol.ID: [Relationship]] = {
108128
Dictionary(grouping: relationships, by: { $0.subject.id })
109129
}()
110130

111-
private lazy var relationshipsByObject: [Symbol.ID: [Relationship]] = {
131+
public private(set) lazy var relationshipsByObject: [Symbol.ID: [Relationship]] = {
112132
Dictionary(grouping: relationships, by: { $0.object.id })
113133
}()
114134

115135
// MARK: -
116136

117137
public func members(of symbol: Symbol) -> [Symbol] {
118-
return relationshipsByObject[symbol.id]?.filter { $0.predicate == .memberOf }.map { $0.subject } ?? []
138+
return relationshipsByObject[symbol.id]?.filter { $0.predicate == .memberOf }.map { $0.subject }.sorted() ?? []
119139
}
120140

121141
public func requirements(of symbol: Symbol) -> [Symbol] {
122-
return relationshipsByObject[symbol.id]?.filter { $0.predicate == .requirementOf }.map { $0.subject } ?? []
142+
return relationshipsByObject[symbol.id]?.filter { $0.predicate == .requirementOf }.map { $0.subject }.sorted() ?? []
123143
}
124144

125145
public func optionalRequirements(of symbol: Symbol) -> [Symbol] {
126-
return relationshipsByObject[symbol.id]?.filter { $0.predicate == .optionalRequirementOf }.map { $0.subject } ?? []
146+
return relationshipsByObject[symbol.id]?.filter { $0.predicate == .optionalRequirementOf }.map { $0.subject }.sorted() ?? []
127147
}
128148

129149
public func typesInherited(by symbol: Symbol) -> [Symbol] {
@@ -143,6 +163,6 @@ public final class Interface: Codable {
143163
}
144164

145165
public func conditionalCounterparts(of symbol: Symbol) -> [Symbol] {
146-
return symbolsByIdentifier[symbol.id]?.filter { $0 != symbol } ?? []
166+
return symbolsGroupedByIdentifier[symbol.id]?.filter { $0 != symbol }.sorted() ?? []
147167
}
148168
}

Sources/SwiftDoc/Module.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ public final class Module: Codable {
88
public let sourceFiles: [SourceFile]
99

1010
public lazy var interface: Interface = {
11-
Interface(imports: sourceFiles.flatMap { $0.imports }, symbols: sourceFiles.flatMap { $0.symbols })
11+
let imports = sourceFiles.flatMap { $0.imports }
12+
let symbols = sourceFiles.flatMap { $0.symbols }
13+
return Interface(imports: imports, symbols: symbols)
1214
}()
1315

1416
public required init(name: String = "Anonymous", sourceFiles: [SourceFile]) {

Sources/SwiftDoc/SourceFile.swift

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,12 @@ public struct SourceFile: Hashable, Codable {
3535
var visitedSymbols: [Symbol] = []
3636
var visitedImports: [Import] = []
3737

38+
let fileURL: URL
3839
let sourceLocationConverter: SourceLocationConverter
3940

4041
init(file url: URL, relativeTo directory: URL) throws {
42+
self.fileURL = url
43+
4144
let tree = try SyntaxParser.parse(url)
4245
sourceLocationConverter = SourceLocationConverter(file: url.path(relativeTo: directory), tree: tree)
4346
super.init()
@@ -48,21 +51,22 @@ public struct SourceFile: Hashable, Codable {
4851
}
4952

5053
func symbol<Node, Declaration>(_ type: Declaration.Type, _ node: Node) -> Symbol? where Declaration: API & ExpressibleBySyntax, Node == Declaration.Syntax {
51-
guard let declaration = Declaration(node) else { return nil }
52-
return symbol(node, declaration: declaration)
54+
guard let api = Declaration(node) else { return nil }
55+
return symbol(node, api: api)
5356
}
5457

55-
func symbol<Node: SyntaxProtocol>(_ node: Node, declaration: API) -> Symbol? {
58+
func symbol<Node: SyntaxProtocol>(_ node: Node, api: API) -> Symbol? {
5659
guard let documentation = try? Documentation.parse(node.documentation) else { return nil }
5760
let sourceLocation = sourceLocationConverter.location(for: node.position)
58-
return Symbol(declaration: declaration, context: context, documentation: documentation, sourceLocation: sourceLocation)
61+
62+
return Symbol(api: api, context: context, declaration: "\(api)", documentation: documentation, sourceLocation: sourceLocation)
5963
}
6064

6165
func push(_ symbol: Symbol?) {
6266
guard let symbol = symbol else { return }
6367
visitedSymbols.append(symbol)
6468

65-
switch symbol.declaration {
69+
switch symbol.api {
6670
case is Class,
6771
is Enumeration,
6872
is Protocol,
@@ -108,7 +112,7 @@ public struct SourceFile: Hashable, Codable {
108112

109113
override func visit(_ node: EnumCaseDeclSyntax) -> SyntaxVisitorContinueKind {
110114
for `case` in Enumeration.Case.cases(from: node) {
111-
push(symbol(node, declaration: `case`))
115+
push(symbol(node, api: `case`))
112116
}
113117
return .skipChildren
114118
}
@@ -175,19 +179,19 @@ public struct SourceFile: Hashable, Codable {
175179

176180
override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
177181
for variable in Variable.variables(from: node) {
178-
push(symbol(node, declaration: variable))
182+
push(symbol(node, api: variable))
179183
}
180184
return .skipChildren
181185
}
182186

183187
// MARK: -
184188

185189
override func visitPost(_ node: ClassDeclSyntax) {
186-
assert((pop() as? Symbol)?.declaration is Class)
190+
assert((pop() as? Symbol)?.api is Class)
187191
}
188192

189193
override func visitPost(_ node: EnumDeclSyntax) {
190-
assert((pop() as? Symbol)?.declaration is Enumeration)
194+
assert((pop() as? Symbol)?.api is Enumeration)
191195
}
192196

193197
override func visitPost(_ node: ExtensionDeclSyntax) {
@@ -199,11 +203,11 @@ public struct SourceFile: Hashable, Codable {
199203
}
200204

201205
override func visitPost(_ node: ProtocolDeclSyntax) {
202-
assert((pop() as? Symbol)?.declaration is Protocol)
206+
assert((pop() as? Symbol)?.api is Protocol)
203207
}
204208

205209
override func visitPost(_ node: StructDeclSyntax) {
206-
assert((pop() as? Symbol)?.declaration is Structure)
210+
assert((pop() as? Symbol)?.api is Structure)
207211
}
208212
}
209213
}

0 commit comments

Comments
 (0)