krz/orgstar

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

Sources/Orgstar/EditorView.swift

176fc78498d9ba962fa963bbeca0dd433d9e0bab
orgstar/Sources/Orgstar/EditorView.swift history · blame · raw

76 lines · 3150 bytes

 1import AppKit
 2import OrgApp
 3import OrgCore
 4import OrgEditorAppKit
 5import OrgHighlight
 6import SwiftUI
 7
 8/// The TextKit 2 editor for the session's file. A new file gets a new editor, which becomes the
 9/// session's buffer; reveal requests go to the existing one.
10struct EditorView: NSViewRepresentable {
11    /// One highlighter for every editor: it caches compiled grammars and results.
12    static let highlighter = TreeSitterHighlighter()
13
14    let session: DocumentSession
15    let commands: AppCommands
16    let keymap: Keymap
17    let modal: Bool
18    let optionAsMeta: OptionAsMeta
19    let options: EditingOptions
20    let onAppCommand: (String) -> Void
21    let onExternal: (ExternalRequest) -> Void
22
23    final class Coordinator {
24        var editor: OrgEditor?
25        var path: String?
26        var revealID = 0
27    }
28
29    func makeCoordinator() -> Coordinator { Coordinator() }
30
31    func makeNSView(context: Context) -> NSView {
32        NSView()
33    }
34
35    func updateNSView(_ container: NSView, context: Context) {
36        let coordinator = context.coordinator
37        if coordinator.path != session.path {
38            coordinator.path = session.path
39            container.subviews.forEach { $0.removeFromSuperview() }
40            coordinator.editor = nil
41            if let document = session.document {
42                let editor = OrgEditor(document: document, frame: container.bounds, editable: document.isEditable, highlighter: Self.highlighter)
43                editor.onChange = { [weak session] in session?.bufferChanged() }
44                editor.onMessage = { [weak commands] in commands?.show($0) }
45                editor.onKeysPending = { [weak commands] keys, next in
46                    commands?.pendingKeys = keys
47                    commands?.keyHints = next.map { KeyHint(key: $0.key, title: $0.title) }
48                }
49                editor.onModeChange = { [weak commands] in commands?.editorState = $0 }
50                editor.onSelectionChange = { [weak commands] in commands?.selection = $0 }
51                editor.onAppCommand = onAppCommand
52                editor.onExternal = onExternal
53                editor.onPrompt = { [weak commands] prompt, reply in commands?.ask(prompt, reply: reply) }
54                commands.editor = editor
55                let scroll = editor.makeScrollView()
56                scroll.frame = container.bounds
57                scroll.autoresizingMask = [.width, .height]
58                container.addSubview(scroll)
59                container.window?.makeFirstResponder(editor.textView)
60                coordinator.editor = editor
61                session.attach(editor)
62            }
63        }
64        if let editor = coordinator.editor {
65            if editor.keymap != keymap { editor.keymap = keymap }
66            editor.modal = modal
67            editor.optionAsMeta = optionAsMeta
68            editor.options = options
69        }
70        container.window?.isDocumentEdited = session.isDirty
71        if let request = session.revealRequest, request.id != coordinator.revealID {
72            coordinator.revealID = request.id
73            coordinator.editor?.reveal(request.offset)
74        }
75    }
76}