Sources/OrgEditorAppKit/EditorCommands.swift
85 lines · 4145 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.all.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.quick-open", title: "Quick Open…", action: .app),
40 EditorCommand(id: "app.palette", title: "Command Palette…", action: .app),
41 EditorCommand(id: "app.search", title: "Search Notes", action: .app),
42 EditorCommand(id: "app.reload-keymap", title: "Reload Keymap", action: .app),
43
44 movement("forward-char", "Forward Character", "moveForward:"),
45 movement("backward-char", "Backward Character", "moveBackward:"),
46 movement("next-line", "Next Line", "moveDown:"),
47 movement("previous-line", "Previous Line", "moveUp:"),
48 movement("beginning-of-line", "Beginning of Line", "moveToBeginningOfParagraph:"),
49 movement("end-of-line", "End of Line", "moveToEndOfParagraph:"),
50 movement("forward-word", "Forward Word", "moveWordForward:"),
51 movement("backward-word", "Backward Word", "moveWordBackward:"),
52 movement("beginning-of-buffer", "Beginning of Buffer", "moveToBeginningOfDocument:"),
53 movement("end-of-buffer", "End of Buffer", "moveToEndOfDocument:"),
54 movement("scroll-up", "Scroll Up", "pageDown:"),
55 movement("scroll-down", "Scroll Down", "pageUp:"),
56 edit("recenter", "Recenter", "centerSelectionInVisibleArea:"),
57 edit("delete-char", "Delete Character", "deleteForward:"),
58 edit("kill-word", "Kill Word", "deleteWordForward:"),
59 edit("backward-kill-word", "Kill Word Backward", "deleteWordBackward:"),
60 edit("kill-line", "Kill Line", "deleteToEndOfParagraph:"),
61 edit("yank", "Yank", "yank:"),
62 edit("select-all", "Select All", "selectAll:"),
63 edit("kill-region", "Kill Region", "cut:"),
64 edit("copy-region", "Copy Region", "copy:"),
65 edit("undo", "Undo", "undo:"),
66 edit("transpose-chars", "Transpose Characters", "transpose:"),
67 edit("find", "Find", "performTextFinderAction:"),
68 EditorCommand(id: "edit.set-mark", title: "Set Mark", action: .mark),
69 EditorCommand(id: "edit.keyboard-quit", title: "Quit", action: .mark),
70 ]
71
72 private static func movement(_ name: String, _ title: String, _ selector: String) -> EditorCommand {
73 let extending = String(selector.dropLast()) + "AndModifySelection:"
74 return EditorCommand(id: "edit.\(name)", title: title, action: .edit(selector: selector, extending: extending))
75 }
76
77 private static func edit(_ name: String, _ title: String, _ selector: String) -> EditorCommand {
78 EditorCommand(id: "edit.\(name)", title: title, action: .edit(selector: selector))
79 }
80
81 private static let byID = Dictionary(uniqueKeysWithValues: all.map { ($0.id, $0) })
82
83 public static func command(_ id: String) -> EditorCommand? { byID[id] }
84}
85#endif