refactor(language-service): reformat using clang-format (#36426)

clang-format was recently updated and any PRs that touch files in the
language service will have to reformat all the files.

Instead of changing the formatting in those PRs, this PR formats all
files in language-service package once and for all.

PR Close #36426
This commit is contained in:
Keen Yee Liau 2020-04-03 20:57:39 -07:00 committed by Kara Erickson
parent 1b4df6484e
commit 1140bbc25c
35 changed files with 686 additions and 412 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AST, AbsoluteSourceSpan, AstPath, AttrAst, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, Element, ElementAst, EmptyExpr, ExpressionBinding, HtmlAstPath, NAMED_ENTITIES, Node as HtmlAst, NullTemplateVisitor, ParseSpan, ReferenceAst, TagContentType, TemplateBinding, Text, VariableBinding, getHtmlTagDefinition} from '@angular/compiler'; import {AbsoluteSourceSpan, AST, AstPath, AttrAst, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, Element, ElementAst, EmptyExpr, ExpressionBinding, getHtmlTagDefinition, HtmlAstPath, NAMED_ENTITIES, Node as HtmlAst, NullTemplateVisitor, ParseSpan, ReferenceAst, TagContentType, TemplateBinding, Text, VariableBinding} from '@angular/compiler';
import {$$, $_, isAsciiLetter, isDigit} from '@angular/compiler/src/chars'; import {$$, $_, isAsciiLetter, isDigit} from '@angular/compiler/src/chars';
import {AstResult} from './common'; import {AstResult} from './common';
@ -216,7 +216,8 @@ export function getTemplateCompletions(
const replacementSpan = getBoundedWordSpan(templateInfo, position); const replacementSpan = getBoundedWordSpan(templateInfo, position);
return result.map(entry => { return result.map(entry => {
return { return {
...entry, replacementSpan, ...entry,
replacementSpan,
}; };
}); });
} }
@ -434,7 +435,9 @@ class ExpressionVisitor extends NullTemplateVisitor {
super(); super();
} }
get results(): ng.CompletionEntry[] { return Array.from(this.completions.values()); } get results(): ng.CompletionEntry[] {
return Array.from(this.completions.values());
}
visitDirectiveProperty(ast: BoundDirectivePropertyAst): void { visitDirectiveProperty(ast: BoundDirectivePropertyAst): void {
this.processExpressionCompletions(ast.value); this.processExpressionCompletions(ast.value);
@ -444,7 +447,9 @@ class ExpressionVisitor extends NullTemplateVisitor {
this.processExpressionCompletions(ast.value); this.processExpressionCompletions(ast.value);
} }
visitEvent(ast: BoundEventAst): void { this.processExpressionCompletions(ast.handler); } visitEvent(ast: BoundEventAst): void {
this.processExpressionCompletions(ast.handler);
}
visitElement(): void { visitElement(): void {
// no-op for now // no-op for now

View File

@ -18,11 +18,11 @@ type DiagnosticName = 'directive_not_in_module' | 'missing_template_and_template
'both_template_and_templateurl'|'invalid_templateurl'|'template_context_missing_member'| 'both_template_and_templateurl'|'invalid_templateurl'|'template_context_missing_member'|
'callable_expression_expected_method_call'|'call_target_not_callable'| 'callable_expression_expected_method_call'|'call_target_not_callable'|
'expression_might_be_null'|'expected_a_number_type'|'expected_a_string_or_number_type'| 'expression_might_be_null'|'expected_a_number_type'|'expected_a_string_or_number_type'|
'expected_operands_of_similar_type_or_any' | 'unrecognized_operator' | 'expected_operands_of_similar_type_or_any'|'unrecognized_operator'|'unrecognized_primitive'|
'unrecognized_primitive' | 'no_pipe_found' | 'unable_to_resolve_compatible_call_signature' | 'no_pipe_found'|'unable_to_resolve_compatible_call_signature'|'unable_to_resolve_signature'|
'unable_to_resolve_signature' | 'could_not_resolve_type' | 'identifier_not_callable' | 'could_not_resolve_type'|'identifier_not_callable'|'identifier_possibly_undefined'|
'identifier_possibly_undefined' | 'identifier_not_defined_in_app_context' | 'identifier_not_defined_in_app_context'|'identifier_not_defined_on_receiver'|
'identifier_not_defined_on_receiver' | 'identifier_is_private'; 'identifier_is_private';
export const Diagnostic: Record<DiagnosticName, DiagnosticMessage> = { export const Diagnostic: Record<DiagnosticName, DiagnosticMessage> = {
directive_not_in_module: { directive_not_in_module: {
@ -156,6 +156,7 @@ export function createDiagnostic(
dm.message.replace(/%(\d+)/g, (_, index: string) => formatArgs[+index - 1]); dm.message.replace(/%(\d+)/g, (_, index: string) => formatArgs[+index - 1]);
return { return {
kind: ts.DiagnosticCategory[dm.kind], kind: ts.DiagnosticCategory[dm.kind],
message: formattedMessage, span, message: formattedMessage,
span,
}; };
} }

View File

@ -11,7 +11,7 @@ import * as path from 'path';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {AstResult} from './common'; import {AstResult} from './common';
import {Diagnostic, createDiagnostic} 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';

View File

@ -6,9 +6,9 @@
* 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, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, ReferenceAst, TemplateAst, TemplateAstPath, VariableAst, identifierName, templateVisitAll, tokenReference} from '@angular/compiler'; 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 {Diagnostic, createDiagnostic} 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';
@ -44,7 +44,9 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
name: reference.name, name: reference.name,
kind: 'reference', kind: 'reference',
type: type || info.query.getBuiltinType(BuiltinType.Any), type: type || info.query.getBuiltinType(BuiltinType.Any),
get definition() { return getDefinitionOf(info, reference); } get definition() {
return getDefinitionOf(info, reference);
}
}); });
} }
} }
@ -109,7 +111,10 @@ function getVarDeclarations(
results.push({ results.push({
name: variable.name, name: variable.name,
kind: 'variable', kind: 'variable',
type: symbol, get definition() { return getDefinitionOf(info, variable); }, type: symbol,
get definition() {
return getDefinitionOf(info, variable);
},
}); });
} }
} }
@ -350,9 +355,13 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
} }
} }
private push(ast: TemplateAst) { this.path.push(ast); } private push(ast: TemplateAst) {
this.path.push(ast);
}
private pop() { this.path.pop(); } private pop() {
this.path.pop();
}
private absSpan(span: Span, additionalOffset: number = 0): Span { private absSpan(span: Span, additionalOffset: number = 0): Span {
return { return {

View File

@ -8,11 +8,13 @@
import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '@angular/compiler'; import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '@angular/compiler';
import {Diagnostic, createDiagnostic} from './diagnostic_messages'; 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 { inEvent?: boolean; } export interface ExpressionDiagnosticsContext {
inEvent?: boolean;
}
// AstType calculatetype of the ast given AST element. // AstType calculatetype of the ast given AST element.
export class AstType implements AstVisitor { export class AstType implements AstVisitor {
@ -22,7 +24,9 @@ export class AstType implements AstVisitor {
private scope: SymbolTable, private query: SymbolQuery, private scope: SymbolTable, private query: SymbolQuery,
private context: ExpressionDiagnosticsContext, private source: string) {} private context: ExpressionDiagnosticsContext, private source: string) {}
getType(ast: AST): Symbol { return ast.visit(this); } getType(ast: AST): Symbol {
return ast.visit(this);
}
getDiagnostics(ast: AST): ng.Diagnostic[] { getDiagnostics(ast: AST): ng.Diagnostic[] {
const type: Symbol = ast.visit(this); const type: Symbol = ast.visit(this);
@ -237,11 +241,24 @@ export class AstType implements AstVisitor {
public: true, public: true,
definition: undefined, definition: undefined,
documentation: [], documentation: [],
members(): SymbolTable{return _this.scope;}, members(): SymbolTable {
signatures(): Signature[]{return [];}, return _this.scope;
selectSignature(types): Signature | undefined{return undefined;}, },
indexed(argument): Symbol | undefined{return undefined;}, signatures(): Signature[] {
typeArguments(): Symbol[] | undefined{return undefined;}, return [];
},
selectSignature(types): Signature |
undefined {
return undefined;
},
indexed(argument): Symbol |
undefined {
return undefined;
},
typeArguments(): Symbol[] |
undefined {
return undefined;
},
}; };
} }

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AST, ASTWithSource, AstPath as AstPathBase, RecursiveAstVisitor} from '@angular/compiler'; import {AST, AstPath as AstPathBase, ASTWithSource, RecursiveAstVisitor} from '@angular/compiler';
import {AstType} from './expression_type'; import {AstType} from './expression_type';
import {BuiltinType, Span, Symbol, SymbolTable, TemplateSource} from './types'; import {BuiltinType, Span, Symbol, SymbolTable, TemplateSource} from './types';
import {inSpan} from './utils'; import {inSpan} from './utils';
@ -57,7 +58,9 @@ export function getExpressionCompletions(
visitConditional(ast) {}, visitConditional(ast) {},
visitFunctionCall(ast) {}, visitFunctionCall(ast) {},
visitImplicitReceiver(ast) {}, visitImplicitReceiver(ast) {},
visitInterpolation(ast) { result = undefined; }, visitInterpolation(ast) {
result = undefined;
},
visitKeyedRead(ast) {}, visitKeyedRead(ast) {},
visitKeyedWrite(ast) {}, visitKeyedWrite(ast) {},
visitLiteralArray(ast) {}, visitLiteralArray(ast) {},

View File

@ -44,7 +44,8 @@ export function getTemplateHover(
containerName = ngModule?.type.reference.name; containerName = ngModule?.type.reference.name;
} }
return createQuickInfo(symbol.name, symbol.kind, span, containerName, symbol.type?.name, symbol.documentation); return createQuickInfo(
symbol.name, symbol.kind, span, containerName, symbol.type?.name, symbol.documentation);
} }
/** /**

View File

@ -104,7 +104,9 @@ const groups: hash<number>[] = [
{class: 1, style: 1}, {class: 1, style: 1},
{hreflang: 2, rel: 1, rev: 1}, {hreflang: 2, rel: 1, rev: 1},
{ismap: 7}, {ismap: 7},
{ defer: 25, event: 1, for : 1 } {
defer: 25, event: 1, for: 1
}
]; ];
const elements: {[name: string]: number[]} = { const elements: {[name: string]: number[]} = {
@ -415,7 +417,9 @@ export class SchemaInformation {
}); });
} }
allKnownElements(): string[] { return Object.keys(this.schema); } allKnownElements(): string[] {
return Object.keys(this.schema);
}
eventsOf(elementName: string): string[] { eventsOf(elementName: string): string[] {
const elementType = this.schema[elementName.toLowerCase()] || {}; const elementType = this.schema[elementName.toLowerCase()] || {};

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AST, Attribute, BoundDirectivePropertyAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, VariableBinding, templateVisitAll, tokenReference} 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 {AstResult} from './common';
@ -121,7 +121,9 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
span = spanOf(ast); span = spanOf(ast);
} }
}, },
visitElementProperty(ast) { attributeValueSymbol(ast.value); }, visitElementProperty(ast) {
attributeValueSymbol(ast.value);
},
visitAttr(ast) { visitAttr(ast) {
const element = path.first(ElementAst); const element = path.first(ElementAst);
if (!element) return; if (!element) return;
@ -188,7 +190,8 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
const {start, end} = offsetSpan(span, info.template.span.start); const {start, end} = offsetSpan(span, info.template.span.start);
return { return {
symbol, symbol,
span: tss.createTextSpanFromBounds(start, end), staticSymbol, span: tss.createTextSpanFromBounds(start, end),
staticSymbol,
}; };
} }
} }
@ -277,7 +280,9 @@ function findParentOfBinding(
} }
visitDirective(ast: DirectiveAst) { visitDirective(ast: DirectiveAst) {
const result = this.visitChildren(ast, visit => { visit(ast.inputs); }); const result = this.visitChildren(ast, visit => {
visit(ast.inputs);
});
return result; return result;
} }
@ -309,33 +314,63 @@ function findInputBinding(info: AstResult, name: string, directiveAst: Directive
*/ */
class OverrideKindSymbol implements Symbol { class OverrideKindSymbol implements Symbol {
public readonly kind: DirectiveKind; public readonly kind: DirectiveKind;
constructor(private sym: Symbol, kindOverride: DirectiveKind) { this.kind = kindOverride; } constructor(private sym: Symbol, kindOverride: DirectiveKind) {
this.kind = kindOverride;
get name(): string { return this.sym.name; } }
get language(): string { return this.sym.language; } get name(): string {
return this.sym.name;
get type(): Symbol|undefined { return this.sym.type; } }
get container(): Symbol|undefined { return this.sym.container; } get language(): string {
return this.sym.language;
get public(): boolean { return this.sym.public; } }
get callable(): boolean { return this.sym.callable; } get type(): Symbol|undefined {
return this.sym.type;
get nullable(): boolean { return this.sym.nullable; } }
get definition(): Definition { return this.sym.definition; } get container(): Symbol|undefined {
return this.sym.container;
get documentation(): ts.SymbolDisplayPart[] { return this.sym.documentation; } }
members() { return this.sym.members(); } get public(): boolean {
return this.sym.public;
signatures() { return this.sym.signatures(); } }
selectSignature(types: Symbol[]) { return this.sym.selectSignature(types); } get callable(): boolean {
return this.sym.callable;
indexed(argument: Symbol) { return this.sym.indexed(argument); } }
typeArguments(): Symbol[]|undefined { return this.sym.typeArguments(); } get nullable(): boolean {
return this.sym.nullable;
}
get definition(): Definition {
return this.sym.definition;
}
get documentation(): ts.SymbolDisplayPart[] {
return this.sym.documentation;
}
members() {
return this.sym.members();
}
signatures() {
return this.sym.signatures();
}
selectSignature(types: Symbol[]) {
return this.sym.selectSignature(types);
}
indexed(argument: Symbol) {
return this.sym.indexed(argument);
}
typeArguments(): Symbol[]|undefined {
return this.sym.typeArguments();
}
} }

View File

@ -7,7 +7,7 @@
*/ */
import {StaticSymbolResolverHost} from '@angular/compiler'; import {StaticSymbolResolverHost} from '@angular/compiler';
import {MetadataCollector, MetadataReaderHost, createMetadataReaderCache, readMetadata} from '@angular/compiler-cli/src/language_services'; import {createMetadataReaderCache, MetadataCollector, MetadataReaderHost, readMetadata} from '@angular/compiler-cli/src/language_services';
import * as path from 'path'; import * as path from 'path';
import * as ts from 'typescript'; import * as ts from 'typescript';
@ -120,5 +120,7 @@ export class ReflectorHost implements StaticSymbolResolverHost {
return resolved ? resolved.resolvedFileName : null; return resolved ? resolved.resolvedFileName : null;
} }
getOutputName(filePath: string) { return filePath; } getOutputName(filePath: string) {
return filePath;
}
} }

