aboutsummaryrefslogtreecommitdiff
path: root/Sources/NorgKit/Models/NorgBlock.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Sources/NorgKit/Models/NorgBlock.swift')
-rw-r--r--Sources/NorgKit/Models/NorgBlock.swift108
1 files changed, 108 insertions, 0 deletions
diff --git a/Sources/NorgKit/Models/NorgBlock.swift b/Sources/NorgKit/Models/NorgBlock.swift
new file mode 100644
index 0000000..32e4eeb
--- /dev/null
+++ b/Sources/NorgKit/Models/NorgBlock.swift
@@ -0,0 +1,108 @@
+/// A block-level element of a Norg document, including its line position in
+/// the source.
+public enum NorgBlock: Equatable, Hashable, Sendable, Codable {
+ case heading(level: Int, status: TaskStatus?, content: [InlineSpan], line: Int)
+ case paragraph(content: [InlineSpan], line: Int)
+ case unorderedListItem(level: Int, status: TaskStatus?, content: [InlineSpan], line: Int)
+ case orderedListItem(level: Int, status: TaskStatus?, content: [InlineSpan], line: Int)
+ case quote(level: Int, status: TaskStatus?, content: [InlineSpan], line: Int)
+ case codeBlock(language: String?, code: String, line: Int)
+ case definition(title: [InlineSpan], status: TaskStatus?, body: [NorgBlock], line: Int)
+ case footnote(title: [InlineSpan], status: TaskStatus?, body: [NorgBlock], line: Int)
+ case tableCell(title: [InlineSpan], status: TaskStatus?, body: [NorgBlock], line: Int)
+ case rangedTag(name: [String], parameters: [String], content: String, line: Int)
+ case horizontalRule(line: Int)
+ case weakDelimiter(line: Int)
+ case strongDelimiter(line: Int)
+
+ /// The source line on which the block begins.
+ public var line: Int {
+ switch self {
+ case .heading(_, _, _, let line),
+ .paragraph(_, let line),
+ .unorderedListItem(_, _, _, let line),
+ .orderedListItem(_, _, _, let line),
+ .quote(_, _, _, let line),
+ .codeBlock(_, _, let line),
+ .definition(_, _, _, let line),
+ .footnote(_, _, _, let line),
+ .tableCell(_, _, _, let line),
+ .rangedTag(_, _, _, let line),
+ .horizontalRule(let line),
+ .weakDelimiter(let line),
+ .strongDelimiter(let line):
+ return line
+ }
+ }
+
+ /// The task status attached to the block, if any.
+ public var status: TaskStatus? {
+ switch self {
+ case .heading(_, let status, _, _),
+ .unorderedListItem(_, let status, _, _),
+ .orderedListItem(_, let status, _, _),
+ .quote(_, let status, _, _),
+ .definition(_, let status, _, _),
+ .footnote(_, let status, _, _),
+ .tableCell(_, let status, _, _):
+ return status
+ default:
+ return nil
+ }
+ }
+
+ /// The block's inline styled content (eg. text of a heading/paragraph, or
+ /// title of a definition/footnote).
+ public var content: [InlineSpan]? {
+ switch self {
+ case .heading(_, _, let content, _),
+ .paragraph(let content, _),
+ .unorderedListItem(_, _, let content, _),
+ .orderedListItem(_, _, let content, _),
+ .quote(_, _, let content, _):
+ return content
+ case .definition(let title, _, _, _),
+ .footnote(let title, _, _, _),
+ .tableCell(let title, _, _, _):
+ return title
+ default:
+ return nil
+ }
+ }
+
+ /// The nested blocks owned by a range-able block (definition, footnote, or
+ /// table cell).
+ public var body: [NorgBlock]? {
+ switch self {
+ case .definition(_, _, let body, _),
+ .footnote(_, _, let body, _),
+ .tableCell(_, _, let body, _):
+ return body
+ default:
+ return nil
+ }
+ }
+
+ /// Returns a copy of the block with its task status replaced (pass `nil` to
+ /// clear it). Blocks that cannot carry a status are returned unchanged.
+ public func settingStatus(_ status: TaskStatus?) -> NorgBlock {
+ switch self {
+ case .heading(let level, _, let content, let line):
+ return .heading(level: level, status: status, content: content, line: line)
+ case .unorderedListItem(let level, _, let content, let line):
+ return .unorderedListItem(level: level, status: status, content: content, line: line)
+ case .orderedListItem(let level, _, let content, let line):
+ return .orderedListItem(level: level, status: status, content: content, line: line)
+ case .quote(let level, _, let content, let line):
+ return .quote(level: level, status: status, content: content, line: line)
+ case .definition(let title, _, let body, let line):
+ return .definition(title: title, status: status, body: body, line: line)
+ case .footnote(let title, _, let body, let line):
+ return .footnote(title: title, status: status, body: body, line: line)
+ case .tableCell(let title, _, let body, let line):
+ return .tableCell(title: title, status: status, body: body, line: line)
+ default:
+ return self
+ }
+ }
+}