refactor(language-service): clean up and exports and consolidate types (#36533)
PR Close #36533
This commit is contained in:
parent
d50cb30443
commit
5e80e7e216
|
@ -1956,10 +1956,6 @@
|
|||
"packages/forms/src/directives/validators.ts",
|
||||
"packages/forms/src/validators.ts"
|
||||
],
|
||||
[
|
||||
"packages/language-service/src/common.ts",
|
||||
"packages/language-service/src/types.ts"
|
||||
],
|
||||
[
|
||||
"packages/language-service/src/completions.ts",
|
||||
"packages/language-service/src/template.ts",
|
||||
|
|
|
@ -15,6 +15,5 @@
|
|||
*/
|
||||
export {createLanguageService} from './src/language_service';
|
||||
export * from './src/ts_plugin';
|
||||
export {Completion, Completions, Declaration, Declarations, Definition, Diagnostic, Diagnostics, Hover, HoverTextSection, LanguageService, LanguageServiceHost, Location, Span, TemplateSource, TemplateSources} from './src/types';
|
||||
export {Declaration, Definition, Diagnostic, LanguageService, LanguageServiceHost, Span, TemplateSource} from './src/types';
|
||||
export {TypeScriptServiceHost, createLanguageServiceFromTypescript} from './src/typescript_host';
|
||||
export {VERSION} from './src/version';
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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 {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CssSelector, Node as HtmlAst, ParseError, Parser, TemplateAst} from '@angular/compiler';
|
||||
|
||||
import {TemplateSource} from './types';
|
||||
|
||||
export interface AstResult {
|
||||
htmlAst: HtmlAst[];
|
||||
templateAst: TemplateAst[];
|
||||
directive: CompileDirectiveMetadata;
|
||||
directives: CompileDirectiveSummary[];
|
||||
pipes: CompilePipeSummary[];
|
||||
parseErrors?: ParseError[];
|
||||
expressionParser: Parser;
|
||||
template: TemplateSource;
|
||||
}
|
||||
|
||||
export type SelectorInfo = {
|
||||
selectors: CssSelector[],
|
||||
map: Map<CssSelector, CompileDirectiveSummary>
|
||||
};
|
|
@ -10,13 +10,12 @@ import {AbsoluteSourceSpan, AST, AstPath, AttrAst, Attribute, BoundDirectiveProp
|
|||
import {$$, $_, isAsciiLetter, isDigit} from '@angular/compiler/src/chars';
|
||||
|
||||
import {ATTR, getBindingDescriptor} from './binding_utils';
|
||||
import {AstResult} from './common';
|
||||
import {diagnosticInfoFromTemplateInfo, getExpressionScope} from './expression_diagnostics';
|
||||
import {getExpressionScope} from './expression_diagnostics';
|
||||
import {getExpressionCompletions} from './expressions';
|
||||
import {attributeNames, elementNames, eventNames, propertyNames} from './html_info';
|
||||
import {InlineTemplate} from './template';
|
||||
import * as ng from './types';
|
||||
import {findTemplateAstAt, getPathToNodeAtPosition, getSelectors, inSpan, isStructuralDirective, spanOf} from './utils';
|
||||
import {diagnosticInfoFromTemplateInfo, findTemplateAstAt, getPathToNodeAtPosition, getSelectors, inSpan, isStructuralDirective, spanOf} from './utils';
|
||||
|
||||
const HIDDEN_HTML_ELEMENTS: ReadonlySet<string> =
|
||||
new Set(['html', 'script', 'noscript', 'base', 'body', 'title', 'head', 'link']);
|
||||
|
@ -56,7 +55,7 @@ function isIdentifierPart(code: number) {
|
|||
* `position`, nothing is returned.
|
||||
*/
|
||||
function getBoundedWordSpan(
|
||||
templateInfo: AstResult, position: number, ast: HtmlAst|undefined): ts.TextSpan|undefined {
|
||||
templateInfo: ng.AstResult, position: number, ast: HtmlAst|undefined): ts.TextSpan|undefined {
|
||||
const {template} = templateInfo;
|
||||
const templateSrc = template.source;
|
||||
|
||||
|
@ -127,7 +126,7 @@ function getBoundedWordSpan(
|
|||
}
|
||||
|
||||
export function getTemplateCompletions(
|
||||
templateInfo: AstResult, position: number): ng.CompletionEntry[] {
|
||||
templateInfo: ng.AstResult, position: number): ng.CompletionEntry[] {
|
||||
let result: ng.CompletionEntry[] = [];
|
||||
const {htmlAst, template} = templateInfo;
|
||||
// The templateNode starts at the delimiter character so we add 1 to skip it.
|
||||
|
@ -204,7 +203,7 @@ export function getTemplateCompletions(
|
|||
});
|
||||
}
|
||||
|
||||
function attributeCompletions(info: AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] {
|
||||
function attributeCompletions(info: ng.AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] {
|
||||
const attr = path.tail;
|
||||
const elem = path.parentOf(attr);
|
||||
if (!(attr instanceof Attribute) || !(elem instanceof Element)) {
|
||||
|
@ -258,7 +257,7 @@ function attributeCompletions(info: AstResult, path: AstPath<HtmlAst>): ng.Compl
|
|||
}
|
||||
|
||||
function attributeCompletionsForElement(
|
||||
info: AstResult, elementName: string): ng.CompletionEntry[] {
|
||||
info: ng.AstResult, elementName: string): ng.CompletionEntry[] {
|
||||
const results: ng.CompletionEntry[] = [];
|
||||
|
||||
if (info.template instanceof InlineTemplate) {
|
||||
|
@ -292,7 +291,8 @@ function attributeCompletionsForElement(
|
|||
* @param info Object that contains the template AST
|
||||
* @param htmlPath Path to the HTML node
|
||||
*/
|
||||
function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.CompletionEntry[] {
|
||||
function attributeValueCompletions(
|
||||
info: ng.AstResult, htmlPath: HtmlAstPath): ng.CompletionEntry[] {
|
||||
// Find the corresponding Template AST path.
|
||||
const templatePath = findTemplateAstAt(info.templateAst, htmlPath.position);
|
||||
const visitor = new ExpressionVisitor(info, htmlPath.position, () => {
|
||||
|
@ -334,7 +334,7 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C
|
|||
return visitor.results;
|
||||
}
|
||||
|
||||
function elementCompletions(info: AstResult): ng.CompletionEntry[] {
|
||||
function elementCompletions(info: ng.AstResult): ng.CompletionEntry[] {
|
||||
const results: ng.CompletionEntry[] = [...ANGULAR_ELEMENTS];
|
||||
|
||||
if (info.template instanceof InlineTemplate) {
|
||||
|
@ -380,7 +380,7 @@ function entityCompletions(value: string, position: number): ng.CompletionEntry[
|
|||
return result;
|
||||
}
|
||||
|
||||
function interpolationCompletions(info: AstResult, position: number): ng.CompletionEntry[] {
|
||||
function interpolationCompletions(info: ng.AstResult, position: number): ng.CompletionEntry[] {
|
||||
// Look for an interpolation in at the position.
|
||||
const templatePath = findTemplateAstAt(info.templateAst, position);
|
||||
if (!templatePath.tail) {
|
||||
|
@ -399,7 +399,7 @@ function interpolationCompletions(info: AstResult, position: number): ng.Complet
|
|||
// code checks for this case and returns element completions if it is detected or undefined
|
||||
// if it is not.
|
||||
function voidElementAttributeCompletions(
|
||||
info: AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] {
|
||||
info: ng.AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] {
|
||||
const tail = path.tail;
|
||||
if (tail instanceof Text) {
|
||||
const match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/);
|
||||
|
@ -417,7 +417,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
|
|||
private readonly completions = new Map<string, ng.CompletionEntry>();
|
||||
|
||||
constructor(
|
||||
private readonly info: AstResult, private readonly position: number,
|
||||
private readonly info: ng.AstResult, private readonly position: number,
|
||||
private readonly getExpressionScope: () => ng.SymbolTable) {
|
||||
super();
|
||||
}
|
||||
|
@ -619,7 +619,7 @@ interface AngularAttributes {
|
|||
* @param info
|
||||
* @param elementName
|
||||
*/
|
||||
function angularAttributes(info: AstResult, elementName: string): AngularAttributes {
|
||||
function angularAttributes(info: ng.AstResult, elementName: string): AngularAttributes {
|
||||
const {selectors, map: selectorMap} = getSelectors(info);
|
||||
const templateRefs = new Set<string>();
|
||||
const inputs = new Set<string>();
|
||||
|
|
|
@ -8,11 +8,10 @@
|
|||
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript'; // used as value and is provided at runtime
|
||||
import {AstResult} from './common';
|
||||
|
||||
import {locateSymbols} from './locate_symbol';
|
||||
import {getPropertyAssignmentFromValue, isClassDecoratorProperty} from './template';
|
||||
import {Span} from './types';
|
||||
import {findTightestNode} from './utils';
|
||||
import {AstResult, Span} from './types';
|
||||
import {findTightestNode, getPropertyAssignmentFromValue, isClassDecoratorProperty} from './utils';
|
||||
|
||||
/**
|
||||
* Convert Angular Span to TypeScript TextSpan. Angular Span has 'start' and
|
||||
|
|
|
@ -10,20 +10,17 @@ import {NgAnalyzedModules} from '@angular/compiler';
|
|||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AstResult} from './common';
|
||||
import {createDiagnostic, Diagnostic} from './diagnostic_messages';
|
||||
import {getTemplateExpressionDiagnostics} from './expression_diagnostics';
|
||||
import * as ng from './types';
|
||||
import {TypeScriptServiceHost} from './typescript_host';
|
||||
import {findPropertyValueOfType, findTightestNode, offsetSpan, spanOf} from './utils';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return diagnostic information for the parsed AST of the template.
|
||||
* @param ast contains HTML and template AST
|
||||
*/
|
||||
export function getTemplateDiagnostics(ast: AstResult): ng.Diagnostic[] {
|
||||
export function getTemplateDiagnostics(ast: ng.AstResult): ng.Diagnostic[] {
|
||||
const {parseErrors, templateAst, htmlAst, template} = ast;
|
||||
if (parseErrors && parseErrors.length) {
|
||||
return parseErrors.map(e => {
|
||||
|
|
|
@ -6,33 +6,22 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AST, AstPath, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, CompileDirectiveSummary, CompileTypeMetadata, DirectiveAst, ElementAst, EmbeddedTemplateAst, identifierName, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, ReferenceAst, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference, VariableAst} from '@angular/compiler';
|
||||
import {AST, AstPath, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, CompileDirectiveSummary, CompileTypeMetadata, DirectiveAst, ElementAst, EmbeddedTemplateAst, identifierName, ParseSourceSpan, RecursiveTemplateAstVisitor, ReferenceAst, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference, VariableAst} from '@angular/compiler';
|
||||
|
||||
import {AstResult} from './common';
|
||||
import {createDiagnostic, Diagnostic} from './diagnostic_messages';
|
||||
import {AstType} from './expression_type';
|
||||
import {BuiltinType, Definition, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols';
|
||||
import * as ng from './types';
|
||||
import {findOutputBinding, getPathToNodeAtPosition} from './utils';
|
||||
|
||||
export interface DiagnosticTemplateInfo {
|
||||
fileName?: string;
|
||||
offset: number;
|
||||
query: SymbolQuery;
|
||||
members: SymbolTable;
|
||||
htmlAst: Node[];
|
||||
templateAst: TemplateAst[];
|
||||
source: string;
|
||||
}
|
||||
|
||||
export function getTemplateExpressionDiagnostics(info: DiagnosticTemplateInfo): ng.Diagnostic[] {
|
||||
export function getTemplateExpressionDiagnostics(info: ng.DiagnosticTemplateInfo): ng.Diagnostic[] {
|
||||
const visitor = new ExpressionDiagnosticsVisitor(
|
||||
info, (path: TemplateAstPath) => getExpressionScope(info, path));
|
||||
templateVisitAll(visitor, info.templateAst);
|
||||
return visitor.diagnostics;
|
||||
}
|
||||
|
||||
function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
|
||||
function getReferences(info: ng.DiagnosticTemplateInfo): SymbolDeclaration[] {
|
||||
const result: SymbolDeclaration[] = [];
|
||||
|
||||
function processReferences(references: ReferenceAst[]) {
|
||||
|
@ -68,7 +57,7 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
|
|||
return result;
|
||||
}
|
||||
|
||||
function getDefinitionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined {
|
||||
function getDefinitionOf(info: ng.DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined {
|
||||
if (info.fileName) {
|
||||
const templateOffset = info.offset;
|
||||
return [{
|
||||
|
@ -88,7 +77,7 @@ function getDefinitionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Defini
|
|||
* @param path template AST path
|
||||
*/
|
||||
function getVarDeclarations(
|
||||
info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration[] {
|
||||
info: ng.DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration[] {
|
||||
const results: SymbolDeclaration[] = [];
|
||||
for (let current = path.head; current; current = path.childOf(current)) {
|
||||
if (!(current instanceof EmbeddedTemplateAst)) {
|
||||
|
@ -154,7 +143,7 @@ function getVariableTypeFromDirectiveContext(
|
|||
* @param templateElement
|
||||
*/
|
||||
function refinedVariableType(
|
||||
value: string, mergedTable: SymbolTable, info: DiagnosticTemplateInfo,
|
||||
value: string, mergedTable: SymbolTable, info: ng.DiagnosticTemplateInfo,
|
||||
templateElement: EmbeddedTemplateAst): Symbol {
|
||||
if (value === '$implicit') {
|
||||
// Special case: ngFor directive
|
||||
|
@ -206,7 +195,7 @@ function refinedVariableType(
|
|||
}
|
||||
|
||||
function getEventDeclaration(
|
||||
info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration|undefined {
|
||||
info: ng.DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration|undefined {
|
||||
const event = path.tail;
|
||||
if (!(event instanceof BoundEventAst)) {
|
||||
// No event available in this context.
|
||||
|
@ -241,7 +230,7 @@ function getEventDeclaration(
|
|||
* derived for.
|
||||
*/
|
||||
export function getExpressionScope(
|
||||
info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolTable {
|
||||
info: ng.DiagnosticTemplateInfo, path: TemplateAstPath): SymbolTable {
|
||||
let result = info.members;
|
||||
const references = getReferences(info);
|
||||
const variables = getVarDeclarations(info, path);
|
||||
|
@ -262,7 +251,7 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
|
|||
diagnostics: ng.Diagnostic[] = [];
|
||||
|
||||
constructor(
|
||||
private info: DiagnosticTemplateInfo,
|
||||
private info: ng.DiagnosticTemplateInfo,
|
||||
private getExpressionScope: (path: TemplateAstPath, includeEvent: boolean) => SymbolTable) {
|
||||
super();
|
||||
this.path = new AstPath<TemplateAst>([]);
|
||||
|
@ -386,16 +375,3 @@ function hasTemplateReference(type: CompileTypeMetadata): boolean {
|
|||
function spanOf(sourceSpan: ParseSourceSpan): Span {
|
||||
return {start: sourceSpan.start.offset, end: sourceSpan.end.offset};
|
||||
}
|
||||
|
||||
|
||||
export function diagnosticInfoFromTemplateInfo(info: AstResult): DiagnosticTemplateInfo {
|
||||
return {
|
||||
fileName: info.template.fileName,
|
||||
offset: info.template.span.start,
|
||||
query: info.template.query,
|
||||
members: info.template.members,
|
||||
htmlAst: info.htmlAst,
|
||||
templateAst: info.templateAst,
|
||||
source: info.template.source,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import {createDiagnostic, Diagnostic} from './diagnostic_messages';
|
|||
import {BuiltinType, Signature, Symbol, SymbolQuery, SymbolTable} from './symbols';
|
||||
import * as ng from './types';
|
||||
|
||||
export interface ExpressionDiagnosticsContext {
|
||||
interface ExpressionDiagnosticsContext {
|
||||
inEvent?: boolean;
|
||||
}
|
||||
|
||||
|
@ -225,7 +225,7 @@ export class AstType implements AstVisitor {
|
|||
return this.anyType;
|
||||
}
|
||||
|
||||
visitImplicitReceiver(ast: ImplicitReceiver): Symbol {
|
||||
visitImplicitReceiver(_ast: ImplicitReceiver): Symbol {
|
||||
const _this = this;
|
||||
// Return a pseudo-symbol for the implicit receiver.
|
||||
// The members of the implicit receiver are what is defined by the
|
||||
|
@ -247,11 +247,11 @@ export class AstType implements AstVisitor {
|
|||
signatures(): Signature[] {
|
||||
return [];
|
||||
},
|
||||
selectSignature(types): Signature |
|
||||
selectSignature(_types): Signature |
|
||||
undefined {
|
||||
return undefined;
|
||||
},
|
||||
indexed(argument): Symbol |
|
||||
indexed(_argument): Symbol |
|
||||
undefined {
|
||||
return undefined;
|
||||
},
|
||||
|
@ -366,7 +366,7 @@ export class AstType implements AstVisitor {
|
|||
return this.getType(ast.value);
|
||||
}
|
||||
|
||||
visitQuote(ast: Quote) {
|
||||
visitQuote(_ast: Quote) {
|
||||
// The type of a quoted expression is any.
|
||||
return this.query.getBuiltinType(BuiltinType.Any);
|
||||
}
|
||||
|
|
|
@ -53,20 +53,20 @@ export function getExpressionCompletions(
|
|||
// (that is the scope of the implicit receiver) is the right scope as the user is typing the
|
||||
// beginning of an expression.
|
||||
tail.visit({
|
||||
visitBinary(ast) {},
|
||||
visitChain(ast) {},
|
||||
visitConditional(ast) {},
|
||||
visitFunctionCall(ast) {},
|
||||
visitImplicitReceiver(ast) {},
|
||||
visitInterpolation(ast) {
|
||||
visitBinary(_ast) {},
|
||||
visitChain(_ast) {},
|
||||
visitConditional(_ast) {},
|
||||
visitFunctionCall(_ast) {},
|
||||
visitImplicitReceiver(_ast) {},
|
||||
visitInterpolation(_ast) {
|
||||
result = undefined;
|
||||
},
|
||||
visitKeyedRead(ast) {},
|
||||
visitKeyedWrite(ast) {},
|
||||
visitLiteralArray(ast) {},
|
||||
visitLiteralMap(ast) {},
|
||||
visitLiteralPrimitive(ast) {},
|
||||
visitMethodCall(ast) {},
|
||||
visitKeyedRead(_ast) {},
|
||||
visitKeyedWrite(_ast) {},
|
||||
visitLiteralArray(_ast) {},
|
||||
visitLiteralMap(_ast) {},
|
||||
visitLiteralPrimitive(_ast) {},
|
||||
visitMethodCall(_ast) {},
|
||||
visitPipe(ast) {
|
||||
if (position >= ast.exp.span.end &&
|
||||
(!ast.args || !ast.args.length || position < (<AST>ast.args[0]).span.start)) {
|
||||
|
@ -74,8 +74,8 @@ export function getExpressionCompletions(
|
|||
result = templateInfo.query.getPipes();
|
||||
}
|
||||
},
|
||||
visitPrefixNot(ast) {},
|
||||
visitNonNullAssert(ast) {},
|
||||
visitPrefixNot(_ast) {},
|
||||
visitNonNullAssert(_ast) {},
|
||||
visitPropertyRead(ast) {
|
||||
const receiverType = getType(ast.receiver);
|
||||
result = receiverType ? receiverType.members() : scope;
|
||||
|
@ -84,7 +84,7 @@ export function getExpressionCompletions(
|
|||
const receiverType = getType(ast.receiver);
|
||||
result = receiverType ? receiverType.members() : scope;
|
||||
},
|
||||
visitQuote(ast) {
|
||||
visitQuote(_ast) {
|
||||
// For a quote, return the members of any (if there are any).
|
||||
result = templateInfo.query.getBuiltinType(BuiltinType.Any).members();
|
||||
},
|
||||
|
@ -127,17 +127,17 @@ export function getExpressionSymbol(
|
|||
// (that is the scope of the implicit receiver) is the right scope as the user is typing the
|
||||
// beginning of an expression.
|
||||
tail.visit({
|
||||
visitBinary(ast) {},
|
||||
visitChain(ast) {},
|
||||
visitConditional(ast) {},
|
||||
visitFunctionCall(ast) {},
|
||||
visitImplicitReceiver(ast) {},
|
||||
visitInterpolation(ast) {},
|
||||
visitKeyedRead(ast) {},
|
||||
visitKeyedWrite(ast) {},
|
||||
visitLiteralArray(ast) {},
|
||||
visitLiteralMap(ast) {},
|
||||
visitLiteralPrimitive(ast) {},
|
||||
visitBinary(_ast) {},
|
||||
visitChain(_ast) {},
|
||||
visitConditional(_ast) {},
|
||||
visitFunctionCall(_ast) {},
|
||||
visitImplicitReceiver(_ast) {},
|
||||
visitInterpolation(_ast) {},
|
||||
visitKeyedRead(_ast) {},
|
||||
visitKeyedWrite(_ast) {},
|
||||
visitLiteralArray(_ast) {},
|
||||
visitLiteralMap(_ast) {},
|
||||
visitLiteralPrimitive(_ast) {},
|
||||
visitMethodCall(ast) {
|
||||
const receiverType = getType(ast.receiver);
|
||||
symbol = receiverType && receiverType.members().get(ast.name);
|
||||
|
@ -159,8 +159,8 @@ export function getExpressionSymbol(
|
|||
};
|
||||
}
|
||||
},
|
||||
visitPrefixNot(ast) {},
|
||||
visitNonNullAssert(ast) {},
|
||||
visitPrefixNot(_ast) {},
|
||||
visitNonNullAssert(_ast) {},
|
||||
visitPropertyRead(ast) {
|
||||
const receiverType = getType(ast.receiver);
|
||||
symbol = receiverType && receiverType.members().get(ast.name);
|
||||
|
@ -177,7 +177,7 @@ export function getExpressionSymbol(
|
|||
// ^^^^^^ value; visited separately as a nested AST
|
||||
span = {start, end: start + ast.name.length};
|
||||
},
|
||||
visitQuote(ast) {},
|
||||
visitQuote(_ast) {},
|
||||
visitSafeMethodCall(ast) {
|
||||
const receiverType = getType(ast.receiver);
|
||||
symbol = receiverType && receiverType.members().get(ast.name);
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import {NgAnalyzedModules} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
import {AstResult} from './common';
|
||||
import {locateSymbols} from './locate_symbol';
|
||||
import * as ng from './types';
|
||||
import {inSpan} from './utils';
|
||||
|
@ -27,7 +26,8 @@ const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.inter
|
|||
* @param analyzedModules all NgModules in the program.
|
||||
*/
|
||||
export function getTemplateHover(
|
||||
info: AstResult, position: number, analyzedModules: NgAnalyzedModules): ts.QuickInfo|undefined {
|
||||
info: ng.AstResult, position: number, analyzedModules: NgAnalyzedModules): ts.QuickInfo|
|
||||
undefined {
|
||||
const symbolInfo = locateSymbols(info, position)[0];
|
||||
if (!symbolInfo) {
|
||||
return;
|
||||
|
|
|
@ -453,7 +453,3 @@ export function eventNames(elementName: string): string[] {
|
|||
export function propertyNames(elementName: string): string[] {
|
||||
return SchemaInformation.instance.propertiesOf(elementName);
|
||||
}
|
||||
|
||||
export function propertyType(elementName: string, propertyName: string): string {
|
||||
return SchemaInformation.instance.typeOf(elementName, propertyName);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ class LanguageServiceImpl implements ng.LanguageService {
|
|||
|
||||
getCompletionsAtPosition(
|
||||
fileName: string, position: number,
|
||||
options?: tss.GetCompletionsAtPositionOptions): tss.CompletionInfo|undefined {
|
||||
_options?: tss.GetCompletionsAtPositionOptions): tss.CompletionInfo|undefined {
|
||||
this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
||||
const ast = this.host.getTemplateAstAtPosition(fileName, position);
|
||||
if (!ast) {
|
||||
|
|
|
@ -9,17 +9,10 @@
|
|||
import {AST, Attribute, BoundDirectivePropertyAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference, VariableBinding} from '@angular/compiler';
|
||||
import * as tss from 'typescript/lib/tsserverlibrary';
|
||||
|
||||
import {AstResult} from './common';
|
||||
import {diagnosticInfoFromTemplateInfo, getExpressionScope} from './expression_diagnostics';
|
||||
import {getExpressionScope} from './expression_diagnostics';
|
||||
import {getExpressionSymbol} from './expressions';
|
||||
import {Definition, DirectiveKind, Span, Symbol} from './types';
|
||||
import {findOutputBinding, findTemplateAstAt, getPathToNodeAtPosition, inSpan, invertMap, isNarrower, offsetSpan, spanOf} from './utils';
|
||||
|
||||
export interface SymbolInfo {
|
||||
symbol: Symbol;
|
||||
span: tss.TextSpan;
|
||||
staticSymbol?: StaticSymbol;
|
||||
}
|
||||
import {AstResult, Definition, DirectiveKind, Span, Symbol, SymbolInfo} from './types';
|
||||
import {diagnosticInfoFromTemplateInfo, findOutputBinding, findTemplateAstAt, getPathToNodeAtPosition, inSpan, invertMap, isNarrower, offsetSpan, spanOf} from './utils';
|
||||
|
||||
/**
|
||||
* Traverses a template AST and locates symbol(s) at a specified position.
|
||||
|
@ -86,8 +79,8 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
|
|||
};
|
||||
ast.visit(
|
||||
{
|
||||
visitNgContent(ast) {},
|
||||
visitEmbeddedTemplate(ast) {},
|
||||
visitNgContent(_ast) {},
|
||||
visitEmbeddedTemplate(_ast) {},
|
||||
visitElement(ast) {
|
||||
const component = ast.directives.find(d => d.directive.isComponent);
|
||||
if (component) {
|
||||
|
@ -113,7 +106,7 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
|
|||
symbol = ast.value && info.template.query.getTypeSymbol(tokenReference(ast.value));
|
||||
span = spanOf(ast);
|
||||
},
|
||||
visitVariable(ast) {},
|
||||
visitVariable(_ast) {},
|
||||
visitEvent(ast) {
|
||||
if (!attributeValueSymbol(ast.handler)) {
|
||||
symbol = findOutputBinding(ast, path, info.template.query);
|
||||
|
@ -158,7 +151,7 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
|
|||
}
|
||||
}
|
||||
},
|
||||
visitText(ast) {},
|
||||
visitText(_ast) {},
|
||||
visitDirective(ast) {
|
||||
// Need to cast because 'reference' is typed as any
|
||||
staticSymbol = ast.directive.type.reference as StaticSymbol;
|
||||
|
|
|
@ -132,62 +132,3 @@ export class ExternalTemplate extends BaseTemplate {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a property assignment from the assignment value, or `undefined` if there is no
|
||||
* assignment.
|
||||
*/
|
||||
export function getPropertyAssignmentFromValue(value: ts.Node): ts.PropertyAssignment|undefined {
|
||||
if (!value.parent || !ts.isPropertyAssignment(value.parent)) {
|
||||
return;
|
||||
}
|
||||
return value.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 propAsgn 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a property assignment is on a class decorator.
|
||||
* See `getClassDeclFromDecoratorProperty`, which gets the class the decorator is applied to, for
|
||||
* more details.
|
||||
*
|
||||
* @param prop property assignment
|
||||
*/
|
||||
export function isClassDecoratorProperty(propAsgn: ts.PropertyAssignment): boolean {
|
||||
return !!getClassDeclFromDecoratorProp(propAsgn);
|
||||
}
|
||||
|
|
|
@ -6,26 +6,13 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompileDirectiveMetadata, NgAnalyzedModules, StaticSymbol} from '@angular/compiler';
|
||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CssSelector, NgAnalyzedModules, Node as HtmlAst, ParseError, Parser, StaticSymbol, TemplateAst} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
import {AstResult} from './common';
|
||||
import {BuiltinType, DeclarationKind, Definition, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols';
|
||||
|
||||
export {
|
||||
BuiltinType,
|
||||
DeclarationKind,
|
||||
Definition,
|
||||
PipeInfo,
|
||||
Pipes,
|
||||
Signature,
|
||||
Span,
|
||||
StaticSymbol,
|
||||
Symbol,
|
||||
SymbolDeclaration,
|
||||
SymbolQuery,
|
||||
SymbolTable
|
||||
};
|
||||
import {Span, Symbol, SymbolQuery, SymbolTable} from './symbols';
|
||||
|
||||
export {StaticSymbol} from '@angular/compiler';
|
||||
export {BuiltinType, Definition, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols';
|
||||
|
||||
/**
|
||||
* The information `LanguageService` needs from the `LanguageServiceHost` to describe the content of
|
||||
|
@ -67,15 +54,6 @@ export interface TemplateSource {
|
|||
readonly fileName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A sequence of template sources.
|
||||
*
|
||||
* A host type; see `LanguageServiceHost`.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export type TemplateSources = TemplateSource[]|undefined;
|
||||
|
||||
/**
|
||||
* Error information found getting declaration information
|
||||
*
|
||||
|
@ -132,15 +110,6 @@ export interface Declaration {
|
|||
readonly errors: DeclarationError[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A sequence of declarations.
|
||||
*
|
||||
* A host type; see `LanguageServiceHost`.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export type Declarations = Declaration[];
|
||||
|
||||
/**
|
||||
* The host for a `LanguageService`. This provides all the `LanguageService` requires to respond
|
||||
* to the `LanguageService` requests.
|
||||
|
@ -178,7 +147,7 @@ export interface LanguageServiceHost {
|
|||
/**
|
||||
* Returns the Angular declarations in the given file.
|
||||
*/
|
||||
getDeclarations(fileName: string): Declarations;
|
||||
getDeclarations(fileName: string): Declaration[];
|
||||
|
||||
/**
|
||||
* Return a summary of all Angular modules in the project.
|
||||
|
@ -196,45 +165,6 @@ export interface LanguageServiceHost {
|
|||
getTemplateAstAtPosition(fileName: string, position: number): AstResult|undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* An item of the completion result to be displayed by an editor.
|
||||
*
|
||||
* A `LanguageService` interface.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export interface Completion {
|
||||
/**
|
||||
* The kind of completion.
|
||||
*/
|
||||
kind: DeclarationKind;
|
||||
|
||||
/**
|
||||
* The name of the completion to be displayed
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The key to use to sort the completions for display.
|
||||
*/
|
||||
sort: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A sequence of completions.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export type Completions = Completion[];
|
||||
|
||||
/**
|
||||
* A file and span.
|
||||
*/
|
||||
export interface Location {
|
||||
fileName: string;
|
||||
span: Span;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of Angular directive. Used for QuickInfo in template.
|
||||
*/
|
||||
|
@ -312,45 +242,6 @@ export interface Diagnostic {
|
|||
message: string|DiagnosticMessageChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* A sequence of diagnostic message.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export type Diagnostics = Diagnostic[];
|
||||
|
||||
/**
|
||||
* A section of hover text. If the text is code then language should be provided.
|
||||
* Otherwise the text is assumed to be Markdown text that will be sanitized.
|
||||
*/
|
||||
export interface HoverTextSection {
|
||||
/**
|
||||
* Source code or markdown text describing the symbol a the hover location.
|
||||
*/
|
||||
readonly text: string;
|
||||
|
||||
/**
|
||||
* The language of the source if `text` is a source code fragment.
|
||||
*/
|
||||
readonly language?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hover information for a symbol at the hover location.
|
||||
*/
|
||||
export interface Hover {
|
||||
/**
|
||||
* The hover text to display for the symbol at the hover location. If the text includes
|
||||
* source code, the section will specify which language it should be interpreted as.
|
||||
*/
|
||||
readonly text: HoverTextSection[];
|
||||
|
||||
/**
|
||||
* The span of source the hover covers.
|
||||
*/
|
||||
readonly span: Span;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of an Angular language service created by `createLanguageService()`.
|
||||
*
|
||||
|
@ -364,3 +255,38 @@ export type LanguageService = Pick<
|
|||
ts.LanguageService,
|
||||
'getCompletionsAtPosition'|'getDefinitionAndBoundSpan'|'getQuickInfoAtPosition'|
|
||||
'getSemanticDiagnostics'>;
|
||||
|
||||
/** Information about an Angular template AST. */
|
||||
export interface AstResult {
|
||||
htmlAst: HtmlAst[];
|
||||
templateAst: TemplateAst[];
|
||||
directive: CompileDirectiveMetadata;
|
||||
directives: CompileDirectiveSummary[];
|
||||
pipes: CompilePipeSummary[];
|
||||
parseErrors?: ParseError[];
|
||||
expressionParser: Parser;
|
||||
template: TemplateSource;
|
||||
}
|
||||
|
||||
/** Information about a directive's selectors. */
|
||||
export type SelectorInfo = {
|
||||
selectors: CssSelector[],
|
||||
map: Map<CssSelector, CompileDirectiveSummary>
|
||||
};
|
||||
|
||||
export interface SymbolInfo {
|
||||
symbol: Symbol;
|
||||
span: ts.TextSpan;
|
||||
staticSymbol?: StaticSymbol;
|
||||
}
|
||||
|
||||
/** TODO: this should probably be merged with AstResult */
|
||||
export interface DiagnosticTemplateInfo {
|
||||
fileName?: string;
|
||||
offset: number;
|
||||
query: SymbolQuery;
|
||||
members: SymbolTable;
|
||||
htmlAst: HtmlAst[];
|
||||
templateAst: TemplateAst[];
|
||||
source: string;
|
||||
}
|
||||
|
|
|
@ -6,16 +6,15 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {analyzeNgModules, AotSummaryResolver, CompileDirectiveSummary, CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompilerConfig, createOfflineCompileUrlResolver, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, FormattedError, FormattedMessageChain, HtmlParser, I18NHtmlParser, isFormattedError, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, Parser, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser} from '@angular/compiler';
|
||||
import {analyzeNgModules, AotSummaryResolver, CompileDirectiveSummary, CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompilerConfig, createOfflineCompileUrlResolver, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, FormattedError, FormattedMessageChain, HtmlParser, isFormattedError, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, Parser, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser} from '@angular/compiler';
|
||||
import {SchemaMetadata, ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||
import * as tss from 'typescript/lib/tsserverlibrary';
|
||||
|
||||
import {AstResult} from './common';
|
||||
import {createLanguageService} from './language_service';
|
||||
import {ReflectorHost} from './reflector_host';
|
||||
import {ExternalTemplate, getClassDeclFromDecoratorProp, getPropertyAssignmentFromValue, InlineTemplate} from './template';
|
||||
import {Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types';
|
||||
import {findTightestNode, getDirectiveClassLike} from './utils';
|
||||
import {ExternalTemplate, InlineTemplate} from './template';
|
||||
import {AstResult, Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types';
|
||||
import {findTightestNode, getClassDeclFromDecoratorProp, getDirectiveClassLike, getPropertyAssignmentFromValue} from './utils';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -44,7 +43,7 @@ export class DummyHtmlParser extends HtmlParser {
|
|||
* Avoid loading resources in the language servcie by using a dummy loader.
|
||||
*/
|
||||
export class DummyResourceLoader extends ResourceLoader {
|
||||
get(url: string): Promise<string> {
|
||||
get(_url: string): Promise<string> {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
}
|
||||
|
@ -78,10 +77,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
readonly tsLsHost: tss.LanguageServiceHost, private readonly tsLS: tss.LanguageService) {
|
||||
this.summaryResolver = new AotSummaryResolver(
|
||||
{
|
||||
loadSummary(filePath: string) {
|
||||
loadSummary(_filePath: string) {
|
||||
return null;
|
||||
},
|
||||
isSourceFile(sourceFilePath: string) {
|
||||
isSourceFile(_sourceFilePath: string) {
|
||||
return true;
|
||||
},
|
||||
toSummaryFileName(sourceFilePath: string) {
|
||||
|
@ -172,7 +171,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
this.resolver.clearCache();
|
||||
|
||||
const analyzeHost = {
|
||||
isSourceFile(filePath: string) {
|
||||
isSourceFile(_filePath: string) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -57,8 +57,14 @@ export function getClassMembersFromDeclaration(
|
|||
return new TypeWrapper(type, {node: source, program, checker}).members();
|
||||
}
|
||||
|
||||
export function getClassFromStaticSymbol(
|
||||
program: ts.Program, type: StaticSymbol): ts.ClassDeclaration|undefined {
|
||||
export function getPipesTable(
|
||||
source: ts.SourceFile, program: ts.Program, checker: ts.TypeChecker,
|
||||
pipes: CompilePipeSummary[]): SymbolTable {
|
||||
return new PipesTable(pipes, {program, checker, node: source});
|
||||
}
|
||||
|
||||
function getClassFromStaticSymbol(program: ts.Program, type: StaticSymbol): ts.ClassDeclaration|
|
||||
undefined {
|
||||
const source = program.getSourceFile(type.filePath);
|
||||
if (source) {
|
||||
return ts.forEachChild(source, child => {
|
||||
|
@ -74,12 +80,6 @@ export function getClassFromStaticSymbol(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
export function getPipesTable(
|
||||
source: ts.SourceFile, program: ts.Program, checker: ts.TypeChecker,
|
||||
pipes: CompilePipeSummary[]): SymbolTable {
|
||||
return new PipesTable(pipes, {program, checker, node: source});
|
||||
}
|
||||
|
||||
class TypeScriptSymbolQuery implements SymbolQuery {
|
||||
private typeCache = new Map<BuiltinType, Symbol>();
|
||||
private pipesCache: SymbolTable|undefined;
|
||||
|
@ -123,7 +123,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
|
|||
return result || this.getBuiltinType(BuiltinType.Any);
|
||||
}
|
||||
|
||||
getArrayType(type: Symbol): Symbol {
|
||||
getArrayType(_type: Symbol): Symbol {
|
||||
return this.getBuiltinType(BuiltinType.Any);
|
||||
}
|
||||
|
||||
|
@ -222,7 +222,7 @@ function signaturesOf(type: ts.Type, context: TypeContext): Signature[] {
|
|||
return type.getCallSignatures().map(s => new SignatureWrapper(s, context));
|
||||
}
|
||||
|
||||
function selectSignature(type: ts.Type, context: TypeContext, types: Symbol[]): Signature|
|
||||
function selectSignature(type: ts.Type, context: TypeContext, _types: Symbol[]): Signature|
|
||||
undefined {
|
||||
// TODO: Do a better job of selecting the right signature. TypeScript does not currently support a
|
||||
// Type Relationship API (see https://github.com/angular/vscode-ng-language-service/issues/143).
|
||||
|
@ -404,7 +404,7 @@ class SymbolWrapper implements Symbol {
|
|||
return selectSignature(this.tsType, this.context, types);
|
||||
}
|
||||
|
||||
indexed(argument: Symbol): Symbol|undefined {
|
||||
indexed(_argument: Symbol): Symbol|undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -475,7 +475,7 @@ class DeclaredSymbol implements Symbol {
|
|||
return this.type.typeArguments();
|
||||
}
|
||||
|
||||
indexed(argument: Symbol): Symbol|undefined {
|
||||
indexed(_argument: Symbol): Symbol|undefined {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
@ -504,7 +504,7 @@ class SignatureResultOverride implements Signature {
|
|||
}
|
||||
}
|
||||
|
||||
export function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable {
|
||||
function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable {
|
||||
// ∀ Typescript version >= 2.2, `SymbolTable` is implemented as an ES6 `Map`
|
||||
const result = new Map<string, ts.Symbol>();
|
||||
for (const symbol of symbols) {
|
||||
|
@ -548,8 +548,7 @@ class SymbolTableWrapper implements SymbolTable {
|
|||
* @param context program context
|
||||
* @param type original TypeScript type of entity owning the symbols, if known
|
||||
*/
|
||||
constructor(
|
||||
symbols: ts.SymbolTable|ts.Symbol[], private context: TypeContext, private type?: ts.Type) {
|
||||
constructor(symbols: ts.SymbolTable|ts.Symbol[], private context: TypeContext, type?: ts.Type) {
|
||||
symbols = symbols || [];
|
||||
|
||||
if (Array.isArray(symbols)) {
|
||||
|
@ -727,7 +726,7 @@ class PipeSymbol implements Symbol {
|
|||
return signature;
|
||||
}
|
||||
|
||||
indexed(argument: Symbol): Symbol|undefined {
|
||||
indexed(_argument: Symbol): Symbol|undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -786,10 +785,10 @@ function findClassSymbolInContext(type: StaticSymbol, context: TypeContext): ts.
|
|||
|
||||
class EmptyTable implements SymbolTable {
|
||||
public readonly size: number = 0;
|
||||
get(key: string): Symbol|undefined {
|
||||
get(_key: string): Symbol|undefined {
|
||||
return undefined;
|
||||
}
|
||||
has(key: string): boolean {
|
||||
has(_key: string): boolean {
|
||||
return false;
|
||||
}
|
||||
values(): Symbol[] {
|
||||
|
|
|
@ -9,16 +9,15 @@
|
|||
import {AstPath, BoundEventAst, CompileDirectiveSummary, CompileTypeMetadata, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, HtmlAstPath, identifierName, Identifiers, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, RecursiveVisitor, TemplateAst, TemplateAstPath, templateVisitAll, visitAll} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AstResult, SelectorInfo} from './common';
|
||||
import {Span, Symbol, SymbolQuery} from './types';
|
||||
import {AstResult, DiagnosticTemplateInfo, SelectorInfo, Span, Symbol, SymbolQuery} from './types';
|
||||
|
||||
export interface SpanHolder {
|
||||
interface SpanHolder {
|
||||
sourceSpan: ParseSourceSpan;
|
||||
endSourceSpan?: ParseSourceSpan|null;
|
||||
children?: SpanHolder[];
|
||||
}
|
||||
|
||||
export function isParseSourceSpan(value: any): value is ParseSourceSpan {
|
||||
function isParseSourceSpan(value: any): value is ParseSourceSpan {
|
||||
return value && !!value.start;
|
||||
}
|
||||
|
||||
|
@ -80,14 +79,16 @@ export function getSelectors(info: AstResult): SelectorInfo {
|
|||
return {selectors: results, map};
|
||||
}
|
||||
|
||||
export function isTypescriptVersion(low: string, high?: string) {
|
||||
const version = ts.version;
|
||||
|
||||
if (version.substring(0, low.length) < low) return false;
|
||||
|
||||
if (high && (version.substring(0, high.length) > high)) return false;
|
||||
|
||||
return true;
|
||||
export function diagnosticInfoFromTemplateInfo(info: AstResult): DiagnosticTemplateInfo {
|
||||
return {
|
||||
fileName: info.template.fileName,
|
||||
offset: info.template.span.start,
|
||||
query: info.template.query,
|
||||
members: info.template.members,
|
||||
htmlAst: info.htmlAst,
|
||||
templateAst: info.templateAst,
|
||||
source: info.template.source,
|
||||
};
|
||||
}
|
||||
|
||||
export function findTemplateAstAt(ast: TemplateAst[], position: number): TemplateAstPath {
|
||||
|
@ -276,3 +277,62 @@ export function findOutputBinding(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a property assignment from the assignment value, or `undefined` if there is no
|
||||
* assignment.
|
||||
*/
|
||||
export function getPropertyAssignmentFromValue(value: ts.Node): ts.PropertyAssignment|undefined {
|
||||
if (!value.parent || !ts.isPropertyAssignment(value.parent)) {
|
||||
return;
|
||||
}
|
||||
return value.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 propAsgn 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a property assignment is on a class decorator.
|
||||
* See `getClassDeclFromDecoratorProperty`, which gets the class the decorator is applied to, for
|
||||
* more details.
|
||||
*
|
||||
* @param prop property assignment
|
||||
*/
|
||||
export function isClassDecoratorProperty(propAsgn: ts.PropertyAssignment): boolean {
|
||||
return !!getClassDeclFromDecoratorProp(propAsgn);
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Entry point for all public APIs of the common package.
|
||||
*/
|
||||
|
||||
import {Version} from '@angular/core';
|
||||
|
||||
export const VERSION = new Version('0.0.0-PLACEHOLDER');
|
|
@ -47,7 +47,6 @@ ts_library(
|
|||
"html_info_spec.ts",
|
||||
"language_service_spec.ts",
|
||||
"reflector_host_spec.ts",
|
||||
"template_spec.ts",
|
||||
"ts_plugin_spec.ts",
|
||||
"typescript_host_spec.ts",
|
||||
"utils_spec.ts",
|
||||
|
|
|
@ -14,7 +14,7 @@ import * as fs from 'fs';
|
|||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {DiagnosticTemplateInfo} from '../src/expression_diagnostics';
|
||||
import {DiagnosticTemplateInfo} from '../src/types';
|
||||
import {getClassMembers, getPipesTable, getSymbolQuery} from '../src/typescript_symbols';
|
||||
|
||||
const realFiles = new Map<string, string>();
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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';
|
||||
import {getClassDeclFromDecoratorProp} from '../src/template';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('getClassDeclFromTemplateNode', () => {
|
||||
it('should find class declaration in syntax-only mode', () => {
|
||||
const sourceFile = ts.createSourceFile(
|
||||
'foo.ts', `
|
||||
@Component({
|
||||
template: '<div></div>'
|
||||
})
|
||||
class MyComponent {}`,
|
||||
ts.ScriptTarget.ES2015, true /* setParentNodes */);
|
||||
function visit(node: ts.Node): ts.ClassDeclaration|undefined {
|
||||
if (ts.isPropertyAssignment(node)) {
|
||||
return getClassDeclFromDecoratorProp(node);
|
||||
}
|
||||
return node.forEachChild(visit);
|
||||
}
|
||||
const classDecl = sourceFile.forEachChild(visit);
|
||||
expect(classDecl).toBeTruthy();
|
||||
expect(classDecl!.kind).toBe(ts.SyntaxKind.ClassDeclaration);
|
||||
expect((classDecl as ts.ClassDeclaration).name!.text).toBe('MyComponent');
|
||||
});
|
||||
|
||||
|
||||
it('should return class declaration for AppComponent', () => {
|
||||
const host = new MockTypescriptHost(['/app/app.component.ts']);
|
||||
const tsLS = ts.createLanguageService(host);
|
||||
const sourceFile = tsLS.getProgram()!.getSourceFile('/app/app.component.ts');
|
||||
expect(sourceFile).toBeTruthy();
|
||||
const classDecl = sourceFile!.forEachChild(function visit(node): ts.Node|undefined {
|
||||
if (ts.isPropertyAssignment(node)) {
|
||||
return getClassDeclFromDecoratorProp(node);
|
||||
}
|
||||
return node.forEachChild(visit);
|
||||
});
|
||||
expect(classDecl).toBeTruthy();
|
||||
expect(ts.isClassDeclaration(classDecl!)).toBe(true);
|
||||
expect((classDecl as ts.ClassDeclaration).name!.text).toBe('AppComponent');
|
||||
});
|
||||
});
|
|
@ -11,7 +11,7 @@ import {ReflectorHost} from '@angular/language-service/src/reflector_host';
|
|||
import * as ts from 'typescript';
|
||||
|
||||
import {BuiltinType, Symbol, SymbolQuery, SymbolTable} from '../src/symbols';
|
||||
import {getSymbolQuery, toSymbolTableFactory} from '../src/typescript_symbols';
|
||||
import {getSymbolQuery} from '../src/typescript_symbols';
|
||||
|
||||
import {DiagnosticContext, MockLanguageServiceHost} from './mocks';
|
||||
|
||||
|
@ -79,14 +79,6 @@ describe('symbol query', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('toSymbolTableFactory(tsVersion)', () => {
|
||||
it('should return a Map for versions of TypeScript >= 2.2', () => {
|
||||
const a = {name: 'a'} as ts.Symbol;
|
||||
const b = {name: 'b'} as ts.Symbol;
|
||||
expect(toSymbolTableFactory([a, b]) instanceof Map).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
function appComponentSource(template: string): string {
|
||||
return `
|
||||
import {Component} from '@angular/core';
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
import * as ng from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {getDirectiveClassLike, getPathToNodeAtPosition} from '../src/utils';
|
||||
import {getClassDeclFromDecoratorProp, getDirectiveClassLike, getPathToNodeAtPosition} from '../src/utils';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('getDirectiveClassLike', () => {
|
||||
it('should return a directive class', () => {
|
||||
|
@ -80,3 +81,42 @@ describe('getPathToNodeAtPosition', () => {
|
|||
expect(path.tail instanceof ng.Attribute).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getClassDeclFromTemplateNode', () => {
|
||||
it('should find class declaration in syntax-only mode', () => {
|
||||
const sourceFile = ts.createSourceFile(
|
||||
'foo.ts', `
|
||||
@Component({
|
||||
template: '<div></div>'
|
||||
})
|
||||
class MyComponent {}`,
|
||||
ts.ScriptTarget.ES2015, true /* setParentNodes */);
|
||||
function visit(node: ts.Node): ts.ClassDeclaration|undefined {
|
||||
if (ts.isPropertyAssignment(node)) {
|
||||
return getClassDeclFromDecoratorProp(node);
|
||||
}
|
||||
return node.forEachChild(visit);
|
||||
}
|
||||
const classDecl = sourceFile.forEachChild(visit);
|
||||
expect(classDecl).toBeTruthy();
|
||||
expect(classDecl!.kind).toBe(ts.SyntaxKind.ClassDeclaration);
|
||||
expect((classDecl as ts.ClassDeclaration).name!.text).toBe('MyComponent');
|
||||
});
|
||||
|
||||
|
||||
it('should return class declaration for AppComponent', () => {
|
||||
const host = new MockTypescriptHost(['/app/app.component.ts']);
|
||||
const tsLS = ts.createLanguageService(host);
|
||||
const sourceFile = tsLS.getProgram()!.getSourceFile('/app/app.component.ts');
|
||||
expect(sourceFile).toBeTruthy();
|
||||
const classDecl = sourceFile!.forEachChild(function visit(node): ts.Node|undefined {
|
||||
if (ts.isPropertyAssignment(node)) {
|
||||
return getClassDeclFromDecoratorProp(node);
|
||||
}
|
||||
return node.forEachChild(visit);
|
||||
});
|
||||
expect(classDecl).toBeTruthy();
|
||||
expect(ts.isClassDeclaration(classDecl!)).toBe(true);
|
||||
expect((classDecl as ts.ClassDeclaration).name!.text).toBe('AppComponent');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue