diff --git a/Sources/SwiftDocC/DocumentationService/Convert/ConvertService.swift b/Sources/SwiftDocC/DocumentationService/Convert/ConvertService.swift index e03d60e729..fc22a5c356 100644 --- a/Sources/SwiftDocC/DocumentationService/Convert/ConvertService.swift +++ b/Sources/SwiftDocC/DocumentationService/Convert/ConvertService.swift @@ -243,7 +243,8 @@ public struct ConvertService: DocumentationService { .compactMap { (value, isDocumentationExtensionContent) -> (ResolvedTopicReference, RenderReferenceStore.TopicContent)? in let (topicReference, article) = value - guard let bundle = context.bundle, bundle.id == topicReference.bundleID else { return nil } + let bundle = context.bundle + guard bundle.id == topicReference.bundleID else { return nil } let renderer = DocumentationContentRenderer(documentationContext: context, bundle: bundle) let documentationNodeKind: DocumentationNode.Kind = isDocumentationExtensionContent ? .unknownSymbol : .article @@ -293,3 +294,11 @@ private extension SymbolGraph.LineList.Line { ) } } + +private extension DocumentationNode { + func meetsExpandedDocumentationRequirements(_ requirements: ConvertRequest.ExpandedDocumentationRequirements) -> Bool { + guard let symbol else { return false } + + return requirements.accessControlLevels.contains(symbol.accessLevel.rawValue) && (!symbol.names.title.starts(with: "_") || requirements.canBeUnderscored) + } +} diff --git a/Sources/SwiftDocC/DocumentationService/Convert/Symbol Link Resolution/AbsoluteSymbolLink.swift b/Sources/SwiftDocC/DocumentationService/Convert/Symbol Link Resolution/AbsoluteSymbolLink.swift deleted file mode 100644 index e4d962db07..0000000000 --- a/Sources/SwiftDocC/DocumentationService/Convert/Symbol Link Resolution/AbsoluteSymbolLink.swift +++ /dev/null @@ -1,343 +0,0 @@ -/* - 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 -import SymbolKit - -/// An absolute link to a symbol. -/// -/// You can use this model to validate a symbol link and access its different parts. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -public struct AbsoluteSymbolLink: CustomStringConvertible { - /// The identifier for the documentation bundle this link is from. - public let bundleID: String - - /// The name of the module that contains this symbol link. - /// - Note: This could be a link to the module itself. - public let module: String - - /// The top level symbol in this documentation link. - /// - /// If this symbol represents a module (see ``representsModule``), then - /// this is just the module and can be ignored. Otherwise, it's the top level symbol within - /// the module. - public let topLevelSymbol: LinkComponent - - /// The ordered path components, excluding the module and top level symbol. - public let basePathComponents: [LinkComponent] - - /// A Boolean value that is true if this is a link to a module. - public let representsModule: Bool - - /// Create a new documentation symbol link from a path. - /// - /// Expects an absolute symbol link structured like one of the following: - /// - doc://org.swift.docc.example/documentation/ModuleName - /// - doc://org.swift.docc.example/documentation/ModuleName/TypeName - /// - doc://org.swift.docc.example/documentation/ModuleName/ParentType/Test-swift.class/testFunc()-k2k9d - /// - doc://org.swift.docc.example/documentation/ModuleName/ClassName/functionName(firstParameter:secondParameter:) - public init?(string: String) { - // Begin by constructing a validated URL from the given string. - // Normally symbol links would be validated with `init(symbolPath:)` but since this is expected - // to be an absolute URL we parse it with `init(parsing:)` instead. - guard let validatedURL = ValidatedURL(parsingExact: string)?.requiring(scheme: ResolvedTopicReference.urlScheme) else { - return nil - } - - // All absolute documentation links include the bundle identifier as their host. - guard let bundleID = validatedURL.components.host, !bundleID.isEmpty else { - return nil - } - self.bundleID = bundleID - - var pathComponents = validatedURL.url.pathComponents - - // Swift's URL interprets the following link "doc://org.swift.docc.example/documentation/ModuleName" - // to have a sole "/" as its first path component. We'll just remove it if it's there. - if pathComponents.first == "/" { - pathComponents.removeFirst() - } - - // Swift-DocC requires absolute symbol links to be prepended with "documentation" - guard pathComponents.first == NodeURLGenerator.Path.documentationFolderName else { - return nil - } - - // Swift-DocC requires absolute symbol links to be prepended with "documentation" - // as their first path component but that's not actually part of the symbol's path - // so we drop it here. - pathComponents.removeFirst() - - // Now that we've cleaned up the link, confirm that it's non-empty - guard !pathComponents.isEmpty else { - return nil - } - - // Validate and construct the link component that represents the module - guard let moduleLinkComponent = LinkComponent(string: pathComponents.removeFirst()) else { - return nil - } - - // We don't allow modules to have disambiguation suffixes - guard !moduleLinkComponent.hasDisambiguationSuffix else { - return nil - } - self.module = moduleLinkComponent.name - - // Next we'll attempt to construct the link component for the top level symbol - // within the module. - if pathComponents.isEmpty { - // There are no more path components, so this is a link to the module itself - self.topLevelSymbol = moduleLinkComponent - self.representsModule = true - } else if let topLevelSymbol = LinkComponent(string: pathComponents.removeFirst()) { - // We were able to build a valid link component for the next path component - // so we'll set the top level symbol value and indicate that this is not a link - // to the module - self.topLevelSymbol = topLevelSymbol - self.representsModule = false - } else { - return nil - } - - // Finally we transform the remaining path components into link components - basePathComponents = pathComponents.compactMap { componentName in - LinkComponent(string: componentName) - } - - // If any of the path components were invalid, we want to mark the entire link as invalid - guard basePathComponents.count == pathComponents.count else { - return nil - } - } - - public var description: String { - """ - { - bundleID: \(bundleID.singleQuoted), - module: \(module.singleQuoted), - topLevelSymbol: \(topLevelSymbol), - representsModule: \(representsModule), - basePathComponents: [\(basePathComponents.map(\.description).joined(separator: ", "))] - } - """ - } -} - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension AbsoluteSymbolLink { - /// A component of a symbol link. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - public struct LinkComponent: CustomStringConvertible { - /// The name of the symbol represented by the link component. - public let name: String - - /// The suffix used to disambiguate this symbol from other symbol's - /// that share the same name. - public let disambiguationSuffix: DisambiguationSuffix - - var hasDisambiguationSuffix: Bool { - disambiguationSuffix != .none - } - - init(name: String, disambiguationSuffix: DisambiguationSuffix) { - self.name = name - self.disambiguationSuffix = disambiguationSuffix - } - - /// Creates an absolute symbol component from a raw string. - /// - /// For example, the input string can be `"foo-swift.var"`. - public init?(string: String) { - // Check to see if this component includes a disambiguation suffix - if string.contains("-") { - // Split the path component into its name and disambiguation suffix. - // It's important to limit to a single split since the disambiguation - // suffix itself could also contain a '-'. - var splitPathComponent = string.split(separator: "-", maxSplits: 1) - guard splitPathComponent.count == 2 else { - // This catches a trailing '-' in the suffix (which we don't allow) - // since a split would then result in a single path component. - return nil - } - - // Set the name from the first half of the split - name = String(splitPathComponent.removeFirst()) - - // The disambiguation suffix is formed from the second half - let disambiguationSuffixString = String(splitPathComponent.removeLast()) - - // Attempt to parse and validate a disambiguation suffix - guard let disambiguationSuffix = DisambiguationSuffix( - string: disambiguationSuffixString - ) else { - // Invalid disambiguation suffix, so we just return nil - return nil - } - - guard disambiguationSuffix != .none else { - // Since a "-" was included, we expect the disambiguation - // suffix to be non-nil. - return nil - } - - self.disambiguationSuffix = disambiguationSuffix - } else { - // The path component had no "-" so we just set the name - // as the entire path component - name = string - disambiguationSuffix = .none - } - } - - public var description: String { - """ - (name: \(name.singleQuoted), suffix: \(disambiguationSuffix)) - """ - } - - /// A string representation of this link component. - public var asLinkComponentString: String { - "\(name)\(disambiguationSuffix.asLinkSuffixString)" - } - } -} - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension AbsoluteSymbolLink.LinkComponent { - /// A suffix attached to a documentation link to disambiguate it from other symbols - /// that share the same base name. - public enum DisambiguationSuffix: Equatable, CustomStringConvertible { - /// The link is not disambiguated. - case none - - /// The symbol's kind. - case kindIdentifier(String) - - /// A hash of the symbol's precise identifier. - case preciseIdentifierHash(String) - - /// The symbol's kind and precise identifier. - /// - /// See ``kindIdentifier(_:)`` and ``preciseIdentifierHash(_:)`` for details. - case kindAndPreciseIdentifier( - kindIdentifier: String, preciseIdentifierHash: String - ) - - private static func isKnownSymbolKindIdentifier(identifier: String) -> Bool { - return SymbolGraph.Symbol.KindIdentifier.isKnownIdentifier(identifier) - } - - /// Creates a disambiguation suffix based on the given kind and precise - /// identifiers. - init(kindIdentifier: String?, preciseIdentifier: String?) { - if let kindIdentifier, let preciseIdentifier { - self = .kindAndPreciseIdentifier( - kindIdentifier: kindIdentifier, - preciseIdentifierHash: preciseIdentifier.stableHashString - ) - } else if let kindIdentifier { - self = .kindIdentifier(kindIdentifier) - } else if let preciseIdentifier { - self = .preciseIdentifierHash(preciseIdentifier.stableHashString) - } else { - self = .none - } - } - - /// Creates a symbol path component disambiguation suffix from the given string. - init?(string: String) { - guard !string.isEmpty else { - self = .none - return - } - - // We begin by splitting the given string in - // case this disambiguation suffix includes both an id hash - // and a kind identifier. - let splitSuffix = string.split(separator: "-") - - if splitSuffix.count == 1 && splitSuffix[0] == string { - // The string didn't contain a "-" so now we check - // to see if the hash is a known symbol kind identifier. - if Self.isKnownSymbolKindIdentifier(identifier: string) { - self = .kindIdentifier(string) - } else { - // Since we've confirmed that it's not a symbol kind identifier - // we now assume its a hash of the symbol's precise identifier. - self = .preciseIdentifierHash(string) - } - } else if splitSuffix.count == 2 { - // The string did contain a "-" so we now know the exact format - // it should be in. - // - // We expect the symbol kind identifier to come first, followed - // by a hash of the symbol's precise identifier. - if Self.isKnownSymbolKindIdentifier(identifier: String(splitSuffix[0])) { - self = .kindAndPreciseIdentifier( - kindIdentifier: String(splitSuffix[0]), - preciseIdentifierHash: String(splitSuffix[1]) - ) - } else { - // We were unable to validate the given symbol kind identifier - // so this is an invalid format for a disambiguation suffix. - return nil - } - } else { - // Unexpected number or configuration of "-" in the given string - // so we just return nil. - return nil - } - } - - public var description: String { - switch self { - case .none: - return "(none)" - case .kindIdentifier(let kindIdentifier): - return "(kind: \(kindIdentifier.singleQuoted))" - case .preciseIdentifierHash(let preciseIdentifierHash): - return "(idHash: \(preciseIdentifierHash.singleQuoted))" - case .kindAndPreciseIdentifier( - kindIdentifier: let kindIdentifier, - preciseIdentifierHash: let preciseIdentifierHash - ): - return "(kind: \(kindIdentifier.singleQuoted), idHash: \(preciseIdentifierHash.singleQuoted))" - } - } - - /// A string representation of the given disambiguation suffix. - /// - /// This value will include the preceding "-" character if necessary. - /// For example, if this is a ``kindAndPreciseIdentifier(kindIdentifier:preciseIdentifierHash:)`` value, - /// the following might be returned: - /// - /// ``` - /// -swift.var-h73kj - /// ``` - /// - /// However, if this is a ``none``, an empty string will be returned. - public var asLinkSuffixString: String { - switch self { - case .none: - return "" - case .kindIdentifier(let kindIdentifier): - return "-\(kindIdentifier)" - case .preciseIdentifierHash(let preciseIdentifierHash): - return "-\(preciseIdentifierHash)" - case .kindAndPreciseIdentifier( - kindIdentifier: let kindIdentifier, - preciseIdentifierHash: let preciseIdentifierHash - ): - return "-\(kindIdentifier)-\(preciseIdentifierHash)" - } - } - } -} diff --git a/Sources/SwiftDocC/DocumentationService/Convert/Symbol Link Resolution/DocCSymbolRepresentable.swift b/Sources/SwiftDocC/DocumentationService/Convert/Symbol Link Resolution/DocCSymbolRepresentable.swift deleted file mode 100644 index a31c77b110..0000000000 --- a/Sources/SwiftDocC/DocumentationService/Convert/Symbol Link Resolution/DocCSymbolRepresentable.swift +++ /dev/null @@ -1,232 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - 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 - See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -import Foundation -public import SymbolKit - -/// A type that can be converted to a DocC symbol. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -public protocol DocCSymbolRepresentable: Equatable { - /// A namespaced, unique identifier for the kind of symbol. - /// - /// For example, a Swift class might use `swift.class`. - var kindIdentifier: String? { get } - - /// A unique identifier for this symbol. - /// - /// For Swift, this is the USR. - var preciseIdentifier: String? { get } - - /// The case-sensitive title of this symbol as would be used in documentation. - /// - /// > Note: DocC embeds function parameter information directly in the title. - /// > For example: `functionName(parameterName:secondParameter)` - /// > or `functionName(_:firstNamedParameter)`. - var title: String { get } -} - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -public extension DocCSymbolRepresentable { - /// The given symbol information as a symbol link component. - /// - /// The component will include a disambiguation suffix - /// based on the included information in the symbol. For example, if the symbol - /// includes a kind identifier and a precise identifier, both - /// will be represented in the link component. - var asLinkComponent: AbsoluteSymbolLink.LinkComponent { - AbsoluteSymbolLink.LinkComponent( - name: title, - disambiguationSuffix: .init( - kindIdentifier: kindIdentifier, - preciseIdentifier: preciseIdentifier - ) - ) - } -} - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension AbsoluteSymbolLink.LinkComponent { - /// Given an array of symbols that are overloads for the symbol represented - /// by this link component, returns those that are precisely identified by the component. - /// - /// If the link is not specific enough to disambiguate between the given symbols, - /// this function will return an empty array. - public func disambiguateBetweenOverloadedSymbols( - _ overloadedSymbols: [SymbolType] - ) -> [SymbolType] { - // First confirm that we were given symbols to disambiguate - guard !overloadedSymbols.isEmpty else { - return [] - } - - // Pair each overloaded symbol with its required disambiguation - // suffix. This will tell us what kind of disambiguation suffix the - // link should have. - let overloadedSymbolsWithSuffixes = zip( - overloadedSymbols, overloadedSymbols.requiredDisambiguationSuffixes - ) - - // Next we filter the given symbols for those that are precise matches - // for the component. - let matchingSymbols = overloadedSymbolsWithSuffixes.filter { (symbol, _) in - // Filter the results by those that are fully represented by the element. - // This includes checking case sensitivity and disambiguation suffix. - // This _should_ always return a single element but we can't be entirely sure. - return fullyRepresentsSymbol(symbol) - } - - // We now check all the returned matching symbols to confirm that - // the current link has the correct disambiguation suffix - for (_, (shouldAddIdHash, shouldAddKind)) in matchingSymbols { - if shouldAddIdHash && shouldAddKind { - guard case .kindAndPreciseIdentifier = disambiguationSuffix else { - return [] - } - } else if shouldAddIdHash { - guard case .preciseIdentifierHash = disambiguationSuffix else { - return [] - } - } else if shouldAddKind { - guard case .kindIdentifier = disambiguationSuffix else { - return [] - } - } else { - guard case .none = disambiguationSuffix else { - return [] - } - } - } - - // Since we've validated that the link has the correct - // disambiguation suffix, we now return all matching symbols. - return matchingSymbols.map(\.0) - } - - /// Returns true if the given symbol is fully represented by the - /// symbol link. - /// - /// This means that the element has the same name (case-sensitive) - /// and, if the symbol link has a disambiguation suffix, the given element has the same - /// type or usr. - private func fullyRepresentsSymbol( - _ symbol: some DocCSymbolRepresentable - ) -> Bool { - guard name == symbol.title else { - return false - } - - switch self.disambiguationSuffix { - case .none: - return true - case .kindIdentifier(let kindIdentifier): - return symbol.kindIdentifier == kindIdentifier - case .preciseIdentifierHash(let preciseIdentifierHash): - return symbol.preciseIdentifier?.stableHashString == preciseIdentifierHash - case .kindAndPreciseIdentifier( - kindIdentifier: let kindIdentifier, - preciseIdentifierHash: let preciseIdentifierHash): - return symbol.preciseIdentifier?.stableHashString == preciseIdentifierHash - && symbol.kindIdentifier == kindIdentifier - } - } -} - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -public extension Collection where Element: DocCSymbolRepresentable { - /// Given a collection of colliding symbols, returns the disambiguation suffix required - /// for each symbol to disambiguate it from the others in the collection. - var requiredDisambiguationSuffixes: [(shouldAddIdHash: Bool, shouldAddKind: Bool)] { - guard let first else { - return [] - } - - guard count > 1 else { - // There are no path collisions - return Array(repeating: (shouldAddIdHash: false, shouldAddKind: false), count: count) - } - - if allSatisfy({ symbol in symbol.kindIdentifier == first.kindIdentifier }) { - // All collisions are the same symbol kind. - return Array(repeating: (shouldAddIdHash: true, shouldAddKind: false), count: count) - } else { - // Disambiguate by kind - return map { currentSymbol in - let kindCount = filter { $0.kindIdentifier == currentSymbol.kindIdentifier }.count - return ( - shouldAddIdHash: kindCount > 1, - shouldAddKind: kindCount == 1 - ) - } - } - } -} - -#if compiler(>=6) -// DocCSymbolRepresentable inherits from Equatable. If SymbolKit added Equatable conformance in the future, this could behave differently. -// It's reasonable to expect that symbols with the same unique ID would be equal but it's possible that SymbolKit's implementation would consider more symbol properties. -// -// In the long term we should try to phase out DocCSymbolRepresentable since it doesn't reflect how DocC resolves links or disambiguated symbols in links. -extension SymbolGraph.Symbol: @retroactive Equatable {} -extension UnifiedSymbolGraph.Symbol: @retroactive Equatable {} -#endif - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension SymbolGraph.Symbol: DocCSymbolRepresentable { - public var preciseIdentifier: String? { - self.identifier.precise - } - - public var title: String { - self.names.title - } - - public var kindIdentifier: String? { - "\(self.identifier.interfaceLanguage).\(self.kind.identifier.identifier)" - } - - public static func == (lhs: SymbolGraph.Symbol, rhs: SymbolGraph.Symbol) -> Bool { - lhs.identifier.precise == rhs.identifier.precise - } -} - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension UnifiedSymbolGraph.Symbol: DocCSymbolRepresentable { - public var preciseIdentifier: String? { - self.uniqueIdentifier - } - - public var title: String { - guard let selector = self.defaultSelector else { - fatalError(""" - Failed to find a supported default selector. \ - Language unsupported or corrupt symbol graph provided. - """ - ) - } - - return self.names[selector]!.title - } - - public var kindIdentifier: String? { - guard let selector = self.defaultSelector else { - fatalError(""" - Failed to find a supported default selector. \ - Language unsupported or corrupt symbol graph provided. - """ - ) - } - - return "\(selector.interfaceLanguage).\(self.kind[selector]!.identifier.identifier)" - } - - public static func == (lhs: UnifiedSymbolGraph.Symbol, rhs: UnifiedSymbolGraph.Symbol) -> Bool { - lhs.uniqueIdentifier == rhs.uniqueIdentifier - } -} diff --git a/Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex+Ext.swift b/Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex+Ext.swift deleted file mode 100644 index d3834d1e1e..0000000000 --- a/Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex+Ext.swift +++ /dev/null @@ -1,75 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - Copyright (c) 2021-2025 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. - */ -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.") -public class FileSystemRenderNodeProvider: RenderNodeProvider { - - /// The internal `FileSystemProvider` reference. - private let dataProvider: any 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: any 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 data = try Data(contentsOf: file.url) - 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 - } -} diff --git a/Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex.swift b/Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex.swift index 63a427ff87..8a6e452f48 100644 --- a/Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex.swift +++ b/Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -11,17 +11,6 @@ public import Foundation import Crypto -/// A protocol to provide data to be indexed. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.") -public protocol RenderNodeProvider { - /// Get an instance of `RenderNode` to be processed by the index. - /// - Note: Returning `nil` will end the indexing process. - func getRenderNode() -> RenderNode? - - /// Returns an array of `Problem` indicating which problems the `Provider` encountered. - func getProblems() -> [Problem] -} - /** A `NavigatorIndex` contains all the necessary information to display the data inside a navigator. The data ranges from the tree to the necessary pieces of information to filter the content and perform actions in a fast way. @@ -477,14 +466,6 @@ extension NavigatorIndex { */ open class Builder { - /// The data provider. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - public var renderNodeProvider: (any RenderNodeProvider)? { - _renderNodeProvider as! (any RenderNodeProvider)? - } - // This property only exist to be able to assign `nil` to `renderNodeProvider` in the new initializer without causing a deprecation warning. - private let _renderNodeProvider: Any? - /// The documentation archive to build an index from. public let archiveURL: URL? @@ -579,20 +560,6 @@ extension NavigatorIndex { /// - usePageTitle: Configure the builder to use the "page title" instead of the "navigator title" as the title for each entry. public init(archiveURL: URL? = nil, outputURL: URL, bundleIdentifier: String, sortRootChildrenByName: Bool = false, groupByLanguage: Bool = false, writePathsOnDisk: Bool = true, usePageTitle: Bool = false) { self.archiveURL = archiveURL - self._renderNodeProvider = nil - self.outputURL = outputURL - self.bundleIdentifier = bundleIdentifier - self.sortRootChildrenByName = sortRootChildrenByName - self.groupByLanguage = groupByLanguage - self.writePathsOnDisk = writePathsOnDisk - self.usePageTitle = usePageTitle - } - - @available(*, deprecated, renamed: "init(archiveURL:outputURL:bundleIdentifier:sortRootChildrenByName:groupByLanguage:writePathsOnDisk:usePageTitle:)", message: "Use 'init(archiveURL:outputURL:bundleIdentifier:sortRootChildrenByName:groupByLanguage:writePathsOnDisk:usePageTitle:)' instead. This deprecated API will be removed after 6.2 is released") - @_disfavoredOverload - public init(renderNodeProvider: (any RenderNodeProvider)? = nil, outputURL: URL, bundleIdentifier: String, sortRootChildrenByName: Bool = false, groupByLanguage: Bool = false, writePathsOnDisk: Bool = true, usePageTitle: Bool = false) { - self._renderNodeProvider = renderNodeProvider - self.archiveURL = nil self.outputURL = outputURL self.bundleIdentifier = bundleIdentifier self.sortRootChildrenByName = sortRootChildrenByName @@ -1292,17 +1259,12 @@ extension NavigatorIndex { /// Build the index using the render nodes files in the provided documentation archive. /// - Returns: A list containing all the errors encountered during indexing. - /// - Precondition: Either ``archiveURL`` or ``renderNodeProvider`` is set. + /// - Precondition: ``archiveURL`` is set. public func build() -> [Problem] { - if let archiveURL { - return _build(archiveURL: archiveURL) - } else { - return (self as (any _DeprecatedRenderNodeProviderAccess))._legacyBuild() + guard let archiveURL else { + fatalError("Calling `build()` requires that `archiveURL` is set.") } - } - - // After 6.2 is released, move this into `build()`. - private func _build(archiveURL: URL) -> [Problem] { + setup() let dataDirectory = archiveURL.appendingPathComponent(NodeURLGenerator.Path.dataFolderName, isDirectory: true) @@ -1323,27 +1285,6 @@ extension NavigatorIndex { return problems } - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - fileprivate func _legacyBuild() -> [Problem] { - precondition(renderNodeProvider != nil, "Calling `build()` without an `archiveURL` or `renderNodeProvider` set is not permitted.") - - setup() - - while let renderNode = renderNodeProvider!.getRenderNode() { - do { - try index(renderNode: renderNode) - } catch { - problems.append(error.problem(source: renderNode.identifier.url, - severity: .warning, - summaryPrefix: "RenderNode indexing process failed")) - } - } - - finalize() - - return problems - } - func availabilityEntryIDs(for availabilityID: UInt64) -> [Int]? { return availabilityIDs[Int(availabilityID)] } @@ -1416,9 +1357,3 @@ enum PathHasher: String { } } } - -private protocol _DeprecatedRenderNodeProviderAccess { - // This private function accesses the deprecated RenderNodeProvider - func _legacyBuild() -> [Problem] -} -extension NavigatorIndex.Builder: _DeprecatedRenderNodeProviderAccess {} diff --git a/Sources/SwiftDocC/Infrastructure/Bundle Assets/DataAssetManager.swift b/Sources/SwiftDocC/Infrastructure/Bundle Assets/DataAssetManager.swift index ac932b1a36..c36f2542dc 100644 --- a/Sources/SwiftDocC/Infrastructure/Bundle Assets/DataAssetManager.swift +++ b/Sources/SwiftDocC/Infrastructure/Bundle Assets/DataAssetManager.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -346,10 +346,6 @@ fileprivate extension NSRegularExpression { public struct AssetReference: Hashable, Codable { /// The name of the asset. public var assetName: String - @available(*, deprecated, renamed: "bundleID", message: "Use 'bundleID' instead. This deprecated API will be removed after 6.2 is released") - public var bundleIdentifier: String { - bundleID.rawValue - } /// The identifier of the bundle the asset is apart of. public let bundleID: DocumentationBundle.Identifier @@ -359,11 +355,4 @@ public struct AssetReference: Hashable, Codable { self.assetName = assetName self.bundleID = bundleID } - @available(*, deprecated, renamed: "init(assetName:bundleID:)", message: "Use 'init(assetName:bundleID:)' instead. This deprecated API will be removed after 6.2 is released") - public init(assetName: String, bundleIdentifier: String) { - self.init( - assetName: assetName, - bundleID: .init(rawValue: bundleIdentifier) - ) - } } diff --git a/Sources/SwiftDocC/Infrastructure/Context/Deprecated/DocumentationContext+Deprecated.swift b/Sources/SwiftDocC/Infrastructure/Context/Deprecated/DocumentationContext+Deprecated.swift deleted file mode 100644 index 97685f8a19..0000000000 --- a/Sources/SwiftDocC/Infrastructure/Context/Deprecated/DocumentationContext+Deprecated.swift +++ /dev/null @@ -1,47 +0,0 @@ -/* - 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 { - - @available(*, deprecated, renamed: "configuration.externalMetadata", message: "Use 'configuration.externalMetadata' instead. This deprecated API will be removed after Swift 6.2 is released.") - public var externalMetadata: ExternalMetadata { - get { configuration.externalMetadata } - set { configuration.externalMetadata = newValue } - } - - @available(*, deprecated, renamed: "configuration.externalDocumentationConfiguration.sources", message: "Use 'configuration.externalDocumentationConfiguration.sources' instead. This deprecated API will be removed after Swift 6.2 is released.") - public var externalDocumentationSources: [BundleIdentifier: any ExternalDocumentationSource] { - get { - var result = [BundleIdentifier: any ExternalDocumentationSource]() - for (key, value) in configuration.externalDocumentationConfiguration.sources { - result[key.rawValue] = value - } - return result - } - set { - configuration.externalDocumentationConfiguration.sources.removeAll() - for (key, value) in newValue { - configuration.externalDocumentationConfiguration.sources[.init(rawValue: key)] = value - } - } - } - - @available(*, deprecated, renamed: "configuration.externalDocumentationConfiguration.globalSymbolResolver", message: "Use 'configuration.externalDocumentationConfiguration.globalSymbolResolver' instead. This deprecated API will be removed after Swift 6.2 is released.") - public var globalExternalSymbolResolver: (any GlobalExternalSymbolResolver)? { - get { configuration.externalDocumentationConfiguration.globalSymbolResolver } - set { configuration.externalDocumentationConfiguration.globalSymbolResolver = newValue } - } - - @available(*, deprecated, renamed: "configuration.experimentalCoverageConfiguration.shouldStoreManuallyCuratedReferences", message: "Use 'configuration.experimentalCoverageConfiguration.shouldStoreManuallyCuratedReferences' instead. This deprecated API will be removed after Swift 6.2 is released.") - public var shouldStoreManuallyCuratedReferences: Bool { - get { configuration.experimentalCoverageConfiguration.shouldStoreManuallyCuratedReferences } - set { configuration.experimentalCoverageConfiguration.shouldStoreManuallyCuratedReferences = newValue } - } -} diff --git a/Sources/SwiftDocC/Infrastructure/ConvertOutputConsumer.swift b/Sources/SwiftDocC/Infrastructure/ConvertOutputConsumer.swift index 830404dda6..f5e1ebd432 100644 --- a/Sources/SwiftDocC/Infrastructure/ConvertOutputConsumer.swift +++ b/Sources/SwiftDocC/Infrastructure/ConvertOutputConsumer.swift @@ -16,8 +16,8 @@ import Foundation /// or store them in memory. public protocol ConvertOutputConsumer { /// Consumes an array of problems that were generated during a conversion. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - func consume(problems: [Problem]) throws + @available(*, deprecated, message: "This deprecated API will be removed after 6.3 is released") + func _deprecated_consume(problems: [Problem]) throws /// Consumes a render node that was generated during a conversion. /// > Warning: This method might be called concurrently. @@ -62,7 +62,7 @@ public extension ConvertOutputConsumer { // Default implementation so that conforming types don't need to implement deprecated API. public extension ConvertOutputConsumer { - func consume(problems: [Problem]) throws {} + func _deprecated_consume(problems: [Problem]) throws {} } // A package-internal protocol that callers can cast to when they need to call `_consume(problems:)` for backwards compatibility (until `consume(problems:)` is removed). @@ -84,7 +84,7 @@ package struct _Deprecated: _DeprecatedConsumeP } // This needs to be deprecated to be able to call `consume(problems:)` without a deprecation warning. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") + @available(*, deprecated, message: "This deprecated API will be removed after 6.3 is released") package func _consume(problems: [Problem]) throws { var problems = problems @@ -94,7 +94,7 @@ package struct _Deprecated: _DeprecatedConsumeP severity: .warning, identifier: "org.swift.docc.DeprecatedDiagnosticsDigets", summary: """ - The 'diagnostics.json' digest file is deprecated and will be removed after 6.2 is released. \ + The 'diagnostics.json' digest file is deprecated and will be removed after 6.3 is released. \ Pass a `--diagnostics-file ` to specify a custom location where DocC will write a diagnostics JSON file with more information. """) ), @@ -102,7 +102,7 @@ package struct _Deprecated: _DeprecatedConsumeP ) } - try consumer.consume(problems: problems) + try consumer._deprecated_consume(problems: problems) } } diff --git a/Sources/SwiftDocC/Infrastructure/DocumentationBundle.swift b/Sources/SwiftDocC/Infrastructure/DocumentationBundle.swift index 353559d890..11cc3ef426 100644 --- a/Sources/SwiftDocC/Infrastructure/DocumentationBundle.swift +++ b/Sources/SwiftDocC/Infrastructure/DocumentationBundle.swift @@ -57,25 +57,10 @@ public struct DocumentationBundle { info.displayName } - @available(*, deprecated, renamed: "id", message: "Use 'id' instead. This deprecated API will be removed after 6.2 is released") - public var identifier: String { - id.rawValue - } - /// The documentation bundle's stable and locally unique identifier. public var id: DocumentationBundle.Identifier { info.id } - - /** - The documentation bundle's version. - - It's not safe to make computations based on assumptions about the format of bundle's version. The version can be in any format. - */ - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - public var version: String? { - info.version - } /// Symbol graph JSON input files for the module that's represented by this unit of documentation. /// @@ -152,19 +137,9 @@ public struct DocumentationBundle { /// Default path to resolve symbol links. public private(set) var documentationRootReference: ResolvedTopicReference - @available(*, deprecated, renamed: "tutorialTableOfContentsContainer", message: "Use 'tutorialTableOfContentsContainer' instead. This deprecated API will be removed after 6.2 is released") - public var tutorialsRootReference: ResolvedTopicReference { - tutorialTableOfContentsContainer - } - /// Default path to resolve tutorial table-of-contents links. public var tutorialTableOfContentsContainer: ResolvedTopicReference - @available(*, deprecated, renamed: "tutorialsContainerReference", message: "Use 'tutorialsContainerReference' instead. This deprecated API will be removed after 6.2 is released") - public var technologyTutorialsRootReference: ResolvedTopicReference { - tutorialsContainerReference - } - /// Default path to resolve tutorial links. public var tutorialsContainerReference: ResolvedTopicReference diff --git a/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift b/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift index 83a9aabba4..cd7fca83d7 100644 --- a/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift +++ b/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift @@ -12,56 +12,6 @@ public import Foundation import Markdown import SymbolKit -/// A type that provides information about documentation bundles and their content. -@available(*, deprecated, message: "Pass the context its inputs at initialization instead. This deprecated API will be removed after 6.2 is released") -public protocol DocumentationContextDataProvider { - /// An object to notify when bundles are added or removed. - var delegate: (any DocumentationContextDataProviderDelegate)? { get set } - - /// The documentation bundles that this data provider provides. - var bundles: [BundleIdentifier: DocumentationBundle] { get } - - /// Returns the data for the specified `url` in the provided `bundle`. - /// - /// - Parameters: - /// - url: The URL of the file to read. - /// - bundle: The bundle that the file is a part of. - /// - /// - Throws: When the file cannot be found in the workspace. - func contentsOfURL(_ url: URL, in bundle: DocumentationBundle) throws -> Data -} - -/// An object that responds to changes in available documentation bundles for a specific provider. -@available(*, deprecated, message: "Pass the context its inputs at initialization instead. This deprecated API will be removed after 6.2 is released") -public protocol DocumentationContextDataProviderDelegate: AnyObject { - - /// Called when the `dataProvider` has added a new documentation bundle to its list of `bundles`. - /// - /// - Parameters: - /// - dataProvider: The provider that added this bundle. - /// - bundle: The bundle that was added. - /// - /// - Note: This method is called after the `dataProvider` has been added the bundle to its `bundles` property. - func dataProvider(_ dataProvider: any DocumentationContextDataProvider, didAddBundle bundle: DocumentationBundle) throws - - /// Called when the `dataProvider` has removed a documentation bundle from its list of `bundles`. - /// - /// - Parameters: - /// - dataProvider: The provider that removed this bundle. - /// - bundle: The bundle that was removed. - /// - /// - Note: This method is called after the `dataProvider` has been removed the bundle from its `bundles` property. - func dataProvider(_ dataProvider: any DocumentationContextDataProvider, didRemoveBundle bundle: DocumentationBundle) throws -} - -/// Documentation bundles use a string value as a unique identifier. -/// -/// This value is typically a reverse host name, for example: `com..`. -/// -/// Documentation links may include the bundle identifier---as a host component of the URL---to reference content in a specific documentation bundle. -@available(*, deprecated, renamed: "DocumentationBundle.Identifier", message: "Use 'DocumentationBundle.Identifier' instead. This deprecated API will be removed after 6.2 is released") -public typealias BundleIdentifier = String - /// The documentation context manages the in-memory model for the built documentation. /// /// A ``DocumentationWorkspace`` discovers serialized documentation bundles from a variety of sources (files on disk, databases, or web services), provides them to the `DocumentationContext`, @@ -116,51 +66,15 @@ public class DocumentationContext { /// A class that resolves documentation links by orchestrating calls to other link resolver implementations. public var linkResolver: LinkResolver - - private enum _Provider { - @available(*, deprecated, message: "Use 'DataProvider' instead. This deprecated API will be removed after 6.2 is released") - case legacy(any DocumentationContextDataProvider) - case new(any DataProvider) - } - private var dataProvider: _Provider - /// The provider of documentation bundles for this context. - @available(*, deprecated, message: "Use 'DataProvider' instead. This deprecated API will be removed after 6.2 is released") - private var _legacyDataProvider: (any DocumentationContextDataProvider)! { - get { - switch dataProvider { - case .legacy(let legacyDataProvider): - legacyDataProvider - case .new: - nil - } - } - set { - dataProvider = .legacy(newValue) - } - } - - func contentsOfURL(_ url: URL, in bundle: DocumentationBundle) throws -> Data { - switch dataProvider { - case .legacy(let legacyDataProvider): - return try legacyDataProvider.contentsOfURL(url, in: bundle) - case .new(let dataProvider): - assert(self.bundle?.id == bundle.id, "New code shouldn't pass unknown bundle identifiers to 'DocumentationContext.bundle(identifier:)'.") - return try dataProvider.contents(of: url) - } - } + /// The data provider that the context can use to read the contents of files that belong to ``bundle``. + let dataProvider: any DataProvider /// The documentation bundle that is registered with the context. - var bundle: DocumentationBundle? + let bundle: DocumentationBundle /// A collection of configuration for this context. - public package(set) var configuration: Configuration { - get { _configuration } - @available(*, deprecated, message: "Pass a configuration at initialization. This property will become read-only after Swift 6.2 is released.") - set { _configuration = newValue } - } - // Having a deprecated setter above requires a computed property. - private var _configuration: Configuration + public let configuration: Configuration /// The graph of all the documentation content and their relationships to each other. /// @@ -173,11 +87,6 @@ public class DocumentationContext { /// The set of all manually curated references if `shouldStoreManuallyCuratedReferences` was true at the time of processing and has remained `true` since.. Nil if curation has not been processed yet. public private(set) var manuallyCuratedReferences: Set? - @available(*, deprecated, renamed: "tutorialTableOfContentsReferences", message: "Use 'tutorialTableOfContentsReferences' This deprecated API will be removed after 6.2 is released") - public var rootTechnologies: [ResolvedTopicReference] { - tutorialTableOfContentsReferences - } - /// The tutorial table-of-contents nodes in the topic graph. public var tutorialTableOfContentsReferences: [ResolvedTopicReference] { return topicGraph.nodes.values.compactMap { node in @@ -285,31 +194,6 @@ public class DocumentationContext { /// Mentions of symbols within articles. var articleSymbolMentions = ArticleSymbolMentions() - /// Initializes a documentation context with a given `dataProvider` and registers all the documentation bundles that it provides. - /// - /// - Parameters: - /// - dataProvider: The data provider to register bundles from. - /// - diagnosticEngine: The pre-configured engine that will collect problems encountered during compilation. - /// - configuration: A collection of configuration for the created context. - /// - Throws: If an error is encountered while registering a documentation bundle. - @available(*, deprecated, message: "Pass the context its inputs at initialization instead. This deprecated API will be removed after 6.2 is released") - public init( - dataProvider: any DocumentationContextDataProvider, - diagnosticEngine: DiagnosticEngine = .init(), - configuration: Configuration = .init() - ) throws { - self.dataProvider = .legacy(dataProvider) - self.diagnosticEngine = diagnosticEngine - self._configuration = configuration - self.linkResolver = LinkResolver(dataProvider: FileManager.default) - - _legacyDataProvider.delegate = self - - for bundle in dataProvider.bundles.values { - try register(bundle) - } - } - /// Initializes a documentation context with a given `bundle`. /// /// - Parameters: @@ -325,77 +209,26 @@ public class DocumentationContext { configuration: Configuration = .init() ) async throws { self.bundle = bundle - self.dataProvider = .new(dataProvider) + self.dataProvider = dataProvider self.diagnosticEngine = diagnosticEngine - self._configuration = configuration + self.configuration = configuration self.linkResolver = LinkResolver(dataProvider: dataProvider) ResolvedTopicReference.enableReferenceCaching(for: bundle.id) try register(bundle) } - - /// Respond to a new `bundle` being added to the `dataProvider` by registering it. - /// - /// - Parameters: - /// - dataProvider: The provider that added this bundle. - /// - bundle: The bundle that was added. - @available(*, deprecated, message: "Pass the context its inputs at initialization instead. This deprecated API will be removed after 6.2 is released") - public func dataProvider(_ dataProvider: any DocumentationContextDataProvider, didAddBundle bundle: DocumentationBundle) throws { - try benchmark(wrap: Benchmark.Duration(id: "bundle-registration")) { - // Enable reference caching for this documentation bundle. - ResolvedTopicReference.enableReferenceCaching(for: bundle.id) - - try self.register(bundle) - } - } - - /// Respond to a new `bundle` being removed from the `dataProvider` by unregistering it. - /// - /// - Parameters: - /// - dataProvider: The provider that removed this bundle. - /// - bundle: The bundle that was removed. - @available(*, deprecated, message: "Pass the context its inputs at initialization instead. This deprecated API will be removed after 6.2 is released") - public func dataProvider(_ dataProvider: any DocumentationContextDataProvider, didRemoveBundle bundle: DocumentationBundle) throws { - linkResolver.localResolver?.unregisterBundle(identifier: bundle.id) - - // Purge the reference cache for this bundle and disable reference caching for - // this bundle moving forward. - ResolvedTopicReference.purgePool(for: bundle.id) - - unregister(bundle) - } - - /// The documentation bundles that are currently registered with the context. - @available(*, deprecated, message: "Use 'bundle' instead. This deprecated API will be removed after 6.2 is released") - public var registeredBundles: some Collection { - _registeredBundles - } - - /// Returns the `DocumentationBundle` with the given `identifier` if it's registered with the context, otherwise `nil`. - @available(*, deprecated, message: "Use 'bundle' instead. This deprecated API will be removed after 6.2 is released") - public func bundle(identifier: String) -> DocumentationBundle? { - _bundle(identifier: identifier) - } // Remove these when removing `registeredBundles` and `bundle(identifier:)`. // These exist so that internal code that need to be compatible with legacy data providers can access the bundles without deprecation warnings. + @available(*, deprecated, renamed: "bundle", message: "REMOVE THIS") var _registeredBundles: [DocumentationBundle] { - switch dataProvider { - case .legacy(let legacyDataProvider): - Array(legacyDataProvider.bundles.values) - case .new: - bundle.map { [$0] } ?? [] - } + [bundle] } + @available(*, deprecated, renamed: "bundle", message: "REMOVE THIS") func _bundle(identifier: String) -> DocumentationBundle? { - switch dataProvider { - case .legacy(let legacyDataProvider): - return legacyDataProvider.bundles[identifier] - case .new: - assert(bundle?.id.rawValue == identifier, "New code shouldn't pass unknown bundle identifiers to 'DocumentationContext.bundle(identifier:)'.") - return bundle?.id.rawValue == identifier ? bundle : nil - } + assert(bundle.id.rawValue == identifier, "New code shouldn't pass unknown bundle identifiers to 'DocumentationContext.bundle(identifier:)'.") + return bundle.id.rawValue == identifier ? bundle : nil } /// Perform semantic analysis on a given `document` at a given `source` location and append any problems found to `problems`. @@ -900,7 +733,7 @@ public class DocumentationContext { guard decodeError.sync({ $0 == nil }) else { return } do { - let data = try contentsOfURL(url, in: bundle) + let data = try dataProvider.contents(of: url) let source = String(decoding: data, as: UTF8.self) let document = Document(parsing: source, source: url, options: [.parseBlockDirectives, .parseSymbolLinks]) @@ -1468,9 +1301,6 @@ public class DocumentationContext { private func shouldContinueRegistration() throws { try Task.checkCancellation() - guard isRegistrationEnabled.sync({ $0 }) else { - throw ContextError.registrationDisabled - } } /// Builds in-memory relationships between symbols based on the relationship information in a given symbol graph file. @@ -1847,11 +1677,6 @@ public class DocumentationContext { registeredAssets(withExtensions: DocumentationContext.supportedImageExtensions, forBundleID: bundleID) } - @available(*, deprecated, renamed: "registeredImageAssets(for:)", message: "registeredImageAssets(for:)' instead. This deprecated API will be removed after 6.2 is released") - public func registeredImageAssets(forBundleID bundleIdentifier: BundleIdentifier) -> [DataAsset] { - registeredImageAssets(for: DocumentationBundle.Identifier(rawValue: bundleIdentifier)) - } - /// Returns a list of all the video assets that registered for a given `bundleIdentifier`. /// /// - Parameter bundleID: The identifier of the bundle to return video assets for. @@ -1860,11 +1685,6 @@ public class DocumentationContext { registeredAssets(withExtensions: DocumentationContext.supportedVideoExtensions, forBundleID: bundleID) } - @available(*, deprecated, renamed: "registeredVideoAssets(for:)", message: "registeredImageAssets(for:)' instead. This deprecated API will be removed after 6.2 is released") - public func registeredVideoAssets(forBundleID bundleIdentifier: BundleIdentifier) -> [DataAsset] { - registeredVideoAssets(for: DocumentationBundle.Identifier(rawValue: bundleIdentifier)) - } - /// Returns a list of all the download assets that registered for a given `bundleIdentifier`. /// /// - Parameter bundleID: The identifier of the bundle to return download assets for. @@ -1873,11 +1693,6 @@ public class DocumentationContext { registeredAssets(inContexts: [DataAsset.Context.download], forBundleID: bundleID) } - @available(*, deprecated, renamed: "registeredDownloadsAssets(for:)", message: "registeredDownloadsAssets(for:)' instead. This deprecated API will be removed after 6.2 is released") - public func registeredDownloadsAssets(forBundleID bundleIdentifier: BundleIdentifier) -> [DataAsset] { - registeredDownloadsAssets(for: DocumentationBundle.Identifier(rawValue: bundleIdentifier)) - } - typealias Articles = [DocumentationContext.SemanticResult
] private typealias ArticlesTuple = (articles: Articles, rootPageArticles: Articles) @@ -1915,18 +1730,6 @@ public class DocumentationContext { } } - /// When `true` bundle registration will be cancelled asap. - private var isRegistrationEnabled = Synchronized(true) - - /// Enables or disables bundle registration. - /// - /// When given `false` the context will try to cancel as quick as possible - /// any ongoing bundle registrations. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - public func setRegistrationEnabled(_ value: Bool) { - isRegistrationEnabled.sync({ $0 = value }) - } - /// Adds articles that are not root pages to the documentation cache. /// /// This method adds all of the `articles` to the documentation cache and inserts a node representing @@ -2220,7 +2023,7 @@ public class DocumentationContext { discoveryGroup.async(queue: discoveryQueue) { [unowned self] in symbolGraphLoader = SymbolGraphLoader( bundle: bundle, - dataLoader: { try self.contentsOfURL($0, in: $1) }, + dataProvider: dataProvider, symbolGraphTransformer: configuration.convertServiceConfiguration.symbolGraphTransformer ) @@ -2654,14 +2457,6 @@ public class DocumentationContext { /// A closure type getting the information about a reference in a context and returns any possible problems with it. public typealias ReferenceCheck = (DocumentationContext, ResolvedTopicReference) -> [Problem] - /// Adds new checks to be run during the global topic analysis; after a bundle has been fully registered and its topic graph has been fully built. - /// - /// - Parameter newChecks: The new checks to add. - @available(*, deprecated, message: "Use 'TopicAnalysisConfiguration.additionalChecks' instead. This deprecated API will be removed after 6.2 is released") - public func addGlobalChecks(_ newChecks: [ReferenceCheck]) { - configuration.topicAnalysisConfiguration.additionalChecks.append(contentsOf: newChecks) - } - /// Crawls the hierarchy of the given list of nodes, adding relationships in the topic graph for all resolvable task group references. /// - Parameters: /// - references: A list of references to crawl. @@ -2871,15 +2666,13 @@ public class DocumentationContext { - Throws: ``ContextError/notFound(_:)` if a resource with the given was not found. */ public func resource(with identifier: ResourceReference, trait: DataTraitCollection = .init()) throws -> Data { - guard let bundle, - let assetManager = assetManagers[identifier.bundleID], - let asset = assetManager.allData(named: identifier.path) else { + guard let asset = assetManagers[identifier.bundleID]?.allData(named: identifier.path) else { throw ContextError.notFound(identifier.url) } let resource = asset.data(bestMatching: trait) - return try contentsOfURL(resource.url, in: bundle) + return try dataProvider.contents(of: resource.url) } /// Returns true if a resource with the given identifier exists in the registered bundle. @@ -3349,6 +3142,3 @@ extension DataAsset { } } } - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension DocumentationContext: DocumentationContextDataProviderDelegate {} diff --git a/Sources/SwiftDocC/Infrastructure/DocumentationConverter.swift b/Sources/SwiftDocC/Infrastructure/DocumentationConverter.swift deleted file mode 100644 index 9b0cd3a86e..0000000000 --- a/Sources/SwiftDocC/Infrastructure/DocumentationConverter.swift +++ /dev/null @@ -1,486 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - Copyright (c) 2021-2025 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 -*/ - -public import Foundation - -/// A converter from a documentation bundle to an output that can be consumed by a renderer. -/// -/// This protocol is primarily used for injecting mock documentation converters during testing. -/// -/// ## See Also -/// -/// - ``DocumentationConverter`` -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -public protocol DocumentationConverterProtocol { - /// Converts documentation, outputting products using the given output consumer. - /// - Parameter outputConsumer: The output consumer for content produced during conversion. - /// - Returns: The problems emitted during analysis of the documentation bundle and during conversion. - /// - Throws: Throws an error if the conversion process was not able to start at all, for example if the bundle could not be read. - /// Partial failures, such as failing to consume a single render node, are returned in the `conversionProblems` component - /// of the returned tuple. - mutating func convert( - outputConsumer: some ConvertOutputConsumer - ) throws -> (analysisProblems: [Problem], conversionProblems: [Problem]) -} - -/// A converter from a documentation bundle to an output that can be consumed by a renderer. -/// -/// A documentation converter analyzes a documentation bundle and converts it to products that can be used by a documentation -/// renderer to render documentation. The output format of the conversion is controlled by a ``ConvertOutputConsumer``, which -/// determines what to do with the conversion products, for example, write them to disk. -/// -/// You can also configure the documentation converter to emit extra metadata such as linkable entities and indexing records -/// information. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -public struct DocumentationConverter: DocumentationConverterProtocol { - let rootURL: URL? - let emitDigest: Bool - let documentationCoverageOptions: DocumentationCoverageOptions - let bundleDiscoveryOptions: BundleDiscoveryOptions - let diagnosticEngine: DiagnosticEngine - - private(set) var context: DocumentationContext - private let workspace: DocumentationWorkspace - private var currentDataProvider: (any DocumentationWorkspaceDataProvider)? - private var dataProvider: any DocumentationWorkspaceDataProvider - - /// An optional closure that sets up a context before the conversion begins. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - public var setupContext: ((inout DocumentationContext) -> Void)? - - /// Conversion batches should be big enough to keep all cores busy but small enough not to keep - /// around too many async blocks that update the conversion results. After running some tests it - /// seems that more than couple hundred of a batch size doesn't bring more performance CPU-wise - /// and it's a fair amount of async tasks to keep in memory before draining the results queue - /// after the batch is converted. - var batchNodeCount = 1 - - /// The external IDs of the symbols to convert. - /// - /// Use this property to indicate what symbol documentation nodes should be converted. When ``externalIDsToConvert`` - /// and ``documentationPathsToConvert`` are both set, the documentation nodes that are in either arrays will be - /// converted. - /// - /// If you want all the symbol render nodes to be returned as part of the conversion's response, set this property to `nil`. - /// For Swift, the external ID of the symbol is its USR. - var externalIDsToConvert: [String]? - - /// The paths of the documentation nodes to convert. - /// - /// Use this property to indicate what documentation nodes should be converted. When ``externalIDsToConvert`` - /// and ``documentationPathsToConvert`` are both set, the documentation nodes that are in either arrays will be - /// converted. - /// - /// If you want all the render nodes to be returned as part of the conversion's response, set this property to `nil`. - var documentPathsToConvert: [String]? - - /// Whether the documentation converter should include source file - /// location metadata in any render nodes representing symbols it creates. - /// - /// Before setting this value to `true` please confirm that your use case doesn't include - /// public distribution of any created render nodes as there are filesystem privacy and security - /// concerns with distributing this data. - var shouldEmitSymbolSourceFileURIs: Bool - - /// Whether the documentation converter should include access level information for symbols. - var shouldEmitSymbolAccessLevels: Bool - - /// The source repository where the documentation's sources are hosted. - var sourceRepository: SourceRepository? - - /// Whether the documentation converter should write documentation extension files containing markdown representations of DocC's automatic curation into the source documentation catalog. - var experimentalModifyCatalogWithGeneratedCuration: Bool - - /// The identifiers and access level requirements for symbols that have an expanded version of their documentation page if the requirements are met - var symbolIdentifiersWithExpandedDocumentation: [String: ConvertRequest.ExpandedDocumentationRequirements]? = nil - - /// `true` if the conversion is cancelled. - private var isCancelled: Synchronized? = nil - - private var processingDurationMetric: Benchmark.Duration? - - /// Creates a documentation converter given a documentation bundle's URL. - /// - /// - Parameters: - /// - documentationBundleURL: The root URL of the documentation bundle to convert. - /// - emitDigest: Whether the conversion should create metadata files, such as linkable entities information. - /// - documentationCoverageOptions: What level of documentation coverage output should be emitted. - /// - currentPlatforms: The current version and beta information for platforms that may be encountered while processing symbol graph files. - /// - workspace: A provided documentation workspace. Creates a new empty workspace if value is `nil`. - /// - context: A provided documentation context. - /// - dataProvider: A data provider to use when registering bundles. - /// - externalIDsToConvert: The external IDs of the documentation nodes to convert. - /// - documentPathsToConvert: The paths of the documentation nodes to convert. - /// - bundleDiscoveryOptions: Options to configure how the converter discovers documentation bundles. - /// - emitSymbolSourceFileURIs: Whether the documentation converter should include - /// source file location metadata in any render nodes representing symbols it creates. - /// - /// Before passing `true` please confirm that your use case doesn't include public - /// distribution of any created render nodes as there are filesystem privacy and security - /// concerns with distributing this data. - /// - emitSymbolAccessLevels: Whether the documentation converter should include access level information for symbols. - /// - sourceRepository: The source repository where the documentation's sources are hosted. - /// - isCancelled: A wrapped boolean value used for the caller to cancel converting the documentation. - /// that have an expanded version of their documentation page if the access level requirement is met. - /// - diagnosticEngine: The diagnostic engine that collects any problems encountered from converting the documentation. - /// - symbolIdentifiersWithExpandedDocumentation: Identifiers and access level requirements for symbols - /// - experimentalModifyCatalogWithGeneratedCuration: Whether the documentation converter should write documentation extension files containing markdown representations of DocC's automatic curation into the source documentation catalog. - public init( - documentationBundleURL: URL?, - emitDigest: Bool, - documentationCoverageOptions: DocumentationCoverageOptions, - currentPlatforms: [String : PlatformVersion]?, - workspace: DocumentationWorkspace, - context: DocumentationContext, - dataProvider: any DocumentationWorkspaceDataProvider, - externalIDsToConvert: [String]? = nil, - documentPathsToConvert: [String]? = nil, - bundleDiscoveryOptions: BundleDiscoveryOptions, - emitSymbolSourceFileURIs: Bool = false, - emitSymbolAccessLevels: Bool = false, - sourceRepository: SourceRepository? = nil, - isCancelled: Synchronized? = nil, - diagnosticEngine: DiagnosticEngine = .init(), - symbolIdentifiersWithExpandedDocumentation: [String: ConvertRequest.ExpandedDocumentationRequirements]? = nil, - experimentalModifyCatalogWithGeneratedCuration: Bool = false - ) { - self.rootURL = documentationBundleURL - self.emitDigest = emitDigest - self.documentationCoverageOptions = documentationCoverageOptions - self.workspace = workspace - self.context = context - self.dataProvider = dataProvider - self.externalIDsToConvert = externalIDsToConvert - self.documentPathsToConvert = documentPathsToConvert - self.bundleDiscoveryOptions = bundleDiscoveryOptions - self.shouldEmitSymbolSourceFileURIs = emitSymbolSourceFileURIs - self.shouldEmitSymbolAccessLevels = emitSymbolAccessLevels - self.sourceRepository = sourceRepository - self.isCancelled = isCancelled - self.diagnosticEngine = diagnosticEngine - self.symbolIdentifiersWithExpandedDocumentation = symbolIdentifiersWithExpandedDocumentation - self.experimentalModifyCatalogWithGeneratedCuration = experimentalModifyCatalogWithGeneratedCuration - } - - /// Returns the first bundle in the source directory, if any. - /// > Note: The result of this function is not cached, it reads the source directory and finds all bundles. - public func firstAvailableBundle() -> DocumentationBundle? { - return (try? dataProvider.bundles(options: bundleDiscoveryOptions)).map(sorted(bundles:))?.first - } - - /// Sorts a list of bundles by the bundle identifier. - private func sorted(bundles: [DocumentationBundle]) -> [DocumentationBundle] { - return bundles.sorted(by: \.identifier) - } - - mutating public func convert( - outputConsumer: some ConvertOutputConsumer - ) throws -> (analysisProblems: [Problem], conversionProblems: [Problem]) { - defer { - diagnosticEngine.flush() - } - - // Unregister the current file data provider and all its bundles - // when running repeated conversions. - if let dataProvider = self.currentDataProvider { - try workspace.unregisterProvider(dataProvider) - } - - let context = self.context - - // Start bundle registration - try workspace.registerProvider(dataProvider, options: bundleDiscoveryOptions) - self.currentDataProvider = dataProvider - - // If cancelled, return early before we emit diagnostics. - func isConversionCancelled() -> Bool { - Task.isCancelled || isCancelled?.sync({ $0 }) == true - } - guard !isConversionCancelled() else { return ([], []) } - - processingDurationMetric = benchmark(begin: Benchmark.Duration(id: "documentation-processing")) - - let bundles = try sorted(bundles: dataProvider.bundles(options: bundleDiscoveryOptions)) - guard !bundles.isEmpty else { - if let rootURL { - throw Error.doesNotContainBundle(url: rootURL) - } else { - try (_Deprecated(outputConsumer) as (any _DeprecatedConsumeProblemsAccess))._consume(problems: context.problems) - throw GeneratedDataProvider.Error.notEnoughDataToGenerateBundle(options: bundleDiscoveryOptions, underlyingError: nil) - } - } - - // For now, we only support one bundle. - let bundle = bundles.first! - - if experimentalModifyCatalogWithGeneratedCuration, let catalogURL = rootURL { - let writer = GeneratedCurationWriter(context: context, catalogURL: catalogURL, outputURL: catalogURL) - let curation = try writer.generateDefaultCurationContents() - for (url, updatedContent) in curation { - guard let data = updatedContent.data(using: .utf8) else { continue } - try? FileManager.default.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) - try? data.write(to: url, options: .atomic) - } - } - - guard !context.problems.containsErrors else { - if emitDigest { - try (_Deprecated(outputConsumer) as (any _DeprecatedConsumeProblemsAccess))._consume(problems: context.problems) - } - return (analysisProblems: context.problems, conversionProblems: []) - } - - // Precompute the render context - let renderContext = RenderContext(documentationContext: context, bundle: bundle) - - try outputConsumer.consume(renderReferenceStore: renderContext.store) - - // Copy images, sample files, and other static assets. - try outputConsumer.consume(assetsInBundle: bundle) - - let symbolIdentifiersMeetingRequirementsForExpandedDocumentation: [String]? = symbolIdentifiersWithExpandedDocumentation?.compactMap { (identifier, expandedDocsRequirement) -> String? in - guard let documentationNode = context.documentationCache[identifier] else { - return nil - } - - return documentationNode.meetsExpandedDocumentationRequirements(expandedDocsRequirement) ? identifier : nil - } - - let converter = DocumentationContextConverter( - bundle: bundle, - context: context, - renderContext: renderContext, - emitSymbolSourceFileURIs: shouldEmitSymbolSourceFileURIs, - emitSymbolAccessLevels: shouldEmitSymbolAccessLevels, - sourceRepository: sourceRepository, - symbolIdentifiersWithExpandedDocumentation: symbolIdentifiersMeetingRequirementsForExpandedDocumentation - ) - - var indexingRecords = [IndexingRecord]() - var linkSummaries = [LinkDestinationSummary]() - var assets = [RenderReferenceType : [any RenderReference]]() - - let references = context.knownPages - let resultsSyncQueue = DispatchQueue(label: "Convert Serial Queue", qos: .unspecified, attributes: []) - let resultsGroup = DispatchGroup() - - var coverageInfo = [CoverageDataEntry]() - // No need to generate this closure more than once. - let coverageFilterClosure = documentationCoverageOptions.generateFilterClosure() - - // Process render nodes in batches allowing us to release memory and sync after each batch - // Keep track of any problems in case emitDigest == true - var conversionProblems: [Problem] = references.concurrentPerform { identifier, results in - // If cancelled skip all concurrent conversion work in this block. - guard !isConversionCancelled() else { return } - - // Wrap JSON encoding in an autorelease pool to avoid retaining the autoreleased ObjC objects returned by `JSONSerialization` - autoreleasepool { - do { - let entity = try context.entity(with: identifier) - - guard shouldConvertEntity(entity: entity, identifier: identifier) else { - return - } - - guard let renderNode = converter.renderNode(for: entity) else { - // No render node was produced for this entity, so just skip it. - return - } - - try outputConsumer.consume(renderNode: renderNode) - - switch documentationCoverageOptions.level { - case .detailed, .brief: - let coverageEntry = try CoverageDataEntry( - documentationNode: entity, - renderNode: renderNode, - context: context - ) - if coverageFilterClosure(coverageEntry) { - resultsGroup.async(queue: resultsSyncQueue) { - coverageInfo.append(coverageEntry) - } - } - case .none: - break - } - - if emitDigest { - let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: true) - let nodeIndexingRecords = try renderNode.indexingRecords(onPage: identifier) - - resultsGroup.async(queue: resultsSyncQueue) { - assets.merge(renderNode.assetReferences, uniquingKeysWith: +) - linkSummaries.append(contentsOf: nodeLinkSummaries) - indexingRecords.append(contentsOf: nodeIndexingRecords) - } - } else if FeatureFlags.current.isExperimentalLinkHierarchySerializationEnabled { - let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: false) - - resultsGroup.async(queue: resultsSyncQueue) { - linkSummaries.append(contentsOf: nodeLinkSummaries) - } - } - } catch { - recordProblem(from: error, in: &results, withIdentifier: "render-node") - } - } - } - - // Wait for any concurrent updates to complete. - resultsGroup.wait() - - // If cancelled, return before producing outputs. - guard !isConversionCancelled() else { return ([], []) } - - // Write various metadata - if emitDigest { - do { - try outputConsumer.consume(linkableElementSummaries: linkSummaries) - try outputConsumer.consume(indexingRecords: indexingRecords) - try outputConsumer.consume(assets: assets) - } catch { - recordProblem(from: error, in: &conversionProblems, withIdentifier: "metadata") - } - } - - if FeatureFlags.current.isExperimentalLinkHierarchySerializationEnabled { - do { - let serializableLinkInformation = try context.linkResolver.localResolver.prepareForSerialization(bundleID: bundle.id) - try outputConsumer.consume(linkResolutionInformation: serializableLinkInformation) - - if !emitDigest { - try outputConsumer.consume(linkableElementSummaries: linkSummaries) - } - } catch { - recordProblem(from: error, in: &conversionProblems, withIdentifier: "link-resolver") - } - } - - if emitDigest { - do { - try (_Deprecated(outputConsumer) as (any _DeprecatedConsumeProblemsAccess))._consume(problems: context.problems + conversionProblems) - } catch { - recordProblem(from: error, in: &conversionProblems, withIdentifier: "problems") - } - } - - switch documentationCoverageOptions.level { - case .detailed, .brief: - do { - try outputConsumer.consume(documentationCoverageInfo: coverageInfo) - } catch { - recordProblem(from: error, in: &conversionProblems, withIdentifier: "coverage") - } - case .none: - break - } - - try outputConsumer.consume( - buildMetadata: BuildMetadata( - bundleDisplayName: bundle.displayName, - bundleIdentifier: bundle.identifier - ) - ) - - // Log the duration of the processing (after the bundle content finished registering). - benchmark(end: processingDurationMetric) - // Log the finalized topic graph checksum. - benchmark(add: Benchmark.TopicGraphHash(context: context)) - // Log the finalized list of topic anchor sections. - benchmark(add: Benchmark.TopicAnchorHash(context: context)) - // Log the finalized external topics checksum. - benchmark(add: Benchmark.ExternalTopicsHash(context: context)) - // Log the peak memory. - benchmark(add: Benchmark.PeakMemory()) - - return (analysisProblems: context.problems, conversionProblems: conversionProblems) - } - - /// Whether the given entity should be converted to a render node. - private func shouldConvertEntity( - entity: DocumentationNode, - identifier: ResolvedTopicReference - ) -> Bool { - let isDocumentPathToConvert: Bool - if let documentPathsToConvert { - isDocumentPathToConvert = documentPathsToConvert.contains(identifier.path) - } else { - isDocumentPathToConvert = true - } - - let isExternalIDToConvert: Bool - if let externalIDsToConvert { - isExternalIDToConvert = entity.symbol.map { - externalIDsToConvert.contains($0.identifier.precise) - } == true - } else { - isExternalIDToConvert = true - } - - // If the identifier of the entity is neither in `documentPathsToConvert` - // nor `externalIDsToConvert`, we don't convert it to a render node. - return isDocumentPathToConvert || isExternalIDToConvert - } - - /// Record a problem from the given error in the given problem array. - /// - /// Creates a ``Problem`` from the given `Error` and identifier, emits it to the - /// ``DocumentationConverter``'s ``DiagnosticEngine``, and appends it to the given - /// problem array. - /// - /// - Parameters: - /// - error: The error that describes the problem. - /// - problems: The array that the created problem should be appended to. - /// - identifier: A unique identifier the problem. - private func recordProblem( - from error: any Swift.Error, - in problems: inout [Problem], - withIdentifier identifier: String - ) { - let singleDiagnostic = Diagnostic( - source: nil, - severity: .error, - range: nil, - identifier: "org.swift.docc.documentation-converter.\(identifier)", - summary: error.localizedDescription - ) - let problem = Problem(diagnostic: singleDiagnostic, possibleSolutions: []) - - diagnosticEngine.emit(problem) - problems.append(problem) - } - - enum Error: DescribedError, Equatable { - case doesNotContainBundle(url: URL) - - var errorDescription: String { - switch self { - case .doesNotContainBundle(let url): - return """ - The directory at '\(url)' and its subdirectories do not contain at least one \ - valid documentation bundle. A documentation bundle is a directory ending in \ - `.docc`. - Pass `--allow-arbitrary-catalog-directories` flag to convert a directory \ - without a `.docc` extension. - """ - } - } - } -} - -extension DocumentationNode { - func meetsExpandedDocumentationRequirements(_ requirements: ConvertRequest.ExpandedDocumentationRequirements) -> Bool { - guard let symbol else { return false } - - return requirements.accessControlLevels.contains(symbol.accessLevel.rawValue) && (!symbol.names.title.starts(with: "_") || requirements.canBeUnderscored) - } -} diff --git a/Sources/SwiftDocC/Infrastructure/External Data/OutOfProcessReferenceResolver.swift b/Sources/SwiftDocC/Infrastructure/External Data/OutOfProcessReferenceResolver.swift index fa3666edde..e4a737fa75 100644 --- a/Sources/SwiftDocC/Infrastructure/External Data/OutOfProcessReferenceResolver.swift +++ b/Sources/SwiftDocC/Infrastructure/External Data/OutOfProcessReferenceResolver.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -51,11 +51,6 @@ public import SymbolKit public class OutOfProcessReferenceResolver: ExternalDocumentationSource, GlobalExternalSymbolResolver { private let externalLinkResolvingClient: any ExternalLinkResolving - @available(*, deprecated, renamed: "id", message: "Use 'id' instead. This deprecated API will be removed after 6.2 is released") - public var bundleIdentifier: String { - bundleID.rawValue - } - /// The bundle identifier for the reference resolver in the other process. public let bundleID: DocumentationBundle.Identifier @@ -85,13 +80,6 @@ public class OutOfProcessReferenceResolver: ExternalDocumentationSource, GlobalE self.bundleID = .init(rawValue: decodedBundleIdentifier) self.externalLinkResolvingClient = longRunningProcess } - - @available(*, deprecated, renamed: "init(bundleID:server:convertRequestIdentifier:)", message: "Use 'init(bundleID:server:convertRequestIdentifier:)' instead. This deprecated API will be removed after 6.2 is released") - public init(bundleIdentifier: String, server: DocumentationServer, convertRequestIdentifier: String?) throws { - self.bundleID = .init(rawValue: bundleIdentifier) - self.externalLinkResolvingClient = LongRunningService( - server: server, convertRequestIdentifier: convertRequestIdentifier) - } /// Creates a new reference resolver that interacts with a documentation service. /// diff --git a/Sources/SwiftDocC/Infrastructure/Link Resolution/LinkResolver.swift b/Sources/SwiftDocC/Infrastructure/Link Resolution/LinkResolver.swift index 0ec34d9b4d..e7b44eb7d5 100644 --- a/Sources/SwiftDocC/Infrastructure/Link Resolution/LinkResolver.swift +++ b/Sources/SwiftDocC/Infrastructure/Link Resolution/LinkResolver.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2023-2024 Apple Inc. and the Swift project authors + Copyright (c) 2023-2025 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 @@ -93,7 +93,8 @@ public class LinkResolver { // Check if this is a link to an external documentation source that should have previously been resolved in `DocumentationContext.preResolveExternalLinks(...)` if let bundleID = unresolvedReference.bundleID, - !context._registeredBundles.contains(where: { $0.id == bundleID || urlReadablePath($0.displayName) == bundleID.rawValue }) + context.bundle.id != bundleID, + urlReadablePath(context.bundle.displayName) != bundleID.rawValue { return .failure(unresolvedReference, TopicReferenceResolutionErrorInfo("No external resolver registered for '\(bundleID)'.")) } @@ -171,9 +172,8 @@ private final class FallbackResolverBasedLinkResolver { // Check if a fallback reference resolver should resolve this let referenceBundleID = unresolvedReference.bundleID ?? parent.bundleID guard let fallbackResolver = context.configuration.convertServiceConfiguration.fallbackResolver, - // This uses an underscored internal variant of `registeredBundles` to avoid deprecation warnings and remain compatible with legacy data providers. - let knownBundleID = context._registeredBundles.first(where: { $0.id == referenceBundleID || urlReadablePath($0.displayName) == referenceBundleID.rawValue })?.id, - fallbackResolver.bundleID == knownBundleID + fallbackResolver.bundleID == context.bundle.id, + context.bundle.id == referenceBundleID || urlReadablePath(context.bundle.displayName) == referenceBundleID.rawValue else { return nil } @@ -191,8 +191,7 @@ private final class FallbackResolverBasedLinkResolver { ) allCandidateURLs.append(alreadyResolved.url) - // This uses an underscored internal variant of `bundle(identifier:)` to avoid deprecation warnings and remain compatible with legacy data providers. - let currentBundle = context._bundle(identifier: knownBundleID.rawValue)! + let currentBundle = context.bundle if !isCurrentlyResolvingSymbolLink { // First look up articles path allCandidateURLs.append(contentsOf: [ diff --git a/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchy.swift b/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchy.swift index be9144cc7e..d4891efab4 100644 --- a/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchy.swift +++ b/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchy.swift @@ -718,23 +718,6 @@ extension PathHierarchy { } } -// MARK: Removing nodes - -extension PathHierarchy { - // When unregistering a documentation bundle from a context, entries for that bundle should no longer be findable. - // The below implementation marks nodes as "not findable" while leaving them in the hierarchy so that they can be - // traversed. - // This would be problematic if it happened repeatedly but in practice the path hierarchy will only be in this state - // after unregistering a data provider until a new data provider is registered. - - /// Removes a node from the path hierarchy so that it can no longer be found. - /// - Parameter id: The unique identifier for the node. - mutating func removeNodeWithID(_ id: ResolvedIdentifier) { - // Remove the node from the lookup and unset its identifier - lookup.removeValue(forKey: id)!.identifier = nil - } -} - // MARK: Disambiguation container extension PathHierarchy { diff --git a/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchyBasedLinkResolver.swift b/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchyBasedLinkResolver.swift index 3c70f61124..4dc85a2968 100644 --- a/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchyBasedLinkResolver.swift +++ b/Sources/SwiftDocC/Infrastructure/Link Resolution/PathHierarchyBasedLinkResolver.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2022-2024 Apple Inc. and the Swift project authors + Copyright (c) 2022-2025 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 @@ -24,19 +24,6 @@ final class PathHierarchyBasedLinkResolver { self.pathHierarchy = pathHierarchy } - /// Remove all matches from a given documentation bundle from the link resolver. - func unregisterBundle(identifier: DocumentationBundle.Identifier) { - var newMap = BidirectionalMap() - for (id, reference) in resolvedReferenceMap { - if reference.bundleID == identifier { - pathHierarchy.removeNodeWithID(id) - } else { - newMap[id] = reference - } - } - resolvedReferenceMap = newMap - } - /// Creates a path string---that can be used to find documentation in the path hierarchy---from an unresolved topic reference, private static func path(for unresolved: UnresolvedTopicReference) -> String { guard let fragment = unresolved.fragment else { diff --git a/Sources/SwiftDocC/Infrastructure/NodeURLGenerator.swift b/Sources/SwiftDocC/Infrastructure/NodeURLGenerator.swift index cac3ab219c..f58a4a32cc 100644 --- a/Sources/SwiftDocC/Infrastructure/NodeURLGenerator.swift +++ b/Sources/SwiftDocC/Infrastructure/NodeURLGenerator.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -55,8 +55,6 @@ public struct NodeURLGenerator { case documentationCuration(parentPath: String, articleName: String) case article(bundleName: String, articleName: String) case tutorialTableOfContents(name: String) - @available(*, deprecated, renamed: "tutorialTableOfContents(name:)", message: "Use 'tutorialTableOfContents(name:)' instead. This deprecated API will be removed after 6.2 is released") - case technology(technologyName: String) case tutorial(bundleName: String, tutorialName: String) /// A URL safe path under the given root path. @@ -94,8 +92,7 @@ public struct NodeURLGenerator { isDirectory: false ) .path - case .technology(let name), - .tutorialTableOfContents(let name): + case .tutorialTableOfContents(let name): // Format: "/tutorials/Name" return Self.tutorialsFolderURL .appendingPathComponent( diff --git a/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift b/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift index 1975f58ea1..4418b6ad60 100644 --- a/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift +++ b/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift @@ -19,23 +19,22 @@ struct SymbolGraphLoader { private(set) var symbolGraphs: [URL: SymbolKit.SymbolGraph] = [:] private(set) var unifiedGraphs: [String: SymbolKit.UnifiedSymbolGraph] = [:] private(set) var graphLocations: [String: [SymbolKit.GraphCollector.GraphKind]] = [:] - // FIXME: After 6.2, when we no longer have `DocumentationContextDataProvider` we can simply this code to not use a closure to read data. - private var dataLoader: (URL, DocumentationBundle) throws -> Data - private var bundle: DocumentationBundle - private var symbolGraphTransformer: ((inout SymbolGraph) -> ())? = nil + private let dataProvider: any DataProvider + private let bundle: DocumentationBundle + private let symbolGraphTransformer: ((inout SymbolGraph) -> ())? /// Creates a new symbol graph loader /// - Parameters: /// - bundle: The documentation bundle from which to load symbol graphs. - /// - dataLoader: A closure that the loader uses to read symbol graph data. + /// - dataProvider: A provider that the loader uses to read symbol graph data. /// - symbolGraphTransformer: An optional closure that transforms the symbol graph after the loader decodes it. init( bundle: DocumentationBundle, - dataLoader: @escaping (URL, DocumentationBundle) throws -> Data, + dataProvider: any DataProvider, symbolGraphTransformer: ((inout SymbolGraph) -> ())? = nil ) { self.bundle = bundle - self.dataLoader = dataLoader + self.dataProvider = dataProvider self.symbolGraphTransformer = symbolGraphTransformer } @@ -61,13 +60,13 @@ struct SymbolGraphLoader { var loadedGraphs = [URL: (usesExtensionSymbolFormat: Bool?, graph: SymbolKit.SymbolGraph)]() var loadError: (any Error)? - let loadGraphAtURL: (URL) -> Void = { [dataLoader, bundle] symbolGraphURL in + let loadGraphAtURL: (URL) -> Void = { [dataProvider] symbolGraphURL in // Bail out in case a symbol graph has already errored guard loadingLock.sync({ loadError == nil }) else { return } do { // Load and decode a single symbol graph file - let data = try dataLoader(symbolGraphURL, bundle) + let data = try dataProvider.contents(of: symbolGraphURL) var symbolGraph: SymbolGraph diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift b/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift index d9b806b61a..28d4490c80 100644 --- a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift +++ b/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -18,18 +18,9 @@ extension DocumentationBundle { /// The display name of the bundle. public var displayName: String - @available(*, deprecated, renamed: "id", message: "Use 'id' instead. This deprecated API will be removed after 6.2 is released") - public var identifier: String { - id.rawValue - } - /// The unique identifier of the bundle. public var id: DocumentationBundle.Identifier - /// The version of the bundle. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - public var version: String? - /// The default language identifier for code listings in the bundle. public var defaultCodeListingLanguage: String? @@ -109,23 +100,6 @@ extension DocumentationBundle { ) } - @available(*, deprecated, renamed: "init(displayName:id:defaultCodeListingLanguage:defaultAvailability:defaultModuleKind:)", message: "Use 'Info.init(displayName:id:defaultCodeListingLanguage:defaultAvailability:defaultModuleKind:)' instead. This deprecated API will be removed after 6.2 is released") - public init( - displayName: String, - identifier: String, - defaultCodeListingLanguage: String?, - defaultAvailability: DefaultAvailability?, - defaultModuleKind: String? - ) { - self.init( - displayName: displayName, - id: .init(rawValue: identifier), - defaultCodeListingLanguage: defaultCodeListingLanguage, - defaultAvailability: defaultAvailability, - defaultModuleKind: defaultModuleKind - ) - } - /// Creates documentation bundle information from the given Info.plist data, falling back to the values /// in the given bundle discovery options if necessary. init( @@ -330,26 +304,6 @@ extension BundleDiscoveryOptions { additionalSymbolGraphFiles: additionalSymbolGraphFiles ) } - - @available(*, deprecated, renamed: "init(fallbackDisplayName:fallbackIdentifier:fallbackDefaultCodeListingLanguage:fallbackDefaultModuleKind:fallbackDefaultAvailability:additionalSymbolGraphFiles:)", message: "Use 'init(fallbackDisplayName:fallbackIdentifier:fallbackDefaultCodeListingLanguage:fallbackDefaultModuleKind:fallbackDefaultAvailability:additionalSymbolGraphFiles:)' instead. This deprecated API will be removed after 6.2 is released") - public init( - fallbackDisplayName: String? = nil, - fallbackIdentifier: String? = nil, - fallbackVersion: String?, - fallbackDefaultCodeListingLanguage: String? = nil, - fallbackDefaultModuleKind: String? = nil, - fallbackDefaultAvailability: DefaultAvailability? = nil, - additionalSymbolGraphFiles: [URL] = [] - ) { - self.init( - fallbackDisplayName: fallbackDisplayName, - fallbackIdentifier: fallbackIdentifier, - fallbackDefaultCodeListingLanguage: fallbackDefaultCodeListingLanguage, - fallbackDefaultModuleKind: fallbackDefaultModuleKind, - fallbackDefaultAvailability: fallbackDefaultAvailability, - additionalSymbolGraphFiles:additionalSymbolGraphFiles - ) - } } private extension CodingUserInfoKey { @@ -358,23 +312,3 @@ private extension CodingUserInfoKey { /// A user info key to store derived display name in the decoder. static let derivedDisplayName = CodingUserInfoKey(rawValue: "derivedDisplayName")! } - -extension DocumentationBundle.Info { - @available(*, deprecated, renamed: "init(displayName:identifier:defaultCodeListingLanguage:defaultAvailability:defaultModuleKind:)", message: "Use 'init(displayName:identifier:defaultCodeListingLanguage:defaultAvailability:defaultModuleKind:)' instead. This deprecated API will be removed after 6.2 is released") - public init( - displayName: String, - identifier: String, - version: String?, - defaultCodeListingLanguage: String?, - defaultAvailability: DefaultAvailability?, - defaultModuleKind: String? - ) { - self.init( - displayName: displayName, - identifier: identifier, - defaultCodeListingLanguage: defaultCodeListingLanguage, - defaultAvailability: defaultAvailability, - defaultModuleKind: defaultModuleKind - ) - } -} diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationWorkspace.swift b/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationWorkspace.swift deleted file mode 100644 index 874bbeb47e..0000000000 --- a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationWorkspace.swift +++ /dev/null @@ -1,148 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - 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 - See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -public import Foundation - -/// The documentation workspace provides a unified interface for accessing serialized documentation bundles and their files, from a variety of sources. -/// -/// The ``DocumentationContext`` and the workspace that the context is operating in are connected in two ways: -/// - The workspace is the context's data provider. -/// - The context is the workspace's ``DocumentationContextDataProviderDelegate``. -/// -/// The first lets the workspace multiplex the bundles from any number of data providers (``DocumentationWorkspaceDataProvider``) into a single list of -/// ``DocumentationContextDataProvider/bundles`` and allows the context to access the contents of the various bundles without knowing any specifics -/// of its source (files on disk, a database, or a web services). -/// -/// The second lets the workspace notify the context when bundles are added or removed so that the context stays up to date, even after the context is created. -/// -/// ``` -/// ┌─────┐ -/// ┌────────────────────────────────│ IDE │─────────────────────────────┐ -/// ┌──────────┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ └─────┘ │ -/// │FileSystem│─▶ WorkspaceDataProvider ─┐ │ │ -/// └──────────┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ │ -/// │ │ │ -/// │ │ │ -/// ┌──────────┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ┌───────────┐ Read-only ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌─────────┐ -/// │WebService│─▶ WorkspaceDataProvider ─┼─▶│ Workspace │◀────interface───── ContextDataProvider ◀────get data────│ Context │ -/// └──────────┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ └───────────┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └─────────┘ -/// │ │ ▲ -/// │ │ │ -/// ┌────────────────┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ │ -/// │MyCustomDatabase│─▶ WorkspaceDataProvider ─┘ │ Bundle or ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ Event push │ -/// └────────────────┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └───────file ───────▶ ContextDataProviderDelegate ─────interface─────┘ -/// change └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ -/// ``` -/// -/// > Note: Each data provider is treated as a separate file system. A single documentation bundle may not span multiple data providers. -/// -/// ## Topics -/// -/// ### Data Providers -/// -/// - ``DocumentationWorkspaceDataProvider`` -/// - ``LocalFileSystemDataProvider`` -/// - ``PrebuiltLocalFileSystemDataProvider`` -/// -/// ## See Also -/// -/// - ``DocumentationContext`` -/// - ``DocumentationContextDataProvider`` -/// - ``DocumentationContextDataProviderDelegate`` -/// -@available(*, deprecated, message: "Pass the context its inputs at initialization instead. This deprecated API will be removed after 6.2 is released") -public class DocumentationWorkspace: DocumentationContextDataProvider { - /// An error when requesting information from a workspace. - public enum WorkspaceError: DescribedError { - /// A bundle with the provided ID wasn't found in the workspace. - case unknownBundle(id: String) - /// A data provider with the provided ID wasn't found in the workspace. - case unknownProvider(id: String) - - /// A plain-text description of the error. - public var errorDescription: String { - switch self { - case .unknownBundle(let id): - return "The requested data could not be located because a containing bundle with id '\(id)' could not be found in the workspace." - case .unknownProvider(let id): - return "The requested data could not be located because a containing data provider with id '\(id)' could not be found in the workspace." - } - } - } - - /// Reads the data for a given file in a given documentation bundle. - /// - /// - Parameters: - /// - url: The URL of the file to read. - /// - bundle: The documentation bundle that the file belongs to. - /// - Throws: A ``WorkspaceError/unknownBundle(id:)`` error if the bundle doesn't exist in the workspace or - /// a ``WorkspaceError/unknownProvider(id:)`` error if the bundle's data provider doesn't exist in the workspace. - /// - Returns: The raw data for the given file. - public func contentsOfURL(_ url: URL, in bundle: DocumentationBundle) throws -> Data { - guard let providerID = bundleToProvider[bundle.identifier] else { - throw WorkspaceError.unknownBundle(id: bundle.identifier) - } - - guard let provider = providers[providerID] else { - throw WorkspaceError.unknownProvider(id: providerID) - } - - return try provider.contentsOfURL(url) - } - - /// A map of bundle identifiers to documentation bundles. - public var bundles: [String: DocumentationBundle] = [:] - /// A map of provider identifiers to data providers. - private var providers: [String: any DocumentationWorkspaceDataProvider] = [:] - /// A map of bundle identifiers to provider identifiers (in other words, a map from a bundle to the provider that vends the bundle). - private var bundleToProvider: [String: String] = [:] - /// The delegate to notify when documentation bundles are added or removed from this workspace. - public weak var delegate: (any DocumentationContextDataProviderDelegate)? - /// Creates a new, empty documentation workspace. - public init() {} - - /// Adds a new data provider to the workspace. - /// - /// Adding a data provider also adds the documentation bundles that it provides, and notifies the ``delegate`` of the added bundles. - /// - /// - Parameters: - /// - provider: The workspace data provider to add to the workspace. - /// - options: The options that the data provider uses to discover documentation bundles that it provides to the delegate. - public func registerProvider(_ provider: any DocumentationWorkspaceDataProvider, options: BundleDiscoveryOptions = .init()) throws { - // We must add the provider before adding the bundle so that the delegate - // may start making requests immediately. - providers[provider.identifier] = provider - - for bundle in try provider.bundles(options: options) { - bundles[bundle.identifier] = bundle - bundleToProvider[bundle.identifier] = provider.identifier - try delegate?.dataProvider(self, didAddBundle: bundle) - } - } - - /// Removes a given data provider from the workspace. - /// - /// Removing a data provider also removes all its provided documentation bundles and notifies the ``delegate`` of the removed bundles. - /// - /// - Parameters: - /// - provider: The workspace data provider to remove from the workspace. - /// - options: The options that the data provider uses to discover documentation bundles that it removes from the delegate. - public func unregisterProvider(_ provider: any DocumentationWorkspaceDataProvider, options: BundleDiscoveryOptions = .init()) throws { - for bundle in try provider.bundles(options: options) { - bundles[bundle.identifier] = nil - bundleToProvider[bundle.identifier] = nil - try delegate?.dataProvider(self, didRemoveBundle: bundle) - } - - // The provider must be removed after removing the bundle so that the delegate - // may continue making requests as part of removing the bundle. - providers[provider.identifier] = nil - } -} diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationWorkspaceDataProvider.swift b/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationWorkspaceDataProvider.swift index dbf5c03772..e068262975 100644 --- a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationWorkspaceDataProvider.swift +++ b/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationWorkspaceDataProvider.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -10,42 +10,6 @@ public import Foundation -/// A type that vends bundles and responds to requests for data. -@available(*, deprecated, message: "Pass the context its inputs at initialization instead. This deprecated API will be removed after 6.2 is released") -public protocol DocumentationWorkspaceDataProvider { - /// A string that uniquely identifies this data provider. - /// - /// Unless your implementation needs a stable identifier to associate with an external system, it's reasonable to - /// use `UUID().uuidString` for the provider's identifier. - var identifier: String { get } - - /// Returns the data backing one of the files that this data provider provides. - /// - /// Your implementation can expect to only receive URLs that it provides. It's acceptable to assert if you receive - /// a URL that wasn't provided by your data provider, because this indicates a bug in the ``DocumentationWorkspace``. - /// - /// - Parameter url: The URL of a file to return the backing data for. - func contentsOfURL(_ url: URL) throws -> Data - - /// Returns the documentation bundles that your data provider provides. - /// - /// - Parameter options: Configuration that controls how the provider discovers documentation bundles. - /// - /// If your data provider also conforms to ``FileSystemProvider``, there is a default implementation of this method - /// that traverses the ``FileSystemProvider/fileSystem`` to find all documentation bundles in it. - func bundles(options: BundleDiscoveryOptions) throws -> [DocumentationBundle] -} - -@available(*, deprecated, message: "Pass the context its inputs at initialization instead. This deprecated API will be removed after 6.2 is released") -public extension DocumentationWorkspaceDataProvider { - /// Returns the documentation bundles that your data provider provides; discovered with the default options. - /// - /// If your data provider also conforms to ``FileSystemProvider``, there is a default implementation of this method - /// that traverses the ``FileSystemProvider/fileSystem`` to find all documentation bundles in it. - func bundles() throws -> [DocumentationBundle] { - return try bundles(options: BundleDiscoveryOptions()) - } -} /// Options to configure the discovery of documentation bundles public struct BundleDiscoveryOptions { diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/FileSystemProvider.swift b/Sources/SwiftDocC/Infrastructure/Workspace/FileSystemProvider.swift deleted file mode 100644 index 2fb4fa60f9..0000000000 --- a/Sources/SwiftDocC/Infrastructure/Workspace/FileSystemProvider.swift +++ /dev/null @@ -1,66 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - 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 - See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -public import Foundation - -/// A type that vends a tree of virtual filesystem objects. -@available(*, deprecated, message: "Use 'FileManagerProtocol.recursiveFiles(startingPoint:)' instead. This deprecated API will be removed after 6.2 is released.") -public protocol FileSystemProvider { - /// The organization of the files that this provider provides. - var fileSystem: FSNode { get } -} - -/// An element in a virtual filesystem. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.") -public enum FSNode { - /// A file in a filesystem. - case file(File) - /// A directory in a filesystem. - case directory(Directory) - - /// A file in a virtual file system - public struct File { - /// The URL to this file. - public var url: URL - - /// Creates a new virtual file with a given URL - /// - Parameter url: The URL to this file. - public init(url: URL) { - self.url = url - } - } - - /// A directory in a virtual file system. - public struct Directory { - /// The URL to this directory. - public var url: URL - /// The contents of this directory. - public var children: [FSNode] - - /// Creates a new virtual directory with a given URL and contents. - /// - Parameters: - /// - url: The URL to this directory. - /// - children: The contents of this directory. - public init(url: URL, children: [FSNode]) { - self.url = url - self.children = children - } - } - - /// The URL for the node in the filesystem. - public var url: URL { - switch self { - case .file(let file): - return file.url - case .directory(let directory): - return directory.url - } - } -} diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/GeneratedDataProvider.swift b/Sources/SwiftDocC/Infrastructure/Workspace/GeneratedDataProvider.swift deleted file mode 100644 index f345158b56..0000000000 --- a/Sources/SwiftDocC/Infrastructure/Workspace/GeneratedDataProvider.swift +++ /dev/null @@ -1,149 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - Copyright (c) 2021-2022 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 -*/ - -public import Foundation -import SymbolKit - -/// A type that provides documentation bundles that it discovers by traversing the local file system. -@available(*, deprecated, message: "Use 'DocumentationContext.InputProvider' instead. This deprecated API will be removed after 6.2 is released") -public class GeneratedDataProvider: DocumentationWorkspaceDataProvider { - public var identifier: String = UUID().uuidString - - public typealias SymbolGraphDataLoader = (URL) -> Data? - private let symbolGraphDataLoader: SymbolGraphDataLoader - private var generatedMarkdownFiles: [String: Data] = [:] - - /// Creates a new provider that generates documentation bundles from the ``BundleDiscoveryOptions`` it is passed in ``bundles(options:)``. - /// - /// - Parameters: - /// - symbolGraphDataLoader: A closure that loads the raw data for a symbol graph file at a given URL. - public init(symbolGraphDataLoader: @escaping SymbolGraphDataLoader) { - self.symbolGraphDataLoader = symbolGraphDataLoader - } - - public func bundles(options: BundleDiscoveryOptions) throws -> [DocumentationBundle] { - // Find all the unique module names from the symbol graph files and generate a top level module page for each of them. - var moduleNames = Set() - for url in options.additionalSymbolGraphFiles { - guard let data = symbolGraphDataLoader(url) else { - throw Error.unableToLoadSymbolGraphData(url: url) - } - let container = try JSONDecoder().decode(SymbolGraphModuleContainer.self, from: data) - moduleNames.insert(container.module.name) - } - let info: DocumentationBundle.Info - do { - let derivedDisplayName: String? - if moduleNames.count == 1, let moduleName = moduleNames.first { - derivedDisplayName = moduleName - } else { - derivedDisplayName = nil - } - info = try DocumentationBundle.Info( - bundleDiscoveryOptions: options, - derivedDisplayName: derivedDisplayName - ) - } catch { - throw Error.notEnoughDataToGenerateBundle(options: options, underlyingError: error) - } - - guard !options.additionalSymbolGraphFiles.isEmpty else { - return [] - } - - if moduleNames.count == 1, let moduleName = moduleNames.first, moduleName != info.displayName { - generatedMarkdownFiles[moduleName] = Data(""" - # ``\(moduleName)`` - - @Metadata { - @DisplayName("\(info.displayName)") - } - """.utf8) - } else { - for moduleName in moduleNames { - generatedMarkdownFiles[moduleName] = Data("# ``\(moduleName)``".utf8) - } - } - - let topLevelPages = generatedMarkdownFiles.keys.map { URL(string: $0 + ".md")! } - - return [ - DocumentationBundle( - info: info, - symbolGraphURLs: options.additionalSymbolGraphFiles, - markupURLs: topLevelPages, - miscResourceURLs: [] - ) - ] - } - - enum Error: DescribedError { - case unableToLoadSymbolGraphData(url: URL) - case notEnoughDataToGenerateBundle(options: BundleDiscoveryOptions, underlyingError: (any Swift.Error)?) - - var errorDescription: String { - switch self { - case .unableToLoadSymbolGraphData(let url): - return "Unable to load data for symbol graph file at \(url.path.singleQuoted)" - case .notEnoughDataToGenerateBundle(let options, let underlyingError): - var symbolGraphFileList = options.additionalSymbolGraphFiles.reduce("") { $0 + "\n\t" + $1.path } - if !symbolGraphFileList.isEmpty { - symbolGraphFileList += "\n" - } - - var errorMessage = """ - The information provided as command line arguments is not enough to generate a documentation bundle: - """ - - if let underlyingError { - errorMessage += """ - \((underlyingError as? (any DescribedError))?.errorDescription ?? underlyingError.localizedDescription) - - """ - } else { - errorMessage += """ - \(options.infoPlistFallbacks.sorted(by: { lhs, rhs in lhs.key < rhs.key }).map { "\($0.key) : '\($0.value)'" }.joined(separator: "\n")) - Additional symbol graph files: [\(symbolGraphFileList)] - - """ - } - - return errorMessage - } - } - } - - public func contentsOfURL(_ url: URL) throws -> Data { - if DocumentationBundleFileTypes.isMarkupFile(url), let content = generatedMarkdownFiles[url.deletingPathExtension().lastPathComponent] { - return content - } else if DocumentationBundleFileTypes.isSymbolGraphFile(url) { - guard let data = symbolGraphDataLoader(url) else { - throw Error.unableToLoadSymbolGraphData(url: url) - } - return data - } else { - preconditionFailure("Unexpected url '\(url)'.") - } - } -} - -/// A wrapper type that decodes only the module in the symbol graph. -private struct SymbolGraphModuleContainer: Decodable { - /// The decoded symbol graph module. - let module: SymbolGraph.Module - - typealias CodingKeys = SymbolGraph.CodingKeys - - public init(from decoder: any Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - self.module = try container.decode(SymbolGraph.Module.self, forKey: .module) - } -} diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/LocalFileSystemDataProvider+BundleDiscovery.swift b/Sources/SwiftDocC/Infrastructure/Workspace/LocalFileSystemDataProvider+BundleDiscovery.swift deleted file mode 100644 index 9101f51ec1..0000000000 --- a/Sources/SwiftDocC/Infrastructure/Workspace/LocalFileSystemDataProvider+BundleDiscovery.swift +++ /dev/null @@ -1,180 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - 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 - See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -import Foundation - -@available(*, deprecated, message: "Use 'DocumentationContext.InputProvider' instead. This deprecated API will be removed after 6.2 is released") -extension LocalFileSystemDataProvider: DocumentationWorkspaceDataProvider { - public func bundles(options: BundleDiscoveryOptions) throws -> [DocumentationBundle] { - var bundles = try bundlesInTree(fileSystem, options: options) - - guard case .directory(let rootDirectory) = fileSystem else { - preconditionFailure("Expected directory object at path '\(fileSystem.url.absoluteString)'.") - } - - // If no bundles were found in the root directory, assume that the directory itself is a bundle. - if bundles.isEmpty && self.allowArbitraryCatalogDirectories { - bundles.append(try createBundle(rootDirectory, rootDirectory.children, options: options)) - } - - return bundles - } - - /// Recursively traverses the file system, searching for documentation bundles. - /// - /// - Parameters: - /// - root: The directory in which to search for documentation bundles. - /// - options: Configuration that controls how the provider discovers documentation bundles. - /// - Throws: A ``WorkspaceError`` if one of the found documentation bundle directories is an invalid documentation bundle. - /// - Returns: A list of all the bundles that the provider discovered in the file system. - private func bundlesInTree(_ root: FSNode, options: BundleDiscoveryOptions) throws -> [DocumentationBundle] { - var bundles: [DocumentationBundle] = [] - - guard case .directory(let rootDirectory) = root else { - preconditionFailure("Expected directory object at path '\(root.url.absoluteString)'.") - } - - if DocumentationBundleFileTypes.isDocumentationCatalog(rootDirectory.url) { - bundles.append(try createBundle(rootDirectory, rootDirectory.children, options: options)) - } else { - // Recursively descend when the current root directory isn't a documentation bundle. - for child in rootDirectory.children { - if case .directory = child { - try bundles.append(contentsOf: bundlesInTree(child, options: options)) - } - } - } - - return bundles - } - - /// Creates a documentation bundle from the content in a given documentation bundle directory. - /// - Parameters: - /// - directory: The documentation bundle directory. - /// - bundleChildren: The top-level files and directories in the documentation bundle directory. - /// - options: Configuration that controls how the provider discovers documentation bundles. - /// - Throws: A ``WorkspaceError`` if the content is an invalid documentation bundle or - /// a ``DocumentationBundle/PropertyListError`` error if the bundle's Info.plist file is invalid. - /// - Returns: The new documentation bundle. - private func createBundle(_ directory: FSNode.Directory, _ bundleChildren: [FSNode], options: BundleDiscoveryOptions) throws -> DocumentationBundle { - let infoPlistData: Data? - if let infoPlistRef = findInfoPlist(bundleChildren) { - infoPlistData = try contentsOfURL(infoPlistRef.url) - } else { - infoPlistData = nil - } - let info = try DocumentationBundle.Info( - from: infoPlistData, - bundleDiscoveryOptions: options, - derivedDisplayName: directory.url.deletingPathExtension().lastPathComponent - ) - - let markupFiles = findMarkupFiles(bundleChildren, recursive: true).map { $0.url } - let miscResources = findNonMarkupFiles(bundleChildren, recursive: true).map { $0.url } - let symbolGraphFiles = findSymbolGraphFiles(bundleChildren, recursive: true).map { $0.url } + options.additionalSymbolGraphFiles - - let customHeader = findCustomHeader(bundleChildren)?.url - let customFooter = findCustomFooter(bundleChildren)?.url - let themeSettings = findThemeSettings(bundleChildren)?.url - - return DocumentationBundle( - info: info, - symbolGraphURLs: symbolGraphFiles, - markupURLs: markupFiles, - miscResourceURLs: miscResources, - customHeader: customHeader, - customFooter: customFooter, - themeSettings: themeSettings - ) - } - - /// Performs a shallow search for the first Info.plist file in the given list of files and directories. - /// - Parameter bundleChildren: The list of files and directories to check. - /// - Returns: The first Info.plist file, or `nil` if none of the files is an Info.plist file. - private func findInfoPlist(_ bundleChildren: [FSNode]) -> FSNode.File? { - return bundleChildren.firstFile { DocumentationBundleFileTypes.isInfoPlistFile($0.url) } - } - - /// Finds all the symbol-graph files in the given list of files and directories. - /// - Parameters: - /// - bundleChildren: The list of files and directories to check. - /// - recursive: If `true`, this function will recursively check the files of all directories in the array. If `false`, it will ignore all directories. - /// - Returns: A list of all the symbol-graph files. - private func findSymbolGraphFiles(_ bundleChildren: [FSNode], recursive: Bool) -> [FSNode.File] { - return bundleChildren.files(recursive: recursive) { DocumentationBundleFileTypes.isSymbolGraphFile($0.url) } - } - - /// Finds all the markup files in the given list of files and directories. - /// - Parameters: - /// - bundleChildren: The list of files and directories to check. - /// - recursive: If `true`, this function will recursively check the files of all directories in the array. If `false`, it will ignore all directories. - /// - Returns: A list of all the markup files. - private func findMarkupFiles(_ bundleChildren: [FSNode], recursive: Bool) -> [FSNode.File] { - return bundleChildren.files(recursive: recursive) { DocumentationBundleFileTypes.isMarkupFile($0.url) } - } - - /// Finds all the non-markup files in the given list of files and directories. - /// - Parameters: - /// - bundleChildren: The list of files and directories to check. - /// - recursive: If `true`, this function will recursively check the files of all directories in the array. If `false`, it will ignore all directories. - /// - Returns: A list of all the non-markup files. - private func findNonMarkupFiles(_ bundleChildren: [FSNode], recursive: Bool) -> [FSNode.File] { - bundleChildren.files(recursive: recursive) { !DocumentationBundleFileTypes.isMarkupFile($0.url) && !DocumentationBundleFileTypes.isSymbolGraphFile($0.url) } - } - - private func findCustomHeader(_ bundleChildren: [FSNode]) -> FSNode.File? { - return bundleChildren.firstFile { DocumentationBundleFileTypes.isCustomHeader($0.url) } - } - - private func findCustomFooter(_ bundleChildren: [FSNode]) -> FSNode.File? { - return bundleChildren.firstFile { DocumentationBundleFileTypes.isCustomFooter($0.url) } - } - - private func findThemeSettings(_ bundleChildren: [FSNode]) -> FSNode.File? { - return bundleChildren.firstFile { DocumentationBundleFileTypes.isThemeSettingsFile($0.url) } - } -} - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.") -fileprivate extension [FSNode] { - /// Returns the first file that matches a given predicate. - /// - Parameter predicate: A closure that takes a file as its argument and returns a Boolean value indicating whether the file should be returned from this function. - /// - Throws: Any error that the predicate closure raises. - /// - Returns: The first file that matches the predicate. - func firstFile(where predicate: (FSNode.File) throws -> Bool) rethrows -> FSNode.File? { - for case .file(let file) in self where try predicate(file) { - return file - } - return nil - } - - /// Returns all the files that match s given predicate. - /// - Parameters: - /// - recursive: If `true`, this function will recursively check the files of all directories in the array. If `false`, it will ignore all directories in the array. - /// - predicate: A closure that takes a file as its argument and returns a Boolean value indicating whether the file should be included in the returned array. - /// - Throws: Any error that the predicate closure raises. - /// - Returns: The first file that matches the predicate. - func files(recursive: Bool, where predicate: (FSNode.File) throws -> Bool) rethrows -> [FSNode.File] { - var matches: [FSNode.File] = [] - for node in self { - switch node { - case .directory(let directory): - guard recursive else { break } - try matches.append(contentsOf: directory.children.files(recursive: true, where: predicate)) - case .file(let file) where try predicate(file): - matches.append(file) - case .file: - break - } - } - - return matches - } -} diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/LocalFileSystemDataProvider.swift b/Sources/SwiftDocC/Infrastructure/Workspace/LocalFileSystemDataProvider.swift deleted file mode 100644 index a24c75444c..0000000000 --- a/Sources/SwiftDocC/Infrastructure/Workspace/LocalFileSystemDataProvider.swift +++ /dev/null @@ -1,57 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - 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 - See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -public import Foundation - -/// A type that provides documentation bundles that it discovers by traversing the local file system. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.") -public struct LocalFileSystemDataProvider: FileSystemProvider { - public var identifier: String = UUID().uuidString - - /// The location that this provider searches for documentation bundles in. - public var rootURL: URL - - public var fileSystem: FSNode - - /// Whether to consider the root location as a documentation bundle if the data provider doesn't discover another bundle in the hierarchy from the root location. - public let allowArbitraryCatalogDirectories: Bool - - /// Creates a new provider that recursively traverses the content of the given root URL to discover documentation bundles. - /// - Parameters: - /// - rootURL: The location that this provider searches for documentation bundles in. - /// - allowArbitraryCatalogDirectories: Configures the data provider to consider the root location as a documentation bundle if it doesn't discover another bundle. - public init(rootURL: URL, allowArbitraryCatalogDirectories: Bool = false) throws { - self.rootURL = rootURL - self.allowArbitraryCatalogDirectories = allowArbitraryCatalogDirectories - fileSystem = try LocalFileSystemDataProvider.buildTree(root: rootURL) - } - - /// Builds a virtual file system hierarchy from the contents of a root URL in the local file system. - /// - Parameter root: The location from which to descend to build the virtual file system. - /// - Returns: A virtual file system that describe the file and directory structure within the given URL. - private static func buildTree(root: URL) throws -> FSNode { - var children: [FSNode] = [] - let childURLs = try FileManager.default.contentsOfDirectory(at: root, includingPropertiesForKeys: [URLResourceKey.isDirectoryKey], options: .skipsHiddenFiles) - - for url in childURLs { - if FileManager.default.directoryExists(atPath: url.path) { - children.append(try buildTree(root: url)) - } else { - children.append(FSNode.file(FSNode.File(url: url))) - } - } - return FSNode.directory(FSNode.Directory(url: root, children: children)) - } - - public func contentsOfURL(_ url: URL) throws -> Data { - precondition(url.isFileURL, "Unexpected non-file url '\(url)'.") - return try Data(contentsOf: url) - } -} diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/PrebuiltLocalFileSystemDataProvider.swift b/Sources/SwiftDocC/Infrastructure/Workspace/PrebuiltLocalFileSystemDataProvider.swift deleted file mode 100644 index 00c3231d35..0000000000 --- a/Sources/SwiftDocC/Infrastructure/Workspace/PrebuiltLocalFileSystemDataProvider.swift +++ /dev/null @@ -1,35 +0,0 @@ -/* - 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 -*/ - -public import Foundation - -/// A data provider that provides existing in-memory documentation bundles with files on the local filesystem. -@available(*, deprecated, message: "Use 'DocumentationContext.InputProvider' instead. This deprecated API will be removed after 6.2 is released") -public struct PrebuiltLocalFileSystemDataProvider: DocumentationWorkspaceDataProvider { - public var identifier: String = UUID().uuidString - - private var _bundles: [DocumentationBundle] - public func bundles(options: BundleDiscoveryOptions) throws -> [DocumentationBundle] { - // Ignore the bundle discovery options, these bundles are already built. - return _bundles - } - - /// Creates a new provider to provide the given documentation bundles. - /// - Parameter bundles: The existing documentation bundles for this provider to provide. - public init(bundles: [DocumentationBundle]) { - _bundles = bundles - } - - public func contentsOfURL(_ url: URL) throws -> Data { - precondition(url.isFileURL, "Unexpected non-file url '\(url)'.") - return try Data(contentsOf: url) - } -} - diff --git a/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift b/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift index 0a901bab56..8c6112dbb1 100644 --- a/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift +++ b/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -311,7 +311,8 @@ public extension DocumentationNode { renderNode: RenderNode, includeTaskGroups: Bool = true ) -> [LinkDestinationSummary] { - guard let bundle = context.bundle, bundle.id == reference.bundleID else { + let bundle = context.bundle + guard bundle.id == reference.bundleID else { // Don't return anything for external references that don't have a bundle in the context. return [] } @@ -330,7 +331,6 @@ public extension DocumentationNode { let taskGroups: [LinkDestinationSummary.TaskGroup]? if includeTaskGroups { switch kind { - case ._technologyOverview: fallthrough // This case is deprecated and will be removed after 6.2 is released. case .tutorial, .tutorialArticle, .tutorialTableOfContents, .chapter, .volume, .onPageLandmark: taskGroups = [.init(title: nil, identifiers: context.children(of: reference).map { $0.reference.absoluteString })] default: diff --git a/Sources/SwiftDocC/Model/BuildMetadata.swift b/Sources/SwiftDocC/Model/BuildMetadata.swift index abe6e57e0c..39a83aa468 100644 --- a/Sources/SwiftDocC/Model/BuildMetadata.swift +++ b/Sources/SwiftDocC/Model/BuildMetadata.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -23,11 +23,6 @@ public struct BuildMetadata: Codable { /// The display name of the documentation bundle that DocC built. public var bundleDisplayName: String - @available(*, deprecated, renamed: "bundleID", message: "Use 'bundleID' instead. This deprecated API will be removed after 6.2 is released") - public var bundleIdentifier: String { - bundleID.rawValue - } - /// The bundle identifier of the documentation bundle that DocC built. public let bundleID: DocumentationBundle.Identifier @@ -40,12 +35,4 @@ public struct BuildMetadata: Codable { self.bundleDisplayName = bundleDisplayName self.bundleID = bundleID } - - @available(*, deprecated, renamed: "init(bundleDisplayName:bundleID:)", message: "Use 'init(bundleDisplayName:bundleID:)' instead. This deprecated API will be removed after 6.2 is released") - public init(bundleDisplayName: String, bundleIdentifier: String) { - self.init( - bundleDisplayName: bundleDisplayName, - bundleID: .init(rawValue: bundleIdentifier) - ) - } } diff --git a/Sources/SwiftDocC/Model/Identifier.swift b/Sources/SwiftDocC/Model/Identifier.swift index d6e980784a..469af2f4ca 100644 --- a/Sources/SwiftDocC/Model/Identifier.swift +++ b/Sources/SwiftDocC/Model/Identifier.swift @@ -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-2025 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 @@ -176,11 +176,6 @@ public struct ResolvedTopicReference: Hashable, Codable, Equatable, CustomString /// The storage for the resolved topic reference's state. let _storage: Storage - @available(*, deprecated, renamed: "bundleID", message: "Use 'bundleID' instead. This deprecated API will be removed after 6.2 is released") - public var bundleIdentifier: String { - bundleID.rawValue - } - /// The identifier of the bundle that owns this documentation topic. public var bundleID: DocumentationBundle.Identifier { _storage.bundleID @@ -224,14 +219,6 @@ public struct ResolvedTopicReference: Hashable, Codable, Equatable, CustomString sourceLanguages: sourceLanguages ) } - @available(*, deprecated, renamed: "init(id:path:fragment:sourceLanguage:)", message: "Use 'init(id:path:fragment:sourceLanguage:)' instead. This deprecated API will be removed after 6.2 is released") - public init(bundleIdentifier: String, path: String, fragment: String? = nil, sourceLanguage: SourceLanguage) { - self.init(bundleIdentifier: bundleIdentifier, path: path, fragment: fragment, sourceLanguages: [sourceLanguage]) - } - @available(*, deprecated, renamed: "init(id:path:fragment:sourceLanguages:)", message: "Use 'init(id:path:fragment:sourceLanguages:)' instead. This deprecated API will be removed after 6.2 is released") - public init(bundleIdentifier: String, path: String, fragment: String? = nil, sourceLanguages: Set) { - self.init(bundleID: .init(rawValue: bundleIdentifier), path: path, fragment: fragment, sourceLanguages: sourceLanguages) - } private init(bundleID: DocumentationBundle.Identifier, urlReadablePath: String, urlReadableFragment: String? = nil, sourceLanguages: Set) { precondition(!sourceLanguages.isEmpty, "ResolvedTopicReference.sourceLanguages cannot be empty") @@ -541,11 +528,6 @@ public struct UnresolvedTopicReference: Hashable, CustomStringConvertible { /// The URL as originally spelled. public let topicURL: ValidatedURL - @available(*, deprecated, renamed: "bundleID", message: "Use 'bundleID' instead. This deprecated API will be removed after 6.2 is released") - public var bundleIdentifier: String? { - bundleID?.rawValue - } - /// The bundle identifier, if one was provided in the host name component of the original URL. public var bundleID: DocumentationBundle.Identifier? { topicURL.components.host.map { .init(rawValue: $0) } @@ -607,11 +589,6 @@ public struct UnresolvedTopicReference: Hashable, CustomStringConvertible { /// A reference to an auxiliary resource such as an image. public struct ResourceReference: Hashable { - @available(*, deprecated, renamed: "bundleID", message: "Use 'bundleID' instead. This deprecated API will be removed after 6.2 is released") - public var bundleIdentifier: String { - bundleID.rawValue - } - /// The documentation bundle identifier for the bundle in which this resource resides. public let bundleID: DocumentationBundle.Identifier diff --git a/Sources/SwiftDocC/Model/Kind.swift b/Sources/SwiftDocC/Model/Kind.swift index c6d5abf621..f241c753d0 100644 --- a/Sources/SwiftDocC/Model/Kind.swift +++ b/Sources/SwiftDocC/Model/Kind.swift @@ -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-2025 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 @@ -203,9 +203,6 @@ extension DocumentationNode.Kind { .extendedModule, .extendedStructure, .extendedClass, .extendedEnumeration, .extendedProtocol, .unknownExtendedType, // Other .keyword, .restAPI, .tag, .propertyList, .object - - // Deprecated, to be removed after 6.2 is released. - , _technologyOverview, ] /// Returns whether this symbol kind is a synthetic "Extended Symbol" symbol kind. @@ -218,12 +215,3 @@ extension DocumentationNode.Kind { } } } - -extension DocumentationNode.Kind { - @available(*, deprecated, renamed: "tutorialTableOfContents", message: "Use 'tutorialTableOfContents' This deprecated API will be removed after 6.2 is released") - public static var technology: Self { tutorialTableOfContents } - - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - public static var technologyOverview: Self { _technologyOverview } - static let _technologyOverview = DocumentationNode.Kind(name: "Technology (Overview)", id: "org.swift.docc.kind.technology.overview", isSymbol: false) -} diff --git a/Sources/SwiftDocC/Model/Rendering/DocumentationContentRenderer.swift b/Sources/SwiftDocC/Model/Rendering/DocumentationContentRenderer.swift index 15d3a439ac..2ad9f43a8b 100644 --- a/Sources/SwiftDocC/Model/Rendering/DocumentationContentRenderer.swift +++ b/Sources/SwiftDocC/Model/Rendering/DocumentationContentRenderer.swift @@ -154,7 +154,6 @@ public class DocumentationContentRenderer { case .chapter: return .collectionGroup case .collection: return .collection case .collectionGroup: return .collectionGroup - case ._technologyOverview: fallthrough // This case is deprecated and will be removed after 6.2 is released. case .tutorialTableOfContents: return .overview case .landingPage: return .article case .module, .extendedModule: return .collection diff --git a/Sources/SwiftDocC/Model/Rendering/LinkTitleResolver.swift b/Sources/SwiftDocC/Model/Rendering/LinkTitleResolver.swift index bf8838c25f..73f85a43f4 100644 --- a/Sources/SwiftDocC/Model/Rendering/LinkTitleResolver.swift +++ b/Sources/SwiftDocC/Model/Rendering/LinkTitleResolver.swift @@ -27,17 +27,15 @@ struct LinkTitleResolver { /// - Parameter page: The page for which to resolve the title. /// - Returns: The variants of the link title for this page, or `nil` if the page doesn't exist in the context. func title(for page: DocumentationNode) -> DocumentationDataVariants? { - if let bundle = context.bundle, - let directive = page.markup.child(at: 0) as? BlockDirective { - + if let directive = page.markup.child(at: 0) as? BlockDirective { var problems = [Problem]() switch directive.name { case Tutorial.directiveName: - if let tutorial = Tutorial(from: directive, source: source, for: bundle, problems: &problems) { + if let tutorial = Tutorial(from: directive, source: source, for: context.bundle, problems: &problems) { return .init(defaultVariantValue: tutorial.intro.title) } case TutorialTableOfContents.directiveName: - if let overview = TutorialTableOfContents(from: directive, source: source, for: bundle, problems: &problems) { + if let overview = TutorialTableOfContents(from: directive, source: source, for: context.bundle, problems: &problems) { return .init(defaultVariantValue: overview.name) } default: break diff --git a/Sources/SwiftDocC/Model/Rendering/RenderNode.swift b/Sources/SwiftDocC/Model/Rendering/RenderNode.swift index 973ff1f74e..520eb5e8a0 100644 --- a/Sources/SwiftDocC/Model/Rendering/RenderNode.swift +++ b/Sources/SwiftDocC/Model/Rendering/RenderNode.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -125,12 +125,6 @@ public struct RenderNode: VariantContainer { /// /// The key for each reference is the ``RenderReferenceIdentifier/identifier`` of the reference's ``RenderReference/identifier``. public var references: [String: any RenderReference] = [:] - - @available(*, deprecated, message: "Use 'hierarchyVariants' instead. This deprecated API will be removed after 6.2 is released") - public var hierarchy: RenderHierarchy? { - get { hierarchyVariants.defaultValue } - set { hierarchyVariants.defaultValue = newValue } - } /// Hierarchy information about the context in which this documentation node is placed. public var hierarchyVariants: VariantCollection = .init(defaultValue: nil) diff --git a/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift b/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift index 15e9ca1753..1fd747b15a 100644 --- a/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift +++ b/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift @@ -1756,7 +1756,7 @@ public struct RenderNodeTranslator: SemanticVisitor { let downloadReference: DownloadReference do { let downloadURL = resolvedAssets.variants.first!.value - let downloadData = try context.contentsOfURL(downloadURL, in: bundle) + let downloadData = try context.dataProvider.contents(of: downloadURL) downloadReference = DownloadReference(identifier: mediaReference, renderURL: downloadURL, checksum: Checksum.sha512(of: downloadData)) diff --git a/Sources/SwiftDocC/Model/Rendering/RenderReferenceStore.swift b/Sources/SwiftDocC/Model/Rendering/RenderReferenceStore.swift index b84ffa7616..d095303fc5 100644 --- a/Sources/SwiftDocC/Model/Rendering/RenderReferenceStore.swift +++ b/Sources/SwiftDocC/Model/Rendering/RenderReferenceStore.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -41,11 +41,6 @@ public struct RenderReferenceStore: Codable { public func content(forAssetNamed assetName: String, bundleID: DocumentationBundle.Identifier) -> DataAsset? { assets[AssetReference(assetName: assetName, bundleID: bundleID)] } - - @available(*, deprecated, renamed: "content(forAssetNamed:bundleID:)", message: "Use 'content(forAssetNamed:bundleID:)' instead. This deprecated API will be removed after 6.2 is released") - public func content(forAssetNamed assetName: String, bundleIdentifier: String) -> DataAsset? { - content(forAssetNamed: assetName, bundleID: .init(rawValue: bundleIdentifier)) - } } public extension RenderReferenceStore { diff --git a/Sources/SwiftDocC/Semantics/Article/MarkupConvertible.swift b/Sources/SwiftDocC/Semantics/Article/MarkupConvertible.swift index 5c3bdfbb76..2eace344cc 100644 --- a/Sources/SwiftDocC/Semantics/Article/MarkupConvertible.swift +++ b/Sources/SwiftDocC/Semantics/Article/MarkupConvertible.swift @@ -21,19 +21,4 @@ public protocol MarkupConvertible { /// - bundle: The documentation bundle that the source file belongs to. /// - problems: A mutable collection of problems to update with any problem encountered while initializing the element. init?(from markup: any Markup, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) - - @available(*, deprecated, renamed: "init(from:source:for:problems:)", message: "Use 'init(from:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - init?(from markup: any Markup, source: URL?, for bundle: DocumentationBundle, in context: DocumentationContext, problems: inout [Problem]) -} - -public extension MarkupConvertible { - // Default implementation to avoid source breaking changes. Remove this after 6.2 is released. - init?(from markup: any Markup, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) { - fatalError("Markup convertible type doesn't implement either 'init(from:source:for:problems:)' or 'init(from:source:for:in:problems:)'") - } - - // Default implementation to new types don't need to implement a deprecated initializer. Remove this after 6.2 is released. - init?(from markup: any Markup, source: URL?, for bundle: DocumentationBundle, in context: DocumentationContext, problems: inout [Problem]) { - self.init(from: markup, source: source, for: bundle, problems: &problems) - } } diff --git a/Sources/SwiftDocC/Semantics/DirectiveConvertable.swift b/Sources/SwiftDocC/Semantics/DirectiveConvertable.swift index b5407c0a93..fb2d4d6f9d 100644 --- a/Sources/SwiftDocC/Semantics/DirectiveConvertable.swift +++ b/Sources/SwiftDocC/Semantics/DirectiveConvertable.swift @@ -38,9 +38,6 @@ public protocol DirectiveConvertible { /// - problems: An inout array of ``Problem`` to be collected for later diagnostic reporting. init?(from directive: BlockDirective, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) - @available(*, deprecated, renamed: "init(from:source:for:problems:)", message: "Use 'init(from:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - init?(from directive: BlockDirective, source: URL?, for bundle: DocumentationBundle, in context: DocumentationContext, problems: inout [Problem]) - /// Returns a Boolean value indicating whether the `DirectiveConvertible` recognizes the given directive. /// /// - Parameter directive: The directive to check for conversion compatibility. @@ -54,15 +51,5 @@ public extension DirectiveConvertible { static func canConvertDirective(_ directive: BlockDirective) -> Bool { directiveName == directive.name } - - // Default implementation to avoid source breaking changes. Remove this after 6.2 is released. - init?(from directive: BlockDirective, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) { - fatalError("Directive named \(directive.name) doesn't implement either 'init(from:source:for:problems:)' or 'init(from:source:for:in:problems:)'") - } - - // Default implementation to new types don't need to implement a deprecated initializer. Remove this after 6.2 is released. - init?(from directive: BlockDirective, source: URL?, for bundle: DocumentationBundle, in context: DocumentationContext, problems: inout [Problem]) { - self.init(from: directive, source: source, for: bundle, problems: &problems) - } } diff --git a/Sources/SwiftDocC/Semantics/DirectiveInfrastructure/AutomaticDirectiveConvertible.swift b/Sources/SwiftDocC/Semantics/DirectiveInfrastructure/AutomaticDirectiveConvertible.swift index d6e172c990..bfd1ab53de 100644 --- a/Sources/SwiftDocC/Semantics/DirectiveInfrastructure/AutomaticDirectiveConvertible.swift +++ b/Sources/SwiftDocC/Semantics/DirectiveInfrastructure/AutomaticDirectiveConvertible.swift @@ -111,11 +111,6 @@ extension AutomaticDirectiveConvertible { ) } - @available(*, deprecated, renamed: "init(from:source:for:)", message: "Use 'init(from:source:for:)' instead. This deprecated API will be removed after 6.2 is released") - public init?(from directive: BlockDirective, source: URL? = nil, for bundle: DocumentationBundle, in _: DocumentationContext) { - self.init(from: directive, source: source, for: bundle) - } - public init?( from directive: BlockDirective, source: URL?, diff --git a/Sources/SwiftDocC/Semantics/ExternalLinks/ExternalReferenceWalker.swift b/Sources/SwiftDocC/Semantics/ExternalLinks/ExternalReferenceWalker.swift index 5e15a2d062..b1f2021f4a 100644 --- a/Sources/SwiftDocC/Semantics/ExternalLinks/ExternalReferenceWalker.swift +++ b/Sources/SwiftDocC/Semantics/ExternalLinks/ExternalReferenceWalker.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -110,11 +110,6 @@ struct ExternalReferenceWalker: SemanticVisitor { visitMarkupContainer(MarkupContainer(markup)) } - @available(*, deprecated) // This is a deprecated protocol requirement. Remove after 6.2 is released - mutating func visitTechnology(_ technology: TutorialTableOfContents) { - visitTutorialTableOfContents(technology) - } - mutating func visitTutorialTableOfContents(_ tutorialTableOfContents: TutorialTableOfContents) -> Void { visit(tutorialTableOfContents.intro) tutorialTableOfContents.volumes.forEach { visit($0) } diff --git a/Sources/SwiftDocC/Semantics/General Purpose Analyses/Extract.swift b/Sources/SwiftDocC/Semantics/General Purpose Analyses/Extract.swift index 9d063731ad..38b3611ea0 100644 --- a/Sources/SwiftDocC/Semantics/General Purpose Analyses/Extract.swift +++ b/Sources/SwiftDocC/Semantics/General Purpose Analyses/Extract.swift @@ -18,11 +18,6 @@ extension Semantic.Analyses { public struct ExtractAll { public init() {} - @available(*, deprecated, renamed: "analyze(_:children:source:for:problems:)", message: "Use 'analyze(_:children:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> ([Child], remainder: MarkupContainer) { - analyze(directive, children: children, source: source, for: bundle, problems: &problems) - } - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) -> ([Child], remainder: MarkupContainer) { return Semantic.Analyses.extractAll( childType: Child.self, @@ -66,11 +61,6 @@ extension Semantic.Analyses { } return (matches, remainder: MarkupContainer(remainder)) } - - @available(*, deprecated, renamed: "analyze(_:children:source:problems:)", message: "Use 'analyze(_:children:source:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for _: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> ([Child], remainder: MarkupContainer) { - analyze(directive, children: children, source: source, problems: &problems) - } } } diff --git a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasAtLeastOne.swift b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasAtLeastOne.swift index ba20a20496..7c8172f0d1 100644 --- a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasAtLeastOne.swift +++ b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasAtLeastOne.swift @@ -21,11 +21,6 @@ extension Semantic.Analyses { self.severityIfNotFound = severityIfNotFound } - @available(*, deprecated, renamed: "analyze(_:children:source:for:problems:)", message: "Use 'analyze(_:children:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> ([Child], remainder: MarkupContainer) { - analyze(directive, children: children, source: source, for: bundle, problems: &problems) - } - public func analyze( _ directive: BlockDirective, children: some Sequence, diff --git a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasAtMostOne.swift b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasAtMostOne.swift index 9c987c89dd..93765fd5d3 100644 --- a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasAtMostOne.swift +++ b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasAtMostOne.swift @@ -16,12 +16,6 @@ extension Semantic.Analyses { Checks to see if a parent directive has at most one child directive of a specified type. If so, return that child and the remainder. */ public struct HasAtMostOne { - - @available(*, deprecated, renamed: "analyze(_:children:source:for:problems:)", message: "Use 'analyze(_:children:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> (Child?, remainder: MarkupContainer) { - analyze(directive, children: children, source: source, for: bundle, problems: &problems) - } - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) -> (Child?, remainder: MarkupContainer) { return Semantic.Analyses.extractAtMostOne( childType: Child.self, diff --git a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasContent.swift b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasContent.swift index 8c0879ec21..4badc40152 100644 --- a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasContent.swift +++ b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasContent.swift @@ -36,10 +36,5 @@ extension Semantic.Analyses { problems.append(Problem(diagnostic: diagnostic, possibleSolutions: [])) return MarkupContainer() } - - @available(*, deprecated, renamed: "analyze(_:children:source:problems:)", message: "Use 'analyze(_:children:source:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for _: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> MarkupContainer { - analyze(directive, children: children, source: source, problems: &problems) - } } } diff --git a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasExactlyOne.swift b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasExactlyOne.swift index 1385323c14..aa05a6dd63 100644 --- a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasExactlyOne.swift +++ b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasExactlyOne.swift @@ -21,11 +21,6 @@ extension Semantic.Analyses { self.severityIfNotFound = severityIfNotFound } - @available(*, deprecated, renamed: "analyze(_:children:source:for:problems:)", message: "Use 'analyze(_:children:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> (Child?, remainder: MarkupContainer) { - analyze(directive, children: children, source: source, for: bundle, problems: &problems) - } - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) -> (Child?, remainder: MarkupContainer) { return Semantic.Analyses.extractExactlyOne( childType: Child.self, @@ -104,11 +99,6 @@ extension Semantic.Analyses { self.severityIfNotFound = severityIfNotFound } - @available(*, deprecated, renamed: "analyze(_:children:source:for:problems:)", message: "Use 'analyze(_:children:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> (Child1?, Child2?, remainder: MarkupContainer) { - analyze(directive, children: children, source: source, for: bundle, problems: &problems) - } - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) -> (Child1?, Child2?, remainder: MarkupContainer) { let (candidates, remainder) = children.categorize { child -> BlockDirective? in guard let childDirective = child as? BlockDirective else { @@ -159,11 +149,6 @@ extension Semantic.Analyses { self.severityIfNotFound = severityIfNotFound } - @available(*, deprecated, renamed: "analyze(_:children:source:for:problems:)", message: "Use 'analyze(_:children:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> ((any Media)?, remainder: MarkupContainer) { - analyze(directive, children: children, source: source, for: bundle, problems: &problems) - } - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) -> ((any Media)?, remainder: MarkupContainer) { let (foundImage, foundVideo, remainder) = HasExactlyOneOf(severityIfNotFound: severityIfNotFound).analyze(directive, children: children, source: source, for: bundle, problems: &problems) return (foundImage ?? foundVideo, remainder) @@ -177,11 +162,6 @@ extension Semantic.Analyses { self.severityIfNotFound = severityIfNotFound } - @available(*, deprecated, renamed: "analyze(_:children:source:for:problems:)", message: "Use 'analyze(_:children:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> ((any Media)?, remainder: MarkupContainer) { - analyze(directive, children: children, source: source, for: bundle, problems: &problems) - } - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, problems: inout [Problem]) -> ((any Media)?, remainder: MarkupContainer) { let (mediaDirectives, remainder) = children.categorize { child -> BlockDirective? in guard let childDirective = child as? BlockDirective else { @@ -279,11 +259,6 @@ extension Semantic.Analyses { return validElements } - @available(*, deprecated, renamed: "analyze(_:children:source:problems:)", message: "Use 'analyze(_:children:source:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for _: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> [ListElement]? { - analyze(directive, children: children, source: source, problems: &problems) - } - func firstChildElement(in markup: any Markup) -> ListElement? { return markup // ListItem .child(at: 0)? // Paragraph diff --git a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlyKnownArguments.swift b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlyKnownArguments.swift index 5ca45fae53..25da33e704 100644 --- a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlyKnownArguments.swift +++ b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlyKnownArguments.swift @@ -42,11 +42,6 @@ extension Semantic.Analyses { } return arguments } - - @available(*, deprecated, renamed: "analyze(_:children:source:problems:)", message: "Use 'analyze(_:children:source:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for _: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> [String: Markdown.DirectiveArgument] { - analyze(directive, children: children, source: source, problems: &problems) - } } } diff --git a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlyKnownDirectives.swift b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlyKnownDirectives.swift index d346015f53..ec54d1a2f3 100644 --- a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlyKnownDirectives.swift +++ b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlyKnownDirectives.swift @@ -75,11 +75,6 @@ extension Semantic.Analyses { } } } - - @available(*, deprecated, renamed: "analyze(_:children:source:problems:)", message: "Use 'analyze(_:children:source:problems:)' instead. This deprecated API will be removed after 6.2 is released") - public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for _: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) { - analyze(directive, children: children, source: source, problems: &problems) - } } } diff --git a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlySequentialHeadings.swift b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlySequentialHeadings.swift index addf05361a..b7515b4efa 100644 --- a/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlySequentialHeadings.swift +++ b/Sources/SwiftDocC/Semantics/General Purpose Analyses/HasOnlySequentialHeadings.swift @@ -35,11 +35,6 @@ extension Semantic.Analyses { self.startingFromLevel = startingFromLevel } - @available(*, deprecated, renamed: "analyze(_:children:source:for:problems:)", message: "Use 'analyze(_:children:source:for:problems:)' instead. This deprecated API will be removed after 6.2 is released") - @discardableResult public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, in _: DocumentationContext, problems: inout [Problem]) -> [Heading] { - analyze(directive, children: children, source: source, for: bundle, problems: &problems) - } - /// Returns all valid headings. @discardableResult public func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for _: DocumentationBundle, problems: inout [Problem]) -> [Heading] { var currentHeadingLevel = startingFromLevel diff --git a/Sources/SwiftDocC/Semantics/ReferenceResolver.swift b/Sources/SwiftDocC/Semantics/ReferenceResolver.swift index 9703463ad0..2bb44a90ec 100644 --- a/Sources/SwiftDocC/Semantics/ReferenceResolver.swift +++ b/Sources/SwiftDocC/Semantics/ReferenceResolver.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -252,11 +252,6 @@ struct ReferenceResolver: SemanticVisitor { return (visitMarkupContainer(MarkupContainer(markup)) as! MarkupContainer).elements.first! } - @available(*, deprecated) // This is a deprecated protocol requirement. Remove after 6.2 is released. - mutating func visitTechnology(_ technology: TutorialTableOfContents) -> Semantic { - visitTutorialTableOfContents(technology) - } - mutating func visitTutorialTableOfContents(_ tutorialTableOfContents: TutorialTableOfContents) -> Semantic { let newIntro = visit(tutorialTableOfContents.intro) as! Intro let newVolumes = tutorialTableOfContents.volumes.map { visit($0) } as! [Volume] diff --git a/Sources/SwiftDocC/Semantics/SemanticAnalysis.swift b/Sources/SwiftDocC/Semantics/SemanticAnalysis.swift deleted file mode 100644 index ac52cc8aae..0000000000 --- a/Sources/SwiftDocC/Semantics/SemanticAnalysis.swift +++ /dev/null @@ -1,76 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - Copyright (c) 2021-2025 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 -*/ - -public import Foundation -public import Markdown - -/** - A focused semantic analysis of a `BlockDirective`, recording problems and producing a result. - - A semantic analysis should check focus on the smallest meaningful aspect of the incoming `BlockDirective`. - This eases testing and helps prevent a tangle of dependencies and side effects. For every analysis, there - should be some number of tests for its robustness. - - For example, if an argument is required and is expected to be an integer, a semantic analysis - would check only that argument, attempt to convert it to an integer, and return it as the result. - - > Important: A ``SemanticAnalysis`` should not mutate outside state or directly depend on the results - of another analysis. This prevents runaway performance problems and strange bugs. - > It also makes it more amenable to parallelization should the need arise. - */ -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -public protocol SemanticAnalysis { - /** - The result of the analysis. - - > Note: This result may be `Void` as some analyses merely validate aspects of a `BlockDirective`. - */ - associatedtype Result - - /** - Perform the analysis on a directive, collect problems, and attempt to return a ``SemanticAnalysis/Result`` if required. - - - parameter directive: The `BlockDirective` that allegedly represents a ``Semantic`` object - - parameter children: The subset of `directive`'s children to analyze - - parameter source: A `URL` to the source file from which the `directive` came, if there was one. This is used for printing the location of a diagnostic. - - parameter bundle: The ``DocumentationBundle`` that owns the incoming `BlockDirective` - - parameter context: The ``DocumentationContext`` in which the bundle resides - - parameter problems: A container to append ``Problem``s encountered during the analysis - - returns: A result of the analysis if required, such as a validated parameter or subsection. - */ - func analyze(_ directive: BlockDirective, children: some Sequence, source: URL?, for bundle: DocumentationBundle, in context: DocumentationContext, problems: inout [Problem]) -> Result -} - -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.ExtractAll: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.ExtractAllMarkup: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasAtLeastOne: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasExactlyOne: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasExactlyOneOf: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasExactlyOneMedia: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasExactlyOneUnorderedList: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasExactlyOneImageOrVideoMedia: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasAtMostOne: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasContent: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasOnlyKnownArguments: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasOnlyKnownDirectives: SemanticAnalysis {} -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -extension Semantic.Analyses.HasOnlySequentialHeadings: SemanticAnalysis {} diff --git a/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift b/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift index 7991ebaf7e..a62aac5ba7 100644 --- a/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift +++ b/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -208,59 +208,24 @@ public final class Symbol: Semantic, Abstracted, Redirected, AutomaticTaskGroups /// Any dictionary keys of the symbol, if the symbol accepts keys. public var dictionaryKeysSection: DictionaryKeysSection? - @available(*, deprecated, renamed: "dictionaryKeysSection", message: "Use 'dictionaryKeysSection' instead. This deprecated API will be removed after 6.2 is released") - public var dictionaryKeysSectionVariants: DocumentationDataVariants { - get { .init(defaultVariantValue: dictionaryKeysSection) } - set { dictionaryKeysSection = newValue.firstValue } - } /// The symbol's possible values, if the symbol is a property list element with possible values. public var possibleValuesSection: PropertyListPossibleValuesSection? - @available(*, deprecated, renamed: "possibleValuesSection", message: "Use 'possibleValuesSection' instead. This deprecated API will be removed after 6.2 is released") - public var possibleValuesSectionVariants: DocumentationDataVariants { - get { .init(defaultVariantValue: possibleValuesSection) } - set { possibleValuesSection = newValue.firstValue } - } /// The HTTP endpoint of an HTTP request. public var httpEndpointSection: HTTPEndpointSection? - @available(*, deprecated, renamed: "httpEndpointSection", message: "Use 'httpEndpointSection' instead. This deprecated API will be removed after 6.2 is released") - public var httpEndpointSectionVariants: DocumentationDataVariants { - get { .init(defaultVariantValue: httpEndpointSection) } - set { httpEndpointSection = newValue.firstValue } - } - + /// The upload body of an HTTP request. public var httpBodySection: HTTPBodySection? - @available(*, deprecated, renamed: "httpBodySection", message: "Use 'httpBodySection' instead. This deprecated API will be removed after 6.2 is released") - public var httpBodySectionVariants: DocumentationDataVariants { - get { .init(defaultVariantValue: httpBodySection) } - set { httpBodySection = newValue.firstValue } - } - + /// The parameters of an HTTP request. public var httpParametersSection: HTTPParametersSection? - @available(*, deprecated, renamed: "httpParametersSection", message: "Use 'httpParametersSection' instead. This deprecated API will be removed after 6.2 is released") - public var httpParametersSectionVariants: DocumentationDataVariants { - get { .init(defaultVariantValue: httpParametersSection) } - set { httpParametersSection = newValue.firstValue } - } /// The responses of an HTTP request. public var httpResponsesSection: HTTPResponsesSection? - @available(*, deprecated, renamed: "httpResponsesSection", message: "Use 'httpResponsesSection' instead. This deprecated API will be removed after 6.2 is released") - public var httpResponsesSectionVariants: DocumentationDataVariants { - get { .init(defaultVariantValue: httpResponsesSection) } - set { httpResponsesSection = newValue.firstValue } - } /// Any redirect information of the symbol, if the symbol has been moved from another location. public var redirects: [Redirect]? - @available(*, deprecated, renamed: "redirects", message: "Use 'redirects' instead. This deprecated API will be removed after 6.2 is released") - public var redirectsVariants: DocumentationDataVariants<[Redirect]> { - get { .init(defaultVariantValue: redirects) } - set { redirects = newValue.firstValue } - } /// The symbol's abstract summary as a single paragraph, in each language variant the symbol is available in. public var abstractVariants: DocumentationDataVariants { diff --git a/Sources/SwiftDocC/Semantics/Technology/TutorialTableOfContents.swift b/Sources/SwiftDocC/Semantics/Technology/TutorialTableOfContents.swift index 1503e32f74..7e7098c0fb 100644 --- a/Sources/SwiftDocC/Semantics/Technology/TutorialTableOfContents.swift +++ b/Sources/SwiftDocC/Semantics/Technology/TutorialTableOfContents.swift @@ -120,6 +120,3 @@ public final class TutorialTableOfContents: Semantic, DirectiveConvertible, Abst return visitor.visitTutorialTableOfContents(self) } } - -@available(*, deprecated, renamed: "TutorialTableOfContents", message: "Use 'TutorialTableOfContents' instead. This deprecated API will be removed after 6.2 is released") -public typealias Technology = TutorialTableOfContents diff --git a/Sources/SwiftDocC/Semantics/TechnologyBound.swift b/Sources/SwiftDocC/Semantics/TechnologyBound.swift deleted file mode 100644 index c06ab6c3d9..0000000000 --- a/Sources/SwiftDocC/Semantics/TechnologyBound.swift +++ /dev/null @@ -1,16 +0,0 @@ -/* - 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 -*/ - -/// An entity directly referring to the technology it belongs to. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -public protocol TechnologyBound { - /// The `name` of the ``TutorialTableOfContents`` this section refers to. - var technology: TopicReference { get } -} diff --git a/Sources/SwiftDocC/Semantics/Visitor/SemanticVisitor.swift b/Sources/SwiftDocC/Semantics/Visitor/SemanticVisitor.swift index b301551338..3426102ccc 100644 --- a/Sources/SwiftDocC/Semantics/Visitor/SemanticVisitor.swift +++ b/Sources/SwiftDocC/Semantics/Visitor/SemanticVisitor.swift @@ -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-2025 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 @@ -86,9 +86,6 @@ public protocol SemanticVisitor { Visit a ``TutorialTableOfContents`` and return the result. */ mutating func visitTutorialTableOfContents(_ tutorialTableOfContents: TutorialTableOfContents) -> Result - - @available(*, deprecated, renamed: "visitTutorialTableOfContents(_:)", message: "Use 'visitTutorialTableOfContents(_:)' instead. This deprecated API will be removed after 6.2 is released") - mutating func visitTechnology(_ technology: TutorialTableOfContents) -> Result /** Visit an ``ImageMedia`` and return the result. @@ -151,11 +148,3 @@ extension SemanticVisitor { return semantic.accept(&self) } } - -@available(*, deprecated) // Remove this default implementation after 6.2 is released. -extension SemanticVisitor { - // We need to provide a default implementation to avoid the breaking change of a new protocol requirement. - mutating func visitTutorialTableOfContents(_ tutorialTableOfContents: TutorialTableOfContents) -> Result { - self.visitTechnology(tutorialTableOfContents) - } -} diff --git a/Sources/SwiftDocC/Semantics/Walker/SemanticWalker.swift b/Sources/SwiftDocC/Semantics/Walker/SemanticWalker.swift index 242476129a..105353b6d6 100644 --- a/Sources/SwiftDocC/Semantics/Walker/SemanticWalker.swift +++ b/Sources/SwiftDocC/Semantics/Walker/SemanticWalker.swift @@ -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-2025 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 @@ -50,8 +50,6 @@ extension SemanticWalker { mutating func visitTile(_ tile: Tile) { descendIntoChildren(of: tile) } /// Visits a comment node. mutating func visitComment(_ comment: Comment) { descendIntoChildren(of: comment) } - @available(*, deprecated) // This is a deprecated protocol requirement. Remove after 6.2 is released. - mutating func visitTechnology(_ technology: TutorialTableOfContents) { descendIntoChildren(of: technology) } /// Visits a tutorials table-of-contents page. mutating func visitTutorialTableOfContents(_ tutorialTableOfContents: TutorialTableOfContents) { descendIntoChildren(of: tutorialTableOfContents) } /// Visits an image node. diff --git a/Sources/SwiftDocC/Semantics/Walker/Walkers/SemanticTreeDumper.swift b/Sources/SwiftDocC/Semantics/Walker/Walkers/SemanticTreeDumper.swift index bc379233c4..c34d9a541d 100644 --- a/Sources/SwiftDocC/Semantics/Walker/Walkers/SemanticTreeDumper.swift +++ b/Sources/SwiftDocC/Semantics/Walker/Walkers/SemanticTreeDumper.swift @@ -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-2025 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 @@ -148,11 +148,6 @@ struct SemanticTreeDumper: SemanticWalker { dump(markupContainer, customDescription: description) } - @available(*, deprecated) // This is a deprecated protocol requirement. Remove after 6.2 is released - mutating func visitTechnology(_ technology: TutorialTableOfContents) { - visitTutorialTableOfContents(technology) - } - mutating func visitTutorialTableOfContents(_ tutorialTableOfContents: TutorialTableOfContents) -> () { dump(tutorialTableOfContents, customDescription: "name: '\(tutorialTableOfContents.name)'") } diff --git a/Sources/SwiftDocC/SwiftDocC.docc/Resources/Diagnostics.json b/Sources/SwiftDocC/SwiftDocC.docc/Resources/Diagnostics.json index 0c268b7b7b..bd53d8e809 100644 --- a/Sources/SwiftDocC/SwiftDocC.docc/Resources/Diagnostics.json +++ b/Sources/SwiftDocC/SwiftDocC.docc/Resources/Diagnostics.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "description": "Specification of the deprecated DocC diagnostics.json digest file. This deprecated file will be removed after 6.2 is released.", + "description": "Specification of the deprecated DocC diagnostics.json digest file. This deprecated file will be removed after 6.3 is released.", "version": "0.1.0", "title": "Diagnostics" }, diff --git a/Sources/SwiftDocC/Utility/FeatureFlags.swift b/Sources/SwiftDocC/Utility/FeatureFlags.swift index b2ec4dbc5d..def7e642d1 100644 --- a/Sources/SwiftDocC/Utility/FeatureFlags.swift +++ b/Sources/SwiftDocC/Utility/FeatureFlags.swift @@ -26,30 +26,11 @@ public struct FeatureFlags: Codable { /// Whether support for automatically rendering links on symbol documentation to articles that mention that symbol is enabled. public var isMentionedInEnabled = true - @available(*, deprecated, renamed: "isMentionedInEnabled", message: "Use 'isMentionedInEnabled' instead. This deprecated API will be removed after 6.2 is released") - public var isExperimentalMentionedInEnabled: Bool { - get { isMentionedInEnabled } - set { isMentionedInEnabled = newValue } - } - /// Whether or not support for validating parameters and return value documentation is enabled. public var isParametersAndReturnsValidationEnabled = true /// Creates a set of feature flags with all default values. public init() {} - - /// Creates a set of feature flags with the given values. - /// - /// - Parameters: - /// - additionalFlags: Any additional flags to set. - /// - /// This field allows clients to set feature flags without adding new API. - @available(*, deprecated, renamed: "init()", message: "Use 'init()' instead. This deprecated API will be removed after 6.2 is released") - @_disfavoredOverload - public init( - additionalFlags: [String : Bool] = [:] - ) { - } /// Set feature flags that were loaded from a bundle's Info.plist. internal mutating func loadFlagsFromBundle(_ bundleFlags: DocumentationBundle.Info.BundleFeatureFlags) { diff --git a/Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertFileWritingConsumer.swift b/Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertFileWritingConsumer.swift index 9d0370dda3..56e4925851 100644 --- a/Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertFileWritingConsumer.swift +++ b/Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertFileWritingConsumer.swift @@ -50,8 +50,8 @@ struct ConvertFileWritingConsumer: ConvertOutputConsumer, ExternalNodeConsumer { self.assetPrefixComponent = bundleID?.rawValue.split(separator: "/").joined(separator: "-") } - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - func consume(problems: [Problem]) throws { + @available(*, deprecated, message: "This deprecated API will be removed after 6.3 is released") + func _deprecated_consume(problems: [Problem]) throws { let diagnostics = problems.map { problem in Digest.Diagnostic(diagnostic: problem.diagnostic, rootURL: bundleRootFolder) } @@ -84,7 +84,6 @@ struct ConvertFileWritingConsumer: ConvertOutputConsumer, ExternalNodeConsumer { } } - // TODO: Supporting a single bundle for the moment. let bundleID = bundle.id assert(bundleID.rawValue == self.assetPrefixComponent, "Unexpectedly encoding assets for a bundle other than the one this output consumer was created for.") @@ -251,7 +250,7 @@ enum Digest { let downloads: [DownloadReference] } - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") + @available(*, deprecated, message: "This deprecated API will be removed after 6.3 is released") struct Diagnostic: Codable { struct Location: Codable { let line: Int @@ -270,7 +269,7 @@ enum Digest { } } -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") +@available(*, deprecated, message: "This deprecated API will be removed after 6.3 is released") private extension Digest.Diagnostic { init(diagnostic: Diagnostic, rootURL: URL?) { self.start = (diagnostic.range?.lowerBound).map { Location(line: $0.line, column: $0.column) } diff --git a/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift b/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift index 557b4f2a52..95b0d098c5 100644 --- a/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift +++ b/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -512,9 +512,10 @@ extension Docc { var enableMentionedIn = true // This flag only exist to allow developers to pass the previous '--enable-experimental-...' flag without errors. + // The last release to support this spelling was 6.2. @Flag(name: .customLong("enable-experimental-mentioned-in"), help: .hidden) - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - var enableExperimentalMentionedIn = false + @available(*, deprecated, message: "This flag is unused and only exist for backwards compatibility") + var _unusedExperimentalMentionedInFlagForBackwardsCompatibility = false @Flag( name: .customLong("parameters-and-returns-validation"), @@ -609,11 +610,6 @@ extension Docc { get { featureFlags.enableMentionedIn } set { featureFlags.enableMentionedIn = newValue } } - @available(*, deprecated, renamed: "enableMentionedIn", message: "Use 'enableMentionedIn' instead. This deprecated API will be removed after 6.2 is released") - public var enableExperimentalMentionedIn: Bool { - get { enableMentionedIn } - set { enableMentionedIn = newValue } - } /// A user-provided value that is true if the user enables experimental validation for parameters and return value documentation. public var enableParametersAndReturnsValidation: Bool { diff --git a/Tests/SwiftDocCTests/Converter/DocumentationConverterTests.swift b/Tests/SwiftDocCTests/Converter/DocumentationConverterTests.swift deleted file mode 100644 index cf021a05df..0000000000 --- a/Tests/SwiftDocCTests/Converter/DocumentationConverterTests.swift +++ /dev/null @@ -1,48 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - 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 - See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - - -import XCTest -@testable import SwiftDocC - -// This test verifies the behavior of `DocumentationConverter` which is a deprecated type. -// Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -class DocumentationConverterTests: XCTestCase { - /// An empty implementation of `ConvertOutputConsumer` that purposefully does nothing. - struct EmptyConvertOutputConsumer: ConvertOutputConsumer, ExternalNodeConsumer { - // Conformance to ConvertOutputConsumer - func consume(renderNode: RenderNode) throws { } - func consume(problems: [Problem]) throws { } - func consume(assetsInBundle bundle: DocumentationBundle) throws {} - func consume(linkableElementSummaries: [LinkDestinationSummary]) throws {} - func consume(indexingRecords: [IndexingRecord]) throws {} - func consume(assets: [RenderReferenceType: [any RenderReference]]) throws {} - func consume(benchmarks: Benchmark) throws {} - func consume(documentationCoverageInfo: [CoverageDataEntry]) throws {} - - // Conformance to ExternalNodeConsumer - func consume(externalRenderNode: SwiftDocC.ExternalRenderNode) throws { } - } - - func testThrowsErrorOnConvertingNoBundles() throws { - let rootURL = try createTemporaryDirectory() - - let dataProvider = try LocalFileSystemDataProvider(rootURL: rootURL) - let workspace = DocumentationWorkspace() - try workspace.registerProvider(dataProvider) - let context = try DocumentationContext(dataProvider: workspace) - var converter = DocumentationConverter(documentationBundleURL: rootURL, emitDigest: false, documentationCoverageOptions: .noCoverage, currentPlatforms: nil, workspace: workspace, context: context, dataProvider: dataProvider, bundleDiscoveryOptions: BundleDiscoveryOptions()) - XCTAssertThrowsError(try converter.convert(outputConsumer: EmptyConvertOutputConsumer())) { error in - let converterError = try? XCTUnwrap(error as? DocumentationConverter.Error) - XCTAssertEqual(converterError, DocumentationConverter.Error.doesNotContainBundle(url: rootURL)) - } - } -} diff --git a/Tests/SwiftDocCTests/DeprecatedDiagnosticsDigestWarningTests.swift b/Tests/SwiftDocCTests/DeprecatedDiagnosticsDigestWarningTests.swift index 2d23726afb..f8875ee299 100644 --- a/Tests/SwiftDocCTests/DeprecatedDiagnosticsDigestWarningTests.swift +++ b/Tests/SwiftDocCTests/DeprecatedDiagnosticsDigestWarningTests.swift @@ -13,6 +13,7 @@ import SwiftDocC import SwiftDocCTestUtilities import XCTest +// THIS SHOULD BE REMOVED, RIGHT?! class DeprecatedDiagnosticsDigestWarningTests: XCTestCase { func testNoDeprecationWarningWhenThereAreNoOtherWarnings() async throws { let catalog = Folder(name: "unit-test.docc", content: [ @@ -66,14 +67,14 @@ class DeprecatedDiagnosticsDigestWarningTests: XCTestCase { let deprecationWarning = try XCTUnwrap(outputConsumer.problems.first?.diagnostic) XCTAssertEqual(deprecationWarning.identifier, "org.swift.docc.DeprecatedDiagnosticsDigets") - XCTAssertEqual(deprecationWarning.summary, "The 'diagnostics.json' digest file is deprecated and will be removed after 6.2 is released. Pass a `--diagnostics-file ` to specify a custom location where DocC will write a diagnostics JSON file with more information.") + XCTAssertEqual(deprecationWarning.summary, "The 'diagnostics.json' digest file is deprecated and will be removed after 6.3 is released. Pass a `--diagnostics-file ` to specify a custom location where DocC will write a diagnostics JSON file with more information.") } } private class TestOutputConsumer: ConvertOutputConsumer, ExternalNodeConsumer { var problems: [Problem] = [] - func consume(problems: [Problem]) throws { + func _deprecated_consume(problems: [Problem]) throws { self.problems.append(contentsOf: problems) } diff --git a/Tests/SwiftDocCTests/Infrastructure/BundleDiscoveryTests.swift b/Tests/SwiftDocCTests/Infrastructure/BundleDiscoveryTests.swift index bd543071c2..323fe5a2c5 100644 --- a/Tests/SwiftDocCTests/Infrastructure/BundleDiscoveryTests.swift +++ b/Tests/SwiftDocCTests/Infrastructure/BundleDiscoveryTests.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021-2024 Apple Inc. and the Swift project authors + Copyright (c) 2021-2025 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 @@ -26,97 +26,6 @@ class BundleDiscoveryTests: XCTestCase { return files } - // This tests registration of multiple catalogs which is deprecated - // Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - func testFirstBundle() throws { - let url = try createTemporaryDirectory() - // Create 3 minimal doc bundles - for i in 1 ... 3 { - let nestedBundle = Folder(name: "TestBundle\(i).docc", content: [ - InfoPlist(displayName: "Test Bundle \(i)", identifier: "com.example.bundle\(i)"), - TextFile(name: "Root.md", utf8Content: """ - # Test Bundle \(i) - @Metadata { - @TechnologyRoot - } - Abstract. - - Content. - """), - ]) - _ = try nestedBundle.write(inside: url) - } - - let workspace = DocumentationWorkspace() - let context = try DocumentationContext(dataProvider: workspace) - let dataProvider = try LocalFileSystemDataProvider(rootURL: url) - try workspace.registerProvider(dataProvider) - - // Verify all bundles are loaded - XCTAssertEqual(context.registeredBundles.map { $0.identifier }.sorted(), - ["com.example.bundle1", "com.example.bundle2", "com.example.bundle3"] - ) - - // Verify the first one is bundle1 - let converter = DocumentationConverter(documentationBundleURL: url, emitDigest: false, documentationCoverageOptions: .noCoverage, currentPlatforms: nil, workspace: workspace, context: context, dataProvider: dataProvider, bundleDiscoveryOptions: .init()) - XCTAssertEqual(converter.firstAvailableBundle()?.identifier, "com.example.bundle1") - } - - // This test registration more than once data provider which is deprecated. - // Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - func testLoadComplexWorkspace() throws { - let allFiles = try flatListOfFiles() - let workspace = Folder(name: "TestWorkspace", content: [ - CopyOfFolder(original: testBundleLocation), - Folder(name: "nested", content: [ - Folder(name: "irrelevant", content: [ - TextFile(name: "irrelevant.txt", utf8Content: "distraction"), - ]), - TextFile(name: "irrelevant.txt", utf8Content: "distraction"), - Folder(name: "TestBundle2.docc", content: [ - InfoPlist(displayName: "Test Bundle", identifier: "com.example.bundle2"), - Folder(name: "Subfolder", content: // All files flattened into one folder - allFiles.map { CopyOfFile(original: $0) } - ), - ]), - ]), - ]) - - let tempURL = try createTemporaryDirectory() - - let workspaceURL = try workspace.write(inside: tempURL) - - let dataProvider = try LocalFileSystemDataProvider(rootURL: workspaceURL) - - let bundles = (try dataProvider.bundles()).sorted { (bundle1, bundle2) -> Bool in - return bundle1.identifier < bundle2.identifier - } - - XCTAssertEqual(bundles.count, 2) - - guard bundles.count == 2 else { return } - - XCTAssertEqual(bundles[0].identifier, "com.example.bundle2") - XCTAssertEqual(bundles[1].identifier, "org.swift.docc.example") - - func checkBundle(_ bundle: DocumentationBundle) { - XCTAssertEqual(bundle.displayName, "Test Bundle") - XCTAssertEqual(bundle.symbolGraphURLs.count, 4) - XCTAssertTrue(bundle.symbolGraphURLs.map { $0.lastPathComponent }.contains("mykit-iOS.symbols.json")) - XCTAssertTrue(bundle.symbolGraphURLs.map { $0.lastPathComponent }.contains("MyKit@SideKit.symbols.json")) - XCTAssertTrue(bundle.symbolGraphURLs.map { $0.lastPathComponent }.contains("sidekit.symbols.json")) - XCTAssertTrue(bundle.symbolGraphURLs.map { $0.lastPathComponent }.contains("FillIntroduced.symbols.json")) - XCTAssertFalse(bundle.markupURLs.isEmpty) - XCTAssertTrue(bundle.miscResourceURLs.map { $0.lastPathComponent }.sorted().contains("intro.png")) - } - - for bundle in bundles { - checkBundle(bundle) - } - } - func testBundleFormat() throws { let allFiles = try flatListOfFiles() diff --git a/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift b/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift index 2d55fa1a10..11326bef88 100644 --- a/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift +++ b/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift @@ -36,52 +36,6 @@ extension CollectionDifference { } class DocumentationContextTests: XCTestCase { - // This test checks unregistration of workspace data providers which is deprecated - // Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - func testResolve() async throws { - let workspace = DocumentationWorkspace() - let context = try DocumentationContext(dataProvider: workspace) - let bundle = try await testBundle(named: "LegacyBundle_DoNotUseInNewTests") - let dataProvider = PrebuiltLocalFileSystemDataProvider(bundles: [bundle]) - try workspace.registerProvider(dataProvider) - - // Test resolving - let unresolved = UnresolvedTopicReference(topicURL: ValidatedURL(parsingExact: "doc:/TestTutorial")!) - let parent = ResolvedTopicReference(bundleIdentifier: bundle.id.rawValue, path: "", sourceLanguage: .swift) - - guard case let .success(resolved) = context.resolve(.unresolved(unresolved), in: parent) else { - XCTFail("Couldn't resolve \(unresolved)") - return - } - - XCTAssertEqual(parent.bundleIdentifier, resolved.bundleIdentifier) - XCTAssertEqual("/tutorials/Test-Bundle/TestTutorial", resolved.path) - - // Test lowercasing of path - let unresolvedUppercase = UnresolvedTopicReference(topicURL: ValidatedURL(parsingExact: "doc:/TESTTUTORIAL")!) - guard case .failure = context.resolve(.unresolved(unresolvedUppercase), in: parent) else { - XCTFail("Did incorrectly resolve \(unresolvedUppercase)") - return - } - - // Test expected URLs - let expectedURL = URL(string: "doc://org.swift.docc.example/tutorials/Test-Bundle/TestTutorial") - XCTAssertEqual(expectedURL, resolved.url) - - guard context.documentURL(for: resolved) != nil else { - XCTFail("Couldn't resolve file URL for \(resolved)") - return - } - - try workspace.unregisterProvider(dataProvider) - - guard case .failure = context.resolve(.unresolved(unresolved), in: parent) else { - XCTFail("Unexpectedly resolved \(unresolved.topicURL) despite removing a data provider for it") - return - } - } - func testLoadEntity() async throws { let (bundle, context) = try await testBundleAndContext(named: "LegacyBundle_DoNotUseInNewTests") @@ -651,35 +605,6 @@ class DocumentationContextTests: XCTestCase { XCTAssertEqual(downloadsAfter.first?.variants.values.first?.lastPathComponent, "intro.png") } - // This test registration more than once data provider which is deprecated. - // Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - func testCreatesCorrectIdentifiers() throws { - let testBundleLocation = Bundle.module.url( - forResource: "LegacyBundle_DoNotUseInNewTests", withExtension: "docc", subdirectory: "Test Bundles")! - let workspaceContent = Folder(name: "TestWorkspace", content: [ - CopyOfFolder(original: testBundleLocation), - - Folder(name: "TestBundle2.docc", content: [ - InfoPlist(displayName: "Test Bundle", identifier: "com.example.bundle2"), - CopyOfFolder(original: testBundleLocation, newName: "Subfolder", filter: { $0.lastPathComponent != "Info.plist" }), - ]) - ]) - - let tempURL = try createTemporaryDirectory() - - let workspaceURL = try workspaceContent.write(inside: tempURL) - let dataProvider = try LocalFileSystemDataProvider(rootURL: workspaceURL) - - let workspace = DocumentationWorkspace() - try workspace.registerProvider(dataProvider) - - let context = try DocumentationContext(dataProvider: workspace) - let identifiers = context.knownIdentifiers - let identifierSet = Set(identifiers) - XCTAssertEqual(identifiers.count, identifierSet.count, "Found duplicate identifiers.") - } - func testDetectsReferenceCollision() async throws { let (_, context) = try await testBundleAndContext(named: "TestBundleWithDupe") diff --git a/Tests/SwiftDocCTests/Infrastructure/DocumentationWorkspaceTests.swift b/Tests/SwiftDocCTests/Infrastructure/DocumentationWorkspaceTests.swift deleted file mode 100644 index 81ea209019..0000000000 --- a/Tests/SwiftDocCTests/Infrastructure/DocumentationWorkspaceTests.swift +++ /dev/null @@ -1,206 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - 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 - See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -import XCTest -@testable import SwiftDocC - -// This test verifies the behavior of `DocumentationWorkspace` which is a deprecated type. -// Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -class DocumentationWorkspaceTests: XCTestCase { - func testEmptyWorkspace() { - let workspace = DocumentationWorkspace() - let workspaceDelegate = SimpleWorkspaceDelegate() - workspace.delegate = workspaceDelegate - - XCTAssertEqual(workspace.bundles.count, 0) - - XCTAssertEqual(workspaceDelegate.record, []) - - checkTestWorkspaceContents(workspace: workspace, bundles: [SimpleDataProvider.bundle1, SimpleDataProvider.bundle2], filled: false) - } - - func testRegisterProvider() throws { - let provider = SimpleDataProvider(bundles: [SimpleDataProvider.bundle1, SimpleDataProvider.bundle2]) - let workspace = DocumentationWorkspace() - let workspaceDelegate = SimpleWorkspaceDelegate() - workspace.delegate = workspaceDelegate - - try workspace.registerProvider(provider) - - let events: [SimpleWorkspaceDelegate.Event] = provider._bundles.map { .add($0.identifier) } - - XCTAssertEqual(workspace.bundles.count, 2) - for bundlePair in workspace.bundles { - XCTAssertEqual(bundlePair.key, bundlePair.value.identifier) - } - - XCTAssertEqual(Set(workspace.bundles.map { $0.value.identifier }), Set(provider._bundles.map { $0.identifier })) - XCTAssertEqual(workspaceDelegate.record, events) - - checkTestWorkspaceContents(workspace: workspace, bundles: provider._bundles, filled: true) - } - - func testUnregisterProvider() throws { - let provider = SimpleDataProvider(bundles: [SimpleDataProvider.bundle1, SimpleDataProvider.bundle2]) - let workspace = DocumentationWorkspace() - let workspaceDelegate = SimpleWorkspaceDelegate() - workspace.delegate = workspaceDelegate - - try workspace.registerProvider(provider) - - var events: [SimpleWorkspaceDelegate.Event] = provider._bundles.map { .add($0.identifier) } - - XCTAssertEqual(workspace.bundles.count, 2) - for bundlePair in workspace.bundles { - XCTAssertEqual(bundlePair.key, bundlePair.value.identifier) - } - - XCTAssertEqual(Set(workspace.bundles.map { $0.value.identifier }), Set(provider._bundles.map { $0.identifier })) - XCTAssertEqual(workspaceDelegate.record, events) - - checkTestWorkspaceContents(workspace: workspace, bundles: provider._bundles, filled: true) - - try workspace.unregisterProvider(provider) - - events.append(contentsOf: provider._bundles.map { .remove($0.identifier) }) - - XCTAssertEqual(workspace.bundles.count, 0) - XCTAssertEqual(workspaceDelegate.record, events) - - checkTestWorkspaceContents(workspace: workspace, bundles: provider._bundles, filled: false) - } - - func testMultipleProviders() throws { - let provider1 = SimpleDataProvider(bundles: [SimpleDataProvider.bundle1, SimpleDataProvider.bundle2]) - let workspace = DocumentationWorkspace() - let workspaceDelegate = SimpleWorkspaceDelegate() - workspace.delegate = workspaceDelegate - - try workspace.registerProvider(provider1) - - var events: [SimpleWorkspaceDelegate.Event] = provider1._bundles.map { .add($0.identifier) } - - XCTAssertEqual(workspace.bundles.count, 2) - for bundlePair in workspace.bundles { - XCTAssertEqual(bundlePair.key, bundlePair.value.identifier) - } - - XCTAssertEqual(Set(workspace.bundles.map { $0.value.identifier }), Set(provider1._bundles.map { $0.identifier })) - XCTAssertEqual(workspaceDelegate.record, events) - - checkTestWorkspaceContents(workspace: workspace, bundles: provider1._bundles, filled: true) - - let provider2 = SimpleDataProvider(bundles: [SimpleDataProvider.bundle3, SimpleDataProvider.bundle4]) - try workspace.registerProvider(provider2) - - events.append(contentsOf: provider2._bundles.map { .add($0.identifier) }) - - XCTAssertEqual(workspace.bundles.count, 4) - for bundlePair in workspace.bundles { - XCTAssertEqual(bundlePair.key, bundlePair.value.identifier) - } - - XCTAssertEqual(Set(workspace.bundles.map { $0.value.identifier }), Set(provider1._bundles.map { $0.identifier } + provider2._bundles.map { $0.identifier })) - XCTAssertEqual(workspaceDelegate.record, events) - - checkTestWorkspaceContents(workspace: workspace, bundles: provider1._bundles + provider2._bundles, filled: true) - } - - func checkTestWorkspaceContents(workspace: DocumentationWorkspace, bundles: [DocumentationBundle], filled: Bool, line: UInt = #line) { - func check(file: URL, bundle: DocumentationBundle, line: UInt) { - if filled { - XCTAssertEqual(try workspace.contentsOfURL(file, in: bundle), SimpleDataProvider.files[file]!, line: line) - } else { - XCTAssertThrowsError(try workspace.contentsOfURL(file, in: bundle), line: line) - } - } - - for bundle in bundles { - check(file: SimpleDataProvider.testMarkupFile, bundle: bundle, line: line) - check(file: SimpleDataProvider.testResourceFile, bundle: bundle, line: line) - check(file: SimpleDataProvider.testSymbolGraphFile, bundle: bundle, line: line) - } - } - - struct SimpleDataProvider: DocumentationWorkspaceDataProvider { - let identifier: String = UUID().uuidString - - static let testMarkupFile = URL(fileURLWithPath: "/test.documentation/markup.md") - static let testResourceFile = URL(fileURLWithPath: "/test.documentation/resource.png") - static let testSymbolGraphFile = URL(fileURLWithPath: "/test.documentation/graph.json") - - static var files: [URL: Data] = [ - testMarkupFile: staticDataFromString("markup"), - testResourceFile: staticDataFromString("image"), - testSymbolGraphFile: staticDataFromString("symbols"), - ] - - private static func staticDataFromString(_ string: String) -> Data { - return string.data(using: .utf8)! - } - - static func bundle(_ suffix: String) -> DocumentationBundle { - return DocumentationBundle( - info: DocumentationBundle.Info( - displayName: "Test" + suffix, - id: DocumentationBundle.Identifier(rawValue: "com.example.test" + suffix) - ), - symbolGraphURLs: [testSymbolGraphFile], - markupURLs: [testMarkupFile], - miscResourceURLs: [testResourceFile] - ) - } - - static let bundle1 = bundle("1") - static let bundle2 = bundle("2") - static let bundle3 = bundle("3") - static let bundle4 = bundle("4") - - enum ProviderError: Error { - case missing - } - - func contentsOfURL(_ url: URL) throws -> Data { - guard let data = SimpleDataProvider.files[url] else { - throw ProviderError.missing - } - - return data - } - - var _bundles: [DocumentationBundle] = [] - - func bundles(options: BundleDiscoveryOptions) throws -> [DocumentationBundle] { - // Ignore the bundle discovery options. These test bundles are already built. - return _bundles - } - - init(bundles: [DocumentationBundle]) { - self._bundles = bundles - } - } - - class SimpleWorkspaceDelegate: DocumentationContextDataProviderDelegate { - enum Event: Equatable { - case add(String) - case remove(String) - } - var record: [Event] = [] - - func dataProvider(_ dataProvider: any DocumentationContextDataProvider, didAddBundle bundle: DocumentationBundle) throws { - record.append(.add(bundle.identifier)) - } - - func dataProvider(_ dataProvider: any DocumentationContextDataProvider, didRemoveBundle bundle: DocumentationBundle) throws { - record.append(.remove(bundle.identifier)) - } - } -} diff --git a/Tests/SwiftDocCTests/Infrastructure/ExternalReferenceResolverTests.swift b/Tests/SwiftDocCTests/Infrastructure/ExternalReferenceResolverTests.swift index 2c55f82aa7..07cba25e1d 100644 --- a/Tests/SwiftDocCTests/Infrastructure/ExternalReferenceResolverTests.swift +++ b/Tests/SwiftDocCTests/Infrastructure/ExternalReferenceResolverTests.swift @@ -153,72 +153,6 @@ class ExternalReferenceResolverTests: XCTestCase { ) } - // This test verifies the behavior of a deprecated functionality (changing external documentation sources after registering the documentation) - // Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. - @available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") - func testResolvesReferencesExternallyOnlyWhenFallbackResolversAreSet() async throws { - let workspace = DocumentationWorkspace() - let bundle = try await testBundle(named: "LegacyBundle_DoNotUseInNewTests") - let dataProvider = PrebuiltLocalFileSystemDataProvider(bundles: [bundle]) - try workspace.registerProvider(dataProvider) - let context = try DocumentationContext(dataProvider: workspace) - let bundleIdentifier = bundle.identifier - - let unresolved = UnresolvedTopicReference(topicURL: ValidatedURL(parsingExact: "doc://\(bundleIdentifier)/ArticleThatDoesNotExistInLocally")!) - let parent = ResolvedTopicReference(bundleIdentifier: bundle.id.rawValue, path: "", sourceLanguage: .swift) - - do { - context.configuration.externalDocumentationConfiguration.sources = [:] - context.configuration.convertServiceConfiguration.fallbackResolver = nil - - if case .success = context.resolve(.unresolved(unresolved), in: parent) { - XCTFail("The reference was unexpectedly resolved.") - } - } - - do { - class TestFallbackResolver: ConvertServiceFallbackResolver { - init(bundleID: DocumentationBundle.Identifier) { - resolver.bundleID = bundleID - } - var bundleID: DocumentationBundle.Identifier { - resolver.bundleID - } - private var resolver = TestExternalReferenceResolver() - func resolve(_ reference: SwiftDocC.TopicReference) -> TopicReferenceResolutionResult { - TestExternalReferenceResolver().resolve(reference) - } - func entityIfPreviouslyResolved(with reference: ResolvedTopicReference) -> LinkResolver.ExternalEntity? { - nil - } - func resolve(assetNamed assetName: String) -> DataAsset? { - nil - } - } - - context.configuration.externalDocumentationConfiguration.sources = [:] - context.configuration.convertServiceConfiguration.fallbackResolver = TestFallbackResolver(bundleID: "org.swift.docc.example") - - guard case let .success(resolved) = context.resolve(.unresolved(unresolved), in: parent) else { - XCTFail("The reference was unexpectedly unresolved.") - return - } - - XCTAssertEqual("com.external.testbundle", resolved.bundleIdentifier) - XCTAssertEqual("/externally/resolved/path", resolved.path) - - let expectedURL = URL(string: "doc://com.external.testbundle/externally/resolved/path") - XCTAssertEqual(expectedURL, resolved.url) - - try workspace.unregisterProvider(dataProvider) - context.configuration.externalDocumentationConfiguration.sources = [:] - guard case .failure = context.resolve(.unresolved(unresolved), in: parent) else { - XCTFail("Unexpectedly resolved \(unresolved.topicURL) despite removing a data provider for it") - return - } - } - } - func testLoadEntityForExternalReference() async throws { let (_, context) = try await testBundleAndContext(named: "LegacyBundle_DoNotUseInNewTests", externalResolvers: ["com.external.testbundle" : TestExternalReferenceResolver()]) let identifier = ResolvedTopicReference(bundleID: "com.external.testbundle", path: "/externally/resolved/path", sourceLanguage: .swift) diff --git a/Tests/SwiftDocCTests/Infrastructure/GeneratedDataProvider.swift b/Tests/SwiftDocCTests/Infrastructure/GeneratedDataProvider.swift deleted file mode 100644 index 56360a8793..0000000000 --- a/Tests/SwiftDocCTests/Infrastructure/GeneratedDataProvider.swift +++ /dev/null @@ -1,154 +0,0 @@ -/* - 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 XCTest -@testable import SwiftDocC -import SymbolKit - -// This test verifies the behavior of `GeneratedDataProvider` which is a deprecated type. -// Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. -@available(*, deprecated, message: "Use 'DocumentationContext.InputProvider' instead. This deprecated API will be removed after 6.2 is released") -class GeneratedDataProviderTests: XCTestCase { - - func testGeneratingBundles() throws { - let firstSymbolGraph = SymbolGraph( - metadata: .init( - formatVersion: .init(major: 0, minor: 0, patch: 1), - generator: "unit-test" - ), - module: .init( - name: "FirstModuleName", - platform: .init() - ), - symbols: [], - relationships: [] - ) - var secondSymbolGraph = firstSymbolGraph - secondSymbolGraph.module.name = "SecondModuleName" - - let thirdSymbolGraph = firstSymbolGraph // Another symbol graph with the same module name - - let dataProvider = GeneratedDataProvider( - symbolGraphDataLoader: { - switch $0.lastPathComponent { - case "first.symbols.json": - return try? JSONEncoder().encode(firstSymbolGraph) - case "second.symbols.json": - return try? JSONEncoder().encode(secondSymbolGraph) - case "third.symbols.json": - return try? JSONEncoder().encode(thirdSymbolGraph) - default: - return nil - } - } - ) - - let options = BundleDiscoveryOptions( - infoPlistFallbacks: [ - "CFBundleDisplayName": "Custom Display Name", - "CFBundleIdentifier": "com.test.example", - ], - additionalSymbolGraphFiles: [ - URL(fileURLWithPath: "first.symbols.json"), - URL(fileURLWithPath: "second.symbols.json"), - URL(fileURLWithPath: "third.symbols.json"), - ] - ) - let bundles = try dataProvider.bundles(options: options) - XCTAssertEqual(bundles.count, 1) - let bundle = try XCTUnwrap(bundles.first) - - XCTAssertEqual(bundle.displayName, "Custom Display Name") - XCTAssertEqual(bundle.symbolGraphURLs.map { $0.lastPathComponent }.sorted(), [ - "first.symbols.json", - "second.symbols.json", - "third.symbols.json", - - ]) - XCTAssertEqual(bundle.markupURLs.map { $0.path }.sorted(), [ - "FirstModuleName.md", - "SecondModuleName.md", - // No third file since that symbol graph has the same module name as the first - ]) - - XCTAssertEqual( - try String(data: dataProvider.contentsOfURL(URL(fileURLWithPath: "FirstModuleName.md")), encoding: .utf8), - "# ``FirstModuleName``" - ) - XCTAssertEqual( - try String(data: dataProvider.contentsOfURL(URL(fileURLWithPath: "SecondModuleName.md")), encoding: .utf8), - "# ``SecondModuleName``" - ) - } - - func testGeneratingSingleModuleBundle() throws { - let firstSymbolGraph = SymbolGraph( - metadata: .init( - formatVersion: .init(major: 0, minor: 0, patch: 1), - generator: "unit-test" - ), - module: .init( - name: "FirstModuleName", - platform: .init() - ), - symbols: [], - relationships: [] - ) - - let secondSymbolGraph = firstSymbolGraph // Another symbol graph with the same module name - - let dataProvider = GeneratedDataProvider( - symbolGraphDataLoader: { - switch $0.lastPathComponent { - case "first.symbols.json": - return try? JSONEncoder().encode(firstSymbolGraph) - case "second.symbols.json": - return try? JSONEncoder().encode(secondSymbolGraph) - default: - return nil - } - } - ) - - let options = BundleDiscoveryOptions( - infoPlistFallbacks: [ - "CFBundleDisplayName": "Custom Display Name", - "CFBundleIdentifier": "com.test.example", - ], - additionalSymbolGraphFiles: [ - URL(fileURLWithPath: "first.symbols.json"), - URL(fileURLWithPath: "second.symbols.json"), - ] - ) - let bundles = try dataProvider.bundles(options: options) - XCTAssertEqual(bundles.count, 1) - let bundle = try XCTUnwrap(bundles.first) - - XCTAssertEqual(bundle.displayName, "Custom Display Name") - XCTAssertEqual(bundle.symbolGraphURLs.map { $0.lastPathComponent }.sorted(), [ - "first.symbols.json", - "second.symbols.json", - ]) - XCTAssertEqual(bundle.markupURLs.map { $0.path }.sorted(), [ - "FirstModuleName.md", - // No second file since that symbol graph has the same module name as the first - ]) - - XCTAssertEqual( - try String(data: dataProvider.contentsOfURL(URL(fileURLWithPath: "FirstModuleName.md")), encoding: .utf8), """ - # ``FirstModuleName`` - - @Metadata { - @DisplayName("Custom Display Name") - } - """ - ) - } -} diff --git a/Tests/SwiftDocCTests/Infrastructure/Input Discovery/DocumentationInputsProviderTests.swift b/Tests/SwiftDocCTests/Infrastructure/Input Discovery/DocumentationInputsProviderTests.swift index 5778863dda..2d6edd7e57 100644 --- a/Tests/SwiftDocCTests/Infrastructure/Input Discovery/DocumentationInputsProviderTests.swift +++ b/Tests/SwiftDocCTests/Infrastructure/Input Discovery/DocumentationInputsProviderTests.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2024 Apple Inc. and the Swift project authors + Copyright (c) 2024-2025 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 @@ -14,9 +14,6 @@ import SwiftDocCTestUtilities class DocumentationInputsProviderTests: XCTestCase { - // After 6.2 we can update this test to verify that the input provider discovers the same inputs regardless of FileManagerProtocol - // Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. - @available(*, deprecated, message: "This test uses `LocalFileSystemDataProvider` as a `DocumentationWorkspaceDataProvider` which is deprecated and will be removed after 6.2 is released") func testDiscoversSameFilesAsPreviousImplementation() throws { let folderHierarchy = Folder(name: "one", content: [ Folder(name: "two", content: [ @@ -68,38 +65,26 @@ class DocumentationInputsProviderTests: XCTestCase { Folder(name: "OutsideSearchScope.docc", content: []), ]) + // Prepare the real on-disk file system let tempDirectory = try createTempFolder(content: [folderHierarchy]) - let realProvider = DocumentationContext.InputsProvider(fileManager: FileManager.default) - let testFileSystem = try TestFileSystem(folders: [folderHierarchy]) - let testProvider = DocumentationContext.InputsProvider(fileManager: testFileSystem) - - let options = BundleDiscoveryOptions(fallbackIdentifier: "com.example.test", additionalSymbolGraphFiles: [ - tempDirectory.appendingPathComponent("/path/to/SomethingAdditional.symbols.json") - ]) + // Prepare the test file system + let testFileSystem = try TestFileSystem(folders: []) + try testFileSystem.addFolder(folderHierarchy, basePath: tempDirectory) - let foundPrevImplBundle = try XCTUnwrap(LocalFileSystemDataProvider(rootURL: tempDirectory.appendingPathComponent("/one/two")).bundles(options: options).first) - let (foundRealBundle, _) = try XCTUnwrap(realProvider.inputsAndDataProvider(startingPoint: tempDirectory.appendingPathComponent("/one/two"), options: options)) - - let (foundTestBundle, _) = try XCTUnwrap(testProvider.inputsAndDataProvider(startingPoint: URL(fileURLWithPath: "/one/two"), options: .init( - infoPlistFallbacks: options.infoPlistFallbacks, - // The test file system has a default base URL and needs different URLs for the symbol graph files - additionalSymbolGraphFiles: [ - URL(fileURLWithPath: "/path/to/SomethingAdditional.symbols.json") + for fileManager in [FileManager.default as FileManagerProtocol, testFileSystem as FileManagerProtocol] { + let inputsProvider = DocumentationContext.InputsProvider(fileManager: fileManager) + let options = BundleDiscoveryOptions(fallbackIdentifier: "com.example.test", additionalSymbolGraphFiles: [ + tempDirectory.appendingPathComponent("/path/to/SomethingAdditional.symbols.json") ]) - )) - - for (bundle, relativeBase) in [ - (foundPrevImplBundle, tempDirectory.appendingPathComponent("/one/two/three")), - (foundRealBundle, tempDirectory.appendingPathComponent("/one/two/three")), - (foundTestBundle, URL(fileURLWithPath: "/one/two/three")), - ] { + let (bundle, _) = try XCTUnwrap(inputsProvider.inputsAndDataProvider(startingPoint: tempDirectory.appendingPathComponent("/one/two"), options: options)) + func relativePathString(_ url: URL) -> String { - url.relative(to: relativeBase)!.path + url.relative(to: tempDirectory.appendingPathComponent("/one/two/three"))!.path } XCTAssertEqual(bundle.displayName, "CustomDisplayName") - XCTAssertEqual(bundle.identifier, "com.example.test") + XCTAssertEqual(bundle.id, "com.example.test") XCTAssertEqual(bundle.markupURLs.map(relativePathString).sorted(), [ "Found.docc/CCC.md", "Found.docc/Inner/DDD.md", diff --git a/Tests/SwiftDocCTests/Infrastructure/Symbol Link Resolution/AbsoluteSymbolLinkTests.swift b/Tests/SwiftDocCTests/Infrastructure/Symbol Link Resolution/AbsoluteSymbolLinkTests.swift deleted file mode 100644 index e6386f511f..0000000000 --- a/Tests/SwiftDocCTests/Infrastructure/Symbol Link Resolution/AbsoluteSymbolLinkTests.swift +++ /dev/null @@ -1,870 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - 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 - See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -import Foundation -import XCTest -@testable import SwiftDocC - -// This test uses ``AbsoluteSymbolLink`` which is deprecated. -// Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -class AbsoluteSymbolLinkTests: XCTestCase { - func testCreationOfValidLinks() throws { - let validLinks = [ - "doc://org.swift.ShapeKit/documentation/ShapeKit", - "doc://org.swift.ShapeKit/documentation/ShapeKit/ParentType/Test-swift.class/", - "doc://org.swift.ShapeKit/documentation/ShapeKit/ParentType/Test-swift.class/testFunc()-k2k9d", - ] - - let expectedLinkDescriptions = [ - """ - { - bundleID: 'org.swift.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'ShapeKit', suffix: (none)), - representsModule: true, - basePathComponents: [] - } - """, - """ - { - bundleID: 'org.swift.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'ParentType', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'Test', suffix: (kind: 'swift.class'))] - } - """, - """ - { - bundleID: 'org.swift.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'ParentType', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'Test', suffix: (kind: 'swift.class')), (name: 'testFunc()', suffix: (idHash: 'k2k9d'))] - } - """, - ] - - let absoluteSymbolLinks = validLinks.compactMap(AbsoluteSymbolLink.init(string:)) - - XCTAssertEqual(absoluteSymbolLinks.count, expectedLinkDescriptions.count) - - for (absoluteSymbolLink, expectedDescription) in zip(absoluteSymbolLinks, expectedLinkDescriptions) { - XCTAssertEqual(absoluteSymbolLink.description, expectedDescription) - } - } - - func testCreationOfInvalidLinkWithBadScheme() { - XCTAssertNil( - AbsoluteSymbolLink(string: "dc://org.swift.ShapeKit/documentation/ShapeKit") - ) - - XCTAssertNil( - AbsoluteSymbolLink(string: "http://org.swift.ShapeKit/documentation/ShapeKit") - ) - - XCTAssertNil( - AbsoluteSymbolLink(string: "https://org.swift.ShapeKit/documentation/ShapeKit") - ) - } - - func testCreationOfInvalidLinkWithoutDocumentationPath() { - XCTAssertNil( - AbsoluteSymbolLink(string: "doc://org.swift.ShapeKit/tutorials/ShapeKit") - ) - - XCTAssertNil( - AbsoluteSymbolLink(string: "doc://org.swift.ShapeKit/ShapeKit") - ) - } - - func testCreationOfInvalidLinkWithNoBundleID() { - XCTAssertNil( - AbsoluteSymbolLink(string: "doc:///documentation/ShapeKit") - ) - XCTAssertNil( - AbsoluteSymbolLink(string: "doc:/documentation/ShapeKit") - ) - } - - func testCreationOfInvalidLinkWithBadSuffix() { - XCTAssertNil( - // Empty suffix - AbsoluteSymbolLink(string: "doc://org.swift.ShapeKit/ShapeKit/ParentType/Test-swift.class/testFunc()-") - ) - - XCTAssertNil( - // Empty suffix - AbsoluteSymbolLink(string: "doc://org.swift.ShapeKit/ShapeKit/ParentType/Test-/testFunc()") - ) - - XCTAssertNil( - // Empty suffix - AbsoluteSymbolLink(string: "doc://org.swift.ShapeKit/ShapeKit/ParentType-/Test/testFunc()") - ) - - XCTAssertNil( - // Empty suffix - AbsoluteSymbolLink(string: "doc://org.swift.ShapeKit/ShapeKit-/ParentType/Test/testFunc()") - ) - - XCTAssertNil( - // Invalid type - AbsoluteSymbolLink(string: "doc://org.swift.ShapeKit/ShapeKit/ParentType/Test-swift.class/testFunc()-swift.funny-1s4Rt") - ) - - XCTAssertNil( - // Invalid type - AbsoluteSymbolLink(string: "doc://org.swift.ShapeKit/ShapeKit/ParentType/Test-swift.clss-5f7h9/testFunc()") - ) - } - - func testCreationOfValidLinksFromRenderNode() throws { - let symbolJSON = try String(contentsOf: Bundle.module.url( - forResource: "symbol-with-automatic-see-also-section", withExtension: "json", - subdirectory: "Converter Fixtures")!) - - let renderNode = try RenderNodeTransformer(renderNodeData: symbolJSON.data(using: .utf8)!) - - let references = Array(renderNode.renderNode.references.keys).sorted() - - let absoluteSymbolLinks = references.map(AbsoluteSymbolLink.init(string:)) - let absoluteSymbolLinkDescriptions = absoluteSymbolLinks.map(\.?.description) - - let expectedDescriptions: [String?] = [ - // doc://org.swift.docc.example/documentation/MyKit - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyKit', suffix: (none)), - representsModule: true, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/MyKit-Basics: (This is an article link) - nil, - // doc://org.swift.docc.example/documentation/MyKit/MyClass: - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyClass', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/MyClass/init(_:)-3743d: - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'init(_:)', suffix: (idHash: '3743d'))] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/MyClass/init(_:)-98u07: - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'init(_:)', suffix: (idHash: '98u07'))] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/MyClass/myFunction(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'myFunction()', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/YourClass: - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'YourClass', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/Reference-From-Automatic-SeeAlso-Section-Only: - nil, - // doc://org.swift.docc.example/documentation/Reference-In-Automatic-SeeAlso-And-Fragments: - nil, - // doc://org.swift.docc.example/tutorials/TechnologyX/Tutorial: (Tutorials link): - nil, - // doc://org.swift.docc.example/tutorials/TechnologyX/Tutorial2: - nil, - // doc://org.swift.docc.example/tutorials/TechnologyX/Tutorial4: - nil, - ] - - for (index, expectedDescription) in expectedDescriptions.enumerated() { - XCTAssertEqual( - absoluteSymbolLinkDescriptions[index], - expectedDescription, - """ - Failed to correctly construct link from '\(references[index])' - """ - ) - } - } - - func testCompileSymbolGraphAndValidateLinks() async throws { - let (_, _, context) = try await testBundleAndContext(named: "LegacyBundle_DoNotUseInNewTests") - let expectedDescriptions = [ - // doc://org.swift.docc.example/documentation/FillIntroduced: - """ - { - bundleID: 'org.swift.docc.example', - module: 'FillIntroduced', - topLevelSymbol: (name: 'FillIntroduced', suffix: (none)), - representsModule: true, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/FillIntroduced/iOSMacOSOnly(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'FillIntroduced', - topLevelSymbol: (name: 'iOSMacOSOnly()', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/FillIntroduced/iOSOnlyDeprecated(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'FillIntroduced', - topLevelSymbol: (name: 'iOSOnlyDeprecated()', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/FillIntroduced/iOSOnlyIntroduced(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'FillIntroduced', - topLevelSymbol: (name: 'iOSOnlyIntroduced()', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/FillIntroduced/macCatalystOnlyDeprecated(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'FillIntroduced', - topLevelSymbol: (name: 'macCatalystOnlyDeprecated()', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/FillIntroduced/macCatalystOnlyIntroduced(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'FillIntroduced', - topLevelSymbol: (name: 'macCatalystOnlyIntroduced()', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/FillIntroduced/macOSOnlyDeprecated(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'FillIntroduced', - topLevelSymbol: (name: 'macOSOnlyDeprecated()', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/FillIntroduced/macOSOnlyIntroduced(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'FillIntroduced', - topLevelSymbol: (name: 'macOSOnlyIntroduced()', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/MyKit: - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyKit', suffix: (none)), - representsModule: true, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/MyClass: - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyClass', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/MyClass/init()-33vaw: - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'init()', suffix: (idHash: '33vaw'))] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/MyClass/init()-3743d: - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'init()', suffix: (idHash: '3743d'))] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/MyClass/myFunction(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'myFunction()', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/MyProtocol: - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'MyProtocol', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/MyKit/globalFunction(_:considering:): - """ - { - bundleID: 'org.swift.docc.example', - module: 'MyKit', - topLevelSymbol: (name: 'globalFunction(_:considering:)', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/SideKit: - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideKit', suffix: (none)), - representsModule: true, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideClass: - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideClass', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideClass/Element: - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'Element', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideClass/Element/inherited(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'Element', suffix: (none)), (name: 'inherited()', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideClass/Value(_:): - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'Value(_:)', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideClass/init(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'init()', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideClass/myFunction(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'myFunction()', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideClass/path: - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'path', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideClass/url: - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'url', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideProtocol: - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideProtocol', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideProtocol/func(): - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideProtocol', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'func()', suffix: (none))] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/SideProtocol/func()-2dxqn: - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'SideProtocol', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'func()', suffix: (idHash: '2dxqn'))] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/UncuratedClass: - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'UncuratedClass', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://org.swift.docc.example/documentation/SideKit/UncuratedClass/angle: - """ - { - bundleID: 'org.swift.docc.example', - module: 'SideKit', - topLevelSymbol: (name: 'UncuratedClass', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'angle', suffix: (none))] - } - """, - ] - XCTAssertEqual(expectedDescriptions.count, context.documentationCache.symbolReferences.count) - - let validatedSymbolLinkDescriptions = context.documentationCache.symbolReferences - .map(\.url.absoluteString) - .sorted() - .compactMap(AbsoluteSymbolLink.init(string:)) - .map(\.description) - - XCTAssertEqual(validatedSymbolLinkDescriptions.count, context.documentationCache.symbolReferences.count) - for (symbolLinkDescription, expectedDescription) in zip(validatedSymbolLinkDescriptions, expectedDescriptions) { - XCTAssertEqual(symbolLinkDescription, expectedDescription) - } - } - - func testCompileOverloadedSymbolGraphAndValidateLinks() async throws { - let (_, _, context) = try await testBundleAndContext(named: "OverloadedSymbols") - - let expectedDescriptions = [ - // doc://com.shapes.ShapeKit/documentation/ShapeKit: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'ShapeKit', suffix: (none)), - representsModule: true, - basePathComponents: [] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedByCaseStruct: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedByCaseStruct', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedByCaseStruct/ThirdTestMemberName-5vyx9: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedByCaseStruct', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'ThirdTestMemberName', suffix: (idHash: '5vyx9'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedByCaseStruct/thirdTestMemberNamE-4irjn: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedByCaseStruct', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'thirdTestMemberNamE', suffix: (idHash: '4irjn'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedByCaseStruct/thirdTestMemberName-8x5kx: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedByCaseStruct', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'thirdTestMemberName', suffix: (idHash: '8x5kx'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedByCaseStruct/thirdtestMemberName-u0gl: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedByCaseStruct', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'thirdtestMemberName', suffix: (idHash: 'u0gl'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedEnum', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)-14g8s: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedEnum', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'firstTestMemberName(_:)', suffix: (idHash: '14g8s'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)-14ife: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedEnum', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'firstTestMemberName(_:)', suffix: (idHash: '14ife'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)-14ob0: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedEnum', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'firstTestMemberName(_:)', suffix: (idHash: '14ob0'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)-4ja8m: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedEnum', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'firstTestMemberName(_:)', suffix: (idHash: '4ja8m'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)-88rbf: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedEnum', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'firstTestMemberName(_:)', suffix: (idHash: '88rbf'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)-swift.enum.case: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedEnum', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'firstTestMemberName(_:)', suffix: (kind: 'swift.enum.case'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedParentStruct-1jr3p: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedParentStruct', suffix: (idHash: '1jr3p')), - representsModule: false, - basePathComponents: [] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedParentStruct-1jr3p/fifthTestMember: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedParentStruct', suffix: (idHash: '1jr3p')), - representsModule: false, - basePathComponents: [(name: 'fifthTestMember', suffix: (none))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedProtocol', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-1h173: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedProtocol', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'fourthTestMemberName(test:)', suffix: (idHash: '1h173'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-8iuz7: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedProtocol', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'fourthTestMemberName(test:)', suffix: (idHash: '8iuz7'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-91hxs: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedProtocol', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'fourthTestMemberName(test:)', suffix: (idHash: '91hxs'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-961zx: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedProtocol', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'fourthTestMemberName(test:)', suffix: (idHash: '961zx'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedStruct: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedStruct', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedStruct/secondTestMemberName-swift.property: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedStruct', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'secondTestMemberName', suffix: (kind: 'swift.property'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedStruct/secondTestMemberName-swift.type.property: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'OverloadedStruct', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'secondTestMemberName', suffix: (kind: 'swift.type.property'))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/RegularParent: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'RegularParent', suffix: (none)), - representsModule: false, - basePathComponents: [] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/RegularParent/FourthMember: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'RegularParent', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'FourthMember', suffix: (none))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/RegularParent/firstMember: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'RegularParent', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'firstMember', suffix: (none))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/RegularParent/secondMember(first:second:): - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'RegularParent', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'secondMember(first:second:)', suffix: (none))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/RegularParent/thirdMember: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'RegularParent', suffix: (none)), - representsModule: false, - basePathComponents: [(name: 'thirdMember', suffix: (none))] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/overloadedparentstruct-6a7lx: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'overloadedparentstruct', suffix: (idHash: '6a7lx')), - representsModule: false, - basePathComponents: [] - } - """, - // doc://com.shapes.ShapeKit/documentation/ShapeKit/overloadedparentstruct-6a7lx/fifthTestMember: - """ - { - bundleID: 'com.shapes.ShapeKit', - module: 'ShapeKit', - topLevelSymbol: (name: 'overloadedparentstruct', suffix: (idHash: '6a7lx')), - representsModule: false, - basePathComponents: [(name: 'fifthTestMember', suffix: (none))] - } - """, - ] - - XCTAssertEqual(expectedDescriptions.count, context.documentationCache.count) - - let validatedSymbolLinkDescriptions = context.documentationCache.allReferences - .map(\.url.absoluteString) - .sorted() - .compactMap(AbsoluteSymbolLink.init(string:)) - .map(\.description) - - XCTAssertEqual(validatedSymbolLinkDescriptions.count, context.documentationCache.count) - for (symbolLinkDescription, expectedDescription) in zip(validatedSymbolLinkDescriptions, expectedDescriptions) { - XCTAssertEqual(symbolLinkDescription, expectedDescription) - } - } - - func testLinkComponentStringConversion() async throws { - let (_, _, context) = try await testBundleAndContext(named: "OverloadedSymbols") - - let bundlePathComponents = context.documentationCache.allReferences - .flatMap(\.pathComponents) - - - bundlePathComponents.forEach { component in - let symbolLinkComponent = AbsoluteSymbolLink.LinkComponent(string: component) - // Assert that round-trip conversion doesn't change the string representation - // of the component - XCTAssertEqual(symbolLinkComponent?.asLinkComponentString, component) - } - } -} diff --git a/Tests/SwiftDocCTests/Infrastructure/Symbol Link Resolution/DocCSymbolRepresentableTests.swift b/Tests/SwiftDocCTests/Infrastructure/Symbol Link Resolution/DocCSymbolRepresentableTests.swift deleted file mode 100644 index 614cf9c378..0000000000 --- a/Tests/SwiftDocCTests/Infrastructure/Symbol Link Resolution/DocCSymbolRepresentableTests.swift +++ /dev/null @@ -1,205 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - 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 - See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -import Foundation - -import Foundation -import XCTest -import SymbolKit -@testable import SwiftDocC - -// This test uses ``DocCSymbolRepresentable`` and ``AbsoluteSymbolLink`` which are deprecated. -// Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. -@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released") -class DocCSymbolRepresentableTests: XCTestCase { - func testDisambiguatedByType() async throws { - try await performOverloadSymbolDisambiguationTest( - correctLink: """ - doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedStruct/secondTestMemberName-swift.property - """, - incorrectLinks: [ - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedStruct/secondTestMemberName-swift.method", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedStruct/secondTestMemberName-swift.enum.case", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedStruct/secondTestMemberName", - ], - symbolTitle: "secondTestMemberName", - expectedNumberOfAmbiguousSymbols: 2 - ) - } - - func testOverloadedByCaseInsensitivity() async throws { - try await performOverloadSymbolDisambiguationTest( - correctLink: """ - doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedByCaseStruct/ThirdTestMemberName-5vyx9 - """, - incorrectLinks: [ - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedByCaseStruct/ThirdTestMemberName", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedByCaseStruct/ThirdTestMemberName-swift.enum.case", - ], - symbolTitle: "thirdtestmembername", - expectedNumberOfAmbiguousSymbols: 4 - ) - } - - func testProtocolMemberWithUSRHash() async throws { - try await performOverloadSymbolDisambiguationTest( - correctLink: """ - doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-961zx - """, - incorrectLinks: [ - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthtestmembername(test:)-961zx", - ], - symbolTitle: "fourthTestMemberName(test:)", - expectedNumberOfAmbiguousSymbols: 4 - ) - } - - func testFunctionWithKindIdentifierAndUSRHash() async throws { - try await performOverloadSymbolDisambiguationTest( - correctLink: """ - doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)-14g8s - """, - incorrectLinks: [ - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)-swift.method-14g8s", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)-swift.method", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedEnum/firstTestMemberName(_:)", - ], - symbolTitle: "firstTestMemberName(_:)", - expectedNumberOfAmbiguousSymbols: 6 - ) - } - - func testSymbolWithNoDisambiguation() async throws { - try await performOverloadSymbolDisambiguationTest( - correctLink: """ - doc://com.shapes.ShapeKit/documentation/ShapeKit/RegularParent/firstMember - """, - incorrectLinks: [ - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/firstMember-961zx", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/firstMember-swift.property", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/firstMember-swift.property-961zx", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/RegularParent/firstmember", - ], - symbolTitle: "firstMember", - expectedNumberOfAmbiguousSymbols: 1 - ) - } - - func testAmbiguousProtocolMember() async throws { - try await performOverloadSymbolDisambiguationTest( - correctLink: """ - doc://com.shapes.ShapeKit/documentation/ShapeKit/RegularParent/firstMember - """, - incorrectLinks: [ - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/firstMember-961zx", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/firstMember-swift.property", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/firstMember-swift.property-961zx", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/RegularParent/firstmember", - ], - symbolTitle: "firstMember", - expectedNumberOfAmbiguousSymbols: 1 - ) - } - - func performOverloadSymbolDisambiguationTest( - correctLink: String, - incorrectLinks: [String], - symbolTitle: String, - expectedNumberOfAmbiguousSymbols: Int - ) async throws { - // Build a bundle with an unusual number of overloaded symbols - let (_, _, context) = try await testBundleAndContext(named: "OverloadedSymbols") - - // Collect the overloaded symbols nodes from the built bundle - let ambiguousSymbols = context.documentationCache - .compactMap { $0.value.symbol } - .filter { $0.names.title.lowercased() == symbolTitle.lowercased() } - XCTAssertEqual(ambiguousSymbols.count, expectedNumberOfAmbiguousSymbols) - - // Find the documentation node based on what we expect the correct link to be - let correctReferenceToSelect = try XCTUnwrap( - context.documentationCache.allReferences.first(where: { $0.absoluteString == correctLink }) - ) - let correctSymbolToSelect = try XCTUnwrap( - context.documentationCache[correctReferenceToSelect]?.symbol - ) - - // First confirm the first link does resolve as expected - do { - // Build an absolute symbol link - let absoluteSymbolLinkLastPathComponent = try XCTUnwrap( - AbsoluteSymbolLink(string: correctLink)?.basePathComponents.last - ) - - // Pass it all of the ambiguous symbols to disambiguate between - let selectedSymbols = absoluteSymbolLinkLastPathComponent.disambiguateBetweenOverloadedSymbols( - ambiguousSymbols - ) - - // Assert that it selects a single symbol - XCTAssertEqual(selectedSymbols.count, 1) - - // Assert that the correct symbol is selected - let selectedSymbol = try XCTUnwrap(selectedSymbols.first) - XCTAssertEqual(correctSymbolToSelect, selectedSymbol) - } - - // Now we'll try a couple of imprecise links and verify they don't resolve - try incorrectLinks.forEach { incorrectLink in - let absoluteSymbolLinkLastPathComponent = try XCTUnwrap( - AbsoluteSymbolLink(string: incorrectLink)?.basePathComponents.last - ) - - // Pass it all of the ambiguous symbols to disambiguate between - let selectedSymbols = absoluteSymbolLinkLastPathComponent.disambiguateBetweenOverloadedSymbols( - ambiguousSymbols - ) - - // We expect it to return an empty array since the given - // absolute symbol link isn't correct - XCTAssertTrue(selectedSymbols.isEmpty) - } - } - - func testLinkComponentInitialization() async throws { - let (_, _, context) = try await testBundleAndContext(named: "OverloadedSymbols") - - var count = 0 - for (reference, documentationNode) in context.documentationCache { - guard let symbolLink = AbsoluteSymbolLink(string: reference.absoluteString) else { - continue - } - - // The `asLinkComponent` property of DocCSymbolRepresentable doesn't have the context - // to know what type disambiguation information it should use, so it always includes - // all the available disambiguation information. Because of this, - // we want to restrict to symbols that require both. - guard case .kindAndPreciseIdentifier = symbolLink.basePathComponents.last?.disambiguationSuffix else { - continue - } - - // Create a link component from the symbol information - let linkComponent = try XCTUnwrap(documentationNode.symbol?.asLinkComponent) - - // Confirm that link component we created is the same on the compiler - // created in a full documentation build. - XCTAssertEqual( - linkComponent.asLinkComponentString, - documentationNode.reference.lastPathComponent - ) - - count += 1 - } - - // It's not necessary to disambiguate with both kind and usr. - XCTAssertEqual(count, 0) - } -} diff --git a/Tests/SwiftDocCTests/Infrastructure/SymbolDisambiguationTests.swift b/Tests/SwiftDocCTests/Infrastructure/SymbolDisambiguationTests.swift index 93f2cd2435..797ac40e49 100644 --- a/Tests/SwiftDocCTests/Infrastructure/SymbolDisambiguationTests.swift +++ b/Tests/SwiftDocCTests/Infrastructure/SymbolDisambiguationTests.swift @@ -191,7 +191,7 @@ class SymbolDisambiguationTests: XCTestCase { func testMixedLanguageFramework() async throws { let (bundle, context) = try await testBundleAndContext(named: "MixedLanguageFramework") - var loader = SymbolGraphLoader(bundle: bundle, dataLoader: { try context.contentsOfURL($0, in: $1) }) + var loader = SymbolGraphLoader(bundle: bundle, dataProvider: context.dataProvider) try loader.loadAll() let references = context.linkResolver.localResolver.referencesForSymbols(in: loader.unifiedGraphs, bundle: bundle, context: context).mapValues(\.path) diff --git a/Tests/SwiftDocCTests/Infrastructure/SymbolGraph/SymbolGraphLoaderTests.swift b/Tests/SwiftDocCTests/Infrastructure/SymbolGraph/SymbolGraphLoaderTests.swift index 668dc47c58..39ad7e8482 100644 --- a/Tests/SwiftDocCTests/Infrastructure/SymbolGraph/SymbolGraphLoaderTests.swift +++ b/Tests/SwiftDocCTests/Infrastructure/SymbolGraph/SymbolGraphLoaderTests.swift @@ -1801,9 +1801,7 @@ class SymbolGraphLoaderTests: XCTestCase { return SymbolGraphLoader( bundle: bundle, - dataLoader: { url, _ in - try FileManager.default.contents(of: url) - }, + dataProvider: FileManager.default, symbolGraphTransformer: configureSymbolGraph ) } diff --git a/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift b/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift index 1e51718c0c..85fc99fa13 100644 --- a/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift +++ b/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift @@ -609,7 +609,7 @@ class ConvertActionTests: XCTestCase { start: nil, source: nil, severity: .warning, - summary: "The 'diagnostics.json' digest file is deprecated and will be removed after 6.2 is released. Pass a `--diagnostics-file ` to specify a custom location where DocC will write a diagnostics JSON file with more information.", + summary: "The 'diagnostics.json' digest file is deprecated and will be removed after 6.3 is released. Pass a `--diagnostics-file ` to specify a custom location where DocC will write a diagnostics JSON file with more information.", explanation: nil, notes: [] ), @@ -672,7 +672,7 @@ class ConvertActionTests: XCTestCase { start: nil, source: nil, severity: .warning, - summary: "The 'diagnostics.json' digest file is deprecated and will be removed after 6.2 is released. Pass a `--diagnostics-file ` to specify a custom location where DocC will write a diagnostics JSON file with more information.", + summary: "The 'diagnostics.json' digest file is deprecated and will be removed after 6.3 is released. Pass a `--diagnostics-file ` to specify a custom location where DocC will write a diagnostics JSON file with more information.", explanation: nil, notes: [] ), @@ -764,7 +764,7 @@ class ConvertActionTests: XCTestCase { start: nil, source: nil, severity: .warning, - summary: "The 'diagnostics.json' digest file is deprecated and will be removed after 6.2 is released. Pass a `--diagnostics-file ` to specify a custom location where DocC will write a diagnostics JSON file with more information.", + summary: "The 'diagnostics.json' digest file is deprecated and will be removed after 6.3 is released. Pass a `--diagnostics-file ` to specify a custom location where DocC will write a diagnostics JSON file with more information.", explanation: nil, notes: [] ),