From a5581a2f38f6993041db77f51f60876b1eda1adb Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Thu, 29 Feb 2024 12:21:58 -0600 Subject: [PATCH] Invalidate Visible Range, Responsive Language Update (#238) ### Description - Updates the visible set of indices when text storage happens, fixing the linked issue. - When setting a new language on the highlighter, adds a small "reset" that clears all current highlights. This makes the language update feel snappier, as the user receives immediate feedback. Before the ts parser could take a few seconds to begin highlighting, so there would be a delay making it seem like the language update was not happening. - Adds a missing `throw` to the tree-sitter client that made edits always run synchronously (didn't feel big enough for a whole PR, found while debugging). - Only updates the font if the font is new, making text layouts occur much less frequently (also doesn't feel big enough for a separate PR) ### Related Issues * #235 ### Checklist - [x] I read and understood the [contributing guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md) as well as the [code of conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md) - [x] The issues this PR addresses are related to each other - [x] My changes generate no new warnings - [x] My code builds and runs on my machine - [x] My changes are all related to the related issue above - [x] I documented my code ### Screenshots https://github.com/CodeEditApp/CodeEditSourceEditor/assets/35942988/e719178e-bc9a-4830-9ca5-8d113dcb9d8b --- .../CodeEditSourceEditor.swift | 10 +++++++++- .../Highlighting/Highlighter.swift | 17 +++++++++++------ .../TreeSitter/TreeSitterClient.swift | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Sources/CodeEditSourceEditor/CodeEditSourceEditor.swift b/Sources/CodeEditSourceEditor/CodeEditSourceEditor.swift index 446ec7a8b..99221d9b1 100644 --- a/Sources/CodeEditSourceEditor/CodeEditSourceEditor.swift +++ b/Sources/CodeEditSourceEditor/CodeEditSourceEditor.swift @@ -156,12 +156,16 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable { return } - controller.font = font + if controller.font != font { + controller.font = font + } + controller.wrapLines = wrapLines controller.useThemeBackground = useThemeBackground controller.lineHeightMultiple = lineHeight controller.editorOverscroll = editorOverscroll controller.contentInsets = contentInsets + if controller.isEditable != isEditable { controller.isEditable = isEditable } @@ -173,15 +177,19 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable { if controller.language.id != language.id { controller.language = language } + if controller.theme != theme { controller.theme = theme } + if controller.indentOption != indentOption { controller.indentOption = indentOption } + if controller.tabWidth != tabWidth { controller.tabWidth = tabWidth } + if controller.letterSpacing != letterSpacing { controller.letterSpacing = letterSpacing } diff --git a/Sources/CodeEditSourceEditor/Highlighting/Highlighter.swift b/Sources/CodeEditSourceEditor/Highlighting/Highlighter.swift index 8beebf067..b617b98d0 100644 --- a/Sources/CodeEditSourceEditor/Highlighting/Highlighter.swift +++ b/Sources/CodeEditSourceEditor/Highlighting/Highlighter.swift @@ -36,10 +36,6 @@ class Highlighter: NSObject { return IndexSet(integersIn: textView?.visibleTextRange ?? NSRange()) }() - // MARK: - Tasks - - private var runningTasks: [UUID: Task] = [:] - // MARK: - UI /// The text view to highlight @@ -114,6 +110,15 @@ class Highlighter: NSObject { /// - Parameter language: The language to update to. public func setLanguage(language: CodeLanguage) { guard let textView = self.textView else { return } + // Remove all current highlights. Makes the language setting feel snappier and tells the user we're doing + // something immediately. + textView.textStorage.setAttributes( + attributeProvider.attributesFor(nil), + range: NSRange(location: 0, length: textView.textStorage.length) + ) + textView.layoutManager.invalidateLayoutForRect(textView.visibleRect) + validSet.removeAll() + pendingSet.removeAll() highlightProvider?.setUp(textView: textView, codeLanguage: language) invalidate() } @@ -304,11 +309,11 @@ extension Highlighter { visibleSet.insert(range: editedRange) } + updateVisibleSet(textView: textView) + highlightProvider?.applyEdit(textView: textView, range: range, delta: delta) { [weak self] invalidIndexSet in let indexSet = invalidIndexSet .union(IndexSet(integersIn: editedRange)) - // Only invalidate indices that are visible. - .intersection(self?.visibleSet ?? IndexSet()) for range in indexSet.rangeView { self?.invalidate(range: NSRange(range)) diff --git a/Sources/CodeEditSourceEditor/TreeSitter/TreeSitterClient.swift b/Sources/CodeEditSourceEditor/TreeSitter/TreeSitterClient.swift index 42ce2263b..cb5ea1f31 100644 --- a/Sources/CodeEditSourceEditor/TreeSitter/TreeSitterClient.swift +++ b/Sources/CodeEditSourceEditor/TreeSitter/TreeSitterClient.swift @@ -218,7 +218,7 @@ public final class TreeSitterClient: HighlightProviding { let longDocument = textView.documentRange.length > Constants.maxSyncContentLength if longEdit || longDocument { - + throw Error.syncUnavailable } try performSync(operation) } catch {