@@ -42,10 +42,7 @@ class Highlighter: NSObject {
42
42
43
43
/// The set of visible indexes in tht text view
44
44
lazy private var visibleSet : IndexSet = {
45
- guard let range = textView. visibleTextRange else {
46
- return IndexSet ( )
47
- }
48
- return IndexSet ( integersIn: Range ( range) !)
45
+ return IndexSet ( integersIn: textView. visibleTextRange ?? NSRange ( ) )
49
46
} ( )
50
47
51
48
// MARK: - UI
@@ -107,7 +104,7 @@ class Highlighter: NSObject {
107
104
if !( treeSitterClient? . hasSetText ?? true ) {
108
105
treeSitterClient? . setText ( text: textView. string)
109
106
}
110
- invalidate ( range: entireTextRange)
107
+ invalidate ( range: NSRange ( entireTextRange) )
111
108
}
112
109
113
110
/// Sets the language and causes a re-highlight of the entire text.
@@ -129,12 +126,6 @@ private extension Highlighter {
129
126
/// Invalidates a given range and adds it to the queue to be highlighted.
130
127
/// - Parameter range: The range to invalidate.
131
128
func invalidate( range: NSRange ) {
132
- invalidate ( range: Range ( range) !)
133
- }
134
-
135
- /// Invalidates a given range and adds it to the queue to be highlighted.
136
- /// - Parameter range: The range to invalidate.
137
- func invalidate( range: Range < Int > ) {
138
129
let set = IndexSet ( integersIn: range)
139
130
140
131
if set. isEmpty {
@@ -161,31 +152,35 @@ private extension Highlighter {
161
152
162
153
/// Highlights the given range
163
154
/// - Parameter range: The range to request highlights for.
164
- func highlight( range nsRange: NSRange ) {
165
- let range = Range ( nsRange) !
166
- pendingSet. insert ( integersIn: range)
155
+ func highlight( range rangeToHighlight: NSRange ) {
156
+ pendingSet. insert ( integersIn: rangeToHighlight)
167
157
168
- treeSitterClient? . queryColorsFor ( range: nsRange ) { [ weak self] highlightRanges in
158
+ treeSitterClient? . queryColorsFor ( range: rangeToHighlight ) { [ weak self] highlightRanges in
169
159
guard let attributeProvider = self ? . attributeProvider,
170
160
let textView = self ? . textView else { return }
171
161
172
162
// Mark these indices as not pending and valid
173
- self ? . pendingSet. remove ( integersIn: range )
174
- self ? . validSet. formUnion ( IndexSet ( integersIn: range ) )
163
+ self ? . pendingSet. remove ( integersIn: rangeToHighlight )
164
+ self ? . validSet. formUnion ( IndexSet ( integersIn: rangeToHighlight ) )
175
165
176
166
// If this range does not exist in the visible set, we can exit.
177
- if !( self ? . visibleSet ?? . init( ) ) . contains ( integersIn: range ) {
167
+ if !( self ? . visibleSet ?? . init( ) ) . contains ( integersIn: rangeToHighlight ) {
178
168
return
179
169
}
180
170
181
171
// Try to create a text range for invalidating. If this fails we fail silently
182
172
guard let textContentManager = textView. textLayoutManager. textContentManager,
183
- let textRange = NSTextRange ( nsRange , provider: textContentManager) else {
173
+ let textRange = NSTextRange ( rangeToHighlight , provider: textContentManager) else {
184
174
return
185
175
}
186
176
187
177
// Loop through each highlight and modify the textStorage accordingly.
188
178
textView. textContentStorage. textStorage? . beginEditing ( )
179
+
180
+ // Create a set of indexes that were not highlighted.
181
+ var ignoredIndexes = IndexSet ( integersIn: rangeToHighlight)
182
+
183
+ // Apply all highlights that need color
189
184
for highlight in highlightRanges {
190
185
// Does not work:
191
186
// textView.textLayoutManager.setRenderingAttributes(attributeProvider.attributesFor(highlight.capture),
@@ -196,7 +191,21 @@ private extension Highlighter {
196
191
attributeProvider. attributesFor ( highlight. capture) ,
197
192
range: highlight. range
198
193
)
194
+
195
+ // Remove highlighted indexes from the "ignored" indexes.
196
+ ignoredIndexes. remove ( integersIn: highlight. range)
197
+ }
198
+
199
+ // For any indices left over, we need to apply normal attributes to them
200
+ // This fixes the case where characters are changed to have a non-text color, and then are skipped when
201
+ // they need to be changed back.
202
+ for ignoredRange in ignoredIndexes. rangeView {
203
+ textView. textContentStorage. textStorage? . setAttributes (
204
+ attributeProvider. attributesFor ( nil ) ,
205
+ range: NSRange ( ignoredRange)
206
+ )
199
207
}
208
+
200
209
textView. textContentStorage. textStorage? . endEditing ( )
201
210
202
211
// After applying edits to the text storage we need to invalidate the layout
@@ -227,7 +236,7 @@ private extension Highlighter {
227
236
private extension Highlighter {
228
237
/// Updates the view to highlight newly visible text when the textview is scrolled or bounds change.
229
238
@objc func visibleTextChanged( _ notification: Notification ) {
230
- visibleSet = IndexSet ( integersIn: Range ( textView. visibleTextRange ?? NSRange ( ) ) ! )
239
+ visibleSet = IndexSet ( integersIn: textView. visibleTextRange ?? NSRange ( ) )
231
240
232
241
// Any indices that are both *not* valid and in the visible text range should be invalidated
233
242
let newlyInvalidSet = visibleSet. subtracting ( validSet)
@@ -263,7 +272,7 @@ extension Highlighter: NSTextStorageDelegate {
263
272
treeSitterClient? . applyEdit ( edit,
264
273
text: textStorage. string) { [ weak self] invalidatedIndexSet in
265
274
let indexSet = invalidatedIndexSet
266
- . union ( IndexSet ( integersIn: Range ( editedRange) ! ) )
275
+ . union ( IndexSet ( integersIn: editedRange) )
267
276
// Only invalidate indices that aren't visible.
268
277
. intersection ( self ? . visibleSet ?? . init( ) )
269
278
0 commit comments