diff --git a/Changelog.md b/Changelog.md index 2ed05c0e..856c6650 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed links to type declarations. + #277 by @Lukas-Stuehrk. - Fixed bug that caused operator implementations to appear in the documentation although they should be omitted because of their lower access level. #264 by @Lukas-Stuehrk diff --git a/Sources/swift-doc/Subcommands/Generate.swift b/Sources/swift-doc/Subcommands/Generate.swift index a209218c..5e69e4aa 100644 --- a/Sources/swift-doc/Subcommands/Generate.swift +++ b/Sources/swift-doc/Subcommands/Generate.swift @@ -74,7 +74,7 @@ extension SwiftDoc { case is Class, is Enumeration, is Structure, is Protocol: pages[route(for: symbol)] = TypePage(module: module, symbol: symbol, baseURL: baseURL, includingChildren: symbolFilter) case let `typealias` as Typealias: - pages[route(for: `typealias`.name)] = TypealiasPage(module: module, symbol: symbol, baseURL: baseURL) + pages[route(for: `typealias`.name)] = TypealiasPage(module: module, symbol: symbol, baseURL: baseURL, includingOtherSymbols: symbolFilter) case is Operator: let operatorPage = OperatorPage(module: module, symbol: symbol, baseURL: baseURL, includingImplementations: symbolFilter) if !operatorPage.implementations.isEmpty { @@ -97,11 +97,11 @@ extension SwiftDoc { symbolsByExternalType[extensionDeclaration.extendedType, default: []] += [symbol] } for (typeName, symbols) in symbolsByExternalType { - pages[route(for: typeName)] = ExternalTypePage(module: module, externalType: typeName, symbols: symbols, baseURL: baseURL) + pages[route(for: typeName)] = ExternalTypePage(module: module, externalType: typeName, symbols: symbols, baseURL: baseURL, includingOtherSymbols: symbolFilter) } for (name, symbols) in globals { - pages[route(for: name)] = GlobalPage(module: module, name: name, symbols: symbols, baseURL: baseURL) + pages[route(for: name)] = GlobalPage(module: module, name: name, symbols: symbols, baseURL: baseURL, includingOtherSymbols: symbolFilter) } guard !pages.isEmpty else { diff --git a/Sources/swift-doc/Supporting Types/Components/Declaration.swift b/Sources/swift-doc/Supporting Types/Components/Declaration.swift index d17e6ad3..95f52f0d 100644 --- a/Sources/swift-doc/Supporting Types/Components/Declaration.swift +++ b/Sources/swift-doc/Supporting Types/Components/Declaration.swift @@ -10,11 +10,13 @@ struct Declaration: Component { var symbol: Symbol var module: Module let baseURL: String + let symbolFilter: (Symbol) -> Bool - init(of symbol: Symbol, in module: Module, baseURL: String) { + init(of symbol: Symbol, in module: Module, baseURL: String, includingOtherSymbols symbolFilter: @escaping (Symbol) -> Bool) { self.symbol = symbol self.module = module self.baseURL = baseURL + self.symbolFilter = symbolFilter } // MARK: - Component @@ -30,9 +32,11 @@ struct Declaration: Component { var html: HypertextLiteral.HTML { let code = symbol.declaration.map { $0.html }.joined() + let html = linkTypes(of: code, for: symbol, in: module, with: baseURL, includingSymbols: symbolFilter) + return #"""
-
\#(unsafeUnescaped: code)
+
\#(unsafeUnescaped: html)
"""# } diff --git a/Sources/swift-doc/Supporting Types/Components/Documentation.swift b/Sources/swift-doc/Supporting Types/Components/Documentation.swift index 3bc07fe0..ec272b1a 100644 --- a/Sources/swift-doc/Supporting Types/Components/Documentation.swift +++ b/Sources/swift-doc/Supporting Types/Components/Documentation.swift @@ -11,11 +11,13 @@ struct Documentation: Component { var symbol: Symbol var module: Module let baseURL: String + let symbolFilter: (Symbol) -> Bool - init(for symbol: Symbol, in module: Module, baseURL: String) { + init(for symbol: Symbol, in module: Module, baseURL: String, includingOtherSymbols symbolFilter: @escaping (Symbol) -> Bool) { self.symbol = symbol self.module = module self.baseURL = baseURL + self.symbolFilter = symbolFilter } // MARK: - Component @@ -39,7 +41,7 @@ struct Documentation: Component { Fragment { "\(documentation.summary!.description.escapingEmojiShortcodes)" } } - Declaration(of: symbol, in: module, baseURL: baseURL) + Declaration(of: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) ForEach(in: documentation.discussionParts) { part in DiscussionPart(part, for: symbol, in: module, baseURL: baseURL) @@ -85,7 +87,7 @@ struct Documentation: Component { var fragments: [HypertextLiteralConvertible] = [] - fragments.append(Declaration(of: symbol, in: module, baseURL: baseURL)) + fragments.append(Declaration(of: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter)) if let summary = documentation.summary { fragments.append(#""" diff --git a/Sources/swift-doc/Supporting Types/Components/Members.swift b/Sources/swift-doc/Supporting Types/Components/Members.swift index f3aa003a..a64ca7cc 100644 --- a/Sources/swift-doc/Supporting Types/Components/Members.swift +++ b/Sources/swift-doc/Supporting Types/Components/Members.swift @@ -9,6 +9,8 @@ struct Members: Component { var module: Module let baseURL: String + let symbolFilter: (Symbol) -> Bool + var members: [Symbol] var typealiases: [Symbol] @@ -20,11 +22,13 @@ struct Members: Component { var genericallyConstrainedMembers: [[GenericRequirement] : [Symbol]] let defaultImplementations: [Symbol] - init(of symbol: Symbol, in module: Module, baseURL: String, symbolFilter: (Symbol) -> Bool) { + init(of symbol: Symbol, in module: Module, baseURL: String, symbolFilter: @escaping (Symbol) -> Bool) { self.symbol = symbol self.module = module self.baseURL = baseURL + self.symbolFilter = symbolFilter + self.members = module.interface.members(of: symbol) .filter { $0.extension?.genericRequirements.isEmpty != false } .filter(symbolFilter) @@ -66,7 +70,7 @@ struct Members: Component { Heading { Code { member.name } } - Documentation(for: member, in: module, baseURL: baseURL) + Documentation(for: member, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) } } } @@ -83,7 +87,7 @@ struct Members: Component { Section { ForEach(in: members) { member in Heading { member.name } - Documentation(for: member, in: module, baseURL: baseURL) + Documentation(for: member, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) } } } @@ -109,7 +113,7 @@ struct Members: Component {

\#(softbreak(member.name))

- \#(Documentation(for: member, in: module, baseURL: baseURL).html) + \#(Documentation(for: member, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html) """# }) @@ -129,7 +133,7 @@ struct Members: Component { \#(members.map { member -> HypertextLiteral.HTML in #"""

\#(softbreak(member.name))

- \#(Documentation(for: member, in: module, baseURL: baseURL).html) + \#(Documentation(for: member, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html) """# }) diff --git a/Sources/swift-doc/Supporting Types/Components/OperatorImplementations.swift b/Sources/swift-doc/Supporting Types/Components/OperatorImplementations.swift index 124acbf4..0e2af2e0 100644 --- a/Sources/swift-doc/Supporting Types/Components/OperatorImplementations.swift +++ b/Sources/swift-doc/Supporting Types/Components/OperatorImplementations.swift @@ -9,13 +9,16 @@ struct OperatorImplementations: Component { var module: Module let baseURL: String + let symbolFilter: (Symbol) -> Bool + var implementations: [Symbol] - init(of symbol: Symbol, in module: Module, baseURL: String, implementations: [Symbol]) { + init(of symbol: Symbol, in module: Module, baseURL: String, implementations: [Symbol], includingOtherSymbols symbolFilter: @escaping (Symbol) -> Bool) { self.symbol = symbol self.module = module self.baseURL = baseURL self.implementations = implementations + self.symbolFilter = symbolFilter } @@ -29,7 +32,7 @@ struct OperatorImplementations: Component { Section { Heading { implementation.name } - Documentation(for: implementation, in: module, baseURL: baseURL) + Documentation(for: implementation, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) } } } @@ -77,7 +80,7 @@ struct OperatorImplementations: Component { \#(heading) \#(unsafeUnescaped: function.genericWhereClause.map({ #"\#($0.escaped)"# }) ?? "") - \#(Documentation(for: implementation, in: module, baseURL: baseURL).html) + \#(Documentation(for: implementation, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html) """# } diff --git a/Sources/swift-doc/Supporting Types/Components/Requirements.swift b/Sources/swift-doc/Supporting Types/Components/Requirements.swift index d7bf8eca..b02c5d37 100644 --- a/Sources/swift-doc/Supporting Types/Components/Requirements.swift +++ b/Sources/swift-doc/Supporting Types/Components/Requirements.swift @@ -8,11 +8,13 @@ struct Requirements: Component { var symbol: Symbol var module: Module let baseURL: String + let symbolFilter: (Symbol) -> Bool - init(of symbol: Symbol, in module: Module, baseURL: String) { + init(of symbol: Symbol, in module: Module, baseURL: String, includingOtherSymbols symbolFilter: @escaping (Symbol) -> Bool) { self.symbol = symbol self.module = module self.baseURL = baseURL + self.symbolFilter = symbolFilter } var sections: [(title: String, requirements: [Symbol])] { @@ -34,7 +36,7 @@ struct Requirements: Component { Section { ForEach(in: section.requirements) { requirement in Heading { requirement.name.escapingEmojiShortcodes } - Documentation(for: requirement, in: module, baseURL: baseURL) + Documentation(for: requirement, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) } } } @@ -57,7 +59,7 @@ struct Requirements: Component {

\#(softbreak(member.name))

- \#(Documentation(for: member, in: module, baseURL: baseURL).html) + \#(Documentation(for: member, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html) """# }) diff --git a/Sources/swift-doc/Supporting Types/Helpers.swift b/Sources/swift-doc/Supporting Types/Helpers.swift index 31002335..2061b395 100644 --- a/Sources/swift-doc/Supporting Types/Helpers.swift +++ b/Sources/swift-doc/Supporting Types/Helpers.swift @@ -20,6 +20,23 @@ public func linkCodeElements(of html: String, for symbol: Symbol, in module: Mod return document.root?.description ?? html } +public func linkTypes(of html: String, for symbol: Symbol, in module: Module, with baseURL: String, includingSymbols symbolFilter: (Symbol) -> Bool) -> String { + let document = try! Document(string: html.description)! + for element in document.search(xpath: "//span[contains(@class,'type')]") { + guard let name = element.content else { continue } + + let candidates = module.interface.symbols(named: "\(symbol.name).\(name)", resolvingTypealiases: true).nonEmpty ?? module.interface.symbols(named: name, resolvingTypealiases: true) + if let candidate = candidates.filter(symbolFilter).filter({ $0 != symbol }).first + { + let a = Element(name: "a") + a["href"] = path(for: candidate, with: baseURL) + element.wrap(inside: a) + } + } + + return document.root?.description ?? html +} + public func sidebar(for html: String) -> String { let toc = Element(name: "ol") @@ -78,3 +95,9 @@ public func softbreak(_ string: String) -> String { return regex.stringByReplacingMatches(in: string, options: [], range: NSRange(string.startIndex.. Bool + + init(module: Module, externalType: String, symbols: [Symbol], baseURL: String, includingOtherSymbols symbolFilter: @escaping (Symbol) -> Bool) { self.module = module self.externalType = externalType self.baseURL = baseURL + self.symbolFilter = symbolFilter self.typealiases = symbols.filter { $0.api is Typealias } self.initializers = symbols.filter { $0.api is Initializer } @@ -49,7 +52,7 @@ struct ExternalTypePage: Page { Heading { Code { member.name } } - Documentation(for: member, in: module, baseURL: baseURL) + Documentation(for: member, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) } } } @@ -77,7 +80,7 @@ struct ExternalTypePage: Page {

\#(softbreak(member.name))

- \#(Documentation(for: member, in: module, baseURL: baseURL).html) + \#(Documentation(for: member, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html) """# }) diff --git a/Sources/swift-doc/Supporting Types/Pages/GlobalPage.swift b/Sources/swift-doc/Supporting Types/Pages/GlobalPage.swift index cd8dad0f..bbb599f3 100644 --- a/Sources/swift-doc/Supporting Types/Pages/GlobalPage.swift +++ b/Sources/swift-doc/Supporting Types/Pages/GlobalPage.swift @@ -8,12 +8,14 @@ struct GlobalPage: Page { let name: String let symbols: [Symbol] let baseURL: String + let symbolFilter: (Symbol) -> Bool - init(module: Module, name: String, symbols: [Symbol], baseURL: String) { + init(module: Module, name: String, symbols: [Symbol], baseURL: String, includingOtherSymbols symbolFilter: @escaping (Symbol) -> Bool) { self.module = module self.name = name self.symbols = symbols self.baseURL = baseURL + self.symbolFilter = symbolFilter } // MARK: - Page @@ -26,7 +28,7 @@ struct GlobalPage: Page { return Document { ForEach(in: symbols) { symbol in Heading { symbol.id.description } - Documentation(for: symbol, in: module, baseURL: baseURL) + Documentation(for: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) } } } @@ -48,7 +50,7 @@ struct GlobalPage: Page { \#(symbols.map { symbol in - Documentation(for: symbol, in: module, baseURL: baseURL).html + Documentation(for: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html }) """# } diff --git a/Sources/swift-doc/Supporting Types/Pages/OperatorPage.swift b/Sources/swift-doc/Supporting Types/Pages/OperatorPage.swift index 740e7829..2d979c5d 100644 --- a/Sources/swift-doc/Supporting Types/Pages/OperatorPage.swift +++ b/Sources/swift-doc/Supporting Types/Pages/OperatorPage.swift @@ -8,13 +8,15 @@ struct OperatorPage: Page { let symbol: Symbol let implementations: [Symbol] let baseURL: String + let symbolFilter: (Symbol) -> Bool - init(module: Module, symbol: Symbol, baseURL: String, includingImplementations symbolFilter: (Symbol) -> Bool) { + init(module: Module, symbol: Symbol, baseURL: String, includingImplementations symbolFilter: @escaping (Symbol) -> Bool) { precondition(symbol.api is Operator) self.module = module self.symbol = symbol self.implementations = module.interface.functionsByOperator[symbol]?.filter(symbolFilter).sorted() ?? [] self.baseURL = baseURL + self.symbolFilter = symbolFilter } // MARK: - Page @@ -27,7 +29,7 @@ struct OperatorPage: Page { return CommonMark.Document { Heading { symbol.id.description } - Documentation(for: symbol, in: module, baseURL: baseURL) + Documentation(for: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) } } @@ -38,8 +40,8 @@ struct OperatorPage: Page { \#(softbreak(symbol.id.description)) - \#(Documentation(for: symbol, in: module, baseURL: baseURL).html) - \#(OperatorImplementations(of: symbol, in: module, baseURL: baseURL, implementations: implementations).html) + \#(Documentation(for: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html) + \#(OperatorImplementations(of: symbol, in: module, baseURL: baseURL, implementations: implementations, includingOtherSymbols: symbolFilter).html) """# } } diff --git a/Sources/swift-doc/Supporting Types/Pages/TypePage.swift b/Sources/swift-doc/Supporting Types/Pages/TypePage.swift index 345acefb..62dbe115 100644 --- a/Sources/swift-doc/Supporting Types/Pages/TypePage.swift +++ b/Sources/swift-doc/Supporting Types/Pages/TypePage.swift @@ -27,10 +27,10 @@ struct TypePage: Page { return CommonMark.Document { Heading { symbol.id.description } - Documentation(for: symbol, in: module, baseURL: baseURL) + Documentation(for: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) Relationships(of: symbol, in: module, baseURL: baseURL, includingChildren: symbolFilter) Members(of: symbol, in: module, baseURL: baseURL, symbolFilter: symbolFilter) - Requirements(of: symbol, in: module, baseURL: baseURL) + Requirements(of: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) } } @@ -41,10 +41,10 @@ struct TypePage: Page { \#(softbreak(symbol.id.description)) - \#(Documentation(for: symbol, in: module, baseURL: baseURL).html) + \#(Documentation(for: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html) \#(Relationships(of: symbol, in: module, baseURL: baseURL, includingChildren: symbolFilter).html) \#(Members(of: symbol, in: module, baseURL: baseURL, symbolFilter: symbolFilter).html) - \#(Requirements(of: symbol, in: module, baseURL: baseURL).html) + \#(Requirements(of: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html) """# } } diff --git a/Sources/swift-doc/Supporting Types/Pages/TypealiasPage.swift b/Sources/swift-doc/Supporting Types/Pages/TypealiasPage.swift index 4c806727..305e14a2 100644 --- a/Sources/swift-doc/Supporting Types/Pages/TypealiasPage.swift +++ b/Sources/swift-doc/Supporting Types/Pages/TypealiasPage.swift @@ -7,12 +7,14 @@ struct TypealiasPage: Page { let module: Module let symbol: Symbol let baseURL: String + let symbolFilter: (Symbol) -> Bool - init(module: Module, symbol: Symbol, baseURL: String) { + init(module: Module, symbol: Symbol, baseURL: String, includingOtherSymbols symbolFilter: @escaping (Symbol) -> Bool) { precondition(symbol.api is Typealias) self.module = module self.symbol = symbol self.baseURL = baseURL + self.symbolFilter = symbolFilter } // MARK: - Page @@ -24,7 +26,7 @@ struct TypealiasPage: Page { var document: CommonMark.Document { Document { Heading { symbol.id.description } - Documentation(for: symbol, in: module, baseURL: baseURL) + Documentation(for: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter) } } @@ -35,7 +37,7 @@ struct TypealiasPage: Page { \#(softbreak(symbol.id.description)) - \#(Documentation(for: symbol, in: module, baseURL: baseURL).html) + \#(Documentation(for: symbol, in: module, baseURL: baseURL, includingOtherSymbols: symbolFilter).html) """# } }