Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Treat external content as pre-rendered #806

Merged
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9a1c383
Treat all externally resolved content as "pre-rendered"
d-ronnqvist Jan 18, 2024
c043a72
Collect external links grouped by bundle ID to avoid need to assert
d-ronnqvist Jan 18, 2024
7c4c28a
Use new cache struct for local content as well
d-ronnqvist Jan 18, 2024
244168e
Remove configuration that's never used
d-ronnqvist Jan 19, 2024
a92f237
Remove test resolver that's never used
d-ronnqvist Jan 19, 2024
6dbe338
Remove private types that are never used
d-ronnqvist Jan 19, 2024
638b5a4
Remove unused argument to problem factory method
d-ronnqvist Jan 19, 2024
7f76c83
Fix a performance regression
d-ronnqvist Jan 19, 2024
09117f7
Fix a bug where external resolved references with updated paths wasn'…
d-ronnqvist Jan 19, 2024
35c60df
Merge branch 'main' into treat-external-content-as-prerendered
d-ronnqvist Jan 22, 2024
71131cc
Merge branch 'main' into treat-external-content-as-prerendered
d-ronnqvist Jan 29, 2024
a7a7c58
Update test to expect relative URL
d-ronnqvist Jan 29, 2024
7322841
Add more documentation for ContentCache struct
d-ronnqvist Jan 29, 2024
994a063
Remove reserveSymbolIDCapacity parameters that was always passed true
d-ronnqvist Jan 29, 2024
733d228
Remove argument label for `value` parameter
d-ronnqvist Jan 29, 2024
014791d
Rename references to allReferences to make it explicit
d-ronnqvist Jan 29, 2024
91254dd
Document when the resolved reference doesn't match the authored link
d-ronnqvist Jan 29, 2024
aba61b3
More documentation for how the convert service fallback resolver works
d-ronnqvist Jan 29, 2024
3616630
More documentation for how the local and external content caches are …
d-ronnqvist Jan 29, 2024
bb1c7bb
Update new test to lookup nodes by symbol ID directly in the cache
d-ronnqvist Jan 29, 2024
bfd1a96
Consistently use `---` instead of em dashes in documentation comments
d-ronnqvist Jan 29, 2024
b1bc2ae
Merge branch 'main' into treat-external-content-as-prerendered
d-ronnqvist Feb 2, 2024
728413f
Merge branch 'main' into treat-external-content-as-prerendered
d-ronnqvist Feb 5, 2024
f16267c
Properly test automatic curation for extended symbols
d-ronnqvist Feb 5, 2024
3c858a0
Merge branch 'main' into treat-external-content-as-prerendered
d-ronnqvist Feb 7, 2024
f7804c1
Merge branch 'main' into treat-external-content-as-prerendered
d-ronnqvist Feb 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions Sources/SwiftDocC/Benchmark/Metrics/ExternalTopicsHash.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2021 Apple Inc. and the Swift project authors
Copyright (c) 2021-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
Expand All @@ -11,19 +11,18 @@
import Foundation

