Sources/OrgEditorAppKit/EditorCommands.swift
103 lines · 5680 bytes
1#if os(macOS)
2import OrgCore
3
4/// Every command a key, menu or the palette can run, by id.
5public struct EditorCommand: Sendable {
6 public enum Action: Sendable {
7 /// An `OrgCore` command, run through `OrgEditor.perform`.
8 case text(any OrgCommand)
9 /// Folding and other view changes the editor makes itself.
10 case view
11 /// A text-system action; `extending` is its variant that extends the selection, used
12 /// while a mark is active.
13 case edit(selector: String, extending: String? = nil)
14 /// Mark handling in the editor.
15 case mark
16 /// Handled by the app (save, quick open, palette).
17 case app
18 }
19
20 public let id: String
21 public let title: String
22 public let action: Action
23
24 /// Shown in the palette; text-system movement and editing are left to keys.
25 public var inPalette: Bool {
26 switch action {
27 case .edit, .mark: false
28 default: true
29 }
30 }
31}
32
33public enum EditorCommands {
34 public static let all: [EditorCommand] = Commands.everything.map { EditorCommand(id: $0.id, title: $0.title, action: .text($0)) } + [
35 EditorCommand(id: "org.cycle", title: "Cycle Visibility", action: .view),
36 EditorCommand(id: "org.cycle-global", title: "Cycle Global Visibility", action: .view),
37
38 EditorCommand(id: "app.save", title: "Save", action: .app),
39 EditorCommand(id: "app.quit", title: "Quit Orgstar", action: .app),
40 EditorCommand(id: "app.quick-open", title: "Quick Open…", action: .app),
41 EditorCommand(id: "app.palette", title: "Command Palette…", action: .app),
42 EditorCommand(id: "app.search", title: "Search Notes", action: .app),
43 EditorCommand(id: "app.reload-keymap", title: "Reload Keymap", action: .app),
44 EditorCommand(id: "app.refile", title: "Refile…", action: .app),
45 EditorCommand(id: "app.archive", title: "Archive Subtree", action: .app),
46 EditorCommand(id: "app.agenda", title: "Agenda", action: .app),
47 EditorCommand(id: "app.capture", title: "Capture…", action: .app),
48 EditorCommand(id: "app.board", title: "Board", action: .app),
49 EditorCommand(id: "app.clock.in", title: "Clock In", action: .app),
50 EditorCommand(id: "app.clock.out", title: "Clock Out", action: .app),
51 EditorCommand(id: "app.clock.cancel", title: "Cancel Clock", action: .app),
52 EditorCommand(id: "app.clock.goto", title: "Go to Clocked Entry", action: .app),
53 EditorCommand(id: "app.clock.report", title: "Clock Report", action: .app),
54 EditorCommand(id: "app.export.html", title: "Export to HTML", action: .app),
55 EditorCommand(id: "app.export.html-open", title: "Export to HTML and Open", action: .app),
56 EditorCommand(id: "app.export.markdown", title: "Export to Markdown", action: .app),
57 EditorCommand(id: "app.export.pdf", title: "Export to PDF with Emacs", action: .app),
58 EditorCommand(id: "app.export.odt", title: "Export to ODT with Emacs", action: .app),
59 EditorCommand(id: "app.export.latex", title: "Export to LaTeX with Emacs", action: .app),
60 EditorCommand(id: "app.export.text", title: "Export to Plain Text with Emacs", action: .app),
61
62 movement("forward-char", "Forward Character", "moveForward:"),
63 movement("backward-char", "Backward Character", "moveBackward:"),
64 movement("next-line", "Next Line", "moveDown:"),
65 movement("previous-line", "Previous Line", "moveUp:"),
66 movement("beginning-of-line", "Beginning of Line", "moveToBeginningOfParagraph:"),
67 movement("end-of-line", "End of Line", "moveToEndOfParagraph:"),
68 movement("forward-word", "Forward Word", "moveWordForward:"),
69 movement("backward-word", "Backward Word", "moveWordBackward:"),
70 movement("beginning-of-buffer", "Beginning of Buffer", "moveToBeginningOfDocument:"),
71 movement("end-of-buffer", "End of Buffer", "moveToEndOfDocument:"),
72 movement("scroll-up", "Scroll Up", "pageDown:"),
73 movement("scroll-down", "Scroll Down", "pageUp:"),
74 edit("recenter", "Recenter", "centerSelectionInVisibleArea:"),
75 edit("delete-char", "Delete Character", "deleteForward:"),
76 edit("kill-word", "Kill Word", "deleteWordForward:"),
77 edit("backward-kill-word", "Kill Word Backward", "deleteWordBackward:"),
78 edit("kill-line", "Kill Line", "deleteToEndOfParagraph:"),
79 edit("yank", "Yank", "yank:"),
80 edit("select-all", "Select All", "selectAll:"),
81 edit("kill-region", "Kill Region", "cut:"),
82 edit("copy-region", "Copy Region", "copy:"),
83 edit("undo", "Undo", "undo:"),
84 edit("transpose-chars", "Transpose Characters", "transpose:"),
85 edit("find", "Find", "performTextFinderAction:"),
86 EditorCommand(id: "edit.set-mark", title: "Set Mark", action: .mark),
87 EditorCommand(id: "edit.keyboard-quit", title: "Quit", action: .mark),
88 ]
89
90 private static func movement(_ name: String, _ title: String, _ selector: String) -> EditorCommand {
91 let extending = String(selector.dropLast()) + "AndModifySelection:"
92 return EditorCommand(id: "edit.\(name)", title: title, action: .edit(selector: selector, extending: extending))
93 }
94
95 private static func edit(_ name: String, _ title: String, _ selector: String) -> EditorCommand {
96 EditorCommand(id: "edit.\(name)", title: title, action: .edit(selector: selector))
97 }
98
99 private static let byID = Dictionary(uniqueKeysWithValues: all.map { ($0.id, $0) })
100
101 public static func command(_ id: String) -> EditorCommand? { byID[id] }
102}
103#endif