Sources/OrgCore/Commands/Properties.swift
48 lines · 2347 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 buffer.setTodoKeyword(keyword)
40 guard previous != keyword else { return }
41 if let keyword, settings.isDone(keyword), !(previous.map(settings.isDone) ?? false),
42 let (marker, note) = try buffer.autoRepeat(doneWord: keyword, previous: previous) {
43 buffer.storeNote(marker, note)
44 }
45 }
46 }
47 }
48}