@@ -36,8 +36,8 @@ package extension TextSelectionManager {
3636 return extendSelectionVerticalCharacter ( from: offset, up: up, suggestedXPos: suggestedXPos)
3737 case . word, . line, . visualLine:
3838 return extendSelectionVerticalLine ( from: offset, up: up)
39- case . container :
40- return extendSelectionContainer ( from: offset, delta: up ? 1 : - 1 )
39+ case . page :
40+ return extendSelectionPage ( from: offset, delta: up ? 1 : - 1 , suggestedXPos : suggestedXPos )
4141 case . document:
4242 if up {
4343 return NSRange ( location: 0 , length: offset)
@@ -61,7 +61,7 @@ package extension TextSelectionManager {
6161 guard let point = layoutManager? . rectForOffset ( offset) ? . origin,
6262 let newOffset = layoutManager? . textOffsetAtPoint (
6363 CGPoint (
64- x: suggestedXPos == nil ? point. x : suggestedXPos! ,
64+ x: suggestedXPos ?? point. x,
6565 y: point. y - ( layoutManager? . estimateLineHeight ( ) ?? 2.0 ) / 2 * ( up ? 1 : - 3 )
6666 )
6767 ) else {
@@ -115,22 +115,36 @@ package extension TextSelectionManager {
115115 }
116116 }
117117
118- /// Extends a selection one "container " long.
118+ /// Extends a selection one "page " long.
119119 /// - Parameters:
120120 /// - offset: The location to start extending the selection from.
121121 /// - delta: The direction the selection should be extended. `1` for forwards, `-1` for backwards.
122122 /// - Returns: The range of the extended selection.
123- private func extendSelectionContainer( from offset: Int , delta: Int ) -> NSRange {
124- guard let textView, let endOffset = layoutManager? . textOffsetAtPoint (
125- CGPoint (
126- x: delta > 0 ? textView. frame. maxX : textView. frame. minX,
127- y: delta > 0 ? textView. frame. maxY : textView. frame. minY
128- )
129- ) else {
123+ private func extendSelectionPage( from offset: Int , delta: Int , suggestedXPos: CGFloat ? ) -> NSRange {
124+ guard let textView = textView,
125+ let layoutManager,
126+ let currentYPos = layoutManager. rectForOffset ( offset) ? . origin. y else {
130127 return NSRange ( location: offset, length: 0 )
131128 }
132- return endOffset > offset
133- ? NSRange ( location: offset, length: endOffset - offset)
134- : NSRange ( location: endOffset, length: offset - endOffset)
129+
130+ let pageHeight = textView. visibleRect. height
131+
132+ // Grab the line where the next selection should be. Then use the suggestedXPos to find where in the line the
133+ // selection should be extended to.
134+ layoutManager. layoutLines (
135+ in: NSRect ( x: 0 , y: currentYPos, width: layoutManager. maxLineWidth, height: pageHeight)
136+ )
137+ guard let nextPageOffset = layoutManager. textOffsetAtPoint ( CGPoint (
138+ x: suggestedXPos ?? 0 ,
139+ y: min ( textView. frame. height, max ( 0 , currentYPos + ( delta > 0 ? - pageHeight : pageHeight) ) )
140+ ) ) else {
141+ return NSRange ( location: offset, length: 0 )
142+ }
143+
144+ if delta > 0 {
145+ return NSRange ( location: nextPageOffset, length: offset - nextPageOffset)
146+ } else {
147+ return NSRange ( location: offset, length: nextPageOffset - offset)
148+ }
135149 }
136150}
0 commit comments