View File

@ -235,8 +235,8 @@ export enum BuiltinType {
* *
* @publicApi * @publicApi
*/ */
export type DeclarationKind = 'attribute' | 'html attribute' | 'component' | 'element' | 'entity' | export type DeclarationKind = 'attribute'|'html attribute'|'component'|'element'|'entity'|'key'|
'key' | 'method' | 'pipe' | 'property' | 'type' | 'reference' | 'variable'; 'method'|'pipe'|'property'|'type'|'reference'|'variable';
/** /**
* Describes a symbol to type binding used to build a symbol table. * Describes a symbol to type binding used to build a symbol table.

View File

@ -39,7 +39,9 @@ abstract class BaseTemplate implements ng.TemplateSource {
/** /**
* Return the Angular StaticSymbol for the class that contains this template. * Return the Angular StaticSymbol for the class that contains this template.
*/ */
get type() { return this.classSymbol; } get type() {
return this.classSymbol;
}
/** /**
* Return a Map-like data structure that allows users to retrieve some or all * Return a Map-like data structure that allows users to retrieve some or all

View File

@ -27,8 +27,7 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService {
const ngLS = createLanguageService(ngLSHost); const ngLS = createLanguageService(ngLSHost);
function getCompletionsAtPosition( function getCompletionsAtPosition(
fileName: string, position: number, fileName: string, position: number, options: tss.GetCompletionsAtPositionOptions|undefined) {
options: tss.GetCompletionsAtPositionOptions | undefined) {
if (!angularOnly) { if (!angularOnly) {
const results = tsLS.getCompletionsAtPosition(fileName, position, options); const results = tsLS.getCompletionsAtPosition(fileName, position, options);
if (results && results.entries.length) { if (results && results.entries.length) {
@ -93,8 +92,11 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService {
{}, tsLS, {}, tsLS,
// Then override the methods supported by Angular language service // Then override the methods supported by Angular language service
{ {
getCompletionsAtPosition, getQuickInfoAtPosition, getSemanticDiagnostics, getCompletionsAtPosition,
getDefinitionAtPosition, getDefinitionAndBoundSpan, getQuickInfoAtPosition,
getSemanticDiagnostics,
getDefinitionAtPosition,
getDefinitionAndBoundSpan,
}); });
return proxy; return proxy;
} }

View File

@ -361,5 +361,6 @@ export interface Hover {
* @publicApi * @publicApi
*/ */
export type LanguageService = Pick< export type LanguageService = Pick<
ts.LanguageService, 'getCompletionsAtPosition'|'getDefinitionAndBoundSpan'| ts.LanguageService,
'getQuickInfoAtPosition'|'getSemanticDiagnostics'>; 'getCompletionsAtPosition'|'getDefinitionAndBoundSpan'|'getQuickInfoAtPosition'|
'getSemanticDiagnostics'>;

View File

@ -6,14 +6,14 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AotSummaryResolver, CompileDirectiveSummary, CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, FormattedError, FormattedMessageChain, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver, isFormattedError} from '@angular/compiler'; 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 {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 {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, InlineTemplate, getClassDeclFromDecoratorProp, getPropertyAssignmentFromValue} from './template'; import {ExternalTemplate, getClassDeclFromDecoratorProp, getPropertyAssignmentFromValue, InlineTemplate} from './template';
import {Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types'; import {Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types';
import {findTightestNode, getDirectiveClassLike} from './utils'; import {findTightestNode, getDirectiveClassLike} from './utils';
@ -35,14 +35,18 @@ export function createLanguageServiceFromTypescript(
* syntactically incorrect templates. * syntactically incorrect templates.
*/ */
export class DummyHtmlParser extends HtmlParser { export class DummyHtmlParser extends HtmlParser {
parse(): ParseTreeResult { return new ParseTreeResult([], []); } parse(): ParseTreeResult {
return new ParseTreeResult([], []);
}
} }
/** /**
* 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> { return Promise.resolve(''); } get(url: string): Promise<string> {
return Promise.resolve('');
}
} }
/** /**
@ -74,10 +78,18 @@ 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) { return null; }, loadSummary(filePath: string) {
isSourceFile(sourceFilePath: string) { return true; }, return null;
toSummaryFileName(sourceFilePath: string) { return sourceFilePath; }, },
fromSummaryFileName(filePath: string): string{return filePath;}, isSourceFile(sourceFilePath: string) {
return true;
},
toSummaryFileName(sourceFilePath: string) {
return sourceFilePath;
},
fromSummaryFileName(filePath: string): string {
return filePath;
},
}, },
this.staticSymbolCache); this.staticSymbolCache);
this.reflectorHost = new ReflectorHost(() => this.program, tsLsHost); this.reflectorHost = new ReflectorHost(() => this.program, tsLsHost);
@ -159,7 +171,11 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
this.collectedErrors.clear(); this.collectedErrors.clear();
this.resolver.clearCache(); this.resolver.clearCache();
const analyzeHost = {isSourceFile(filePath: string) { return true; }}; const analyzeHost = {
isSourceFile(filePath: string) {
return true;
}
};
const programFiles = this.program.getSourceFiles().map(sf => sf.fileName); const programFiles = this.program.getSourceFiles().map(sf => sf.fileName);
this.analyzedModules = this.analyzedModules =
analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver); analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver);
@ -509,8 +525,12 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
return { return {
htmlAst: htmlResult.rootNodes, htmlAst: htmlResult.rootNodes,
templateAst: parseResult.templateAst, templateAst: parseResult.templateAst,
directive: data.metadata, directives, pipes, directive: data.metadata,
parseErrors: parseResult.errors, expressionParser, template, directives,
pipes,
parseErrors: parseResult.errors,
expressionParser,
template,
}; };
} }

View File

@ -123,7 +123,9 @@ class TypeScriptSymbolQuery implements SymbolQuery {
return result || this.getBuiltinType(BuiltinType.Any); return result || this.getBuiltinType(BuiltinType.Any);
} }
getArrayType(type: Symbol): Symbol { return this.getBuiltinType(BuiltinType.Any); } getArrayType(type: Symbol): Symbol {
return this.getBuiltinType(BuiltinType.Any);
}
getElementType(type: Symbol): Symbol|undefined { getElementType(type: Symbol): Symbol|undefined {
if (type instanceof TypeWrapper) { if (type instanceof TypeWrapper) {
@ -237,7 +239,9 @@ class TypeWrapper implements Symbol {
} }
} }
get name(): string { return this.context.checker.typeToString(this.tsType); } get name(): string {
return this.context.checker.typeToString(this.tsType);
}
public readonly kind: DeclarationKind = 'type'; public readonly kind: DeclarationKind = 'type';
@ -249,7 +253,9 @@ class TypeWrapper implements Symbol {
public readonly public: boolean = true; public readonly public: boolean = true;
get callable(): boolean { return typeCallable(this.tsType); } get callable(): boolean {
return typeCallable(this.tsType);
}
get nullable(): boolean { get nullable(): boolean {
return this.context.checker.getNonNullableType(this.tsType) != this.tsType; return this.context.checker.getNonNullableType(this.tsType) != this.tsType;
@ -276,7 +282,9 @@ class TypeWrapper implements Symbol {
return new SymbolTableWrapper(this.tsType.getApparentProperties(), this.context, this.tsType); return new SymbolTableWrapper(this.tsType.getApparentProperties(), this.context, this.tsType);
} }
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); } signatures(): Signature[] {
return signaturesOf(this.tsType, this.context);
}
selectSignature(types: Symbol[]): Signature|undefined { selectSignature(types: Symbol[]): Signature|undefined {
return selectSignature(this.tsType, this.context, types); return selectSignature(this.tsType, this.context, types);
@ -332,30 +340,44 @@ class SymbolWrapper implements Symbol {
symbol: ts.Symbol, symbol: ts.Symbol,
/** TypeScript type context of the symbol. */ /** TypeScript type context of the symbol. */
private context: TypeContext, private context: TypeContext,
/** Type of the TypeScript symbol, if known. If not provided, the type of the symbol /**
* will be determined dynamically; see `SymbolWrapper#tsType`. */ * Type of the TypeScript symbol, if known. If not provided, the type of the symbol
* will be determined dynamically; see `SymbolWrapper#tsType`.
*/
private _tsType?: ts.Type) { private _tsType?: ts.Type) {
this.symbol = symbol && context && (symbol.flags & ts.SymbolFlags.Alias) ? this.symbol = symbol && context && (symbol.flags & ts.SymbolFlags.Alias) ?
context.checker.getAliasedSymbol(symbol) : context.checker.getAliasedSymbol(symbol) :
symbol; symbol;
} }
get name(): string { return this.symbol.name; } get name(): string {
return this.symbol.name;
}
get kind(): DeclarationKind { return this.callable ? 'method' : 'property'; } get kind(): DeclarationKind {
return this.callable ? 'method' : 'property';
}
get type(): TypeWrapper { return new TypeWrapper(this.tsType, this.context); } get type(): TypeWrapper {
return new TypeWrapper(this.tsType, this.context);
}
get container(): Symbol|undefined { return getContainerOf(this.symbol, this.context); } get container(): Symbol|undefined {
return getContainerOf(this.symbol, this.context);
}
get public(): boolean { get public(): boolean {
// Symbols that are not explicitly made private are public. // Symbols that are not explicitly made private are public.
return !isSymbolPrivate(this.symbol); return !isSymbolPrivate(this.symbol);
} }
get callable(): boolean { return typeCallable(this.tsType); } get callable(): boolean {
return typeCallable(this.tsType);
}
get definition(): Definition { return definitionFromTsSymbol(this.symbol); } get definition(): Definition {
return definitionFromTsSymbol(this.symbol);
}
get documentation(): ts.SymbolDisplayPart[] { get documentation(): ts.SymbolDisplayPart[] {
return this.symbol.getDocumentationComment(this.context.checker); return this.symbol.getDocumentationComment(this.context.checker);
@ -374,15 +396,21 @@ class SymbolWrapper implements Symbol {
return this._members; return this._members;
} }
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); } signatures(): Signature[] {
return signaturesOf(this.tsType, this.context);
}
selectSignature(types: Symbol[]): Signature|undefined { selectSignature(types: Symbol[]): Signature|undefined {
return selectSignature(this.tsType, this.context, types); return selectSignature(this.tsType, this.context, types);
} }
indexed(argument: Symbol): Symbol|undefined { return undefined; } indexed(argument: Symbol): Symbol|undefined {
return undefined;
}
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); } typeArguments(): Symbol[]|undefined {
return this.type.typeArguments();
}
private get tsType(): ts.Type { private get tsType(): ts.Type {
let type = this._tsType; let type = this._tsType;
@ -403,29 +431,53 @@ class DeclaredSymbol implements Symbol {
constructor(private declaration: SymbolDeclaration) {} constructor(private declaration: SymbolDeclaration) {}
get name() { return this.declaration.name; } get name() {
return this.declaration.name;
}
get kind() { return this.declaration.kind; } get kind() {
return this.declaration.kind;
}
get container(): Symbol|undefined { return undefined; } get container(): Symbol|undefined {
return undefined;
}
get type(): Symbol { return this.declaration.type; } get type(): Symbol {
return this.declaration.type;
}
get callable(): boolean { return this.type.callable; } get callable(): boolean {
return this.type.callable;
}
get definition(): Definition { return this.declaration.definition; } get definition(): Definition {
return this.declaration.definition;
}
get documentation(): ts.SymbolDisplayPart[] { return this.declaration.type.documentation; } get documentation(): ts.SymbolDisplayPart[] {
return this.declaration.type.documentation;
}
members(): SymbolTable { return this.type.members(); } members(): SymbolTable {
return this.type.members();
}
signatures(): Signature[] { return this.type.signatures(); } signatures(): Signature[] {
return this.type.signatures();
}
selectSignature(types: Symbol[]): Signature|undefined { return this.type.selectSignature(types); } selectSignature(types: Symbol[]): Signature|undefined {
return this.type.selectSignature(types);
}
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); } typeArguments(): Symbol[]|undefined {
return this.type.typeArguments();
}
indexed(argument: Symbol): Symbol|undefined { return undefined; } indexed(argument: Symbol): Symbol|undefined {
return undefined;
}
} }
class SignatureWrapper implements Signature { class SignatureWrapper implements Signature {
@ -435,15 +487,21 @@ class SignatureWrapper implements Signature {
return new SymbolTableWrapper(this.signature.getParameters(), this.context); return new SymbolTableWrapper(this.signature.getParameters(), this.context);
} }
get result(): Symbol { return new TypeWrapper(this.signature.getReturnType(), this.context); } get result(): Symbol {
return new TypeWrapper(this.signature.getReturnType(), this.context);
}
} }
class SignatureResultOverride implements Signature { class SignatureResultOverride implements Signature {
constructor(private signature: Signature, private resultType: Symbol) {} constructor(private signature: Signature, private resultType: Symbol) {}
get arguments(): SymbolTable { return this.signature.arguments; } get arguments(): SymbolTable {
return this.signature.arguments;
}
get result(): Symbol { return this.resultType; } get result(): Symbol {
return this.resultType;
}
} }
export function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable { export function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable {
@ -507,7 +565,9 @@ class SymbolTableWrapper implements SymbolTable {
} }
} }
get size(): number { return this.symbols.length; } get size(): number {
return this.symbols.length;
}
get(key: string): Symbol|undefined { get(key: string): Symbol|undefined {
const symbol = getFromSymbolTable(this.symbolTable, key); const symbol = getFromSymbolTable(this.symbolTable, key);
@ -535,16 +595,22 @@ class SymbolTableWrapper implements SymbolTable {
this.stringIndexType !== undefined; this.stringIndexType !== undefined;
} }
values(): Symbol[] { return this.symbols.map(s => new SymbolWrapper(s, this.context)); } values(): Symbol[] {
return this.symbols.map(s => new SymbolWrapper(s, this.context));
}
} }
class MapSymbolTable implements SymbolTable { class MapSymbolTable implements SymbolTable {
private map = new Map<string, Symbol>(); private map = new Map<string, Symbol>();
private _values: Symbol[] = []; private _values: Symbol[] = [];
get size(): number { return this.map.size; } get size(): number {
return this.map.size;
}
get(key: string): Symbol|undefined { return this.map.get(key); } get(key: string): Symbol|undefined {
return this.map.get(key);
}
add(symbol: Symbol) { add(symbol: Symbol) {
if (this.map.has(symbol.name)) { if (this.map.has(symbol.name)) {
@ -561,7 +627,9 @@ class MapSymbolTable implements SymbolTable {
} }
} }
has(key: string): boolean { return this.map.has(key); } has(key: string): boolean {
return this.map.has(key);
}
values(): Symbol[] { values(): Symbol[] {
// Switch to this.map.values once iterables are supported by the target language. // Switch to this.map.values once iterables are supported by the target language.
@ -572,7 +640,9 @@ class MapSymbolTable implements SymbolTable {
class PipesTable implements SymbolTable { class PipesTable implements SymbolTable {
constructor(private pipes: CompilePipeSummary[], private context: TypeContext) {} constructor(private pipes: CompilePipeSummary[], private context: TypeContext) {}
get size() { return this.pipes.length; } get size() {
return this.pipes.length;
}
get(key: string): Symbol|undefined { get(key: string): Symbol|undefined {
const pipe = this.pipes.find(pipe => pipe.name == key); const pipe = this.pipes.find(pipe => pipe.name == key);
@ -581,9 +651,13 @@ class PipesTable implements SymbolTable {
} }
} }
has(key: string): boolean { return this.pipes.find(pipe => pipe.name == key) != null; } has(key: string): boolean {
return this.pipes.find(pipe => pipe.name == key) != null;
}
values(): Symbol[] { return this.pipes.map(pipe => new PipeSymbol(pipe, this.context)); } values(): Symbol[] {
return this.pipes.map(pipe => new PipeSymbol(pipe, this.context));
}
} }
// This matches .d.ts files that look like ".../<package-name>/<package-name>.d.ts", // This matches .d.ts files that look like ".../<package-name>/<package-name>.d.ts",
@ -600,9 +674,13 @@ class PipeSymbol implements Symbol {
constructor(private pipe: CompilePipeSummary, private context: TypeContext) {} constructor(private pipe: CompilePipeSummary, private context: TypeContext) {}
get name(): string { return this.pipe.name; } get name(): string {
return this.pipe.name;
}
get type(): TypeWrapper { return new TypeWrapper(this.tsType, this.context); } get type(): TypeWrapper {
return new TypeWrapper(this.tsType, this.context);
}
get definition(): Definition|undefined { get definition(): Definition|undefined {
const symbol = this.tsType.getSymbol(); const symbol = this.tsType.getSymbol();
@ -617,9 +695,13 @@ class PipeSymbol implements Symbol {
return symbol.getDocumentationComment(this.context.checker); return symbol.getDocumentationComment(this.context.checker);
} }
members(): SymbolTable { return EmptyTable.instance; } members(): SymbolTable {
return EmptyTable.instance;
}
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); } signatures(): Signature[] {
return signaturesOf(this.tsType, this.context);
}
selectSignature(types: Symbol[]): Signature|undefined { selectSignature(types: Symbol[]): Signature|undefined {
let signature = selectSignature(this.tsType, this.context, types)!; let signature = selectSignature(this.tsType, this.context, types)!;
@ -645,9 +727,13 @@ class PipeSymbol implements Symbol {
return signature; return signature;
} }
indexed(argument: Symbol): Symbol|undefined { return undefined; } indexed(argument: Symbol): Symbol|undefined {
return undefined;
}
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); } typeArguments(): Symbol[]|undefined {
return this.type.typeArguments();
}
private get tsType(): ts.Type { private get tsType(): ts.Type {
let type = this._tsType; let type = this._tsType;
@ -700,9 +786,15 @@ 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 { return undefined; } get(key: string): Symbol|undefined {
has(key: string): boolean { return false; } return undefined;
values(): Symbol[] { return []; } }
has(key: string): boolean {
return false;
}
values(): Symbol[] {
return [];
}
static instance = new EmptyTable(); static instance = new EmptyTable();
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AstPath, BoundEventAst, CompileDirectiveSummary, CompileTypeMetadata, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, HtmlAstPath, Identifiers, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, RecursiveVisitor, TemplateAst, TemplateAstPath, identifierName, 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, SelectorInfo} from './common';
@ -44,7 +44,8 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
} }
export function inSpan(position: number, span?: Span, exclusive?: boolean): boolean { export function inSpan(position: number, span?: Span, exclusive?: boolean): boolean {
return span != null && (exclusive ? position >= span.start && position < span.end : return span != null &&
(exclusive ? position >= span.start && position < span.end :
position >= span.start && position <= span.end); position >= span.start && position <= span.end);
} }
@ -141,7 +142,9 @@ export function findTemplateAstAt(ast: TemplateAst[], position: number): Templat
visitDirective(ast: DirectiveAst, context: any): any { visitDirective(ast: DirectiveAst, context: any): any {
// Ignore the host properties of a directive // Ignore the host properties of a directive
const result = this.visitChildren(context, visit => { visit(ast.inputs); }); const result = this.visitChildren(context, visit => {
visit(ast.inputs);
});
// We never care about the diretive itself, just its inputs. // We never care about the diretive itself, just its inputs.
if (path[path.length - 1] === ast) { if (path[path.length - 1] === ast) {
path.pop(); path.pop();

View File

@ -25,7 +25,9 @@ describe('completions', () => {
const ngHost = new TypeScriptServiceHost(mockHost, tsLS); const ngHost = new TypeScriptServiceHost(mockHost, tsLS);
const ngLS = createLanguageService(ngHost); const ngLS = createLanguageService(ngHost);
beforeEach(() => { mockHost.reset(); }); beforeEach(() => {
mockHost.reset();
});
it('should be able to get entity completions', () => { it('should be able to get entity completions', () => {
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'entity-amp'); const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'entity-amp');

View File

@ -22,7 +22,9 @@ describe('definitions', () => {
const ngHost = new TypeScriptServiceHost(mockHost, service); const ngHost = new TypeScriptServiceHost(mockHost, service);
const ngService = createLanguageService(ngHost); const ngService = createLanguageService(ngHost);
beforeEach(() => { mockHost.reset(); }); beforeEach(() => {
mockHost.reset();
});
it('should be able to find field in an interpolation', () => { it('should be able to find field in an interpolation', () => {
const fileName = mockHost.addCode(` const fileName = mockHost.addCode(`

View File

@ -7,7 +7,8 @@
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import {DiagnosticMessage, createDiagnostic} from '../src/diagnostic_messages';
import {createDiagnostic, DiagnosticMessage} from '../src/diagnostic_messages';
describe('create diagnostic', () => { describe('create diagnostic', () => {
it('should format and create diagnostics correctly', () => { it('should format and create diagnostics correctly', () => {

View File

@ -34,7 +34,9 @@ describe('diagnostics', () => {
const ngHost = new TypeScriptServiceHost(mockHost, tsLS); const ngHost = new TypeScriptServiceHost(mockHost, tsLS);
const ngLS = createLanguageService(ngHost); const ngLS = createLanguageService(ngHost);
beforeEach(() => { mockHost.reset(); }); beforeEach(() => {
mockHost.reset();
});
it('should produce no diagnostics for test.ng', () => { it('should produce no diagnostics for test.ng', () => {
// there should not be any errors on existing external template // there should not be any errors on existing external template
@ -300,7 +302,8 @@ describe('diagnostics', () => {
expect(messageText) expect(messageText)
.toBe( .toBe(
`The template context of 'CounterDirective' does not define an implicit value.\n` + `The template context of 'CounterDirective' does not define an implicit value.\n` +
`If the context type is a base type or 'any', consider refining it to a more specific type.`, ); `If the context type is a base type or 'any', consider refining it to a more specific type.`,
);
const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb'); const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb');
expect(start).toBe(span.start); expect(start).toBe(span.start);
@ -337,7 +340,8 @@ describe('diagnostics', () => {
expect(category).toBe(ts.DiagnosticCategory.Error); expect(category).toBe(ts.DiagnosticCategory.Error);
expect(messageText) expect(messageText)
.toBe( .toBe(
`Identifier 'missingField' is not defined. '{ implicitPerson: Person; }' does not contain such a member`, ); `Identifier 'missingField' is not defined. '{ implicitPerson: Person; }' does not contain such a member`,
);
const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb'); const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb');
expect(start).toBe(span.start); expect(start).toBe(span.start);
expect(length).toBe(span.length); expect(length).toBe(span.length);
@ -355,7 +359,8 @@ describe('diagnostics', () => {
expect(category).toBe(ts.DiagnosticCategory.Error); expect(category).toBe(ts.DiagnosticCategory.Error);
expect(messageText) expect(messageText)
.toBe( .toBe(
`Identifier 'missingField' is not defined. 'Person' does not contain such a member`, ); `Identifier 'missingField' is not defined. 'Person' does not contain such a member`,
);
const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb'); const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb');
expect(start).toBe(span.start); expect(start).toBe(span.start);
expect(length).toBe(span.length); expect(length).toBe(span.length);
@ -973,7 +978,6 @@ describe('diagnostics', () => {
`or non-null assertion operator (optional!.toLowerCase).`); `or non-null assertion operator (optional!.toLowerCase).`);
expect(category).toBe(ts.DiagnosticCategory.Suggestion); expect(category).toBe(ts.DiagnosticCategory.Suggestion);
expect(content.substring(start!, start! + length!)).toBe('optional.toLowerCase()'); expect(content.substring(start!, start! + length!)).toBe('optional.toLowerCase()');
}); });
it('should suggest ? or ! operator if property receiver is nullable', () => { it('should suggest ? or ! operator if property receiver is nullable', () => {

View File

@ -13,7 +13,7 @@ import * as ts from 'typescript';
import {getTemplateExpressionDiagnostics} from '../src/expression_diagnostics'; import {getTemplateExpressionDiagnostics} from '../src/expression_diagnostics';
import {DiagnosticContext, MockLanguageServiceHost, getDiagnosticTemplateInfo} from './mocks'; import {DiagnosticContext, getDiagnosticTemplateInfo, MockLanguageServiceHost} from './mocks';
describe('expression diagnostics', () => { describe('expression diagnostics', () => {
let registry: ts.DocumentRegistry; let registry: ts.DocumentRegistry;
@ -107,7 +107,8 @@ describe('expression diagnostics', () => {
{{p.name.first}} {{p.name.last}} {{p.name.first}} {{p.name.last}}
</div> </div>
`)); `));
it('should reject misspelled field in *ngFor', () => reject( it('should reject misspelled field in *ngFor',
() => reject(
` `
<div *ngFor="let p of people"> <div *ngFor="let p of people">
{{p.names.first}} {{p.name.last}} {{p.names.first}} {{p.name.last}}
@ -124,7 +125,8 @@ describe('expression diagnostics', () => {
{{p.name.first}} {{p.name.last}} {{p.name.first}} {{p.name.last}}
</div> </div>
`)); `));
it('should reject misspelled field an async *ngFor', () => reject( it('should reject misspelled field an async *ngFor',
() => reject(
` `
<div *ngFor="let p of promised_people | async"> <div *ngFor="let p of promised_people | async">
{{p.name.first}} {{p.nume.last}} {{p.name.first}} {{p.nume.last}}
@ -136,7 +138,8 @@ describe('expression diagnostics', () => {
{{p.name.first}} {{p.name.last}} {{p.name.first}} {{p.name.last}}
</div> </div>
`)); `));
it('should reject misspelled field in async *ngIf', () => reject( it('should reject misspelled field in async *ngIf',
() => reject(
` `
<div *ngIf="promised_person | async as p"> <div *ngIf="promised_person | async as p">
{{p.name.first}} {{p.nume.last}} {{p.name.first}} {{p.nume.last}}

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript/lib/tsserverlibrary'; import * as ts from 'typescript/lib/tsserverlibrary';
import {EMPTY_SYMBOL_TABLE, createGlobalSymbolTable} from '../src/global_symbols'; import {createGlobalSymbolTable, EMPTY_SYMBOL_TABLE} from '../src/global_symbols';
import {getSymbolQuery} from '../src/typescript_symbols'; import {getSymbolQuery} from '../src/typescript_symbols';
import {MockTypescriptHost} from './test_utils'; import {MockTypescriptHost} from './test_utils';

View File

@ -22,7 +22,9 @@ describe('hover', () => {
const ngLSHost = new TypeScriptServiceHost(mockHost, tsLS); const ngLSHost = new TypeScriptServiceHost(mockHost, tsLS);
const ngLS = createLanguageService(ngLSHost); const ngLS = createLanguageService(ngLSHost);
beforeEach(() => { mockHost.reset(); }); beforeEach(() => {
mockHost.reset();
});
describe('location of hover', () => { describe('location of hover', () => {
it('should find members in a text interpolation', () => { it('should find members in a text interpolation', () => {

View File

@ -32,7 +32,6 @@ describe('html_info', () => {
} }
} }
}); });
}); });
function uniqueElements<T>(a: T[], b: T[]): T[] { function uniqueElements<T>(a: T[], b: T[]): T[] {

View File

@ -21,20 +21,25 @@ describe('service without angular', () => {
const fileName = '/app/test.ng'; const fileName = '/app/test.ng';
const position = mockHost.getLocationMarkerFor(fileName, 'h1-content').start; const position = mockHost.getLocationMarkerFor(fileName, 'h1-content').start;
beforeEach(() => { mockHost.reset(); }); beforeEach(() => {
mockHost.reset();
});
it('should not crash a get diagnostics', it('should not crash a get diagnostics', () => {
() => { expect(() => ngService.getSemanticDiagnostics(fileName)).not.toThrow(); }); expect(() => ngService.getSemanticDiagnostics(fileName)).not.toThrow();
});
it('should not crash a completion', it('should not crash a completion', () => {
() => { expect(() => ngService.getCompletionsAtPosition(fileName, position)).not.toThrow(); }); expect(() => ngService.getCompletionsAtPosition(fileName, position)).not.toThrow();
});
it('should not crash a get definition', () => { it('should not crash a get definition', () => {
expect(() => ngService.getDefinitionAndBoundSpan(fileName, position)).not.toThrow(); expect(() => ngService.getDefinitionAndBoundSpan(fileName, position)).not.toThrow();
}); });
it('should not crash a hover', it('should not crash a hover', () => {
() => { expect(() => ngService.getQuickInfoAtPosition(fileName, position)).not.toThrow(); }); expect(() => ngService.getQuickInfoAtPosition(fileName, position)).not.toThrow();
});
it('should not crash with an incomplete class', () => { it('should not crash with an incomplete class', () => {
mockHost.addCode('\nexport class'); mockHost.addCode('\nexport class');

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver} from '@angular/compiler'; import {analyzeNgModules, AotSummaryResolver, CompileMetadataResolver, CompilerConfig, createOfflineCompileUrlResolver, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, Parser, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, TemplateParser} from '@angular/compiler';
import {Directory, MockAotContext} from '@angular/compiler-cli/test/mocks'; import {Directory, MockAotContext} from '@angular/compiler-cli/test/mocks';
import {setup} from '@angular/compiler-cli/test/test_support'; import {setup} from '@angular/compiler-cli/test/test_support';
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core'; import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
@ -45,11 +45,17 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost {
this.context = new MockAotContext(currentDirectory, files); this.context = new MockAotContext(currentDirectory, files);
} }
getCompilationSettings(): ts.CompilerOptions { return this.options; } getCompilationSettings(): ts.CompilerOptions {
return this.options;
}
getScriptFileNames(): string[] { return this.scripts; } getScriptFileNames(): string[] {
return this.scripts;
}
getScriptVersion(fileName: string): string { return '0'; } getScriptVersion(fileName: string): string {
return '0';
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined { getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined {
const content = this.internalReadFile(fileName); const content = this.internalReadFile(fileName);
@ -58,15 +64,25 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost {
} }
} }
getCurrentDirectory(): string { return this.context.currentDirectory; } getCurrentDirectory(): string {
return this.context.currentDirectory;
}
getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; } getDefaultLibFileName(options: ts.CompilerOptions): string {
return 'lib.d.ts';
}
readFile(fileName: string): string { return this.internalReadFile(fileName) as string; } readFile(fileName: string): string {
return this.internalReadFile(fileName) as string;
}
readResource(fileName: string): Promise<string> { return Promise.resolve(''); } readResource(fileName: string): Promise<string> {
return Promise.resolve('');
}
assumeFileExists(fileName: string): void { this.assumedExist.add(fileName); } assumeFileExists(fileName: string): void {
this.assumedExist.add(fileName);
}
fileExists(fileName: string): boolean { fileExists(fileName: string): boolean {
return this.assumedExist.has(fileName) || this.internalReadFile(fileName) != null; return this.assumedExist.has(fileName) || this.internalReadFile(fileName) != null;
@ -99,10 +115,18 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost {
const staticSymbolCache = new StaticSymbolCache(); const staticSymbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver( const summaryResolver = new AotSummaryResolver(
{ {
loadSummary(filePath: string) { return null; }, loadSummary(filePath: string) {
isSourceFile(sourceFilePath: string) { return true; }, return null;
toSummaryFileName(sourceFilePath: string) { return sourceFilePath; }, },
fromSummaryFileName(filePath: string): string{return filePath;}, isSourceFile(sourceFilePath: string) {
return true;
},
toSummaryFileName(sourceFilePath: string) {
return sourceFilePath;
},
fromSummaryFileName(filePath: string): string {
return filePath;
},
}, },
staticSymbolCache); staticSymbolCache);
@ -117,7 +141,9 @@ export class DiagnosticContext {
public service: ts.LanguageService, public program: ts.Program, public service: ts.LanguageService, public program: ts.Program,
public checker: ts.TypeChecker, public host: StaticSymbolResolverHost) {} public checker: ts.TypeChecker, public host: StaticSymbolResolverHost) {}
private collectError(e: any, path?: string) { this._errors.push({e, path}); } private collectError(e: any, path?: string) {
this._errors.push({e, path});
}
private get staticSymbolResolver(): StaticSymbolResolver { private get staticSymbolResolver(): StaticSymbolResolver {
let result = this._staticSymbolResolver; let result = this._staticSymbolResolver;
@ -148,11 +174,15 @@ export class DiagnosticContext {
const pipeResolver = new PipeResolver(this.reflector); const pipeResolver = new PipeResolver(this.reflector);
const elementSchemaRegistry = new DomElementSchemaRegistry(); const elementSchemaRegistry = new DomElementSchemaRegistry();
const resourceLoader = new class extends ResourceLoader { const resourceLoader = new class extends ResourceLoader {
get(url: string): Promise<string> { return Promise.resolve(''); } get(url: string): Promise<string> {
return Promise.resolve('');
}
}; };
const urlResolver = createOfflineCompileUrlResolver(); const urlResolver = createOfflineCompileUrlResolver();
const htmlParser = new class extends HtmlParser { const htmlParser = new class extends HtmlParser {
parse(): ParseTreeResult { return new ParseTreeResult([], []); } parse(): ParseTreeResult {
return new ParseTreeResult([], []);
}
}; };
// This tracks the CompileConfig in codegen.ts. Currently these options // This tracks the CompileConfig in codegen.ts. Currently these options
@ -174,7 +204,11 @@ export class DiagnosticContext {
get analyzedModules(): NgAnalyzedModules { get analyzedModules(): NgAnalyzedModules {
let analyzedModules = this._analyzedModules; let analyzedModules = this._analyzedModules;
if (!analyzedModules) { if (!analyzedModules) {
const analyzeHost = {isSourceFile(filePath: string) { return true; }}; const analyzeHost = {
isSourceFile(filePath: string) {
return true;
}
};
const programFiles = this.program.getSourceFiles().map(sf => sf.fileName); const programFiles = this.program.getSourceFiles().map(sf => sf.fileName);
analyzedModules = this._analyzedModules = analyzedModules = this._analyzedModules =
analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver); analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver);
@ -214,8 +248,11 @@ function compileTemplate(context: DiagnosticContext, type: StaticSymbol, templat
return { return {
htmlAst: htmlResult.rootNodes, htmlAst: htmlResult.rootNodes,
templateAst: parseResult.templateAst, templateAst: parseResult.templateAst,
directive: metadata, directives, pipes, directive: metadata,
parseErrors: parseResult.errors, expressionParser directives,
pipes,
parseErrors: parseResult.errors,
expressionParser
}; };
} }
} }
@ -236,7 +273,9 @@ export function getDiagnosticTemplateInfo(
sourceFile, context.program, context.checker, compiledTemplate.pipes)); sourceFile, context.program, context.checker, compiledTemplate.pipes));
return { return {
fileName: templateFile, fileName: templateFile,
offset: 0, query, members, offset: 0,
query,
members,
htmlAst: compiledTemplate.htmlAst, htmlAst: compiledTemplate.htmlAst,
templateAst: compiledTemplate.templateAst, templateAst: compiledTemplate.templateAst,
source: sourceFile.text, source: sourceFile.text,

View File

@ -15,7 +15,6 @@ import {TypeScriptServiceHost} from '../src/typescript_host';
import {MockTypescriptHost} from './test_utils'; import {MockTypescriptHost} from './test_utils';
describe('reflector_host_spec', () => { describe('reflector_host_spec', () => {
// Regression #21811 // Regression #21811
it('should be able to find angular under windows', () => { it('should be able to find angular under windows', () => {
const originalJoin = path.join; const originalJoin = path.join;
@ -24,15 +23,16 @@ describe('reflector_host_spec', () => {
new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], 'node_modules', { new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], 'node_modules', {
...path, ...path,
join: (...args: string[]) => originalJoin.apply(path, args), join: (...args: string[]) => originalJoin.apply(path, args),
posix: posix: {...path.posix, join: (...args: string[]) => originalPosixJoin.apply(path, args)}
{...path.posix, join: (...args: string[]) => originalPosixJoin.apply(path, args)}
}); });
const reflectorHost = new ReflectorHost(() => undefined as any, mockHost); const reflectorHost = new ReflectorHost(() => undefined as any, mockHost);
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
// If we call this in Windows it will cause a 'Maximum call stack size exceeded error' // If we call this in Windows it will cause a 'Maximum call stack size exceeded error'
// Because we are spying on the same function that we are call faking // Because we are spying on the same function that we are call faking
spyOn(path, 'join').and.callFake((...args: string[]) => { return path.win32.join(...args); }); spyOn(path, 'join').and.callFake((...args: string[]) => {
return path.win32.join(...args);
});
} }
const result = reflectorHost.moduleNameToFileName('@angular/core'); const result = reflectorHost.moduleNameToFileName('@angular/core');

View File

@ -11,7 +11,6 @@ import {getClassDeclFromDecoratorProp} from '../src/template';
import {MockTypescriptHost} from './test_utils'; import {MockTypescriptHost} from './test_utils';
describe('getClassDeclFromTemplateNode', () => { describe('getClassDeclFromTemplateNode', () => {
it('should find class declaration in syntax-only mode', () => { it('should find class declaration in syntax-only mode', () => {
const sourceFile = ts.createSourceFile( const sourceFile = ts.createSourceFile(
'foo.ts', ` 'foo.ts', `

View File

@ -140,11 +140,17 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
this.projectVersion++; this.projectVersion++;
} }
getCompilationSettings(): ts.CompilerOptions { return {...this.options}; } getCompilationSettings(): ts.CompilerOptions {
return {...this.options};
}
getProjectVersion(): string { return this.projectVersion.toString(); } getProjectVersion(): string {
return this.projectVersion.toString();
}
getScriptFileNames(): string[] { return this.scriptNames; } getScriptFileNames(): string[] {
return this.scriptNames;
}
getScriptVersion(fileName: string): string { getScriptVersion(fileName: string): string {
return (this.scriptVersion.get(fileName) || 0).toString(); return (this.scriptVersion.get(fileName) || 0).toString();
@ -156,9 +162,13 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
return undefined; return undefined;
} }
getCurrentDirectory(): string { return '/'; } getCurrentDirectory(): string {
return '/';
}
getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; } getDefaultLibFileName(options: ts.CompilerOptions): string {
return 'lib.d.ts';
}
directoryExists(directoryName: string): boolean { directoryExists(directoryName: string): boolean {
if (this.overrideDirectory.has(directoryName)) return true; if (this.overrideDirectory.has(directoryName)) return true;
@ -172,7 +182,9 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
return this.pathExists(effectiveName); return this.pathExists(effectiveName);
} }
fileExists(fileName: string): boolean { return this.getRawFileContent(fileName) != null; } fileExists(fileName: string): boolean {
return this.getRawFileContent(fileName) != null;
}
readFile(fileName: string): string|undefined { readFile(fileName: string): string|undefined {
const content = this.getRawFileContent(fileName); const content = this.getRawFileContent(fileName);
@ -411,8 +423,9 @@ function getReferenceMarkers(value: string): ReferenceResult {
let adjustment = 0; let adjustment = 0;
const text = value.replace( const text = value.replace(
referenceMarker, (match: string, text: string, reference: string, _: string, referenceMarker,
definition: string, definitionName: string, index: number): string => { (match: string, text: string, reference: string, _: string, definition: string,
definitionName: string, index: number): string => {
const result = reference ? text : text.replace(/ᐱ/g, ''); const result = reference ? text : text.replace(/ᐱ/g, '');
const span: Span = {start: index - adjustment, end: index - adjustment + result.length}; const span: Span = {start: index - adjustment, end: index - adjustment + result.length};
const markers = reference ? references : definitions; const markers = reference ? references : definitions;

View File

@ -35,7 +35,9 @@ describe('plugin', () => {
config: {}, config: {},
}); });
beforeEach(() => { mockHost.reset(); }); beforeEach(() => {
mockHost.reset();
});
it('should produce TypeScript diagnostics', () => { it('should produce TypeScript diagnostics', () => {
const fileName = '/foo.ts'; const fileName = '/foo.ts';

View File

@ -10,7 +10,7 @@ import * as ts from 'typescript';
import {TypeScriptServiceHost} from '../src/typescript_host'; import {TypeScriptServiceHost} from '../src/typescript_host';
import {MockTypescriptHost, findDirectiveMetadataByName} from './test_utils'; import {findDirectiveMetadataByName, MockTypescriptHost} from './test_utils';
describe('TypeScriptServiceHost', () => { describe('TypeScriptServiceHost', () => {

View File

@ -18,9 +18,15 @@ import {DiagnosticContext, MockLanguageServiceHost} from './mocks';
function emptyPipes(): SymbolTable { function emptyPipes(): SymbolTable {
return { return {
size: 0, size: 0,
get(key: string) { return undefined; }, get(key: string) {
has(key: string) { return false; }, return undefined;
values(): Symbol[]{return [];} },
has(key: string) {
return false;
},
values(): Symbol[] {
return [];
}
}; };
} }