Sources/Orgstar/EditorView.swift
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}