CleanCocoa / CleanCocoa/DeclarativeTextKit
Skip evaluation of a Modifying block iff it doesn't do anything
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 51
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
The Modifying(<Range>) { <Block> } construct always evaluates even if the block is empty.
With TextKit integration, this means
- an undo group is being started (and ended)
NSTextView.shouldChangeText(in:replacementString:)anddidChangeText()are being run to guard against unwanted changes
With syntax highlighting in the text storage, you may end up processing the text for what's essentially a no-op.
How to test
To get an empty block, use a for-loop to trigger the buildArray path of the result builder, but without any actual iterations:
Modifying(selectedRange) { _ in
for _ in 0 ..< 0 {
Insert(0) { "loop never runs" }
}
}
I'm not sure whether we can figure out at all whether a result builder produces nothing (i.e. empty array).
Complete test case
This test fails with a thrown error at try buffer.evaluate because the text view doesn't permit changes in the range.
This should not be a problem, because the range is not actually changed.
func testModifying_EmptyLoopBlock_SkipsEvaluation() throws {
class TextViewSpy: NSTextView {
var didCallShouldChangeText = false
var didCallDidChangeText = false
override func shouldChangeText(in affectedCharRange: NSRange, replacementString: String?) -> Bool {
didCallShouldChangeText = true
return false // Would abort modification an error
}
override func didChangeText() {
didCallDidChangeText = true
}
}
let textViewSpy = TextViewSpy()
textViewSpy.string = "Lorem ipsum."
let buffer = NSTextViewBuffer(textView: textViewSpy)
let selectedRange: SelectedRange = .init(location: 6, length: 5)
assertBufferState(buffer, "Lorem ipsum.ˇ")
try buffer.evaluate {
Modifying(selectedRange) { _ in
for _ in 0 ..< 0 {
Insert(0) { "loop never runs" }
}
}
}
XCTAssertFalse(textViewSpy.didCallShouldChangeText)
XCTAssertFalse(textViewSpy.didCallDidChangeText)
}
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the Modifying construct and the buffer.evaluate entry point, then inspect how an empty result-builder block is represented for the buildArray path. Reproduce the issue with testModifying_EmptyLoopBlock_SkipsEvaluation and the provided TextViewSpy. Done means the empty loop block skips evaluation and neither text-change callback is called.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100