Skip to content

Commit 3a7fc91

Browse files
committed
Merge branch 'main' into fix/highlighter-highlight-minimap-viewport
2 parents 2ca5b03 + bb2cbf5 commit 3a7fc91

File tree

14 files changed

+221
-40
lines changed

14 files changed

+221
-40
lines changed

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
6C1365462B8A7F2D004A1D18 /* LanguagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */; };
2121
6C1365482B8A7FBF004A1D18 /* EditorTheme+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1365472B8A7FBF004A1D18 /* EditorTheme+Default.swift */; };
2222
6C13654D2B8A821E004A1D18 /* NSColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C13654C2B8A821E004A1D18 /* NSColor+Hex.swift */; };
23-
6C2EE57E2DB1522E007E0A26 /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C2EE57D2DB1522E007E0A26 /* Toolbar.swift */; };
23+
6CF31D4E2DB6A252006A77FD /* StatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF31D4D2DB6A252006A77FD /* StatusBar.swift */; };
2424
/* End PBXBuildFile section */
2525

2626
/* Begin PBXFileReference section */
@@ -38,7 +38,7 @@
3838
6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguagePicker.swift; sourceTree = "<group>"; };
3939
6C1365472B8A7FBF004A1D18 /* EditorTheme+Default.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditorTheme+Default.swift"; sourceTree = "<group>"; };
4040
6C13654C2B8A821E004A1D18 /* NSColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Hex.swift"; sourceTree = "<group>"; };
41-
6C2EE57D2DB1522E007E0A26 /* Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toolbar.swift; sourceTree = "<group>"; };
41+
6CF31D4D2DB6A252006A77FD /* StatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBar.swift; sourceTree = "<group>"; };
4242
/* End PBXFileReference section */
4343

