diff --git a/Sources/DocCHTML/CMakeLists.txt b/Sources/DocCHTML/CMakeLists.txt index 3af4f6d1f..590c395ef 100644 --- a/Sources/DocCHTML/CMakeLists.txt +++ b/Sources/DocCHTML/CMakeLists.txt @@ -9,6 +9,7 @@ See https://swift.org/LICENSE.txt for license information add_library(DocCHTML STATIC LinkProvider.swift + MarkdownRenderer+Availability.swift MarkdownRenderer.swift WordBreak.swift XMLNode+element.swift) diff --git a/Sources/DocCHTML/MarkdownRenderer+Availability.swift b/Sources/DocCHTML/MarkdownRenderer+Availability.swift new file mode 100644 index 000000000..eb302aa90 --- /dev/null +++ b/Sources/DocCHTML/MarkdownRenderer+Availability.swift @@ -0,0 +1,76 @@ +/* + This source file is part of the Swift.org open source project + + Copyright (c) 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 +*/ + +#if canImport(FoundationXML) +// TODO: Consider other HTML rendering options as a future improvement (rdar://165755530) +package import FoundationXML +#else +package import Foundation +#endif + +package extension MarkdownRenderer { + /// Information about the versions that a piece of API is available for a given platform. + struct AvailabilityInfo { + /// The name of the platform that this information applies to. + package var name: String + /// The pre-formatted version string that describes the version that this API was introduced in for this platform. + package var introduced: String? + /// The pre-formatted version string that describes the version that this API was deprecated in for this platform. + package var deprecated: String? + /// A Boolean value indicating if the platform is currently in beta. + package var isBeta: Bool + + package init(name: String, introduced: String? = nil, deprecated: String? = nil, isBeta: Bool) { + self.name = name + self.introduced = introduced + self.deprecated = deprecated + self.isBeta = isBeta + } + } + + /// Creates an HTML element that describes the versions that a piece of API is available for the platforms described in the given availability information. + func availability(_ info: [AvailabilityInfo]) -> XMLNode { + let items: [XMLNode] = info.map { + var text = $0.name + + let description: String + if let introduced = $0.introduced { + if let deprecated = $0.deprecated{ + text += " \(introduced)–\(deprecated)" + description = "Introduced in \($0.name) \(introduced) and deprecated in \($0.name) \(deprecated)" + } else { + text += " \(introduced)+" + description = "Available on \(introduced) and later" + } + } else { + description = "Available on \($0.name)" + } + + var attributes = [ + "role": "text", + "aria-label": "\(text), \(description)", + "title": description + ] + if $0.isBeta { + attributes["class"] = "beta" + } else if $0.deprecated != nil { + attributes["class"] = "deprecated" + } + + return .element(named: "li", children: [.text(text)], attributes: goal == .richness ? attributes : [:]) + } + + return .element( + named: "ul", + children: items, + attributes: ["id": "availability"] + ) + } +} diff --git a/Tests/DocCHTMLTests/MarkdownRenderer+PageElementsTests.swift b/Tests/DocCHTMLTests/MarkdownRenderer+PageElementsTests.swift new file mode 100644 index 000000000..a8a2619e3 --- /dev/null +++ b/Tests/DocCHTMLTests/MarkdownRenderer+PageElementsTests.swift @@ -0,0 +1,117 @@ +/* + This source file is part of the Swift.org open source project + + Copyright (c) 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 +*/ + +#if canImport(FoundationXML) +// TODO: Consider other HTML rendering options as a future improvement (rdar://165755530) +import FoundationXML +import FoundationEssentials +#else +import Foundation +#endif + +import Testing +import DocCHTML +import Markdown + +struct MarkdownRenderer_PageElementsTests { + @Test(arguments: RenderGoal.allCases) + func testRenderAvailability(goal: RenderGoal) { + let availability = makeRenderer(goal: goal).availability([ + .init(name: "First", introduced: "1.2", deprecated: "3.4", isBeta: false), + .init(name: "Second", introduced: "1.2.3", isBeta: false), + .init(name: "Third", introduced: "4.5", isBeta: true), + ]) + switch goal { + case .richness: + availability.assertMatches(prettyFormatted: true, expectedXMLString: """ +