-
Notifications
You must be signed in to change notification settings - Fork 140
/
Copy pathNavigatorIndex+Ext.swift
137 lines (115 loc) · 5.28 KB
/
NavigatorIndex+Ext.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
This source file is part of the Swift.org open source project
Copyright (c) 2021 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
import Foundation
/**
This class provides a simple way to transform a `FileSystemProvider` into a `RenderNodeProvider` to feed an index builder.
The data from the disk is fetched and processed in an efficient way to build a navigator index.
*/
public class FileSystemRenderNodeProvider: RenderNodeProvider {
/// The internal `FileSystemProvider` reference.
private let dataProvider: FileSystemProvider
/// The list of problems the provider encountered during the process.
private var problems = [Problem]()
/// The enqueued file system nodes.
private var queue = [FSNode]()
/**
Initialize an instance to provide `RenderNode` instances from a give `FileSystemProvider`.
*/
public init(fileSystemProvider: FileSystemProvider) {
dataProvider = fileSystemProvider
// Insert the first node in the queue
queue.append(fileSystemProvider.fileSystem)
}
/// Returns a render node that can be processed by an index creator, for example.
public func getRenderNode() -> RenderNode? {
var renderNode: RenderNode? = nil
while let next = queue.first, renderNode == nil {
switch next {
case .directory(let dir):
queue.append(contentsOf: dir.children)
case .file(let file):
// we need to process JSON files only
if file.url.pathExtension.lowercased() == "json" {
do {
let fileHandle = try FileHandle(forReadingFrom: file.url)
if let data = try fileHandle.readToEnd() {
renderNode = try RenderNode.decode(fromJSON: data)
}
} catch {
let diagnostic = Diagnostic(source: file.url,
severity: .warning,
range: nil,
identifier: "org.swift.docc",
summary: "Invalid file found while indexing content: \(error.localizedDescription)")
let problem = Problem(diagnostic: diagnostic, possibleSolutions: [])
problems.append(problem)
}
}
}
queue.removeFirst()
}
return renderNode
}
/// Get the problems that happened during the process.
/// - Returns: An array with the problems encountered during the filesystem read of render nodes.
public func getProblems() -> [Problem] {
return problems
}
}
extension RenderNode {
private static let typesThatShouldNotUseNavigatorTitle: Set<NavigatorIndex.PageType> = [
.framework, .class, .structure, .enumeration, .protocol, .typeAlias, .associatedType, .extension
]
/// Returns a navigator title preferring the fragments inside the metadata, if applicable.
func navigatorTitle() -> String? {
let fragments: [DeclarationRenderSection.Token]?
// FIXME: Use `metadata.navigatorTitle` for all Swift symbols (github.com/apple/swift-docc/issues/176).
if identifier.sourceLanguage == .swift || (metadata.navigatorTitle ?? []).isEmpty {
let pageType = navigatorPageType()
guard !Self.typesThatShouldNotUseNavigatorTitle.contains(pageType) else {
return metadata.title
}
fragments = metadata.fragments
} else {
fragments = metadata.navigatorTitle
}
return fragments?.map(\.text).joined() ?? metadata.title
}
/// Returns the NavigatorIndex.PageType indicating the type of the page.
public func navigatorPageType() -> NavigatorIndex.PageType {
// This is a workaround to support plist keys.
if let roleHeading = metadata.roleHeading?.lowercased() {
if roleHeading == "property list key" {
return .propertyListKey
} else if roleHeading == "property list key reference" {
return .propertyListKeyReference
}
}
switch self.kind {
case .article:
if let role = metadata.role {
return NavigatorIndex.PageType(role: role)
}
return NavigatorIndex.PageType.article
case .tutorial:
return NavigatorIndex.PageType.tutorial
case .section:
return NavigatorIndex.PageType.section
case .overview:
return NavigatorIndex.PageType.overview
case .symbol:
if let symbolKind = metadata.symbolKind {
return NavigatorIndex.PageType(symbolKind: symbolKind)
}
if let role = metadata.role {
return NavigatorIndex.PageType(role: role)
}
return NavigatorIndex.PageType.symbol
}
}
}