Skip to content

Commit d45d727

Browse files
committed
Merge branch 'main' into feat/find-replace
2 parents 938e279 + bb2cbf5 commit d45d727

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1434
-225
lines changed

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
1CB30C3A2DAA1C28008058A7 /* IndentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CB30C392DAA1C28008058A7 /* IndentPicker.swift */; };
1011
61621C612C74FB2200494A4A /* CodeEditSourceEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 61621C602C74FB2200494A4A /* CodeEditSourceEditor */; };
1112
61CE772F2D19BF7D00908C57 /* CodeEditSourceEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 61CE772E2D19BF7D00908C57 /* CodeEditSourceEditor */; };
1213
61CE77322D19BFAA00908C57 /* CodeEditSourceEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 61CE77312D19BFAA00908C57 /* CodeEditSourceEditor */; };
@@ -19,9 +20,11 @@
1920
6C1365462B8A7F2D004A1D18 /* LanguagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */; };
2021
6C1365482B8A7FBF004A1D18 /* EditorTheme+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1365472B8A7FBF004A1D18 /* EditorTheme+Default.swift */; };
2122
6C13654D2B8A821E004A1D18 /* NSColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C13654C2B8A821E004A1D18 /* NSColor+Hex.swift */; };
23+
6CF31D4E2DB6A252006A77FD /* StatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF31D4D2DB6A252006A77FD /* StatusBar.swift */; };
2224
/* End PBXBuildFile section */
2325

2426
/* Begin PBXFileReference section */
27+
1CB30C392DAA1C28008058A7 /* IndentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndentPicker.swift; sourceTree = "<group>"; };
2528
6C13652A2B8A7B94004A1D18 /* CodeEditSourceEditorExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CodeEditSourceEditorExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
2629
6C13652D2B8A7B94004A1D18 /* CodeEditSourceEditorExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeEditSourceEditorExampleApp.swift; sourceTree = "<group>"; };
2730
6C13652F2B8A7B94004A1D18 /* CodeEditSourceEditorExampleDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeEditSourceEditorExampleDocument.swift; sourceTree = "<group>"; };
@@ -35,6 +38,7 @@
3538
6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguagePicker.swift; sourceTree = "<group>"; };
3639
6C1365472B8A7FBF004A1D18 /* EditorTheme+Default.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditorTheme+Default.swift"; sourceTree = "<group>"; };
3740
6C13654C2B8A821E004A1D18 /* NSColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Hex.swift"; sourceTree = "<group>"; };
41+
6CF31D4D2DB6A252006A77FD /* StatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBar.swift; sourceTree = "<group>"; };
3842
/* End PBXFileReference section */
3943

4044
/* Begin PBXFrameworksBuildPhase section */
@@ -113,7 +117,9 @@
113117
isa = PBXGroup;
114118
children = (
115119
6C1365312B8A7B94004A1D18 /* ContentView.swift */,
120+
6CF31D4D2DB6A252006A77FD /* StatusBar.swift */,
116121
6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */,
122+
1CB30C392DAA1C28008058A7 /* IndentPicker.swift */,
117123
);
118124
path = Views;
119125
sourceTree = "<group>";
@@ -206,8 +212,10 @@
206212
6C1365482B8A7FBF004A1D18 /* EditorTheme+Default.swift in Sources */,
207213
6C13654D2B8A821E004A1D18 /* NSColor+Hex.swift in Sources */,
208214
6C1365302B8A7B94004A1D18 /* CodeEditSourceEditorExampleDocument.swift in Sources */,
215+
6CF31D4E2DB6A252006A77FD /* StatusBar.swift in Sources */,
209216
6C13652E2B8A7B94004A1D18 /* CodeEditSourceEditorExampleApp.swift in Sources */,
210217
6C1365442B8A7EED004A1D18 /* String+Lines.swift in Sources */,
218+
1CB30C3A2DAA1C28008058A7 /* IndentPicker.swift in Sources */,
211219
6C1365322B8A7B94004A1D18 /* ContentView.swift in Sources */,
212220
6C1365462B8A7F2D004A1D18 /* LanguagePicker.swift in Sources */,
213221
);

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

