X-Git-Url: https://git.r.bdr.sh/rbdr/map/blobdiff_plain/5e8ff4850c4827125fe12788dd5b153c4f636f48..77d0155b661e813a85a7312ed809fc7e5a9f7440:/Map/Views/MapTextEditor.swift diff --git a/Map/Views/MapTextEditor.swift b/Map/Views/MapTextEditor.swift index 7251c59..e030d64 100644 --- a/Map/Views/MapTextEditor.swift +++ b/Map/Views/MapTextEditor.swift @@ -4,9 +4,19 @@ import SwiftUI class MapTextEditorController: NSViewController { @Binding var text: String + var colorScheme: ColorScheme - init(text: Binding) { + private let vertexRegex = MapParsingPatterns.vertex + private let edgeRegex = MapParsingPatterns.edge + private let blockerRegex = MapParsingPatterns.blocker + private let opportunityRegex = MapParsingPatterns.opportunity + private let stageRegex = MapParsingPatterns.stage + + private let debouncer: Debouncer = Debouncer(seconds: 0.2) + + init(text: Binding, colorScheme: ColorScheme) { self._text = text + self.colorScheme = colorScheme super.init(nibName: nil, bundle: nil) } @@ -21,6 +31,7 @@ class MapTextEditorController: NSViewController { scrollView.translatesAutoresizingMaskIntoConstraints = false textView.delegate = self + textView.textStorage?.delegate = self textView.string = self.text textView.isEditable = true textView.font = .monospacedSystemFont(ofSize: 16.0, weight: .regular) @@ -30,6 +41,10 @@ class MapTextEditorController: NSViewController { override func viewDidAppear() { self.view.window?.makeFirstResponder(self.view) } + + func updateColorScheme(_ colorScheme: ColorScheme) { + self.colorScheme = colorScheme + } } extension MapTextEditorController: NSTextViewDelegate { @@ -53,19 +68,80 @@ extension MapTextEditorController: NSTextViewDelegate { } } +extension MapTextEditorController: NSTextStorageDelegate { + override func textStorageDidProcessEditing(_ obj: Notification) { + if let textStorage = obj.object as? NSTextStorage { + debouncer.debounce { + DispatchQueue.main.async { + 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) + let colors = MapColor.colorForScheme(colorScheme) + + for match in matches { + textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 1)) + textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 2)) + textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 3)) + textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 4)) + } + + matches = edgeRegex.matches(in: textStorage.string, options: [], range: range) + + for match in matches { + textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 1)) + let arrowRange = match.range(at: 2) + textStorage.addAttributes( + [.foregroundColor: colors.syntax.symbol], + range: NSMakeRange(arrowRange.lowerBound - 1, arrowRange.length + 1)) + textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 3)) + } + + matches = opportunityRegex.matches(in: textStorage.string, options: [], range: range) + + for match in matches { + textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 1)) + textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 2)) + textStorage.addAttributes([.foregroundColor: colors.syntax.symbol], range: match.range(at: 3)) + textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 4)) + } + + matches = blockerRegex.matches(in: textStorage.string, options: [], range: range) + + for match in matches { + textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 1)) + textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 2)) + } + + matches = stageRegex.matches(in: textStorage.string, options: [], range: range) + + for match in matches { + textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 1)) + textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 2)) + } + } +} + struct MapTextEditor: NSViewControllerRepresentable { @Binding var text: String + let colorScheme: ColorScheme func makeNSViewController( context: NSViewControllerRepresentableContext ) -> MapTextEditorController { - return MapTextEditorController(text: $text) + return MapTextEditorController(text: $text, colorScheme: colorScheme) } func updateNSViewController( _ nsViewController: MapTextEditorController, context: NSViewControllerRepresentableContext ) { + nsViewController.updateColorScheme(context.environment.colorScheme) } }