@@ -71,6 +71,8 @@ import SymbolKit
71
71
/// - In a paragraph of text, a link to this element will use the ``title`` as the link text and style the tile in code font if the ``kind`` is a type of symbol.
72
72
/// - In a task group, the the ``title`` and ``abstract-swift.property`` is displayed together to give more context about this element and the element may be marked as deprecated
73
73
/// based on the values of its ``platforms`` and other metadata about the current versions of the platforms.
74
+ ///
75
+ /// The summary may include content that vary based on the source language. The content that is different in another source language is specified in a ``Variant``. Any property on the variant that is `nil` has the same value as the summarized element's value.
74
76
public struct LinkDestinationSummary : Codable , Equatable {
75
77
/// The kind of the summarized element.
76
78
public let kind : DocumentationNode . Kind
@@ -142,6 +144,54 @@ public struct LinkDestinationSummary: Codable, Equatable {
142
144
///
143
145
/// A web server can use this list of URLs to redirect to the current URL.
144
146
public let redirects : [ URL ] ?
147
+
148
+ /// A variant of content for a summarized element.
149
+ ///
150
+ /// - Note: All properties except for ``traits`` are optional. If a property is `nil` it means that the value is the same as the summarized element's value.
151
+ public struct Variant : Codable , Equatable {
152
+ /// The traits of the variant.
153
+ public let traits : [ RenderNode . Variant . Trait ]
154
+
155
+ /// A wrapper for variant values that can either be specified, meaning the variant has a custom value, or not, meaning the variant has the same value as the summarized element.
156
+ ///
157
+ /// This alias is used to make the property declarations more explicit while at the same time offering the convenient syntax of optionals.
158
+ public typealias VariantValue = Optional
159
+
160
+ /// The kind of the variant or `nil` if the kind is the same as the summarized element.
161
+ public let kind : VariantValue < DocumentationNode . Kind >
162
+
163
+ /// The source language of the variant or `nil` if the kind is the same as the summarized element.
164
+ public let language : VariantValue < SourceLanguage >
165
+
166
+ /// The relative path of the variant or `nil` if the relative is the same as the summarized element.
167
+ public let path : VariantValue < String >
168
+
169
+ /// The title of the variant or `nil` if the title is the same as the summarized element.
170
+ public let title : VariantValue < String ? >
171
+
172
+ /// The abstract of the variant or `nil` if the abstract is the same as the summarized element.
173
+ ///
174
+ /// If the summarized element has an abstract but the variant doesn't, this property will be `Optional.some(nil)`.
175
+ public let abstract : VariantValue < Abstract ? >
176
+
177
+ /// The taskGroups of the variant or `nil` if the taskGroups is the same as the summarized element.
178
+ ///
179
+ /// If the summarized element has task groups but the variant doesn't, this property will be `Optional.some(nil)`.
180
+ public let taskGroups : VariantValue < [ TaskGroup ] ? >
181
+
182
+ /// The precise symbol identifier of the variant or `nil` if the precise symbol identifier is the same as the summarized element.
183
+ ///
184
+ /// If the summarized element has a precise symbol identifier but the variant doesn't, this property will be `Optional.some(nil)`.
185
+ public let usr : VariantValue < String ? >
186
+
187
+ /// The declaration of the variant or `nil` if the declaration is the same as the summarized element.
188
+ ///
189
+ /// If the summarized element has a declaration but the variant doesn't, this property will be `Optional.some(nil)`.
190
+ public let declarationFragments : VariantValue < DeclarationFragments ? >
191
+ }
192
+
193
+ /// The variants of content (kind, title, abstract, path, urs, declaration, and task groups) for this summarized element.
194
+ public let variants : [ Variant ]
145
195
}
146
196
147
197
// MARK: - Accessing the externally linkable elements
@@ -205,11 +255,7 @@ extension LinkDestinationSummary {
205
255
/// - taskGroups: The task groups that lists the children of this page.
206
256
/// - compiler: The content compiler that's used to render the node's abstract.
207
257
init ( documentationNode: DocumentationNode , path: String , taskGroups: [ TaskGroup ] , platforms: [ PlatformAvailability ] ? , compiler: inout RenderContentCompiler ) {
208
- let declaration = ( documentationNode. semantic as? Symbol ) ? . subHeading. map { declaration in
209
- return declaration. map { fragment in
210
- DeclarationRenderSection . Token ( fragment: fragment, identifier: nil )
211
- }
212
- }
258
+ let symbol = documentationNode. semantic as? Symbol
213
259
214
260
self . init (
215
261
kind: documentationNode. kind,
@@ -221,9 +267,10 @@ extension LinkDestinationSummary {
221
267
availableLanguages: documentationNode. availableSourceLanguages,
222
268
platforms: platforms,
223
269
taskGroups: taskGroups,
224
- usr: ( documentationNode. semantic as? Symbol ) ? . externalID,
225
- declarationFragments: declaration,
226
- redirects: ( documentationNode. semantic as? Redirected ) ? . redirects? . map { $0. oldPath }
270
+ usr: symbol? . externalID,
271
+ declarationFragments: symbol? . subHeading? . map { . init( fragment: $0, identifier: nil ) } ,
272
+ redirects: ( documentationNode. semantic as? Redirected ) ? . redirects? . map { $0. oldPath } ,
273
+ variants: [ ]
227
274
)
228
275
}
229
276
}
@@ -240,13 +287,13 @@ extension LinkDestinationSummary {
240
287
init ( landmark: Landmark , basePath: String , page: DocumentationNode , platforms: [ PlatformAvailability ] ? , compiler: inout RenderContentCompiler ) {
241
288
let anchor = urlReadableFragment ( landmark. title)
242
289
243
- let abstract : Abstract
290
+ let abstract : Abstract ?
244
291
if let abstracted = landmark as? Abstracted {
245
292
abstract = abstracted. renderedAbstract ( using: & compiler) ?? [ ]
246
293
} else if let paragraph = landmark. markup. children. lazy. compactMap ( { $0 as? Paragraph } ) . first, case RenderBlockContent . paragraph( let inlineContent) ? = compiler. visitParagraph ( paragraph) . first {
247
294
abstract = inlineContent
248
295
} else {
249
- abstract = [ ]
296
+ abstract = nil
250
297
}
251
298
252
299
self . init (
@@ -261,7 +308,8 @@ extension LinkDestinationSummary {
261
308
taskGroups: [ ] , // Landmarks have no children
262
309
usr: nil , // Only symbols have a USR
263
310
declarationFragments: nil , // Only symbols have declarations
264
- redirects: ( landmark as? Redirected ) ? . redirects? . map { $0. oldPath }
311
+ redirects: ( landmark as? Redirected ) ? . redirects? . map { $0. oldPath } ,
312
+ variants: [ ]
265
313
)
266
314
}
267
315
}
@@ -271,7 +319,7 @@ extension LinkDestinationSummary {
271
319
// Add Codable methods—which include an initializer—in an extension so that it doesn't override the member-wise initializer.
272
320
extension LinkDestinationSummary {
273
321
enum CodingKeys : String , CodingKey {
274
- case kind, path, referenceURL, title, abstract, language, taskGroups, usr, availableLanguages, platforms, redirects
322
+ case kind, path, referenceURL, title, abstract, language, taskGroups, usr, availableLanguages, platforms, redirects, variants
275
323
case declarationFragments = " fragments "
276
324
}
277
325
@@ -289,6 +337,9 @@ extension LinkDestinationSummary {
289
337
try container. encodeIfPresent ( usr, forKey: . usr)
290
338
try container. encodeIfPresent ( declarationFragments, forKey: . declarationFragments)
291
339
try container. encodeIfPresent ( redirects, forKey: . redirects)
340
+ if !variants. isEmpty {
341
+ try container. encode ( variants, forKey: . variants)
342
+ }
292
343
}
293
344
294
345
public init ( from decoder: Decoder ) throws {
@@ -320,5 +371,64 @@ extension LinkDestinationSummary {
320
371
usr = try container. decodeIfPresent ( String . self, forKey: . usr)
321
372
declarationFragments = try container. decodeIfPresent ( DeclarationFragments . self, forKey: . declarationFragments)
322
373
redirects = try container. decodeIfPresent ( [ URL ] . self, forKey: . redirects)
374
+
375
+ variants = try container. decodeIfPresent ( [ Variant ] . self, forKey: . variants) ?? [ ]
376
+ }
377
+ }
378
+
379
+ extension LinkDestinationSummary . Variant {
380
+ enum CodingKeys : String , CodingKey {
381
+ case traits, kind, path, title, abstract, language, usr, declarationFragments = " fragments " , taskGroups
382
+ }
383
+
384
+ public func encode( to encoder: Encoder ) throws {
385
+ var container = encoder. container ( keyedBy: CodingKeys . self)
386
+ try container. encode ( traits, forKey: . traits)
387
+ try container. encodeIfPresent ( kind? . id, forKey: . kind)
388
+ try container. encodeIfPresent ( path, forKey: . path)
389
+ try container. encodeIfPresent ( title, forKey: . title)
390
+ try container. encodeIfPresent ( abstract, forKey: . abstract)
391
+ try container. encodeIfPresent ( language, forKey: . language)
392
+ try container. encodeIfPresent ( usr, forKey: . usr)
393
+ try container. encodeIfPresent ( declarationFragments, forKey: . declarationFragments)
394
+ try container. encodeIfPresent ( taskGroups, forKey: . taskGroups)
395
+ }
396
+
397
+ public init ( from decoder: Decoder ) throws {
398
+ let container = try decoder. container ( keyedBy: CodingKeys . self)
399
+
400
+ let traits = try container. decode ( [ RenderNode . Variant . Trait ] . self, forKey: . traits)
401
+ for case . interfaceLanguage( let languageID) in traits {
402
+ guard SourceLanguage . knownLanguages. contains ( where: { $0. id == languageID } ) else {
403
+ throw DecodingError . dataCorruptedError ( forKey: . traits, in: container, debugDescription: " Unknown SourceLanguage identifier: ' \( languageID) '. " )
404
+ }
405
+ }
406
+ self . traits = traits
407
+
408
+ let kindID = try container. decodeIfPresent ( String . self, forKey: . kind)
409
+ if let kindID = kindID {
410
+ guard let foundKind = DocumentationNode . Kind. allKnownValues. first ( where: { $0. id == kindID } ) else {
411
+ throw DecodingError . dataCorruptedError ( forKey: . kind, in: container, debugDescription: " Unknown DocumentationNode.Kind identifier: ' \( kindID) '. " )
412
+ }
413
+ kind = foundKind
414
+ } else {
415
+ kind = nil
416
+ }
417
+
418
+ let languageID = try container. decodeIfPresent ( String . self, forKey: . language)
419
+ if let languageID = languageID {
420
+ guard let foundLanguage = SourceLanguage . knownLanguages. first ( where: { $0. id == languageID } ) else {
421
+ throw DecodingError . dataCorruptedError ( forKey: . language, in: container, debugDescription: " Unknown SourceLanguage identifier: ' \( languageID) '. " )
422
+ }
423
+ language = foundLanguage
424
+ } else {
425
+ language = nil
426
+ }
427
+ path = try container. decodeIfPresent ( String . self, forKey: . path)
428
+ title = try container. decodeIfPresent ( String ? . self, forKey: . title)
429
+ abstract = try container. decodeIfPresent ( LinkDestinationSummary . Abstract? . self, forKey: . abstract)
430
+ usr = try container. decodeIfPresent ( String ? . self, forKey: . title)
431
+ declarationFragments = try container. decodeIfPresent ( LinkDestinationSummary . DeclarationFragments? . self, forKey: . declarationFragments)
432
+ taskGroups = try container. decodeIfPresent ( [ LinkDestinationSummary . TaskGroup] ? . self, forKey: . taskGroups)
323
433
}
324
434
}
0 commit comments