diff --git a/Package.swift b/Package.swift index 544c13b596..6630ecfd4c 100644 --- a/Package.swift +++ b/Package.swift @@ -43,6 +43,7 @@ let package = Package( .target( name: "SwiftDocC", dependencies: [ + .target(name: "Common"), .product(name: "Markdown", package: "swift-markdown"), .product(name: "SymbolKit", package: "swift-docc-symbolkit"), .product(name: "CLMDB", package: "swift-lmdb"), @@ -55,6 +56,7 @@ let package = Package( name: "SwiftDocCTests", dependencies: [ .target(name: "SwiftDocC"), + .target(name: "Common"), .target(name: "TestUtilities"), ], resources: [ @@ -70,6 +72,7 @@ let package = Package( name: "CommandLine", dependencies: [ .target(name: "SwiftDocC"), + .target(name: "Common"), .product(name: "NIOHTTP1", package: "swift-nio", condition: .when(platforms: [.macOS, .iOS, .linux, .android])), .product(name: "ArgumentParser", package: "swift-argument-parser") ], @@ -81,6 +84,7 @@ let package = Package( dependencies: [ .target(name: "CommandLine"), .target(name: "SwiftDocC"), + .target(name: "Common"), .target(name: "TestUtilities"), ], resources: [ @@ -95,6 +99,7 @@ let package = Package( name: "TestUtilities", dependencies: [ .target(name: "SwiftDocC"), + .target(name: "Common"), .product(name: "SymbolKit", package: "swift-docc-symbolkit"), ], swiftSettings: swiftSettings @@ -109,6 +114,25 @@ let package = Package( exclude: ["CMakeLists.txt"], swiftSettings: swiftSettings ), + + // A few common types and core functionality that's useable by all other targets. + .target( + name: "Common", + dependencies: [ + // This target shouldn't have any local dependencies so that all other targets can depend on it. + // We can add dependencies on SymbolKit and Markdown here but they're not needed yet. + ], + swiftSettings: [.swiftLanguageMode(.v6)] + ), + + .testTarget( + name: "CommonTests", + dependencies: [ + .target(name: "Common"), + .target(name: "TestUtilities"), + ], + swiftSettings: [.swiftLanguageMode(.v6)] + ), // Test app for CommandLine .executableTarget( diff --git a/Sources/SwiftDocCUtilities/CMakeLists.txt b/Sources/CommandLine/CMakeLists.txt similarity index 100% rename from Sources/SwiftDocCUtilities/CMakeLists.txt rename to Sources/CommandLine/CMakeLists.txt diff --git a/Sources/Common/SourceLanguage.swift b/Sources/Common/SourceLanguage.swift new file mode 100644 index 0000000000..4483219757 --- /dev/null +++ b/Sources/Common/SourceLanguage.swift @@ -0,0 +1,176 @@ +/* + 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 +*/ + +/// A programming language. +public struct SourceLanguage: Hashable, Codable, Comparable, Sendable { + /// The display name of the programming language. + public var name: String + /// A globally unique identifier for the language. + public var id: String + /// Aliases for the language's identifier. + public var idAliases: [String] = [] + /// The identifier to use for link disambiguation purposes. + public var linkDisambiguationID: String + + /// Creates a new language with a given name and identifier. + /// - Parameters: + /// - name: The display name of the programming language. + /// - id: A globally unique identifier for the language. + /// - idAliases: Aliases for the language's identifier. + /// - linkDisambiguationID: The identifier to use for link disambiguation purposes. + public init(name: String, id: String, idAliases: [String] = [], linkDisambiguationID: String? = nil) { + self.name = name + self.id = id + self.idAliases = idAliases + self.linkDisambiguationID = linkDisambiguationID ?? id + } + + /// Finds the programming language that matches a given identifier, or creates a new one if it finds no existing language. + /// - Parameter id: The identifier of the programming language. + public init(id: String) { + switch id { + case "swift": self = .swift + case "occ", "objc", "objective-c", "c": self = .objectiveC + // FIXME: DocC should display C++ and Objective-C++ as their own languages (https://github.com/swiftlang/swift-docc/issues/767) + case "occ++", "objc++", "objective-c++", "c++": self = .objectiveC + case "javascript": self = .javaScript + case "data": self = .data + case "metal": self = .metal + default: + self.name = id + self.id = id + self.linkDisambiguationID = id + } + } + + /// Finds the programming language that matches a given display name, or creates a new one if it finds no existing language. + /// + /// - Parameter name: The display name of the programming language. + public init(name: String) { + if let knownLanguage = SourceLanguage.firstKnownLanguage(withName: name) { + self = knownLanguage + } else { + self.name = name + + let id = name.lowercased() + self.id = id + self.linkDisambiguationID = id + } + } + + /// Finds the programming language that matches a given display name. + /// + /// If the language name doesn't match any known language, this initializer returns `nil`. + /// + /// - Parameter knownLanguageName: The display name of the programming language. + public init?(knownLanguageName: String) { + if let knownLanguage = SourceLanguage.firstKnownLanguage(withName: knownLanguageName) { + self = knownLanguage + } else { + return nil + } + } + + /// Finds the programming language that matches a given identifier. + /// + /// If the language identifier doesn't match any known language, this initializer returns `nil`. + /// + /// - Parameter knownLanguageIdentifier: The identifier name of the programming language. + public init?(knownLanguageIdentifier: String) { + if let knownLanguage = SourceLanguage.firstKnownLanguage(withIdentifier: knownLanguageIdentifier) { + self = knownLanguage + } else { + return nil + } + } + + private static func firstKnownLanguage(withName name: String) -> SourceLanguage? { + SourceLanguage.knownLanguages.first { $0.name.lowercased() == name.lowercased() } + } + + private static func firstKnownLanguage(withIdentifier id: String) -> SourceLanguage? { + SourceLanguage.knownLanguages.first { knownLanguage in + ([knownLanguage.id] + knownLanguage.idAliases) + .map { $0.lowercased() } + .contains(id) + } + } + + /// The Swift programming language. + public static let swift = SourceLanguage(name: "Swift", id: "swift") + + /// The Objective-C programming language. + public static let objectiveC = SourceLanguage( + name: "Objective-C", + id: "occ", + idAliases: [ + "objective-c", + "objc", + "c", // FIXME: DocC should display C as its own language (github.com/swiftlang/swift-docc/issues/169). + "c++", // FIXME: DocC should display C++ and Objective-C++ as their own languages (https://github.com/swiftlang/swift-docc/issues/767) + "objective-c++", + "objc++", + "occ++", + ], + linkDisambiguationID: "c" + ) + + /// The JavaScript programming language or another language that conforms to the ECMAScript specification. + public static let javaScript = SourceLanguage(name: "JavaScript", id: "javascript") + /// Miscellaneous data, that's not a programming language. + /// + /// For example, use this to represent JSON or XML content. + public static let data = SourceLanguage(name: "Data", id: "data") + /// The Metal programming language. + public static let metal = SourceLanguage(name: "Metal", id: "metal") + + /// The list of programming languages that are known to DocC. + public static let knownLanguages: [SourceLanguage] = [.swift, .objectiveC, .javaScript, .data, .metal] + + enum CodingKeys: CodingKey { + case name + case id + case idAliases + case linkDisambiguationID + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: SourceLanguage.CodingKeys.self) + + let name = try container.decode(String.self, forKey: SourceLanguage.CodingKeys.name) + let id = try container.decode(String.self, forKey: SourceLanguage.CodingKeys.id) + let idAliases = try container.decodeIfPresent([String].self, forKey: SourceLanguage.CodingKeys.idAliases) ?? [] + let linkDisambiguationID = try container.decodeIfPresent(String.self, forKey: SourceLanguage.CodingKeys.linkDisambiguationID) + + self.init(name: name, id: id, idAliases: idAliases, linkDisambiguationID: linkDisambiguationID) + } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: SourceLanguage.CodingKeys.self) + + try container.encode(self.name, forKey: SourceLanguage.CodingKeys.name) + try container.encode(self.id, forKey: SourceLanguage.CodingKeys.id) + if !self.idAliases.isEmpty { + try container.encode(self.idAliases, forKey: SourceLanguage.CodingKeys.idAliases) + } + try container.encode(self.linkDisambiguationID, forKey: SourceLanguage.CodingKeys.linkDisambiguationID) + } + + public static func < (lhs: SourceLanguage, rhs: SourceLanguage) -> Bool { + // Sort Swift before other languages. + if lhs == .swift { + return true + } else if rhs == .swift { + return false + } + // Otherwise, sort by ID for a stable order. + return lhs.id < rhs.id + } +} diff --git a/Sources/SwiftDocC/Infrastructure/Topic Graph/AutomaticCuration.swift b/Sources/SwiftDocC/Infrastructure/Topic Graph/AutomaticCuration.swift index d0deba7971..3a205c89f2 100644 --- a/Sources/SwiftDocC/Infrastructure/Topic Graph/AutomaticCuration.swift +++ b/Sources/SwiftDocC/Infrastructure/Topic Graph/AutomaticCuration.swift @@ -12,7 +12,6 @@ import Foundation import Markdown import SymbolKit - private let automaticSeeAlsoLimit: Int = { ProcessInfo.processInfo.environment["DOCC_AUTOMATIC_SEE_ALSO_LIMIT"].flatMap { Int($0) } ?? 15 }() diff --git a/Sources/SwiftDocC/Model/SourceLanguage.swift b/Sources/SwiftDocC/Model/SourceLanguage.swift index 2866213456..097f35ca62 100644 --- a/Sources/SwiftDocC/Model/SourceLanguage.swift +++ b/Sources/SwiftDocC/Model/SourceLanguage.swift @@ -1,174 +1,13 @@ /* 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 See https://swift.org/CONTRIBUTORS.txt for Swift project authors */ -/// A programming language. -public struct SourceLanguage: Hashable, Codable, Comparable { - /// The display name of the programming language. - public var name: String - /// A globally unique identifier for the language. - public var id: String - /// Aliases for the language's identifier. - public var idAliases: [String] = [] - /// The identifier to use for link disambiguation purposes. - public var linkDisambiguationID: String +public import Common - /// Creates a new language with a given name and identifier. - /// - Parameters: - /// - name: The display name of the programming language. - /// - id: A globally unique identifier for the language. - /// - idAliases: Aliases for the language's identifier. - /// - linkDisambiguationID: The identifier to use for link disambiguation purposes. - public init(name: String, id: String, idAliases: [String] = [], linkDisambiguationID: String? = nil) { - self.name = name - self.id = id - self.idAliases = idAliases - self.linkDisambiguationID = linkDisambiguationID ?? id - } - - /// Finds the programming language that matches a given identifier, or creates a new one if it finds no existing language. - /// - Parameter id: The identifier of the programming language. - public init(id: String) { - switch id { - case "swift": self = .swift - case "occ", "objc", "objective-c", "c": self = .objectiveC - // FIXME: DocC should display C++ and Objective-C++ as their own languages (https://github.com/swiftlang/swift-docc/issues/767) - case "occ++", "objc++", "objective-c++", "c++": self = .objectiveC - case "javascript": self = .javaScript - case "data": self = .data - case "metal": self = .metal - default: - self.name = id - self.id = id - self.linkDisambiguationID = id - } - } - - /// Finds the programming language that matches a given display name, or creates a new one if it finds no existing language. - /// - /// - Parameter name: The display name of the programming language. - public init(name: String) { - if let knownLanguage = SourceLanguage.firstKnownLanguage(withName: name) { - self = knownLanguage - } else { - self.name = name - - let id = name.lowercased() - self.id = id - self.linkDisambiguationID = id - } - } - - /// Finds the programming language that matches a given display name. - /// - /// If the language name doesn't match any known language, this initializer returns `nil`. - /// - /// - Parameter knownLanguageName: The display name of the programming language. - public init?(knownLanguageName: String) { - if let knownLanguage = SourceLanguage.firstKnownLanguage(withName: knownLanguageName) { - self = knownLanguage - } else { - return nil - } - } - - /// Finds the programming language that matches a given identifier. - /// - /// If the language identifier doesn't match any known language, this initializer returns `nil`. - /// - /// - Parameter knownLanguageIdentifier: The identifier name of the programming language. - public init?(knownLanguageIdentifier: String) { - if let knownLanguage = SourceLanguage.firstKnownLanguage(withIdentifier: knownLanguageIdentifier) { - self = knownLanguage - } else { - return nil - } - } - - private static func firstKnownLanguage(withName name: String) -> SourceLanguage? { - SourceLanguage.knownLanguages.first { $0.name.lowercased() == name.lowercased() } - } - - private static func firstKnownLanguage(withIdentifier id: String) -> SourceLanguage? { - SourceLanguage.knownLanguages.first { knownLanguage in - ([knownLanguage.id] + knownLanguage.idAliases) - .map { $0.lowercased() } - .contains(id) - } - } - - /// The Swift programming language. - public static let swift = SourceLanguage(name: "Swift", id: "swift") - - /// The Objective-C programming language. - public static let objectiveC = SourceLanguage( - name: "Objective-C", - id: "occ", - idAliases: [ - "objective-c", - "objc", - "c", // FIXME: DocC should display C as its own language (github.com/swiftlang/swift-docc/issues/169). - "c++", // FIXME: DocC should display C++ and Objective-C++ as their own languages (https://github.com/swiftlang/swift-docc/issues/767) - "objective-c++", - "objc++", - "occ++", - ], - linkDisambiguationID: "c" - ) - - /// The JavaScript programming language or another language that conforms to the ECMAScript specification. - public static let javaScript = SourceLanguage(name: "JavaScript", id: "javascript") - /// Miscellaneous data, that's not a programming language. - /// - /// For example, use this to represent JSON or XML content. - public static let data = SourceLanguage(name: "Data", id: "data") - /// The Metal programming language. - public static let metal = SourceLanguage(name: "Metal", id: "metal") - - /// The list of programming languages that are known to DocC. - public static var knownLanguages: [SourceLanguage] = [.swift, .objectiveC, .javaScript, .data, .metal] - - enum CodingKeys: CodingKey { - case name - case id - case idAliases - case linkDisambiguationID - } - - public init(from decoder: any Decoder) throws { - let container = try decoder.container(keyedBy: SourceLanguage.CodingKeys.self) - - let name = try container.decode(String.self, forKey: SourceLanguage.CodingKeys.name) - let id = try container.decode(String.self, forKey: SourceLanguage.CodingKeys.id) - let idAliases = try container.decodeIfPresent([String].self, forKey: SourceLanguage.CodingKeys.idAliases) ?? [] - let linkDisambiguationID = try container.decodeIfPresent(String.self, forKey: SourceLanguage.CodingKeys.linkDisambiguationID) - - self.init(name: name, id: id, idAliases: idAliases, linkDisambiguationID: linkDisambiguationID) - } - - public func encode(to encoder: any Encoder) throws { - var container = encoder.container(keyedBy: SourceLanguage.CodingKeys.self) - - try container.encode(self.name, forKey: SourceLanguage.CodingKeys.name) - try container.encode(self.id, forKey: SourceLanguage.CodingKeys.id) - try container.encodeIfNotEmpty(self.idAliases, forKey: SourceLanguage.CodingKeys.idAliases) - try container.encode(self.linkDisambiguationID, forKey: SourceLanguage.CodingKeys.linkDisambiguationID) - } - - public static func < (lhs: SourceLanguage, rhs: SourceLanguage) -> Bool { - // Sort Swift before other languages. - if lhs == .swift { - return true - } else if rhs == .swift { - return false - } - // Otherwise, sort by ID for a stable order. - return lhs.id < rhs.id - } -} +public typealias SourceLanguage = Common.SourceLanguage diff --git a/Tests/SwiftDocCTests/Model/SourceLanguageTests.swift b/Tests/CommonTests/SourceLanguageTests.swift similarity index 96% rename from Tests/SwiftDocCTests/Model/SourceLanguageTests.swift rename to Tests/CommonTests/SourceLanguageTests.swift index 734c5c7a1d..55e6d11ce3 100644 --- a/Tests/SwiftDocCTests/Model/SourceLanguageTests.swift +++ b/Tests/CommonTests/SourceLanguageTests.swift @@ -8,7 +8,7 @@ See https://swift.org/CONTRIBUTORS.txt for Swift project authors */ -@testable import SwiftDocC +import Common import XCTest class SourceLanguageTests: XCTestCase {