@@ -212,6 +212,43 @@ export class WordOperations {
212212 return new Position ( lineNumber , prevWordOnLine ? prevWordOnLine . end + 1 : 1 ) ;
213213 }
214214
215+ public static _moveWordPartLeft ( model : ICursorSimpleModel , position : Position ) : Position {
216+ const lineNumber = position . lineNumber ;
217+ const maxColumn = model . getLineMaxColumn ( lineNumber ) ;
218+
219+ if ( position . column === 1 ) {
220+ return ( lineNumber > 1 ? new Position ( lineNumber - 1 , model . getLineMaxColumn ( lineNumber - 1 ) ) : position ) ;
221+ }
222+
223+ const lineContent = model . getLineContent ( lineNumber ) ;
224+ for ( let column = position . column - 1 ; column > 1 ; column -- ) {
225+ const left = lineContent . charCodeAt ( column - 2 ) ;
226+ const right = lineContent . charCodeAt ( column - 1 ) ;
227+
228+ if ( left !== CharCode . Underline && right === CharCode . Underline ) {
229+ // snake_case_variables
230+ return new Position ( lineNumber , column ) ;
231+ }
232+
233+ if ( strings . isLowerAsciiLetter ( left ) && strings . isUpperAsciiLetter ( right ) ) {
234+ // camelCaseVariables
235+ return new Position ( lineNumber , column ) ;
236+ }
237+
238+ if ( strings . isUpperAsciiLetter ( left ) && strings . isUpperAsciiLetter ( right ) ) {
239+ // thisIsACamelCaseWithOneLetterWords
240+ if ( column + 1 < maxColumn ) {
241+ const rightRight = lineContent . charCodeAt ( column ) ;
242+ if ( strings . isLowerAsciiLetter ( rightRight ) ) {
243+ return new Position ( lineNumber , column ) ;
244+ }
245+ }
246+ }
247+ }
248+
249+ return new Position ( lineNumber , 1 ) ;
250+ }
251+
215252 public static moveWordRight ( wordSeparators : WordCharacterClassifier , model : ICursorSimpleModel , position : Position , wordNavigationType : WordNavigationType ) : Position {
216253 let lineNumber = position . lineNumber ;
217254 let column = position . column ;
@@ -251,6 +288,43 @@ export class WordOperations {
251288 return new Position ( lineNumber , column ) ;
252289 }
253290
291+ public static _moveWordPartRight ( model : ICursorSimpleModel , position : Position ) : Position {
292+ const lineNumber = position . lineNumber ;
293+ const maxColumn = model . getLineMaxColumn ( lineNumber ) ;
294+
295+ if ( position . column === maxColumn ) {
296+ return ( lineNumber < model . getLineCount ( ) ? new Position ( lineNumber + 1 , 1 ) : position ) ;
297+ }
298+
299+ const lineContent = model . getLineContent ( lineNumber ) ;
300+ for ( let column = position . column + 1 ; column < maxColumn ; column ++ ) {
301+ const left = lineContent . charCodeAt ( column - 2 ) ;
302+ const right = lineContent . charCodeAt ( column - 1 ) ;
303+
304+ if ( left === CharCode . Underline && right !== CharCode . Underline ) {
305+ // snake_case_variables
306+ return new Position ( lineNumber , column ) ;
307+ }
308+
309+ if ( strings . isLowerAsciiLetter ( left ) && strings . isUpperAsciiLetter ( right ) ) {
310+ // camelCaseVariables
311+ return new Position ( lineNumber , column ) ;
312+ }
313+
314+ if ( strings . isUpperAsciiLetter ( left ) && strings . isUpperAsciiLetter ( right ) ) {
315+ // thisIsACamelCaseWithOneLetterWords
316+ if ( column + 1 < maxColumn ) {
317+ const rightRight = lineContent . charCodeAt ( column ) ;
318+ if ( strings . isLowerAsciiLetter ( rightRight ) ) {
319+ return new Position ( lineNumber , column ) ;
320+ }
321+ }
322+ }
323+ }
324+
325+ return new Position ( lineNumber , maxColumn ) ;
326+ }
327+
254328 protected static _deleteWordLeftWhitespace ( model : ICursorSimpleModel , position : Position ) : Range {
255329 const lineContent = model . getLineContent ( position . lineNumber ) ;
256330 const startIndex = position . column - 2 ;
@@ -315,6 +389,16 @@ export class WordOperations {
315389 return new Range ( lineNumber , column , position . lineNumber , position . column ) ;
316390 }
317391
392+ public static _deleteWordPartLeft ( model : ICursorSimpleModel , selection : Selection ) : Range {
393+ if ( ! selection . isEmpty ( ) ) {
394+ return selection ;
395+ }
396+
397+ const pos = selection . getPosition ( ) ;
398+ const toPosition = WordOperations . _moveWordPartLeft ( model , pos ) ;
399+ return new Range ( pos . lineNumber , pos . column , toPosition . lineNumber , toPosition . column ) ;
400+ }
401+
318402 private static _findFirstNonWhitespaceChar ( str : string , startIndex : number ) : number {
319403 let len = str . length ;
320404 for ( let chIndex = startIndex ; chIndex < len ; chIndex ++ ) {
@@ -403,6 +487,16 @@ export class WordOperations {
403487 return new Range ( lineNumber , column , position . lineNumber , position . column ) ;
404488 }
405489
490+ public static _deleteWordPartRight ( model : ICursorSimpleModel , selection : Selection ) : Range {
491+ if ( ! selection . isEmpty ( ) ) {
492+ return selection ;
493+ }
494+
495+ const pos = selection . getPosition ( ) ;
496+ const toPosition = WordOperations . _moveWordPartRight ( model , pos ) ;
497+ return new Range ( pos . lineNumber , pos . column , toPosition . lineNumber , toPosition . column ) ;
498+ }
499+
406500 public static word ( config : CursorConfiguration , model : ICursorSimpleModel , cursor : SingleCursorState , inSelectionMode : boolean , position : Position ) : SingleCursorState {
407501 const wordSeparators = getMapForWordSeparators ( config . wordSeparators ) ;
408502 let prevWord = WordOperations . _findPreviousWordOnLine ( wordSeparators , model , position ) ;
@@ -480,175 +574,44 @@ export class WordOperations {
480574 }
481575}
482576
483- export function _lastWordPartEnd ( str : string , startIndex : number = str . length - 1 ) : number {
484- let ignoreUpperCase = ! strings . isLowerAsciiLetter ( str . charCodeAt ( startIndex + 1 ) ) ;
485- for ( let i = startIndex ; i >= 0 ; i -- ) {
486- const chCode = str . charCodeAt ( i ) ;
487- if ( chCode === CharCode . Space || chCode === CharCode . Tab ) {
488- if ( i === 0 ) {
489- return 0 ;
490- }
491- const prevChCode = str . charCodeAt ( i - 1 ) ;
492- if ( prevChCode !== CharCode . Space && prevChCode !== CharCode . Tab ) {
493- return i - 1 ;
494- }
495- }
496- if ( ! ignoreUpperCase && strings . isUpperAsciiLetter ( chCode ) ) {
497- return i - 1 ;
498- }
499- if ( ignoreUpperCase && i < startIndex && strings . isLowerAsciiLetter ( chCode ) ) {
500- return i ;
501- }
502- if ( chCode === CharCode . Underline ) {
503- if ( i === 0 ) {
504- return 0 ;
505- }
506- const prevChCode = str . charCodeAt ( i - 1 ) ;
507- if ( prevChCode !== CharCode . Underline ) {
508- return i - 1 ;
509- }
510- }
511- ignoreUpperCase = ignoreUpperCase && strings . isUpperAsciiLetter ( chCode ) ;
512- }
513- return - 1 ;
514- }
515-
516- export function _nextWordPartBegin ( str : string , startIndex : number = 0 ) : number {
517- let prevChCode = str . charCodeAt ( startIndex - 1 ) ;
518- let chCode = str . charCodeAt ( startIndex ) ;
519- // handle the special case ' X' and ' x' which is different from the standard methods
520- if ( ( prevChCode === CharCode . Space || prevChCode === CharCode . Tab ) && ( strings . isLowerAsciiLetter ( chCode ) || strings . isUpperAsciiLetter ( chCode ) ) ) {
521- return startIndex + 1 ;
522- }
523- let ignoreUpperCase = strings . isUpperAsciiLetter ( chCode ) ;
524- for ( let i = startIndex ; i < str . length ; ++ i ) {
525- chCode = str . charCodeAt ( i ) ;
526- if ( chCode === CharCode . Space || chCode === CharCode . Tab ) {
527- if ( i + 1 >= str . length ) {
528- return i + 1 ;
529- }
530- const nextChCode = str . charCodeAt ( i + 1 ) ;
531- if ( nextChCode !== CharCode . Space && nextChCode !== CharCode . Tab ) {
532- return i + 2 ;
533- }
534- }
535- if ( ! ignoreUpperCase && strings . isUpperAsciiLetter ( chCode ) ) {
536- return i + 1 ;
537- }
538- if ( ignoreUpperCase && strings . isLowerAsciiLetter ( chCode ) ) {
539- return i ; // multiple UPPERCase : assume an upper case word and a CamelCase word - like DSLModel
540- }
541- ignoreUpperCase = ignoreUpperCase && strings . isUpperAsciiLetter ( chCode ) ;
542- if ( chCode === CharCode . Underline ) {
543- if ( i + 1 >= str . length ) {
544- return i + 1 ;
545- }
546- const nextChCode = str . charCodeAt ( i + 1 ) ;
547- if ( nextChCode !== CharCode . Underline ) {
548- return i + 2 ;
549- }
550- }
551- }
552- return str . length + 1 ;
553- }
554-
555577export class WordPartOperations extends WordOperations {
556- public static deleteWordPartLeft ( wordSeparators : WordCharacterClassifier , model : ICursorSimpleModel , selection : Selection , whitespaceHeuristics : boolean , wordNavigationType : WordNavigationType ) : Range {
557- if ( ! selection . isEmpty ( ) ) {
558- return selection ;
559- }
560-
561- const position = new Position ( selection . positionLineNumber , selection . positionColumn ) ;
562- const lineNumber = position . lineNumber ;
563- const column = position . column ;
564-
565- if ( lineNumber === 1 && column === 1 ) {
566- // Ignore deleting at beginning of file
567- return null ;
568- }
569-
570- if ( whitespaceHeuristics ) {
571- let r = WordOperations . _deleteWordLeftWhitespace ( model , position ) ;
572- if ( r ) {
573- return r ;
574- }
575- }
576-
577- const wordRange = WordOperations . deleteWordLeft ( wordSeparators , model , selection , whitespaceHeuristics , wordNavigationType ) ;
578- const lastWordPartEnd = _lastWordPartEnd ( model . getLineContent ( position . lineNumber ) , position . column - 2 ) ;
579- const wordPartRange = new Range ( lineNumber , column , lineNumber , lastWordPartEnd + 2 ) ;
580-
581- if ( wordPartRange . getStartPosition ( ) . isBeforeOrEqual ( wordRange . getStartPosition ( ) ) ) {
582- return wordRange ;
583- }
584- return wordPartRange ;
578+ public static deleteWordPartLeft ( wordSeparators : WordCharacterClassifier , model : ICursorSimpleModel , selection : Selection , whitespaceHeuristics : boolean ) : Range {
579+ const candidates = [
580+ WordOperations . deleteWordLeft ( wordSeparators , model , selection , whitespaceHeuristics , WordNavigationType . WordStart ) ,
581+ WordOperations . deleteWordLeft ( wordSeparators , model , selection , whitespaceHeuristics , WordNavigationType . WordEnd ) ,
582+ WordOperations . _deleteWordPartLeft ( model , selection )
583+ ] ;
584+ candidates . sort ( Range . compareRangesUsingEnds ) ;
585+ return candidates [ 2 ] ;
585586 }
586587
587- public static deleteWordPartRight ( wordSeparators : WordCharacterClassifier , model : ICursorSimpleModel , selection : Selection , whitespaceHeuristics : boolean , wordNavigationType : WordNavigationType ) : Range {
588- if ( ! selection . isEmpty ( ) ) {
589- return selection ;
590- }
591-
592- const position = new Position ( selection . positionLineNumber , selection . positionColumn ) ;
593- const lineNumber = position . lineNumber ;
594- const column = position . column ;
595-
596- const lineCount = model . getLineCount ( ) ;
597- const maxColumn = model . getLineMaxColumn ( lineNumber ) ;
598- if ( lineNumber === lineCount && column === maxColumn ) {
599- // Ignore deleting at end of file
600- return null ;
601- }
602-
603- if ( whitespaceHeuristics ) {
604- let r = WordOperations . _deleteWordRightWhitespace ( model , position ) ;
605- if ( r ) {
606- return r ;
607- }
608- }
609-
610- const wordRange = WordOperations . deleteWordRight ( wordSeparators , model , selection , whitespaceHeuristics , wordNavigationType ) ;
611- const nextWordPartBegin = _nextWordPartBegin ( model . getLineContent ( position . lineNumber ) , position . column ) ;
612- const wordPartRange = new Range ( lineNumber , column , lineNumber , nextWordPartBegin ) ;
613-
614- if ( wordRange . getEndPosition ( ) . isBeforeOrEqual ( wordPartRange . getEndPosition ( ) ) ) {
615- return wordRange ;
616- }
617- return wordPartRange ;
588+ public static deleteWordPartRight ( wordSeparators : WordCharacterClassifier , model : ICursorSimpleModel , selection : Selection , whitespaceHeuristics : boolean ) : Range {
589+ const candidates = [
590+ WordOperations . deleteWordRight ( wordSeparators , model , selection , whitespaceHeuristics , WordNavigationType . WordStart ) ,
591+ WordOperations . deleteWordRight ( wordSeparators , model , selection , whitespaceHeuristics , WordNavigationType . WordEnd ) ,
592+ WordOperations . _deleteWordPartRight ( model , selection )
593+ ] ;
594+ candidates . sort ( Range . compareRangesUsingStarts ) ;
595+ return candidates [ 0 ] ;
618596 }
619597
620- public static moveWordPartLeft ( wordSeparators : WordCharacterClassifier , model : ICursorSimpleModel , position : Position , wordNavigationType : WordNavigationType ) : Position {
621- const lineNumber = position . lineNumber ;
622- const column = position . column ;
623- if ( column === 1 ) {
624- return ( lineNumber > 1 ? new Position ( lineNumber - 1 , model . getLineMaxColumn ( lineNumber - 1 ) ) : position ) ;
625- }
626-
627- const wordPos = WordOperations . moveWordLeft ( wordSeparators , model , position , wordNavigationType ) ;
628- const lastWordPartEnd = _lastWordPartEnd ( model . getLineContent ( lineNumber ) , column - 2 ) ;
629- const wordPartPos = new Position ( lineNumber , lastWordPartEnd + 2 ) ;
630-
631- if ( wordPartPos . isBeforeOrEqual ( wordPos ) ) {
632- return wordPos ;
633- }
634- return wordPartPos ;
598+ public static moveWordPartLeft ( wordSeparators : WordCharacterClassifier , model : ICursorSimpleModel , position : Position ) : Position {
599+ const candidates = [
600+ WordOperations . moveWordLeft ( wordSeparators , model , position , WordNavigationType . WordStart ) ,
601+ WordOperations . moveWordLeft ( wordSeparators , model , position , WordNavigationType . WordEnd ) ,
602+ WordOperations . _moveWordPartLeft ( model , position )
603+ ] ;
604+ candidates . sort ( Position . compare ) ;
605+ return candidates [ 2 ] ;
635606 }
636607
637- public static moveWordPartRight ( wordSeparators : WordCharacterClassifier , model : ICursorSimpleModel , position : Position , wordNavigationType : WordNavigationType ) : Position {
638- const lineNumber = position . lineNumber ;
639- const column = position . column ;
640- const maxColumn = model . getLineMaxColumn ( lineNumber ) ;
641- if ( column === maxColumn ) {
642- return ( lineNumber < model . getLineCount ( ) ? new Position ( lineNumber + 1 , 1 ) : position ) ;
643- }
644-
645- const wordPos = WordOperations . moveWordRight ( wordSeparators , model , position , wordNavigationType ) ;
646- const nextWordPartBegin = _nextWordPartBegin ( model . getLineContent ( lineNumber ) , column ) ;
647- const wordPartPos = new Position ( lineNumber , nextWordPartBegin ) ;
648-
649- if ( wordPos . isBeforeOrEqual ( wordPartPos ) ) {
650- return wordPos ;
651- }
652- return wordPartPos ;
608+ public static moveWordPartRight ( wordSeparators : WordCharacterClassifier , model : ICursorSimpleModel , position : Position ) : Position {
609+ const candidates = [
610+ WordOperations . moveWordRight ( wordSeparators , model , position , WordNavigationType . WordStart ) ,
611+ WordOperations . moveWordRight ( wordSeparators , model , position , WordNavigationType . WordEnd ) ,
612+ WordOperations . _moveWordPartRight ( model , position )
613+ ] ;
614+ candidates . sort ( Position . compare ) ;
615+ return candidates [ 0 ] ;
653616 }
654617}
0 commit comments