Skip to content

Commit e341e0b

Browse files
committed
Naming Consistency, Documentation
1 parent cb352fb commit e341e0b

File tree

11 files changed

+71
-41
lines changed

11 files changed

+71
-41
lines changed

Sources/CodeEditSourceEditor/Gutter/GutterView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,14 @@ public class GutterView: NSView {
101101
}
102102

103103
/// The view that draws the fold decoration in the gutter.
104-
var foldingRibbon: FoldingRibbonView
104+
var foldingRibbon: LineFoldRibbonView
105105

106106
/// Syntax helper for determining the required space for the folding ribbon.
107107
private var foldingRibbonWidth: CGFloat {
108108
if foldingRibbon.isHidden {
109109
0.0
110110
} else {
111-
FoldingRibbonView.width + foldingRibbonPadding
111+
LineFoldRibbonView.width + foldingRibbonPadding
112112
}
113113
}
114114

@@ -160,7 +160,7 @@ public class GutterView: NSView {
160160
self.textView = controller.textView
161161
self.delegate = delegate
162162

163-
foldingRibbon = FoldingRibbonView(controller: controller)
163+
foldingRibbon = LineFoldRibbonView(controller: controller)
164164

165165
super.init(frame: .zero)
166166
clipsToBounds = true

Sources/CodeEditSourceEditor/LineFolding/LineFoldProviders/LineFoldProvider.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import AppKit
99
import CodeEditTextView
1010

11+
/// Represents a fold's start or end.
1112
public enum LineFoldProviderLineInfo {
1213
case startFold(rangeStart: Int, newDepth: Int)
1314
case endFold(rangeEnd: Int, newDepth: Int)
@@ -31,6 +32,14 @@ public enum LineFoldProviderLineInfo {
3132
}
3233
}
3334

35+
/// ``LineFoldProvider`` is an interface used by the editor to find fold regions in a document.
36+
///
37+
/// The only required method, ``LineFoldProvider/foldLevelAtLine(lineNumber:lineRange:previousDepth:controller:)``,
38+
/// will be called very often. Return as fast as possible from this method, keeping in mind it is taking time on the
39+
/// main thread.
40+
///
41+
/// Ordering between calls is not guaranteed, the provider may restart at any time. The implementation should provide fold info
42+
/// for only the given lines.
3443
@MainActor
3544
public protocol LineFoldProvider: AnyObject {
3645
func foldLevelAtLine(
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// FoldRange.swift
3+
// CodeEditSourceEditor
4+
//
5+
// Created by Khan Winter on 6/26/25.
6+
//
7+
8+
/// Represents a single fold region with stable identifier and collapse state
9+
struct FoldRange: Sendable, Equatable {
10+
typealias FoldIdentifier = UInt32
11+
12+
let id: FoldIdentifier
13+
let depth: Int
14+
let range: Range<Int>
15+
var isCollapsed: Bool
16+
17+
func isHoveringEqual(_ other: FoldRange) -> Bool {
18+
depth == other.depth && range.contains(other.range)
19+
}
20+
}

Sources/CodeEditSourceEditor/LineFolding/Model/LineFoldCalculator.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
import AppKit
99
import CodeEditTextView
1010

11-
/// A utility that calculates foldable line ranges in a text document based on indentation depth.
11+
/// `LineFoldCalculator` receives text edits and rebuilds fold regions asynchronously.
1212
///
13-
/// `LineFoldCalculator` observes text edits and rebuilds fold regions asynchronously.
13+
/// This is an actor, all methods and modifications happen in isolation in it's async region. All text requests are
14+
/// marked `@MainActor` for safety.
1415
actor LineFoldCalculator {
1516
weak var foldProvider: LineFoldProvider?
1617
weak var controller: TextViewController?
@@ -26,10 +27,12 @@ actor LineFoldCalculator {
2627
/// - controller: The text controller to use for text and attachment fetching.
2728
/// - textChangedStream: A stream of text changes, received as the document is edited.
2829
init(
30+
foldProvider: LineFoldProvider,
2931
controller: TextViewController,
3032
textChangedStream: AsyncStream<Void>
3133
) {
32-
self.foldProvider = controller.foldProvider
34+
// This could be grabbed from the controller, but Swift 6 doesn't like that (concurrency safety)
35+
self.foldProvider = foldProvider
3336
self.controller = controller
3437
(valueStream, valueStreamContinuation) = AsyncStream<LineFoldStorage>.makeStream()
3538
Task { await listenToTextChanges(textChangedStream: textChangedStream) }
@@ -43,7 +46,7 @@ actor LineFoldCalculator {
4346
/// - Parameter textChangedStream: A stream of text changes.
4447
private func listenToTextChanges(textChangedStream: AsyncStream<Void>) {
4548
textChangedTask = Task {
46-
for await edit in textChangedStream {
49+
for await _ in textChangedStream {
4750
await buildFoldsForDocument()
4851
}
4952
}

Sources/CodeEditSourceEditor/LineFolding/Model/LineFoldingModel.swift renamed to Sources/CodeEditSourceEditor/LineFolding/Model/LineFoldModel.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// LineFoldingModel.swift
2+
// LineFoldModel.swift
33
// CodeEditSourceEditor
44
//
55
// Created by Khan Winter on 5/7/25.
@@ -9,16 +9,15 @@ import AppKit
99
import CodeEditTextView
1010
import Combine
1111

12-
/// # Basic Premise
12+
/// This object acts as the conductor between the line folding components.
1313
///
14-
/// We need to update, delete, or add fold ranges in the invalidated lines.
14+
/// This receives text changed events, and notifies the line fold calculator.
15+
/// It then receives fold calculation updates, and notifies the drawing view.
16+
/// It manages a cache of fold ranges for drawing.
1517
///
16-
/// # Implementation
17-
///
18-
/// - For each line in the document, put its indent level into a list.
19-
/// - Loop through the list, creating nested folds as indents go up and down.
20-
///
21-
class LineFoldingModel: NSObject, NSTextStorageDelegate, ObservableObject {
18+
/// For fold storage and querying, see ``LineFoldStorage``. For fold calculation see ``LineFoldCalculator``
19+
/// and ``LineFoldProvider``. For drawing see ``LineFoldRibbonView``.
20+
class LineFoldModel: NSObject, NSTextStorageDelegate, ObservableObject {
2221
static let emphasisId = "lineFolding"
2322

2423
/// An ordered tree of fold ranges in a document. Can be traversed using ``FoldRange/parent``
@@ -38,6 +37,7 @@ class LineFoldingModel: NSObject, NSTextStorageDelegate, ObservableObject {
3837
self.foldView = foldView
3938
(textChangedStream, textChangedStreamContinuation) = AsyncStream<Void>.makeStream()
4039
self.calculator = LineFoldCalculator(
40+
foldProvider: controller.foldProvider,
4141
controller: controller,
4242
textChangedStream: textChangedStream
4343
)
@@ -130,7 +130,7 @@ class LineFoldingModel: NSObject, NSTextStorageDelegate, ObservableObject {
130130

131131
// MARK: - LineFoldPlaceholderDelegate
132132

133-
extension LineFoldingModel: LineFoldPlaceholderDelegate {
133+
extension LineFoldModel: LineFoldPlaceholderDelegate {
134134
func placeholderBackgroundColor() -> NSColor {
135135
controller?.configuration.appearance.theme.invisibles.color ?? .lightGray
136136
}

Sources/CodeEditSourceEditor/LineFolding/Model/LineFoldStorage.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,6 @@
77
import _RopeModule
88
import Foundation
99

10-
/// Represents a single fold region with stable identifier and collapse state
11-
struct FoldRange: Sendable, Equatable {
12-
typealias FoldIdentifier = UInt32
13-
14-
let id: FoldIdentifier
15-
let depth: Int
16-
let range: Range<Int>
17-
var isCollapsed: Bool
18-
19-
func isHoveringEqual(_ other: FoldRange) -> Bool {
20-
depth == other.depth && range.contains(other.range)
21-
}
22-
}
23-
2410
/// Sendable data model for code folding using RangeStore
2511
struct LineFoldStorage: Sendable {
2612
/// A temporary fold representation without stable ID

Sources/CodeEditSourceEditor/LineFolding/Placeholder/LineFoldPlaceholder.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ protocol LineFoldPlaceholderDelegate: AnyObject {
1818
func placeholderDiscarded(fold: FoldRange)
1919
}
2020

21+
/// Used to display a folded region in a text document.
22+
///
23+
/// To stay up-to-date with the user's theme, it uses the ``LineFoldPlaceholderDelegate`` to query for current colors
24+
/// to use for drawing.
2125
class LineFoldPlaceholder: TextAttachment {
2226
let fold: FoldRange
2327
let charWidth: CGFloat

Sources/CodeEditSourceEditor/LineFolding/View/FoldingRibbonView+Draw.swift renamed to Sources/CodeEditSourceEditor/LineFolding/View/LineFoldRibbonView+Draw.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// FoldingRibbonView.swift
2+
// LineFoldRibbonView+Draw.swift
33
// CodeEditSourceEditor
44
//
55
// Created by Khan Winter on 5/8/25.
@@ -8,7 +8,7 @@
88
import AppKit
99
import CodeEditTextView
1010

11-
extension FoldingRibbonView {
11+
extension LineFoldRibbonView {
1212
struct DrawingFoldInfo {
1313
let fold: FoldRange
1414
let startLine: TextLineStorage<TextLine>.TextLinePosition
@@ -29,12 +29,14 @@ extension FoldingRibbonView {
2929
context.saveGState()
3030
context.clip(to: dirtyRect)
3131

32+
// Only draw folds in the requested dirty rect
3233
let folds = getDrawingFolds(
3334
forTextRange: rangeStart.range.location..<rangeEnd.range.upperBound,
3435
layoutManager: layoutManager
3536
)
3637
let foldCaps = FoldCapInfo(folds)
3738

39+
// Draw non-collapsed folds first
3840
for fold in folds.filter({ !$0.fold.isCollapsed }) {
3941
drawFoldMarker(
4042
fold,
@@ -44,6 +46,7 @@ extension FoldingRibbonView {
4446
)
4547
}
4648

49+
// Collapsed folds should *always* be on top of non-collapsed, so we draw them last.
4750
for fold in folds.filter({ $0.fold.isCollapsed }) {
4851
drawFoldMarker(
4952
fold,

Sources/CodeEditSourceEditor/LineFolding/View/FoldingRibbonView+FoldCapInfo.swift renamed to Sources/CodeEditSourceEditor/LineFolding/View/LineFoldRibbonView+FoldCapInfo.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
//
2-
// FoldCapInfo.swift
2+
// LineFoldRibbonView.swift
33
// CodeEditSourceEditor
44
//
55
// Created by Khan Winter on 6/3/25.
66
//
77

88
import AppKit
99

10-
extension FoldingRibbonView {
10+
extension LineFoldRibbonView {
1111
/// A helper type that determines if a fold should be drawn with a cap on the top or bottom if
1212
/// there's an adjacent fold on the same text line. It also provides a helper method to adjust fold rects using
1313
/// the cap information.

Sources/CodeEditSourceEditor/LineFolding/View/FoldingRibbonView.swift renamed to Sources/CodeEditSourceEditor/LineFolding/View/LineFoldRibbonView.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// FoldingRibbonView.swift
2+
// LineFoldRibbonView.swift
33
// CodeEditSourceEditor
44
//
55
// Created by Khan Winter on 5/6/25.
@@ -11,8 +11,11 @@ import CodeEditTextView
1111

1212
/// Displays the code folding ribbon in the ``GutterView``.
1313
///
14-
/// This view draws its contents
15-
class FoldingRibbonView: NSView {
14+
/// This view draws its contents manually. This was chosen over managing views on a per-fold basis, which would come
15+
/// with needing to manage view reuse and positioning. Drawing allows this view to draw only what macOS requests, and
16+
/// ends up being extremely efficient. This does mean that animations have to be done manually with a timer.
17+
/// Re: the `hoveredFold` property.
18+
class LineFoldRibbonView: NSView {
1619
struct HoverAnimationDetails: Equatable {
1720
var fold: FoldRange?
1821
var foldMask: CGPath?
@@ -28,7 +31,7 @@ class FoldingRibbonView: NSView {
2831

2932
static let width: CGFloat = 7.0
3033

31-
var model: LineFoldingModel?
34+
var model: LineFoldModel?
3235

3336
@Invalidating(.display)
3437
var hoveringFold: HoverAnimationDetails = .empty
@@ -80,7 +83,7 @@ class FoldingRibbonView: NSView {
8083
super.init(frame: .zero)
8184
layerContentsRedrawPolicy = .onSetNeedsDisplay
8285
clipsToBounds = false
83-
self.model = LineFoldingModel(
86+
self.model = LineFoldModel(
8487
controller: controller,
8588
foldView: self
8689
)
@@ -113,6 +116,8 @@ class FoldingRibbonView: NSView {
113116
self.mouseMoved(with: event)
114117
}
115118

119+
// MARK: - Mouse Events
120+
116121
override func mouseDown(with event: NSEvent) {
117122
let clickPoint = convert(event.locationInWindow, from: nil)
118123
guard let layoutManager = model?.controller?.textView.layoutManager,

0 commit comments

Comments
 (0)