2021-01-21 11:42:23 -05:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
import {AST, BindingPipe, LiteralPrimitive, MethodCall, PropertyRead, PropertyWrite, SafeMethodCall, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstNode, TmplAstReference, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler';
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core';
|
2021-01-21 11:42:23 -05:00
|
|
|
import {absoluteFrom} from '@angular/compiler-cli/src/ngtsc/file_system';
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
import {DirectiveMeta, PipeMeta} from '@angular/compiler-cli/src/ngtsc/metadata';
|
|
|
|
import {DirectiveSymbol, ShimLocation, Symbol, SymbolKind, TemplateTypeChecker} from '@angular/compiler-cli/src/ngtsc/typecheck/api';
|
2021-01-21 11:42:23 -05:00
|
|
|
import {ExpressionIdentifier, hasExpressionIdentifier} from '@angular/compiler-cli/src/ngtsc/typecheck/src/comments';
|
|
|
|
import * as ts from 'typescript';
|
|
|
|
|
|
|
|
import {getTargetAtPosition, TargetNodeKind} from './template_target';
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
import {findTightestNode, getParentClassDeclaration} from './ts_utils';
|
2021-01-21 11:42:23 -05:00
|
|
|
import {getDirectiveMatchesForAttribute, getDirectiveMatchesForElementTag, getTemplateLocationFromShimLocation, isWithin, TemplateInfo, toTextSpan} from './utils';
|
|
|
|
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
/** Represents a location in a file. */
|
|
|
|
export interface FilePosition {
|
2021-01-21 11:42:23 -05:00
|
|
|
fileName: string;
|
|
|
|
position: number;
|
|
|
|
}
|
|
|
|
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
/**
|
|
|
|
* Converts a `ShimLocation` to a more genericly named `FilePosition`.
|
|
|
|
*/
|
2021-01-21 11:42:23 -05:00
|
|
|
function toFilePosition(shimLocation: ShimLocation): FilePosition {
|
|
|
|
return {fileName: shimLocation.shimPath, position: shimLocation.positionInShimFile};
|
|
|
|
}
|
|
|
|
export interface TemplateLocationDetails {
|
|
|
|
/**
|
|
|
|
* A target node in a template.
|
|
|
|
*/
|
|
|
|
templateTarget: TmplAstNode|AST;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TypeScript locations which the template node maps to. A given template node might map to
|
|
|
|
* several TS nodes. For example, a template node for an attribute might resolve to several
|
|
|
|
* directives or a directive and one of its inputs.
|
|
|
|
*/
|
|
|
|
typescriptLocations: FilePosition[];
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The resolved Symbol for the template target.
|
|
|
|
*/
|
|
|
|
symbol: Symbol;
|
2021-01-21 11:42:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
/**
|
|
|
|
* Takes a position in a template and finds equivalent targets in TS files as well as details about
|
|
|
|
* the targeted template node.
|
|
|
|
*/
|
2021-01-21 11:42:23 -05:00
|
|
|
export function getTargetDetailsAtTemplatePosition(
|
|
|
|
{template, component}: TemplateInfo, position: number,
|
|
|
|
templateTypeChecker: TemplateTypeChecker): TemplateLocationDetails[]|null {
|
|
|
|
// Find the AST node in the template at the position.
|
|
|
|
const positionDetails = getTargetAtPosition(template, position);
|
|
|
|
if (positionDetails === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const nodes = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
|
|
|
|
positionDetails.context.nodes :
|
|
|
|
[positionDetails.context.node];
|
|
|
|
|
|
|
|
const details: TemplateLocationDetails[] = [];
|
|
|
|
|
|
|
|
for (const node of nodes) {
|
|
|
|
// Get the information about the TCB at the template position.
|
|
|
|
const symbol = templateTypeChecker.getSymbolOfNode(node, component);
|
|
|
|
if (symbol === null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const templateTarget = node;
|
|
|
|
switch (symbol.kind) {
|
|
|
|
case SymbolKind.Directive:
|
|
|
|
case SymbolKind.Template:
|
|
|
|
// References to elements, templates, and directives will be through template references
|
|
|
|
// (#ref). They shouldn't be used directly for a Language Service reference request.
|
|
|
|
break;
|
|
|
|
case SymbolKind.Element: {
|
|
|
|
const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
details.push({
|
|
|
|
typescriptLocations: getPositionsForDirectives(matches),
|
|
|
|
templateTarget,
|
|
|
|
symbol,
|
|
|
|
});
|
2021-01-21 11:42:23 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind.DomBinding: {
|
|
|
|
// Dom bindings aren't currently type-checked (see `checkTypeOfDomBindings`) so they don't
|
|
|
|
// have a shim location. This means we can't match dom bindings to their lib.dom
|
|
|
|
// reference, but we can still see if they match to a directive.
|
|
|
|
if (!(node instanceof TmplAstTextAttribute) && !(node instanceof TmplAstBoundAttribute)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
const directives = getDirectiveMatchesForAttribute(
|
|
|
|
node.name, symbol.host.templateNode, symbol.host.directives);
|
|
|
|
details.push({
|
|
|
|
typescriptLocations: getPositionsForDirectives(directives),
|
|
|
|
templateTarget,
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
symbol,
|
2021-01-21 11:42:23 -05:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind.Reference: {
|
|
|
|
details.push({
|
|
|
|
typescriptLocations: [toFilePosition(symbol.referenceVarLocation)],
|
|
|
|
templateTarget,
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
symbol,
|
2021-01-21 11:42:23 -05:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind.Variable: {
|
|
|
|
if ((templateTarget instanceof TmplAstVariable)) {
|
|
|
|
if (templateTarget.valueSpan !== undefined &&
|
|
|
|
isWithin(position, templateTarget.valueSpan)) {
|
|
|
|
// In the valueSpan of the variable, we want to get the reference of the initializer.
|
|
|
|
details.push({
|
|
|
|
typescriptLocations: [toFilePosition(symbol.initializerLocation)],
|
|
|
|
templateTarget,
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
symbol,
|
2021-01-21 11:42:23 -05:00
|
|
|
});
|
|
|
|
} else if (isWithin(position, templateTarget.keySpan)) {
|
|
|
|
// In the keySpan of the variable, we want to get the reference of the local variable.
|
|
|
|
details.push({
|
|
|
|
typescriptLocations: [toFilePosition(symbol.localVarLocation)],
|
|
|
|
templateTarget,
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
symbol,
|
2021-01-21 11:42:23 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If the templateNode is not the `TmplAstVariable`, it must be a usage of the
|
|
|
|
// variable somewhere in the template.
|
|
|
|
details.push({
|
|
|
|
typescriptLocations: [toFilePosition(symbol.localVarLocation)],
|
|
|
|
templateTarget,
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
symbol,
|
2021-01-21 11:42:23 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind.Input:
|
|
|
|
case SymbolKind.Output: {
|
|
|
|
details.push({
|
|
|
|
typescriptLocations: symbol.bindings.map(binding => toFilePosition(binding.shimLocation)),
|
|
|
|
templateTarget,
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
symbol,
|
2021-01-21 11:42:23 -05:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind.Pipe:
|
|
|
|
case SymbolKind.Expression: {
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
details.push({
|
|
|
|
typescriptLocations: [toFilePosition(symbol.shimLocation)],
|
|
|
|
templateTarget,
|
|
|
|
symbol,
|
|
|
|
});
|
2021-01-21 11:42:23 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return details.length > 0 ? details : null;
|
|
|
|
}
|
|
|
|
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
/**
|
|
|
|
* Given a set of `DirectiveSymbol`s, finds the equivalent `FilePosition` of the class declaration.
|
|
|
|
*/
|
2021-01-21 11:42:23 -05:00
|
|
|
function getPositionsForDirectives(directives: Set<DirectiveSymbol>): FilePosition[] {
|
|
|
|
const allDirectives: FilePosition[] = [];
|
|
|
|
for (const dir of directives.values()) {
|
|
|
|
const dirClass = dir.tsSymbol.valueDeclaration;
|
|
|
|
if (dirClass === undefined || !ts.isClassDeclaration(dirClass) || dirClass.name === undefined) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const {fileName} = dirClass.getSourceFile();
|
|
|
|
const position = dirClass.name.getStart();
|
|
|
|
allDirectives.push({fileName, position});
|
|
|
|
}
|
|
|
|
|
|
|
|
return allDirectives;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a "key" for a rename/reference location by concatenating file name, span start, and span
|
|
|
|
* length. This allows us to de-duplicate template results when an item may appear several times
|
|
|
|
* in the TCB but map back to the same template location.
|
|
|
|
*/
|
|
|
|
export function createLocationKey(ds: ts.DocumentSpan) {
|
|
|
|
return ds.fileName + ds.textSpan.start + ds.textSpan.length;
|
|
|
|
}
|
|
|
|
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
/**
|
|
|
|
* Converts a given `ts.DocumentSpan` in a shim file to its equivalent `ts.DocumentSpan` in the
|
|
|
|
* template.
|
|
|
|
*
|
|
|
|
* You can optionally provide a `requiredNodeText` that ensures the equivalent template node's text
|
|
|
|
* matches. If it does not, this function will return `null`.
|
|
|
|
*/
|
2021-01-21 11:42:23 -05:00
|
|
|
export function convertToTemplateDocumentSpan<T extends ts.DocumentSpan>(
|
|
|
|
shimDocumentSpan: T, templateTypeChecker: TemplateTypeChecker, program: ts.Program,
|
|
|
|
requiredNodeText?: string): T|null {
|
|
|
|
const sf = program.getSourceFile(shimDocumentSpan.fileName);
|
|
|
|
if (sf === undefined) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
const tcbNode = findTightestNode(sf, shimDocumentSpan.textSpan.start);
|
|
|
|
if (tcbNode === undefined ||
|
|
|
|
hasExpressionIdentifier(sf, tcbNode, ExpressionIdentifier.EVENT_PARAMETER)) {
|
|
|
|
// If the reference result is the $event parameter in the subscribe/addEventListener
|
|
|
|
// function in the TCB, we want to filter this result out of the references. We really only
|
|
|
|
// want to return references to the parameter in the template itself.
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
// TODO(atscott): Determine how to consistently resolve paths. i.e. with the project
|
|
|
|
// serverHost or LSParseConfigHost in the adapter. We should have a better defined way to
|
|
|
|
// normalize paths.
|
|
|
|
const mapping = getTemplateLocationFromShimLocation(
|
|
|
|
templateTypeChecker, absoluteFrom(shimDocumentSpan.fileName),
|
|
|
|
shimDocumentSpan.textSpan.start);
|
|
|
|
if (mapping === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const {span, templateUrl} = mapping;
|
|
|
|
if (requiredNodeText !== undefined && span.toString() !== requiredNodeText) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
...shimDocumentSpan,
|
|
|
|
fileName: templateUrl,
|
|
|
|
textSpan: toTextSpan(span),
|
|
|
|
// Specifically clear other text span values because we do not have enough knowledge to
|
|
|
|
// convert these to spans in the template.
|
|
|
|
contextSpan: undefined,
|
|
|
|
originalContextSpan: undefined,
|
|
|
|
originalTextSpan: undefined,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
/**
|
|
|
|
* Finds the text and `ts.TextSpan` for the node at a position in a template.
|
|
|
|
*/
|
2021-01-21 11:42:23 -05:00
|
|
|
export function getRenameTextAndSpanAtPosition(
|
|
|
|
node: TmplAstNode|AST, position: number): {text: string, span: ts.TextSpan}|null {
|
|
|
|
if (node instanceof TmplAstBoundAttribute || node instanceof TmplAstTextAttribute ||
|
|
|
|
node instanceof TmplAstBoundEvent) {
|
|
|
|
if (node.keySpan === undefined) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return {text: node.name, span: toTextSpan(node.keySpan)};
|
|
|
|
} else if (node instanceof TmplAstVariable || node instanceof TmplAstReference) {
|
|
|
|
if (isWithin(position, node.keySpan)) {
|
|
|
|
return {text: node.keySpan.toString(), span: toTextSpan(node.keySpan)};
|
|
|
|
} else if (node.valueSpan && isWithin(position, node.valueSpan)) {
|
|
|
|
return {text: node.valueSpan.toString(), span: toTextSpan(node.valueSpan)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node instanceof PropertyRead || node instanceof MethodCall || node instanceof PropertyWrite ||
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
node instanceof SafePropertyRead || node instanceof SafeMethodCall ||
|
|
|
|
node instanceof BindingPipe) {
|
2021-01-21 11:42:23 -05:00
|
|
|
return {text: node.name, span: toTextSpan(node.nameSpan)};
|
|
|
|
} else if (node instanceof LiteralPrimitive) {
|
|
|
|
const span = toTextSpan(node.sourceSpan);
|
|
|
|
const text = node.value;
|
|
|
|
if (typeof text === 'string') {
|
|
|
|
// The span of a string literal includes the quotes but they should be removed for renaming.
|
|
|
|
span.start += 1;
|
|
|
|
span.length -= 2;
|
|
|
|
}
|
|
|
|
return {text, span};
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
feat(language-service): Enable renaming of pipes (#40523)
This commit updates the logic in the LS renaming to handle renaming of
pipes, both from the name expression in the pipe metadata as well as
from the template.
The approach here is to introduce a new concept for renaming: an
"indirect" rename. In this type of rename, we find rename locations
in with the native TS Language Service using a different node than the
one we are renaming. Using pipes as an example, if we want to rename the
pipe name from the string literal expression, we use the transform
method to find rename locations rather than the string literal itself
(which will not return any results because it's just a string).
So the general approach is:
* Determine the details about the requested rename location, i.e. the
targeted template node and symbol for a template rename, or the TS
node for a rename outside a template.
* Using the details of the location, determine if the node is attempting
to rename something that is an indirect rename (pipes, selectors,
bindings). Other renames are considered "direct" and we use whatever
results the native TSLS returns for the rename locations.
* In the case of indirect renames, we throw out results that do not
appear in the templates (in this case, the shim files). These results will be
for the "indirect" rename that we don't want to touch, but are only
using to find template results.
* Create an additional rename result for the string literal expression
that is used for the input/output alias, the pipe name, or the
selector.
Note that renaming is moving towards being much more accurate in its
results than "find references". When the approach for renaming
stabilizes, we may want to then port the changes back to being shared
with the approach for retrieving references.
PR Close #40523
2021-01-20 20:54:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrives the `PipeMeta` or `DirectiveMeta` of the given `ts.Node`'s parent class.
|
|
|
|
*
|
|
|
|
* Returns `null` if the node has no parent class or there is no meta associated with the class.
|
|
|
|
*/
|
|
|
|
export function getParentClassMeta(requestNode: ts.Node, compiler: NgCompiler): PipeMeta|
|
|
|
|
DirectiveMeta|null {
|
|
|
|
const parentClass = getParentClassDeclaration(requestNode);
|
|
|
|
if (parentClass === undefined) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return compiler.getMeta(parentClass);
|
2021-06-04 10:57:07 -04:00
|
|
|
}
|