extension Benchmark {
/// A hash metric produced off the externally resolved links and symbols.
/// A hash metric produced off the externally resolved links.
///
/// Use this metric to verify that your code changes
/// did not affect external resolving.
/// Use this metric to verify that your code changes did not affect external link resolution.
public class ExternalTopicsHash: BenchmarkMetric {
public static let identifier = "external-topics-hash"
public static let displayName = "External Topics Checksum"

/// Creates a new metric and stores the checksum of the given documentation context external topics.
/// - Parameter context: A documentation context.
/// Creates a new metric that stores the checksum of the successfully externally resolved links.
/// - Parameter context: A documentation context that the external links were resolved in.
public init(context: DocumentationContext) {
// If there are no externally resolved topics return quickly.
guard !context.externallyResolvedLinks.isEmpty || !context.externallyResolvedSymbols.isEmpty else {
guard !context.externallyResolvedLinks.isEmpty else {
return
}

Expand All @@ -37,7 +36,6 @@ extension Benchmark {
return nil
}
}).sorted().joined()
+ context.externallyResolvedSymbols.map({ $0.absoluteString }).sorted().joined()

result = .checksum(Checksum.md5(of: Data(sourceString.utf8)))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
Copyright (c) 2021-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
Expand Down Expand Up @@ -155,7 +155,6 @@ public struct ConvertService: DocumentationService {

// Enable support for generating documentation for standalone articles and tutorials.
context.allowsRegisteringArticlesWithoutTechnologyRoot = true
context.allowsRegisteringUncuratedTutorials = true
context.considerDocumentationExtensionsThatDoNotMatchSymbolsAsResolved = true

context.configureSymbolGraph = { symbolGraph in
Expand All @@ -173,9 +172,8 @@ public struct ConvertService: DocumentationService {
convertRequestIdentifier: messageIdentifier
)

context.fallbackReferenceResolvers[request.bundleInfo.identifier] = resolver
context.fallbackAssetResolvers[request.bundleInfo.identifier] = resolver
context.externalSymbolResolver = resolver
context.convertServiceFallbackResolver = resolver
context.globalExternalSymbolResolver = resolver
}

var converter = try self.converter ?? DocumentationConverter(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
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 resolver that attempts to resolve local references that wasn't included in the catalog or symbol input.
///
/// The ``ConvertService`` builds documentation for a single page at a time. If this page's content contains references to other local symbols, pages, or
/// assets that aren't included in the original ``ConvertRequest``, this fallback resolver resolves those references.
d-ronnqvist marked this conversation as resolved.
Show resolved Hide resolved
///
/// The ``ConvertService`` only renders the one page that it provided inputs for. Because of this, the content that this fallback resolver returns is considered
d-ronnqvist marked this conversation as resolved.
Show resolved Hide resolved
/// "external" content, even if it represents pages that would be "local" if the full project was built together.
protocol ConvertServiceFallbackResolver {

// MARK: References

/// Attempts to resolve an unresolved reference for a page that couldn't be resolved locally.
///
/// - Parameter reference: The unresolved local reference.
/// - Returns: The resolved reference, or information about why the resolver failed to resolve the reference.
func resolve(_ reference: TopicReference) -> TopicReferenceResolutionResult

/// Returns a Creates a new documentation node with the documentation content for the external reference, if the given reference was
d-ronnqvist marked this conversation as resolved.
Show resolved Hide resolved
/// resolved by this resolver.
///
/// - Parameter reference: The local reference that this resolver may have previously resolved.
/// - Returns: A node with the documentation content for the referenced topic, or `nil` if the reference wasn't previously resolved by this resolver.
func entityIfPreviouslyResolved(with reference: ResolvedTopicReference) -> LinkResolver.ExternalEntity?

// MARK: Assets

/// Attempts to resolve an asset that couldn't be resolved locally.
///
/// - Parameter assetName: The name of the local asset to resolve.
/// - Returns: The local asset with the given name if found; otherwise `nil`.
func resolve(assetNamed assetName: String) -> DataAsset?
}
93 changes: 93 additions & 0 deletions Sources/SwiftDocC/Infrastructure/ContentCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
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
*/

extension DocumentationContext {
/// A cache for symbol and page content.
struct ContentCache<Value> {
private var storage = [ResolvedTopicReference: Value]()
private var symbolIndex = [String: ResolvedTopicReference]()

/// Accesses the value for a given reference.
/// - Parameter reference: The reference to find in the cache.
subscript(reference: ResolvedTopicReference) -> Value? {
// Avoid copying the values if possible
_read { yield storage[reference] }
d-ronnqvist marked this conversation as resolved.
Show resolved Hide resolved
_modify { yield &storage[reference] }
}

/// Adds a value to the cache for a given reference _and_ symbol ID.
/// - Parameters:
/// - value: The value to add to the cache.
/// - reference: The reference associated with that value.
/// - symbolID: The symbol ID associated with that value.
mutating func add(value: Value, reference: ResolvedTopicReference, symbolID: String) {
symbolIndex[symbolID] = reference
d-ronnqvist marked this conversation as resolved.
Show resolved Hide resolved
storage[reference] = value
}

/// Accesses the reference for a given symbol ID.
/// - Parameter symbolID: The symbol ID to find in the cache.
func reference(symbolID: String) -> ResolvedTopicReference? {
symbolIndex[symbolID]
}

/// Accesses the value for a given symbol ID.
/// - Parameter symbolID: The symbol ID to find in the cache.
subscript(symbolID: String) -> Value? {
// Avoid copying the values if possible
_read { yield symbolIndex[symbolID].map { storage[$0]! } }
d-ronnqvist marked this conversation as resolved.
Show resolved Hide resolved
}

/// Reserves enough space to store the specified number of values.
mutating func reserveCapacity(_ minimumCapacity: Int, reserveSymbolIDCapacity: Bool) {
storage.reserveCapacity(minimumCapacity)
if reserveSymbolIDCapacity {
symbolIndex.reserveCapacity(minimumCapacity)
}
}

/// Returns a list of all the references in the cache.
var references: [ResolvedTopicReference] {
return Array(storage.keys)
d-ronnqvist marked this conversation as resolved.
Show resolved Hide resolved
}

/// Returns a list of all the references in the cache.
var symbolReferences: [ResolvedTopicReference] {
return Array(symbolIndex.values)
}
}
}

// Support iterating over the cached values.
extension DocumentationContext.ContentCache: Collection {
typealias Wrapped = [ResolvedTopicReference: Value]
typealias Index = Wrapped.Index
typealias Element = Wrapped.Element

func makeIterator() -> Wrapped.Iterator {
storage.makeIterator()
}

var startIndex: Wrapped.Index {
d-ronnqvist marked this conversation as resolved.
Show resolved Hide resolved
storage.startIndex
}

var endIndex: Wrapped.Index {
storage.endIndex
}

func index(after i: Wrapped.Index) -> Wrapped.Index {
storage.index(after: i)
}

subscript(position: Wrapped.Index) -> Wrapped.Element {
_read { yield storage[position] }
d-ronnqvist marked this conversation as resolved.
Show resolved Hide resolved
}
}
4 changes: 2 additions & 2 deletions Sources/SwiftDocC/Infrastructure/CoverageDataEntry.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
Copyright (c) 2021-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
Expand Down Expand Up @@ -371,7 +371,7 @@ extension CoverageDataEntry {
)
let total = children.count
let documented = children.filter {
(context.nodeWithSymbolIdentifier($0.reference.description)?.semantic as? Symbol)?.abstractSection != nil
(context.documentationCache[$0.reference.description]?.semantic as? Symbol)?.abstractSection != nil
}.count

if total == 0 {
Expand Down
Loading