Skip to content

Commit bb2cbf5

Browse files
Smarter Default Highlight Provider Management (#310)
### Description Updates the highlight provider parameter to correctly reflect its documentation, and use a smarter method for keeping around the default highlighter. This fixes an issue where the default highlight provider was re-initialized on every view update (keyboard navigation, keystrokes, etc.). Now, highlight providers have two states: `nil` or `Array`. If CESE sees a `nil` argument, it creates and keeps around a `TreeSitterClient` like the documentation says. This client is kept as a variable on the view's coordinator so it isn't re-initialized between view updates. This also allows highlighting to be disabled by passing an empty array for the highlight providers parameter, causing CESE to not make any default providers. ### Related Issues * closes #293 * closes #298 ### 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 N/A
1 parent 645456d commit bb2cbf5

File tree

2 files changed

+35
-21
lines changed

2 files changed

+35
-21
lines changed

Sources/CodeEditSourceEditor/CodeEditSourceEditor/CodeEditSourceEditor+Coordinator.swift

+12-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ extension CodeEditSourceEditor {
1818
var text: TextAPI
1919
@Binding var cursorPositions: [CursorPosition]
2020

21-
init(text: TextAPI, cursorPositions: Binding<[CursorPosition]>) {
21+
private(set) var highlightProviders: [any HighlightProviding]
22+
23+
init(text: TextAPI, cursorPositions: Binding<[CursorPosition]>, highlightProviders: [any HighlightProviding]?) {
2224
self.text = text
2325
self._cursorPositions = cursorPositions
26+
self.highlightProviders = highlightProviders ?? [TreeSitterClient()]
2427
super.init()
2528

2629
NotificationCenter.default.addObserver(
@@ -38,6 +41,14 @@ extension CodeEditSourceEditor {
3841
)
3942
}
4043

44+
func updateHighlightProviders(_ highlightProviders: [any HighlightProviding]?) {
45+
guard let highlightProviders else {
46+
return // Keep our default `TreeSitterClient` if they're `nil`
47+
}
48+
// Otherwise, we can replace the stored providers.
49+
self.highlightProviders = highlightProviders
50+
}
51+
4152
@objc func textViewDidChangeText(_ notification: Notification) {
4253
guard let textView = notification.object as? TextView,
4354
let controller,

Sources/CodeEditSourceEditor/CodeEditSourceEditor/CodeEditSourceEditor.swift

+23-20
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
3535
/// - cursorPositions: The cursor's position in the editor, measured in `(lineNum, columnNum)`
3636
/// - useThemeBackground: Determines whether the editor uses the theme's background color, or a transparent
3737
/// background color
38-
/// - highlightProvider: A class you provide to perform syntax highlighting. Leave this as `nil` to use the
39-
/// built-in `TreeSitterClient` highlighter.
38+
/// - highlightProviders: A set of classes you provide to perform syntax highlighting. Leave this as `nil` to use
39+
/// the default `TreeSitterClient` highlighter.
4040
/// - contentInsets: Insets to use to offset the content in the enclosing scroll view. Leave as `nil` to let the
4141
/// scroll view automatically adjust content insets.
4242
/// - additionalTextInsets: An additional amount to inset the text of the editor by.
@@ -62,7 +62,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
6262
editorOverscroll: CGFloat = 0,
6363
cursorPositions: Binding<[CursorPosition]>,
6464
useThemeBackground: Bool = true,
65-
highlightProviders: [any HighlightProviding] = [TreeSitterClient()],
65+
highlightProviders: [any HighlightProviding]? = nil,
6666
contentInsets: NSEdgeInsets? = nil,
6767
additionalTextInsets: NSEdgeInsets? = nil,
6868
isEditable: Bool = true,
@@ -116,8 +116,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
116116
/// - cursorPositions: The cursor's position in the editor, measured in `(lineNum, columnNum)`
117117
/// - useThemeBackground: Determines whether the editor uses the theme's background color, or a transparent
118118
/// background color
119-
/// - highlightProvider: A class you provide to perform syntax highlighting. Leave this as `nil` to use the
120-
/// built-in `TreeSitterClient` highlighter.
119+
/// - highlightProviders: A set of classes you provide to perform syntax highlighting. Leave this as `nil` to use
120+
/// the default `TreeSitterClient` highlighter.
121121
/// - contentInsets: Insets to use to offset the content in the enclosing scroll view. Leave as `nil` to let the
122122
/// scroll view automatically adjust content insets.
123123
/// - isEditable: A Boolean value that controls whether the text view allows the user to edit text.
@@ -141,7 +141,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
141141
editorOverscroll: CGFloat = 0,
142142
cursorPositions: Binding<[CursorPosition]>,
143143
useThemeBackground: Bool = true,
144-
highlightProviders: [any HighlightProviding] = [TreeSitterClient()],
144+
highlightProviders: [any HighlightProviding]? = nil,
145145
contentInsets: NSEdgeInsets? = nil,
146146
additionalTextInsets: NSEdgeInsets? = nil,
147147
isEditable: Bool = true,
@@ -192,7 +192,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
192192
private var editorOverscroll: CGFloat
193193
package var cursorPositions: Binding<[CursorPosition]>
194194
private var useThemeBackground: Bool
195-
private var highlightProviders: [any HighlightProviding]
195+
private var highlightProviders: [any HighlightProviding]?
196196
private var contentInsets: NSEdgeInsets?
197197
private var additionalTextInsets: NSEdgeInsets?
198198
private var isEditable: Bool
@@ -219,7 +219,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
219219
cursorPositions: cursorPositions.wrappedValue,
220220
editorOverscroll: editorOverscroll,
221221
useThemeBackground: useThemeBackground,
222-
highlightProviders: highlightProviders,
222+
highlightProviders: context.coordinator.highlightProviders,
223223
contentInsets: contentInsets,
224224
additionalTextInsets: additionalTextInsets,
225225
isEditable: isEditable,
@@ -249,10 +249,12 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
249249
}
250250

251251
public func makeCoordinator() -> Coordinator {
252-
Coordinator(text: text, cursorPositions: cursorPositions)
252+
Coordinator(text: text, cursorPositions: cursorPositions, highlightProviders: highlightProviders)
253253
}
254254

255255
public func updateNSViewController(_ controller: TextViewController, context: Context) {
256+
context.coordinator.updateHighlightProviders(highlightProviders)
257+
256258
if !context.coordinator.isUpdateFromTextView {
257259
// Prevent infinite loop of update notifications
258260
context.coordinator.isUpdatingFromRepresentable = true
@@ -267,23 +269,23 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
267269

268270
// Do manual diffing to reduce the amount of reloads.
269271
// This helps a lot in view performance, as it otherwise gets triggered on each environment change.
270-
guard !paramsAreEqual(controller: controller) else {
272+
guard !paramsAreEqual(controller: controller, coordinator: context.coordinator) else {
271273
return
272274
}
273275

274-
updateControllerParams(controller: controller)
276+
updateControllerParams(controller: controller, coordinator: context.coordinator)
275277

276278
controller.reloadUI()
277279
return
278280
}
279281

280282
/// Update the parameters of the controller.
281283
/// - Parameter controller: The controller to update.
282-
func updateControllerParams(controller: TextViewController) {
284+
func updateControllerParams(controller: TextViewController, coordinator: Coordinator) {
283285
updateTextProperties(controller)
284286
updateEditorProperties(controller)
285287
updateThemeAndLanguage(controller)
286-
updateHighlighting(controller)
288+
updateHighlighting(controller, coordinator: coordinator)
287289
}
288290

289291
private func updateTextProperties(_ controller: TextViewController) {
@@ -336,9 +338,9 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
336338
}
337339
}
338340

339-
private func updateHighlighting(_ controller: TextViewController) {
340-
if !areHighlightProvidersEqual(controller: controller) {
341-
controller.setHighlightProviders(highlightProviders)
341+
private func updateHighlighting(_ controller: TextViewController, coordinator: Coordinator) {
342+
if !areHighlightProvidersEqual(controller: controller, coordinator: coordinator) {
343+
controller.setHighlightProviders(coordinator.highlightProviders)
342344
}
343345

344346
if controller.bracketPairEmphasis != bracketPairEmphasis {
@@ -349,7 +351,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
349351
/// Checks if the controller needs updating.
350352
/// - Parameter controller: The controller to check.
351353
/// - Returns: True, if the controller's parameters should be updated.
352-
func paramsAreEqual(controller: NSViewControllerType) -> Bool {
354+
func paramsAreEqual(controller: NSViewControllerType, coordinator: Coordinator) -> Bool {
353355
controller.font == font &&
354356
controller.isEditable == isEditable &&
355357
controller.isSelectable == isSelectable &&
@@ -367,11 +369,12 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
367369
controller.bracketPairEmphasis == bracketPairEmphasis &&
368370
controller.useSystemCursor == useSystemCursor &&
369371
controller.showMinimap == showMinimap &&
370-
areHighlightProvidersEqual(controller: controller)
372+
areHighlightProvidersEqual(controller: controller, coordinator: coordinator)
371373
}
372374

373-
private func areHighlightProvidersEqual(controller: TextViewController) -> Bool {
374-
controller.highlightProviders.map { ObjectIdentifier($0) } == highlightProviders.map { ObjectIdentifier($0) }
375+
private func areHighlightProvidersEqual(controller: TextViewController, coordinator: Coordinator) -> Bool {
376+
controller.highlightProviders.map { ObjectIdentifier($0) }
377+
== coordinator.highlightProviders.map { ObjectIdentifier($0) }
375378
}
376379
}
377380

0 commit comments

Comments
 (0)