Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,16 @@ public extension TextLineStorage {

public mutating func next() -> TextLinePosition? {
if let currentPosition {
guard currentPosition.range.max < range.max,
let nextPosition = storage.getLine(atIndex: currentPosition.index + 1) else {
guard let nextPosition = storage.getLine(atIndex: currentPosition.index + 1) else {
return nil
}
// An empty trailing line (length 0) sits at the same offset as the previous
// line's `range.max`. Without this exception, iterating to the end of the
// document would skip it — see issue #121 (Cmd+A highlight missing the last
// empty line on documents ending with a newline).
let isTrailingEmptyLineAtRangeEnd = nextPosition.range.length == 0
&& nextPosition.range.location == range.max
guard currentPosition.range.max < range.max || isTrailingEmptyLineAtRangeEnd else {
return nil
}
self.currentPosition = nextPosition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,23 @@ struct TextLayoutManagerTests {
}
}

/// Documents that end with a line ending get an empty trailing line in storage. Iterating the
/// document range must include it, otherwise `Cmd+A` selects the line but never highlights it.
/// Regression test for https://github.com/CodeEditApp/CodeEditTextView/issues/121.
@Test
func rangeIteratorIncludesTrailingEmptyLine() {
textStorage.mutableString.setString("a\nb\nc\n")
layoutManager.layoutLines(in: NSRect(x: 0, y: 0, width: 1000, height: 1000))

let docRange = NSRange(location: 0, length: textStorage.length)
let iterated = Array(layoutManager.lineStorage.linesInRange(docRange))

#expect(layoutManager.lineStorage.count == 4, "Storage should hold 3 lines + 1 trailing empty line")
#expect(iterated.count == layoutManager.lineStorage.count, "Trailing empty line was skipped")
#expect(iterated.last?.range.length == 0)
#expect(iterated.last?.range.location == textStorage.length)
}

@Test
func afterLayoutDoesntNeedLayout() {
layoutManager.layoutLines(in: NSRect(x: 0, y: 0, width: 1000, height: 1000))
Expand Down