Skip to content

Added custom inline nodes support #412

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
35 changes: 31 additions & 4 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// swift-tools-version:5.6
// swift-tools-version:5.7

import PackageDescription

let package = Package(
name: "swift-markdown-ui",
platforms: [
.macOS(.v12),
.iOS(.v15),
.iOS(.v16),
.tvOS(.v15),
.macCatalyst(.v15),
.watchOS(.v8),
Expand All @@ -15,19 +15,19 @@ let package = Package(
.library(
name: "MarkdownUI",
targets: ["MarkdownUI"]
)
),
],
dependencies: [
.package(url: "https://github.com/gonzalezreal/NetworkImage", from: "6.0.0"),
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.10.0"),
.package(url: "https://github.com/swiftlang/swift-cmark", from: "0.4.0"),
.package(id: "spm-external.cmark-gfm", exact: "0.5.0+3ccff77b2dc5b96b77db3da0d68d28068593fa53"),
],
targets: [
.target(
name: "MarkdownUI",
dependencies: [
.product(name: "cmark-gfm", package: "swift-cmark"),
.product(name: "cmark-gfm-extensions", package: "swift-cmark"),
.product(name: "cmark-gfm", package: "spm-external.cmark-gfm"),
.product(name: "cmark-gfm-extensions", package: "spm-external.cmark-gfm"),
.product(name: "NetworkImage", package: "NetworkImage"),
]
),
Expand Down
6 changes: 3 additions & 3 deletions Sources/MarkdownUI/DSL/Blocks/MarkdownContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public protocol MarkdownContentProtocol {
/// }
/// }
/// ```
public struct MarkdownContent: Equatable, MarkdownContentProtocol {
public struct MarkdownContent: Hashable, MarkdownContentProtocol, Sendable {
/// Returns a Markdown content value with the sum of the contents of all the container blocks
/// present in this content.
///
Expand Down Expand Up @@ -87,8 +87,8 @@ public struct MarkdownContent: Equatable, MarkdownContentProtocol {

/// Creates a Markdown content value from a Markdown-formatted string.
/// - Parameter markdown: A Markdown-formatted string.
public init(_ markdown: String) {
self.init(blocks: .init(markdown: markdown))
public init(_ markdown: String, extensions: [CmarkExtension] = []) {
self.init(blocks: .init(markdown: markdown, extensions: extensions))
}

/// Creates a Markdown content value composed of any number of blocks.
Expand Down
26 changes: 26 additions & 0 deletions Sources/MarkdownUI/Parser/CmarkExtension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/// Describes extension to parse custom inline nodes in cmark.
public struct CmarkExtension: Sendable {
/// Extension name to pass to cmark
public var name: String

/// Registers extension in cmark
public var register: @Sendable () -> Void

/// Name to identify cmark node
public var nodeName: String

/// This is pointer to `cmark_node`
public var makeNode: @Sendable (UnsafeMutableRawPointer) -> CustomInline?

public init(
name: String,
register: @escaping @Sendable () -> Void,
nodeName: String,
makeNode: @escaping @Sendable (UnsafeMutableRawPointer) -> CustomInline?,
) {
self.name = name
self.register = register
self.nodeName = nodeName
self.makeNode = makeNode
}
}
34 changes: 34 additions & 0 deletions Sources/MarkdownUI/Parser/CustomInline.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import SwiftUI

/// Describes custom inline node.
/// As this is inline node, it is type erased to SwiftUI's `Text`
/// to properly render inside surrounding text.
public struct CustomInline: Hashable {
/// Must uniquely identify renderers provided.
/// Failure to do so will result in undefined behavior.
public var id: String

/// Sync render must be as lightweight as possible.
public var renderSync: @Sendable () -> Text

/// Optional async renderer for heavy operations like image render/load.
public var renderAsync: (@Sendable () async -> Text)?

public init(
id: String,
renderSync: @escaping @Sendable () -> Text,
renderAsync: (@Sendable () async -> Text)? = nil,
) {
self.id = id
self.renderSync = renderSync
self.renderAsync = renderAsync
}

public static func == (lhs: CustomInline, rhs: CustomInline) -> Bool {
lhs.id == rhs.id
}

public func hash(into hasher: inout Hasher) {
hasher.combine(self.id)
}
}
1 change: 1 addition & 0 deletions Sources/MarkdownUI/Parser/InlineNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum InlineNode: Hashable, Sendable {
case strikethrough(children: [InlineNode])
case link(destination: String, children: [InlineNode])
case image(source: String, children: [InlineNode])
case custom(CustomInline)
}

extension InlineNode {
Expand Down
Loading