Skip to content
Merged
36 changes: 26 additions & 10 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
name: Swift
on: [push, pull_request]
on: [pull_request]

jobs:
test:
runs-on: macos-latest
runs-on: macos-15

steps:
- uses: actions/checkout@v1
- name: Run tests
run: set -o pipefail && time xcodebuild clean test -scheme MarkdownSyntax -sdk macosx -enableCodeCoverage YES | xcpretty
- name: Codecov
uses: codecov/[email protected]
with:
token: ${{ secrets.CODECOV_TOKEN }}
- uses: actions/[email protected]
- uses: maxim-lobanov/[email protected]
with:
xcode-version: '16.4.0'
- name: Run tests
run: swift test --enable-code-coverage
- name: Generate coverage report
run: |
CODECOV_DIR=$(find .build -name 'codecov' -type d | head -1)

xcrun llvm-profdata merge -sparse "$CODECOV_DIR"/*.profraw -o default.profdata
PROFDATA_PATH="default.profdata"

ALL_OBJECT_FILES=$(find .build -name "*.o" -type f)
SOURCE_OBJECT_FILES=$(echo "$ALL_OBJECT_FILES" | grep "MarkdownSyntaxTests.build")

xcrun llvm-cov report -instr-profile $PROFDATA_PATH $SOURCE_OBJECT_FILES
xcrun llvm-cov export -format="lcov" -instr-profile $PROFDATA_PATH $SOURCE_OBJECT_FILES >> lcov.info
- name: Upload coverage to Codecov
uses: codecov/[email protected]
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: lcov.info
8 changes: 4 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// swift-tools-version:5.3
// swift-tools-version:6.1

import PackageDescription

let package = Package(
name: "MarkdownSyntax",
platforms: [.macOS(.v10_10), .iOS(.v9), .tvOS(.v9), .watchOS(.v2)],
platforms: [.macOS(.v10_13), .iOS(.v12), .tvOS(.v12), .watchOS(.v4)],
products: [
.library(name: "MarkdownSyntax", targets: ["MarkdownSyntax"]),
],
dependencies: [
.package(name: "cmark_gfm", url: "https://github.com/hebertialmeida/swift-cmark-gfm", .upToNextMajor(from: "1.1.0"))
.package(url: "https://github.com/hebertialmeida/swift-cmark-gfm", .upToNextMajor(from: "1.1.0"))
],
targets: [
.target(name: "MarkdownSyntax", dependencies: ["cmark_gfm"]),
.target(name: "MarkdownSyntax", dependencies: [.product(name: "cmark_gfm", package: "swift-cmark-gfm")]),
.testTarget(name: "MarkdownSyntaxTests", dependencies: ["MarkdownSyntax"]),
]
)
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ MarkdownSyntax is a wrapper on top of the Github Flavoured Markdown that conform

```swift
let input = "Hi this is **alpha**"
let tree = try Markdown(text: input).parse()
let tree = try await Markdown(text: input).parse()
```

Outputs a normalized tree:
Expand All @@ -20,41 +20,41 @@ Root(
Paragraph(
children: [
Text(
value: "Hi this is ",
value: "Hi this is ",
position: Position(
start: Point(line: 1, column: 1, offset: 0),
end: Point(line: 1, column: 11, offset: 10),
start: Point(line: 1, column: 1, offset: 0),
end: Point(line: 1, column: 11, offset: 10),
indent: nil
)
),
),
Strong(
children: [
Text(
value: "alpha",
value: "alpha",
position: Position(
start: Point(line: 1, column: 14, offset: 13),
end: Point(line: 1, column: 18, offset: 17),
start: Point(line: 1, column: 14, offset: 13),
end: Point(line: 1, column: 18, offset: 17),
indent: nil
)
)
],
],
position: Position(
start: Point(line: 1, column: 12, offset: 11),
end: Point(line: 1, column: 20, offset: 19),
start: Point(line: 1, column: 12, offset: 11),
end: Point(line: 1, column: 20, offset: 19),
indent: nil
)
)
],
],
position: Position(
start: Point(line: 1, column: 1, offset: 0),
end: Point(line: 1, column: 20, offset: 19),
start: Point(line: 1, column: 1, offset: 0),
end: Point(line: 1, column: 20, offset: 19),
indent: nil
)
)
],
],
position: Position(
start: Point(line: 1, column: 1, offset: 0),
end: Point(line: 1, column: 20, offset: 19),
start: Point(line: 1, column: 1, offset: 0),
end: Point(line: 1, column: 20, offset: 19),
indent: nil
)
)
Expand All @@ -71,7 +71,7 @@ Once you have your Swift package set up, adding MarkdownSyntax as a dependency i

```swift
dependencies: [
.package(url: "https://github.com/hebertialmeida/MarkdownSyntax", from: "1.1.0")
.package(url: "https://github.com/hebertialmeida/MarkdownSyntax", from: "1.2.0")
]
```

Expand Down
26 changes: 13 additions & 13 deletions Sources/MarkdownSyntax/CMark/CMDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public enum CMDocumentError: Error {
}

/// Represents a cmark document.
public class CMDocument {
public class CMDocument: @unchecked Sendable {

/// The root node of the document.
public let node: CMNode
Expand Down Expand Up @@ -117,8 +117,8 @@ public extension CMDocument {
/// `CMDocumentError.renderError` if there is an error rendering the HTML.
/// - Returns:
/// The HTML as a string.
func renderHtml() throws -> String {
return try node.renderHtml(options, extensions: extensions)
func renderHtml() async throws -> String {
try await node.renderHtml(options, extensions: extensions)
}

/// Renders the document as XML.
Expand All @@ -127,8 +127,8 @@ public extension CMDocument {
/// `CMDocumentError.renderError` if there is an error rendering the XML.
/// - Returns:
/// The XML as a string.
func renderXml() throws -> String {
return try node.renderXml(options)
func renderXml() async throws -> String {
try await node.renderXml(options)
}

/// Renders the document as groff man page.
Expand All @@ -139,8 +139,8 @@ public extension CMDocument {
/// `CMDocumentError.renderError` if there is an error rendering the man page.
/// - Returns:
/// The man page as a string.
func renderMan(width: Int32) throws -> String {
return try node.renderMan(options, width: width)
func renderMan(width: Int32) async throws -> String {
try await node.renderMan(options, width: width)
}

/// Renders the document as common mark.
Expand All @@ -151,8 +151,8 @@ public extension CMDocument {
/// `CMDocumentError.renderError` if there is an error rendering the common mark.
/// - Returns:
/// The common mark as a string.
func renderCommonMark(width: Int32) throws -> String {
return try node.renderCommonMark(options, width: width)
func renderCommonMark(width: Int32) async throws -> String {
try await node.renderCommonMark(options, width: width)
}

/// Renders the document as Latex.
Expand All @@ -163,8 +163,8 @@ public extension CMDocument {
/// `CMDocumentError.renderError` if there is an error rendering the Latex.
/// - Returns:
/// The Latex as a string.
func renderLatex(width: Int32) throws -> String {
return try node.renderLatex(options, width: width)
func renderLatex(width: Int32) async throws -> String {
try await node.renderLatex(options, width: width)
}

/// Renders the document as plain text.
Expand All @@ -175,8 +175,8 @@ public extension CMDocument {
/// `CMDocumentError.renderError` if there is an error rendering the plain text.
/// - Returns:
/// The plain text as a string.
func renderPlainText(width: Int32) throws -> String {
return try node.renderPlainText(options, width: width)
func renderPlainText(width: Int32) async throws -> String {
try await node.renderPlainText(options, width: width)
}

}
2 changes: 1 addition & 1 deletion Sources/MarkdownSyntax/CMark/CMDocumentOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import cmark_gfm

/// Represents a cmark document option.
public struct CMDocumentOption: OptionSet {
public struct CMDocumentOption: OptionSet, Sendable {

/// The raw value.
public let rawValue: Int32
Expand Down
2 changes: 1 addition & 1 deletion Sources/MarkdownSyntax/CMark/CMExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ enum CMExtensionName: String {
}

/// Represents a cmark extension option.
public struct CMExtensionOption: OptionSet {
public struct CMExtensionOption: OptionSet, Sendable {

/// The raw value.
public let rawValue: Int32
Expand Down
24 changes: 12 additions & 12 deletions Sources/MarkdownSyntax/CMark/CMNode+Render.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public extension CMNode {
/// `CMDocumentError.renderError` if there is an error rendering the HTML.
/// - Returns:
/// The HTML as a string.
func renderHtml(_ options: CMDocumentOption, extensions: CMExtensionOption) throws -> String {
func renderHtml(_ options: CMDocumentOption, extensions: CMExtensionOption) async throws -> String {
var htmlExtensions: UnsafeMutablePointer<cmark_llist>?

if extensions.contains(.tagfilters), let tagfilter = cmark_find_syntax_extension("tagfilter") {
Expand All @@ -35,7 +35,7 @@ public extension CMNode {
free(buffer)
}

guard let html = String(validatingUTF8: buffer) else {
guard let html = String(validatingCString: buffer) else {
throw CMDocumentError.renderError
}

Expand All @@ -50,7 +50,7 @@ public extension CMNode {
/// `CMDocumentError.renderError` if there is an error rendering the XML.
/// - Returns:
/// The XML as a string.
func renderXml(_ options: CMDocumentOption) throws -> String {
func renderXml(_ options: CMDocumentOption) async throws -> String {
guard let buffer = cmark_render_xml(cmarkNode, options.rawValue) else {
throw CMDocumentError.renderError
}
Expand All @@ -59,7 +59,7 @@ public extension CMNode {
free(buffer)
}

guard let xml = String(validatingUTF8: buffer) else {
guard let xml = String(validatingCString: buffer) else {
throw CMDocumentError.renderError
}

Expand All @@ -75,7 +75,7 @@ public extension CMNode {
/// `CMDocumentError.renderError` if there is an error rendering the man page.
/// - Returns:
/// The man page as a string.
func renderMan(_ options: CMDocumentOption, width: Int32) throws -> String {
func renderMan(_ options: CMDocumentOption, width: Int32) async throws -> String {
guard let buffer = cmark_render_man(cmarkNode, options.rawValue, width) else {
throw CMDocumentError.renderError
}
Expand All @@ -84,7 +84,7 @@ public extension CMNode {
free(buffer)
}

guard let man = String(validatingUTF8: buffer) else {
guard let man = String(validatingCString: buffer) else {
throw CMDocumentError.renderError
}

Expand All @@ -100,7 +100,7 @@ public extension CMNode {
/// `CMDocumentError.renderError` if there is an error rendering the common mark.
/// - Returns:
/// The common mark as a string.
func renderCommonMark(_ options: CMDocumentOption, width: Int32) throws -> String {
func renderCommonMark(_ options: CMDocumentOption, width: Int32) async throws -> String {
guard let buffer = cmark_render_commonmark(cmarkNode, options.rawValue, width) else {
throw CMDocumentError.renderError
}
Expand All @@ -109,7 +109,7 @@ public extension CMNode {
free(buffer)
}

guard let commonMark = String(validatingUTF8: buffer) else {
guard let commonMark = String(validatingCString: buffer) else {
throw CMDocumentError.renderError
}

Expand All @@ -125,7 +125,7 @@ public extension CMNode {
/// `CMDocumentError.renderError` if there is an error rendering the Latex.
/// - Returns:
/// The Latex as a string.
func renderLatex(_ options: CMDocumentOption, width: Int32) throws -> String {
func renderLatex(_ options: CMDocumentOption, width: Int32) async throws -> String {
guard let buffer = cmark_render_latex(cmarkNode, options.rawValue, width) else {
throw CMDocumentError.renderError
}
Expand All @@ -134,7 +134,7 @@ public extension CMNode {
free(buffer)
}

guard let latex = String(validatingUTF8: buffer) else {
guard let latex = String(validatingCString: buffer) else {
throw CMDocumentError.renderError
}

Expand All @@ -150,7 +150,7 @@ public extension CMNode {
/// `CMDocumentError.renderError` if there is an error rendering the plain text.
/// - Returns:
/// The plain text as a string.
func renderPlainText(_ options: CMDocumentOption, width: Int32) throws -> String {
func renderPlainText(_ options: CMDocumentOption, width: Int32) async throws -> String {
guard let buffer = cmark_render_plaintext(cmarkNode, options.rawValue, width) else {
throw CMDocumentError.renderError
}
Expand All @@ -159,7 +159,7 @@ public extension CMNode {
free(buffer)
}

guard let text = String(validatingUTF8: buffer) else {
guard let text = String(validatingCString: buffer) else {
throw CMDocumentError.renderError
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/MarkdownSyntax/CMark/CMNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import struct Foundation.URL
import cmark_gfm

/// Represents a cmark node.
public class CMNode {
public final class CMNode: @unchecked Sendable {

/// The underlying cmark node pointer.
public let cmarkNode: UnsafeMutablePointer<cmark_node>
Expand Down
24 changes: 19 additions & 5 deletions Sources/MarkdownSyntax/CMark/CMNodeType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,38 @@
import cmark_gfm

/// Represents a cmark extension node type.
public enum CMNodeExtensionType: Equatable {
public enum CMNodeExtensionType: Equatable, Sendable {
case strikethrough
case table
case tableRow
case tableCell
case other(UInt32)

/// Ideally this should be
/// CMARK_NODE_STRIKETHROUGH.rawValue,
/// CMARK_NODE_TABLE.rawValue,
/// CMARK_NODE_TABLE_ROW.rawValue,
/// CMARK_NODE_TABLE_CELL.rawValue
///
/// But Swift 6 strict concurrency complains about that.
private struct CMarkConstants {
static let strikethrough: UInt32 = 49164 // 0xBFFC
static let table: UInt32 = 32780 // 0x800C
static let tableRow: UInt32 = 32781 // 0x800D
static let tableCell: UInt32 = 32782 // 0x800E
}

/// The raw value.
var rawValue: UInt32 {
switch self {
case .strikethrough:
return CMARK_NODE_STRIKETHROUGH.rawValue
return CMarkConstants.strikethrough
case .table:
return CMARK_NODE_TABLE.rawValue
return CMarkConstants.table
case .tableRow:
return CMARK_NODE_TABLE_ROW.rawValue
return CMarkConstants.tableRow
case .tableCell:
return CMARK_NODE_TABLE_CELL.rawValue
return CMarkConstants.tableCell
case let .other(rawValue):
return rawValue
}
Expand Down
Loading