krz/orgstar

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

Sources/OrgCore/Commands/Properties.swift

322a87d47526d505d903e05d14e65a5464e1468f
orgstar/Sources/OrgCore/Commands/Properties.swift history · blame · raw

45 lines · 2179 bytes

 1import Foundation
 2
 3/// `org-set-property` / `org-entry-put` for one entry.
 4public struct SetProperty: OrgCommand {
 5    public let key: String
 6    public let value: String
 7    public init(key: String, value: String) {
 8        self.key = key
 9        self.value = value
10    }
11    public var id: String { "org.property.set" }
12    public var title: String { "Set Property" }
13    public func applies(in context: EditContext) -> Bool { entryHeading(at: context.caret, in: context.tree) != nil }
14    public func run(in context: EditContext) -> CommandStep {
15        if key.uppercased() == "TODO" { return SetTodo(keyword: value.isEmpty ? nil : value).run(in: context) }
16        guard key.range(of: "^[^\\s:]+$", options: .regularExpression) != nil else { return .failed("Invalid property name: \"\(key)\"") }
17        return commitBuffer(context) { buffer in
18            buffer.saveExcursion { buffer.entryPut(key, value) }
19        }
20    }
21}
22
23/// `org-todo` with a keyword (nil for none), with the statistics and repeat it brings.
24public struct SetTodo: OrgCommand {
25    public let keyword: String?
26    public init(keyword: String?) { self.keyword = keyword }
27    public var id: String { "org.todo.set" }
28    public var title: String { "Set TODO Keyword" }
29    public func applies(in context: EditContext) -> Bool { entryHeading(at: context.caret, in: context.tree) != nil }
30    public func run(in context: EditContext) -> CommandStep {
31        guard entryHeading(at: context.caret, in: context.tree) != nil else { return .failed("Before first headline") }
32        let settings = context.tree.settings
33        if let keyword, !settings.todoSequences.contains(where: { ($0.active + $0.done).contains { $0.name == keyword } }) {
34            return .failed("\"\(keyword)\" is not a valid TODO state")
35        }
36        return commitBuffer(context) { buffer in
37            try buffer.saveExcursion {
38                let previous = buffer.todoKeyword
39                try buffer.checkTodoBlocking(from: previous, to: keyword)
40                buffer.setTodoKeyword(keyword)
41                try buffer.afterTodoChange(previous: previous, answers: context.answers)
42            }
43        }
44    }
45}