From 6926feead1a7fadb7f5f974996bef8c30b3bfdbe Mon Sep 17 00:00:00 2001 From: Ruben Beltran del Rio Date: Wed, 9 Jul 2025 01:55:50 +0200 Subject: Use AST based text editor --- .../WmapSyntaxHighlighter/SyntaxHighlighter.swift | 83 ++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 Map/Business/WmapSyntaxHighlighter/SyntaxHighlighter.swift (limited to 'Map/Business/WmapSyntaxHighlighter/SyntaxHighlighter.swift') diff --git a/Map/Business/WmapSyntaxHighlighter/SyntaxHighlighter.swift b/Map/Business/WmapSyntaxHighlighter/SyntaxHighlighter.swift new file mode 100644 index 0000000..e0aaee7 --- /dev/null +++ b/Map/Business/WmapSyntaxHighlighter/SyntaxHighlighter.swift @@ -0,0 +1,83 @@ +// 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 AppKit + +extension Wmap { + class SyntaxHighlighter { + private var cachedSyntaxElements: [SyntaxElement] = [] + private var lineToElementsMap: [Int: [SyntaxElement]] = [:] + + func updateSyntaxElements(from parsedMap: [Entity]) { + cachedSyntaxElements = parsedMap.flatMap { $0.syntaxElements } + + // Build line-to-elements mapping for fast lookup + lineToElementsMap.removeAll() + for element in cachedSyntaxElements { + let line = element.range.start.line + lineToElementsMap[line, default: []].append(element) + } + } + + func applySyntaxHighlighting(textStorage: NSTextStorage, range: NSRange) { + // Get the line range that encompasses the edited range + let string = textStorage.string as NSString + let lineRange = string.lineRange(for: range) + + // Apply highlighting to elements that intersect with the line range + for element in cachedSyntaxElements { + // Safely convert string range to NSRange with bounds checking + guard element.range.stringRange.lowerBound <= element.range.stringRange.upperBound, + element.range.stringRange.upperBound <= textStorage.string.endIndex + else { + continue + } + + let nsRange = NSRange(element.range.stringRange, in: textStorage.string) + + // Only process elements that intersect with our update area + guard nsRange.location != NSNotFound, + nsRange.location + nsRange.length <= textStorage.length, + NSIntersectionRange(nsRange, lineRange).length > 0 + else { + continue + } + + let color = colorForSyntaxType(element.type) + textStorage.addAttribute(.foregroundColor, value: color, range: nsRange) + } + } + + private func colorForSyntaxType(_ type: SyntaxType) -> NSColor { + switch type { + case .keyword: + return NSColor.Theme.Syntax.option + case .vertexLabel: + return NSColor.Theme.Syntax.vertex + case .number: + return NSColor.Theme.Syntax.number + case .symbol: + return NSColor.Theme.Syntax.symbol + case .shape: + return NSColor.Theme.Syntax.option + case .text: + return NSColor.Theme.Syntax.text + case .punctuation: + return NSColor.Theme.Syntax.punctuation + case .stageNumber: + return NSColor.Theme.Syntax.option + } + } + } +} -- cgit