@@ -39,6 +39,11 @@ namespace ts.FindAllReferences {
3939 readonly isForRename ?: boolean ;
4040 /** True if we are searching for implementations. We will have a different method of adding references if so. */
4141 readonly implementations ?: boolean ;
42+ /**
43+ * True to opt in for enhanced renaming of shorthand properties and import/export specifiers.
44+ * Default is false for backwards compatibility.
45+ */
46+ readonly providePrefixAndSuffixTextForRename ?: boolean ;
4247 }
4348
4449 export function findReferencedSymbols ( program : Program , cancellationToken : CancellationToken , sourceFiles : ReadonlyArray < SourceFile > , sourceFile : SourceFile , position : number ) : ReferencedSymbol [ ] | undefined {
@@ -157,8 +162,8 @@ namespace ts.FindAllReferences {
157162 return { displayParts, kind : symbolKind } ;
158163 }
159164
160- export function toRenameLocation ( entry : Entry , originalNode : Node , checker : TypeChecker ) : RenameLocation {
161- return { ...entryToDocumentSpan ( entry ) , ...getPrefixAndSuffixText ( entry , originalNode , checker ) } ;
165+ export function toRenameLocation ( entry : Entry , originalNode : Node , checker : TypeChecker , providePrefixAndSuffixText : boolean ) : RenameLocation {
166+ return { ...entryToDocumentSpan ( entry ) , ...( providePrefixAndSuffixText && getPrefixAndSuffixText ( entry , originalNode , checker ) ) } ;
162167 }
163168
164169 export function toReferenceEntry ( entry : Entry ) : ReferenceEntry {
@@ -484,15 +489,15 @@ namespace ts.FindAllReferences.Core {
484489
485490 /** Core find-all-references algorithm for a normal symbol. */
486491 function getReferencedSymbolsForSymbol ( originalSymbol : Symbol , node : Node | undefined , sourceFiles : ReadonlyArray < SourceFile > , sourceFilesSet : ReadonlyMap < true > , checker : TypeChecker , cancellationToken : CancellationToken , options : Options ) : SymbolAndEntries [ ] {
487- const symbol = node && skipPastExportOrImportSpecifierOrUnion ( originalSymbol , node , checker , ! ! options . isForRename ) || originalSymbol ;
492+ const symbol = node && skipPastExportOrImportSpecifierOrUnion ( originalSymbol , node , checker , /*useLocalSymbolForExportSpecifier*/ ! isForRenameWithPrefixAndSuffixText ( options ) ) || originalSymbol ;
488493
489494 // Compute the meaning from the location and the symbol it references
490495 const searchMeaning = node ? getIntersectingMeaningFromDeclarations ( node , symbol ) : SemanticMeaning . All ;
491496
492497 const result : SymbolAndEntries [ ] = [ ] ;
493498 const state = new State ( sourceFiles , sourceFilesSet , node ? getSpecialSearchKind ( node ) : SpecialSearchKind . None , checker , cancellationToken , searchMeaning , options , result ) ;
494499
495- const exportSpecifier = ! options . isForRename ? undefined : find ( symbol . declarations , isExportSpecifier ) ;
500+ const exportSpecifier = ! isForRenameWithPrefixAndSuffixText ( options ) ? undefined : find ( symbol . declarations , isExportSpecifier ) ;
496501 if ( exportSpecifier ) {
497502 // When renaming at an export specifier, rename the export and not the thing being exported.
498503 getReferencesAtExportSpecifier ( exportSpecifier . name , symbol , exportSpecifier , state . createSearch ( node , originalSymbol , /*comingFrom*/ undefined ) , state , /*addReferencesHere*/ true , /*alwaysGetReferences*/ true ) ;
@@ -502,7 +507,7 @@ namespace ts.FindAllReferences.Core {
502507 searchForImportsOfExport ( node , symbol , { exportingModuleSymbol : Debug . assertDefined ( symbol . parent , "Expected export symbol to have a parent" ) , exportKind : ExportKind . Default } , state ) ;
503508 }
504509 else {
505- const search = state . createSearch ( node , symbol , /*comingFrom*/ undefined , { allSearchSymbols : node ? populateSearchSymbolSet ( symbol , node , checker , ! ! options . isForRename , ! ! options . implementations ) : [ symbol ] } ) ;
510+ const search = state . createSearch ( node , symbol , /*comingFrom*/ undefined , { allSearchSymbols : node ? populateSearchSymbolSet ( symbol , node , checker , ! ! options . isForRename , ! ! options . providePrefixAndSuffixTextForRename , ! ! options . implementations ) : [ symbol ] } ) ;
506511
507512 // Try to get the smallest valid scope that we can limit our search to;
508513 // otherwise we'll need to search globally (i.e. include each file).
@@ -538,9 +543,9 @@ namespace ts.FindAllReferences.Core {
538543 }
539544
540545 /** Handle a few special cases relating to export/import specifiers. */
541- function skipPastExportOrImportSpecifierOrUnion ( symbol : Symbol , node : Node , checker : TypeChecker , isForRename : boolean ) : Symbol | undefined {
546+ function skipPastExportOrImportSpecifierOrUnion ( symbol : Symbol , node : Node , checker : TypeChecker , useLocalSymbolForExportSpecifier : boolean ) : Symbol | undefined {
542547 const { parent } = node ;
543- if ( isExportSpecifier ( parent ) && ! isForRename ) {
548+ if ( isExportSpecifier ( parent ) && useLocalSymbolForExportSpecifier ) {
544549 return getLocalSymbolForExportSpecifier ( node as Identifier , symbol , parent , checker ) ;
545550 }
546551 // If the symbol is declared as part of a declaration like `{ type: "a" } | { type: "b" }`, use the property on the union type to get more references.
@@ -1071,6 +1076,8 @@ namespace ts.FindAllReferences.Core {
10711076 addReferencesHere : boolean ,
10721077 alwaysGetReferences ?: boolean ,
10731078 ) : void {
1079+ Debug . assert ( ! alwaysGetReferences || ! ! state . options . providePrefixAndSuffixTextForRename , "If alwaysGetReferences is true, then prefix/suffix text must be enabled" ) ;
1080+
10741081 const { parent, propertyName, name } = exportSpecifier ;
10751082 const exportDeclaration = parent . parent ;
10761083 const localSymbol = getLocalSymbolForExportSpecifier ( referenceLocation , referenceSymbol , exportSpecifier , state . checker ) ;
@@ -1102,15 +1109,15 @@ namespace ts.FindAllReferences.Core {
11021109 }
11031110
11041111 // For `export { foo as bar }`, rename `foo`, but not `bar`.
1105- if ( ! state . options . isForRename || alwaysGetReferences ) {
1112+ if ( ! isForRenameWithPrefixAndSuffixText ( state . options ) || alwaysGetReferences ) {
11061113 const exportKind = referenceLocation . originalKeywordKind === SyntaxKind . DefaultKeyword ? ExportKind . Default : ExportKind . Named ;
11071114 const exportSymbol = Debug . assertDefined ( exportSpecifier . symbol ) ;
11081115 const exportInfo = Debug . assertDefined ( getExportInfo ( exportSymbol , exportKind , state . checker ) ) ;
11091116 searchForImportsOfExport ( referenceLocation , exportSymbol , exportInfo , state ) ;
11101117 }
11111118
11121119 // At `export { x } from "foo"`, also search for the imported symbol `"foo".x`.
1113- if ( search . comingFrom !== ImportExport . Export && exportDeclaration . moduleSpecifier && ! propertyName && ! state . options . isForRename ) {
1120+ if ( search . comingFrom !== ImportExport . Export && exportDeclaration . moduleSpecifier && ! propertyName && ! isForRenameWithPrefixAndSuffixText ( state . options ) ) {
11141121 const imported = state . checker . getExportSpecifierLocalTargetSymbol ( exportSpecifier ) ;
11151122 if ( imported ) searchForImportedSymbol ( imported , state ) ;
11161123 }
@@ -1145,7 +1152,7 @@ namespace ts.FindAllReferences.Core {
11451152 const { symbol } = importOrExport ;
11461153
11471154 if ( importOrExport . kind === ImportExport . Import ) {
1148- if ( ! state . options . isForRename ) {
1155+ if ( ! ( isForRenameWithPrefixAndSuffixText ( state . options ) ) ) {
11491156 searchForImportedSymbol ( symbol , state ) ;
11501157 }
11511158 }
@@ -1514,16 +1521,16 @@ namespace ts.FindAllReferences.Core {
15141521
15151522 // For certain symbol kinds, we need to include other symbols in the search set.
15161523 // This is not needed when searching for re-exports.
1517- function populateSearchSymbolSet ( symbol : Symbol , location : Node , checker : TypeChecker , isForRename : boolean , implementations : boolean ) : Symbol [ ] {
1524+ function populateSearchSymbolSet ( symbol : Symbol , location : Node , checker : TypeChecker , isForRename : boolean , providePrefixAndSuffixText : boolean , implementations : boolean ) : Symbol [ ] {
15181525 const result : Symbol [ ] = [ ] ;
1519- forEachRelatedSymbol < void > ( symbol , location , checker , isForRename ,
1526+ forEachRelatedSymbol < void > ( symbol , location , checker , isForRename , ! ( isForRename && providePrefixAndSuffixText ) ,
15201527 ( sym , root , base ) => { result . push ( base || root || sym ) ; } ,
15211528 /*allowBaseTypes*/ ( ) => ! implementations ) ;
15221529 return result ;
15231530 }
15241531
15251532 function forEachRelatedSymbol < T > (
1526- symbol : Symbol , location : Node , checker : TypeChecker , isForRenamePopulateSearchSymbolSet : boolean ,
1533+ symbol : Symbol , location : Node , checker : TypeChecker , isForRenamePopulateSearchSymbolSet : boolean , onlyIncludeBindingElementAtReferenceLocation : boolean ,
15271534 cbSymbol : ( symbol : Symbol , rootSymbol ?: Symbol , baseSymbol ?: Symbol , kind ?: NodeEntryKind ) => T | undefined ,
15281535 allowBaseTypes : ( rootSymbol : Symbol ) => boolean ,
15291536 ) : T | undefined {
@@ -1577,9 +1584,25 @@ namespace ts.FindAllReferences.Core {
15771584 }
15781585
15791586 // symbolAtLocation for a binding element is the local symbol. See if the search symbol is the property.
1580- // Don't do this when populating search set for a rename -- just rename the local.
1587+ // Don't do this when populating search set for a rename when prefix and suffix text will be provided -- just rename the local.
15811588 if ( ! isForRenamePopulateSearchSymbolSet ) {
1582- const bindingElementPropertySymbol = isObjectBindingElementWithoutPropertyName ( location . parent ) ? getPropertySymbolFromBindingElement ( checker , location . parent ) : undefined ;
1589+ let bindingElementPropertySymbol : Symbol | undefined ;
1590+ if ( onlyIncludeBindingElementAtReferenceLocation ) {
1591+ bindingElementPropertySymbol = isObjectBindingElementWithoutPropertyName ( location . parent ) ? getPropertySymbolFromBindingElement ( checker , location . parent ) : undefined ;
1592+ }
1593+ else {
1594+ bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName ( symbol , checker ) ;
1595+ }
1596+ return bindingElementPropertySymbol && fromRoot ( bindingElementPropertySymbol , EntryKind . SearchedPropertyFoundLocal ) ;
1597+ }
1598+
1599+ Debug . assert ( isForRenamePopulateSearchSymbolSet ) ;
1600+ // due to the above assert and the arguments at the uses of this function,
1601+ // (onlyIncludeBindingElementAtReferenceLocation <=> !providePrefixAndSuffixTextForRename) holds
1602+ const includeOriginalSymbolOfBindingElement = onlyIncludeBindingElementAtReferenceLocation ;
1603+
1604+ if ( includeOriginalSymbolOfBindingElement ) {
1605+ const bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName ( symbol , checker ) ;
15831606 return bindingElementPropertySymbol && fromRoot ( bindingElementPropertySymbol , EntryKind . SearchedPropertyFoundLocal ) ;
15841607 }
15851608
@@ -1597,6 +1620,13 @@ namespace ts.FindAllReferences.Core {
15971620 ? getPropertySymbolsFromBaseTypes ( rootSymbol . parent , rootSymbol . name , checker , base => cbSymbol ( sym , rootSymbol , base , kind ) )
15981621 : undefined ) ) ;
15991622 }
1623+
1624+ function getPropertySymbolOfObjectBindingPatternWithoutPropertyName ( symbol : Symbol , checker : TypeChecker ) : Symbol | undefined {
1625+ const bindingElement = getDeclarationOfKind < BindingElement > ( symbol , SyntaxKind . BindingElement ) ;
1626+ if ( bindingElement && isObjectBindingElementWithoutPropertyName ( bindingElement ) ) {
1627+ return getPropertySymbolFromBindingElement ( checker , bindingElement ) ;
1628+ }
1629+ }
16001630 }
16011631
16021632 interface RelatedSymbol {
@@ -1606,6 +1636,7 @@ namespace ts.FindAllReferences.Core {
16061636 function getRelatedSymbol ( search : Search , referenceSymbol : Symbol , referenceLocation : Node , state : State ) : RelatedSymbol | undefined {
16071637 const { checker } = state ;
16081638 return forEachRelatedSymbol ( referenceSymbol , referenceLocation , checker , /*isForRenamePopulateSearchSymbolSet*/ false ,
1639+ /*onlyIncludeBindingElementAtReferenceLocation*/ ! state . options . isForRename || ! ! state . options . providePrefixAndSuffixTextForRename ,
16091640 ( sym , rootSymbol , baseSymbol , kind ) : RelatedSymbol | undefined => search . includes ( baseSymbol || rootSymbol || sym )
16101641 // For a base type, use the symbol for the derived type. For a synthetic (e.g. union) property, use the union symbol.
16111642 ? { symbol : rootSymbol && ! ( getCheckFlags ( sym ) & CheckFlags . Synthetic ) ? rootSymbol : sym , kind }
@@ -1696,4 +1727,8 @@ namespace ts.FindAllReferences.Core {
16961727 t . symbol && t . symbol . flags & ( SymbolFlags . Class | SymbolFlags . Interface ) ? t . symbol : undefined ) ;
16971728 return res . length === 0 ? undefined : res ;
16981729 }
1730+
1731+ function isForRenameWithPrefixAndSuffixText ( options : Options ) {
1732+ return options . isForRename && options . providePrefixAndSuffixTextForRename ;
1733+ }
16991734}
0 commit comments