'use strict' ; import * as vscode from 'vscode' ; import * as xregexp from 'xregexp' ; export class CamelComponent { private _text = "" ; private static _letterRegexp = new xregexp ( '\\pL+' ) ; public get text ( ) : string { return this . _text ; } public get isSpaceComponent ( ) { if ( this . text . length == 0 ) return false ; let areAllCharactersSpace = true ; for ( var index = 0 ; index < this . text . length ; index ++ ) { var element = this . text [ index ] ; if ( element != ' ' && element != '\t' ) { areAllCharactersSpace = false ; break ; } } return areAllCharactersSpace ; } public get isSymbolComponent ( ) { if ( this . text . length == 0 ) return false ; let areAllCharactersSymbols = true ; for ( var index = 0 ; index < this . text . length ; index ++ ) { var element = this . text [ index ] ; if ( CamelComponent . isSpace ( element ) || CamelComponent . isLetterOrNumber ( element ) ) { areAllCharactersSymbols = false ; break ; } } return areAllCharactersSymbols ; } public static isLetterOrNumber ( char ) { return CamelComponent . isLetter ( char ) || CamelComponent . isNumber ( char ) ; } public static isLetter ( char ) { return CamelComponent . _letterRegexp . test ( char ) ; } public static isSymbol ( char ) { return ! /[0-9a-zA-Z\u00C0-\u017F]+/ . test ( char ) ; } public static isNumber ( char ) { return /[0-9]+/ . test ( char ) ; } public static isUpperCase ( char ) { var isLetter = CamelComponent . isLetter ( char ) ; return isLetter && char . toUpperCase ( ) == char ; } public static isLowerFase ( char ) { var isLetter = CamelComponent . isLetter ( char ) ; return isLetter && char . toLowerCase ( ) == char ; } public static isSpace ( char ) { var retva = /[\s\t]+/ . test ( char ) ; return retva ; } public get isLettersAndNumbersOnly ( ) { if ( this . text . length == 0 ) return false ; let areAllCharsLettersOrDigits = true ; for ( var index = 0 ; index < this . text . length ; index ++ ) { var element = this . text [ index ] ; if ( ! CamelComponent . isLetterOrNumber ( element ) ) { areAllCharsLettersOrDigits = false ; break ; } } return areAllCharsLettersOrDigits ; } public get isWordComponent ( ) { if ( this . text . length == 0 ) return false ; let areAllCharsLettersOrDigits = true ; for ( var index = 0 ; index < this . text . length ; index ++ ) { var element = this . text [ index ] ; if ( ! CamelComponent . isLetterOrNumber ( element ) ) { areAllCharsLettersOrDigits = false ; break ; } } if ( this . text . length == 1 ) return areAllCharsLettersOrDigits ; let remainder = this . text . substr ( 1 ) ; let isFirstLetterUpperCase = CamelComponent . isUpperCase ( this . text . substr ( 0 , 1 ) ) ; let remainderIsLowerCaseOrDigits = true ; for ( let index = 0 ; index < remainder . length ; index ++ ) { let element = remainder [ index ] ; if ( CamelComponent . isLowerFase ( element ) ) { } else if ( CamelComponent . isNumber ( element ) ) { } else { remainderIsLowerCaseOrDigits = false ; } } let remainderIsUpper = true ; for ( let index = 0 ; index < remainder . length ; index ++ ) { let element = remainder [ index ] ; if ( CamelComponent . isUpperCase ( element ) ) { } else { remainderIsUpper = false ; } } return areAllCharsLettersOrDigits && ( remainderIsLowerCaseOrDigits || ( remainderIsUpper && isFirstLetterUpperCase ) ) ; } public addChar ( c ) { let canAddChar = this . canAddChar ( c ) ; if ( ! canAddChar ) throw "Cannot add the character '" + c + "' to the word '" + this . text + "'." ; this . _text += c ; } public tryAddChar ( c ) { let canAddChar = this . canAddChar ( c ) ; if ( ! canAddChar ) return false ; this . _text += c ; return true ; } public canAddChar ( c ) { if ( this . text == null || this . text . length == 0 ) return true ; if ( this . isSpaceComponent ) return c == ' ' ; if ( this . isSymbolComponent ) { let isLetterOrDigit = CamelComponent . isLetterOrNumber ( c ) ; return ( c != ' ' ) && ( ! isLetterOrDigit ) && this . text [ 0 ] == c ; } let isAllUpper = true ; for ( var index = 0 ; index < this . text . length ; index ++ ) { var element = this . text [ index ] ; var isUpper = CamelComponent . isUpperCase ( element ) ; if ( ! isUpper ) { isAllUpper = false ; } } if ( isAllUpper ) { if ( CamelComponent . isUpperCase ( c ) ) return true ; if ( CamelComponent . isLowerFase ( c ) || CamelComponent . isNumber ( c ) ) { return true ; } } return ( CamelComponent . isLowerFase ( c ) || CamelComponent . isNumber ( c ) ) ; } } export class CamelString { private _camelComponents : CamelComponent [ ] = [ ] ; public get camelComponents ( ) { return this . _camelComponents ; } public static transformCS ( camelString ) { var retval = new CamelString ( "" ) ; var camelComponents = camelString . camelComponents ; for ( let i = 0 ; i < camelComponents . length ; i ++ ) { var cc = camelComponents [ i ] ; if ( cc . isLettersAndNumbersOnly && cc . text . length > 2 && cc . text [ 0 ] == "I" && CamelComponent . isUpperCase ( cc . text [ 1 ] ) ) { var cc1 = new CamelComponent ( ) ; cc1 . addChar ( cc . text [ 0 ] ) ; retval . camelComponents . push ( cc1 ) ; var cc2 = new CamelComponent ( ) ; for ( var j = 1 ; j < cc . text . length ; j ++ ) { cc2 . addChar ( cc . text [ j ] ) ; } retval . camelComponents . push ( cc2 ) ; } else { retval . camelComponents . push ( cc ) ; } } return retval ; } constructor ( sourceString ) { if ( ! sourceString ) return ; this . _camelComponents = [ ] ; let lastCamelComponent = new CamelComponent ( ) ; this . camelComponents . push ( lastCamelComponent ) ; for ( let i = 0 ; i < sourceString . length ; i ++ ) { let hasAddedCharacter = lastCamelComponent . tryAddChar ( sourceString [ i ] ) ; if ( ! hasAddedCharacter ) { lastCamelComponent = new CamelComponent ( ) ; this . camelComponents . push ( lastCamelComponent ) ; lastCamelComponent . addChar ( sourceString [ i ] ) ; } } } public toString ( ) { let retval = "" ; for ( var index = 0 ; index < this . _camelComponents . length ; index ++ ) { var element = this . _camelComponents [ index ] ; retval += element . text ; } return retval ; } } export class CamelCasingHelper { public static isCSharpMode ( ) { var langId = vscode . window . activeTextEditor . document . languageId ; var isSupportedLanguage = langId == "csharp" || langId == "typescript" ; var isDisabled = vscode . workspace . getConfiguration ( "maptz.camelCaseNavigation" ) . get ( "csharpmode" ) === false ; return isSupportedLanguage && ! isDisabled ; } public static getCamelWords ( source ) { if ( source == null ) throw "ArgumentNullException('source')" ; let camelString = new CamelString ( source ) ; var useCSMode = CamelCasingHelper . isCSharpMode ( ) ; if ( useCSMode ) { camelString = CamelString . transformCS ( camelString ) ; } let camelWords = [ ] ; for ( var index = 0 ; index < camelString . camelComponents . length ; index ++ ) { var element = camelString . camelComponents [ index ] ; camelWords . push ( element . text ) ; } return camelWords ; } public static indexOfCamelComponent ( source ) { if ( source == null ) throw "ArgumentNullException('source'')" ; let camelString = new CamelString ( source ) ; var useCSMode = CamelCasingHelper . isCSharpMode ( ) ; if ( useCSMode ) { camelString = CamelString . transformCS ( camelString ) ; } if ( camelString . camelComponents . length == 1 ) return source . length ; else return camelString . camelComponents [ 0 ] . text . length ; } public static lastIndexOfCamelComponent ( source ) { if ( source == null ) throw "ArgumentNullException('source')" ; let camelString = new CamelString ( source ) ; var useCSMode = CamelCasingHelper . isCSharpMode ( ) ; if ( useCSMode ) { camelString = CamelString . transformCS ( camelString ) ; } if ( camelString . camelComponents . length == 1 ) return 0 ; else return source . length - camelString . camelComponents [ camelString . camelComponents . length - 1 ] . text . length ; } } export interface ICamelCaseNavigatorService { deleteCamelRight ( ) ; deleteCamelLeft ( ) ; moveCamelCaseLeft ( extendSelection ) ; moveCamelCaseRight ( extendSelection ) ; } export class CamelCaseNavigatorService implements ICamelCaseNavigatorService { public deleteCamel ( isLeft ) { var deleteCamel = true ; for ( let sel of vscode . window . activeTextEditor . selections ) { deleteCamel = deleteCamel && sel . end . isEqual ( sel . start ) ; } if ( ! deleteCamel ) { vscode . commands . executeCommand ( "deleteLeft" ) ; return ; } var newSelections = [ ] ; for ( let sel of vscode . window . activeTextEditor . selections ) { if ( isLeft ) { var selection = this . moveCamelCaseLeftInternal ( true , sel ) ; newSelections . push ( selection ) ; } else { var selection = this . moveCamelCaseRightInternal ( true , sel ) ; newSelections . push ( selection ) ; } } vscode . window . activeTextEditor . edit ( editBuilder => { for ( let selection of newSelections ) { editBuilder . delete ( selection ) ; } } , { undoStopAfter : true , undoStopBefore : true } ) ; return ; } public deleteCamelLeft ( ) { this . deleteCamel ( true ) ; } public deleteCamelRight ( ) { this . deleteCamel ( false ) ; } public moveCamelCaseLeft ( extendSelection ) { var newSelections = [ ] ; for ( let sel of vscode . window . activeTextEditor . selections ) { var selection = this . moveCamelCaseLeftInternal ( extendSelection , sel ) ; newSelections . push ( selection ) ; } vscode . window . activeTextEditor . selections = newSelections ; } private moveCamelCaseLeftInternal ( extendSelection , currentSelection ) { let currentLineText = vscode . window . activeTextEditor . document . lineAt ( currentSelection . active . line ) . text ; let textFromStartOfLine = currentLineText . substr ( 0 , currentSelection . active . character ) ; textFromStartOfLine = textFromStartOfLine . replace ( /[\u0300-\u036f]/g , "a" ) ; let lineDelta = 0 ; let charDelta = 0 ; if ( textFromStartOfLine . length == 0 ) { if ( currentSelection . active . line == 0 ) { return currentSelection ; } lineDelta = - 1 ; let previousLineLength = vscode . window . activeTextEditor . document . lineAt ( currentSelection . active . line + lineDelta ) . text . length ; charDelta = previousLineLength - currentSelection . active . character ; } else { let lastCamelCaseComponentIndex = CamelCasingHelper . lastIndexOfCamelComponent ( textFromStartOfLine ) ; charDelta = 0 - ( textFromStartOfLine . length - lastCamelCaseComponentIndex ) ; } var newPosition = currentSelection . active . translate ( lineDelta , charDelta ) ; var newAnchor = extendSelection ? currentSelection . anchor : newPosition ; var newSelection = new vscode . Selection ( newAnchor , newPosition ) ; return newSelection ; } public moveCamelCaseRight ( extendSelection ) { var newSelections = [ ] ; for ( let sel of vscode . window . activeTextEditor . selections ) { var selection = this . moveCamelCaseRightInternal ( extendSelection , sel ) ; newSelections . push ( selection ) ; } vscode . window . activeTextEditor . selections = newSelections ; } private moveCamelCaseRightInternal ( extendSelection , currentSelection ) { let currentLineText = vscode . window . activeTextEditor . document . lineAt ( currentSelection . active . line ) . text ; let textToEndOfLine = currentLineText . substr ( currentSelection . active . character ) ; textToEndOfLine = textToEndOfLine . replace ( /[\u0300-\u036f]/g , "a" ) ; let lineDelta = 0 ; let charDelta = 0 ; if ( textToEndOfLine . length == 0 ) { if ( currentSelection . active . line == vscode . window . activeTextEditor . document . lineCount - 1 ) { return currentSelection ; } lineDelta = + 1 ; let nextLineLength = vscode . window . activeTextEditor . document . lineAt ( currentSelection . active . line + lineDelta ) . text . length ; charDelta = 0 - currentSelection . active . character ; } else { let camelCaseComponentIndex = CamelCasingHelper . indexOfCamelComponent ( textToEndOfLine ) ; charDelta = camelCaseComponentIndex ; } var newPosition = currentSelection . active . translate ( lineDelta , charDelta ) ; var newAnchor = extendSelection ? currentSelection . anchor : newPosition ; var newSelection = new vscode . Selection ( newAnchor , newPosition ) ; return newSelection ; } }