From 521dd433df903c405e1d7edc4d442ba7933f893c Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Wed, 10 May 2023 15:08:54 -0500 Subject: [PATCH] Add range check to `highlightsFromCursor` (#187) ### Description Fixes a bug found found by @cengelbart39 [on discord](https://canary.discord.com/channels/951544472238444645/952640521812193411/1104143145765187644) where a parser would consume ranges already consumed by injected languages. This caused the injected ranges to be realized as plain text, when they should have been kept as their injected language's highlights. This should have been avoided by the call to `query.setRange(range)` on line [43 in TreeSitterClient+Highlight.swift](https://github.com/CodeEditApp/CodeEditTextView/blob/045bd359ef9c4addf2a5bf51e22ba660d69c5d10/Sources/CodeEditTextView/TreeSitter/TreeSitterClient%2BHighlight.swift#L44) but it was found that for some reason in the case found by @cengelbart39 it just didn't work. To fix, an additional check was added in `highlightsFromCursor` to only take any ranges that have indices in the intersection of it's range and the included range. ### Related Issues * See discord link. ### 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 Before: https://user-images.githubusercontent.com/35942988/236696171-d30dfd7b-8545-4396-8aa8-490ceac65551.mov After: https://user-images.githubusercontent.com/35942988/236697575-3f605c0d-3dda-45c2-8f69-0f41148f1c2d.mov --- .../TreeSitter/TreeSitterClient+Highlight.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Sources/CodeEditTextView/TreeSitter/TreeSitterClient+Highlight.swift b/Sources/CodeEditTextView/TreeSitter/TreeSitterClient+Highlight.swift index de8764f0a..a3b4f487f 100644 --- a/Sources/CodeEditTextView/TreeSitter/TreeSitterClient+Highlight.swift +++ b/Sources/CodeEditTextView/TreeSitter/TreeSitterClient+Highlight.swift @@ -44,23 +44,28 @@ extension TreeSitterClient { cursor.setRange(range) cursor.matchLimit = Constants.treeSitterMatchLimit - return highlightsFromCursor(cursor: ResolvingQueryCursor(cursor: cursor)) + return highlightsFromCursor(cursor: ResolvingQueryCursor(cursor: cursor), includedRange: range) } /// Resolves a query cursor to the highlight ranges it contains. /// **Must be called on the main thread** - /// - Parameter cursor: The cursor to resolve. + /// - Parameters: + /// - cursor: The cursor to resolve. + /// - includedRange: The range to include highlights from. /// - Returns: Any highlight ranges contained in the cursor. - internal func highlightsFromCursor(cursor: ResolvingQueryCursor) -> [HighlightRange] { + internal func highlightsFromCursor(cursor: ResolvingQueryCursor, includedRange: NSRange) -> [HighlightRange] { cursor.prepare(with: self.textProvider) return cursor .flatMap { $0.captures } .compactMap { + // Sometimes `cursor.setRange` just doesnt work :( so we have to do a redundant check for a valid range + // in the included range + let intersectionRange = $0.range.intersection(includedRange) ?? .zero // Some languages add an "@spell" capture to indicate a portion of text that should be spellchecked // (usually comments). But this causes other captures in the same range to be overriden. So we ignore // that specific capture type. - if $0.name != "spell" && $0.name != "injection.content" { - return HighlightRange(range: $0.range, capture: CaptureName.fromString($0.name ?? "")) + if intersectionRange.length > 0 && $0.name != "spell" && $0.name != "injection.content" { + return HighlightRange(range: intersectionRange, capture: CaptureName.fromString($0.name ?? "")) } return nil }