Skip to content

Commit

Permalink
Make --include-extended-types default for Swift 5.9 (#57)
Browse files Browse the repository at this point in the history
* make --include-extended-types default for Swift 5.9 and introduce --exclude-extended-types flag

* address review comments
 - mention default behavior earlier in docs
 - be more precise about method/function terminology in docs
  • Loading branch information
theMomax committed May 4, 2023
1 parent 106fd09 commit 26ac575
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 59 deletions.
88 changes: 63 additions & 25 deletions IntegrationTests/Tests/TargetWithSwiftExtensionsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,31 @@ final class TargetWithSwiftExtensionsTests: ConcurrencyRequiringTestCase {
try super.setUpWithError()
}

func testGenerateDocumentationWithoutEnablementFlag() throws {
func testGenerateDocumentationWithoutExtendedTypesFlag() throws {
let result = try swiftPackage(
"generate-documentation",
workingDirectory: try setupTemporaryDirectoryForFixture(named: "LibraryTargetWithExtensionSymbols")
)

result.assertExitStatusEquals(0)
XCTAssertEqual(result.referencedDocCArchives.count, 1)
let dataDirectoryContents = try unwrapDataDirectoryContents(of: result)

let doccArchiveURL = try XCTUnwrap(result.referencedDocCArchives.first)
#if swift(>=5.9)
try assertDirectoryContentsWithExtendedTypes(dataDirectoryContents)
#else
try assertDirectoryContentsWithoutExtendedTypes(dataDirectoryContents)
#endif
}

func testGenerateDocumentationWithDisablementFlag() throws {
let result = try swiftPackage(
"generate-documentation",
"--exclude-extended-types",
workingDirectory: try setupTemporaryDirectoryForFixture(named: "LibraryTargetWithExtensionSymbols")
)

let dataDirectoryContents = try filesIn(.dataSubdirectory, of: doccArchiveURL)
let dataDirectoryContents = try unwrapDataDirectoryContents(of: result)

XCTAssertEqual(
Set(dataDirectoryContents.map(\.lastTwoPathComponents)),
[
"documentation/library.json",

"library/foo.json",
"foo/foo().json",

"library/customfooconvertible.json",
"customfooconvertible/asfoo.json",
]
)
try assertDirectoryContentsWithoutExtendedTypes(dataDirectoryContents)
}

func testGenerateDocumentationWithEnablementFlag() throws {
Expand All @@ -59,15 +59,17 @@ final class TargetWithSwiftExtensionsTests: ConcurrencyRequiringTestCase {
workingDirectory: try setupTemporaryDirectoryForFixture(named: "LibraryTargetWithExtensionSymbols")
)

result.assertExitStatusEquals(0)
XCTAssertEqual(result.referencedDocCArchives.count, 1)

let doccArchiveURL = try XCTUnwrap(result.referencedDocCArchives.first)

let dataDirectoryContents = try filesIn(.dataSubdirectory, of: doccArchiveURL)
let dataDirectoryContents = try unwrapDataDirectoryContents(of: result)

try assertDirectoryContentsWithExtendedTypes(dataDirectoryContents)
}

func assertDirectoryContentsWithExtendedTypes(_ contents: [URL],
_ message: @autoclosure () -> String = "",
file: StaticString = #filePath,
line: UInt = #line) throws {
XCTAssertEqual(
Set(dataDirectoryContents.map(\.lastTwoPathComponents)),
Set(contents.map(\.lastTwoPathComponents)),
[
"documentation/library.json",
"library/swift.json",
Expand All @@ -85,7 +87,43 @@ final class TargetWithSwiftExtensionsTests: ConcurrencyRequiringTestCase {

"library/customfooconvertible.json",
"customfooconvertible/asfoo.json",
]
],
message(),
file: file,
line: line
)
}

func assertDirectoryContentsWithoutExtendedTypes(_ contents: [URL],
_ message: @autoclosure () -> String = "",
file: StaticString = #filePath,
line: UInt = #line) throws {
XCTAssertEqual(
Set(contents.map(\.lastTwoPathComponents)),
[
"documentation/library.json",

"library/foo.json",
"foo/foo().json",

"library/customfooconvertible.json",
"customfooconvertible/asfoo.json",
],
message(),
file: file,
line: line
)
}

func unwrapDataDirectoryContents(of result: SwiftInvocationResult,
_ message: @autoclosure () -> String = "",
file: StaticString = #filePath,
line: UInt = #line) throws -> [URL] {
result.assertExitStatusEquals(0)
XCTAssertEqual(result.referencedDocCArchives.count, 1, message(), file: file, line: line)

let doccArchiveURL = try XCTUnwrap(result.referencedDocCArchives.first, message(), file: file, line: line)

return try filesIn(.dataSubdirectory, of: doccArchiveURL)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,17 @@ extension PackageManager {
// Modify the symbol graph options with the custom ones
for customSymbolGraphOption in customSymbolGraphOptions {
switch customSymbolGraphOption {
case .extendedTypes:
case .extendedTypes.positive:
#if swift(>=5.8)
symbolGraphOptions.emitExtensionBlocks = true
#else
print("warning: detected '--include-extended-types' option, which is incompatible with your swift version (required: 5.8)")
#endif
case .extendedTypes.negative:
#if swift(>=5.8)
symbolGraphOptions.emitExtensionBlocks = false
#else
print("warning: detected '--exclude-extended-types' option, which is incompatible with your swift version (required: 5.8)")
#endif
case .skipSynthesizedSymbols:
symbolGraphOptions.includeSynthesized = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,17 @@ extension SwiftSourceModuleTarget {
targetMinimumAccessLevel = .public
}

#if swift(>=5.9)
let emitExtensionBlockSymbolDefault = true
#else
let emitExtensionBlockSymbolDefault = false
#endif

return PackageManager.SymbolGraphOptions(
minimumAccessLevel: targetMinimumAccessLevel,
includeSynthesized: true,
includeSPI: false,
emitExtensionBlocks: false
emitExtensionBlocks: emitExtensionBlockSymbolDefault
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,36 @@ Generate documentation for the extensions you make to types from other modules.

## Overview

By default, the Swift-DocC plugin ignores extensions you make to types that are not from the module you're generating documentation for.
The Swift-DocC plugin allows you to document extensions you make to types that are not from the module you're generating documentation for.

To include documentation for extended types, add the `--include-extended-types` flag to your invocations:
As of Swift 5.9, extension support is enabled by default. If you're using an older version of Swift or would like to configure this behavior pass the `--include-extended-types` or `--exclude-extended-types` flag to enable or disable the feature, respectively:

$ swift package generate-documentation --include-extended-types

> Note: Extension support is available when using Swift 5.8 or later and the Swift-DocC plugin 1.2 or later.
$ swift package generate-documentation --exclude-extended-types

## Understanding What is Included by Default
> Note: Extension support is available when using Swift 5.8 or later and the Swift-DocC plugin 1.2 or later. Extension support is enabled by default starting with Swift 5.9 and the Swift-DocC plugin 1.3.
Not everything that is declared in an extension is hidden behind the `--include-extended-types` flag. If the extension is declared in the same target as the type it is extending, the extension's contents will be included in the documentation by default.
## Understanding What is an Extended Type

Not every type you add an extension to is an extended type. If the extension is declared in the same target as the type it is extending, the extension's contents will always be included in the documentation. Only extensions you make to types from other targets are represented as an external type in your documentation archive.

```swift
public struct Sloth { }

extension Sloth {
// This function is included in the
// documentation by default.
// This method is always included
// in the documentation.
public func wake() { /* ... */ }
}

// `Collection` is from the standard library,
// not the `SlothCreator` library, so this is
// what we call an "extended type".
extension Collection where Element == Sloth {
// This property is not included in
// the documentation by default.
// This property is only included in
// the documentation if extension
// support is enabled.
public func wake() {
for sloth in self {
sloth.wake()
Expand Down
7 changes: 6 additions & 1 deletion Sources/SwiftDocCPluginUtilities/HelpInformation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,13 @@ public enum HelpInformation {
#endif

for flag in supportedPluginFlags {
var flagListText = flag.positive.parsedValues.sorted().joined(separator: ", ")
if !flag.negative.parsedValues.isEmpty {
flagListText += " / \(flag.negative.parsedValues.sorted().joined(separator: ", "))"
}

helpText += """
\(flag.parsedValues.sorted().joined(separator: ", "))
\(flagListText)
\(flag.abstract)
\(flag.description)
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftDocCPluginUtilities/ParsedArguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ public struct ParsedArguments {

let symbolGraphArguments = arguments.filter(for: .dumpSymbolGraph)

self.symbolGraphArguments = ParsedArguments.ArgumentConsumer.dumpSymbolGraph.flags.filter { option in
option.parsedValues.contains(where: symbolGraphArguments.contains)
self.symbolGraphArguments = ParsedArguments.ArgumentConsumer.dumpSymbolGraph.flags.compactMap {
$0.value(for: symbolGraphArguments)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,30 @@
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors

extension PluginFlag {
/// Include extended types in documentation archives.
/// Include or exclude extended types in documentation archives.
///
/// Enables the extension block symbol format when calling the
/// Enables/disables the extension block symbol format when calling the
/// dump symbol graph API.
///
/// - Note: This flag is only available starting from Swift 5.8. It should
/// be hidden from the `--help` command for lower toolchain versions.
/// However, we do not hide the flag entirely, because this enables us to give
/// a more precise warning when accidentally used with Swift 5.7 or lower.
static let extendedTypes = PluginFlag(
parsedValues: [
positiveValues: [
"--include-extended-types",
],
abstract: "Include extended types from other modules in the produced DocC archive.",
description: """
Allows documenting symbols that a target adds to its dependencies.
""",
negativeValues: [
"--exclude-extended-types",
],
abstract: "Control whether extended types from other modules are shown in the produced DocC archive. (default: \(Self.default))",
description: "Allows documenting symbols that a target adds to its dependencies.",
argumentTransformation: { $0 }
)

#if swift(>=5.9)
private static let `default` = "--include-extended-types"
#else
private static let `default` = "--exclude-extended-types"
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

extension PluginFlag: Equatable {
static func ==(lhs: PluginFlag, rhs: PluginFlag) -> Bool {
return lhs.parsedValues == rhs.parsedValues
return lhs.positive.parsedValues == rhs.positive.parsedValues
&& lhs.negative.parsedValues == rhs.negative.parsedValues
}
}
73 changes: 69 additions & 4 deletions Sources/SwiftDocCPluginUtilities/PluginFlags/PluginFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ struct PluginFlag: ArgumentsTransforming {
/// The string values that will be parsed when detecting this flag.
///
/// For example, this might be `["--disable-index"]`.
let parsedValues: Set<String>
var parsedValues: Set<String> {
positiveValues.union(negativeValues)
}

private let positiveValues: Set<String>

private let negativeValues: Set<String>

/// A short, user-facing description of this flag.
let abstract: String
Expand All @@ -23,6 +29,41 @@ struct PluginFlag: ArgumentsTransforming {

let argumentTransformation: (Arguments) -> Arguments

/// A version of this flag that only parses its positive values.
var positive: Self {
.init(positiveValues: positiveValues,
negativeValues: [],
abstract: abstract,
description: description,
argumentTransformation: argumentTransformation)
}

/// A version of this flag that only parses its negative values.
var negative: Self {
.init(positiveValues: [],
negativeValues: negativeValues,
abstract: abstract,
description: description,
argumentTransformation: argumentTransformation)
}

/// Returns either the ``positive`` or ``negative`` version
/// of this flag, depending on the last element in `arguments` that
/// triggers this flag.
func value(for arguments: Arguments) -> Self? {
guard let last = arguments
.filter({ parsedValues.contains($0) })
.last else {
return nil
}

if positiveValues.contains(last) {
return positive
} else {
return negative
}
}

/// Transforms the given set of arguments if they include any of this flag's
/// parsed values.
///
Expand All @@ -49,20 +90,44 @@ struct PluginFlag: ArgumentsTransforming {
/// Create a new command-line flag.
///
/// - Parameters:
/// - parsedValues: The string values that should be parsed to detect this flag.
/// - positiveValues: The string values that should be parsed to enable this flag.
/// - negativeValues: The string values that should be parsed to disable this flag.
/// - abstract: The user-facing description of this flag.
/// - description: An expanded, user-facing description of this flag.
/// - argumentTransformation: A closure that can be applied to a given
/// set of parsed arguments if the arguments include any of the this flag's parsed values.
init(
parsedValues: Set<String>,
positiveValues: Set<String>,
negativeValues: Set<String>,
abstract: String,
description: String,
argumentTransformation: @escaping (Arguments) -> Arguments
) {
self.parsedValues = parsedValues
self.positiveValues = positiveValues
self.negativeValues = negativeValues
self.abstract = abstract
self.description = description
self.argumentTransformation = argumentTransformation
}

/// Create a new command-line flag.
///
/// - Parameters:
/// - parsedValues: The string values that should be parsed to detect this flag.
/// - abstract: The user-facing description of this flag.
/// - description: An expanded, user-facing description of this flag.
/// - argumentTransformation: A closure that can be applied to a given
/// set of parsed arguments if the arguments include any of the this flag's parsed values.
init(
parsedValues: Set<String>,
abstract: String,
description: String,
argumentTransformation: @escaping (Arguments) -> Arguments
) {
self.init(positiveValues: parsedValues,
negativeValues: [],
abstract: abstract,
description: description,
argumentTransformation: argumentTransformation)
}
}
Loading

0 comments on commit 26ac575

Please sign in to comment.