aboutsummaryrefslogtreecommitdiff
path: root/Map/Presentation/Base Components/MapTextEditor.swift
diff options
context:
space:
mode:
authorRuben Beltran del Rio <ruben@unlimited.pizza>2023-05-07 15:22:54 +0200
committerRuben Beltran del Rio <ruben@unlimited.pizza>2023-05-07 15:22:54 +0200
commitfdb4633d3e9158e457d57e820df6e1efb4df39c2 (patch)
tree176cad99cb9befc0a65a7af02561019e811814de /Map/Presentation/Base Components/MapTextEditor.swift
parent75a0e4509a70055851b085f3f7293ae1cf48164c (diff)
Update to support notes + new style2.0.0
Diffstat (limited to 'Map/Presentation/Base Components/MapTextEditor.swift')
-rw-r--r--Map/Presentation/Base Components/MapTextEditor.swift154
1 files changed, 154 insertions, 0 deletions
diff --git a/Map/Presentation/Base Components/MapTextEditor.swift b/Map/Presentation/Base Components/MapTextEditor.swift
new file mode 100644
index 0000000..f3838a6
--- /dev/null
+++ b/Map/Presentation/Base Components/MapTextEditor.swift
@@ -0,0 +1,154 @@
+import Cocoa
+import SwiftUI
+
+class MapTextEditorController: NSViewController {
+
+ @Binding var text: String
+ let onChange: () -> Void
+
+ private let vertexRegex = MapParsingPatterns.vertex
+ private let edgeRegex = MapParsingPatterns.edge
+ private let blockerRegex = MapParsingPatterns.blocker
+ private let opportunityRegex = MapParsingPatterns.opportunity
+ private let noteRegex = MapParsingPatterns.note
+ private let stageRegex = MapParsingPatterns.stage
+
+ private let changeDebouncer: Debouncer = Debouncer(seconds: 1)
+
+ init(text: Binding<String>, onChange: @escaping () -> Void) {
+ self._text = text
+ self.onChange = onChange
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func loadView() {
+ let scrollView = NSTextView.scrollableTextView()
+ let textView = scrollView.documentView as! NSTextView
+
+ scrollView.translatesAutoresizingMaskIntoConstraints = false
+
+ textView.allowsUndo = true
+ textView.delegate = self
+ textView.textStorage?.delegate = self
+ textView.string = self.text
+ textView.isEditable = true
+ textView.font = .monospacedSystemFont(ofSize: 16.0, weight: .regular)
+ self.view = scrollView
+ }
+
+ override func viewDidAppear() {
+ self.view.window?.makeFirstResponder(self.view)
+ }
+}
+
+extension MapTextEditorController: NSTextViewDelegate {
+
+ func textDidChange(_ obj: Notification) {
+ if let textField = obj.object as? NSTextView {
+ self.text = textField.string
+
+
+ changeDebouncer.debounce {
+ DispatchQueue.main.async {
+ self.onChange()
+ }
+ }
+ }
+ }
+
+ func textView(_ view: NSTextView, shouldChangeTextIn: NSRange, replacementString: String?) -> Bool
+ {
+ let range = Range(shouldChangeTextIn, in: view.string)
+ let target = view.string[range!]
+
+ if target == "--" {
+ return false
+ }
+
+ return true
+ }
+}
+
+extension MapTextEditorController: NSTextStorageDelegate {
+
+ override func textStorageDidProcessEditing(_ obj: Notification) {
+ if let textStorage = obj.object as? NSTextStorage {
+ self.colorizeText(textStorage: textStorage)
+ }
+ }
+
+ private func colorizeText(textStorage: NSTextStorage) {
+ let range = NSMakeRange(0, textStorage.length)
+ var matches = vertexRegex.matches(in: textStorage.string, options: [], range: range)
+
+ for match in matches {
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 2))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 3))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 4))
+ }
+
+ matches = edgeRegex.matches(in: textStorage.string, options: [], range: range)
+
+ for match in matches {
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 1))
+ let arrowRange = match.range(at: 2)
+ textStorage.addAttributes(
+ [.foregroundColor: NSColor.syntax.symbol],
+ range: NSMakeRange(arrowRange.lowerBound - 1, arrowRange.length + 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 3))
+ }
+
+ matches = opportunityRegex.matches(in: textStorage.string, options: [], range: range)
+
+ for match in matches {
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 2))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.symbol], range: match.range(at: 3))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 4))
+ }
+
+ matches = blockerRegex.matches(in: textStorage.string, options: [], range: range)
+
+ for match in matches {
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 2))
+ }
+
+ matches = noteRegex.matches(in: textStorage.string, options: [], range: range)
+
+ for match in matches {
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 2))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 3))
+ }
+
+ matches = stageRegex.matches(in: textStorage.string, options: [], range: range)
+
+ for match in matches {
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+ textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 2))
+ }
+ }
+}
+
+struct MapTextEditor: NSViewControllerRepresentable {
+
+ @Binding var text: String
+ var onChange: () -> Void = {}
+
+ func makeNSViewController(
+ context: NSViewControllerRepresentableContext<MapTextEditor>
+ ) -> MapTextEditorController {
+ return MapTextEditorController(text: $text, onChange: onChange)
+ }
+
+ func updateNSViewController(
+ _ nsViewController: MapTextEditorController,
+ context: NSViewControllerRepresentableContext<MapTextEditor>
+ ) {}
+}