Skip to content

Commit 98808d5

Browse files
committed
Toggle Minimap
1 parent da173ae commit 98808d5

File tree

7 files changed

+179
-95
lines changed

7 files changed

+179
-95
lines changed

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
6C1365462B8A7F2D004A1D18 /* LanguagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */; };
2020
6C1365482B8A7FBF004A1D18 /* EditorTheme+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1365472B8A7FBF004A1D18 /* EditorTheme+Default.swift */; };
2121
6C13654D2B8A821E004A1D18 /* NSColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C13654C2B8A821E004A1D18 /* NSColor+Hex.swift */; };
22+
6C2EE57E2DB1522E007E0A26 /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C2EE57D2DB1522E007E0A26 /* Toolbar.swift */; };
2223
/* End PBXBuildFile section */
2324

2425
/* Begin PBXFileReference section */
@@ -35,6 +36,7 @@
3536
6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguagePicker.swift; sourceTree = "<group>"; };
3637
6C1365472B8A7FBF004A1D18 /* EditorTheme+Default.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EditorTheme+Default.swift"; sourceTree = "<group>"; };
3738
6C13654C2B8A821E004A1D18 /* NSColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Hex.swift"; sourceTree = "<group>"; };
39+
6C2EE57D2DB1522E007E0A26 /* Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toolbar.swift; sourceTree = "<group>"; };
3840
/* End PBXFileReference section */
3941

4042
/* Begin PBXFrameworksBuildPhase section */
@@ -113,6 +115,7 @@
113115
isa = PBXGroup;
114116
children = (
115117
6C1365312B8A7B94004A1D18 /* ContentView.swift */,
118+
6C2EE57D2DB1522E007E0A26 /* Toolbar.swift */,
116119
6C1365452B8A7F2D004A1D18 /* LanguagePicker.swift */,
117120
);
118121
path = Views;
@@ -206,6 +209,7 @@
206209
6C1365482B8A7FBF004A1D18 /* EditorTheme+Default.swift in Sources */,
207210
6C13654D2B8A821E004A1D18 /* NSColor+Hex.swift in Sources */,
208211
6C1365302B8A7B94004A1D18 /* CodeEditSourceEditorExampleDocument.swift in Sources */,
212+
6C2EE57E2DB1522E007E0A26 /* Toolbar.swift in Sources */,
209213
6C13652E2B8A7B94004A1D18 /* CodeEditSourceEditorExampleApp.swift in Sources */,
210214
6C1365442B8A7EED004A1D18 /* String+Lines.swift in Sources */,
211215
6C1365322B8A7B94004A1D18 /* ContentView.swift in Sources */,

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample/Views/ContentView.swift

Lines changed: 14 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ 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
2930

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

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

Sources/CodeEditSourceEditor/CodeEditSourceEditor/CodeEditSourceEditor.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
7171
bracketPairEmphasis: BracketPairEmphasis? = .flash,
7272
useSystemCursor: Bool = true,
7373
undoManager: CEUndoManager? = nil,
74-
coordinators: [any TextViewCoordinator] = []
74+
coordinators: [any TextViewCoordinator] = [],
75+
showMinimap: Bool
7576
) {
7677
self.text = .binding(text)
7778
self.language = language
@@ -98,6 +99,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
9899
}
99100
self.undoManager = undoManager
100101
self.coordinators = coordinators
102+
self.showMinimap = showMinimap
101103
}
102104

103105
/// Initializes a Text Editor
@@ -148,7 +150,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
148150
bracketPairEmphasis: BracketPairEmphasis? = .flash,
149151
useSystemCursor: Bool = true,
150152
undoManager: CEUndoManager? = nil,
151-
coordinators: [any TextViewCoordinator] = []
153+
coordinators: [any TextViewCoordinator] = [],
154+
showMinimap: Bool
152155
) {
153156
self.text = .storage(text)
154157
self.language = language
@@ -175,6 +178,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
175178
}
176179
self.undoManager = undoManager
177180
self.coordinators = coordinators
181+
self.showMinimap = showMinimap
178182
}
179183

180184
package var text: TextAPI
@@ -198,6 +202,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
198202
private var useSystemCursor: Bool
199203
private var undoManager: CEUndoManager?
200204
package var coordinators: [any TextViewCoordinator]
205+
package var showMinimap: Bool
201206

202207
public typealias NSViewControllerType = TextViewController
203208

@@ -223,7 +228,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
223228
useSystemCursor: useSystemCursor,
224229
bracketPairEmphasis: bracketPairEmphasis,
225230
undoManager: undoManager,
226-
coordinators: coordinators
231+
coordinators: coordinators,
232+
showMinimap: showMinimap
227233
)
228234
switch text {
229235
case .binding(let binding):
@@ -301,6 +307,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
301307
controller.editorOverscroll = editorOverscroll
302308
controller.contentInsets = contentInsets
303309
controller.additionalTextInsets = additionalTextInsets
310+
controller.showMinimap = showMinimap
304311

305312
if controller.indentOption != indentOption {
306313
controller.indentOption = indentOption
@@ -359,6 +366,7 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
359366
controller.letterSpacing == letterSpacing &&
360367
controller.bracketPairEmphasis == bracketPairEmphasis &&
361368
controller.useSystemCursor == useSystemCursor &&
369+
controller.showMinimap == showMinimap &&
362370
areHighlightProvidersEqual(controller: controller)
363371
}
364372

Sources/CodeEditSourceEditor/Controller/TextViewController+LoadView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ extension TextViewController {
2929
)
3030

3131
minimapView = MinimapView(textView: textView, theme: theme)
32+
minimapView.isHidden = !showMinimap
3233
scrollView.addFloatingSubview(minimapView, for: .vertical)
3334

3435
let findViewController = FindViewController(target: self, childView: scrollView)
@@ -114,6 +115,7 @@ extension TextViewController {
114115
self?.textView.updatedViewport(self?.scrollView.documentVisibleRect ?? .zero)
115116
self?.gutterView.needsDisplay = true
116117
self?.emphasisManager?.removeEmphases(for: EmphasisGroup.brackets)
118+
self?.updateTextInsets()
117119
}
118120

119121
NotificationCenter.default.addObserver(

0 commit comments

Comments
 (0)