krz/orgstar

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

Sources/OrgCore/Commands/Dwim.swift

abca8dc581329472f7fb8ccd3af147e5fb882f29
orgstar/Sources/OrgCore/Commands/Dwim.swift history · blame · raw

39 lines · 2146 bytes

 1import Foundation
 2
 3/// Doom's `+org/dwim-at-point` (RET in normal state): on a heading, TODO becomes the first
 4/// done keyword of its sequence and a done keyword the first TODO one; on an item, its
 5/// checkbox toggles; in a src block, the block runs; on a `#+TBLFM` line or in a table with
 6/// formulas, the table recalculates, otherwise aligns.
 7public struct OrgDwim: OrgCommand {
 8    public init() {}
 9    public var id: String { "org.dwim" }
10    public var title: String { "Act at Point" }
11
12    public func applies(in context: EditContext) -> Bool { true }
13
14    public func run(in context: EditContext) -> CommandStep {
15        if TableRecalculate.tblfmLine(in: context) != nil { return TableRecalculate(all: true).run(in: context) }
16        if KeyContext.holds("src", in: context) { return ExecuteSrcBlock().run(in: context) }
17        if KeyContext.holds("table", in: context) {
18            let buffer = EmacsBuffer(context.text, point: context.caret, settings: context.tree.settings)
19            if buffer.firstTblfm(after: buffer.tableEnd()) != nil { return TableRecalculate(all: true).run(in: context) }
20            return TableCommand(.align).run(in: context)
21        }
22        if KeyContext.holds("item", in: context) {
23            let toggle = ToggleCheckbox()
24            if toggle.applies(in: context) { return toggle.run(in: context) }
25        }
26        guard headingOnLine(at: context.caret, in: context.tree) != nil else { return .failed("Nothing to do here") }
27        let settings = context.tree.settings
28        return commitBuffer(context) { buffer in
29            guard let current = buffer.todoKeyword,
30                  let sequence = settings.todoSequences.first(where: { ($0.active + $0.done).contains { $0.name == current } }) else { return }
31            if settings.isDone(current) {
32                buffer.setTodoKeyword(sequence.active.first?.name)
33            } else if let done = sequence.done.first?.name {
34                buffer.setTodoKeyword(done)
35                if let (marker, note) = try buffer.autoRepeat(doneWord: done, previous: current) { buffer.storeNote(marker, note) }
36            }
37        }
38    }
39}