@@ -67,7 +67,7 @@ extension TextViewController {
6767 var selectionIndex = 0
6868 textView. editSelections { textView, selection in
6969 // get lineindex, i.e line-numbers+1
70- guard let lineIndexes = getHighlightedLines ( for: selection. range) else { return }
70+ guard let lineIndexes = getOverlappingLines ( for: selection. range) else { return }
7171
7272 adjustIndentation ( lineIndexes: lineIndexes, inwards: inwards)
7373
@@ -128,8 +128,25 @@ extension TextViewController {
128128 }
129129 return false
130130 }
131-
132- private func getHighlightedLines( for range: NSRange ) -> ClosedRange < Int > ? {
131+
132+ /// Find the range of lines overlapping a text range.
133+ ///
134+ /// Use this method to determine what lines to apply a text transformation on using a text selection. For instance,
135+ /// when indenting a selected line.
136+ ///
137+ /// Does not determine the *visible* lines, which is a very slight change from most
138+ /// ``CodeEditTextView/TextLayoutManager`` APIs.
139+ /// Given the text:
140+ /// ```
141+ /// A
142+ /// B
143+ /// ```
144+ /// This method will return lines `0...0` for the text range `0..<2`. The layout manager might return lines
145+ /// `0...1`, as the text range contains the newline, which appears *visually* in line index `1`.
146+ ///
147+ /// - Parameter range: The text range in the document to find contained lines for.
148+ /// - Returns: A closed range of line indexes (0-indexed) where each line is overlapping the given text range.
149+ func getOverlappingLines( for range: NSRange ) -> ClosedRange < Int > ? {
133150 guard let startLineInfo = textView. layoutManager. textLineForOffset ( range. lowerBound) else {
134151 return nil
135152 }
@@ -139,7 +156,16 @@ extension TextViewController {
139156 return startLineInfo. index... startLineInfo. index
140157 }
141158
142- return startLineInfo. index... endLineInfo. index
159+ // If we've selected up to the start of a line (just over the newline character), the layout manager tells us
160+ // we've selected the next line. However, we aren't overlapping the *text line* with that range, so we
161+ // decrement it if it's not the end of the document
162+ var endLineIndex = endLineInfo. index
163+ if endLineInfo. range. lowerBound == range. upperBound
164+ && endLineInfo. index != textView. layoutManager. lineCount - 1 {
165+ endLineIndex -= 1
166+ }
167+
168+ return startLineInfo. index... endLineIndex
143169 }
144170
145171 private func adjustIndentation( lineIndexes: ClosedRange < Int > , inwards: Bool ) {
0 commit comments