feat(language-service): support autocomplete string literal union types in templates (#42729)
The native TS language service has the ability to provide autocompletions for string literal union types. This pr is for Angular to do the same in templates. Fixes https://github.com/angular/vscode-ng-language-service/issues/1096 PR Close #42729
This commit is contained in:
@@ -6,8 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AST, MethodCall, ParseError, ParseSourceSpan, PropertyRead, SafeMethodCall, SafePropertyRead, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler';
|
||||
import {AST, LiteralPrimitive, MethodCall, ParseError, ParseSourceSpan, PropertyRead, SafeMethodCall, SafePropertyRead, TmplAstElement, TmplAstNode, TmplAstTemplate} from '@angular/compiler';
|
||||
import {AbsoluteFsPath} from '@angular/compiler-cli/src/ngtsc/file_system';
|
||||
import {TextAttribute} from '@angular/compiler/src/render3/r3_ast';
|
||||
import * as ts from 'typescript';
|
||||
import {ErrorCode} from '../../diagnostics';
|
||||
|
||||
@@ -119,6 +120,14 @@ export interface TemplateTypeChecker {
|
||||
expr: PropertyRead|SafePropertyRead|MethodCall|SafeMethodCall,
|
||||
component: ts.ClassDeclaration): ShimLocation|null;
|
||||
|
||||
/**
|
||||
* For the given node represents a `LiteralPrimitive`(the `TextAttribute` represents a string
|
||||
* literal), retrieve a `ShimLocation` that can be used to perform autocompletion at that point in
|
||||
* the node, if such a location exists.
|
||||
*/
|
||||
getLiteralCompletionLocation(
|
||||
strNode: LiteralPrimitive|TextAttribute, component: ts.ClassDeclaration): ShimLocation|null;
|
||||
|
||||
/**
|
||||
* Get basic metadata on the directives which are in scope for the given component.
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AST, CssSelector, DomElementSchemaRegistry, MethodCall, ParseError, ParseSourceSpan, parseTemplate, PropertyRead, SafeMethodCall, SafePropertyRead, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstVariable} from '@angular/compiler';
|
||||
import {AST, CssSelector, DomElementSchemaRegistry, LiteralPrimitive, MethodCall, ParseError, ParseSourceSpan, parseTemplate, PropertyRead, SafeMethodCall, SafePropertyRead, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstVariable} from '@angular/compiler';
|
||||
import {TextAttribute} from '@angular/compiler/src/render3/r3_ast';
|
||||
import * as ts from 'typescript';
|
||||
import {ErrorCode} from '../../diagnostics';
|
||||
|
||||
@@ -280,6 +281,16 @@ export class TemplateTypeCheckerImpl implements TemplateTypeChecker {
|
||||
PerfPhase.TtcAutocompletion, () => engine.getExpressionCompletionLocation(ast));
|
||||
}
|
||||
|
||||
getLiteralCompletionLocation(
|
||||
node: LiteralPrimitive|TextAttribute, component: ts.ClassDeclaration): ShimLocation|null {
|
||||
const engine = this.getOrCreateCompletionEngine(component);
|
||||
if (engine === null) {
|
||||
return null;
|
||||
}
|
||||
return this.perf.inPhase(
|
||||
PerfPhase.TtcAutocompletion, () => engine.getLiteralCompletionLocation(node));
|
||||
}
|
||||
|
||||
invalidateClass(clazz: ts.ClassDeclaration): void {
|
||||
this.completionCache.delete(clazz);
|
||||
this.symbolBuilderCache.delete(clazz);
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
*/
|
||||
|
||||
import {TmplAstReference, TmplAstTemplate} from '@angular/compiler';
|
||||
import {AST, EmptyExpr, MethodCall, PropertyRead, PropertyWrite, SafeMethodCall, SafePropertyRead, TmplAstNode} from '@angular/compiler/src/compiler';
|
||||
import {AST, EmptyExpr, LiteralPrimitive, MethodCall, PropertyRead, PropertyWrite, SafeMethodCall, SafePropertyRead, TmplAstNode} from '@angular/compiler/src/compiler';
|
||||
import {TextAttribute} from '@angular/compiler/src/render3/r3_ast';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../file_system';
|
||||
@@ -32,8 +33,9 @@ export class CompletionEngine {
|
||||
private templateContextCache =
|
||||
new Map<TmplAstTemplate|null, Map<string, ReferenceCompletion|VariableCompletion>>();
|
||||
|
||||
private expressionCompletionCache =
|
||||
new Map<PropertyRead|SafePropertyRead|MethodCall|SafeMethodCall, ShimLocation>();
|
||||
private expressionCompletionCache = new Map<
|
||||
PropertyRead|SafePropertyRead|MethodCall|SafeMethodCall|LiteralPrimitive|TextAttribute,
|
||||
ShimLocation>();
|
||||
|
||||
|
||||
constructor(private tcb: ts.Node, private data: TemplateData, private shimPath: AbsoluteFsPath) {
|
||||
@@ -144,6 +146,46 @@ export class CompletionEngine {
|
||||
return res;
|
||||
}
|
||||
|
||||
getLiteralCompletionLocation(expr: LiteralPrimitive|TextAttribute): ShimLocation|null {
|
||||
if (this.expressionCompletionCache.has(expr)) {
|
||||
return this.expressionCompletionCache.get(expr)!;
|
||||
}
|
||||
|
||||
let tsExpr: ts.StringLiteral|ts.NumericLiteral|null = null;
|
||||
|
||||
if (expr instanceof TextAttribute) {
|
||||
const strNode = findFirstMatchingNode(this.tcb, {
|
||||
filter: ts.isParenthesizedExpression,
|
||||
withSpan: expr.sourceSpan,
|
||||
});
|
||||
if (strNode !== null && ts.isStringLiteral(strNode.expression)) {
|
||||
tsExpr = strNode.expression;
|
||||
}
|
||||
} else {
|
||||
tsExpr = findFirstMatchingNode(this.tcb, {
|
||||
filter: (n: ts.Node): n is ts.NumericLiteral | ts.StringLiteral =>
|
||||
ts.isStringLiteral(n) || ts.isNumericLiteral(n),
|
||||
withSpan: expr.sourceSpan,
|
||||
});
|
||||
}
|
||||
|
||||
if (tsExpr === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let positionInShimFile = tsExpr.getEnd();
|
||||
if (ts.isStringLiteral(tsExpr)) {
|
||||
// In the shimFile, if `tsExpr` is a string, the position should be in the quotes.
|
||||
positionInShimFile -= 1;
|
||||
}
|
||||
const res: ShimLocation = {
|
||||
shimPath: this.shimPath,
|
||||
positionInShimFile,
|
||||
};
|
||||
this.expressionCompletionCache.set(expr, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global completions within the given template context - either a `TmplAstTemplate` embedded
|
||||
* view, or `null` for the root context.
|
||||
|
||||
Reference in New Issue
Block a user