Lines changed: 20 additions & 2 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: 19 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ struct ContentView: View {
2626
@State private var isInLongParse = false
2727
@State private var settingsIsPresented: Bool = false
2828
@State private var treeSitterClient = TreeSitterClient()
29+
@AppStorage("showMinimap") private var showMinimap: Bool = true
30+
@State private var indentOption: IndentOption = .spaces(count: 4)
2931

3032
init(document: Binding<CodeEditSourceEditorExampleDocument>, fileURL: URL?) {
3133
self._document = document
@@ -40,73 +42,31 @@ struct ContentView: View {
4042
theme: theme,
4143
font: font,
4244
tabWidth: 4,
45+
indentOption: indentOption,
4346
lineHeight: 1.2,
4447
wrapLines: wrapLines,
48+
editorOverscroll: 0.3,
4549
cursorPositions: $cursorPositions,
4650
useThemeBackground: true,
4751
highlightProviders: [treeSitterClient],
4852
contentInsets: NSEdgeInsets(top: proxy.safeAreaInsets.top, left: 0, bottom: 28.0, right: 0),
49-
useSystemCursor: useSystemCursor
53+
additionalTextInsets: NSEdgeInsets(top: 1, left: 0, bottom: 1, right: 0),
54+
useSystemCursor: useSystemCursor,
55+
showMinimap: showMinimap
5056
)
5157
.overlay(alignment: .bottom) {
52-
HStack {
53-
Menu {
54-
Toggle("Wrap Lines", isOn: $wrapLines)
55-
if #available(macOS 14, *) {
56-
Toggle("Use System Cursor", isOn: $useSystemCursor)
57-
} else {
58-
Toggle("Use System Cursor", isOn: $useSystemCursor)
59-
.disabled(true)
60-
.help("macOS 14 required")
61-
}
62-
} label: {}
63-
.background {
64-
Image(systemName: "switch.2")
65-
.foregroundStyle(.secondary)
66-
.font(.system(size: 13.5, weight: .regular))
67-
}
68-
.menuStyle(.borderlessButton)
69-
.menuIndicator(.hidden)
70-
.frame(maxWidth: 18, alignment: .center)
71-
Spacer()
72-
Group {
73-
if isInLongParse {
74-
HStack(spacing: 5) {
75-
ProgressView()
76-
.controlSize(.small)
77-
Text("Parsing Document")
78-
}
79-
} else {
80-
Text(getLabel(cursorPositions))
81-
}
82-
}
83-
.foregroundStyle(.secondary)
84-
Divider()
85-
.frame(height: 12)
86-
LanguagePicker(language: $language)
87-
.buttonStyle(.borderless)
88-
}
89-
.font(.subheadline)
90-
.fontWeight(.medium)
91-
.controlSize(.small)
92-
.padding(.horizontal, 8)
93-
.frame(height: 28)
94-
.background(.bar)
95-
.overlay(alignment: .top) {
96-
VStack {
97-
Divider()
98-
.overlay {
99-
if colorScheme == .dark {
100-
Color.black
101-
}
102-
}
103-
}
104-
}
105-
.zIndex(2)
106-
.onAppear {
107-
self.language = detectLanguage(fileURL: fileURL) ?? .default
108-
self.theme = colorScheme == .dark ? .dark : .light
109-
}
58+
StatusBar(
59+
fileURL: fileURL,
60+
document: $document,
61+
wrapLines: $wrapLines,
62+
useSystemCursor: $useSystemCursor,
63+
cursorPositions: $cursorPositions,
64+
isInLongParse: $isInLongParse,
65+
language: $language,
66+
theme: $theme,
67+
showMinimap: $showMinimap,
68+
indentOption: $indentOption
69+
)
11070
}
11171
.ignoresSafeArea()
11272
.frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -129,32 +89,6 @@ struct ContentView: View {
12989
}
13090
}
13191
}
132-
133-
private func detectLanguage(fileURL: URL?) -> CodeLanguage? {
134-
guard let fileURL else { return nil }
135-
return CodeLanguage.detectLanguageFrom(
136-
url: fileURL,
137-
prefixBuffer: document.text.getFirstLines(5),
138-
suffixBuffer: document.text.getLastLines(5)
139-
)
140-
}
141-
142-
/// Create a label string for cursor positions.
143-
/// - Parameter cursorPositions: The cursor positions to create the label for.
144-
/// - Returns: A string describing the user's location in a document.
145-
func getLabel(_ cursorPositions: [CursorPosition]) -> String {
146-
if cursorPositions.isEmpty {
147-
return "No cursor"
148-
}
149-
150-
// More than one selection, display the number of selections.
151-
if cursorPositions.count > 1 {
152-
return "\(cursorPositions.count) selected ranges"
153-
}
154-
155-
// When there's a single cursor, display the line and column.
156-
return "Line: \(cursorPositions[0].line) Col: \(cursorPositions[0].column) Range: \(cursorPositions[0].range)"
157-
}
15892
}
15993

16094
#Preview {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import SwiftUI
2+
import CodeEditSourceEditor
3+
4+
struct IndentPicker: View {
5+
@Binding var indentOption: IndentOption
6+
let enabled: Bool
7+
8+
private let possibleIndents: [IndentOption] = [
9+
.spaces(count: 4),
10+
.spaces(count: 2),
11+
.tab
12+
]
13+
14+
var body: some View {
15+
Picker(
16+
"Indent",
17+
selection: $indentOption
18+
) {
19+
ForEach(possibleIndents, id: \.optionDescription) { indent in
20+
Text(indent.optionDescription)
21+
.tag(indent)
22+
}
23+
}
24+
.labelsHidden()
25+
.disabled(!enabled)
26+
}
27+
}
28+
29+
extension IndentOption {
30+
var optionDescription: String {
31+
switch self {
32+
case .spaces(count: let count):
33+
return "Spaces (\(count))"
34+
case .tab:
35+
return "Tab"
36+
}
37+
}
38+
}
39+
40+
#Preview {
41+
IndentPicker(indentOption: .constant(.spaces(count: 4)), enabled: true)
42+
}
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+
}

0 commit comments

Comments
 (0)