4444
/* Begin PBXFrameworksBuildPhase section */
@@ -117,7 +117,7 @@
117117
isa = PBXGroup;
118118
children = (
119119
6C1365312B8A7B94004A1D18 /* ContentView.swift */,
120-
6C2EE57D2DB1522E007E0A26 /* Toolbar.swift */,
120+
6CF31D4D2DB6A252006A77FD /* StatusBar.swift */,
121121
6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */,
122122
1CB30C392DAA1C28008058A7 /* IndentPicker.swift */,
123123
);
@@ -212,7 +212,7 @@
212212
6C1365482B8A7FBF004A1D18 /* EditorTheme+Default.swift in Sources */,
213213
6C13654D2B8A821E004A1D18 /* NSColor+Hex.swift in Sources */,
214214
6C1365302B8A7B94004A1D18 /* CodeEditSourceEditorExampleDocument.swift in Sources */,
215-
6C2EE57E2DB1522E007E0A26 /* Toolbar.swift in Sources */,
215+
6CF31D4E2DB6A252006A77FD /* StatusBar.swift in Sources */,
216216
6C13652E2B8A7B94004A1D18 /* CodeEditSourceEditorExampleApp.swift in Sources */,
217217
6C1365442B8A7EED004A1D18 /* String+Lines.swift in Sources */,
218218
1CB30C3A2DAA1C28008058A7 /* IndentPicker.swift in Sources */,

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample/Views/ContentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ struct ContentView: View {
5555
showMinimap: showMinimap
5656
)
5757
.overlay(alignment: .bottom) {
58-
Toolbar(
58+
StatusBar(
5959
fileURL: fileURL,
6060
document: $document,
6161
wrapLines: $wrapLines,
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//
2+
// StatusBar.swift
3+
// CodeEditSourceEditorExample
4+
//
5+
// Created by Khan Winter on 4/17/25.
6+
//
7+
8+
import SwiftUI
9+
import CodeEditSourceEditor
10+
import CodeEditLanguages
11+
12+
struct StatusBar: View {
13+
let fileURL: URL?
14+
15+
@Environment(\.colorScheme)
16+
var colorScheme
17+
18+
@Binding var document: CodeEditSourceEditorExampleDocument
19+
@Binding var wrapLines: Bool
20+
@Binding var useSystemCursor: Bool
21+
@Binding var cursorPositions: [CursorPosition]
22+
@Binding var isInLongParse: Bool
23+
@Binding var language: CodeLanguage
24+
@Binding var theme: EditorTheme
25+
@Binding var showMinimap: Bool
26+
@Binding var indentOption: IndentOption
27+
28+
var body: some View {
29+
HStack {
30+
Menu {
31+
Toggle("Wrap Lines", isOn: $wrapLines)
32+
Toggle("Show Minimap", isOn: $showMinimap)
33+
if #available(macOS 14, *) {
34+
Toggle("Use System Cursor", isOn: $useSystemCursor)
35+
} else {
36+
Toggle("Use System Cursor", isOn: $useSystemCursor)
37+
.disabled(true)
38+
.help("macOS 14 required")
39+
}
40+
} label: {}
41+
.background {
42+
Image(systemName: "switch.2")
43+
.foregroundStyle(.secondary)
44+
.font(.system(size: 13.5, weight: .regular))
45+
}
46+
.menuStyle(.borderlessButton)
47+
.menuIndicator(.hidden)
48+
.frame(maxWidth: 18, alignment: .center)
49+
50+
Spacer()
51+
52+
Group {
53+
if isInLongParse {
54+
HStack(spacing: 5) {
55+
ProgressView()
56+
.controlSize(.small)
57+
Text("Parsing Document")
58+
}
59+
} else {
60+
Text(getLabel(cursorPositions))
61+
}
62+
}
63+
.foregroundStyle(.secondary)
64+
Divider()
65+
.frame(height: 12)
66+
LanguagePicker(language: $language)
67+
.buttonStyle(.borderless)
68+
IndentPicker(indentOption: $indentOption, enabled: document.text.isEmpty)
69+
.buttonStyle(.borderless)
70+
}
71+
.font(.subheadline)
72+
.fontWeight(.medium)
73+
.controlSize(.small)
74+
.padding(.horizontal, 8)
75+
.frame(height: 28)
76+
.background(.bar)
77+
.overlay(alignment: .top) {
78+
VStack {
79+
Divider()
80+
.overlay {
81+
if colorScheme == .dark {
82+
Color.black
83+
}
84+
}
85+
}
86+
}
87+
.zIndex(2)
88+
.onAppear {
89+
self.language = detectLanguage(fileURL: fileURL) ?? .default
90+
self.theme = colorScheme == .dark ? .dark : .light
91+
}
92+
}
93+
94+
private func detectLanguage(fileURL: URL?) -> CodeLanguage? {
95+
guard let fileURL else { return nil }
96+
return CodeLanguage.detectLanguageFrom(
97+
url: fileURL,
98+
prefixBuffer: document.text.getFirstLines(5),
99+
suffixBuffer: document.text.getLastLines(5)
100+
)
101+
}
102+
103+
/// Create a label string for cursor positions.
104+
/// - Parameter cursorPositions: The cursor positions to create the label for.
105+
/// - Returns: A string describing the user's location in a document.
106+
func getLabel(_ cursorPositions: [CursorPosition]) -> String {
107+
if cursorPositions.isEmpty {
108+
return "No cursor"
109+
}
110+
111+
// More than one selection, display the number of selections.
112+
if cursorPositions.count > 1 {
113+
return "\(cursorPositions.count) selected ranges"
114+
}
115+
116+
// When there's a single cursor, display the line and column.
117+
return "Line: \(cursorPositions[0].line) Col: \(cursorPositions[0].column) Range: \(cursorPositions[0].range)"
118+
}
119+
}

Package.resolved

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ let package = Package(
1616
dependencies: [
1717
// A fast, efficient, text view for code.
1818
.package(
19-
// url: "https://github.com/CodeEditApp/CodeEditTextView.git",
20-
// from: "0.9.1"
21-
path: "../CodeEditTextView"
19+
url: "https://github.com/CodeEditApp/CodeEditTextView.git",
20+
from: "0.10.1"
2221
),
2322
// tree-sitter languages
2423
.package(

Sources/CodeEditSourceEditor/CodeEditSourceEditor/CodeEditSourceEditor+Coordinator.swift

Lines changed: 12 additions & 1 deletion
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

Lines changed: 23 additions & 20 deletions
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)