Skip to content

Commit e5369da

Browse files
Remove Problematic CATransaction, Fix Lines Disappearing (#52)
Remove Problematic CATransaction, Assert Overlapping Layout
1 parent 6fa44d6 commit e5369da

File tree

4 files changed

+35
-25
lines changed

4 files changed

+35
-25
lines changed

Package.resolved

Lines changed: 8 additions & 17 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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ let package = Package(
1717
// Text mutation, storage helpers
1818
.package(
1919
url: "https://github.com/ChimeHQ/TextStory",
20-
from: "0.8.0"
20+
from: "0.9.0"
2121
),
2222
// Useful data structures
2323
.package(
@@ -27,7 +27,7 @@ let package = Package(
2727
// SwiftLint
2828
.package(
2929
url: "https://github.com/lukepistrol/SwiftLintPlugin",
30-
from: "0.2.2"
30+
from: "0.52.2"
3131
)
3232
],
3333
targets: [

Sources/CodeEditTextView/TextLayoutManager/TextLayoutManager.swift

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ public class TextLayoutManager: NSObject {
7979
public var isInTransaction: Bool {
8080
transactionCounter > 0
8181
}
82+
#if DEBUG
83+
/// Guard variable for an assertion check in debug builds.
84+
/// Ensures that layout calls are not overlapping, potentially causing layout issues.
85+
/// This is used over a lock, as locks in performant code such as this would be detrimental to performance.
86+
/// Also only included in debug builds. DO NOT USE for checking if layout is active or not. That is an anti-pattern.
87+
private var isInLayout: Bool = false
88+
#endif
8289

8390
weak var layoutView: NSView?
8491

@@ -188,15 +195,26 @@ public class TextLayoutManager: NSObject {
188195

189196
// MARK: - Layout
190197

198+
/// Asserts that the caller is not in an active layout pass.
199+
/// See docs on ``isInLayout`` for more details.
200+
private func assertNotInLayout() {
201+
#if DEBUG // This is redundant, but it keeps the flag debug-only too which helps prevent misuse.
202+
assert(!isInLayout, "layoutLines called while already in a layout pass. This is a programmer error.")
203+
#endif
204+
}
205+
191206
/// Lays out all visible lines
192207
func layoutLines(in rect: NSRect? = nil) { // swiftlint:disable:this function_body_length
208+
assertNotInLayout()
193209
guard layoutView?.superview != nil,
194210
let visibleRect = rect ?? delegate?.visibleRect,
195211
!isInTransaction,
196212
let textStorage else {
197213
return
198214
}
199-
CATransaction.begin()
215+
#if DEBUG
216+
isInLayout = true
217+
#endif
200218
let minY = max(visibleRect.minY - verticalLayoutPadding, 0)
201219
let maxY = max(visibleRect.maxY + verticalLayoutPadding, 0)
202220
let originalHeight = lineStorage.height
@@ -237,13 +255,11 @@ public class TextLayoutManager: NSObject {
237255
}
238256
} else {
239257
// Make sure the used fragment views aren't dequeued.
240-
usedFragmentIDs.formUnion(linePosition.data.typesetter.lineFragments.map(\.data.id))
258+
usedFragmentIDs.formUnion(linePosition.data.lineFragments.map(\.data.id))
241259
}
242260
newVisibleLines.insert(linePosition.data.id)
243261
}
244262

245-
CATransaction.commit()
246-
247263
// Enqueue any lines not used in this layout pass.
248264
viewReuseQueue.enqueueViews(notInSet: usedFragmentIDs)
249265

@@ -262,6 +278,9 @@ public class TextLayoutManager: NSObject {
262278
delegate?.layoutManagerYAdjustment(yContentAdjustment)
263279
}
264280

281+
#if DEBUG
282+
isInLayout = false
283+
#endif
265284
needsLayout = false
266285
}
267286

@@ -302,7 +321,7 @@ public class TextLayoutManager: NSObject {
302321
let relativeMinY = max(layoutData.minY - position.yPos, 0)
303322
let relativeMaxY = max(layoutData.maxY - position.yPos, relativeMinY)
304323

305-
for lineFragmentPosition in line.typesetter.lineFragments.linesStartingAt(
324+
for lineFragmentPosition in line.lineFragments.linesStartingAt(
306325
relativeMinY,
307326
until: relativeMaxY
308327
) {

Sources/CodeEditTextView/TextView/TextView+ReplaceCharacters.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ extension TextView {
3838
delegate?.textView(self, didReplaceContentsIn: range, with: string)
3939
}
4040

41-
layoutManager.endTransaction()
4241
textStorage.endEditing()
42+
layoutManager.endTransaction()
4343
selectionManager.notifyAfterEdit()
4444
NotificationCenter.default.post(name: Self.textDidChangeNotification, object: self)
4545
}

0 commit comments

Comments
 (0)