55 * Use of this source code is governed by an MIT-style license that can be
66 * found in the LICENSE file at https://angular.io/license
77 */
8- import { AST , BindingPipe , LiteralPrimitive , MethodCall , PropertyRead , PropertyWrite , SafeMethodCall , SafePropertyRead , TmplAstBoundAttribute , TmplAstBoundEvent , TmplAstNode , TmplAstReference , TmplAstTextAttribute , TmplAstVariable } from '@angular/compiler' ;
8+ import { AbsoluteSourceSpan , AST , BindingPipe , LiteralPrimitive , MethodCall , ParseSourceSpan , PropertyRead , PropertyWrite , SafeMethodCall , SafePropertyRead , TmplAstBoundAttribute , TmplAstBoundEvent , TmplAstNode , TmplAstReference , TmplAstTextAttribute , TmplAstVariable } from '@angular/compiler' ;
99import { NgCompiler } from '@angular/compiler-cli/src/ngtsc/core' ;
1010import { absoluteFrom , absoluteFromSourceFile , AbsoluteFsPath } from '@angular/compiler-cli/src/ngtsc/file_system' ;
1111import { DirectiveSymbol , ShimLocation , SymbolKind , TemplateTypeChecker , TypeCheckingProgramStrategy } from '@angular/compiler-cli/src/ngtsc/typecheck/api' ;
@@ -14,7 +14,7 @@ import * as ts from 'typescript';
1414
1515import { getTargetAtPosition , TargetNodeKind } from './template_target' ;
1616import { findTightestNode } from './ts_utils' ;
17- import { getDirectiveMatchesForAttribute , getDirectiveMatchesForElementTag , getTemplateInfoAtPosition , isTemplateNode , isWithin , TemplateInfo , toTextSpan } from './utils' ;
17+ import { getDirectiveMatchesForAttribute , getDirectiveMatchesForElementTag , getTemplateInfoAtPosition , isWithin , TemplateInfo , toTextSpan } from './utils' ;
1818
1919interface FilePosition {
2020 fileName : string ;
@@ -64,10 +64,37 @@ export class ReferencesAndRenameBuilder {
6464 private readonly strategy : TypeCheckingProgramStrategy ,
6565 private readonly tsLS : ts . LanguageService , private readonly compiler : NgCompiler ) { }
6666
67+ getRenameInfo ( filePath : string , position : number ) :
68+ Omit < ts . RenameInfoSuccess , 'kind' | 'kindModifiers' > | ts . RenameInfoFailure {
69+ const templateInfo = getTemplateInfoAtPosition ( filePath , position , this . compiler ) ;
70+ // We could not get a template at position so we assume the request came from outside the
71+ // template.
72+ if ( templateInfo === undefined ) {
73+ return this . tsLS . getRenameInfo ( filePath , position ) ;
74+ }
75+
76+ const allTargetDetails = this . getTargetDetailsAtTemplatePosition ( templateInfo , position ) ;
77+ if ( allTargetDetails === null ) {
78+ return { canRename : false , localizedErrorMessage : 'Could not find template node at position.' } ;
79+ }
80+ const { templateTarget} = allTargetDetails [ 0 ] ;
81+ const templateTextAndSpan = getRenameTextAndSpanAtPosition ( templateTarget , position ) ;
82+ if ( templateTextAndSpan === null ) {
83+ return { canRename : false , localizedErrorMessage : 'Could not determine template node text.' } ;
84+ }
85+ const { text, span} = templateTextAndSpan ;
86+ return {
87+ canRename : true ,
88+ displayName : text ,
89+ fullDisplayName : text ,
90+ triggerSpan : toTextSpan ( span ) ,
91+ } ;
92+ }
93+
6794 findRenameLocations ( filePath : string , position : number ) : readonly ts . RenameLocation [ ] | undefined {
6895 this . ttc . generateAllTypeCheckBlocks ( ) ;
6996 const templateInfo = getTemplateInfoAtPosition ( filePath , position , this . compiler ) ;
70- // We could not get a template at position so we assume the request is came from outside the
97+ // We could not get a template at position so we assume the request came from outside the
7198 // template.
7299 if ( templateInfo === undefined ) {
73100 const requestNode = this . getTsNodeAtPosition ( filePath , position ) ;
@@ -126,11 +153,11 @@ export class ReferencesAndRenameBuilder {
126153 originalNodeText = requestOrigin . requestNode . getText ( ) ;
127154 } else {
128155 const templateNodeText =
129- getTemplateNodeRenameTextAtPosition ( requestOrigin . requestNode , requestOrigin . position ) ;
156+ getRenameTextAndSpanAtPosition ( requestOrigin . requestNode , requestOrigin . position ) ;
130157 if ( templateNodeText === null ) {
131158 return undefined ;
132159 }
133- originalNodeText = templateNodeText ;
160+ originalNodeText = templateNodeText . text ;
134161 }
135162
136163 const locations = this . tsLS . findRenameLocations (
@@ -207,11 +234,11 @@ export class ReferencesAndRenameBuilder {
207234 for ( const node of nodes ) {
208235 // Get the information about the TCB at the template position.
209236 const symbol = this . ttc . getSymbolOfNode ( node , component ) ;
210- const templateTarget = node ;
211-
212237 if ( symbol === null ) {
213238 continue ;
214239 }
240+
241+ const templateTarget = node ;
215242 switch ( symbol . kind ) {
216243 case SymbolKind . Directive :
217244 case SymbolKind . Template :
@@ -233,13 +260,17 @@ export class ReferencesAndRenameBuilder {
233260 }
234261 const directives = getDirectiveMatchesForAttribute (
235262 node . name , symbol . host . templateNode , symbol . host . directives ) ;
236- details . push (
237- { typescriptLocations : this . getPositionsForDirectives ( directives ) , templateTarget} ) ;
263+ details . push ( {
264+ typescriptLocations : this . getPositionsForDirectives ( directives ) ,
265+ templateTarget,
266+ } ) ;
238267 break ;
239268 }
240269 case SymbolKind . Reference : {
241- details . push (
242- { typescriptLocations : [ toFilePosition ( symbol . referenceVarLocation ) ] , templateTarget} ) ;
270+ details . push ( {
271+ typescriptLocations : [ toFilePosition ( symbol . referenceVarLocation ) ] ,
272+ templateTarget,
273+ } ) ;
243274 break ;
244275 }
245276 case SymbolKind . Variable : {
@@ -253,14 +284,18 @@ export class ReferencesAndRenameBuilder {
253284 } ) ;
254285 } else if ( isWithin ( position , templateTarget . keySpan ) ) {
255286 // In the keySpan of the variable, we want to get the reference of the local variable.
256- details . push (
257- { typescriptLocations : [ toFilePosition ( symbol . localVarLocation ) ] , templateTarget} ) ;
287+ details . push ( {
288+ typescriptLocations : [ toFilePosition ( symbol . localVarLocation ) ] ,
289+ templateTarget,
290+ } ) ;
258291 }
259292 } else {
260293 // If the templateNode is not the `TmplAstVariable`, it must be a usage of the
261294 // variable somewhere in the template.
262- details . push (
263- { typescriptLocations : [ toFilePosition ( symbol . localVarLocation ) ] , templateTarget} ) ;
295+ details . push ( {
296+ typescriptLocations : [ toFilePosition ( symbol . localVarLocation ) ] ,
297+ templateTarget,
298+ } ) ;
264299 }
265300 break ;
266301 }
@@ -374,15 +409,19 @@ export class ReferencesAndRenameBuilder {
374409 }
375410}
376411
377- function getTemplateNodeRenameTextAtPosition ( node : TmplAstNode | AST , position : number ) : string | null {
412+ function getRenameTextAndSpanAtPosition ( node : TmplAstNode | AST , position : number ) :
413+ { text : string , span : ParseSourceSpan | AbsoluteSourceSpan } | null {
378414 if ( node instanceof TmplAstBoundAttribute || node instanceof TmplAstTextAttribute ||
379415 node instanceof TmplAstBoundEvent ) {
380- return node . name ;
416+ if ( node . keySpan === undefined ) {
417+ return null ;
418+ }
419+ return { text : node . name , span : node . keySpan } ;
381420 } else if ( node instanceof TmplAstVariable || node instanceof TmplAstReference ) {
382421 if ( isWithin ( position , node . keySpan ) ) {
383- return node . keySpan . toString ( ) ;
422+ return { text : node . keySpan . toString ( ) , span : node . keySpan } ;
384423 } else if ( node . valueSpan && isWithin ( position , node . valueSpan ) ) {
385- return node . valueSpan . toString ( ) ;
424+ return { text : node . valueSpan . toString ( ) , span : node . valueSpan } ;
386425 }
387426 }
388427
@@ -392,9 +431,16 @@ function getTemplateNodeRenameTextAtPosition(node: TmplAstNode|AST, position: nu
392431 }
393432 if ( node instanceof PropertyRead || node instanceof MethodCall || node instanceof PropertyWrite ||
394433 node instanceof SafePropertyRead || node instanceof SafeMethodCall ) {
395- return node . name ;
434+ return { text : node . name , span : node . nameSpan } ;
396435 } else if ( node instanceof LiteralPrimitive ) {
397- return node . value ;
436+ const span = node . span ;
437+ const text = node . value ;
438+ if ( typeof text === 'string' ) {
439+ // The span of a string literal includes the quotes but they should be removed for renaming.
440+ span . start += 1 ;
441+ span . end -= 1 ;
442+ }
443+ return { text, span} ;
398444 }
399445
400446 return null ;
0 commit comments