aboutsummaryrefslogtreecommitdiff
path: root/Sources/NorgKit/Models/NorgBlock.swift
blob: 32e4eeb36723a5b1a3e9c6aa2fa567860e11aa90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
    }
  }
}