Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Doxygen discussion/note tags #159

Merged
merged 6 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Sources/Markdown/Base/Markup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ func makeMarkup(_ data: _MarkupData) -> Markup {
return SymbolLink(data)
case .inlineAttributes:
return InlineAttributes(data)
case .doxygenDiscussion:
return DoxygenDiscussion(data)
case .doxygenNote:
return DoxygenNote(data)
case .doxygenParam:
return DoxygenParameter(data)
case .doxygenReturns:
Expand Down
10 changes: 10 additions & 0 deletions Sources/Markdown/Base/RawMarkup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ enum RawMarkupData: Equatable {
case tableRow
case tableCell(colspan: UInt, rowspan: UInt)

case doxygenDiscussion
case doxygenNote
case doxygenParam(name: String)
case doxygenReturns
}
Expand Down Expand Up @@ -334,6 +336,14 @@ final class RawMarkup: ManagedBuffer<RawMarkupHeader, RawMarkup> {
return .create(data: .tableCell(colspan: colspan, rowspan: rowspan), parsedRange: parsedRange, children: children)
}

static func doxygenDiscussion(parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup {
return .create(data: .doxygenDiscussion, parsedRange: parsedRange, children: children)
}

static func doxygenNote(parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup {
return .create(data: .doxygenNote, parsedRange: parsedRange, children: children)
}

static func doxygenParam(name: String, parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup {
return .create(data: .doxygenParam(name: name), parsedRange: parsedRange, children: children)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2024 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Foundation

/// A parsed Doxygen `\discussion` command.
///
/// The Doxygen support in Swift-Markdown parses `\discussion` commands of the form
/// `\discussion description`, where `description` continues until the next blank
/// line or parsed command.
///
/// ```markdown
/// \discussion This object can give other objects in your program magical powers.
/// ```
public struct DoxygenDiscussion: BlockContainer {
public var _data: _MarkupData

init(_ raw: RawMarkup) throws {
guard case .doxygenDiscussion = raw.data else {
throw RawMarkup.Error.concreteConversionError(from: raw, to: DoxygenDiscussion.self)
}
let absoluteRaw = AbsoluteRawMarkup(markup: raw, metadata: MarkupMetadata(id: .newRoot(), indexInParent: 0))
self.init(_MarkupData(absoluteRaw))
}

init(_ data: _MarkupData) {
self._data = data
}

public func accept<V: MarkupVisitor>(_ visitor: inout V) -> V.Result {
return visitor.visitDoxygenDiscussion(self)
}
}

public extension DoxygenDiscussion {
/// Create a new Doxygen discussion definition.
///
/// - Parameter children: Block child elements.
init<Children: Sequence>(children: Children) where Children.Element == BlockMarkup {
try! self.init(.doxygenDiscussion(parsedRange: nil, children.map({ $0.raw.markup })))
}

/// Create a new Doxygen discussion definition.
///
/// - Parameter children: Block child elements.
init(children: BlockMarkup...) {
self.init(children: children)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2024 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Foundation

/// A parsed Doxygen `\note` command.
///
/// The Doxygen support in Swift-Markdown parses `\note` commands of the form
/// `\note description`, where `description` continues until the next blank
/// line or parsed command.
///
/// ```markdown
/// \note This method is only meant to be called an odd number of times.
/// ```
public struct DoxygenNote: BlockContainer {
public var _data: _MarkupData

init(_ raw: RawMarkup) throws {
guard case .doxygenNote = raw.data else {
throw RawMarkup.Error.concreteConversionError(from: raw, to: DoxygenNote.self)
}
let absoluteRaw = AbsoluteRawMarkup(markup: raw, metadata: MarkupMetadata(id: .newRoot(), indexInParent: 0))
self.init(_MarkupData(absoluteRaw))
}

init(_ data: _MarkupData) {
self._data = data
}

public func accept<V: MarkupVisitor>(_ visitor: inout V) -> V.Result {
return visitor.visitDoxygenNote(self)
}
}

public extension DoxygenNote {
/// Create a new Doxygen note definition.
///
/// - Parameter children: Block child elements.
init<Children: Sequence>(children: Children) where Children.Element == BlockMarkup {
try! self.init(.doxygenNote(parsedRange: nil, children.map({ $0.raw.markup })))
}

/// Create a new Doxygen note definition.
///
/// - Parameter children: Block child elements.
init(children: BlockMarkup...) {
self.init(children: children)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,15 @@ public struct DoxygenReturns: BlockContainer {
}

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

/// Create a new Doxygen parameter definition.
/// Create a new Doxygen returns definition.
///
/// - Parameter name: The name of the parameter being described.
/// - Parameter children: Block child elements.
init(children: BlockMarkup...) {
self.init(children: children)
Expand Down
2 changes: 2 additions & 0 deletions Sources/Markdown/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ add_library(Markdown
Base/MarkupData.swift
Base/PlainTextConvertibleMarkup.swift
Base/RawMarkup.swift
"Block Nodes/Block Container Blocks/Doxygen Commands/DoxygenDiscussion.swift"
"Block Nodes/Block Container Blocks/Doxygen Commands/DoxygenNote.swift"
"Block Nodes/Block Container Blocks/Doxygen Commands/DoxygenParameter.swift"
"Block Nodes/Block Container Blocks/Doxygen Commands/DoxygenReturns.swift"
"Block Nodes/Block Container Blocks/BlockDirective.swift"
Expand Down
42 changes: 26 additions & 16 deletions Sources/Markdown/Parser/BlockDirectiveParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,17 @@ struct PendingBlockDirective {

struct PendingDoxygenCommand {
enum CommandKind {
case discussion
case note
case param(name: Substring)
case returns

var debugDescription: String {
switch self {
case .discussion:
return "'discussion'"
case .note:
return "'note'"
case .param(name: let name):
return "'param' Argument: '\(name)'"
case .returns:
Expand Down Expand Up @@ -745,6 +751,10 @@ private enum ParseContainer: CustomStringConvertible {
let children = ParseContainer.lineRun(lines, isInCodeFence: false)
.convertToRawMarkup(ranges: &ranges, parent: self, options: options)
switch pendingDoxygenCommand.kind {
case .discussion:
return [.doxygenDiscussion(parsedRange: range, children)]
case .note:
return [.doxygenNote(parsedRange: range, children)]
case .param(let name):
return [.doxygenParam(name: String(name), parsedRange: range, children)]
case .returns:
Expand Down Expand Up @@ -873,7 +883,12 @@ struct ParseContainerStack {
}) else { return nil }
remainder.lexWhitespace()

let kind: PendingDoxygenCommand.CommandKind
switch name.text.lowercased() {
case "discussion":
kind = .discussion
case "note":
kind = .note
case "param":
guard let paramName = remainder.lex(until: { ch in
if ch.isWhitespace {
Expand All @@ -883,26 +898,21 @@ struct ParseContainerStack {
}
}) else { return nil }
remainder.lexWhitespace()
var pendingCommand = PendingDoxygenCommand(
atLocation: at.range!.lowerBound,
atSignIndentation: indent?.text.count ?? 0,
nameLocation: name.range!.lowerBound,
kind: .param(name: paramName.text),
endLocation: name.range!.upperBound)
pendingCommand.addLine(remainder)
return (pendingCommand, remainder)
kind = .param(name: paramName.text)
case "return", "returns", "result":
var pendingCommand = PendingDoxygenCommand(
atLocation: at.range!.lowerBound,
atSignIndentation: indent?.text.count ?? 0,
nameLocation: name.range!.lowerBound,
kind: .returns,
endLocation: name.range!.upperBound)
pendingCommand.addLine(remainder)
return (pendingCommand, remainder)
kind = .returns
default:
return nil
}

var pendingCommand = PendingDoxygenCommand(
atLocation: at.range!.lowerBound,
atSignIndentation: indent?.text.count ?? 0,
nameLocation: name.range!.lowerBound,
kind: kind,
endLocation: name.range!.upperBound)
pendingCommand.addLine(remainder)
return (pendingCommand, remainder)
}

/// Accept a trimmed line, opening new block directives as indicated by the source,
Expand Down
22 changes: 22 additions & 0 deletions Sources/Markdown/Visitor/MarkupVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,22 @@ public protocol MarkupVisitor {
*/
mutating func visitInlineAttributes(_ attributes: InlineAttributes) -> Result

/**
Visit a `DoxygenDiscussion` element and return the result.

- parameter doxygenDiscussion: A `DoxygenDiscussion` element.
- returns: The result of the visit.
*/
mutating func visitDoxygenDiscussion(_ doxygenDiscussion: DoxygenDiscussion) -> Result

/**
Visit a `DoxygenNote` element and return the result.

- parameter doxygenNote: A `DoxygenNote` element.
- returns: The result of the visit.
*/
mutating func visitDoxygenNote(_ doxygenNote: DoxygenNote) -> Result

/**
Visit a `DoxygenParam` element and return the result.

Expand Down Expand Up @@ -389,6 +405,12 @@ extension MarkupVisitor {
public mutating func visitInlineAttributes(_ attributes: InlineAttributes) -> Result {
return defaultVisit(attributes)
}
public mutating func visitDoxygenDiscussion(_ doxygenDiscussion: DoxygenDiscussion) -> Result {
return defaultVisit(doxygenDiscussion)
}
public mutating func visitDoxygenNote(_ doxygenNote: DoxygenNote) -> Result {
return defaultVisit(doxygenNote)
}
public mutating func visitDoxygenParameter(_ doxygenParam: DoxygenParameter) -> Result {
return defaultVisit(doxygenParam)
}
Expand Down
36 changes: 36 additions & 0 deletions Tests/MarkdownTests/Parsing/DoxygenCommandParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,42 @@ import XCTest
class DoxygenCommandParserTests: XCTestCase {
let parseOptions: ParseOptions = [.parseMinimalDoxygen, .parseBlockDirectives]

func testParseDiscussion() {
func assertValidParse(source: String) {
let document = Document(parsing: source, options: parseOptions)
XCTAssert(document.child(at: 0) is DoxygenDiscussion)

let expectedDump = """
Document
└─ DoxygenDiscussion
└─ Paragraph
└─ Text "The thing."
"""
XCTAssertEqual(document.debugDescription(), expectedDump)
}

assertValidParse(source: "@discussion The thing.")
assertValidParse(source: #"\discussion The thing."#)
}

func testParseNote() {
func assertValidParse(source: String) {
let document = Document(parsing: source, options: parseOptions)
XCTAssert(document.child(at: 0) is DoxygenNote)

let expectedDump = """
Document
└─ DoxygenNote
└─ Paragraph
└─ Text "The thing."
"""
XCTAssertEqual(document.debugDescription(), expectedDump)
}

assertValidParse(source: "@note The thing.")
assertValidParse(source: #"\note The thing."#)
}

func testParseParam() throws {
let source = """
@param thing The thing.
Expand Down