krz/orgstar

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

Sources/Orgstar/BlockEditorView.swift

191ddb506f3a91b93c755ba6e8123a4e8ac4f90d
orgstar/Sources/Orgstar/BlockEditorView.swift history · blame · raw

65 lines · 2456 bytes

 1import AppKit
 2import OrgCore
 3import OrgDocument
 4import OrgEditorAppKit
 5import SwiftUI
 6
 7/// `org-edit-special` for a block: its text in an editor of its own, highlighted as its
 8/// language. ⌘Return or C-c ' puts it back; Escape or C-c C-k leaves it.
 9struct BlockEditorView: View {
10    let edit: BlockEdit
11    let finish: (String?) -> Void
12    @State private var box = EditorBox()
13
14    var body: some View {
15        VStack(alignment: .leading, spacing: 8) {
16            Text(edit.title)
17                .font(.headline)
18            CodeEditor(text: edit.content, language: edit.language, box: box) { save in finish(save ? box.text : nil) }
19                .frame(minWidth: 560, minHeight: 320)
20                .border(.separator)
21            HStack {
22                Text("C-c ' or ⌘Return saves, Escape or C-c C-k leaves").font(.caption).foregroundStyle(.secondary)
23                Spacer()
24                Button("Cancel", role: .cancel) { finish(nil) }
25                    .keyboardShortcut(.cancelAction)
26                Button(edit.kind == "field" ? "Save Field" : "Save Block") { finish(box.text) }
27                    .keyboardShortcut(.return, modifiers: .command)
28            }
29        }
30        .padding(16)
31    }
32}
33
34/// Holds the editor so the view can read its text when saving.
35@MainActor
36final class EditorBox {
37    var editor: OrgEditor?
38    var text: String { editor?.document.text ?? "" }
39}
40
41/// An `OrgEditor` in plain mode for the block's language.
42struct CodeEditor: NSViewRepresentable {
43    let text: String
44    let language: String?
45    let box: EditorBox
46    let done: (Bool) -> Void
47
48    func makeNSView(context: Context) -> NSScrollView {
49        let editor = OrgEditor(document: DocumentState(bytes: [UInt8](text.utf8)), highlighter: EditorView.highlighter, mode: .plain(language: language))
50        editor.keymap = Keymap([
51            KeyBinding(keys: KeySequence.parse("C-c '")!, command: "app.block.save"),
52            KeyBinding(keys: KeySequence.parse("C-c C-k")!, command: "app.block.cancel"),
53        ])
54        editor.onAppCommand = { id in
55            if id == "app.block.save" { done(true) }
56            if id == "app.block.cancel" { done(false) }
57        }
58        box.editor = editor
59        let scroll = editor.makeScrollView()
60        DispatchQueue.main.async { scroll.window?.makeFirstResponder(editor.textView) }
61        return scroll
62    }
63
64    func updateNSView(_ view: NSScrollView, context: Context) {}
65}