Skip to content

Smarter Default Highlight Provider Management #310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ extension CodeEditSourceEditor {
var text: TextAPI
@Binding var cursorPositions: [CursorPosition]

init(text: TextAPI, cursorPositions: Binding<[CursorPosition]>) {
private(set) var highlightProviders: [any HighlightProviding]

init(text: TextAPI, cursorPositions: Binding<[CursorPosition]>, highlightProviders: [any HighlightProviding]?) {
self.text = text
self._cursorPositions = cursorPositions
self.highlightProviders = highlightProviders ?? [TreeSitterClient()]
super.init()

NotificationCenter.default.addObserver(
Expand All @@ -38,6 +41,14 @@ extension CodeEditSourceEditor {
)
}

func updateHighlightProviders(_ highlightProviders: [any HighlightProviding]?) {
guard let highlightProviders else {
return // Keep our default `TreeSitterClient` if they're `nil`
}
// Otherwise, we can replace the stored providers.
self.highlightProviders = highlightProviders
}

@objc func textViewDidChangeText(_ notification: Notification) {
guard let textView = notification.object as? TextView,
let controller,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
/// - cursorPositions: The cursor's position in the editor, measured in `(lineNum, columnNum)`
/// - useThemeBackground: Determines whether the editor uses the theme's background color, or a transparent
/// background color
/// - highlightProvider: A class you provide to perform syntax highlighting. Leave this as `nil` to use the
/// built-in `TreeSitterClient` highlighter.
/// - highlightProviders: A set of classes you provide to perform syntax highlighting. Leave this as `nil` to use
/// the default `TreeSitterClient` highlighter.
/// - contentInsets: Insets to use to offset the content in the enclosing scroll view. Leave as `nil` to let the
/// scroll view automatically adjust content insets.
/// - additionalTextInsets: An additional amount to inset the text of the editor by.
Expand All @@ -62,7 +62,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
editorOverscroll: CGFloat = 0,
cursorPositions: Binding<[CursorPosition]>,
useThemeBackground: Bool = true,
highlightProviders: [any HighlightProviding] = [TreeSitterClient()],
highlightProviders: [any HighlightProviding]? = nil,
contentInsets: NSEdgeInsets? = nil,
additionalTextInsets: NSEdgeInsets? = nil,
isEditable: Bool = true,
Expand Down Expand Up @@ -114,8 +114,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
/// - cursorPositions: The cursor's position in the editor, measured in `(lineNum, columnNum)`
/// - useThemeBackground: Determines whether the editor uses the theme's background color, or a transparent
/// background color
/// - highlightProvider: A class you provide to perform syntax highlighting. Leave this as `nil` to use the
/// built-in `TreeSitterClient` highlighter.
/// - highlightProviders: A set of classes you provide to perform syntax highlighting. Leave this as `nil` to use
/// the default `TreeSitterClient` highlighter.
/// - contentInsets: Insets to use to offset the content in the enclosing scroll view. Leave as `nil` to let the
/// scroll view automatically adjust content insets.
/// - isEditable: A Boolean value that controls whether the text view allows the user to edit text.
Expand All @@ -139,7 +139,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
editorOverscroll: CGFloat = 0,
cursorPositions: Binding<[CursorPosition]>,
useThemeBackground: Bool = true,
highlightProviders: [any HighlightProviding] = [TreeSitterClient()],
highlightProviders: [any HighlightProviding]? = nil,
contentInsets: NSEdgeInsets? = nil,
additionalTextInsets: NSEdgeInsets? = nil,
isEditable: Bool = true,
Expand Down Expand Up @@ -188,7 +188,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
private var editorOverscroll: CGFloat
package var cursorPositions: Binding<[CursorPosition]>
private var useThemeBackground: Bool
private var highlightProviders: [any HighlightProviding]
private var highlightProviders: [any HighlightProviding]?
private var contentInsets: NSEdgeInsets?
private var additionalTextInsets: NSEdgeInsets?
private var isEditable: Bool
Expand All @@ -214,7 +214,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
cursorPositions: cursorPositions.wrappedValue,
editorOverscroll: editorOverscroll,
useThemeBackground: useThemeBackground,
highlightProviders: highlightProviders,
highlightProviders: context.coordinator.highlightProviders,
contentInsets: contentInsets,
additionalTextInsets: additionalTextInsets,
isEditable: isEditable,
Expand Down Expand Up @@ -243,10 +243,12 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
}

public func makeCoordinator() -> Coordinator {
Coordinator(text: text, cursorPositions: cursorPositions)
Coordinator(text: text, cursorPositions: cursorPositions, highlightProviders: highlightProviders)
}

public func updateNSViewController(_ controller: TextViewController, context: Context) {
context.coordinator.updateHighlightProviders(highlightProviders)

if !context.coordinator.isUpdateFromTextView {
// Prevent infinite loop of update notifications
context.coordinator.isUpdatingFromRepresentable = true
Expand All @@ -261,23 +263,23 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {

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

updateControllerParams(controller: controller)
updateControllerParams(controller: controller, coordinator: context.coordinator)

controller.reloadUI()
return
}

/// Update the parameters of the controller.
/// - Parameter controller: The controller to update.
func updateControllerParams(controller: TextViewController) {
func updateControllerParams(controller: TextViewController, coordinator: Coordinator) {
updateTextProperties(controller)
updateEditorProperties(controller)
updateThemeAndLanguage(controller)
updateHighlighting(controller)
updateHighlighting(controller, coordinator: coordinator)
}

private func updateTextProperties(_ controller: TextViewController) {
Expand Down Expand Up @@ -329,9 +331,9 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
}
}

private func updateHighlighting(_ controller: TextViewController) {
if !areHighlightProvidersEqual(controller: controller) {
controller.setHighlightProviders(highlightProviders)
private func updateHighlighting(_ controller: TextViewController, coordinator: Coordinator) {
if !areHighlightProvidersEqual(controller: controller, coordinator: coordinator) {
controller.setHighlightProviders(coordinator.highlightProviders)
}

if controller.bracketPairEmphasis != bracketPairEmphasis {
Expand All @@ -342,7 +344,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
/// Checks if the controller needs updating.
/// - Parameter controller: The controller to check.
/// - Returns: True, if the controller's parameters should be updated.
func paramsAreEqual(controller: NSViewControllerType) -> Bool {
func paramsAreEqual(controller: NSViewControllerType, coordinator: Coordinator) -> Bool {
controller.font == font &&
controller.isEditable == isEditable &&
controller.isSelectable == isSelectable &&
Expand All @@ -359,11 +361,12 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
controller.letterSpacing == letterSpacing &&
controller.bracketPairEmphasis == bracketPairEmphasis &&
controller.useSystemCursor == useSystemCursor &&
areHighlightProvidersEqual(controller: controller)
areHighlightProvidersEqual(controller: controller, coordinator: coordinator)
}

private func areHighlightProvidersEqual(controller: TextViewController) -> Bool {
controller.highlightProviders.map { ObjectIdentifier($0) } == highlightProviders.map { ObjectIdentifier($0) }
private func areHighlightProvidersEqual(controller: TextViewController, coordinator: Coordinator) -> Bool {
controller.highlightProviders.map { ObjectIdentifier($0) }
== coordinator.highlightProviders.map { ObjectIdentifier($0) }
}
}

Expand Down