Skip to content

Commit eaa3e16

Browse files
committed
Implemented reformatting guide, styled it, and added configuration options to show or hide it and change it's position.
1 parent 5bf097f commit eaa3e16

File tree

8 files changed

+206
-63
lines changed

8 files changed

+206
-63
lines changed

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample/Views/ContentView.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ struct ContentView: View {
2828
@State private var treeSitterClient = TreeSitterClient()
2929
@AppStorage("showMinimap") private var showMinimap: Bool = true
3030
@State private var indentOption: IndentOption = .spaces(count: 4)
31+
@AppStorage("reformatAtColumn") private var reformatAtColumn: Int = 80 {
32+
didSet {
33+
print("reformatAtColumn changed to: \(reformatAtColumn)")
34+
}
35+
}
36+
@AppStorage("showReformattingGuide") private var showReformattingGuide: Bool = false {
37+
didSet {
38+
print("showReformattingGuide changed to: \(showReformattingGuide)")
39+
}
40+
}
3141

3242
init(document: Binding<CodeEditSourceEditorExampleDocument>, fileURL: URL?) {
3343
self._document = document
@@ -52,7 +62,9 @@ struct ContentView: View {
5262
contentInsets: NSEdgeInsets(top: proxy.safeAreaInsets.top, left: 0, bottom: 28.0, right: 0),
5363
additionalTextInsets: NSEdgeInsets(top: 1, left: 0, bottom: 1, right: 0),
5464
useSystemCursor: useSystemCursor,
55-
showMinimap: showMinimap
65+
showMinimap: showMinimap,
66+
reformatAtColumn: reformatAtColumn,
67+
showReformattingGuide: showReformattingGuide
5668
)
5769
.overlay(alignment: .bottom) {
5870
StatusBar(
@@ -65,7 +77,9 @@ struct ContentView: View {
6577
language: $language,
6678
theme: $theme,
6779
showMinimap: $showMinimap,
68-
indentOption: $indentOption
80+
indentOption: $indentOption,
81+
reformatAtColumn: $reformatAtColumn,
82+
showReformattingGuide: $showReformattingGuide
6983
)
7084
}
7185
.ignoresSafeArea()
@@ -87,6 +101,12 @@ struct ContentView: View {
87101
theme = .light
88102
}
89103
}
104+
.onChange(of: reformatAtColumn) { _, newValue in
105+
print("ContentView: reformatAtColumn changed to \(newValue)")
106+
}
107+
.onChange(of: showReformattingGuide) { _, newValue in
108+
print("ContentView: showReformattingGuide changed to \(newValue)")
109+
}
90110
}
91111
}
92112
}

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample/Views/StatusBar.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,25 @@ struct StatusBar: View {
2424
@Binding var theme: EditorTheme
2525
@Binding var showMinimap: Bool
2626
@Binding var indentOption: IndentOption
27+
@Binding var reformatAtColumn: Int
28+
@Binding var showReformattingGuide: Bool
2729

2830
var body: some View {
2931
HStack {
3032
Menu {
33+
IndentPicker(indentOption: $indentOption, enabled: document.text.isEmpty)
34+
.buttonStyle(.borderless)
3135
Toggle("Wrap Lines", isOn: $wrapLines)
3236
Toggle("Show Minimap", isOn: $showMinimap)
37+
Toggle("Show Reformatting Guide", isOn: $showReformattingGuide)
38+
Picker("Reformat column at column", selection: $reformatAtColumn) {
39+
ForEach([40, 60, 80, 100, 120, 140, 160, 180, 200], id: \.self) { column in
40+
Text("\(column)").tag(column)
41+
}
42+
}
43+
.onChange(of: reformatAtColumn) { _, newValue in
44+
reformatAtColumn = max(1, min(200, newValue))
45+
}
3346
if #available(macOS 14, *) {
3447
Toggle("Use System Cursor", isOn: $useSystemCursor)
3548
} else {
@@ -65,8 +78,6 @@ struct StatusBar: View {
6578
.frame(height: 12)
6679
LanguagePicker(language: $language)
6780
.buttonStyle(.borderless)
68-
IndentPicker(indentOption: $indentOption, enabled: document.text.isEmpty)
69-
.buttonStyle(.borderless)
7081
}
7182
.font(.subheadline)
7283
.fontWeight(.medium)

Sources/CodeEditSourceEditor/CodeEditSourceEditor/CodeEditSourceEditor.swift

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
5050
/// - useSystemCursor: If true, uses the system cursor on `>=macOS 14`.
5151
/// - undoManager: The undo manager for the text view. Defaults to `nil`, which will create a new CEUndoManager
5252
/// - coordinators: Any text coordinators for the view to use. See ``TextViewCoordinator`` for more information.
53+
/// - showMinimap: Whether to show the minimap
54+
/// - reformatAtColumn: The column to reformat at
55+
/// - showReformattingGuide: Whether to show the reformatting guide
5356
public init(
5457
_ text: Binding<String>,
5558
language: CodeLanguage,
@@ -72,7 +75,9 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
7275
useSystemCursor: Bool = true,
7376
undoManager: CEUndoManager? = nil,
7477
coordinators: [any TextViewCoordinator] = [],
75-
showMinimap: Bool
78+
showMinimap: Bool,
79+
reformatAtColumn: Int,
80+
showReformattingGuide: Bool
7681
) {
7782
self.text = .binding(text)
7883
self.language = language
@@ -100,6 +105,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
100105
self.undoManager = undoManager
101106
self.coordinators = coordinators
102107
self.showMinimap = showMinimap
108+
self.reformatAtColumn = reformatAtColumn
109+
self.showReformattingGuide = showReformattingGuide
103110
}
104111

105112
/// Initializes a Text Editor
@@ -129,6 +136,9 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
129136
/// See `BracketPairEmphasis` for more information. Defaults to `nil`
130137
/// - undoManager: The undo manager for the text view. Defaults to `nil`, which will create a new CEUndoManager
131138
/// - coordinators: Any text coordinators for the view to use. See ``TextViewCoordinator`` for more information.
139+
/// - showMinimap: Whether to show the minimap
140+
/// - reformatAtColumn: The column to reformat at
141+
/// - showReformattingGuide: Whether to show the reformatting guide
132142
public init(
133143
_ text: NSTextStorage,
134144
language: CodeLanguage,
@@ -151,7 +161,9 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
151161
useSystemCursor: Bool = true,
152162
undoManager: CEUndoManager? = nil,
153163
coordinators: [any TextViewCoordinator] = [],
154-
showMinimap: Bool
164+
showMinimap: Bool,
165+
reformatAtColumn: Int,
166+
showReformattingGuide: Bool
155167
) {
156168
self.text = .storage(text)
157169
self.language = language
@@ -179,6 +191,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
179191
self.undoManager = undoManager
180192
self.coordinators = coordinators
181193
self.showMinimap = showMinimap
194+
self.reformatAtColumn = reformatAtColumn
195+
self.showReformattingGuide = showReformattingGuide
182196
}
183197

184198
package var text: TextAPI
@@ -203,6 +217,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
203217
private var undoManager: CEUndoManager?
204218
package var coordinators: [any TextViewCoordinator]
205219
package var showMinimap: Bool
220+
private var reformatAtColumn: Int
221+
private var showReformattingGuide: Bool
206222

207223
public typealias NSViewControllerType = TextViewController
208224

@@ -229,7 +245,9 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
229245
bracketPairEmphasis: bracketPairEmphasis,
230246
undoManager: undoManager,
231247
coordinators: coordinators,
232-
showMinimap: showMinimap
248+
showMinimap: showMinimap,
249+
reformatAtColumn: reformatAtColumn,
250+
showReformattingGuide: showReformattingGuide
233251
)
234252
switch text {
235253
case .binding(let binding):
@@ -286,6 +304,14 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
286304
updateEditorProperties(controller)
287305
updateThemeAndLanguage(controller)
288306
updateHighlighting(controller, coordinator: coordinator)
307+
308+
if controller.reformatAtColumn != reformatAtColumn {
309+
controller.reformatAtColumn = reformatAtColumn
310+
}
311+
312+
if controller.showReformattingGuide != showReformattingGuide {
313+
controller.showReformattingGuide = showReformattingGuide
314+
}
289315
}
290316

291317
private func updateTextProperties(_ controller: TextViewController) {
@@ -369,6 +395,8 @@ public struct CodeEditSourceEditor: NSViewControllerRepresentable {
369395
controller.bracketPairEmphasis == bracketPairEmphasis &&
370396
controller.useSystemCursor == useSystemCursor &&
371397
controller.showMinimap == showMinimap &&
398+
controller.reformatAtColumn == reformatAtColumn &&
399+
controller.showReformattingGuide == showReformattingGuide &&
372400
areHighlightProvidersEqual(controller: controller, coordinator: coordinator)
373401
}
374402

Sources/CodeEditSourceEditor/Controller/TextViewController+LoadView.swift

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ extension TextViewController {
1212
override public func loadView() {
1313
super.loadView()
1414

15+
// Create scroll view
1516
scrollView = NSScrollView()
1617
scrollView.documentView = textView
1718

19+
// Create gutter view
1820
gutterView = GutterView(
1921
font: font.rulerFont,
2022
textColor: theme.text.color.withAlphaComponent(0.35),
@@ -23,18 +25,23 @@ extension TextViewController {
2325
delegate: self
2426
)
2527
gutterView.updateWidthIfNeeded()
26-
scrollView.addFloatingSubview(
27-
gutterView,
28-
for: .horizontal
28+
scrollView.addFloatingSubview(gutterView, for: .horizontal)
29+
30+
// Create reformatting guide view
31+
guideView = ReformattingGuideView(
32+
column: self.reformatAtColumn,
33+
isVisible: self.showReformattingGuide,
34+
theme: theme
2935
)
36+
guideView.wantsLayer = true
37+
scrollView.addFloatingSubview(guideView, for: .vertical)
38+
guideView.updatePosition(in: textView)
3039

40+
// Create minimap view
3141
minimapView = MinimapView(textView: textView, theme: theme)
3242
scrollView.addFloatingSubview(minimapView, for: .vertical)
3343

34-
// Add reformatting guide view
35-
let guideView = ReformattingGuideView(frame: NSRect(x: 0, y: 0, width: 100, height: 100))
36-
textView.addSubview(guideView)
37-
44+
// Create find view
3845
let findViewController = FindViewController(target: self, childView: scrollView)
3946
addChild(findViewController)
4047
self.findViewController = findViewController
@@ -46,10 +53,13 @@ extension TextViewController {
4653
textView.setUndoManager(_undoManager)
4754
}
4855

56+
// Style views
4957
styleTextView()
5058
styleScrollView()
5159
styleGutterView()
5260
styleMinimapView()
61+
62+
// Set up
5363
setUpHighlighter()
5464
setUpTextFormation()
5565

@@ -104,7 +114,8 @@ extension TextViewController {
104114
object: scrollView.contentView,
105115
queue: .main
106116
) { [weak self] notification in
107-
guard let clipView = notification.object as? NSClipView else { return }
117+
guard let clipView = notification.object as? NSClipView,
118+
let textView = self?.textView else { return }
108119
self?.textView.updatedViewport(self?.scrollView.documentVisibleRect ?? .zero)
109120
self?.gutterView.needsDisplay = true
110121
self?.minimapXConstraint?.constant = clipView.bounds.origin.x
@@ -127,8 +138,10 @@ extension TextViewController {
127138
object: textView,
128139
queue: .main
129140
) { [weak self] _ in
141+
guard let textView = self?.textView else { return }
130142
self?.gutterView.frame.size.height = (self?.textView.frame.height ?? 0) + 10
131143
self?.gutterView.needsDisplay = true
144+
self?.guideView?.updatePosition(in: textView)
132145
}
133146

134147
NotificationCenter.default.addObserver(

Sources/CodeEditSourceEditor/Controller/TextViewController+ReloadUI.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extension TextViewController {
1919
highlighter?.invalidate()
2020
minimapView.updateContentViewHeight()
2121
minimapView.updateDocumentVisibleViewPosition()
22-
22+
2323
// Update reformatting guide position
2424
if let guideView = textView.subviews.first(where: { $0 is ReformattingGuideView }) as? ReformattingGuideView {
2525
guideView.updatePosition(in: textView)

Sources/CodeEditSourceEditor/Controller/TextViewController.swift

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public class TextViewController: NSViewController {
6969
gutterView.textColor = theme.text.color.withAlphaComponent(0.35)
7070
gutterView.selectedLineTextColor = theme.text.color
7171
minimapView.setTheme(theme)
72+
guideView?.setTheme(theme)
7273
}
7374
}
7475

@@ -233,6 +234,37 @@ public class TextViewController: NSViewController {
233234
)
234235
}
235236

237+
/// The column at which to show the reformatting guide
238+
public var reformatAtColumn: Int = 80 {
239+
didSet {
240+
if let guideView = textView.subviews.first(
241+
where: { $0 is ReformattingGuideView }
242+
) as? ReformattingGuideView {
243+
guideView.setColumn(reformatAtColumn)
244+
}
245+
}
246+
}
247+
248+
/// Whether to show the reformatting guide
249+
public var showReformattingGuide: Bool = false {
250+
didSet {
251+
if let guideView = self.guideView {
252+
guideView.setVisible(showReformattingGuide)
253+
guideView.updatePosition(in: textView)
254+
guideView.needsDisplay = true
255+
}
256+
}
257+
}
258+
259+
/// The reformatting guide view
260+
var guideView: ReformattingGuideView! {
261+
didSet {
262+
if let oldValue = oldValue {
263+
oldValue.removeFromSuperview()
264+
}
265+
}
266+
}
267+
236268
// MARK: Init
237269

238270
init(
@@ -257,7 +289,9 @@ public class TextViewController: NSViewController {
257289
bracketPairEmphasis: BracketPairEmphasis?,
258290
undoManager: CEUndoManager? = nil,
259291
coordinators: [TextViewCoordinator] = [],
260-
showMinimap: Bool
292+
showMinimap: Bool,
293+
reformatAtColumn: Int = 80,
294+
showReformattingGuide: Bool = false
261295
) {
262296
self.language = language
263297
self.font = font
@@ -278,6 +312,8 @@ public class TextViewController: NSViewController {
278312
self.bracketPairEmphasis = bracketPairEmphasis
279313
self._undoManager = undoManager
280314
self.showMinimap = showMinimap
315+
self.reformatAtColumn = reformatAtColumn
316+
self.showReformattingGuide = showReformattingGuide
281317

282318
super.init(nibName: nil, bundle: nil)
283319

@@ -306,6 +342,15 @@ public class TextViewController: NSViewController {
306342
delegate: self
307343
)
308344

345+
// Initialize guide view
346+
self.guideView = ReformattingGuideView(column: reformatAtColumn, isVisible: showReformattingGuide, theme: theme)
347+
if let guideView = self.guideView {
348+
guideView.wantsLayer = true
349+
guideView.layer?.zPosition = 1
350+
textView.addSubview(guideView)
351+
guideView.updatePosition(in: textView)
352+
}
353+
309354
coordinators.forEach {
310355
$0.prepareCoordinator(controller: self)
311356
}

0 commit comments

Comments
 (0)