@@ -4891,8 +4891,8 @@ namespace ts {
48914891 node . kind === SyntaxKind . ThisKeyword ||
48924892 node . kind === SyntaxKind . ThisType ||
48934893 node . kind === SyntaxKind . SuperKeyword ||
4894- isLiteralNameOfPropertyDeclarationOrIndexAccess ( node ) ||
4895- isNameOfExternalModuleImportOrDeclaration ( node ) ) {
4894+ node . kind === SyntaxKind . StringLiteral ||
4895+ isLiteralNameOfPropertyDeclarationOrIndexAccess ( node ) ) {
48964896
48974897 const referencedSymbols = getReferencedSymbolsForNode ( node , sourceFilesToSearch , /*findInStrings*/ false , /*findInComments*/ false ) ;
48984898 return convertReferencedSymbols ( referencedSymbols ) ;
@@ -5559,8 +5559,8 @@ namespace ts {
55595559 // TODO (drosen): This should be enabled in a later release - currently breaks rename.
55605560 // node.kind !== SyntaxKind.ThisKeyword &&
55615561 // node.kind !== SyntaxKind.SuperKeyword &&
5562- ! isLiteralNameOfPropertyDeclarationOrIndexAccess ( node ) &&
5563- ! isNameOfExternalModuleImportOrDeclaration ( node ) ) {
5562+ node . kind !== SyntaxKind . StringLiteral &&
5563+ ! isLiteralNameOfPropertyDeclarationOrIndexAccess ( node ) ) {
55645564 return undefined ;
55655565 }
55665566
@@ -5595,6 +5595,10 @@ namespace ts {
55955595
55965596 const symbol = typeChecker . getSymbolAtLocation ( node ) ;
55975597
5598+ if ( ! symbol && node . kind === SyntaxKind . StringLiteral ) {
5599+ return getReferencesForStringLiteral ( < StringLiteral > node , sourceFiles ) ;
5600+ }
5601+
55985602 // Could not find a symbol e.g. unknown identifier
55995603 if ( ! symbol ) {
56005604 // Can't have references to something that we have no symbol for.
@@ -6151,6 +6155,52 @@ namespace ts {
61516155 }
61526156 }
61536157
6158+
6159+ function getReferencesForStringLiteral ( node : StringLiteral , sourceFiles : SourceFile [ ] ) : ReferencedSymbol [ ] {
6160+ const typeChecker = program . getTypeChecker ( ) ;
6161+ const type = getStringLiteralTypeForNode ( node , typeChecker ) ;
6162+
6163+ if ( ! type ) {
6164+ // nothing to do here. moving on
6165+ return undefined ;
6166+ }
6167+
6168+ const references : ReferenceEntry [ ] = [ ] ;
6169+
6170+ for ( const sourceFile of sourceFiles ) {
6171+ const possiblePositions = getPossibleSymbolReferencePositions ( sourceFile , type . text , sourceFile . getStart ( ) , sourceFile . getEnd ( ) ) ;
6172+ getReferencesForStringLiteralInFile ( sourceFile , type , possiblePositions , references ) ;
6173+ }
6174+
6175+ return [ {
6176+ definition : {
6177+ containerKind : "" ,
6178+ containerName : "" ,
6179+ fileName : node . getSourceFile ( ) . fileName ,
6180+ kind : ScriptElementKind . variableElement ,
6181+ name : type . text ,
6182+ textSpan : createTextSpanFromBounds ( node . getStart ( ) , node . getEnd ( ) )
6183+ } ,
6184+ references : references
6185+ } ] ;
6186+
6187+ function getReferencesForStringLiteralInFile ( sourceFile : SourceFile , searchType : Type , possiblePositions : number [ ] , references : ReferenceEntry [ ] ) : void {
6188+ for ( const position of possiblePositions ) {
6189+ cancellationToken . throwIfCancellationRequested ( ) ;
6190+
6191+ const node = getTouchingWord ( sourceFile , position ) ;
6192+ if ( ! node || node . kind !== SyntaxKind . StringLiteral ) {
6193+ return ;
6194+ }
6195+
6196+ const type = getStringLiteralTypeForNode ( < StringLiteral > node , typeChecker ) ;
6197+ if ( type === searchType ) {
6198+ references . push ( getReferenceEntryFromNode ( node ) ) ;
6199+ }
6200+ }
6201+ }
6202+ }
6203+
61546204 function populateSearchSymbolSet ( symbol : Symbol , location : Node ) : Symbol [ ] {
61556205 // The search set contains at least the current symbol
61566206 let result = [ symbol ] ;
@@ -7671,53 +7721,75 @@ namespace ts {
76717721 }
76727722 }
76737723
7724+ function getStringLiteralTypeForNode ( node : StringLiteral | StringLiteralTypeNode , typeChecker : TypeChecker ) : StringLiteralType {
7725+ const searchNode = node . parent . kind === SyntaxKind . StringLiteralType ? < StringLiteralTypeNode > node . parent : node ;
7726+ const type = typeChecker . getTypeAtLocation ( searchNode ) ;
7727+ if ( type && type . flags & TypeFlags . StringLiteral ) {
7728+ return < StringLiteralType > type ;
7729+ }
7730+ return undefined ;
7731+ }
76747732
76757733 function getRenameInfo ( fileName : string , position : number ) : RenameInfo {
76767734 synchronizeHostData ( ) ;
76777735
76787736 const sourceFile = getValidSourceFile ( fileName ) ;
76797737 const typeChecker = program . getTypeChecker ( ) ;
76807738
7739+ const defaultLibFileName = host . getDefaultLibFileName ( host . getCompilationSettings ( ) ) ;
7740+ const canonicalDefaultLibName = getCanonicalFileName ( ts . normalizePath ( defaultLibFileName ) ) ;
7741+
76817742 const node = getTouchingWord ( sourceFile , position ) ;
76827743
76837744 // Can only rename an identifier.
7684- if ( node && node . kind === SyntaxKind . Identifier ) {
7685- const symbol = typeChecker . getSymbolAtLocation ( node ) ;
7686-
7687- // Only allow a symbol to be renamed if it actually has at least one declaration.
7688- if ( symbol ) {
7689- const declarations = symbol . getDeclarations ( ) ;
7690- if ( declarations && declarations . length > 0 ) {
7691- // Disallow rename for elements that are defined in the standard TypeScript library.
7692- const defaultLibFileName = host . getDefaultLibFileName ( host . getCompilationSettings ( ) ) ;
7693- const canonicalDefaultLibName = getCanonicalFileName ( ts . normalizePath ( defaultLibFileName ) ) ;
7694- if ( defaultLibFileName ) {
7695- for ( const current of declarations ) {
7696- const sourceFile = current . getSourceFile ( ) ;
7697- // TODO (drosen): When is there no source file?
7698- if ( ! sourceFile ) {
7699- continue ;
7700- }
7745+ if ( node ) {
7746+ if ( node . kind === SyntaxKind . Identifier ||
7747+ node . kind === SyntaxKind . StringLiteral ||
7748+ isLiteralNameOfPropertyDeclarationOrIndexAccess ( node ) ) {
7749+ const symbol = typeChecker . getSymbolAtLocation ( node ) ;
7750+
7751+ // Only allow a symbol to be renamed if it actually has at least one declaration.
7752+ if ( symbol ) {
7753+ const declarations = symbol . getDeclarations ( ) ;
7754+ if ( declarations && declarations . length > 0 ) {
7755+ // Disallow rename for elements that are defined in the standard TypeScript library.
7756+ if ( forEach ( declarations , isDefinedInLibraryFile ) ) {
7757+ return getRenameInfoError ( getLocaleSpecificMessage ( Diagnostics . You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library ) ) ;
7758+ }
77017759
7702- const canonicalName = getCanonicalFileName ( ts . normalizePath ( sourceFile . fileName ) ) ;
7703- if ( canonicalName === canonicalDefaultLibName ) {
7704- return getRenameInfoError ( getLocaleSpecificMessage ( Diagnostics . You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library ) ) ;
7705- }
7760+ const displayName = stripQuotes ( getDeclaredName ( typeChecker , symbol , node ) ) ;
7761+ const kind = getSymbolKind ( symbol , node ) ;
7762+ if ( kind ) {
7763+ return {
7764+ canRename : true ,
7765+ kind,
7766+ displayName,
7767+ localizedErrorMessage : undefined ,
7768+ fullDisplayName : typeChecker . getFullyQualifiedName ( symbol ) ,
7769+ kindModifiers : getSymbolModifiers ( symbol ) ,
7770+ triggerSpan : createTriggerSpanForNode ( node , sourceFile )
7771+ } ;
77067772 }
77077773 }
7708-
7709- const displayName = stripQuotes ( getDeclaredName ( typeChecker , symbol , node ) ) ;
7710- const kind = getSymbolKind ( symbol , node ) ;
7711- if ( kind ) {
7712- return {
7713- canRename : true ,
7714- kind,
7715- displayName,
7716- localizedErrorMessage : undefined ,
7717- fullDisplayName : typeChecker . getFullyQualifiedName ( symbol ) ,
7718- kindModifiers : getSymbolModifiers ( symbol ) ,
7719- triggerSpan : createTextSpan ( node . getStart ( ) , node . getWidth ( ) )
7720- } ;
7774+ }
7775+ else if ( node . kind === SyntaxKind . StringLiteral ) {
7776+ const type = getStringLiteralTypeForNode ( < StringLiteral > node , typeChecker ) ;
7777+ if ( type ) {
7778+ if ( isDefinedInLibraryFile ( node ) ) {
7779+ return getRenameInfoError ( getLocaleSpecificMessage ( Diagnostics . You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library ) ) ;
7780+ }
7781+ else {
7782+ const displayName = stripQuotes ( type . text ) ;
7783+ return {
7784+ canRename : true ,
7785+ kind : ScriptElementKind . variableElement ,
7786+ displayName,
7787+ localizedErrorMessage : undefined ,
7788+ fullDisplayName : displayName ,
7789+ kindModifiers : ScriptElementKindModifier . none ,
7790+ triggerSpan : createTriggerSpanForNode ( node , sourceFile )
7791+ } ;
7792+ }
77217793 }
77227794 }
77237795 }
@@ -7736,6 +7808,28 @@ namespace ts {
77367808 triggerSpan : undefined
77377809 } ;
77387810 }
7811+
7812+ function isDefinedInLibraryFile ( declaration : Node ) {
7813+ if ( defaultLibFileName ) {
7814+ const sourceFile = declaration . getSourceFile ( ) ;
7815+ const canonicalName = getCanonicalFileName ( ts . normalizePath ( sourceFile . fileName ) ) ;
7816+ if ( canonicalName === canonicalDefaultLibName ) {
7817+ return true ;
7818+ }
7819+ }
7820+ return false ;
7821+ }
7822+
7823+ function createTriggerSpanForNode ( node : Node , sourceFile : SourceFile ) {
7824+ let start = node . getStart ( sourceFile ) ;
7825+ let width = node . getWidth ( sourceFile ) ;
7826+ if ( node . kind === SyntaxKind . StringLiteral ) {
7827+ // Exclude the quotes
7828+ start += 1 ;
7829+ width -= 2 ;
7830+ }
7831+ return createTextSpan ( start , width ) ;
7832+ }
77397833 }
77407834
77417835 return {
0 commit comments