Skip to content

Commit 3cdca22

Browse files
committed
Merge branch 'main' into enable-external-link-support-by-default
2 parents f2f263b + 926d85b commit 3cdca22

File tree

161 files changed

+4354
-896
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

161 files changed

+4354
-896
lines changed

Package.resolved

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
130130
// Building standalone, so fetch all dependencies remotely.
131131
package.dependencies += [
132132
.package(url: "https://github.com/apple/swift-nio.git", from: "2.53.0"),
133-
.package(url: "https://github.com/apple/swift-markdown.git", branch: "main"),
134-
.package(url: "https://github.com/apple/swift-lmdb.git", branch: "main"),
133+
.package(url: "https://github.com/swiftlang/swift-markdown.git", branch: "main"),
134+
.package(url: "https://github.com/swiftlang/swift-lmdb.git", branch: "main"),
135135
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.2"),
136-
.package(url: "https://github.com/apple/swift-docc-symbolkit.git", branch: "main"),
136+
.package(url: "https://github.com/swiftlang/swift-docc-symbolkit.git", branch: "main"),
137137
.package(url: "https://github.com/apple/swift-crypto.git", from: "3.0.0"),
138-
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.2.0"),
138+
.package(url: "https://github.com/swiftlang/swift-docc-plugin.git", from: "1.2.0"),
139139
]
140140
} else {
141141
// Building in the Swift.org CI system, so rely on local versions of dependencies.

Sources/SwiftDocC/Benchmark/Benchmark.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public class Benchmark: Encodable {
4646
public let platform = "Linux"
4747
#elseif os(Android)
4848
public let platform = "Android"
49+
#elseif os(FreeBSD)
50+
public let platform = "FreeBSD"
4951
#else
5052
public let platform = "unsupported"
5153
#endif

Sources/SwiftDocC/Benchmark/Metrics/PeakMemory.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ extension Benchmark {
6767
) else { return nil }
6868
return Int64(pmcStats.PeakWorkingSetSize)
6969
}
70+
#elseif os(FreeBSD)
71+
private static func peakMemory() -> Int64? {
72+
var usage = rusage()
73+
if (getrusage(RUSAGE_SELF, &usage) == -1) {
74+
return nil
75+
} else {
76+
return Int64(usage.ru_maxrss * 1024)
77+
}
78+
}
7079
#endif
7180

7281
public var result: MetricValue? {

Sources/SwiftDocC/DocumentationService/Convert/Symbol Link Resolution/AbsoluteSymbolLink.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import SymbolKit
1414
/// An absolute link to a symbol.
1515
///
1616
/// You can use this model to validate a symbol link and access its different parts.
17+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
1718
public struct AbsoluteSymbolLink: CustomStringConvertible {
1819
/// The identifier for the documentation bundle this link is from.
1920
public let bundleID: String
@@ -130,8 +131,10 @@ public struct AbsoluteSymbolLink: CustomStringConvertible {
130131
}
131132
}
132133

134+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
133135
extension AbsoluteSymbolLink {
134136
/// A component of a symbol link.
137+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
135138
public struct LinkComponent: CustomStringConvertible {
136139
/// The name of the symbol represented by the link component.
137140
public let name: String
@@ -207,6 +210,7 @@ extension AbsoluteSymbolLink {
207210
}
208211
}
209212

213+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
210214
extension AbsoluteSymbolLink.LinkComponent {
211215
/// A suffix attached to a documentation link to disambiguate it from other symbols
212216
/// that share the same base name.

Sources/SwiftDocC/DocumentationService/Convert/Symbol Link Resolution/DocCSymbolRepresentable.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Foundation
1212
import SymbolKit
1313

1414
/// A type that can be converted to a DocC symbol.
15+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
1516
public protocol DocCSymbolRepresentable: Equatable {
1617
/// A namespaced, unique identifier for the kind of symbol.
1718
///
@@ -31,6 +32,7 @@ public protocol DocCSymbolRepresentable: Equatable {
3132
var title: String { get }
3233
}
3334

35+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
3436
public extension DocCSymbolRepresentable {
3537
/// The given symbol information as a symbol link component.
3638
///
@@ -49,6 +51,7 @@ public extension DocCSymbolRepresentable {
4951
}
5052
}
5153

54+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
5255
extension AbsoluteSymbolLink.LinkComponent {
5356
/// Given an array of symbols that are overloads for the symbol represented
5457
/// by this link component, returns those that are precisely identified by the component.
@@ -135,6 +138,7 @@ extension AbsoluteSymbolLink.LinkComponent {
135138
}
136139
}
137140

141+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
138142
public extension Collection where Element: DocCSymbolRepresentable {
139143
/// Given a collection of colliding symbols, returns the disambiguation suffix required
140144
/// for each symbol to disambiguate it from the others in the collection.
@@ -173,6 +177,7 @@ extension SymbolGraph.Symbol: @retroactive Equatable {}
173177
extension UnifiedSymbolGraph.Symbol: @retroactive Equatable {}
174178
#endif
175179

180+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
176181
extension SymbolGraph.Symbol: DocCSymbolRepresentable {
177182
public var preciseIdentifier: String? {
178183
self.identifier.precise
@@ -191,6 +196,7 @@ extension SymbolGraph.Symbol: DocCSymbolRepresentable {
191196
}
192197
}
193198

199+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
194200
extension UnifiedSymbolGraph.Symbol: DocCSymbolRepresentable {
195201
public var preciseIdentifier: String? {
196202
self.uniqueIdentifier
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2024-2025 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import Foundation
12+
13+
/// A collection of API for link completion.
14+
///
15+
/// An example link completion workflow could look something like this;
16+
/// Assume that there's already an partial link in progress: `First/Second-enum/`
17+
///
18+
/// - First, parse the link into link components using ``parse(linkString:)``.
19+
/// - Second, narrow down the possible symbols to suggest as completion using ``SymbolInformation/matches(_:)``
20+
/// - Third, determine the minimal unique disambiguation for each completion suggestion using ``suggestedDisambiguation(forCollidingSymbols:)``
21+
///
22+
/// > Tip: You can use ``SymbolInformation/hash(uniqueSymbolID:)`` to compute the hashed symbol identifiers needed for steps 2 and 3 above.
23+
@_spi(LinkCompletion) // LinkCompletionTools isn't stable API yet
24+
public enum LinkCompletionTools {
25+
26+
// MARK: Parsing
27+
28+
/// Parses link string into link components; each consisting of a base name and a disambiguation suffix.
29+
///
30+
/// - Parameter linkString: The link string to parse.
31+
/// - Returns: A list of link components, each consisting of a base name and a disambiguation suffix.
32+
public static func parse(linkString: String) -> [(name: String, disambiguation: ParsedDisambiguation)] {
33+
PathHierarchy.PathParser.parse(path: linkString).components.map { pathComponent in
34+
(name: String(pathComponent.name), disambiguation: ParsedDisambiguation(pathComponent.disambiguation) )
35+
}
36+
}
37+
38+
/// A disambiguation suffix for a parsed link component.
39+
public enum ParsedDisambiguation: Equatable {
40+
/// This link component isn't disambiguated.
41+
case none
42+
43+
/// This path component uses a combination of kind and hash disambiguation.
44+
///
45+
/// At least one of `kind` and `hash` will be non-`nil`.
46+
/// It's never _necessary_ to specify both a `kind` and a `hash` to disambiguate a link component, but it's supported for the developer to include both.
47+
case kindAndOrHash(kind: String?, hash: String?)
48+
49+
/// This path component uses type signature information for disambiguation.
50+
///
51+
/// At least one of `parameterTypes` and `returnTypes` will be non-`nil`.
52+
case typeSignature(parameterTypes: [String]?, returnTypes: [String]?)
53+
54+
// This empty-marker case is here because non-frozen enums are only available when Library Evolution is enabled,
55+
// which is not available to Swift Packages without unsafe flags (rdar://78773361).
56+
// This can be removed once that is available and applied to Swift-DocC (rdar://89033233).
57+
@available(*, deprecated, message: "this enum is non-frozen and may be expanded in the future; add a `default` case instead of matching this one")
58+
case _nonFrozenEnum_useDefaultCase
59+
60+
init(_ disambiguation: PathHierarchy.PathComponent.Disambiguation?) {
61+
// This initializer is intended to be internal-only.
62+
switch disambiguation {
63+
case .kindAndHash(let kind, let hash):
64+
self = .kindAndOrHash(
65+
kind: kind.map { String($0) },
66+
hash: hash.map { String($0) }
67+
)
68+
case .typeSignature(let parameterTypes, let returnTypes):
69+
self = .typeSignature(
70+
parameterTypes: parameterTypes?.map { String($0) },
71+
returnTypes: returnTypes?.map { String($0) }
72+
)
73+
case nil:
74+
self = .none
75+
}
76+
}
77+
78+
/// A string representation of the disambiguation.
79+
public var suffix: String {
80+
typealias Disambiguation = PathHierarchy.DisambiguationContainer.Disambiguation
81+
82+
switch self {
83+
case .kindAndOrHash(let kind?, nil):
84+
return Disambiguation.kind(kind).makeSuffix()
85+
case .kindAndOrHash(nil, let hash?):
86+
return Disambiguation.hash(hash).makeSuffix()
87+
case .kindAndOrHash(let kind?, let hash?): // This is never necessary but a developer could redundantly write it in a parsed link
88+
return Disambiguation.kind(kind).makeSuffix() + Disambiguation.hash(hash).makeSuffix()
89+
90+
case .typeSignature(let parameterTypes?, nil):
91+
return Disambiguation.parameterTypes(parameterTypes).makeSuffix()
92+
case .typeSignature(nil, let returnTypes?):
93+
return Disambiguation.returnTypes(returnTypes).makeSuffix()
94+
case .typeSignature(let parameterTypes?, let returnTypes?):
95+
return Disambiguation.mixedTypes(parameterTypes: parameterTypes, returnTypes: returnTypes).makeSuffix()
96+
97+
// Unexpected error cases
98+
case .kindAndOrHash(kind: nil, hash: nil):
99+
assertionFailure("Parsed `.kindAndOrHash` disambiguation missing both kind and hash should use `.none` instead. This is a logic bug.")
100+
return Disambiguation.none.makeSuffix()
101+
case .typeSignature(parameterTypes: nil, returnTypes: nil):
102+
assertionFailure("Parsed `.typeSignature` disambiguation missing both parameter types and return types should use `.none` instead. This is a logic bug.")
103+
return Disambiguation.none.makeSuffix()
104+
105+
// Since this is within DocC we want to have an error if we don't handle new future cases.
106+
case .none, ._nonFrozenEnum_useDefaultCase:
107+
return Disambiguation.none.makeSuffix()
108+
}
109+
}
110+
}
111+
112+
/// Suggests the minimal most readable disambiguation string for each symbol with the same name.
113+
/// - Parameters:
114+
/// - collidingSymbols: A list of symbols that all have the same name.
115+
/// - Returns: A collection of disambiguation strings in the same order as the provided symbol information.
116+
///
117+
/// - Important: It's the callers responsibility to create symbol information that matches what the compilers emit in symbol graph files.
118+
/// If there are mismatches, DocC may suggest disambiguation that won't resolve with the real compiler emitted symbol data.
119+
public static func suggestedDisambiguation(forCollidingSymbols collidingSymbols: [SymbolInformation]) -> [String] {
120+
// Track the order of the symbols so that the disambiguations can be ordered to align with their respective symbols.
121+
var identifiersInOrder: [ResolvedIdentifier] = []
122+
identifiersInOrder.reserveCapacity(collidingSymbols.count)
123+
124+
// Construct a disambiguation container with all the symbol's information.
125+
var disambiguationContainer = PathHierarchy.DisambiguationContainer()
126+
for symbol in collidingSymbols {
127+
let (node, identifier) = Self._makeNodeAndIdentifier(name: "unused")
128+
identifiersInOrder.append(identifier)
129+
130+
disambiguationContainer.add(
131+
node,
132+
kind: symbol.kind,
133+
hash: symbol.symbolIDHash,
134+
parameterTypes: symbol.parameterTypes,
135+
returnTypes: symbol.returnTypes
136+
)
137+
}
138+
139+
let disambiguatedValues = disambiguationContainer.disambiguatedValues()
140+
// Compute the minimal suggested disambiguation for each symbol and return their string suffixes in the original symbol's order.
141+
return identifiersInOrder.map { identifier in
142+
guard let (_, disambiguation) = disambiguatedValues.first(where: { $0.value.identifier == identifier }) else {
143+
fatalError("Each node in the `DisambiguationContainer` should always have a entry in the `disambiguatedValues`")
144+
}
145+
return disambiguation.makeSuffix()
146+
}
147+
}
148+
149+
/// Information about a symbol for link completion purposes.
150+
///
151+
/// > Note:
152+
/// > This symbol information doesn't include the name.
153+
/// > It's the callers responsibility to group symbols by their name.
154+
///
155+
/// > Important:
156+
/// > It's the callers responsibility to create symbol information that matches what the compilers emit in symbol graph files.
157+
/// > If there are mismatches, DocC may suggest disambiguation that won't resolve with the real compiler emitted symbol data.
158+
public struct SymbolInformation {
159+
/// The kind of symbol, for example `"class"` or `"func.op`.
160+
///
161+
/// ## See Also
162+
/// - ``/SymbolKit/SymbolGraph/Symbol/KindIdentifier``
163+
public var kind: String
164+
/// A hash of the symbol's unique identifier.
165+
///
166+
/// ## See Also
167+
/// - ``hash(uniqueSymbolID:)``
168+
public var symbolIDHash: String
169+
/// The type names of this symbol's parameters, or `nil` if this symbol has no function signature information.
170+
///
171+
/// A function without parameters represents i
172+
public var parameterTypes: [String]?
173+
/// The type names of this symbol's return value, or `nil` if this symbol has no function signature information.
174+
public var returnTypes: [String]?
175+
176+
public init(
177+
kind: String,
178+
symbolIDHash: String,
179+
parameterTypes: [String]? = nil,
180+
returnTypes: [String]? = nil
181+
) {
182+
self.kind = kind
183+
self.symbolIDHash = symbolIDHash
184+
self.parameterTypes = parameterTypes
185+
self.returnTypes = returnTypes
186+
}
187+
188+
/// Creates a hashed representation of a symbol's unique identifier.
189+
///
190+
/// # See Also
191+
/// - ``symbolIDHash``
192+
public static func hash(uniqueSymbolID: String) -> String {
193+
uniqueSymbolID.stableHashString
194+
}
195+
196+
// MARK: Filtering
197+
198+
/// Returns a Boolean value that indicates whether this symbol information matches the parsed disambiguation from one of the link components of a parsed link string.
199+
public func matches(_ parsedDisambiguation: LinkCompletionTools.ParsedDisambiguation) -> Bool {
200+
guard let disambiguation = PathHierarchy.PathComponent.Disambiguation(parsedDisambiguation) else {
201+
return true // No disambiguation to match against.
202+
}
203+
204+
var disambiguationContainer = PathHierarchy.DisambiguationContainer()
205+
let (node, _) = LinkCompletionTools._makeNodeAndIdentifier(name: "unused")
206+
207+
disambiguationContainer.add(
208+
node,
209+
kind: self.kind,
210+
hash: self.symbolIDHash,
211+
parameterTypes: self.parameterTypes,
212+
returnTypes: self.returnTypes
213+
)
214+
215+
do {
216+
return try disambiguationContainer.find(disambiguation) != nil
217+
} catch {
218+
return false
219+
}
220+
}
221+
}
222+
}
223+
224+
private extension PathHierarchy.PathComponent.Disambiguation {
225+
init?(_ parsedDisambiguation: LinkCompletionTools.ParsedDisambiguation) {
226+
switch parsedDisambiguation {
227+
case .kindAndOrHash(let kind, let hash):
228+
self = .kindAndHash(kind: kind.map { $0[...] }, hash: hash.map { $0[...] })
229+
230+
case .typeSignature(let parameterTypes, let returnTypes):
231+
self = .typeSignature(parameterTypes: parameterTypes?.map { $0[...] }, returnTypes: returnTypes?.map { $0[...] })
232+
233+
// Since this is within DocC we want to have an error if we don't handle new future cases.
234+
case .none, ._nonFrozenEnum_useDefaultCase:
235+
return nil
236+
}
237+
}
238+
}

0 commit comments

Comments
 (0)