krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Sources/OrgCore/Semantic/HeadingInfo.swift

0271e942ff3d2f72a407b3d9a6463fab8e4b48cf
orgstar/Sources/OrgCore/Semantic/HeadingInfo.swift history · blame · raw

67 lines · 2655 bytes

 1import Foundation
 2
 3public struct Property: Sendable, Equatable {
 4    public let key: String
 5    public let value: String
 6    /// `:KEY+:` appends to the value instead of replacing it.
 7    public let additive: Bool
 8
 9    public init(key: String, value: String, additive: Bool = false) {
10        self.key = key
11        self.value = value
12        self.additive = additive
13    }
14
15    /// A `:KEY: value` or `:KEY+: value` drawer line.
16    init?(line: String) {
17        let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines)
18        guard trimmed.hasPrefix(":") else { return nil }
19        let rest = trimmed.dropFirst()
20        guard let colon = rest.firstIndex(of: ":") else { return nil }
21        var key = rest[..<colon]
22        guard !key.isEmpty else { return nil }
23        let additive = key.hasSuffix("+")
24        if additive { key = key.dropLast() }
25        let value = rest[rest.index(after: colon)...].trimmingCharacters(in: .whitespaces)
26        self.init(key: String(key), value: value, additive: additive)
27    }
28}
29
30public struct Clock: Sendable, Equatable {
31    public let start: Timestamp.Point
32    /// Nil while the clock is running.
33    public let end: Timestamp.Point?
34    /// From `=> H:MM`, when present.
35    public let minutes: Int?
36}
37
38/// One heading and the facts that belong to it directly, not to its children.
39public struct HeadingInfo: Sendable, Equatable {
40    public internal(set) var sectionRange: Range<Int>
41    public internal(set) var headingRange: Range<Int>
42    public internal(set) var level: Int
43    /// Index of the parent heading in `DocumentModel.headings`.
44    public internal(set) var parent: Int?
45    public internal(set) var todo: String?
46    public internal(set) var isDone: Bool
47    /// The cookie's value (`A`, `10`), when the heading has one.
48    public internal(set) var priority: String?
49    /// The title as written, markup included.
50    public internal(set) var title: String
51    public internal(set) var localTags: [String]
52    /// Property drawer lines in order.
53    public internal(set) var properties: [Property]
54    public internal(set) var scheduled: Timestamp?
55    public internal(set) var deadline: Timestamp?
56    public internal(set) var closed: Timestamp?
57    public internal(set) var clocks: [Clock]
58    /// Timestamps in the title and body, excluding planning and clock lines.
59    public internal(set) var timestamps: [Timestamp]
60    /// Link targets in the title and body.
61    public internal(set) var links: [String]
62
63    /// The heading's own `ID`. Never inherited.
64    public var id: String? {
65        properties.last { $0.key.uppercased() == "ID" && !$0.additive }?.value
66    }
67}