From 8564439356f4bcc3e21b19738eb87b4c0722c623 Mon Sep 17 00:00:00 2001 From: Ruben Beltran del Rio Date: Fri, 11 Jul 2025 09:27:49 +0200 Subject: Add missing vertex detection --- .../Base Components/MapTextEditor.swift | 284 --------------------- 1 file changed, 284 deletions(-) delete mode 100644 Map/Presentation/Base Components/MapTextEditor.swift (limited to 'Map/Presentation/Base Components/MapTextEditor.swift') diff --git a/Map/Presentation/Base Components/MapTextEditor.swift b/Map/Presentation/Base Components/MapTextEditor.swift deleted file mode 100644 index 4295918..0000000 --- a/Map/Presentation/Base Components/MapTextEditor.swift +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (C) 2024 Rubén Beltrán del Río - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see https://map.tranquil.systems. -import Cocoa -import SwiftUI - -class MapTextEditorController: NSViewController { - - @Binding var text: String - - var parsedMap: [Wmap.Entity] = [] { - didSet { - syntaxHighlighter.updateSyntaxElements(from: parsedMap) - // Don't re-highlight entire document here - let the text change handler do it - } - } - - var highlightRanges: [Range] { - didSet { - updateHighlights() - } - } - - var selectedRange: Int { - didSet { - updateHighlights() - focusOnResult() - } - } - - let onChange: () -> Void - - private let syntaxHighlighter = Wmap.SyntaxHighlighter() - private let changeDebouncer: Debouncer = Debouncer(seconds: 1) - - init( - text: Binding, - parsedMap: [Wmap.Entity] = [], - highlightRanges: [Range], - selectedRange: Int, - onChange: @escaping () -> Void - ) { - self._text = text - self.parsedMap = parsedMap - self.onChange = onChange - self.highlightRanges = highlightRanges - self.selectedRange = selectedRange - super.init(nibName: nil, bundle: nil) - - Task { - syntaxHighlighter.updateSyntaxElements(from: parsedMap) - } - } - - 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.backgroundColor = .Theme.UI.background - textView.allowsUndo = true - textView.delegate = self - textView.textStorage?.delegate = self - textView.string = self.text - textView.isEditable = true - textView.font = NSFont.Theme.Editor.regular - textView.textContainerInset = NSSize(width: 0, height: Dimensions.Spacing.coziest) - - // Configure text wrapping based on user preference - let softWrapLines = UserDefaults.standard.bool(forKey: "softWrapLines") - if !softWrapLines { - // Disable word wrapping to prevent flashing on soft-wrapped lines - textView.textContainer?.widthTracksTextView = false - textView.textContainer?.containerSize = NSSize( - width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) - } else { - textView.textContainer?.widthTracksTextView = true - } - - textView.isHorizontallyResizable = true - textView.isVerticallyResizable = true - - self.view = scrollView - } - - override func viewDidAppear() { - self.view.window?.makeFirstResponder(self.view) - updateHighlights() - // Initial colorization of the entire document - if let textView = currentTextView, let textStorage = textView.textStorage { - colorizeText(textStorage: textStorage) - } - } - - var currentTextView: NSTextView? { - return (view as? NSScrollView)?.documentView as? NSTextView - } - - private func updateHighlights() { - if let textView = currentTextView { - if let textStorage = textView.textStorage { - textStorage.removeAttribute( - .backgroundColor, range: NSRange(location: 0, length: textStorage.length)) - - for (index, range) in highlightRanges.enumerated() { - let nsRange = NSRange(range, in: textStorage.string) - - let color = - index == selectedRange - ? NSColor.Theme.Syntax.highlightMatch : NSColor.Theme.Syntax.match - textStorage.addAttribute(.backgroundColor, value: color, range: nsRange) - } - - textView.needsDisplay = true - - } - } - } - - private func focusOnResult() { - if let textView = currentTextView { - if let textStorage = textView.textStorage { - if selectedRange < highlightRanges.count { - let range = highlightRanges[selectedRange] - let nsRange = NSRange(range, in: textStorage.string) - textView.scrollRangeToVisible(nsRange) - } - } - } - } -} - -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 { - // Only process if this is a text content change, not an attribute change - guard textStorage.editedMask.contains(.editedCharacters) else { - return - } - - let editedRange = textStorage.editedRange - if editedRange.location != NSNotFound { - // Use a small delay to batch rapid changes and avoid flashing - DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in - guard let self = self, - let currentTextView = self.currentTextView, - let currentTextStorage = currentTextView.textStorage - else { return } - - self.colorizeEditedText(textStorage: currentTextStorage, editedRange: editedRange) - } - } - } - } - - private func colorizeEditedText(textStorage: NSTextStorage, editedRange: NSRange) { - // Expand range to include the entire line(s) that were edited - let string = textStorage.string as NSString - let lineRange = string.lineRange(for: editedRange) - - // Clear existing syntax highlighting attributes in the range, but preserve background colors - textStorage.removeAttribute(.foregroundColor, range: lineRange) - - // Apply syntax highlighting - syntaxHighlighter.applySyntaxHighlighting(textStorage: textStorage, range: lineRange) - } - - private func colorizeText(textStorage: NSTextStorage) { - let range = NSMakeRange(0, textStorage.length) - - // Clear all existing attributes - textStorage.removeAttribute(.foregroundColor, range: range) - - syntaxHighlighter.applySyntaxHighlighting(textStorage: textStorage, range: range) - } -} - -struct MapTextEditor: NSViewControllerRepresentable { - - @Binding var text: String - var parsedMap: [Wmap.Entity] = [] - var highlightRanges: [Range] - var selectedRange: Int - var onChange: () -> Void = {} - - init( - text: Binding, parsedMap: [Wmap.Entity] = [], - highlightRanges: [Range] = [], selectedRange: Int = 0, - onChange: @escaping () -> Void = {} - ) { - self._text = text - self.parsedMap = parsedMap - self.highlightRanges = highlightRanges - self.selectedRange = selectedRange - self.onChange = onChange - } - - func makeNSViewController( - context: NSViewControllerRepresentableContext - ) -> MapTextEditorController { - return MapTextEditorController( - text: $text, parsedMap: parsedMap, highlightRanges: highlightRanges, - selectedRange: selectedRange, - onChange: onChange) - } - - func updateNSViewController( - _ nsViewController: MapTextEditorController, - context: NSViewControllerRepresentableContext - ) { - // Only update if something actually changed to prevent excessive updates - var hasChanges = false - - // Always update parsed map since the content may have changed even if count is same - nsViewController.parsedMap = parsedMap - - if nsViewController.highlightRanges != highlightRanges { - nsViewController.highlightRanges = highlightRanges - hasChanges = true - } - - if nsViewController.selectedRange != selectedRange { - nsViewController.selectedRange = selectedRange - hasChanges = true - } - - // Update text view content if text has changed - if let textView = nsViewController.currentTextView { - if textView.string != text { - textView.string = text - hasChanges = true - } - } - - // Only log if we made changes (for debugging) - if hasChanges { - print("MapTextEditor: Updated view controller") - } - } -} -- cgit