@@ -91,6 +91,7 @@ class SelectionService: CustomDebugStringConvertible {
9191 public func startSelection ( row: Int , col: Int )
9292 {
9393 setSoftStart ( row: row, col: col)
94+ selectionMode = . character
9495 setActiveAndNotify ( )
9596 }
9697
@@ -118,6 +119,7 @@ class SelectionService: CustomDebugStringConvertible {
118119 {
119120 end = start
120121 selectingRows = false
122+ selectionMode = . character
121123 setActiveAndNotify ( )
122124 }
123125
@@ -178,23 +180,31 @@ class SelectionService: CustomDebugStringConvertible {
178180 * The bufferPosition is buffer-relative
179181 */
180182 public func shiftExtend ( bufferPosition newEnd: Position ) {
183+ var adjustedNewEnd = newEnd
184+
185+ // If we're in word selection mode, extend to word boundaries
186+ if selectionMode == . word {
187+ let direction = Position . compare ( newEnd, start) == . before ? - 1 : 1
188+ adjustedNewEnd = extendToWordBoundary ( position: newEnd, in: terminal. buffer, direction: direction)
189+ }
190+
181191 var shouldSwapStart = false
182192 if Position . compare ( start, end) == . before {
183193 // start is before end, is the new end before Start
184- if Position . compare ( newEnd , start) == . before {
194+ if Position . compare ( adjustedNewEnd , start) == . before {
185195 // yes, swap Start and End
186196 shouldSwapStart = true
187197 }
188198 } else if Position . compare ( start, end) == . after {
189- if Position . compare ( newEnd , start) == . after {
199+ if Position . compare ( adjustedNewEnd , start) == . after {
190200 // yes, swap Start and End
191201 shouldSwapStart = true
192202 }
193203 }
194204 if ( shouldSwapStart) {
195205 start = end
196206 }
197- end = newEnd
207+ end = adjustedNewEnd
198208
199209 setActiveAndNotify ( )
200210 }
@@ -221,12 +231,21 @@ class SelectionService: CustomDebugStringConvertible {
221231 guard let pivot = pivot else {
222232 return
223233 }
224- switch Position . compare ( bufferPosition, pivot) {
234+
235+ var adjustedPosition = bufferPosition
236+
237+ // If we're in word selection mode, extend to word boundaries
238+ if selectionMode == . word {
239+ let direction = Position . compare ( bufferPosition, pivot) == . before ? - 1 : 1
240+ adjustedPosition = extendToWordBoundary ( position: bufferPosition, in: terminal. buffer, direction: direction)
241+ }
242+
243+ switch Position . compare ( adjustedPosition, pivot) {
225244 case . after:
226245 start = pivot
227- end = bufferPosition
246+ end = adjustedPosition
228247 case . before:
229- start = bufferPosition
248+ start = adjustedPosition
230249 end = pivot
231250 case . equal:
232251 start = pivot
@@ -250,7 +269,15 @@ class SelectionService: CustomDebugStringConvertible {
250269 * The position is in buffer coordinates
251270 */
252271 public func dragExtend ( bufferPosition: Position ) {
253- end = bufferPosition
272+ var adjustedEnd = bufferPosition
273+
274+ // If we're in word selection mode, extend to word boundaries
275+ if selectionMode == . word {
276+ let direction = Position . compare ( bufferPosition, start) == . before ? - 1 : 1
277+ adjustedEnd = extendToWordBoundary ( position: bufferPosition, in: terminal. buffer, direction: direction)
278+ }
279+
280+ end = adjustedEnd
254281 setActiveAndNotify ( )
255282 }
256283
@@ -266,6 +293,15 @@ class SelectionService: CustomDebugStringConvertible {
266293
267294 public var selectingRows : Bool = false
268295
296+ /// Tracks the current selection mode to maintain consistency during extension
297+ public enum SelectionMode {
298+ case character
299+ case word
300+ case row
301+ }
302+
303+ public var selectionMode : SelectionMode = . character
304+
269305 /**
270306 * Selectss the specified row and triggers the selection
271307 */
@@ -274,6 +310,7 @@ class SelectionService: CustomDebugStringConvertible {
274310 start = Position ( col: 0 , row: row)
275311 end = Position ( col: terminal. cols- 1 , row: row)
276312 selectingRows = true
313+ selectionMode = . row
277314 setActiveAndNotify ( )
278315 }
279316
@@ -392,6 +429,52 @@ class SelectionService: CustomDebugStringConvertible {
392429 }
393430
394431 let nullChar = Character ( UnicodeScalar ( 0 ) )
432+
433+ /**
434+ * Extends a position to the nearest word boundary based on the character at that position
435+ */
436+ func extendToWordBoundary( position: Position , in buffer: Buffer , direction: Int ) -> Position {
437+ let ch = buffer. getChar ( atBufferRelative: position) . getCharacter ( )
438+ var includeFunc : ( Character ) -> Bool
439+
440+ switch ch {
441+ case Character ( UnicodeScalar ( 0 ) ) :
442+ includeFunc = { ch in ch == Character ( UnicodeScalar ( 0 ) ) }
443+ case " " :
444+ includeFunc = { ch in ch == " " }
445+ case let ch where ch. isLetter || ch. isNumber:
446+ includeFunc = { ch in ch. isLetter || ch. isNumber || ch == " . " || ch == " _ " || ch == " - " }
447+ default :
448+ return position
449+ }
450+
451+ var result = position
452+ if direction < 0 {
453+ // Extend backward
454+ var col = position. col
455+ while col >= 0 {
456+ let testCh = buffer. getChar ( atBufferRelative: Position ( col: col, row: position. row) ) . getCharacter ( )
457+ if !includeFunc( testCh) {
458+ break
459+ }
460+ result. col = col
461+ col -= 1
462+ }
463+ } else {
464+ // Extend forward
465+ var col = position. col
466+ while col < terminal. cols {
467+ let testCh = buffer. getChar ( atBufferRelative: Position ( col: col, row: position. row) ) . getCharacter ( )
468+ if !includeFunc( testCh) {
469+ break
470+ }
471+ col += 1
472+ result. col = col
473+ }
474+ }
475+
476+ return result
477+ }
395478 /**
396479 * Implements the behavior to select the word at the specified position or an expression
397480 * which is a balanced set parenthesis, braces or brackets
@@ -428,6 +511,7 @@ class SelectionService: CustomDebugStringConvertible {
428511 start = position
429512 end = position
430513 }
514+ selectionMode = . word
431515 setActiveAndNotify ( )
432516 }
433517
@@ -438,6 +522,7 @@ class SelectionService: CustomDebugStringConvertible {
438522 {
439523 if active {
440524 active = false
525+ selectionMode = . character
441526 }
442527 }
443528
0 commit comments