File: RenderNode%2BNavigatorIndex.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (212 lines) | stat: -rw-r--r-- 8,553 bytes parent folder | download
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 2024 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

/// A language specific representation of a render node value for building a navigator index.
protocol NavigatorIndexableRenderNodeRepresentation<Metadata> {
    associatedtype Metadata: NavigatorIndexableRenderMetadataRepresentation
    
    // Information that's the same for all language variants
    var identifier: ResolvedTopicReference { get }
    var references: [String: RenderReference] { get }
    var kind: RenderNode.Kind { get }
    var sections: [RenderSection] { get }
    
    // Information that's different for each language variant
    var metadata: Metadata { get }
    var topicSections: [TaskGroupRenderSection] { get }
    var defaultImplementationsSections: [TaskGroupRenderSection] { get }
}

/// A language specific representation of a render metadata value for building a navigator index.
protocol NavigatorIndexableRenderMetadataRepresentation {
    // Information that's the same for all language variants
    var role: String? { get }
    var images: [TopicImage] { get }
    
    // Information that's different for each language variant
    var title: String? { get }
    var navigatorTitle: [DeclarationRenderSection.Token]? { get }
    var fragments: [DeclarationRenderSection.Token]? { get }
    var externalID: String? { get }
    var roleHeading: String? { get }
    var symbolKind: String? { get }
    var platforms: [AvailabilityRenderItem]? { get }
}

extension NavigatorIndexableRenderNodeRepresentation {
    var icon: RenderReferenceIdentifier? {
        metadata.images.first { $0.type == .icon }?.identifier
    }
}

extension RenderNode: NavigatorIndexableRenderNodeRepresentation {}
extension RenderMetadata: NavigatorIndexableRenderMetadataRepresentation {}

struct RenderMetadataVariantView: NavigatorIndexableRenderMetadataRepresentation {
    var wrapped: RenderMetadata
    var traits: [RenderNode.Variant.Trait]
    
    // The same for all language variants
    var role: String? {
        wrapped.role
    }
    var images: [TopicImage] {
        wrapped.images
    }
    
    // Different for each language variant
    var title: String? {
        wrapped.titleVariants.value(for: traits)
    }
    var navigatorTitle: [DeclarationRenderSection.Token]? {
        wrapped.navigatorTitleVariants.value(for: traits)
    }
    var fragments: [DeclarationRenderSection.Token]? {
        wrapped.fragmentsVariants.value(for: traits)
    }
    var externalID: String? {
        wrapped.externalIDVariants.value(for: traits)
    }
    var roleHeading: String? {
        wrapped.roleHeadingVariants.value(for: traits)
    }
    var symbolKind: String? {
        wrapped.symbolKindVariants.value(for: traits)
    }
    var platforms: [AvailabilityRenderItem]? {
        wrapped.platformsVariants.value(for: traits)
    }
}

struct RenderNodeVariantView: NavigatorIndexableRenderNodeRepresentation {
    var wrapped: RenderNode
    var traits: [RenderNode.Variant.Trait]
    
    init(wrapped: RenderNode, traits: [RenderNode.Variant.Trait]) {
        self.wrapped = wrapped
        self.traits = traits
        let traitLanguages = traits.map {
            switch $0 {
            case .interfaceLanguage(let id):
                return SourceLanguage(id: id)
            }
        }
        self.identifier = wrapped.identifier.withSourceLanguages(Set(traitLanguages))
        self.metadata = RenderMetadataVariantView(wrapped: wrapped.metadata, traits: traits)
    }
    
    // Computed during initialization
    var identifier: ResolvedTopicReference
    var metadata: RenderMetadataVariantView
    
    // The same for all language variants
    var references: [String: any RenderReference] { wrapped.references }
    var kind: RenderNode.Kind { wrapped.kind }
    var sections: [any RenderSection] { wrapped.sections }
    
    // Different for each language variant
    var topicSections: [TaskGroupRenderSection] {
        wrapped.topicSectionsVariants.value(for: traits)
    }
    var defaultImplementationsSections: [TaskGroupRenderSection] {
        wrapped.defaultImplementationsSectionsVariants.value(for: traits)
    }
}

private let typesThatShouldNotUseNavigatorTitle: Set<NavigatorIndex.PageType> = [
    .framework, .class, .structure, .enumeration, .protocol, .typeAlias, .associatedType, .extension
]

extension NavigatorIndexableRenderNodeRepresentation {
    /// Returns a navigator title preferring the fragments inside the metadata, if applicable.
    func navigatorTitle() -> String? {
        let tokens: [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 !typesThatShouldNotUseNavigatorTitle.contains(pageType) else {
                return metadata.title
            }
            tokens = metadata.fragments
        } else {
            tokens = metadata.navigatorTitle
        }
        
        return tokens?.map(\.text).joined() ?? metadata.title
    }
    
    /// Returns the type of page for the render node.
    func navigatorPageType() -> NavigatorIndex.PageType {
        // This is a workaround to support plist keys.
        switch metadata.roleHeading?.lowercased() {
            case "property list key":           return .propertyListKey
            case "property list key reference": return .propertyListKeyReference
            default: break
        }
        
        switch kind {
            case .article:  return metadata.role.map { .init(role: $0) }
                                ?? .article
            case .tutorial: return .tutorial
            case .section:  return .section
            case .overview: return .overview
            case .symbol:   return metadata.symbolKind.map { .init(symbolKind: $0) }
                                ?? metadata.role.map { .init(role: $0) }
                                ?? .symbol
        }
    }
}

extension NavigatorIndexableRenderNodeRepresentation {
    func navigatorChildren(for traits: [RenderNode.Variant.Trait]?) -> [RenderRelationshipsGroup] {
        switch kind {
        case .overview:
            var groups = [RenderRelationshipsGroup]()
            for case let section as VolumeRenderSection in sections {
                groups.append(contentsOf: section.chapters.map { chapter in
                    RenderRelationshipsGroup(
                        name: chapter.name,
                        abstract: nil,
                        references: chapter.tutorials.compactMap { self.references[$0.identifier] as? TopicRenderReference }
                    )
                })
            }
            return groups
        default:
            // Gather all topic references, transformed based on the traits, organizer by their identifier
            let references: [String: TopicRenderReference] = references.values.reduce(into: [:]) { acc, renderReference in
                guard var renderReference = renderReference as? TopicRenderReference else { return }
                // Transform the topic reference to hold the variant title
                if let traits {
                    renderReference.title = renderReference.titleVariants.applied(to: renderReference.title, for: traits)
                }
                acc[renderReference.identifier.identifier] = renderReference
            }
            
            func makeGroup(topicSection: TaskGroupRenderSection, isNestingReferences: Bool) -> RenderRelationshipsGroup {
                RenderRelationshipsGroup(
                    name: topicSection.title,
                    abstract: nil, // The navigator index only needs the title and the references.
                    references: topicSection.identifiers.map { references[$0]! },
                    referencesAreNested: isNestingReferences
                )
            }
            
            return topicSections.map {
                makeGroup(topicSection: $0, isNestingReferences: false)
            } + defaultImplementationsSections.map {
                makeGroup(topicSection: $0, isNestingReferences: true)
            }
        }
    }
}