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/directives/validators.ts",
|
||||||
"packages/forms/src/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/completions.ts",
|
||||||
"packages/language-service/src/template.ts",
|
"packages/language-service/src/template.ts",
|
||||||
|
|
|
@ -15,6 +15,5 @@
|
||||||
*/
|
*/
|
||||||
export {createLanguageService} from './src/language_service';
|
export {createLanguageService} from './src/language_service';
|
||||||
export * from './src/ts_plugin';
|
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 {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 {$$, $_, isAsciiLetter, isDigit} from '@angular/compiler/src/chars';
|
||||||
|
|
||||||
import {ATTR, getBindingDescriptor} from './binding_utils';
|
import {ATTR, getBindingDescriptor} from './binding_utils';
|
||||||
import {AstResult} from './common';
|
import {getExpressionScope} from './expression_diagnostics';
|
||||||
import {diagnosticInfoFromTemplateInfo, getExpressionScope} from './expression_diagnostics';
|
|
||||||
import {getExpressionCompletions} from './expressions';
|
import {getExpressionCompletions} from './expressions';
|
||||||
import {attributeNames, elementNames, eventNames, propertyNames} from './html_info';
|
import {attributeNames, elementNames, eventNames, propertyNames} from './html_info';
|
||||||
import {InlineTemplate} from './template';
|
import {InlineTemplate} from './template';
|
||||||
import * as ng from './types';
|
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> =
|
const HIDDEN_HTML_ELEMENTS: ReadonlySet<string> =
|
||||||
new Set(['html', 'script', 'noscript', 'base', 'body', 'title', 'head', 'link']);
|
new Set(['html', 'script', 'noscript', 'base', 'body', 'title', 'head', 'link']);
|
||||||
|
@ -56,7 +55,7 @@ function isIdentifierPart(code: number) {
|
||||||
* `position`, nothing is returned.
|
* `position`, nothing is returned.
|
||||||
*/
|
*/
|
||||||
function getBoundedWordSpan(
|
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 {template} = templateInfo;
|
||||||
const templateSrc = template.source;
|
const templateSrc = template.source;
|
||||||
|
|
||||||
|
@ -127,7 +126,7 @@ function getBoundedWordSpan(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTemplateCompletions(
|
export function getTemplateCompletions(
|
||||||
templateInfo: AstResult, position: number): ng.CompletionEntry[] {
|
templateInfo: ng.AstResult, position: number): ng.CompletionEntry[] {
|
||||||
let result: ng.CompletionEntry[] = [];
|
let result: ng.CompletionEntry[] = [];
|
||||||
const {htmlAst, template} = templateInfo;
|
const {htmlAst, template} = templateInfo;
|
||||||
// The templateNode starts at the delimiter character so we add 1 to skip it.
|
// 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 attr = path.tail;
|
||||||
const elem = path.parentOf(attr);
|
const elem = path.parentOf(attr);
|
||||||
if (!(attr instanceof Attribute) || !(elem instanceof Element)) {
|
if (!(attr instanceof Attribute) || !(elem instanceof Element)) {
|
||||||
|
@ -258,7 +257,7 @@ function attributeCompletions(info: AstResult, path: AstPath<HtmlAst>): ng.Compl
|
||||||
}
|
}
|
||||||
|
|
||||||
function attributeCompletionsForElement(
|
function attributeCompletionsForElement(
|
||||||
info: AstResult, elementName: string): ng.CompletionEntry[] {
|
info: ng.AstResult, elementName: string): ng.CompletionEntry[] {
|
||||||
const results: ng.CompletionEntry[] = [];
|
const results: ng.CompletionEntry[] = [];
|
||||||
|
|
||||||
if (info.template instanceof InlineTemplate) {
|
if (info.template instanceof InlineTemplate) {
|
||||||
|
@ -292,7 +291,8 @@ function attributeCompletionsForElement(
|
||||||
* @param info Object that contains the template AST
|
* @param info Object that contains the template AST
|
||||||
* @param htmlPath Path to the HTML node
|
* @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.
|
// Find the corresponding Template AST path.
|
||||||
const templatePath = findTemplateAstAt(info.templateAst, htmlPath.position);
|
const templatePath = findTemplateAstAt(info.templateAst, htmlPath.position);
|
||||||
const visitor = new ExpressionVisitor(info, htmlPath.position, () => {
|
const visitor = new ExpressionVisitor(info, htmlPath.position, () => {
|
||||||
|
@ -334,7 +334,7 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C
|
||||||
return visitor.results;
|
return visitor.results;
|
||||||
}
|
}
|
||||||
|
|
||||||
function elementCompletions(info: AstResult): ng.CompletionEntry[] {
|
function elementCompletions(info: ng.AstResult): ng.CompletionEntry[] {
|
||||||
const results: ng.CompletionEntry[] = [...ANGULAR_ELEMENTS];
|
const results: ng.CompletionEntry[] = [...ANGULAR_ELEMENTS];
|
||||||
|
|
||||||
if (info.template instanceof InlineTemplate) {
|
if (info.template instanceof InlineTemplate) {
|
||||||
|
@ -380,7 +380,7 @@ function entityCompletions(value: string, position: number): ng.CompletionEntry[
|
||||||
return result;
|
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.
|
// Look for an interpolation in at the position.
|
||||||
const templatePath = findTemplateAstAt(info.templateAst, position);
|
const templatePath = findTemplateAstAt(info.templateAst, position);
|
||||||
if (!templatePath.tail) {
|
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
|
// code checks for this case and returns element completions if it is detected or undefined
|
||||||
// if it is not.
|
// if it is not.
|
||||||
function voidElementAttributeCompletions(
|
function voidElementAttributeCompletions(
|
||||||
info: AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] {
|
info: ng.AstResult, path: AstPath<HtmlAst>): ng.CompletionEntry[] {
|
||||||
const tail = path.tail;
|
const tail = path.tail;
|
||||||
if (tail instanceof Text) {
|
if (tail instanceof Text) {
|
||||||
const match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/);
|
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>();
|
private readonly completions = new Map<string, ng.CompletionEntry>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly info: AstResult, private readonly position: number,
|
private readonly info: ng.AstResult, private readonly position: number,
|
||||||
private readonly getExpressionScope: () => ng.SymbolTable) {
|
private readonly getExpressionScope: () => ng.SymbolTable) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -619,7 +619,7 @@ interface AngularAttributes {
|
||||||
* @param info
|
* @param info
|
||||||
* @param elementName
|
* @param elementName
|
||||||
*/
|
*/
|
||||||
function angularAttributes(info: AstResult, elementName: string): AngularAttributes {
|
function angularAttributes(info: ng.AstResult, elementName: string): AngularAttributes {
|
||||||
const {selectors, map: selectorMap} = getSelectors(info);
|
const {selectors, map: selectorMap} = getSelectors(info);
|
||||||
const templateRefs = new Set<string>();
|
const templateRefs = new Set<string>();
|
||||||
const inputs = new Set<string>();
|
const inputs = new Set<string>();
|
||||||
|
|
|
@ -8,11 +8,10 @@
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript'; // used as value and is provided at runtime
|
import * as ts from 'typescript'; // used as value and is provided at runtime
|
||||||
import {AstResult} from './common';
|
|
||||||
import {locateSymbols} from './locate_symbol';
|
import {locateSymbols} from './locate_symbol';
|
||||||
import {getPropertyAssignmentFromValue, isClassDecoratorProperty} from './template';
|
import {AstResult, Span} from './types';
|
||||||
import {Span} from './types';
|
import {findTightestNode, getPropertyAssignmentFromValue, isClassDecoratorProperty} from './utils';
|
||||||
import {findTightestNode} from './utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert Angular Span to TypeScript TextSpan. Angular Span has 'start' and
|
* 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 path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {AstResult} from './common';
|
|
||||||
import {createDiagnostic, Diagnostic} from './diagnostic_messages';
|
import {createDiagnostic, Diagnostic} from './diagnostic_messages';
|
||||||
import {getTemplateExpressionDiagnostics} from './expression_diagnostics';
|
import {getTemplateExpressionDiagnostics} from './expression_diagnostics';
|
||||||
import * as ng from './types';
|
import * as ng from './types';
|
||||||
import {TypeScriptServiceHost} from './typescript_host';
|
import {TypeScriptServiceHost} from './typescript_host';
|
||||||
import {findPropertyValueOfType, findTightestNode, offsetSpan, spanOf} from './utils';
|
import {findPropertyValueOfType, findTightestNode, offsetSpan, spanOf} from './utils';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return diagnostic information for the parsed AST of the template.
|
* Return diagnostic information for the parsed AST of the template.
|
||||||
* @param ast contains HTML and template AST
|
* @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;
|
const {parseErrors, templateAst, htmlAst, template} = ast;
|
||||||
if (parseErrors && parseErrors.length) {
|
if (parseErrors && parseErrors.length) {
|
||||||
return parseErrors.map(e => {
|
return parseErrors.map(e => {
|
||||||
|
|
|
@ -6,33 +6,22 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* 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 {createDiagnostic, Diagnostic} from './diagnostic_messages';
|
||||||
import {AstType} from './expression_type';
|
import {AstType} from './expression_type';
|
||||||
import {BuiltinType, Definition, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols';
|
import {BuiltinType, Definition, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols';
|
||||||
import * as ng from './types';
|
import * as ng from './types';
|
||||||
import {findOutputBinding, getPathToNodeAtPosition} from './utils';
|
import {findOutputBinding, getPathToNodeAtPosition} from './utils';
|
||||||
|
|
||||||
export interface DiagnosticTemplateInfo {
|
export function getTemplateExpressionDiagnostics(info: ng.DiagnosticTemplateInfo): ng.Diagnostic[] {
|
||||||
fileName?: string;
|
|
||||||
offset: number;
|
|
||||||
query: SymbolQuery;
|
|
||||||
members: SymbolTable;
|
|
||||||
htmlAst: Node[];
|
|
||||||
templateAst: TemplateAst[];
|
|
||||||
source: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTemplateExpressionDiagnostics(info: DiagnosticTemplateInfo): ng.Diagnostic[] {
|
|
||||||
const visitor = new ExpressionDiagnosticsVisitor(
|
const visitor = new ExpressionDiagnosticsVisitor(
|
||||||
info, (path: TemplateAstPath) => getExpressionScope(info, path));
|
info, (path: TemplateAstPath) => getExpressionScope(info, path));
|
||||||
templateVisitAll(visitor, info.templateAst);
|
templateVisitAll(visitor, info.templateAst);
|
||||||
return visitor.diagnostics;
|
return visitor.diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
|
function getReferences(info: ng.DiagnosticTemplateInfo): SymbolDeclaration[] {
|
||||||
const result: SymbolDeclaration[] = [];
|
const result: SymbolDeclaration[] = [];
|
||||||
|
|
||||||
function processReferences(references: ReferenceAst[]) {
|
function processReferences(references: ReferenceAst[]) {
|
||||||
|
@ -68,7 +57,7 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefinitionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined {
|
function getDefinitionOf(info: ng.DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined {
|
||||||
if (info.fileName) {
|
if (info.fileName) {
|
||||||
const templateOffset = info.offset;
|
const templateOffset = info.offset;
|
||||||
return [{
|
return [{
|
||||||
|
@ -88,7 +77,7 @@ function getDefinitionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Defini
|
||||||
* @param path template AST path
|
* @param path template AST path
|
||||||
*/
|
*/
|
||||||
function getVarDeclarations(
|
function getVarDeclarations(
|
||||||
info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration[] {
|
info: ng.DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration[] {
|
||||||
const results: SymbolDeclaration[] = [];
|
const results: SymbolDeclaration[] = [];
|
||||||
for (let current = path.head; current; current = path.childOf(current)) {
|
for (let current = path.head; current; current = path.childOf(current)) {
|
||||||
if (!(current instanceof EmbeddedTemplateAst)) {
|
if (!(current instanceof EmbeddedTemplateAst)) {
|
||||||
|
@ -154,7 +143,7 @@ function getVariableTypeFromDirectiveContext(
|
||||||
* @param templateElement
|
* @param templateElement
|
||||||
*/
|
*/
|
||||||
function refinedVariableType(
|
function refinedVariableType(
|
||||||
value: string, mergedTable: SymbolTable, info: DiagnosticTemplateInfo,
|
value: string, mergedTable: SymbolTable, info: ng.DiagnosticTemplateInfo,
|
||||||
templateElement: EmbeddedTemplateAst): Symbol {
|
templateElement: EmbeddedTemplateAst): Symbol {
|
||||||
if (value === '$implicit') {
|
if (value === '$implicit') {
|
||||||
// Special case: ngFor directive
|
// Special case: ngFor directive
|
||||||
|
@ -206,7 +195,7 @@ function refinedVariableType(
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEventDeclaration(
|
function getEventDeclaration(
|
||||||
info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration|undefined {
|
info: ng.DiagnosticTemplateInfo, path: TemplateAstPath): SymbolDeclaration|undefined {
|
||||||
const event = path.tail;
|
const event = path.tail;
|
||||||
if (!(event instanceof BoundEventAst)) {
|
if (!(event instanceof BoundEventAst)) {
|
||||||
// No event available in this context.
|
// No event available in this context.
|
||||||
|
@ -241,7 +230,7 @@ function getEventDeclaration(
|
||||||
* derived for.
|
* derived for.
|
||||||
*/
|
*/
|
||||||
export function getExpressionScope(
|
export function getExpressionScope(
|
||||||
info: DiagnosticTemplateInfo, path: TemplateAstPath): SymbolTable {
|
info: ng.DiagnosticTemplateInfo, path: TemplateAstPath): SymbolTable {
|
||||||
let result = info.members;
|
let result = info.members;
|
||||||
const references = getReferences(info);
|
const references = getReferences(info);
|
||||||
const variables = getVarDeclarations(info, path);
|
const variables = getVarDeclarations(info, path);
|
||||||
|
@ -262,7 +251,7 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
|
||||||
diagnostics: ng.Diagnostic[] = [];
|
diagnostics: ng.Diagnostic[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private info: DiagnosticTemplateInfo,
|
private info: ng.DiagnosticTemplateInfo,
|
||||||
private getExpressionScope: (path: TemplateAstPath, includeEvent: boolean) => SymbolTable) {
|
private getExpressionScope: (path: TemplateAstPath, includeEvent: boolean) => SymbolTable) {
|
||||||
super();
|
super();
|
||||||
this.path = new AstPath<TemplateAst>([]);
|
this.path = new AstPath<TemplateAst>([]);
|
||||||
|
@ -386,16 +375,3 @@ function hasTemplateReference(type: CompileTypeMetadata): boolean {
|
||||||
function spanOf(sourceSpan: ParseSourceSpan): Span {
|
function spanOf(sourceSpan: ParseSourceSpan): Span {
|
||||||
return {start: sourceSpan.start.offset, end: sourceSpan.end.offset};
|
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 {BuiltinType, Signature, Symbol, SymbolQuery, SymbolTable} from './symbols';
|
||||||
import * as ng from './types';
|
import * as ng from './types';
|
||||||
|
|
||||||
export interface ExpressionDiagnosticsContext {
|
interface ExpressionDiagnosticsContext {
|
||||||
inEvent?: boolean;
|
inEvent?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ export class AstType implements AstVisitor {
|
||||||
return this.anyType;
|
return this.anyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitImplicitReceiver(ast: ImplicitReceiver): Symbol {
|
visitImplicitReceiver(_ast: ImplicitReceiver): Symbol {
|
||||||
const _this = this;
|
const _this = this;
|
||||||
// Return a pseudo-symbol for the implicit receiver.
|
// Return a pseudo-symbol for the implicit receiver.
|
||||||
// The members of the implicit receiver are what is defined by the
|
// The members of the implicit receiver are what is defined by the
|
||||||
|
@ -247,11 +247,11 @@ export class AstType implements AstVisitor {
|
||||||
signatures(): Signature[] {
|
signatures(): Signature[] {
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
selectSignature(types): Signature |
|
selectSignature(_types): Signature |
|
||||||
undefined {
|
undefined {
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
indexed(argument): Symbol |
|
indexed(_argument): Symbol |
|
||||||
undefined {
|
undefined {
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
@ -366,7 +366,7 @@ export class AstType implements AstVisitor {
|
||||||
return this.getType(ast.value);
|
return this.getType(ast.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitQuote(ast: Quote) {
|
visitQuote(_ast: Quote) {
|
||||||
// The type of a quoted expression is any.
|
// The type of a quoted expression is any.
|
||||||
return this.query.getBuiltinType(BuiltinType.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
|
// (that is the scope of the implicit receiver) is the right scope as the user is typing the
|
||||||
// beginning of an expression.
|
// beginning of an expression.
|
||||||
tail.visit({
|
tail.visit({
|
||||||
visitBinary(ast) {},
|
visitBinary(_ast) {},
|
||||||
visitChain(ast) {},
|
visitChain(_ast) {},
|
||||||
visitConditional(ast) {},
|
visitConditional(_ast) {},
|
||||||
visitFunctionCall(ast) {},
|
visitFunctionCall(_ast) {},
|
||||||
visitImplicitReceiver(ast) {},
|
visitImplicitReceiver(_ast) {},
|
||||||
visitInterpolation(ast) {
|
visitInterpolation(_ast) {
|
||||||
result = undefined;
|
result = undefined;
|
||||||
},
|
},
|
||||||
visitKeyedRead(ast) {},
|
visitKeyedRead(_ast) {},
|
||||||
visitKeyedWrite(ast) {},
|
visitKeyedWrite(_ast) {},
|
||||||
visitLiteralArray(ast) {},
|
visitLiteralArray(_ast) {},
|
||||||
visitLiteralMap(ast) {},
|
visitLiteralMap(_ast) {},
|
||||||
visitLiteralPrimitive(ast) {},
|
visitLiteralPrimitive(_ast) {},
|
||||||
visitMethodCall(ast) {},
|
visitMethodCall(_ast) {},
|
||||||
visitPipe(ast) {
|
visitPipe(ast) {
|
||||||
if (position >= ast.exp.span.end &&
|
if (position >= ast.exp.span.end &&
|
||||||
(!ast.args || !ast.args.length || position < (<AST>ast.args[0]).span.start)) {
|
(!ast.args || !ast.args.length || position < (<AST>ast.args[0]).span.start)) {
|
||||||
|
@ -74,8 +74,8 @@ export function getExpressionCompletions(
|
||||||
result = templateInfo.query.getPipes();
|
result = templateInfo.query.getPipes();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visitPrefixNot(ast) {},
|
visitPrefixNot(_ast) {},
|
||||||
visitNonNullAssert(ast) {},
|
visitNonNullAssert(_ast) {},
|
||||||
visitPropertyRead(ast) {
|
visitPropertyRead(ast) {
|
||||||
const receiverType = getType(ast.receiver);
|
const receiverType = getType(ast.receiver);
|
||||||
result = receiverType ? receiverType.members() : scope;
|
result = receiverType ? receiverType.members() : scope;
|
||||||
|
@ -84,7 +84,7 @@ export function getExpressionCompletions(
|
||||||
const receiverType = getType(ast.receiver);
|
const receiverType = getType(ast.receiver);
|
||||||
result = receiverType ? receiverType.members() : scope;
|
result = receiverType ? receiverType.members() : scope;
|
||||||
},
|
},
|
||||||
visitQuote(ast) {
|
visitQuote(_ast) {
|
||||||
// For a quote, return the members of any (if there are any).
|
// For a quote, return the members of any (if there are any).
|
||||||
result = templateInfo.query.getBuiltinType(BuiltinType.Any).members();
|
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
|
// (that is the scope of the implicit receiver) is the right scope as the user is typing the
|
||||||
// beginning of an expression.
|
// beginning of an expression.
|
||||||
tail.visit({
|
tail.visit({
|
||||||
visitBinary(ast) {},
|
visitBinary(_ast) {},
|
||||||
visitChain(ast) {},
|
visitChain(_ast) {},
|
||||||
visitConditional(ast) {},
|
visitConditional(_ast) {},
|
||||||
visitFunctionCall(ast) {},
|
visitFunctionCall(_ast) {},
|
||||||
visitImplicitReceiver(ast) {},
|
visitImplicitReceiver(_ast) {},
|
||||||
visitInterpolation(ast) {},
|
visitInterpolation(_ast) {},
|
||||||
visitKeyedRead(ast) {},
|
visitKeyedRead(_ast) {},
|
||||||
visitKeyedWrite(ast) {},
|
visitKeyedWrite(_ast) {},
|
||||||
visitLiteralArray(ast) {},
|
visitLiteralArray(_ast) {},
|
||||||
visitLiteralMap(ast) {},
|
visitLiteralMap(_ast) {},
|
||||||
visitLiteralPrimitive(ast) {},
|
visitLiteralPrimitive(_ast) {},
|
||||||
visitMethodCall(ast) {
|
visitMethodCall(ast) {
|
||||||
const receiverType = getType(ast.receiver);
|
const receiverType = getType(ast.receiver);
|
||||||
symbol = receiverType && receiverType.members().get(ast.name);
|
symbol = receiverType && receiverType.members().get(ast.name);
|
||||||
|
@ -159,8 +159,8 @@ export function getExpressionSymbol(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visitPrefixNot(ast) {},
|
visitPrefixNot(_ast) {},
|
||||||
visitNonNullAssert(ast) {},
|
visitNonNullAssert(_ast) {},
|
||||||
visitPropertyRead(ast) {
|
visitPropertyRead(ast) {
|
||||||
const receiverType = getType(ast.receiver);
|
const receiverType = getType(ast.receiver);
|
||||||
symbol = receiverType && receiverType.members().get(ast.name);
|
symbol = receiverType && receiverType.members().get(ast.name);
|
||||||
|
@ -177,7 +177,7 @@ export function getExpressionSymbol(
|
||||||
// ^^^^^^ value; visited separately as a nested AST
|
// ^^^^^^ value; visited separately as a nested AST
|
||||||
span = {start, end: start + ast.name.length};
|
span = {start, end: start + ast.name.length};
|
||||||
},
|
},
|
||||||
visitQuote(ast) {},
|
visitQuote(_ast) {},
|
||||||
visitSafeMethodCall(ast) {
|
visitSafeMethodCall(ast) {
|
||||||
const receiverType = getType(ast.receiver);
|
const receiverType = getType(ast.receiver);
|
||||||
symbol = receiverType && receiverType.members().get(ast.name);
|
symbol = receiverType && receiverType.members().get(ast.name);
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import {NgAnalyzedModules} from '@angular/compiler';
|
import {NgAnalyzedModules} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import {AstResult} from './common';
|
|
||||||
import {locateSymbols} from './locate_symbol';
|
import {locateSymbols} from './locate_symbol';
|
||||||
import * as ng from './types';
|
import * as ng from './types';
|
||||||
import {inSpan} from './utils';
|
import {inSpan} from './utils';
|
||||||
|
@ -27,7 +26,8 @@ const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.inter
|
||||||
* @param analyzedModules all NgModules in the program.
|
* @param analyzedModules all NgModules in the program.
|
||||||
*/
|
*/
|
||||||
export function getTemplateHover(
|
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];
|
const symbolInfo = locateSymbols(info, position)[0];
|
||||||
if (!symbolInfo) {
|
if (!symbolInfo) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -453,7 +453,3 @@ export function eventNames(elementName: string): string[] {
|
||||||
export function propertyNames(elementName: string): string[] {
|
export function propertyNames(elementName: string): string[] {
|
||||||
return SchemaInformation.instance.propertiesOf(elementName);
|
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(
|
getCompletionsAtPosition(
|
||||||
fileName: string, position: number,
|
fileName: string, position: number,
|
||||||
options?: tss.GetCompletionsAtPositionOptions): tss.CompletionInfo|undefined {
|
_options?: tss.GetCompletionsAtPositionOptions): tss.CompletionInfo|undefined {
|
||||||
this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
||||||
const ast = this.host.getTemplateAstAtPosition(fileName, position);
|
const ast = this.host.getTemplateAstAtPosition(fileName, position);
|
||||||
if (!ast) {
|
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 {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 * as tss from 'typescript/lib/tsserverlibrary';
|
||||||
|
|
||||||
import {AstResult} from './common';
|
import {getExpressionScope} from './expression_diagnostics';
|
||||||
import {diagnosticInfoFromTemplateInfo, getExpressionScope} from './expression_diagnostics';
|
|
||||||
import {getExpressionSymbol} from './expressions';
|
import {getExpressionSymbol} from './expressions';
|
||||||
import {Definition, DirectiveKind, Span, Symbol} from './types';
|
import {AstResult, Definition, DirectiveKind, Span, Symbol, SymbolInfo} from './types';
|
||||||
import {findOutputBinding, findTemplateAstAt, getPathToNodeAtPosition, inSpan, invertMap, isNarrower, offsetSpan, spanOf} from './utils';
|
import {diagnosticInfoFromTemplateInfo, findOutputBinding, findTemplateAstAt, getPathToNodeAtPosition, inSpan, invertMap, isNarrower, offsetSpan, spanOf} from './utils';
|
||||||
|
|
||||||
export interface SymbolInfo {
|
|
||||||
symbol: Symbol;
|
|
||||||
span: tss.TextSpan;
|
|
||||||
staticSymbol?: StaticSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Traverses a template AST and locates symbol(s) at a specified position.
|
* 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(
|
ast.visit(
|
||||||
{
|
{
|
||||||
visitNgContent(ast) {},
|
visitNgContent(_ast) {},
|
||||||
visitEmbeddedTemplate(ast) {},
|
visitEmbeddedTemplate(_ast) {},
|
||||||
visitElement(ast) {
|
visitElement(ast) {
|
||||||
const component = ast.directives.find(d => d.directive.isComponent);
|
const component = ast.directives.find(d => d.directive.isComponent);
|
||||||
if (component) {
|
if (component) {
|
||||||
|
@ -113,7 +106,7 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
|
||||||
symbol = ast.value && info.template.query.getTypeSymbol(tokenReference(ast.value));
|
symbol = ast.value && info.template.query.getTypeSymbol(tokenReference(ast.value));
|
||||||
span = spanOf(ast);
|
span = spanOf(ast);
|
||||||
},
|
},
|
||||||
visitVariable(ast) {},
|
visitVariable(_ast) {},
|
||||||
visitEvent(ast) {
|
visitEvent(ast) {
|
||||||
if (!attributeValueSymbol(ast.handler)) {
|
if (!attributeValueSymbol(ast.handler)) {
|
||||||
symbol = findOutputBinding(ast, path, info.template.query);
|
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) {
|
visitDirective(ast) {
|
||||||
// Need to cast because 'reference' is typed as any
|
// Need to cast because 'reference' is typed as any
|
||||||
staticSymbol = ast.directive.type.reference as StaticSymbol;
|
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
|
* 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 * as ts from 'typescript';
|
||||||
import {AstResult} from './common';
|
|
||||||
import {BuiltinType, DeclarationKind, Definition, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols';
|
|
||||||
|
|
||||||
export {
|
import {Span, Symbol, SymbolQuery, SymbolTable} from './symbols';
|
||||||
BuiltinType,
|
|
||||||
DeclarationKind,
|
|
||||||
Definition,
|
|
||||||
PipeInfo,
|
|
||||||
Pipes,
|
|
||||||
Signature,
|
|
||||||
Span,
|
|
||||||
StaticSymbol,
|
|
||||||
Symbol,
|
|
||||||
SymbolDeclaration,
|
|
||||||
SymbolQuery,
|
|
||||||
SymbolTable
|
|
||||||
};
|
|
||||||
|
|
||||||
|
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
|
* The information `LanguageService` needs from the `LanguageServiceHost` to describe the content of
|
||||||
|
@ -67,15 +54,6 @@ export interface TemplateSource {
|
||||||
readonly fileName: string;
|
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
|
* Error information found getting declaration information
|
||||||
*
|
*
|
||||||
|
@ -132,15 +110,6 @@ export interface Declaration {
|
||||||
readonly errors: DeclarationError[];
|
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
|
* The host for a `LanguageService`. This provides all the `LanguageService` requires to respond
|
||||||
* to the `LanguageService` requests.
|
* to the `LanguageService` requests.
|
||||||
|
@ -178,7 +147,7 @@ export interface LanguageServiceHost {
|
||||||
/**
|
/**
|
||||||
* Returns the Angular declarations in the given file.
|
* 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.
|
* Return a summary of all Angular modules in the project.
|
||||||
|
@ -196,45 +165,6 @@ export interface LanguageServiceHost {
|
||||||
getTemplateAstAtPosition(fileName: string, position: number): AstResult|undefined;
|
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.
|
* The type of Angular directive. Used for QuickInfo in template.
|
||||||
*/
|
*/
|
||||||
|
@ -312,45 +242,6 @@ export interface Diagnostic {
|
||||||
message: string|DiagnosticMessageChain;
|
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()`.
|
* An instance of an Angular language service created by `createLanguageService()`.
|
||||||
*
|
*
|
||||||
|
@ -364,3 +255,38 @@ export type LanguageService = Pick<
|
||||||
ts.LanguageService,
|
ts.LanguageService,
|
||||||
'getCompletionsAtPosition'|'getDefinitionAndBoundSpan'|'getQuickInfoAtPosition'|
|
'getCompletionsAtPosition'|'getDefinitionAndBoundSpan'|'getQuickInfoAtPosition'|
|
||||||
'getSemanticDiagnostics'>;
|
'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
|
* 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 {SchemaMetadata, ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||||
import * as tss from 'typescript/lib/tsserverlibrary';
|
import * as tss from 'typescript/lib/tsserverlibrary';
|
||||||
|
|
||||||
import {AstResult} from './common';
|
|
||||||
import {createLanguageService} from './language_service';
|
import {createLanguageService} from './language_service';
|
||||||
import {ReflectorHost} from './reflector_host';
|
import {ReflectorHost} from './reflector_host';
|
||||||
import {ExternalTemplate, getClassDeclFromDecoratorProp, getPropertyAssignmentFromValue, InlineTemplate} from './template';
|
import {ExternalTemplate, InlineTemplate} from './template';
|
||||||
import {Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types';
|
import {AstResult, Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types';
|
||||||
import {findTightestNode, getDirectiveClassLike} from './utils';
|
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.
|
* Avoid loading resources in the language servcie by using a dummy loader.
|
||||||
*/
|
*/
|
||||||
export class DummyResourceLoader extends ResourceLoader {
|
export class DummyResourceLoader extends ResourceLoader {
|
||||||
get(url: string): Promise<string> {
|
get(_url: string): Promise<string> {
|
||||||
return Promise.resolve('');
|
return Promise.resolve('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,10 +77,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
readonly tsLsHost: tss.LanguageServiceHost, private readonly tsLS: tss.LanguageService) {
|
readonly tsLsHost: tss.LanguageServiceHost, private readonly tsLS: tss.LanguageService) {
|
||||||
this.summaryResolver = new AotSummaryResolver(
|
this.summaryResolver = new AotSummaryResolver(
|
||||||
{
|
{
|
||||||
loadSummary(filePath: string) {
|
loadSummary(_filePath: string) {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
isSourceFile(sourceFilePath: string) {
|
isSourceFile(_sourceFilePath: string) {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
toSummaryFileName(sourceFilePath: string) {
|
toSummaryFileName(sourceFilePath: string) {
|
||||||
|
@ -172,7 +171,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
this.resolver.clearCache();
|
this.resolver.clearCache();
|
||||||
|
|
||||||
const analyzeHost = {
|
const analyzeHost = {
|
||||||
isSourceFile(filePath: string) {
|
isSourceFile(_filePath: string) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -57,8 +57,14 @@ export function getClassMembersFromDeclaration(
|
||||||
return new TypeWrapper(type, {node: source, program, checker}).members();
|
return new TypeWrapper(type, {node: source, program, checker}).members();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getClassFromStaticSymbol(
|
export function getPipesTable(
|
||||||
program: ts.Program, type: StaticSymbol): ts.ClassDeclaration|undefined {
|
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);
|
const source = program.getSourceFile(type.filePath);
|
||||||
if (source) {
|
if (source) {
|
||||||
return ts.forEachChild(source, child => {
|
return ts.forEachChild(source, child => {
|
||||||
|
@ -74,12 +80,6 @@ export function getClassFromStaticSymbol(
|
||||||
return undefined;
|
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 {
|
class TypeScriptSymbolQuery implements SymbolQuery {
|
||||||
private typeCache = new Map<BuiltinType, Symbol>();
|
private typeCache = new Map<BuiltinType, Symbol>();
|
||||||
private pipesCache: SymbolTable|undefined;
|
private pipesCache: SymbolTable|undefined;
|
||||||
|
@ -123,7 +123,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
|
||||||
return result || this.getBuiltinType(BuiltinType.Any);
|
return result || this.getBuiltinType(BuiltinType.Any);
|
||||||
}
|
}
|
||||||
|
|
||||||
getArrayType(type: Symbol): Symbol {
|
getArrayType(_type: Symbol): Symbol {
|
||||||
return this.getBuiltinType(BuiltinType.Any);
|
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));
|
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 {
|
undefined {
|
||||||
// TODO: Do a better job of selecting the right signature. TypeScript does not currently support a
|
// 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).
|
// 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);
|
return selectSignature(this.tsType, this.context, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed(argument: Symbol): Symbol|undefined {
|
indexed(_argument: Symbol): Symbol|undefined {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,7 +475,7 @@ class DeclaredSymbol implements Symbol {
|
||||||
return this.type.typeArguments();
|
return this.type.typeArguments();
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed(argument: Symbol): Symbol|undefined {
|
indexed(_argument: Symbol): Symbol|undefined {
|
||||||
return 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`
|
// ∀ Typescript version >= 2.2, `SymbolTable` is implemented as an ES6 `Map`
|
||||||
const result = new Map<string, ts.Symbol>();
|
const result = new Map<string, ts.Symbol>();
|
||||||
for (const symbol of symbols) {
|
for (const symbol of symbols) {
|
||||||
|
@ -548,8 +548,7 @@ class SymbolTableWrapper implements SymbolTable {
|
||||||
* @param context program context
|
* @param context program context
|
||||||
* @param type original TypeScript type of entity owning the symbols, if known
|
* @param type original TypeScript type of entity owning the symbols, if known
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(symbols: ts.SymbolTable|ts.Symbol[], private context: TypeContext, type?: ts.Type) {
|
||||||
symbols: ts.SymbolTable|ts.Symbol[], private context: TypeContext, private type?: ts.Type) {
|
|
||||||
symbols = symbols || [];
|
symbols = symbols || [];
|
||||||
|
|
||||||
if (Array.isArray(symbols)) {
|
if (Array.isArray(symbols)) {
|
||||||
|
@ -727,7 +726,7 @@ class PipeSymbol implements Symbol {
|
||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed(argument: Symbol): Symbol|undefined {
|
indexed(_argument: Symbol): Symbol|undefined {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -786,10 +785,10 @@ function findClassSymbolInContext(type: StaticSymbol, context: TypeContext): ts.
|
||||||
|
|
||||||
class EmptyTable implements SymbolTable {
|
class EmptyTable implements SymbolTable {
|
||||||
public readonly size: number = 0;
|
public readonly size: number = 0;
|
||||||
get(key: string): Symbol|undefined {
|
get(_key: string): Symbol|undefined {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
has(key: string): boolean {
|
has(_key: string): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
values(): Symbol[] {
|
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 {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 * as ts from 'typescript';
|
||||||
|
|
||||||
import {AstResult, SelectorInfo} from './common';
|
import {AstResult, DiagnosticTemplateInfo, SelectorInfo, Span, Symbol, SymbolQuery} from './types';
|
||||||
import {Span, Symbol, SymbolQuery} from './types';
|
|
||||||
|
|
||||||
export interface SpanHolder {
|
interface SpanHolder {
|
||||||
sourceSpan: ParseSourceSpan;
|
sourceSpan: ParseSourceSpan;
|
||||||
endSourceSpan?: ParseSourceSpan|null;
|
endSourceSpan?: ParseSourceSpan|null;
|
||||||
children?: SpanHolder[];
|
children?: SpanHolder[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isParseSourceSpan(value: any): value is ParseSourceSpan {
|
function isParseSourceSpan(value: any): value is ParseSourceSpan {
|
||||||
return value && !!value.start;
|
return value && !!value.start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,14 +79,16 @@ export function getSelectors(info: AstResult): SelectorInfo {
|
||||||
return {selectors: results, map};
|
return {selectors: results, map};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isTypescriptVersion(low: string, high?: string) {
|
export function diagnosticInfoFromTemplateInfo(info: AstResult): DiagnosticTemplateInfo {
|
||||||
const version = ts.version;
|
return {
|
||||||
|
fileName: info.template.fileName,
|
||||||
if (version.substring(0, low.length) < low) return false;
|
offset: info.template.span.start,
|
||||||
|
query: info.template.query,
|
||||||
if (high && (version.substring(0, high.length) > high)) return false;
|
members: info.template.members,
|
||||||
|
htmlAst: info.htmlAst,
|
||||||
return true;
|
templateAst: info.templateAst,
|
||||||
|
source: info.template.source,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findTemplateAstAt(ast: TemplateAst[], position: number): TemplateAstPath {
|
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",
|
"html_info_spec.ts",
|
||||||
"language_service_spec.ts",
|
"language_service_spec.ts",
|
||||||
"reflector_host_spec.ts",
|
"reflector_host_spec.ts",
|
||||||
"template_spec.ts",
|
|
||||||
"ts_plugin_spec.ts",
|
"ts_plugin_spec.ts",
|
||||||
"typescript_host_spec.ts",
|
"typescript_host_spec.ts",
|
||||||
"utils_spec.ts",
|
"utils_spec.ts",
|
||||||
|
|
|
@ -14,7 +14,7 @@ import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {DiagnosticTemplateInfo} from '../src/expression_diagnostics';
|
import {DiagnosticTemplateInfo} from '../src/types';
|
||||||
import {getClassMembers, getPipesTable, getSymbolQuery} from '../src/typescript_symbols';
|
import {getClassMembers, getPipesTable, getSymbolQuery} from '../src/typescript_symbols';
|
||||||
|
|
||||||
const realFiles = new Map<string, string>();
|
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 * as ts from 'typescript';
|
||||||
|
|
||||||
import {BuiltinType, Symbol, SymbolQuery, SymbolTable} from '../src/symbols';
|
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';
|
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 {
|
function appComponentSource(template: string): string {
|
||||||
return `
|
return `
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
import * as ng from '@angular/compiler';
|
import * as ng from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
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', () => {
|
describe('getDirectiveClassLike', () => {
|
||||||
it('should return a directive class', () => {
|
it('should return a directive class', () => {
|
||||||
|
@ -80,3 +81,42 @@ describe('getPathToNodeAtPosition', () => {
|
||||||
expect(path.tail instanceof ng.Attribute).toBe(true);
|
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