Skip to content

Commit cfe8c84

Browse files
authored
Add support for Doxygen discussion/note tags (#159)
* Fix DoxygenReturns doc comments * Add support for parsing Doxygen \discussion and \note block-level commands * Add some tests * Update CMakeLists.txt
1 parent 0b59ad6 commit cfe8c84

File tree

9 files changed

+214
-20
lines changed

9 files changed

+214
-20
lines changed

Sources/Markdown/Base/Markup.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ func makeMarkup(_ data: _MarkupData) -> Markup {
7171
return SymbolLink(data)
7272
case .inlineAttributes:
7373
return InlineAttributes(data)
74+
case .doxygenDiscussion:
75+
return DoxygenDiscussion(data)
76+
case .doxygenNote:
77+
return DoxygenNote(data)
7478
case .doxygenParam:
7579
return DoxygenParameter(data)
7680
case .doxygenReturns:

Sources/Markdown/Base/RawMarkup.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ enum RawMarkupData: Equatable {
5252
case tableRow
5353
case tableCell(colspan: UInt, rowspan: UInt)
5454

55+
case doxygenDiscussion
56+
case doxygenNote
5557
case doxygenParam(name: String)
5658
case doxygenReturns
5759
}
@@ -334,6 +336,14 @@ final class RawMarkup: ManagedBuffer<RawMarkupHeader, RawMarkup> {
334336
return .create(data: .tableCell(colspan: colspan, rowspan: rowspan), parsedRange: parsedRange, children: children)
335337
}
336338

339+
static func doxygenDiscussion(parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup {
340+
return .create(data: .doxygenDiscussion, parsedRange: parsedRange, children: children)
341+
}
342+
343+
static func doxygenNote(parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup {
344+
return .create(data: .doxygenNote, parsedRange: parsedRange, children: children)
345+
}
346+
337347
static func doxygenParam(name: String, parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup {
338348
return .create(data: .doxygenParam(name: name), parsedRange: parsedRange, children: children)
339349
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2024 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 parsed Doxygen `\discussion` command.
14+
///
15+
/// The Doxygen support in Swift-Markdown parses `\discussion` commands of the form
16+
/// `\discussion description`, where `description` continues until the next blank
17+
/// line or parsed command.
18+
///
19+
/// ```markdown
20+
/// \discussion This object can give other objects in your program magical powers.
21+
/// ```
22+
public struct DoxygenDiscussion: BlockContainer {
23+
public var _data: _MarkupData
24+
25+
init(_ raw: RawMarkup) throws {
26+
guard case .doxygenDiscussion = raw.data else {
27+
throw RawMarkup.Error.concreteConversionError(from: raw, to: DoxygenDiscussion.self)
28+
}
29+
let absoluteRaw = AbsoluteRawMarkup(markup: raw, metadata: MarkupMetadata(id: .newRoot(), indexInParent: 0))
30+
self.init(_MarkupData(absoluteRaw))
31+
}
32+
33+
init(_ data: _MarkupData) {
34+
self._data = data
35+
}
36+
37+
public func accept<V: MarkupVisitor>(_ visitor: inout V) -> V.Result {
38+
return visitor.visitDoxygenDiscussion(self)
39+
}
40+
}
41+
42+
public extension DoxygenDiscussion {
43+
/// Create a new Doxygen discussion definition.
44+
///
45+
/// - Parameter children: Block child elements.
46+
init<Children: Sequence>(children: Children) where Children.Element == BlockMarkup {
47+
try! self.init(.doxygenDiscussion(parsedRange: nil, children.map({ $0.raw.markup })))
48+
}
49+
50+
/// Create a new Doxygen discussion definition.
51+
///
52+
/// - Parameter children: Block child elements.
53+
init(children: BlockMarkup...) {
54+
self.init(children: children)
55+
}
56+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2024 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 parsed Doxygen `\note` command.
14+
///
15+
/// The Doxygen support in Swift-Markdown parses `\note` commands of the form
16+
/// `\note description`, where `description` continues until the next blank
17+
/// line or parsed command.
18+
///
19+
/// ```markdown
20+
/// \note This method is only meant to be called an odd number of times.
21+
/// ```
22+
public struct DoxygenNote: BlockContainer {
23+
public var _data: _MarkupData
24+
25+
init(_ raw: RawMarkup) throws {
26+
guard case .doxygenNote = raw.data else {
27+
throw RawMarkup.Error.concreteConversionError(from: raw, to: DoxygenNote.self)
28+
}
29+
let absoluteRaw = AbsoluteRawMarkup(markup: raw, metadata: MarkupMetadata(id: .newRoot(), indexInParent: 0))
30+
self.init(_MarkupData(absoluteRaw))
31+
}
32+
33+
init(_ data: _MarkupData) {
34+
self._data = data
35+
}
36+
37+
public func accept<V: MarkupVisitor>(_ visitor: inout V) -> V.Result {
38+
return visitor.visitDoxygenNote(self)
39+
}
40+
}
41+
42+
public extension DoxygenNote {
43+
/// Create a new Doxygen note definition.
44+
///
45+
/// - Parameter children: Block child elements.
46+
init<Children: Sequence>(children: Children) where Children.Element == BlockMarkup {
47+
try! self.init(.doxygenNote(parsedRange: nil, children.map({ $0.raw.markup })))
48+
}
49+
50+
/// Create a new Doxygen note definition.
51+
///
52+
/// - Parameter children: Block child elements.
53+
init(children: BlockMarkup...) {
54+
self.init(children: children)
55+
}
56+
}

Sources/Markdown/Block Nodes/Block Container Blocks/Doxygen Commands/DoxygenReturns.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,15 @@ public struct DoxygenReturns: BlockContainer {
4040
}
4141

4242
public extension DoxygenReturns {
43-
/// Create a new Doxygen parameter definition.
43+
/// Create a new Doxygen returns definition.
4444
///
45-
/// - Parameter name: The name of the parameter being described.
4645
/// - Parameter children: Block child elements.
4746
init<Children: Sequence>(children: Children) where Children.Element == BlockMarkup {
4847
try! self.init(.doxygenReturns(parsedRange: nil, children.map({ $0.raw.markup })))
4948
}
5049

51-
/// Create a new Doxygen parameter definition.
50+
/// Create a new Doxygen returns definition.
5251
///
53-
/// - Parameter name: The name of the parameter being described.
5452
/// - Parameter children: Block child elements.
5553
init(children: BlockMarkup...) {
5654
self.init(children: children)

Sources/Markdown/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ add_library(Markdown
1717
Base/MarkupData.swift
1818
Base/PlainTextConvertibleMarkup.swift
1919
Base/RawMarkup.swift
20+
"Block Nodes/Block Container Blocks/Doxygen Commands/DoxygenDiscussion.swift"
21+
"Block Nodes/Block Container Blocks/Doxygen Commands/DoxygenNote.swift"
2022
"Block Nodes/Block Container Blocks/Doxygen Commands/DoxygenParameter.swift"
2123
"Block Nodes/Block Container Blocks/Doxygen Commands/DoxygenReturns.swift"
2224
"Block Nodes/Block Container Blocks/BlockDirective.swift"

Sources/Markdown/Parser/BlockDirectiveParser.swift

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,17 @@ struct PendingBlockDirective {
224224

225225
struct PendingDoxygenCommand {
226226
enum CommandKind {
227+
case discussion
228+
case note
227229
case param(name: Substring)
228230
case returns
229231

230232
var debugDescription: String {
231233
switch self {
234+
case .discussion:
235+
return "'discussion'"
236+
case .note:
237+
return "'note'"
232238
case .param(name: let name):
233239
return "'param' Argument: '\(name)'"
234240
case .returns:
@@ -745,6 +751,10 @@ private enum ParseContainer: CustomStringConvertible {
745751
let children = ParseContainer.lineRun(lines, isInCodeFence: false)
746752
.convertToRawMarkup(ranges: &ranges, parent: self, options: options)
747753
switch pendingDoxygenCommand.kind {
754+
case .discussion:
755+
return [.doxygenDiscussion(parsedRange: range, children)]
756+
case .note:
757+
return [.doxygenNote(parsedRange: range, children)]
748758
case .param(let name):
749759
return [.doxygenParam(name: String(name), parsedRange: range, children)]
750760
case .returns:
@@ -873,7 +883,12 @@ struct ParseContainerStack {
873883
}) else { return nil }
874884
remainder.lexWhitespace()
875885

886+
let kind: PendingDoxygenCommand.CommandKind
876887
switch name.text.lowercased() {
888+
case "discussion":
889+
kind = .discussion
890+
case "note":
891+
kind = .note
877892
case "param":
878893
guard let paramName = remainder.lex(until: { ch in
879894
if ch.isWhitespace {
@@ -883,26 +898,21 @@ struct ParseContainerStack {
883898
}
884899
}) else { return nil }
885900
remainder.lexWhitespace()
886-
var pendingCommand = PendingDoxygenCommand(
887-
atLocation: at.range!.lowerBound,
888-
atSignIndentation: indent?.text.count ?? 0,
889-
nameLocation: name.range!.lowerBound,
890-
kind: .param(name: paramName.text),
891-
endLocation: name.range!.upperBound)
892-
pendingCommand.addLine(remainder)
893-
return (pendingCommand, remainder)
901+
kind = .param(name: paramName.text)
894902
case "return", "returns", "result":
895-
var pendingCommand = PendingDoxygenCommand(
896-
atLocation: at.range!.lowerBound,
897-
atSignIndentation: indent?.text.count ?? 0,
898-
nameLocation: name.range!.lowerBound,
899-
kind: .returns,
900-
endLocation: name.range!.upperBound)
901-
pendingCommand.addLine(remainder)
902-
return (pendingCommand, remainder)
903+
kind = .returns
903904
default:
904905
return nil
905906
}
907+
908+
var pendingCommand = PendingDoxygenCommand(
909+
atLocation: at.range!.lowerBound,
910+
atSignIndentation: indent?.text.count ?? 0,
911+
nameLocation: name.range!.lowerBound,
912+
kind: kind,
913+
endLocation: name.range!.upperBound)
914+
pendingCommand.addLine(remainder)
915+
return (pendingCommand, remainder)
906916
}
907917

908918
/// Accept a trimmed line, opening new block directives as indicated by the source,

Sources/Markdown/Visitor/MarkupVisitor.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,22 @@ public protocol MarkupVisitor {
275275
*/
276276
mutating func visitInlineAttributes(_ attributes: InlineAttributes) -> Result
277277

278+
/**
279+
Visit a `DoxygenDiscussion` element and return the result.
280+
281+
- parameter doxygenDiscussion: A `DoxygenDiscussion` element.
282+
- returns: The result of the visit.
283+
*/
284+
mutating func visitDoxygenDiscussion(_ doxygenDiscussion: DoxygenDiscussion) -> Result
285+
286+
/**
287+
Visit a `DoxygenNote` element and return the result.
288+
289+
- parameter doxygenNote: A `DoxygenNote` element.
290+
- returns: The result of the visit.
291+
*/
292+
mutating func visitDoxygenNote(_ doxygenNote: DoxygenNote) -> Result
293+
278294
/**
279295
Visit a `DoxygenParam` element and return the result.
280296

@@ -389,6 +405,12 @@ extension MarkupVisitor {
389405
public mutating func visitInlineAttributes(_ attributes: InlineAttributes) -> Result {
390406
return defaultVisit(attributes)
391407
}
408+
public mutating func visitDoxygenDiscussion(_ doxygenDiscussion: DoxygenDiscussion) -> Result {
409+
return defaultVisit(doxygenDiscussion)
410+
}
411+
public mutating func visitDoxygenNote(_ doxygenNote: DoxygenNote) -> Result {
412+
return defaultVisit(doxygenNote)
413+
}
392414
public mutating func visitDoxygenParameter(_ doxygenParam: DoxygenParameter) -> Result {
393415
return defaultVisit(doxygenParam)
394416
}

Tests/MarkdownTests/Parsing/DoxygenCommandParserTests.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,42 @@ import XCTest
1414
class DoxygenCommandParserTests: XCTestCase {
1515
let parseOptions: ParseOptions = [.parseMinimalDoxygen, .parseBlockDirectives]
1616

17+
func testParseDiscussion() {
18+
func assertValidParse(source: String) {
19+
let document = Document(parsing: source, options: parseOptions)
20+
XCTAssert(document.child(at: 0) is DoxygenDiscussion)
21+
22+
let expectedDump = """
23+
Document
24+
└─ DoxygenDiscussion
25+
└─ Paragraph
26+
└─ Text "The thing."
27+
"""
28+
XCTAssertEqual(document.debugDescription(), expectedDump)
29+
}
30+
31+
assertValidParse(source: "@discussion The thing.")
32+
assertValidParse(source: #"\discussion The thing."#)
33+
}
34+
35+
func testParseNote() {
36+
func assertValidParse(source: String) {
37+
let document = Document(parsing: source, options: parseOptions)
38+
XCTAssert(document.child(at: 0) is DoxygenNote)
39+
40+
let expectedDump = """
41+
Document
42+
└─ DoxygenNote
43+
└─ Paragraph
44+
└─ Text "The thing."
45+
"""
46+
XCTAssertEqual(document.debugDescription(), expectedDump)
47+
}
48+
49+
assertValidParse(source: "@note The thing.")
50+
assertValidParse(source: #"\note The thing."#)
51+
}
52+
1753
func testParseParam() throws {
1854
let source = """
1955
@param thing The thing.

0 commit comments

Comments
 (0)