From 4a32acd182b7a8c23330d09ef4924923c646310f Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Mon, 21 Apr 2025 10:08:11 -0500 Subject: [PATCH 1/2] Fix Iterator Edge Case --- .../TextLineStorage+Iterator.swift | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Sources/CodeEditTextView/TextLineStorage/TextLineStorage+Iterator.swift b/Sources/CodeEditTextView/TextLineStorage/TextLineStorage+Iterator.swift index a7c4b7eb7..8136c1bca 100644 --- a/Sources/CodeEditTextView/TextLineStorage/TextLineStorage+Iterator.swift +++ b/Sources/CodeEditTextView/TextLineStorage/TextLineStorage+Iterator.swift @@ -7,11 +7,24 @@ import Foundation +/// # Dev Note +/// +/// For these iterators, prefer `.getLine(atIndex: )` for finding the next item in the iteration. +/// Using plain indexes instead of y positions or ranges has led to far fewer edge cases. public extension TextLineStorage { + /// Iterate over all lines overlapping a range of `y` positions. Positions in the middle of line contents will + /// return that line. + /// - Parameters: + /// - minY: The minimum y position to start at. + /// - maxY: The maximum y position to stop at. + /// - Returns: A lazy iterator for retrieving lines. func linesStartingAt(_ minY: CGFloat, until maxY: CGFloat) -> TextLineStorageYIterator { TextLineStorageYIterator(storage: self, minY: minY, maxY: maxY) } - + + /// Iterate over all lines overlapping a range in the document. + /// - Parameter range: The range to query. + /// - Returns: A lazy iterator for retrieving lines. func linesInRange(_ range: NSRange) -> TextLineStorageRangeIterator { TextLineStorageRangeIterator(storage: self, range: range) } @@ -36,7 +49,7 @@ public extension TextLineStorage { return nil } self.currentPosition = nextPosition - return self.currentPosition! + return nextPosition } else if let nextPosition = storage.getLine(atPosition: minY) { self.currentPosition = nextPosition return nextPosition @@ -60,11 +73,11 @@ public extension TextLineStorage { public mutating func next() -> TextLinePosition? { if let currentPosition { guard currentPosition.range.max < range.max, - let nextPosition = storage.getLine(atOffset: currentPosition.range.max) else { + let nextPosition = storage.getLine(atIndex: currentPosition.index + 1) else { return nil } self.currentPosition = nextPosition - return self.currentPosition! + return nextPosition } else if let nextPosition = storage.getLine(atOffset: range.location) { self.currentPosition = nextPosition return nextPosition @@ -92,11 +105,11 @@ extension TextLineStorage: LazySequenceProtocol { public mutating func next() -> TextLinePosition? { if let currentPosition { guard currentPosition.range.max < storage.length, - let nextPosition = storage.getLine(atOffset: currentPosition.range.max) else { + let nextPosition = storage.getLine(atIndex: currentPosition.index + 1) else { return nil } self.currentPosition = nextPosition - return self.currentPosition! + return nextPosition } else if let nextPosition = storage.getLine(atOffset: 0) { self.currentPosition = nextPosition return nextPosition From 258e664a766c1c135b96be7e63d3809726c1c72d Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Mon, 21 Apr 2025 10:10:55 -0500 Subject: [PATCH 2/2] Lint :( --- .../TextLineStorage/TextLineStorage+Iterator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CodeEditTextView/TextLineStorage/TextLineStorage+Iterator.swift b/Sources/CodeEditTextView/TextLineStorage/TextLineStorage+Iterator.swift index 8136c1bca..c79d4adcb 100644 --- a/Sources/CodeEditTextView/TextLineStorage/TextLineStorage+Iterator.swift +++ b/Sources/CodeEditTextView/TextLineStorage/TextLineStorage+Iterator.swift @@ -21,7 +21,7 @@ public extension TextLineStorage { func linesStartingAt(_ minY: CGFloat, until maxY: CGFloat) -> TextLineStorageYIterator { TextLineStorageYIterator(storage: self, minY: minY, maxY: maxY) } - + /// Iterate over all lines overlapping a range in the document. /// - Parameter range: The range to query. /// - Returns: A lazy iterator for retrieving lines.