Skip to content

Commit fe51e34

Browse files
authored
Merge branch 'main' into same-shape
2 parents 6d81acb + 55fbcce commit fe51e34

File tree

167 files changed

+6435
-2976
lines changed

Some content is hidden

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

167 files changed

+6435
-2976
lines changed

Package.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:5.9
1+
// swift-tools-version:6.0
22
/*
33
This source file is part of the Swift.org open source project
44

@@ -15,6 +15,8 @@ import class Foundation.ProcessInfo
1515
let swiftSettings: [SwiftSetting] = [
1616
.unsafeFlags(["-Xfrontend", "-warn-long-expression-type-checking=1000"], .when(configuration: .debug)),
1717

18+
.swiftLanguageMode(.v5),
19+
1820
.enableUpcomingFeature("ConciseMagicFile"), // SE-0274: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0274-magic-file.md
1921
.enableUpcomingFeature("ExistentialAny"), // SE-0335: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0335-existential-any.md
2022
.enableUpcomingFeature("InternalImportsByDefault"), // SE-0409: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md
@@ -23,8 +25,8 @@ let swiftSettings: [SwiftSetting] = [
2325
let package = Package(
2426
name: "SwiftDocC",
2527
platforms: [
26-
.macOS(.v12),
27-
.iOS(.v15)
28+
.macOS(.v13),
29+
.iOS(.v16)
2830
],
2931
products: [
3032
.library(

Sources/SwiftDocC/Checker/Checkers/AbstractContainsFormattedTextOnly.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2025 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -14,6 +14,7 @@ public import Markdown
1414
/**
1515
A document's abstract may only contain formatted text. Images and links are not allowed.
1616
*/
17+
@available(*, deprecated, message: "This check is no longer applicable. This deprecated API will be removed after 6.4 is released")
1718
public struct AbstractContainsFormattedTextOnly: Checker {
1819
public var problems: [Problem] = [Problem]()
1920
private var sourceFile: URL?
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 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+
internal import Foundation
12+
internal import Markdown
13+
14+
/**
15+
Code blocks can have a `nocopy` option after the \`\`\`, in the language line.
16+
`nocopy` can be immediately after the \`\`\` or after a specified language and a comma (`,`).
17+
*/
18+
internal struct InvalidCodeBlockOption: Checker {
19+
var problems = [Problem]()
20+
21+
/// Parsing options for code blocks
22+
private let knownOptions = RenderBlockContent.CodeBlockOptions.knownOptions
23+
24+
private var sourceFile: URL?
25+
26+
/// Creates a new checker that detects documents with multiple titles.
27+
///
28+
/// - Parameter sourceFile: The URL to the documentation file that the checker checks.
29+
init(sourceFile: URL?) {
30+
self.sourceFile = sourceFile
31+
}
32+
33+
mutating func visitCodeBlock(_ codeBlock: CodeBlock) {
34+
let (lang, tokens) = RenderBlockContent.CodeBlockOptions.tokenizeLanguageString(codeBlock.language)
35+
36+
func matches(token: RenderBlockContent.CodeBlockOptions.OptionName, value: String?) {
37+
guard token == .unknown, let value = value else { return }
38+
39+
let matches = NearMiss.bestMatches(for: knownOptions, against: value)
40+
41+
if !matches.isEmpty {
42+
let diagnostic = Diagnostic(source: sourceFile, severity: .warning, range: codeBlock.range, identifier: "org.swift.docc.InvalidCodeBlockOption", summary: "Unknown option \(value.singleQuoted) in code block.")
43+
let possibleSolutions = matches.map { candidate in
44+
Solution(
45+
summary: "Replace \(value.singleQuoted) with \(candidate.singleQuoted).",
46+
replacements: []
47+
)
48+
}
49+
problems.append(Problem(diagnostic: diagnostic, possibleSolutions: possibleSolutions))
50+
} else if lang == nil {
51+
let diagnostic = Diagnostic(source: sourceFile, severity: .warning, range: codeBlock.range, identifier: "org.swift.docc.InvalidCodeBlockOption", summary: "Unknown option \(value.singleQuoted) in code block.")
52+
let possibleSolutions =
53+
Solution(
54+
summary: "If \(value.singleQuoted) is the language for this code block, then write \(value.singleQuoted) as the first option.",
55+
replacements: []
56+
)
57+
problems.append(Problem(diagnostic: diagnostic, possibleSolutions: [possibleSolutions]))
58+
}
59+
}
60+
61+
func validateArrayIndices(token: RenderBlockContent.CodeBlockOptions.OptionName, value: String?) {
62+
guard token == .highlight || token == .strikeout, let value = value else { return }
63+
// code property ends in a newline. this gives us a bogus extra line.
64+
let lineCount: Int = codeBlock.code.split(omittingEmptySubsequences: false, whereSeparator: { $0.isNewline }).count - 1
65+
66+
let indices = RenderBlockContent.CodeBlockOptions.parseCodeBlockOptionsArray(value)
67+
68+
if !value.isEmpty, indices.isEmpty {
69+
let diagnostic = Diagnostic(source: sourceFile, severity: .warning, range: codeBlock.range, identifier: "org.swift.docc.InvalidCodeBlockOption", summary: "Could not parse \(token.rawValue.singleQuoted) indices from \(value.singleQuoted). Expected an integer (e.g. 3) or an array (e.g. [1, 3, 5])")
70+
problems.append(Problem(diagnostic: diagnostic, possibleSolutions: []))
71+
return
72+
}
73+
74+
let invalid = indices.filter { $0 < 1 || $0 > lineCount }
75+
guard !invalid.isEmpty else { return }
76+
77+
let diagnostic = Diagnostic(source: sourceFile, severity: .warning, range: codeBlock.range, identifier: "org.swift.docc.InvalidCodeBlockOption", summary: "Invalid \(token.rawValue.singleQuoted) index\(invalid.count == 1 ? "" : "es") in \(value.singleQuoted) for a code block with \(lineCount) line\(lineCount == 1 ? "" : "s"). Valid range is 1...\(lineCount).")
78+
let solutions: [Solution] = {
79+
if invalid.contains(where: {$0 == lineCount + 1}) {
80+
return [Solution(
81+
summary: "If you intended the last line, change '\(lineCount + 1)' to \(lineCount).",
82+
replacements: []
83+
)]
84+
}
85+
return []
86+
}()
87+
problems.append(Problem(diagnostic: diagnostic, possibleSolutions: solutions))
88+
}
89+
90+
for (token, value) in tokens {
91+
matches(token: token, value: value)
92+
validateArrayIndices(token: token, value: value)
93+
}
94+
// check if first token (lang) might be a typo
95+
matches(token: .unknown, value: lang)
96+
}
97+
}

Sources/SwiftDocC/Converter/DocumentationContextConverter.swift

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ public class DocumentationContextConverter {
2020
/// The context the converter uses to resolve references it finds in the documentation node's content.
2121
let context: DocumentationContext
2222

23-
/// The bundle that contains the content from which the documentation node originated.
24-
let bundle: DocumentationBundle
25-
2623
/// A context that contains common pre-rendered pieces of content.
2724
let renderContext: RenderContext
2825

@@ -43,12 +40,11 @@ public class DocumentationContextConverter {
4340
/// The remote source control repository where the documented module's source is hosted.
4441
let sourceRepository: SourceRepository?
4542

46-
/// Creates a new node converter for the given bundle and context.
43+
/// Creates a new node converter for the given context.
4744
///
4845
/// The converter uses bundle and context to resolve references to other documentation and describe the documentation hierarchy.
4946
///
5047
/// - Parameters:
51-
/// - bundle: The bundle that contains the content from which the documentation node originated.
5248
/// - context: The context that the converter uses to to resolve references it finds in the documentation node's content.
5349
/// - renderContext: A context that contains common pre-rendered pieces of content.
5450
/// - emitSymbolSourceFileURIs: Whether the documentation converter should include
@@ -61,22 +57,39 @@ public class DocumentationContextConverter {
6157
/// - sourceRepository: The source repository where the documentation's sources are hosted.
6258
/// - symbolIdentifiersWithExpandedDocumentation: A list of symbol IDs that have version of their documentation page with more content that a renderer can link to.
6359
public init(
64-
bundle: DocumentationBundle,
6560
context: DocumentationContext,
6661
renderContext: RenderContext,
6762
emitSymbolSourceFileURIs: Bool = false,
6863
emitSymbolAccessLevels: Bool = false,
6964
sourceRepository: SourceRepository? = nil,
7065
symbolIdentifiersWithExpandedDocumentation: [String]? = nil
7166
) {
72-
self.bundle = bundle
7367
self.context = context
7468
self.renderContext = renderContext
7569
self.shouldEmitSymbolSourceFileURIs = emitSymbolSourceFileURIs
7670
self.shouldEmitSymbolAccessLevels = emitSymbolAccessLevels
7771
self.sourceRepository = sourceRepository
7872
self.symbolIdentifiersWithExpandedDocumentation = symbolIdentifiersWithExpandedDocumentation
7973
}
74+
@available(*, deprecated, renamed: "init(context:renderContext:emitSymbolSourceFileURIs:emitSymbolAccessLevels:sourceRepository:symbolIdentifiersWithExpandedDocumentation:)", message: "Use 'init(context:renderContext:emitSymbolSourceFileURIs:emitSymbolAccessLevels:sourceRepository:symbolIdentifiersWithExpandedDocumentation:)' instead. This deprecated API will be removed after 6.4 is released.")
75+
public convenience init(
76+
bundle _: DocumentationBundle,
77+
context: DocumentationContext,
78+
renderContext: RenderContext,
79+
emitSymbolSourceFileURIs: Bool = false,
80+
emitSymbolAccessLevels: Bool = false,
81+
sourceRepository: SourceRepository? = nil,
82+
symbolIdentifiersWithExpandedDocumentation: [String]? = nil
83+
) {
84+
self.init(
85+
context: context,
86+
renderContext: renderContext,
87+
emitSymbolSourceFileURIs: emitSymbolSourceFileURIs,
88+
emitSymbolAccessLevels: emitSymbolAccessLevels,
89+
sourceRepository: sourceRepository,
90+
symbolIdentifiersWithExpandedDocumentation: symbolIdentifiersWithExpandedDocumentation
91+
)
92+
}
8093

8194
/// Converts a documentation node to a render node.
8295
///
@@ -91,7 +104,6 @@ public class DocumentationContextConverter {
91104

92105
var translator = RenderNodeTranslator(
93106
context: context,
94-
bundle: bundle,
95107
identifier: node.reference,
96108
renderContext: renderContext,
97109
emitSymbolSourceFileURIs: shouldEmitSymbolSourceFileURIs,

Sources/SwiftDocC/Converter/DocumentationNodeConverter.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,19 @@ public struct DocumentationNodeConverter {
1515
/// The context the converter uses to resolve references it finds in the documentation node's content.
1616
let context: DocumentationContext
1717

18-
/// The bundle that contains the content from which the documentation node originated.
19-
let bundle: DocumentationBundle
20-
21-
/// Creates a new node converter for the given bundle and context.
18+
/// Creates a new node converter for the given context.
2219
///
23-
/// The converter uses bundle and context to resolve references to other documentation and describe the documentation hierarchy.
20+
/// The converter uses context to resolve references to other documentation and describe the documentation hierarchy.
2421
///
2522
/// - Parameters:
26-
/// - bundle: The bundle that contains the content from which the documentation node originated.
2723
/// - context: The context that the converter uses to to resolve references it finds in the documentation node's content.
28-
public init(bundle: DocumentationBundle, context: DocumentationContext) {
29-
self.bundle = bundle
24+
public init(context: DocumentationContext) {
3025
self.context = context
3126
}
27+
@available(*, deprecated, renamed: "init(context:)", message: "Use 'init(context:)' instead. This deprecated API will be removed after 6.4 is released.")
28+
public init(bundle _: DocumentationBundle, context: DocumentationContext) {
29+
self.init(context: context)
30+
}
3231

3332
/// Converts a documentation node to a render node.
3433
///
@@ -37,7 +36,7 @@ public struct DocumentationNodeConverter {
3736
/// - node: The documentation node to convert.
3837
/// - Returns: The render node representation of the documentation node.
3938
public func convert(_ node: DocumentationNode) -> RenderNode {
40-
var translator = RenderNodeTranslator(context: context, bundle: bundle, identifier: node.reference)
39+
var translator = RenderNodeTranslator(context: context, identifier: node.reference)
4140
return translator.visit(node.semantic) as! RenderNode
4241
}
4342
}

Sources/SwiftDocC/DocumentationService/Convert/ConvertService.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public struct ConvertService: DocumentationService {
158158
let context = try await DocumentationContext(bundle: bundle, dataProvider: dataProvider, configuration: configuration)
159159

160160
// Precompute the render context
161-
let renderContext = RenderContext(documentationContext: context, bundle: bundle)
161+
let renderContext = RenderContext(documentationContext: context)
162162

163163
let symbolIdentifiersMeetingRequirementsForExpandedDocumentation: [String]? = request.symbolIdentifiersWithExpandedDocumentation?.compactMap { identifier, expandedDocsRequirement in
164164
guard let documentationNode = context.documentationCache[identifier] else {
@@ -168,7 +168,6 @@ public struct ConvertService: DocumentationService {
168168
return documentationNode.meetsExpandedDocumentationRequirements(expandedDocsRequirement) ? identifier : nil
169169
}
170170
let converter = DocumentationContextConverter(
171-
bundle: bundle,
172171
context: context,
173172
renderContext: renderContext,
174173
emitSymbolSourceFileURIs: request.emitSymbolSourceFileURIs,
@@ -243,12 +242,11 @@ public struct ConvertService: DocumentationService {
243242
.compactMap { (value, isDocumentationExtensionContent) -> (ResolvedTopicReference, RenderReferenceStore.TopicContent)? in
244243
let (topicReference, article) = value
245244

246-
let bundle = context.bundle
247-
guard bundle.id == topicReference.bundleID else { return nil }
248-
let renderer = DocumentationContentRenderer(documentationContext: context, bundle: bundle)
245+
guard context.inputs.id == topicReference.bundleID else { return nil }
246+
let renderer = DocumentationContentRenderer(context: context)
249247

250248
let documentationNodeKind: DocumentationNode.Kind = isDocumentationExtensionContent ? .unknownSymbol : .article
251-
let overridingDocumentationNode = DocumentationContext.documentationNodeAndTitle(for: article, kind: documentationNodeKind, in: bundle)?.node
249+
let overridingDocumentationNode = DocumentationContext.documentationNodeAndTitle(for: article, kind: documentationNodeKind, in: context.inputs)?.node
252250
var dependencies = RenderReferenceDependencies()
253251
let renderReference = renderer.renderReference(for: topicReference, with: overridingDocumentationNode, dependencies: &dependencies)
254252

Sources/SwiftDocC/Indexing/Navigator/AvailabilityIndex+Ext.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public struct InterfaceLanguage: Hashable, CustomStringConvertible, Codable, Equ
8383
/// > ``from(string:)`` function.
8484
public let id: String
8585

86-
/// A mask to use to identify the interface language..
86+
/// A mask to use to identify the interface language.
8787
public let mask: ID
8888

8989

0 commit comments

Comments
 (0)