krz/orgstar

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

Sources/OrgCore/Commands/Dwim.swift

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

41 lines · 2286 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        // `+org/dwim-at-point` follows a link first.
16        if Links.link(at: context.caret, text: context.text, tree: context.tree) != nil { return OpenLinkAtPoint().run(in: context) }
17        if TableRecalculate.tblfmLine(in: context) != nil { return TableRecalculate(all: true).run(in: context) }
18        if KeyContext.holds("src", in: context) { return ExecuteSrcBlock().run(in: context) }
19        if KeyContext.holds("table", in: context) {
20            let buffer = EmacsBuffer(context.text, point: context.caret, settings: context.tree.settings)
21            if buffer.firstTblfm(after: buffer.tableEnd()) != nil { return TableRecalculate(all: true).run(in: context) }
22            return TableCommand(.align).run(in: context)
23        }
24        if KeyContext.holds("item", in: context) {
25            let toggle = ToggleCheckbox()
26            if toggle.applies(in: context) { return toggle.run(in: context) }
27        }
28        guard headingOnLine(at: context.caret, in: context.tree) != nil else { return .failed("Nothing to do here") }
29        let settings = context.tree.settings
30        return commitBuffer(context) { buffer in
31            guard let current = buffer.todoKeyword,
32                  let sequence = settings.todoSequences.first(where: { ($0.active + $0.done).contains { $0.name == current } }) else { return }
33            if settings.isDone(current) {
34                buffer.setTodoKeyword(sequence.active.first?.name)
35            } else if let done = sequence.done.first?.name {
36                buffer.setTodoKeyword(done)
37            }
38            try buffer.afterTodoChange(previous: current, answers: context.answers)
39        }
40    }
41}