@@ -11,11 +11,6 @@ import STTextView
1111import SwiftTreeSitter
1212import CodeEditLanguages
1313
14- /// Classes conforming to this protocol can provide attributes for text given a capture type.
15- public protocol ThemeAttributesProviding {
16- func attributesFor( _ capture: CaptureName ? ) -> [ NSAttributedString . Key : Any ]
17- }
18-
1914/// The `Highlighter` class handles efficiently highlighting the `STTextView` it's provided with.
2015/// It will listen for text and visibility changes, and highlight syntax as needed.
2116///
@@ -49,13 +44,21 @@ class Highlighter: NSObject {
4944
5045 /// The text view to highlight
5146 private var textView : STTextView
47+
48+ /// The editor theme
5249 private var theme : EditorTheme
50+
51+ /// The object providing attributes for captures.
5352 private var attributeProvider : ThemeAttributesProviding !
5453
55- // MARK: - TreeSitter Client
54+ /// The current language of the editor.
55+ private var language : CodeLanguage
5656
5757 /// Calculates invalidated ranges given an edit.
58- private var treeSitterClient : TreeSitterClient ?
58+ private var highlightProvider : HighlightProviding ?
59+
60+ /// The length to chunk ranges into when passing to the highlighter.
61+ fileprivate let rangeChunkLimit = 256
5962
6063 // MARK: - Init
6164
@@ -65,17 +68,19 @@ class Highlighter: NSObject {
6568 /// - treeSitterClient: The tree-sitter client to handle tree updates and highlight queries.
6669 /// - theme: The theme to use for highlights.
6770 init ( textView: STTextView ,
68- treeSitterClient : TreeSitterClient ? ,
71+ highlightProvider : HighlightProviding ? ,
6972 theme: EditorTheme ,
70- attributeProvider: ThemeAttributesProviding ) {
73+ attributeProvider: ThemeAttributesProviding ,
74+ language: CodeLanguage ) {
7175 self . textView = textView
72- self . treeSitterClient = treeSitterClient
76+ self . highlightProvider = highlightProvider
7377 self . theme = theme
7478 self . attributeProvider = attributeProvider
79+ self . language = language
7580
7681 super. init ( )
7782
78- treeSitterClient ? . setText ( text : textView . string )
83+ highlightProvider ? . setLanguage ( codeLanguage : language )
7984
8085 guard textView. textContentStorage. textStorage != nil else {
8186 assertionFailure ( " Text view does not have a textStorage " )
@@ -100,17 +105,22 @@ class Highlighter: NSObject {
100105 // MARK: - Public
101106
102107 /// Invalidates all text in the textview. Useful for updating themes.
103- func invalidate( ) {
104- if !( treeSitterClient? . hasSetText ?? true ) {
105- treeSitterClient? . setText ( text: textView. string)
106- }
108+ public func invalidate( ) {
107109 invalidate ( range: NSRange ( entireTextRange) )
108110 }
109111
110112 /// Sets the language and causes a re-highlight of the entire text.
111113 /// - Parameter language: The language to update to.
112- func setLanguage( language: CodeLanguage ) throws {
113- try treeSitterClient? . setLanguage ( codeLanguage: language, text: textView. string)
114+ public func setLanguage( language: CodeLanguage ) {
115+ highlightProvider? . setLanguage ( codeLanguage: language)
116+ invalidate ( )
117+ }
118+
119+ /// Sets the highlight provider. Will cause a re-highlight of the entire text.
120+ /// - Parameter provider: The provider to use for future syntax highlights.
121+ public func setHighlightProvider( _ provider: HighlightProviding ) {
122+ self . highlightProvider = provider
123+ highlightProvider? . setLanguage ( codeLanguage: language)
114124 invalidate ( )
115125 }
116126
@@ -155,7 +165,8 @@ private extension Highlighter {
155165 func highlight( range rangeToHighlight: NSRange ) {
156166 pendingSet. insert ( integersIn: rangeToHighlight)
157167
158- treeSitterClient? . queryColorsFor ( range: rangeToHighlight) { [ weak self] highlightRanges in
168+ highlightProvider? . queryHighlightsFor ( textView: self . textView,
169+ range: rangeToHighlight) { [ weak self] highlightRanges in
159170 guard let attributeProvider = self ? . attributeProvider,
160171 let textView = self ? . textView else { return }
161172
@@ -222,11 +233,13 @@ private extension Highlighter {
222233 . intersection ( visibleSet) // Only visible indexes
223234 . subtracting ( pendingSet) // Don't include pending indexes
224235
225- guard let range = set. rangeView. map ( { NSRange ( $0 ) } ) . first else {
236+ guard let range = set. rangeView. first else {
226237 return nil
227238 }
228239
229- return range
240+ // Chunk the ranges in sets of rangeChunkLimit characters.
241+ return NSRange ( location: range. lowerBound,
242+ length: min ( rangeChunkLimit, range. upperBound - range. lowerBound) )
230243 }
231244
232245}
@@ -256,7 +269,6 @@ extension Highlighter: NSTextStorageDelegate {
256269 didProcessEditing editedMask: NSTextStorageEditActions ,
257270 range editedRange: NSRange ,
258271 changeInLength delta: Int ) {
259-
260272 // This method is called whenever attributes are updated, so to avoid re-highlighting the entire document
261273 // each time an attribute is applied, we check to make sure this is in response to an edit.
262274 guard editedMask. contains ( . editedCharacters) else {
@@ -265,12 +277,9 @@ extension Highlighter: NSTextStorageDelegate {
265277
266278 let range = NSRange ( location: editedRange. location, length: editedRange. length - delta)
267279
268- guard let edit = InputEdit ( range: range, delta: delta, oldEndPoint: . zero) else {
269- return
270- }
271-
272- treeSitterClient? . applyEdit ( edit,
273- text: textStorage. string) { [ weak self] invalidatedIndexSet in
280+ highlightProvider? . applyEdit ( textView: self . textView,
281+ range: range,
282+ delta: delta) { [ weak self] invalidatedIndexSet in
274283 let indexSet = invalidatedIndexSet
275284 . union ( IndexSet ( integersIn: editedRange) )
276285 // Only invalidate indices that aren't visible.
0 commit comments