@@ -19,30 +19,34 @@ extension PathHierarchy {
19
19
///
20
20
/// Includes information about:
21
21
/// - The node that was found
22
- /// - The remaining portion of the path.
23
- typealias PartialResult = ( node: Node , path : [ PathComponent ] )
22
+ /// - The portion of the path up and including to the found node and its trailing path separator .
23
+ typealias PartialResult = ( node: Node , pathPrefix : Substring )
24
24
25
25
/// No element was found at the beginning of the path.
26
26
///
27
27
/// Includes information about:
28
+ /// - The portion of the path up to the first path component.
28
29
/// - The remaining portion of the path. This may be empty
29
30
/// - A list of the names for the top level elements.
30
- case notFound( remaining: [ PathComponent ] , availableChildren: Set < String > )
31
+ case notFound( pathPrefix : Substring , remaining: [ PathComponent ] , availableChildren: Set < String > )
31
32
32
33
/// No element was found at the beginning of an absolute path.
33
34
///
34
35
/// Includes information about:
35
- /// - The remaining portion of the path.
36
+ /// - The portion of the path up to the first path component .
36
37
/// - A list of the names for the available modules.
37
- case moduleNotFound( remaining: [ PathComponent ] , availableChildren: Set < String > )
38
+ case moduleNotFound( pathPrefix : Substring , remaining: [ PathComponent ] , availableChildren: Set < String > )
38
39
39
40
/// Matched node does not correspond to a documentation page.
40
41
///
41
42
/// For partial symbol graph files, sometimes sparse nodes that don't correspond to known documentation need to be created to form a hierarchy. These nodes are not findable.
42
43
case unfindableMatch( Node )
43
44
44
45
/// A symbol link found a non-symbol match.
45
- case nonSymbolMatchForSymbolLink
46
+ ///
47
+ /// Includes information about:
48
+ /// - The path to the non-symbol match.
49
+ case nonSymbolMatchForSymbolLink( path: Substring )
46
50
47
51
/// Encountered an unknown disambiguation for a found node.
48
52
///
@@ -71,40 +75,27 @@ extension PathHierarchy {
71
75
}
72
76
73
77
extension PathHierarchy . Error {
74
- /// Generate a ``TopicReferenceResolutionError`` from this error using the given `context` and `originalReference`.
75
- ///
76
- /// The resulting ``TopicReferenceResolutionError`` is human-readable and provides helpful solutions.
77
- ///
78
+ /// Creates a value with structured information that can be used to present diagnostics about the error.
78
79
/// - Parameters:
79
- /// - originalReference: The raw input string that represents the body of the reference that failed to resolve. This string is used to calculate the proper replacement-ranges for fixits.
80
80
/// - fullNameOfNode: A closure that determines the full name of a node, to be displayed in collision diagnostics to precisely identify symbols and other pages.
81
- ///
82
- /// - Note: `Replacement`s produced by this function use `SourceLocation`s relative to the `originalReference`, i.e. the beginning
83
- /// of the _body_ of the original reference.
84
- func asTopicReferenceResolutionErrorInfo( originalReference: String , fullNameOfNode: ( PathHierarchy . Node ) -> String ) -> TopicReferenceResolutionErrorInfo {
81
+ /// - Note: `Replacement`s produced by this function use `SourceLocation`s relative to the link text excluding its surrounding syntax.
82
+ func makeTopicReferenceResolutionErrorInfo( fullNameOfNode: ( PathHierarchy . Node ) -> String ) -> TopicReferenceResolutionErrorInfo {
85
83
// This is defined inline because it captures `fullNameOfNode`.
86
84
func collisionIsBefore( _ lhs: ( node: PathHierarchy . Node , disambiguation: String ) , _ rhs: ( node: PathHierarchy . Node , disambiguation: String ) ) -> Bool {
87
85
return fullNameOfNode ( lhs. node) + lhs . disambiguation
88
86
< fullNameOfNode ( rhs. node) + rhs. disambiguation
89
87
}
90
88
91
89
switch self {
92
- case . moduleNotFound( remaining: let remaining, availableChildren: let availableChildren) :
90
+ case . moduleNotFound( pathPrefix : let pathPrefix , remaining: let remaining, availableChildren: let availableChildren) :
93
91
let firstPathComponent = remaining. first! // This would be a .notFound error if the remaining components were empty.
94
92
95
- let solutions : [ Solution ]
96
- if let pathComponentIndex = originalReference. range ( of: firstPathComponent. full) {
97
- let startColumn = originalReference. distance ( from: originalReference. startIndex, to: pathComponentIndex. lowerBound)
98
- let replacementRange = SourceRange . makeRelativeRange ( startColumn: startColumn, length: firstPathComponent. full. count)
99
-
100
- let nearMisses = NearMiss . bestMatches ( for: availableChildren, against: firstPathComponent. name)
101
- solutions = nearMisses. map { candidate in
102
- Solution ( summary: " \( Self . replacementOperationDescription ( from: firstPathComponent. full, to: candidate) ) " , replacements: [
103
- Replacement ( range: replacementRange, replacement: candidate)
104
- ] )
105
- }
106
- } else {
107
- solutions = [ ]
93
+ let replacementRange = SourceRange . makeRelativeRange ( startColumn: pathPrefix. count, length: firstPathComponent. full. count)
94
+ let nearMisses = NearMiss . bestMatches ( for: availableChildren, against: String ( firstPathComponent. name) )
95
+ let solutions = nearMisses. map { candidate in
96
+ Solution ( summary: " \( Self . replacementOperationDescription ( from: firstPathComponent. full, to: candidate) ) " , replacements: [
97
+ Replacement ( range: replacementRange, replacement: candidate)
98
+ ] )
108
99
}
109
100
110
101
return TopicReferenceResolutionErrorInfo ( """
@@ -113,26 +104,19 @@ extension PathHierarchy.Error {
113
104
solutions: solutions
114
105
)
115
106
116
- case . notFound( remaining: let remaining, availableChildren: let availableChildren) :
107
+ case . notFound( pathPrefix : let pathPrefix , remaining: let remaining, availableChildren: let availableChildren) :
117
108
guard let firstPathComponent = remaining. first else {
118
109
return TopicReferenceResolutionErrorInfo (
119
110
" No local documentation matches this reference "
120
111
)
121
112
}
122
113
123
- let solutions : [ Solution ]
124
- if let pathComponentIndex = originalReference. range ( of: firstPathComponent. full) {
125
- let startColumn = originalReference. distance ( from: originalReference. startIndex, to: pathComponentIndex. lowerBound)
126
- let replacementRange = SourceRange . makeRelativeRange ( startColumn: startColumn, length: firstPathComponent. full. count)
127
-
128
- let nearMisses = NearMiss . bestMatches ( for: availableChildren, against: firstPathComponent. name)
129
- solutions = nearMisses. map { candidate in
130
- Solution ( summary: " \( Self . replacementOperationDescription ( from: firstPathComponent. full, to: candidate) ) " , replacements: [
131
- Replacement ( range: replacementRange, replacement: candidate)
132
- ] )
133
- }
134
- } else {
135
- solutions = [ ]
114
+ let replacementRange = SourceRange . makeRelativeRange ( startColumn: pathPrefix. count, length: firstPathComponent. full. count)
115
+ let nearMisses = NearMiss . bestMatches ( for: availableChildren, against: String ( firstPathComponent. name) )
116
+ let solutions = nearMisses. map { candidate in
117
+ Solution ( summary: " \( Self . replacementOperationDescription ( from: firstPathComponent. full, to: candidate) ) " , replacements: [
118
+ Replacement ( range: replacementRange, replacement: candidate)
119
+ ] )
136
120
}
137
121
138
122
return TopicReferenceResolutionErrorInfo ( """
@@ -146,23 +130,19 @@ extension PathHierarchy.Error {
146
130
\( node. name. singleQuoted) can't be linked to in a partial documentation build
147
131
""" )
148
132
149
- case . nonSymbolMatchForSymbolLink:
133
+ case . nonSymbolMatchForSymbolLink( path : let path ) :
150
134
return TopicReferenceResolutionErrorInfo ( " Symbol links can only resolve symbols " , solutions: [
151
135
Solution ( summary: " Use a '<doc:>' style reference. " , replacements: [
152
136
// the SourceRange points to the opening double-backtick
153
137
Replacement ( range: . makeRelativeRange( startColumn: - 2 , endColumn: 0 ) , replacement: " <doc: " ) ,
154
138
// the SourceRange points to the closing double-backtick
155
- Replacement ( range: . makeRelativeRange( startColumn: originalReference . count, endColumn: originalReference . count+ 2 ) , replacement: " > " ) ,
139
+ Replacement ( range: . makeRelativeRange( startColumn: path . count, endColumn: path . count+ 2 ) , replacement: " > " ) ,
156
140
] )
157
141
] )
158
142
159
143
case . unknownDisambiguation( partialResult: let partialResult, remaining: let remaining, candidates: let candidates) :
160
144
let nextPathComponent = remaining. first!
161
- var validPrefix = " "
162
- if !partialResult. path. isEmpty {
163
- validPrefix += PathHierarchy . joined ( partialResult. path) + " / "
164
- }
165
- validPrefix += nextPathComponent. name
145
+ let validPrefix = partialResult. pathPrefix + nextPathComponent. name
166
146
167
147
let disambiguations = nextPathComponent. full. dropFirst ( nextPathComponent. name. count)
168
148
let replacementRange = SourceRange . makeRelativeRange ( startColumn: validPrefix. count, length: disambiguations. count)
@@ -184,30 +164,27 @@ extension PathHierarchy.Error {
184
164
185
165
case . unknownName( partialResult: let partialResult, remaining: let remaining, availableChildren: let availableChildren) :
186
166
let nextPathComponent = remaining. first!
187
- let nearMisses = NearMiss . bestMatches ( for: availableChildren, against: nextPathComponent. name)
167
+ let nearMisses = NearMiss . bestMatches ( for: availableChildren, against: String ( nextPathComponent. name) )
188
168
189
169
// Use the authored disambiguation to try and reduce the possible near misses. For example, if the link was disambiguated with `-struct` we should
190
170
// only make suggestions for similarly spelled structs.
191
171
let filteredNearMisses = nearMisses. filter { name in
192
- ( try ? partialResult. node. children [ name] ? . find ( nextPathComponent. kind, nextPathComponent. hash) ) != nil
172
+ ( try ? partialResult. node. children [ name] ? . find ( nextPathComponent. kind. map ( String . init ) , nextPathComponent. hash. map ( String . init ) ) ) != nil
193
173
}
194
174
195
- var validPrefix = " "
196
- if !partialResult. path. isEmpty {
197
- validPrefix += PathHierarchy . joined ( partialResult. path) + " / "
198
- }
175
+ let pathPrefix = partialResult. pathPrefix
199
176
let solutions : [ Solution ]
200
177
if filteredNearMisses. isEmpty {
201
178
// If there are no near-misses where the authored disambiguation narrow down the results, replace the full path component
202
- let replacementRange = SourceRange . makeRelativeRange ( startColumn: validPrefix . count, length: nextPathComponent. full. count)
179
+ let replacementRange = SourceRange . makeRelativeRange ( startColumn: pathPrefix . count, length: nextPathComponent. full. count)
203
180
solutions = nearMisses. map { candidate in
204
181
Solution ( summary: " \( Self . replacementOperationDescription ( from: nextPathComponent. full, to: candidate) ) " , replacements: [
205
182
Replacement ( range: replacementRange, replacement: candidate)
206
183
] )
207
184
}
208
185
} else {
209
186
// If the authored disambiguation narrows down the possible near-misses, only replace the name part of the path component
210
- let replacementRange = SourceRange . makeRelativeRange ( startColumn: validPrefix . count, length: nextPathComponent. name. count)
187
+ let replacementRange = SourceRange . makeRelativeRange ( startColumn: pathPrefix . count, length: nextPathComponent. name. count)
211
188
solutions = filteredNearMisses. map { candidate in
212
189
Solution ( summary: " \( Self . replacementOperationDescription ( from: nextPathComponent. name, to: candidate) ) " , replacements: [
213
190
Replacement ( range: replacementRange, replacement: candidate)
@@ -219,20 +196,16 @@ extension PathHierarchy.Error {
219
196
\( nextPathComponent. full. singleQuoted) doesn't exist at \( partialResult. node. pathWithoutDisambiguation ( ) . singleQuoted)
220
197
""" ,
221
198
solutions: solutions,
222
- rangeAdjustment: . makeRelativeRange( startColumn: validPrefix . count, length: nextPathComponent. full. count)
199
+ rangeAdjustment: . makeRelativeRange( startColumn: pathPrefix . count, length: nextPathComponent. full. count)
223
200
)
224
201
225
202
case . lookupCollision( partialResult: let partialResult, remaining: let remaining, collisions: let collisions) :
226
203
let nextPathComponent = remaining. first!
227
204
228
- var validPrefix = " "
229
- if !partialResult. path. isEmpty {
230
- validPrefix += PathHierarchy . joined ( partialResult. path) + " / "
231
- }
232
- validPrefix += nextPathComponent. name
205
+ let pathPrefix = partialResult. pathPrefix + nextPathComponent. name
233
206
234
207
let disambiguations = nextPathComponent. full. dropFirst ( nextPathComponent. name. count)
235
- let replacementRange = SourceRange . makeRelativeRange ( startColumn: validPrefix . count, length: disambiguations. count)
208
+ let replacementRange = SourceRange . makeRelativeRange ( startColumn: pathPrefix . count, length: disambiguations. count)
236
209
237
210
let solutions : [ Solution ] = collisions. sorted ( by: collisionIsBefore) . map { ( node: PathHierarchy . Node , disambiguation: String ) -> Solution in
238
211
return Solution ( summary: " \( Self . replacementOperationDescription ( from: disambiguations. dropFirst ( ) , to: disambiguation) ) for \n \( fullNameOfNode ( node) . singleQuoted) " , replacements: [
@@ -244,7 +217,7 @@ extension PathHierarchy.Error {
244
217
\( nextPathComponent. full. singleQuoted) is ambiguous at \( partialResult. node. pathWithoutDisambiguation ( ) . singleQuoted)
245
218
""" ,
246
219
solutions: solutions,
247
- rangeAdjustment: . makeRelativeRange( startColumn: validPrefix . count - nextPathComponent. full. count, length: nextPathComponent. full. count)
220
+ rangeAdjustment: . makeRelativeRange( startColumn: pathPrefix . count - nextPathComponent. full. count, length: nextPathComponent. full. count)
248
221
)
249
222
}
250
223
}
0 commit comments