From 4af3470068cf5ccdb6df9c8ee914c6931497ebc3 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Sat, 19 Apr 2025 14:12:03 -0700 Subject: [PATCH 1/5] display arguments as term lists in docc-flavored markdown generation - added a Boolean to distinguish regular (GH-flavored) markdown from docc-flavored markdown - represent the arguments in generated files with term lists for the docc-flavored variant - updated the snapshot tests to work both GH and DocC flavored markdown generation - removed an added "\n" in the recording of content for snapshot tests, which caused the snapshot tests some issues (recorded tests with new values wouldn't pass on second-run) --- .../TestHelpers.swift | 22 +- .../GenerateDoccReferenceTests.swift | 30 ++- .../Snapshots/testColorDoccReference().md | 8 +- .../Snapshots/testColorMarkdownReference().md | 39 ++++ .../testCountLinesDoccReference().md | 10 +- .../testCountLinesMarkdownReference().md | 42 ++++ .../Snapshots/testMathDoccReference().md | 58 ++--- .../Snapshots/testMathMarkdownReference().md | 212 ++++++++++++++++++ .../Snapshots/testRepeatDoccReference().md | 10 +- .../testRepeatMarkdownReference().md | 42 ++++ .../Snapshots/testRollDoccReference().md | 12 +- .../Snapshots/testRollMarkdownReference().md | 49 ++++ .../Extensions/ArgumentParser+Markdown.swift | 21 +- .../GenerateDoccReference.swift | 26 ++- 14 files changed, 512 insertions(+), 69 deletions(-) create mode 100644 Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testColorMarkdownReference().md create mode 100644 Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testCountLinesMarkdownReference().md create mode 100644 Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testMathMarkdownReference().md create mode 100644 Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRepeatMarkdownReference().md create mode 100644 Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRollMarkdownReference().md diff --git a/Sources/ArgumentParserTestHelpers/TestHelpers.swift b/Sources/ArgumentParserTestHelpers/TestHelpers.swift index c2b8bb4e6..8373c578b 100644 --- a/Sources/ArgumentParserTestHelpers/TestHelpers.swift +++ b/Sources/ArgumentParserTestHelpers/TestHelpers.swift @@ -448,7 +448,7 @@ extension XCTest { ProcessInfo.processInfo.environment["RECORD_SNAPSHOTS"] != nil if record || recordEnvironment || !snapshotExists { - let recordedValue = actual + "\n" + let recordedValue = actual try FileManager.default.createDirectory( at: snapshotDirectoryURL, withIntermediateDirectories: true, @@ -507,8 +507,9 @@ extension XCTest { line: line) } - public func assertGenerateDoccReference( + public func assertGeneratedReference( command: String, + doccFlavored: Bool, record: Bool = false, test: StaticString = #function, file: StaticString = #filePath, @@ -519,10 +520,19 @@ extension XCTest { #endif let commandURL = debugURL.appendingPathComponent(command) - let command = [ - "generate-docc-reference", commandURL.path, - "--output-directory", "-", - ] + let command: [String] + if doccFlavored { + command = [ + "generate-docc-reference", commandURL.path, + "--output-directory", "-", + "--docc-flavored", "true", + ] + } else { + command = [ + "generate-docc-reference", commandURL.path, + "--output-directory", "-" + ] + } let actual = try AssertExecuteCommand( command: command, file: file, diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/GenerateDoccReferenceTests.swift b/Tests/ArgumentParserGenerateDoccReferenceTests/GenerateDoccReferenceTests.swift index 599ee86cd..1fd65423b 100644 --- a/Tests/ArgumentParserGenerateDoccReferenceTests/GenerateDoccReferenceTests.swift +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/GenerateDoccReferenceTests.swift @@ -14,25 +14,43 @@ import XCTest final class GenerateDoccReferenceTests: XCTestCase { #if os(macOS) + func testCountLinesMarkdownReference() throws { + guard #available(macOS 12, *) else { return } + try assertGeneratedReference(command: "count-lines", doccFlavored: false) + } + func testCountLinesDoccReference() throws { guard #available(macOS 12, *) else { return } - try assertGenerateDoccReference(command: "count-lines") + try assertGeneratedReference(command: "count-lines", doccFlavored: true) } - #endif +#endif + func testColorMarkdownReference() throws { + try assertGeneratedReference(command: "color", doccFlavored: false) + } func testColorDoccReference() throws { - try assertGenerateDoccReference(command: "color") + try assertGeneratedReference(command: "color", doccFlavored: true) } + func testMathMarkdownReference() throws { + try assertGeneratedReference(command: "math", doccFlavored: false) + } func testMathDoccReference() throws { - try assertGenerateDoccReference(command: "math") + try assertGeneratedReference(command: "math", doccFlavored: true) } + + func testRepeatMarkdownReference() throws { + try assertGeneratedReference(command: "repeat", doccFlavored: false) + } func testRepeatDoccReference() throws { - try assertGenerateDoccReference(command: "repeat") + try assertGeneratedReference(command: "repeat", doccFlavored: true) } + func testRollMarkdownReference() throws { + try assertGeneratedReference(command: "roll", doccFlavored: false) + } func testRollDoccReference() throws { - try assertGenerateDoccReference(command: "roll") + try assertGeneratedReference(command: "roll", doccFlavored: true) } } diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testColorDoccReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testColorDoccReference().md index 966fd9cdd..ccdb71eb2 100644 --- a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testColorDoccReference().md +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testColorDoccReference().md @@ -6,19 +6,19 @@ color --fav= [--second=] [--help] ``` -**--fav=\:** +- term **--fav=\:** *Your favorite color.* -**--second=\:** +- term **--second=\:** *Your second favorite color.* This is optional. -**--help:** +- term **--help:** *Show help information.* @@ -31,7 +31,7 @@ Show subcommand help information. color help [...] ``` -**subcommands:** +- term **subcommands:** diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testColorMarkdownReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testColorMarkdownReference().md new file mode 100644 index 000000000..966fd9cdd --- /dev/null +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testColorMarkdownReference().md @@ -0,0 +1,39 @@ +# color + + + +``` +color --fav= [--second=] [--help] +``` + +**--fav=\:** + +*Your favorite color.* + + +**--second=\:** + +*Your second favorite color.* + +This is optional. + + +**--help:** + +*Show help information.* + + +## color.help + +Show subcommand help information. + +``` +color help [...] +``` + +**subcommands:** + + + + + diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testCountLinesDoccReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testCountLinesDoccReference().md index 88b97b538..ca99aa54b 100644 --- a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testCountLinesDoccReference().md +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testCountLinesDoccReference().md @@ -6,22 +6,22 @@ count-lines [] [--prefix=] [--verbose] [--help] ``` -**input-file:** +- term **input-file:** *A file to count lines in. If omitted, counts the lines of stdin.* -**--prefix=\:** +- term **--prefix=\:** *Only count lines with this prefix.* -**--verbose:** +- term **--verbose:** *Include extra information in the output.* -**--help:** +- term **--help:** *Show help information.* @@ -34,7 +34,7 @@ Show subcommand help information. count-lines help [...] ``` -**subcommands:** +- term **subcommands:** diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testCountLinesMarkdownReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testCountLinesMarkdownReference().md new file mode 100644 index 000000000..88b97b538 --- /dev/null +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testCountLinesMarkdownReference().md @@ -0,0 +1,42 @@ +# count-lines + + + +``` +count-lines [] [--prefix=] [--verbose] [--help] +``` + +**input-file:** + +*A file to count lines in. If omitted, counts the lines of stdin.* + + +**--prefix=\:** + +*Only count lines with this prefix.* + + +**--verbose:** + +*Include extra information in the output.* + + +**--help:** + +*Show help information.* + + +## count-lines.help + +Show subcommand help information. + +``` +count-lines help [...] +``` + +**subcommands:** + + + + + diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testMathDoccReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testMathDoccReference().md index adcaac132..f7d37d8fa 100644 --- a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testMathDoccReference().md +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testMathDoccReference().md @@ -8,12 +8,12 @@ A utility for performing maths. math [--version] [--help] ``` -**--version:** +- term **--version:** *Show the version.* -**--help:** +- term **--help:** *Show help information.* @@ -26,22 +26,22 @@ Print the sum of the values. math add [--hex-output] [...] [--version] [--help] ``` -**--hex-output:** +- term **--hex-output:** *Use hexadecimal notation for the result.* -**values:** +- term **values:** *A group of integers to operate on.* -**--version:** +- term **--version:** *Show the version.* -**--help:** +- term **--help:** *Show help information.* @@ -56,22 +56,22 @@ Print the product of the values. math multiply [--hex-output] [...] [--version] [--help] ``` -**--hex-output:** +- term **--hex-output:** *Use hexadecimal notation for the result.* -**values:** +- term **values:** *A group of integers to operate on.* -**--version:** +- term **--version:** *Show the version.* -**--help:** +- term **--help:** *Show help information.* @@ -86,12 +86,12 @@ Calculate descriptive statistics. math stats [--version] [--help] ``` -**--version:** +- term **--version:** *Show the version.* -**--help:** +- term **--help:** *Show help information.* @@ -104,22 +104,22 @@ Print the average of the values. math stats average [--kind=] [...] [--version] [--help] ``` -**--kind=\:** +- term **--kind=\:** *The kind of average to provide.* -**values:** +- term **values:** *A group of floating-point values to operate on.* -**--version:** +- term **--version:** *Show the version.* -**--help:** +- term **--help:** *Show help information.* @@ -134,17 +134,17 @@ Print the standard deviation of the values. math stats stdev [...] [--version] [--help] ``` -**values:** +- term **values:** *A group of floating-point values to operate on.* -**--version:** +- term **--version:** *Show the version.* -**--help:** +- term **--help:** *Show help information.* @@ -159,35 +159,35 @@ Print the quantiles of the values (TBD). math stats quantiles [] [] [...] [--file=] [--directory=] [--shell=] [--custom=] [--version] [--help] ``` -**one-of-four:** +- term **one-of-four:** -**custom-arg:** +- term **custom-arg:** -**values:** +- term **values:** *A group of floating-point values to operate on.* -**--file=\:** +- term **--file=\:** -**--directory=\:** +- term **--directory=\:** -**--shell=\:** +- term **--shell=\:** -**--custom=\:** +- term **--custom=\:** -**--version:** +- term **--version:** *Show the version.* -**--help:** +- term **--help:** *Show help information.* @@ -204,7 +204,7 @@ Show subcommand help information. math help [...] ``` -**subcommands:** +- term **subcommands:** diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testMathMarkdownReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testMathMarkdownReference().md new file mode 100644 index 000000000..adcaac132 --- /dev/null +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testMathMarkdownReference().md @@ -0,0 +1,212 @@ +# math + + + +A utility for performing maths. + +``` +math [--version] [--help] +``` + +**--version:** + +*Show the version.* + + +**--help:** + +*Show help information.* + + +## math.add + +Print the sum of the values. + +``` +math add [--hex-output] [...] [--version] [--help] +``` + +**--hex-output:** + +*Use hexadecimal notation for the result.* + + +**values:** + +*A group of integers to operate on.* + + +**--version:** + +*Show the version.* + + +**--help:** + +*Show help information.* + + + + +## math.multiply + +Print the product of the values. + +``` +math multiply [--hex-output] [...] [--version] [--help] +``` + +**--hex-output:** + +*Use hexadecimal notation for the result.* + + +**values:** + +*A group of integers to operate on.* + + +**--version:** + +*Show the version.* + + +**--help:** + +*Show help information.* + + + + +## math.stats + +Calculate descriptive statistics. + +``` +math stats [--version] [--help] +``` + +**--version:** + +*Show the version.* + + +**--help:** + +*Show help information.* + + +### math.stats.average + +Print the average of the values. + +``` +math stats average [--kind=] [...] [--version] [--help] +``` + +**--kind=\:** + +*The kind of average to provide.* + + +**values:** + +*A group of floating-point values to operate on.* + + +**--version:** + +*Show the version.* + + +**--help:** + +*Show help information.* + + + + +### math.stats.stdev + +Print the standard deviation of the values. + +``` +math stats stdev [...] [--version] [--help] +``` + +**values:** + +*A group of floating-point values to operate on.* + + +**--version:** + +*Show the version.* + + +**--help:** + +*Show help information.* + + + + +### math.stats.quantiles + +Print the quantiles of the values (TBD). + +``` +math stats quantiles [] [] [...] [--file=] [--directory=] [--shell=] [--custom=] [--version] [--help] +``` + +**one-of-four:** + + +**custom-arg:** + + +**values:** + +*A group of floating-point values to operate on.* + + +**--file=\:** + + +**--directory=\:** + + +**--shell=\:** + + +**--custom=\:** + + +**--version:** + +*Show the version.* + + +**--help:** + +*Show help information.* + + + + + + +## math.help + +Show subcommand help information. + +``` +math help [...] +``` + +**subcommands:** + + + + + diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRepeatDoccReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRepeatDoccReference().md index 8d0714b48..a7ec2089d 100644 --- a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRepeatDoccReference().md +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRepeatDoccReference().md @@ -6,22 +6,22 @@ repeat [--count=] [--include-counter] [--help] ``` -**--count=\:** +- term **--count=\:** *The number of times to repeat 'phrase'.* -**--include-counter:** +- term **--include-counter:** *Include a counter with each repetition.* -**phrase:** +- term **phrase:** *The phrase to repeat.* -**--help:** +- term **--help:** *Show help information.* @@ -34,7 +34,7 @@ Show subcommand help information. repeat help [...] ``` -**subcommands:** +- term **subcommands:** diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRepeatMarkdownReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRepeatMarkdownReference().md new file mode 100644 index 000000000..8d0714b48 --- /dev/null +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRepeatMarkdownReference().md @@ -0,0 +1,42 @@ +# repeat + + + +``` +repeat [--count=] [--include-counter] [--help] +``` + +**--count=\:** + +*The number of times to repeat 'phrase'.* + + +**--include-counter:** + +*Include a counter with each repetition.* + + +**phrase:** + +*The phrase to repeat.* + + +**--help:** + +*Show help information.* + + +## repeat.help + +Show subcommand help information. + +``` +repeat help [...] +``` + +**subcommands:** + + + + + diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRollDoccReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRollDoccReference().md index d84e3e2b0..03a66a718 100644 --- a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRollDoccReference().md +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRollDoccReference().md @@ -6,29 +6,29 @@ roll [--times=] [--sides=] [--seed=] [--verbose] [--help] ``` -**--times=\:** +- term **--times=\:** *Rolls the dice times.* -**--sides=\:** +- term **--sides=\:** *Rolls an -sided dice.* Use this option to override the default value of a six-sided die. -**--seed=\:** +- term **--seed=\:** *A seed to use for repeatable random generation.* -**--verbose:** +- term **--verbose:** *Show all roll results.* -**--help:** +- term **--help:** *Show help information.* @@ -41,7 +41,7 @@ Show subcommand help information. roll help [...] ``` -**subcommands:** +- term **subcommands:** diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRollMarkdownReference().md b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRollMarkdownReference().md new file mode 100644 index 000000000..d84e3e2b0 --- /dev/null +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/Snapshots/testRollMarkdownReference().md @@ -0,0 +1,49 @@ +# roll + + + +``` +roll [--times=] [--sides=] [--seed=] [--verbose] [--help] +``` + +**--times=\:** + +*Rolls the dice times.* + + +**--sides=\:** + +*Rolls an -sided dice.* + +Use this option to override the default value of a six-sided die. + + +**--seed=\:** + +*A seed to use for repeatable random generation.* + + +**--verbose:** + +*Show all roll results.* + + +**--help:** + +*Show help information.* + + +## roll.help + +Show subcommand help information. + +``` +roll help [...] +``` + +**subcommands:** + + + + + diff --git a/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift b/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift index 05e187061..e6117bac7 100644 --- a/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift +++ b/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift @@ -34,7 +34,15 @@ extension CommandInfoV0 { } extension CommandInfoV0 { - func toMarkdown(_ path: [String]) -> String { + /// Recursively parses a command to generate markdown content that describes the command. + /// - Parameters: + /// - path: The path of subcommands from the root command. + /// - doccFlavored: a Boolean value that indicates whether to use docc-flavored markdown. + /// - Returns: A multi-line markdown file that describes the command. + /// + /// If `path` is empty, it represents a top-level command. + /// Otherwise it's a subcommand, potentially recursive to multiple levels. + func toMarkdown(_ path: [String], doccFlavored: Bool) -> String { var result = String(repeating: "#", count: path.count + 1) + " \(self.doccReferenceTitle)\n\n" @@ -64,7 +72,11 @@ extension CommandInfoV0 { continue } - result += "**\(arg.identity()):**\n\n" + if doccFlavored { + result += "- term **\(arg.identity()):**\n\n" + } else { + result += "**\(arg.identity()):**\n\n" + } if let abstract = arg.abstract { result += "*\(abstract)*\n\n" } @@ -76,7 +88,7 @@ extension CommandInfoV0 { } for subcommand in self.subcommands ?? [] { - result += subcommand.toMarkdown(path + [self.commandName]) + "\n\n" + result += subcommand.toMarkdown(path + [self.commandName], doccFlavored: doccFlavored) + "\n\n" } return result @@ -92,6 +104,9 @@ extension CommandInfoV0 { } extension ArgumentInfoV0 { + /// Returns a string that describes the use of the argument. + /// + /// If `shouldDisplay` is `false`, an empty string is returned. public func usage() -> String { guard self.shouldDisplay else { return "" diff --git a/Tools/generate-docc-reference/GenerateDoccReference.swift b/Tools/generate-docc-reference/GenerateDoccReference.swift index c5f1c6a0e..73a84a7aa 100644 --- a/Tools/generate-docc-reference/GenerateDoccReference.swift +++ b/Tools/generate-docc-reference/GenerateDoccReference.swift @@ -49,6 +49,11 @@ struct GenerateDoccReference: ParsableCommand { name: .shortAndLong, help: "Directory to save generated docc reference. Use '-' for stdout.") var outputDirectory: String + + @Option( + name: .shortAndLong, + help: "Use docc flavored markdown for the generated output.") + var doccFlavored: Bool = false func validate() throws { if outputDirectory != "-" { @@ -71,6 +76,8 @@ struct GenerateDoccReference: ParsableCommand { func run() throws { let data: Data + // runs the tool with the --experimental-dump-help argument to capture + // the output. do { let tool = URL(fileURLWithPath: tool) let output = try executeCommand( @@ -80,9 +87,12 @@ struct GenerateDoccReference: ParsableCommand { throw GenerateDoccReferenceError.failedToRunSubprocess(error: error) } + // ToolInfoHeader is intentionally kept internal to argument parser to + // allow the library some flexibility to update/change its content/format. do { let toolInfoThin = try JSONDecoder().decode( ToolInfoHeader.self, from: data) + // verify the serialization version is known/expected guard toolInfoThin.serializationVersion == 0 else { throw GenerateDoccReferenceError.unsupportedDumpHelpVersion( expected: 0, @@ -101,22 +111,28 @@ struct GenerateDoccReference: ParsableCommand { do { if self.outputDirectory == "-" { - try self.generatePages(from: toolInfo.command, savingTo: nil) + try self.generatePages(from: toolInfo.command, savingTo: nil, doccFlavored: doccFlavored) } else { try self.generatePages( from: toolInfo.command, - savingTo: URL(fileURLWithPath: outputDirectory)) + savingTo: URL(fileURLWithPath: outputDirectory), + doccFlavored: doccFlavored) } } catch { throw GenerateDoccReferenceError.failedToGenerateDoccReference( error: error) } } - - func generatePages(from command: CommandInfoV0, savingTo directory: URL?) + + /// Generates a markdown file from the CommandInfoV0 object you provide. + /// - Parameters: + /// - command: The command to parse into a markdown output. + /// - directory: The directory to save the generated markdown file, printing it if `nil`. + /// - doccFlavored: A Boolean value the indicates whether to generate docc-flavored markdown. + func generatePages(from command: CommandInfoV0, savingTo directory: URL?, doccFlavored: Bool) throws { - let page = command.toMarkdown([]) + let page = command.toMarkdown([], doccFlavored: doccFlavored) if let directory = directory { let fileName = command.doccReferenceFileName From 74012e2cb92be545bb2dad2f48ae98e4c411b07b Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Sat, 19 Apr 2025 14:20:08 -0700 Subject: [PATCH 2/5] applying formatting using Script/format --- Sources/ArgumentParserTestHelpers/TestHelpers.swift | 2 +- .../GenerateDoccReferenceTests.swift | 3 +-- .../Extensions/ArgumentParser+Markdown.swift | 4 +++- .../GenerateDoccReference.swift | 11 +++++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Sources/ArgumentParserTestHelpers/TestHelpers.swift b/Sources/ArgumentParserTestHelpers/TestHelpers.swift index 8373c578b..4672ade7a 100644 --- a/Sources/ArgumentParserTestHelpers/TestHelpers.swift +++ b/Sources/ArgumentParserTestHelpers/TestHelpers.swift @@ -530,7 +530,7 @@ extension XCTest { } else { command = [ "generate-docc-reference", commandURL.path, - "--output-directory", "-" + "--output-directory", "-", ] } let actual = try AssertExecuteCommand( diff --git a/Tests/ArgumentParserGenerateDoccReferenceTests/GenerateDoccReferenceTests.swift b/Tests/ArgumentParserGenerateDoccReferenceTests/GenerateDoccReferenceTests.swift index 1fd65423b..a976f26da 100644 --- a/Tests/ArgumentParserGenerateDoccReferenceTests/GenerateDoccReferenceTests.swift +++ b/Tests/ArgumentParserGenerateDoccReferenceTests/GenerateDoccReferenceTests.swift @@ -23,7 +23,7 @@ final class GenerateDoccReferenceTests: XCTestCase { guard #available(macOS 12, *) else { return } try assertGeneratedReference(command: "count-lines", doccFlavored: true) } -#endif + #endif func testColorMarkdownReference() throws { try assertGeneratedReference(command: "color", doccFlavored: false) @@ -39,7 +39,6 @@ final class GenerateDoccReferenceTests: XCTestCase { try assertGeneratedReference(command: "math", doccFlavored: true) } - func testRepeatMarkdownReference() throws { try assertGeneratedReference(command: "repeat", doccFlavored: false) } diff --git a/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift b/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift index e6117bac7..ff40735ab 100644 --- a/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift +++ b/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift @@ -88,7 +88,9 @@ extension CommandInfoV0 { } for subcommand in self.subcommands ?? [] { - result += subcommand.toMarkdown(path + [self.commandName], doccFlavored: doccFlavored) + "\n\n" + result += + subcommand.toMarkdown( + path + [self.commandName], doccFlavored: doccFlavored) + "\n\n" } return result diff --git a/Tools/generate-docc-reference/GenerateDoccReference.swift b/Tools/generate-docc-reference/GenerateDoccReference.swift index 73a84a7aa..53744415a 100644 --- a/Tools/generate-docc-reference/GenerateDoccReference.swift +++ b/Tools/generate-docc-reference/GenerateDoccReference.swift @@ -49,7 +49,7 @@ struct GenerateDoccReference: ParsableCommand { name: .shortAndLong, help: "Directory to save generated docc reference. Use '-' for stdout.") var outputDirectory: String - + @Option( name: .shortAndLong, help: "Use docc flavored markdown for the generated output.") @@ -111,7 +111,8 @@ struct GenerateDoccReference: ParsableCommand { do { if self.outputDirectory == "-" { - try self.generatePages(from: toolInfo.command, savingTo: nil, doccFlavored: doccFlavored) + try self.generatePages( + from: toolInfo.command, savingTo: nil, doccFlavored: doccFlavored) } else { try self.generatePages( from: toolInfo.command, @@ -123,13 +124,15 @@ struct GenerateDoccReference: ParsableCommand { error: error) } } - + /// Generates a markdown file from the CommandInfoV0 object you provide. /// - Parameters: /// - command: The command to parse into a markdown output. /// - directory: The directory to save the generated markdown file, printing it if `nil`. /// - doccFlavored: A Boolean value the indicates whether to generate docc-flavored markdown. - func generatePages(from command: CommandInfoV0, savingTo directory: URL?, doccFlavored: Bool) + func generatePages( + from command: CommandInfoV0, savingTo directory: URL?, doccFlavored: Bool + ) throws { let page = command.toMarkdown([], doccFlavored: doccFlavored) From b5e26611ff22cf0757efac3a9c587ff3089d9ffe Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Sat, 19 Apr 2025 14:30:27 -0700 Subject: [PATCH 3/5] resolve soundness check issue for throwing function --- Tools/generate-docc-reference/GenerateDoccReference.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/generate-docc-reference/GenerateDoccReference.swift b/Tools/generate-docc-reference/GenerateDoccReference.swift index 53744415a..8d4f06cee 100644 --- a/Tools/generate-docc-reference/GenerateDoccReference.swift +++ b/Tools/generate-docc-reference/GenerateDoccReference.swift @@ -130,6 +130,7 @@ struct GenerateDoccReference: ParsableCommand { /// - command: The command to parse into a markdown output. /// - directory: The directory to save the generated markdown file, printing it if `nil`. /// - doccFlavored: A Boolean value the indicates whether to generate docc-flavored markdown. + /// - Throws: An error if the markdown file cannot be generated or saved. func generatePages( from command: CommandInfoV0, savingTo directory: URL?, doccFlavored: Bool ) From b58be3735040cbbe2692e6d0b0c7dc8c2890826f Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Tue, 22 Apr 2025 15:58:58 -0700 Subject: [PATCH 4/5] switches argument to --style, defaulting to github flavored markdown, and accepting as 'docc' as an alternative --- .../TestHelpers.swift | 2 +- .../Extensions/ArgumentParser+Markdown.swift | 12 ++++++----- .../GenerateDoccReference.swift | 20 +++++++++++++------ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Sources/ArgumentParserTestHelpers/TestHelpers.swift b/Sources/ArgumentParserTestHelpers/TestHelpers.swift index 4672ade7a..a71e324f8 100644 --- a/Sources/ArgumentParserTestHelpers/TestHelpers.swift +++ b/Sources/ArgumentParserTestHelpers/TestHelpers.swift @@ -525,7 +525,7 @@ extension XCTest { command = [ "generate-docc-reference", commandURL.path, "--output-directory", "-", - "--docc-flavored", "true", + "--style", "docc", ] } else { command = [ diff --git a/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift b/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift index ff40735ab..6ec7850aa 100644 --- a/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift +++ b/Tools/generate-docc-reference/Extensions/ArgumentParser+Markdown.swift @@ -37,12 +37,12 @@ extension CommandInfoV0 { /// Recursively parses a command to generate markdown content that describes the command. /// - Parameters: /// - path: The path of subcommands from the root command. - /// - doccFlavored: a Boolean value that indicates whether to use docc-flavored markdown. + /// - markdownStyle: The flavor of markdown to emit, either `docc` or `github` /// - Returns: A multi-line markdown file that describes the command. /// /// If `path` is empty, it represents a top-level command. /// Otherwise it's a subcommand, potentially recursive to multiple levels. - func toMarkdown(_ path: [String], doccFlavored: Bool) -> String { + func toMarkdown(_ path: [String], markdownStyle: OutputStyle) -> String { var result = String(repeating: "#", count: path.count + 1) + " \(self.doccReferenceTitle)\n\n" @@ -72,11 +72,13 @@ extension CommandInfoV0 { continue } - if doccFlavored { + switch markdownStyle { + case .docc: result += "- term **\(arg.identity()):**\n\n" - } else { + case .github: result += "**\(arg.identity()):**\n\n" } + if let abstract = arg.abstract { result += "*\(abstract)*\n\n" } @@ -90,7 +92,7 @@ extension CommandInfoV0 { for subcommand in self.subcommands ?? [] { result += subcommand.toMarkdown( - path + [self.commandName], doccFlavored: doccFlavored) + "\n\n" + path + [self.commandName], markdownStyle: markdownStyle) + "\n\n" } return result diff --git a/Tools/generate-docc-reference/GenerateDoccReference.swift b/Tools/generate-docc-reference/GenerateDoccReference.swift index 8d4f06cee..922ded496 100644 --- a/Tools/generate-docc-reference/GenerateDoccReference.swift +++ b/Tools/generate-docc-reference/GenerateDoccReference.swift @@ -36,6 +36,14 @@ extension GenerateDoccReferenceError: CustomStringConvertible { } } +/// The flavor of generated markdown to emit. +enum OutputStyle: String, EnumerableFlag, ExpressibleByArgument { + /// DocC-supported markdown + case docc + /// GitHub-flavored markdown + case github +} + @main struct GenerateDoccReference: ParsableCommand { static let configuration = CommandConfiguration( @@ -53,7 +61,7 @@ struct GenerateDoccReference: ParsableCommand { @Option( name: .shortAndLong, help: "Use docc flavored markdown for the generated output.") - var doccFlavored: Bool = false + var style: OutputStyle = .github func validate() throws { if outputDirectory != "-" { @@ -112,12 +120,12 @@ struct GenerateDoccReference: ParsableCommand { do { if self.outputDirectory == "-" { try self.generatePages( - from: toolInfo.command, savingTo: nil, doccFlavored: doccFlavored) + from: toolInfo.command, savingTo: nil, flavor: style) } else { try self.generatePages( from: toolInfo.command, savingTo: URL(fileURLWithPath: outputDirectory), - doccFlavored: doccFlavored) + flavor: style) } } catch { throw GenerateDoccReferenceError.failedToGenerateDoccReference( @@ -129,14 +137,14 @@ struct GenerateDoccReference: ParsableCommand { /// - Parameters: /// - command: The command to parse into a markdown output. /// - directory: The directory to save the generated markdown file, printing it if `nil`. - /// - doccFlavored: A Boolean value the indicates whether to generate docc-flavored markdown. + /// - flavor: The flavor of markdown to use when generating the content. /// - Throws: An error if the markdown file cannot be generated or saved. func generatePages( - from command: CommandInfoV0, savingTo directory: URL?, doccFlavored: Bool + from command: CommandInfoV0, savingTo directory: URL?, flavor: OutputStyle ) throws { - let page = command.toMarkdown([], doccFlavored: doccFlavored) + let page = command.toMarkdown([], markdownStyle: style) if let directory = directory { let fileName = command.doccReferenceFileName From 34fc135a51d0e9ebb54b2ec9d731b14c3894f1bb Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Tue, 22 Apr 2025 16:17:51 -0700 Subject: [PATCH 5/5] adding comments for future-me to understand flow in generate manual to match generate reference --- Tools/generate-manual/GenerateManual.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tools/generate-manual/GenerateManual.swift b/Tools/generate-manual/GenerateManual.swift index 0b42af303..00b8f60d0 100644 --- a/Tools/generate-manual/GenerateManual.swift +++ b/Tools/generate-manual/GenerateManual.swift @@ -92,6 +92,8 @@ struct GenerateManual: ParsableCommand { func run() throws { let data: Data + // runs the tool with the --experimental-dump-help argument to capture + // the output. do { let tool = URL(fileURLWithPath: tool) let output = try executeCommand( @@ -101,9 +103,12 @@ struct GenerateManual: ParsableCommand { throw GenerateManualError.failedToRunSubprocess(error: error) } + // ToolInfoHeader is intentionally kept internal to argument parser to + // allow the library some flexibility to update/change its content/format. do { let toolInfoThin = try JSONDecoder().decode( ToolInfoHeader.self, from: data) + // verify the serialization version is known/expected guard toolInfoThin.serializationVersion == 0 else { throw GenerateManualError.unsupportedDumpHelpVersion( expected: 0,