When certain information is requested from the Angular Language Service, we know that there will be no additional Angular information if the requested position is not in an inline template, template url, or style url. To avoid unnecessary compiler compilations, we short circuit and return `undefined` before asking the compiler for any type of answer which would trigger a partial compilation, at the very least. fixes https://github.com/angular/vscode-ng-language-service/issues/1104 PR Close #40946
82 lines
2.5 KiB
TypeScript
82 lines
2.5 KiB
TypeScript
/**
|
|
* @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 * as ts from 'typescript';
|
|
|
|
/**
|
|
* Return the node that most tightly encompasses the specified `position`.
|
|
* @param node The starting node to start the top-down search.
|
|
* @param position The target position within the `node`.
|
|
*/
|
|
export function findTightestNode(node: ts.Node, position: number): ts.Node|undefined {
|
|
if (node.getStart() <= position && position < node.getEnd()) {
|
|
return node.forEachChild(c => findTightestNode(c, position)) ?? node;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function getParentClassDeclaration(startNode: ts.Node): ts.ClassDeclaration|undefined {
|
|
while (startNode) {
|
|
if (ts.isClassDeclaration(startNode)) {
|
|
return startNode;
|
|
}
|
|
startNode = startNode.parent;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Returns a property assignment from the assignment value if the property name
|
|
* matches the specified `key`, or `null` if there is no match.
|
|
*/
|
|
export function getPropertyAssignmentFromValue(value: ts.Node, key: string): ts.PropertyAssignment|
|
|
null {
|
|
const propAssignment = value.parent;
|
|
if (!propAssignment || !ts.isPropertyAssignment(propAssignment) ||
|
|
propAssignment.name.getText() !== key) {
|
|
return null;
|
|
}
|
|
return propAssignment;
|
|
}
|
|
|
|
/**
|
|
* Given a decorator property assignment, return the ClassDeclaration node that corresponds to the
|
|
* directive class the property applies to.
|
|
* If the property assignment is not on a class decorator, no declaration is returned.
|
|
*
|
|
* For example,
|
|
*
|
|
* @Component({
|
|
* template: '<div></div>'
|
|
* ^^^^^^^^^^^^^^^^^^^^^^^---- property assignment
|
|
* })
|
|
* class AppComponent {}
|
|
* ^---- class declaration node
|
|
*
|
|
* @param propAsgnNode property assignment
|
|
*/
|
|
export function getClassDeclFromDecoratorProp(propAsgnNode: ts.PropertyAssignment):
|
|
ts.ClassDeclaration|undefined {
|
|
if (!propAsgnNode.parent || !ts.isObjectLiteralExpression(propAsgnNode.parent)) {
|
|
return;
|
|
}
|
|
const objLitExprNode = propAsgnNode.parent;
|
|
if (!objLitExprNode.parent || !ts.isCallExpression(objLitExprNode.parent)) {
|
|
return;
|
|
}
|
|
const callExprNode = objLitExprNode.parent;
|
|
if (!callExprNode.parent || !ts.isDecorator(callExprNode.parent)) {
|
|
return;
|
|
}
|
|
const decorator = callExprNode.parent;
|
|
if (!decorator.parent || !ts.isClassDeclaration(decorator.parent)) {
|
|
return;
|
|
}
|
|
const classDeclNode = decorator.parent;
|
|
return classDeclNode;
|
|
}
|