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:
parent
1b4df6484e
commit
1140bbc25c
@ -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,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -331,7 +332,7 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C
|
|||||||
elemAst = parent;
|
elemAst = parent;
|
||||||
}
|
}
|
||||||
} else if (templatePath.tail instanceof ElementAst) {
|
} else if (templatePath.tail instanceof ElementAst) {
|
||||||
refAst = new ReferenceAst(htmlAttr.name, null !, htmlAttr.value, htmlAttr.valueSpan !);
|
refAst = new ReferenceAst(htmlAttr.name, null!, htmlAttr.value, htmlAttr.valueSpan!);
|
||||||
elemAst = templatePath.tail;
|
elemAst = templatePath.tail;
|
||||||
}
|
}
|
||||||
if (refAst && elemAst) {
|
if (refAst && elemAst) {
|
||||||
@ -340,7 +341,7 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C
|
|||||||
} else {
|
} else {
|
||||||
// HtmlAst contains the `Attribute` node, however the corresponding `AttrAst`
|
// HtmlAst contains the `Attribute` node, however the corresponding `AttrAst`
|
||||||
// node is missing from the TemplateAst.
|
// node is missing from the TemplateAst.
|
||||||
const attrAst = new AttrAst(htmlAttr.name, htmlAttr.value, htmlAttr.valueSpan !);
|
const attrAst = new AttrAst(htmlAttr.name, htmlAttr.value, htmlAttr.valueSpan!);
|
||||||
attrAst.visit(visitor, null);
|
attrAst.visit(visitor, null);
|
||||||
}
|
}
|
||||||
return visitor.results;
|
return visitor.results;
|
||||||
@ -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
|
||||||
@ -577,7 +582,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
|
|||||||
}
|
}
|
||||||
} else if (binding instanceof ExpressionBinding) {
|
} else if (binding instanceof ExpressionBinding) {
|
||||||
if (inSpan(this.position, binding.value?.ast.sourceSpan)) {
|
if (inSpan(this.position, binding.value?.ast.sourceSpan)) {
|
||||||
this.processExpressionCompletions(binding.value !.ast);
|
this.processExpressionCompletions(binding.value!.ast);
|
||||||
return;
|
return;
|
||||||
} else if (!binding.value && this.position > binding.key.span.end) {
|
} else if (!binding.value && this.position > binding.key.span.end) {
|
||||||
// No expression is defined for the value of the key expression binding, but the cursor is
|
// No expression is defined for the value of the key expression binding, but the cursor is
|
||||||
@ -637,7 +642,7 @@ function angularAttributes(info: AstResult, elementName: string): AngularAttribu
|
|||||||
if (selector.element && selector.element !== elementName) {
|
if (selector.element && selector.element !== elementName) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const summary = selectorMap.get(selector) !;
|
const summary = selectorMap.get(selector)!;
|
||||||
const hasTemplateRef = isStructuralDirective(summary.type);
|
const hasTemplateRef = isStructuralDirective(summary.type);
|
||||||
// attributes are listed in (attribute, value) pairs
|
// attributes are listed in (attribute, value) pairs
|
||||||
for (let i = 0; i < selector.attrs.length; i += 2) {
|
for (let i = 0; i < selector.attrs.length; i += 2) {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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 {AstResult} from './common';
|
||||||
import {locateSymbols} from './locate_symbol';
|
import {locateSymbols} from './locate_symbol';
|
||||||
import {getPropertyAssignmentFromValue, isClassDecoratorProperty} from './template';
|
import {getPropertyAssignmentFromValue, isClassDecoratorProperty} from './template';
|
||||||
|
@ -14,15 +14,15 @@ export interface DiagnosticMessage {
|
|||||||
kind: keyof typeof ts.DiagnosticCategory;
|
kind: keyof typeof ts.DiagnosticCategory;
|
||||||
}
|
}
|
||||||
|
|
||||||
type DiagnosticName = 'directive_not_in_module' | 'missing_template_and_templateurl' |
|
type DiagnosticName = 'directive_not_in_module'|'missing_template_and_templateurl'|
|
||||||
'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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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';
|
||||||
@ -193,7 +193,7 @@ function chainDiagnostics(chain: ng.DiagnosticMessageChain): ts.DiagnosticMessag
|
|||||||
* @param file
|
* @param file
|
||||||
*/
|
*/
|
||||||
export function ngDiagnosticToTsDiagnostic(
|
export function ngDiagnosticToTsDiagnostic(
|
||||||
d: ng.Diagnostic, file: ts.SourceFile | undefined): ts.Diagnostic {
|
d: ng.Diagnostic, file: ts.SourceFile|undefined): ts.Diagnostic {
|
||||||
return {
|
return {
|
||||||
file,
|
file,
|
||||||
start: d.span.start,
|
start: d.span.start,
|
||||||
|
@ -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);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,7 +301,7 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
|
|||||||
visitVariable(ast: VariableAst): void {
|
visitVariable(ast: VariableAst): void {
|
||||||
const directive = this.directiveSummary;
|
const directive = this.directiveSummary;
|
||||||
if (directive && ast.value) {
|
if (directive && ast.value) {
|
||||||
const context = this.info.query.getTemplateContext(directive.type.reference) !;
|
const context = this.info.query.getTemplateContext(directive.type.reference)!;
|
||||||
if (context && !context.has(ast.value)) {
|
if (context && !context.has(ast.value)) {
|
||||||
const missingMember =
|
const missingMember =
|
||||||
ast.value === '$implicit' ? 'an implicit value' : `a member called '${ast.value}'`;
|
ast.value === '$implicit' ? 'an implicit value' : `a member called '${ast.value}'`;
|
||||||
@ -322,7 +327,7 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
|
|||||||
|
|
||||||
// Find directive that references this template
|
// Find directive that references this template
|
||||||
this.directiveSummary =
|
this.directiveSummary =
|
||||||
ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type)) !;
|
ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type))!;
|
||||||
|
|
||||||
// Process children
|
// Process children
|
||||||
super.visitEmbeddedTemplate(ast, context);
|
super.visitEmbeddedTemplate(ast, context);
|
||||||
@ -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 {
|
||||||
@ -366,7 +375,7 @@ function hasTemplateReference(type: CompileTypeMetadata): boolean {
|
|||||||
if (type.diDeps) {
|
if (type.diDeps) {
|
||||||
for (let diDep of type.diDeps) {
|
for (let diDep of type.diDeps) {
|
||||||
if (diDep.token && diDep.token.identifier &&
|
if (diDep.token && diDep.token.identifier &&
|
||||||
identifierName(diDep.token !.identifier !) == 'TemplateRef')
|
identifierName(diDep.token!.identifier!) == 'TemplateRef')
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
@ -204,10 +208,10 @@ export class AstType implements AstVisitor {
|
|||||||
// support contextual typing of arguments so this is simpler than TypeScript's
|
// support contextual typing of arguments so this is simpler than TypeScript's
|
||||||
// version.
|
// version.
|
||||||
const args = ast.args.map(arg => this.getType(arg));
|
const args = ast.args.map(arg => this.getType(arg));
|
||||||
const target = this.getType(ast.target !);
|
const target = this.getType(ast.target!);
|
||||||
if (!target || !target.callable) {
|
if (!target || !target.callable) {
|
||||||
this.diagnostics.push(createDiagnostic(
|
this.diagnostics.push(createDiagnostic(
|
||||||
ast.span, Diagnostic.call_target_not_callable, this.sourceOf(ast.target !), target.name));
|
ast.span, Diagnostic.call_target_not_callable, this.sourceOf(ast.target!), target.name));
|
||||||
return this.anyType;
|
return this.anyType;
|
||||||
}
|
}
|
||||||
const signature = target.selectSignature(args);
|
const signature = target.selectSignature(args);
|
||||||
@ -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;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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';
|
||||||
@ -41,7 +42,7 @@ export function getExpressionCompletions(
|
|||||||
undefined {
|
undefined {
|
||||||
const path = findAstAt(ast, position);
|
const path = findAstAt(ast, position);
|
||||||
if (path.empty) return undefined;
|
if (path.empty) return undefined;
|
||||||
const tail = path.tail !;
|
const tail = path.tail!;
|
||||||
let result: SymbolTable|undefined = scope;
|
let result: SymbolTable|undefined = scope;
|
||||||
|
|
||||||
function getType(ast: AST): Symbol {
|
function getType(ast: AST): Symbol {
|
||||||
@ -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) {},
|
||||||
@ -111,7 +114,7 @@ export function getExpressionSymbol(
|
|||||||
templateInfo: TemplateSource): {symbol: Symbol, span: Span}|undefined {
|
templateInfo: TemplateSource): {symbol: Symbol, span: Span}|undefined {
|
||||||
const path = findAstAt(ast, position, /* excludeEmpty */ true);
|
const path = findAstAt(ast, position, /* excludeEmpty */ true);
|
||||||
if (path.empty) return undefined;
|
if (path.empty) return undefined;
|
||||||
const tail = path.tail !;
|
const tail = path.tail!;
|
||||||
|
|
||||||
function getType(ast: AST): Symbol {
|
function getType(ast: AST): Symbol {
|
||||||
return new AstType(scope, templateInfo.query, {}, templateInfo.source).getType(ast);
|
return new AstType(scope, templateInfo.query, {}, templateInfo.source).getType(ast);
|
||||||
|
@ -37,14 +37,15 @@ export function getTemplateHover(
|
|||||||
// The container is either the symbol's container (for example, 'AppComponent'
|
// The container is either the symbol's container (for example, 'AppComponent'
|
||||||
// is the container of the symbol 'title' in its template) or the NgModule
|
// is the container of the symbol 'title' in its template) or the NgModule
|
||||||
// that the directive belongs to (the container of AppComponent is AppModule).
|
// that the directive belongs to (the container of AppComponent is AppModule).
|
||||||
let containerName: string|undefined = symbol.container ?.name;
|
let containerName: string|undefined = symbol.container?.name;
|
||||||
if (!containerName && staticSymbol) {
|
if (!containerName && staticSymbol) {
|
||||||
// If there is a static symbol then the target is a directive.
|
// If there is a static symbol then the target is a directive.
|
||||||
const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
|
const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,7 +64,7 @@ export function getTsHover(
|
|||||||
const kind = metadata.isComponent ? 'component' : 'directive';
|
const kind = metadata.isComponent ? 'component' : 'directive';
|
||||||
const textSpan = ts.createTextSpanFromBounds(declarationSpan.start, declarationSpan.end);
|
const textSpan = ts.createTextSpanFromBounds(declarationSpan.start, declarationSpan.end);
|
||||||
const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
|
const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
|
||||||
const moduleName = ngModule ?.type.reference.name;
|
const moduleName = ngModule?.type.reference.name;
|
||||||
return createQuickInfo(
|
return createQuickInfo(
|
||||||
directiveName, kind, textSpan, moduleName, ts.ScriptElementKind.classElement);
|
directiveName, kind, textSpan, moduleName, ts.ScriptElementKind.classElement);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
// This section defines the HTML elements and attribute surface of HTML 4
|
// This section defines the HTML elements and attribute surface of HTML 4
|
||||||
// which is derived from https://www.w3.org/TR/html4/strict.dtd
|
// which is derived from https://www.w3.org/TR/html4/strict.dtd
|
||||||
type attrType = string | string[];
|
type attrType = string|string[];
|
||||||
type hash<T> = {
|
type hash<T> = {
|
||||||
[name: string]: T
|
[name: string]: T
|
||||||
};
|
};
|
||||||
@ -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[]} = {
|
||||||
@ -193,7 +195,7 @@ export function elementNames(): string[] {
|
|||||||
return Object.keys(elements).sort().map(v => v.toLowerCase());
|
return Object.keys(elements).sort().map(v => v.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
function compose(indexes: number[] | undefined): hash<attrType> {
|
function compose(indexes: number[]|undefined): hash<attrType> {
|
||||||
const result: hash<attrType> = {};
|
const result: hash<attrType> = {};
|
||||||
if (indexes) {
|
if (indexes) {
|
||||||
for (let index of indexes) {
|
for (let index of indexes) {
|
||||||
@ -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()] || {};
|
||||||
|
@ -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';
|
||||||
@ -77,7 +77,7 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
|
|||||||
}
|
}
|
||||||
if (result) {
|
if (result) {
|
||||||
symbol = result.symbol;
|
symbol = result.symbol;
|
||||||
span = offsetSpan(result.span, attribute.valueSpan !.start.offset);
|
span = offsetSpan(result.span, attribute.valueSpan!.start.offset);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,7 +219,7 @@ function getSymbolInMicrosyntax(info: AstResult, path: TemplateAstPath, attribut
|
|||||||
if (inSpan(path.position, tb.value?.ast.sourceSpan)) {
|
if (inSpan(path.position, tb.value?.ast.sourceSpan)) {
|
||||||
const dinfo = diagnosticInfoFromTemplateInfo(info);
|
const dinfo = diagnosticInfoFromTemplateInfo(info);
|
||||||
const scope = getExpressionScope(dinfo, path);
|
const scope = getExpressionScope(dinfo, path);
|
||||||
result = getExpressionSymbol(scope, tb.value !, path.position, info.template);
|
result = getExpressionSymbol(scope, tb.value!, path.position, info.template);
|
||||||
} else if (inSpan(path.position, tb.sourceSpan)) {
|
} else if (inSpan(path.position, tb.sourceSpan)) {
|
||||||
const template = path.first(EmbeddedTemplateAst);
|
const template = path.first(EmbeddedTemplateAst);
|
||||||
if (template) {
|
if (template) {
|
||||||
@ -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 name(): string {
|
||||||
|
return this.sym.name;
|
||||||
|
}
|
||||||
|
|
||||||
get language(): string { return this.sym.language; }
|
get language(): string {
|
||||||
|
return this.sym.language;
|
||||||
|
}
|
||||||
|
|
||||||
get type(): Symbol|undefined { return this.sym.type; }
|
get type(): Symbol|undefined {
|
||||||
|
return this.sym.type;
|
||||||
|
}
|
||||||
|
|
||||||
get container(): Symbol|undefined { return this.sym.container; }
|
get container(): Symbol|undefined {
|
||||||
|
return this.sym.container;
|
||||||
|
}
|
||||||
|
|
||||||
get public(): boolean { return this.sym.public; }
|
get public(): boolean {
|
||||||
|
return this.sym.public;
|
||||||
|
}
|
||||||
|
|
||||||
get callable(): boolean { return this.sym.callable; }
|
get callable(): boolean {
|
||||||
|
return this.sym.callable;
|
||||||
|
}
|
||||||
|
|
||||||
get nullable(): boolean { return this.sym.nullable; }
|
get nullable(): boolean {
|
||||||
|
return this.sym.nullable;
|
||||||
|
}
|
||||||
|
|
||||||
get definition(): Definition { return this.sym.definition; }
|
get definition(): Definition {
|
||||||
|
return this.sym.definition;
|
||||||
|
}
|
||||||
|
|
||||||
get documentation(): ts.SymbolDisplayPart[] { return this.sym.documentation; }
|
get documentation(): ts.SymbolDisplayPart[] {
|
||||||
|
return this.sym.documentation;
|
||||||
|
}
|
||||||
|
|
||||||
members() { return this.sym.members(); }
|
members() {
|
||||||
|
return this.sym.members();
|
||||||
|
}
|
||||||
|
|
||||||
signatures() { return this.sym.signatures(); }
|
signatures() {
|
||||||
|
return this.sym.signatures();
|
||||||
|
}
|
||||||
|
|
||||||
selectSignature(types: Symbol[]) { return this.sym.selectSignature(types); }
|
selectSignature(types: Symbol[]) {
|
||||||
|
return this.sym.selectSignature(types);
|
||||||
|
}
|
||||||
|
|
||||||
indexed(argument: Symbol) { return this.sym.indexed(argument); }
|
indexed(argument: Symbol) {
|
||||||
|
return this.sym.indexed(argument);
|
||||||
|
}
|
||||||
|
|
||||||
typeArguments(): Symbol[]|undefined { return this.sym.typeArguments(); }
|
typeArguments(): Symbol[]|undefined {
|
||||||
|
return this.sym.typeArguments();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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';
|
||||||
|
|
||||||
@ -26,10 +26,10 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost, Me
|
|||||||
private readonly tsLSHost: ts.LanguageServiceHost,
|
private readonly tsLSHost: ts.LanguageServiceHost,
|
||||||
private readonly getProgram: () => ts.Program) {
|
private readonly getProgram: () => ts.Program) {
|
||||||
if (tsLSHost.directoryExists) {
|
if (tsLSHost.directoryExists) {
|
||||||
this.directoryExists = directoryName => tsLSHost.directoryExists !(directoryName);
|
this.directoryExists = directoryName => tsLSHost.directoryExists!(directoryName);
|
||||||
}
|
}
|
||||||
if (tsLSHost.realpath) {
|
if (tsLSHost.realpath) {
|
||||||
this.realpath = path => tsLSHost.realpath !(path);
|
this.realpath = path => tsLSHost.realpath!(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,14 +53,14 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost, Me
|
|||||||
// resolution, and it's used by Angular to read metadata.json during
|
// resolution, and it's used by Angular to read metadata.json during
|
||||||
// metadata resolution.
|
// metadata resolution.
|
||||||
if (this.tsLSHost.readFile) {
|
if (this.tsLSHost.readFile) {
|
||||||
return this.tsLSHost.readFile(fileName) !;
|
return this.tsLSHost.readFile(fileName)!;
|
||||||
}
|
}
|
||||||
// As a fallback, read the JSON files from the editor snapshot.
|
// As a fallback, read the JSON files from the editor snapshot.
|
||||||
const snapshot = this.tsLSHost.getScriptSnapshot(fileName);
|
const snapshot = this.tsLSHost.getScriptSnapshot(fileName);
|
||||||
if (!snapshot) {
|
if (!snapshot) {
|
||||||
// MetadataReaderHost readFile() declaration should be
|
// MetadataReaderHost readFile() declaration should be
|
||||||
// `readFile(fileName: string): string | undefined`
|
// `readFile(fileName: string): string | undefined`
|
||||||
return undefined !;
|
return undefined!;
|
||||||
}
|
}
|
||||||
return snapshot.getText(0, snapshot.getLength());
|
return snapshot.getText(0, snapshot.getLength());
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ export interface Location {
|
|||||||
/**
|
/**
|
||||||
* A defnition location(s).
|
* A defnition location(s).
|
||||||
*/
|
*/
|
||||||
export type Definition = Location[] | undefined;
|
export type Definition = Location[]|undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A symbol describing a language element that can be referenced by expressions
|
* A symbol describing a language element that can be referenced by expressions
|
||||||
@ -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.
|
||||||
@ -287,7 +287,7 @@ export interface PipeInfo {
|
|||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export type Pipes = PipeInfo[] | undefined;
|
export type Pipes = PipeInfo[]|undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the language context in which an Angular expression is evaluated.
|
* Describes the language context in which an Angular expression is evaluated.
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ export interface TemplateSource {
|
|||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export type TemplateSources = TemplateSource[] | undefined;
|
export type TemplateSources = TemplateSource[]|undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error information found getting declaration information
|
* Error information found getting declaration information
|
||||||
@ -264,7 +264,7 @@ export enum CompletionKind {
|
|||||||
VARIABLE = 'variable',
|
VARIABLE = 'variable',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CompletionEntry = Omit<ts.CompletionEntry, 'kind'>& {
|
export type CompletionEntry = Omit<ts.CompletionEntry, 'kind'>&{
|
||||||
kind: CompletionKind,
|
kind: CompletionKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -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'>;
|
||||||
|
@ -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);
|
||||||
@ -168,7 +184,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||||||
const urlResolver = createOfflineCompileUrlResolver();
|
const urlResolver = createOfflineCompileUrlResolver();
|
||||||
for (const ngModule of this.analyzedModules.ngModules) {
|
for (const ngModule of this.analyzedModules.ngModules) {
|
||||||
for (const directive of ngModule.declaredDirectives) {
|
for (const directive of ngModule.declaredDirectives) {
|
||||||
const {metadata} = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference) !;
|
const {metadata} = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference)!;
|
||||||
if (metadata.isComponent && metadata.template && metadata.template.templateUrl) {
|
if (metadata.isComponent && metadata.template && metadata.template.templateUrl) {
|
||||||
const templateName = urlResolver.resolve(
|
const templateName = urlResolver.resolve(
|
||||||
this.reflector.componentModuleUrl(directive.reference),
|
this.reflector.componentModuleUrl(directive.reference),
|
||||||
@ -494,9 +510,9 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||||||
const parser = new TemplateParser(
|
const parser = new TemplateParser(
|
||||||
new CompilerConfig(), this.reflector, expressionParser, new DomElementSchemaRegistry(),
|
new CompilerConfig(), this.reflector, expressionParser, new DomElementSchemaRegistry(),
|
||||||
htmlParser,
|
htmlParser,
|
||||||
null !, // console
|
null!, // console
|
||||||
[] // tranforms
|
[] // tranforms
|
||||||
);
|
);
|
||||||
const htmlResult = htmlParser.parse(template.source, fileName, {
|
const htmlResult = htmlParser.parse(template.source, fileName, {
|
||||||
tokenizeExpansionForms: true,
|
tokenizeExpansionForms: true,
|
||||||
preserveLineEndings: true, // do not convert CRLF to LF
|
preserveLineEndings: true, // do not convert CRLF to LF
|
||||||
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,7 +594,7 @@ function spanOf(node: tss.Node): Span {
|
|||||||
function spanAt(sourceFile: tss.SourceFile, line: number, column: number): Span|undefined {
|
function spanAt(sourceFile: tss.SourceFile, line: number, column: number): Span|undefined {
|
||||||
if (line != null && column != null) {
|
if (line != null && column != null) {
|
||||||
const position = tss.getPositionOfLineAndCharacter(sourceFile, line, column);
|
const position = tss.getPositionOfLineAndCharacter(sourceFile, line, column);
|
||||||
const findChild = function findChild(node: tss.Node): tss.Node | undefined {
|
const findChild = function findChild(node: tss.Node): tss.Node|undefined {
|
||||||
if (node.kind > tss.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
|
if (node.kind > tss.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
|
||||||
const betterNode = tss.forEachChild(node, findChild);
|
const betterNode = tss.forEachChild(node, findChild);
|
||||||
return betterNode || node;
|
return betterNode || node;
|
||||||
|
@ -68,7 +68,7 @@ export function getClassFromStaticSymbol(
|
|||||||
return classDeclaration;
|
return classDeclaration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) as(ts.ClassDeclaration | undefined);
|
}) as (ts.ClassDeclaration | undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -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) {
|
||||||
@ -193,13 +195,13 @@ class TypeScriptSymbolQuery implements SymbolQuery {
|
|||||||
private getTemplateRefContextType(typeSymbol: ts.Symbol, context: TypeContext): Symbol|undefined {
|
private getTemplateRefContextType(typeSymbol: ts.Symbol, context: TypeContext): Symbol|undefined {
|
||||||
const type = this.checker.getTypeOfSymbolAtLocation(typeSymbol, this.source);
|
const type = this.checker.getTypeOfSymbolAtLocation(typeSymbol, this.source);
|
||||||
const constructor = type.symbol && type.symbol.members &&
|
const constructor = type.symbol && type.symbol.members &&
|
||||||
getFromSymbolTable(type.symbol.members !, '__constructor');
|
getFromSymbolTable(type.symbol.members!, '__constructor');
|
||||||
|
|
||||||
if (constructor) {
|
if (constructor) {
|
||||||
const constructorDeclaration = constructor.declarations ![0] as ts.ConstructorTypeNode;
|
const constructorDeclaration = constructor.declarations![0] as ts.ConstructorTypeNode;
|
||||||
for (const parameter of constructorDeclaration.parameters) {
|
for (const parameter of constructorDeclaration.parameters) {
|
||||||
const type = this.checker.getTypeAtLocation(parameter.type !);
|
const type = this.checker.getTypeAtLocation(parameter.type!);
|
||||||
if (type.symbol !.name == 'TemplateRef' && isReferenceType(type)) {
|
if (type.symbol!.name == 'TemplateRef' && isReferenceType(type)) {
|
||||||
const typeWrapper = new TypeWrapper(type, context);
|
const typeWrapper = new TypeWrapper(type, context);
|
||||||
const typeArguments = typeWrapper.typeArguments();
|
const typeArguments = typeWrapper.typeArguments();
|
||||||
if (typeArguments && typeArguments.length === 1) {
|
if (typeArguments && typeArguments.length === 1) {
|
||||||
@ -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);
|
||||||
@ -368,21 +390,27 @@ class SymbolWrapper implements Symbol {
|
|||||||
const typeWrapper = new TypeWrapper(declaredType, this.context);
|
const typeWrapper = new TypeWrapper(declaredType, this.context);
|
||||||
this._members = typeWrapper.members();
|
this._members = typeWrapper.members();
|
||||||
} else {
|
} else {
|
||||||
this._members = new SymbolTableWrapper(this.symbol.members !, this.context, this.tsType);
|
this._members = new SymbolTableWrapper(this.symbol.members!, this.context, this.tsType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
||||||
@ -456,7 +514,7 @@ export function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable {
|
|||||||
return result as ts.SymbolTable;
|
return result as ts.SymbolTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toSymbols(symbolTable: ts.SymbolTable | undefined): ts.Symbol[] {
|
function toSymbols(symbolTable: ts.SymbolTable|undefined): ts.Symbol[] {
|
||||||
if (!symbolTable) return [];
|
if (!symbolTable) return [];
|
||||||
|
|
||||||
const table = symbolTable as any;
|
const table = symbolTable as any;
|
||||||
@ -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,20 +595,26 @@ 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)) {
|
||||||
const previous = this.map.get(symbol.name) !;
|
const previous = this.map.get(symbol.name)!;
|
||||||
this._values[this._values.indexOf(previous)] = symbol;
|
this._values[this._values.indexOf(previous)] = symbol;
|
||||||
}
|
}
|
||||||
this.map.set(symbol.name, symbol);
|
this.map.set(symbol.name, symbol);
|
||||||
@ -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,12 +695,16 @@ 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)!;
|
||||||
if (types.length > 0) {
|
if (types.length > 0) {
|
||||||
const parameterType = types[0];
|
const parameterType = types[0];
|
||||||
let resultType: Symbol|undefined = undefined;
|
let resultType: Symbol|undefined = undefined;
|
||||||
@ -645,16 +727,20 @@ 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;
|
||||||
if (!type) {
|
if (!type) {
|
||||||
const classSymbol = this.findClassSymbol(this.pipe.type.reference);
|
const classSymbol = this.findClassSymbol(this.pipe.type.reference);
|
||||||
if (classSymbol) {
|
if (classSymbol) {
|
||||||
type = this._tsType = this.findTransformMethodType(classSymbol) !;
|
type = this._tsType = this.findTransformMethodType(classSymbol)!;
|
||||||
}
|
}
|
||||||
if (!type) {
|
if (!type) {
|
||||||
type = this._tsType = getTsTypeFromBuiltinType(BuiltinType.Any, this.context);
|
type = this._tsType = getTsTypeFromBuiltinType(BuiltinType.Any, this.context);
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,7 +835,7 @@ function getTsTypeFromBuiltinType(builtinType: BuiltinType, ctx: TypeContext): t
|
|||||||
function spanAt(sourceFile: ts.SourceFile, line: number, column: number): Span|undefined {
|
function spanAt(sourceFile: ts.SourceFile, line: number, column: number): Span|undefined {
|
||||||
if (line != null && column != null) {
|
if (line != null && column != null) {
|
||||||
const position = ts.getPositionOfLineAndCharacter(sourceFile, line, column);
|
const position = ts.getPositionOfLineAndCharacter(sourceFile, line, column);
|
||||||
const findChild = function findChild(node: ts.Node): ts.Node | undefined {
|
const findChild = function findChild(node: ts.Node): ts.Node|undefined {
|
||||||
if (node.kind > ts.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
|
if (node.kind > ts.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
|
||||||
const betterNode = ts.forEachChild(node, findChild);
|
const betterNode = ts.forEachChild(node, findChild);
|
||||||
return betterNode || node;
|
return betterNode || node;
|
||||||
@ -779,7 +871,7 @@ function parentDeclarationOf(node: ts.Node): ts.Node|undefined {
|
|||||||
case ts.SyntaxKind.SourceFile:
|
case ts.SyntaxKind.SourceFile:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
node = node.parent !;
|
node = node.parent!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -797,7 +889,7 @@ function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol|undefin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function typeKindOf(type: ts.Type | undefined): BuiltinType {
|
function typeKindOf(type: ts.Type|undefined): BuiltinType {
|
||||||
if (type) {
|
if (type) {
|
||||||
if (type.flags & ts.TypeFlags.Any) {
|
if (type.flags & ts.TypeFlags.Any) {
|
||||||
return BuiltinType.Any;
|
return BuiltinType.Any;
|
||||||
|
@ -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';
|
||||||
@ -25,8 +25,8 @@ export function isParseSourceSpan(value: any): value is ParseSourceSpan {
|
|||||||
|
|
||||||
export function spanOf(span: SpanHolder): Span;
|
export function spanOf(span: SpanHolder): Span;
|
||||||
export function spanOf(span: ParseSourceSpan): Span;
|
export function spanOf(span: ParseSourceSpan): Span;
|
||||||
export function spanOf(span: SpanHolder | ParseSourceSpan | undefined): Span|undefined;
|
export function spanOf(span: SpanHolder|ParseSourceSpan|undefined): Span|undefined;
|
||||||
export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
|
export function spanOf(span?: SpanHolder|ParseSourceSpan): Span|undefined {
|
||||||
if (!span) return undefined;
|
if (!span) return undefined;
|
||||||
if (isParseSourceSpan(span)) {
|
if (isParseSourceSpan(span)) {
|
||||||
return {start: span.start.offset, end: span.end.offset};
|
return {start: span.start.offset, end: span.end.offset};
|
||||||
@ -36,7 +36,7 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
|
|||||||
} else if (span.children && span.children.length) {
|
} else if (span.children && span.children.length) {
|
||||||
return {
|
return {
|
||||||
start: span.sourceSpan.start.offset,
|
start: span.sourceSpan.start.offset,
|
||||||
end: spanOf(span.children[span.children.length - 1]) !.end
|
end: spanOf(span.children[span.children.length - 1])!.end
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {start: span.sourceSpan.start.offset, end: span.sourceSpan.end.offset};
|
return {start: span.sourceSpan.start.offset, end: span.sourceSpan.end.offset};
|
||||||
@ -44,8 +44,9 @@ 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 &&
|
||||||
position >= span.start && position <= span.end);
|
(exclusive ? position >= span.start && position < span.end :
|
||||||
|
position >= span.start && position <= span.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function offsetSpan(span: Span, amount: number): Span {
|
export function offsetSpan(span: Span, amount: number): Span {
|
||||||
@ -71,7 +72,7 @@ export function getSelectors(info: AstResult): SelectorInfo {
|
|||||||
const map = new Map<CssSelector, CompileDirectiveSummary>();
|
const map = new Map<CssSelector, CompileDirectiveSummary>();
|
||||||
const results: CssSelector[] = [];
|
const results: CssSelector[] = [];
|
||||||
for (const directive of info.directives) {
|
for (const directive of info.directives) {
|
||||||
const selectors: CssSelector[] = CssSelector.parse(directive.selector !);
|
const selectors: CssSelector[] = CssSelector.parse(directive.selector!);
|
||||||
for (const selector of selectors) {
|
for (const selector of selectors) {
|
||||||
results.push(selector);
|
results.push(selector);
|
||||||
map.set(selector, directive);
|
map.set(selector, directive);
|
||||||
@ -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();
|
||||||
|
@ -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');
|
||||||
@ -102,7 +104,7 @@ describe('completions', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'h2-hero');
|
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'h2-hero');
|
||||||
const completions = ngLS.getCompletionsAtPosition(APP_COMPONENT, marker.start);
|
const completions = ngLS.getCompletionsAtPosition(APP_COMPONENT, marker.start);
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const internal = completions !.entries.find(e => e.name === 'internal');
|
const internal = completions!.entries.find(e => e.name === 'internal');
|
||||||
expect(internal).toBeUndefined();
|
expect(internal).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -202,7 +204,7 @@ describe('completions', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
expect(completions !.entries).toContain(jasmine.objectContaining({
|
expect(completions!.entries).toContain(jasmine.objectContaining({
|
||||||
name: 'myClick',
|
name: 'myClick',
|
||||||
kind: CompletionKind.METHOD as any,
|
kind: CompletionKind.METHOD as any,
|
||||||
insertText: 'myClick()',
|
insertText: 'myClick()',
|
||||||
@ -214,7 +216,7 @@ describe('completions', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'pipe-method');
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'pipe-method');
|
||||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
expect(completions !.entries).toContain(jasmine.objectContaining({
|
expect(completions!.entries).toContain(jasmine.objectContaining({
|
||||||
name: 'lowercase',
|
name: 'lowercase',
|
||||||
kind: CompletionKind.PIPE as any,
|
kind: CompletionKind.PIPE as any,
|
||||||
insertText: 'lowercase',
|
insertText: 'lowercase',
|
||||||
@ -234,7 +236,7 @@ describe('completions', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, location);
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, location);
|
||||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const {entries} = completions !;
|
const {entries} = completions!;
|
||||||
expect(entries).not.toContain(jasmine.objectContaining({name: 'div'}));
|
expect(entries).not.toContain(jasmine.objectContaining({name: 'div'}));
|
||||||
expect(entries).not.toContain(jasmine.objectContaining({name: 'h1'}));
|
expect(entries).not.toContain(jasmine.objectContaining({name: 'h1'}));
|
||||||
expect(entries).not.toContain(jasmine.objectContaining({name: 'h2'}));
|
expect(entries).not.toContain(jasmine.objectContaining({name: 'h2'}));
|
||||||
@ -257,7 +259,7 @@ describe('completions', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'h1-after-space');
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'h1-after-space');
|
||||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const {entries} = completions !;
|
const {entries} = completions!;
|
||||||
expect(entries).not.toContain(jasmine.objectContaining({name: 'class'}));
|
expect(entries).not.toContain(jasmine.objectContaining({name: 'class'}));
|
||||||
expect(entries).not.toContain(jasmine.objectContaining({name: 'id'}));
|
expect(entries).not.toContain(jasmine.objectContaining({name: 'id'}));
|
||||||
expect(entries).not.toContain(jasmine.objectContaining({name: 'onclick'}));
|
expect(entries).not.toContain(jasmine.objectContaining({name: 'onclick'}));
|
||||||
@ -536,9 +538,9 @@ describe('completions', () => {
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'key');
|
const location = mockHost.getLocationMarkerFor(fileName, 'key');
|
||||||
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === 'key') !;
|
const completion = completions.entries.find(entry => entry.name === 'key')!;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('property');
|
expect(completion.kind).toBe('property');
|
||||||
expect(completion.replacementSpan).toBeUndefined();
|
expect(completion.replacementSpan).toBeUndefined();
|
||||||
@ -553,9 +555,9 @@ describe('completions', () => {
|
|||||||
export class FooComponent {}
|
export class FooComponent {}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'start');
|
const location = mockHost.getLocationMarkerFor(fileName, 'start');
|
||||||
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === 'acronym') !;
|
const completion = completions.entries.find(entry => entry.name === 'acronym')!;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('html element');
|
expect(completion.kind).toBe('html element');
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start, length: 3});
|
expect(completion.replacementSpan).toEqual({start: location.start, length: 3});
|
||||||
@ -570,9 +572,9 @@ describe('completions', () => {
|
|||||||
export class FooComponent {}
|
export class FooComponent {}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'end');
|
const location = mockHost.getLocationMarkerFor(fileName, 'end');
|
||||||
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === 'acronym') !;
|
const completion = completions.entries.find(entry => entry.name === 'acronym')!;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('html element');
|
expect(completion.kind).toBe('html element');
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 4, length: 4});
|
expect(completion.replacementSpan).toEqual({start: location.start - 4, length: 4});
|
||||||
@ -591,9 +593,9 @@ describe('completions', () => {
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'key');
|
const location = mockHost.getLocationMarkerFor(fileName, 'key');
|
||||||
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === 'key') !;
|
const completion = completions.entries.find(entry => entry.name === 'key')!;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('property');
|
expect(completion.kind).toBe('property');
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 5});
|
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 5});
|
||||||
@ -612,9 +614,9 @@ describe('completions', () => {
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'field');
|
const location = mockHost.getLocationMarkerFor(fileName, 'field');
|
||||||
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === '$title_1') !;
|
const completion = completions.entries.find(entry => entry.name === '$title_1')!;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('property');
|
expect(completion.kind).toBe('property');
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start, length: 8});
|
expect(completion.replacementSpan).toEqual({start: location.start, length: 8});
|
||||||
@ -631,9 +633,9 @@ describe('completions', () => {
|
|||||||
export class FooComponent {}
|
export class FooComponent {}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'click');
|
const location = mockHost.getLocationMarkerFor(fileName, 'click');
|
||||||
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === 'click') !;
|
const completion = completions.entries.find(entry => entry.name === 'click')!;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe(CompletionKind.ATTRIBUTE);
|
expect(completion.kind).toBe(CompletionKind.ATTRIBUTE);
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
|
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
|
||||||
@ -652,9 +654,9 @@ describe('completions', () => {
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'handleClick');
|
const location = mockHost.getLocationMarkerFor(fileName, 'handleClick');
|
||||||
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === 'handleClick') !;
|
const completion = completions.entries.find(entry => entry.name === 'handleClick')!;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('method');
|
expect(completion.kind).toBe('method');
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 3, length: 3});
|
expect(completion.replacementSpan).toEqual({start: location.start - 3, length: 3});
|
||||||
@ -671,9 +673,9 @@ describe('completions', () => {
|
|||||||
export class FooComponent {}
|
export class FooComponent {}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'div');
|
const location = mockHost.getLocationMarkerFor(fileName, 'div');
|
||||||
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === 'div') !;
|
const completion = completions.entries.find(entry => entry.name === 'div')!;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('html element');
|
expect(completion.kind).toBe('html element');
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
|
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
|
||||||
@ -690,9 +692,9 @@ describe('completions', () => {
|
|||||||
export class FooComponent {}
|
export class FooComponent {}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'model');
|
const location = mockHost.getLocationMarkerFor(fileName, 'model');
|
||||||
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === 'ngModel') !;
|
const completion = completions.entries.find(entry => entry.name === 'ngModel')!;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe(CompletionKind.ATTRIBUTE);
|
expect(completion.kind).toBe(CompletionKind.ATTRIBUTE);
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 5, length: 5});
|
expect(completion.replacementSpan).toEqual({start: location.start - 5, length: 5});
|
||||||
@ -741,14 +743,14 @@ describe('completions', () => {
|
|||||||
it('should be able to get the completions (ref- prefix)', () => {
|
it('should be able to get the completions (ref- prefix)', () => {
|
||||||
mockHost.override(TEST_TEMPLATE, `<form ref-itemForm="ngF~{reference}"></form>`);
|
mockHost.override(TEST_TEMPLATE, `<form ref-itemForm="ngF~{reference}"></form>`);
|
||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'reference');
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'reference');
|
||||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start) !;
|
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start)!;
|
||||||
expectContain(completions, CompletionKind.REFERENCE, ['ngForm']);
|
expectContain(completions, CompletionKind.REFERENCE, ['ngForm']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to get the completions (# prefix)', () => {
|
it('should be able to get the completions (# prefix)', () => {
|
||||||
mockHost.override(TEST_TEMPLATE, `<form #itemForm="ngF~{reference}"></form>`);
|
mockHost.override(TEST_TEMPLATE, `<form #itemForm="ngF~{reference}"></form>`);
|
||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'reference');
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'reference');
|
||||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start) !;
|
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start)!;
|
||||||
expectContain(completions, CompletionKind.REFERENCE, ['ngForm']);
|
expectContain(completions, CompletionKind.REFERENCE, ['ngForm']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -789,9 +791,9 @@ describe('completions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function expectContain(
|
function expectContain(
|
||||||
completions: ts.CompletionInfo | undefined, kind: CompletionKind, names: string[]) {
|
completions: ts.CompletionInfo|undefined, kind: CompletionKind, names: string[]) {
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
expect(completions !.entries).toContain(jasmine.objectContaining({ name, kind } as any));
|
expect(completions!.entries).toContain(jasmine.objectContaining({name, kind} as any));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(`
|
||||||
@ -36,12 +38,12 @@ describe('definitions', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(fileName, 'name');
|
const marker = mockHost.getReferenceMarkerFor(fileName, 'name');
|
||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
|
|
||||||
expect(def.fileName).toBe(fileName);
|
expect(def.fileName).toBe(fileName);
|
||||||
expect(def.name).toBe('name');
|
expect(def.name).toBe('name');
|
||||||
@ -55,20 +57,20 @@ describe('definitions', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
||||||
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
|
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
|
|
||||||
expect(def.fileName).toBe(PARSING_CASES);
|
expect(def.fileName).toBe(PARSING_CASES);
|
||||||
expect(def.name).toBe('title');
|
expect(def.name).toBe('title');
|
||||||
expect(def.kind).toBe('property');
|
expect(def.kind).toBe('property');
|
||||||
|
|
||||||
const fileContent = mockHost.readFile(def.fileName);
|
const fileContent = mockHost.readFile(def.fileName);
|
||||||
expect(fileContent !.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length))
|
expect(fileContent!.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length))
|
||||||
.toEqual(`title = 'Some title';`);
|
.toEqual(`title = 'Some title';`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -84,12 +86,12 @@ describe('definitions', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(fileName, 'myClick');
|
const marker = mockHost.getReferenceMarkerFor(fileName, 'myClick');
|
||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
expect(textSpan).toEqual(mockHost.getLocationMarkerFor(fileName, 'my'));
|
expect(textSpan).toEqual(mockHost.getLocationMarkerFor(fileName, 'my'));
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
|
|
||||||
expect(def.fileName).toBe(fileName);
|
expect(def.fileName).toBe(fileName);
|
||||||
expect(def.name).toBe('myClick');
|
expect(def.name).toBe('myClick');
|
||||||
@ -109,12 +111,12 @@ describe('definitions', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(fileName, 'include');
|
const marker = mockHost.getReferenceMarkerFor(fileName, 'include');
|
||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
|
|
||||||
expect(def.fileName).toBe(fileName);
|
expect(def.fileName).toBe(fileName);
|
||||||
expect(def.name).toBe('include');
|
expect(def.name).toBe('include');
|
||||||
@ -134,7 +136,7 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
// Get the marker for bounded text in the code added above.
|
// Get the marker for bounded text in the code added above.
|
||||||
const boundedText = mockHost.getLocationMarkerFor(fileName, 'my');
|
const boundedText = mockHost.getLocationMarkerFor(fileName, 'my');
|
||||||
@ -142,14 +144,14 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
// There should be exactly 1 definition
|
// There should be exactly 1 definition
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
|
|
||||||
const refFileName = '/app/parsing-cases.ts';
|
const refFileName = '/app/parsing-cases.ts';
|
||||||
expect(def.fileName).toBe(refFileName);
|
expect(def.fileName).toBe(refFileName);
|
||||||
expect(def.name).toBe('TestComponent');
|
expect(def.name).toBe('TestComponent');
|
||||||
expect(def.kind).toBe('component');
|
expect(def.kind).toBe('component');
|
||||||
const content = mockHost.readFile(refFileName) !;
|
const content = mockHost.readFile(refFileName)!;
|
||||||
const begin = '/*BeginTestComponent*/ ';
|
const begin = '/*BeginTestComponent*/ ';
|
||||||
const start = content.indexOf(begin) + begin.length;
|
const start = content.indexOf(begin) + begin.length;
|
||||||
const end = content.indexOf(' /*EndTestComponent*/');
|
const end = content.indexOf(' /*EndTestComponent*/');
|
||||||
@ -171,7 +173,7 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
// Get the marker for bounded text in the code added above
|
// Get the marker for bounded text in the code added above
|
||||||
const boundedText = mockHost.getLocationMarkerFor(fileName, 'my');
|
const boundedText = mockHost.getLocationMarkerFor(fileName, 'my');
|
||||||
@ -179,14 +181,14 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
// There should be exactly 1 definition
|
// There should be exactly 1 definition
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
|
|
||||||
const refFileName = '/app/parsing-cases.ts';
|
const refFileName = '/app/parsing-cases.ts';
|
||||||
expect(def.fileName).toBe(refFileName);
|
expect(def.fileName).toBe(refFileName);
|
||||||
expect(def.name).toBe('testEvent');
|
expect(def.name).toBe('testEvent');
|
||||||
expect(def.kind).toBe('event');
|
expect(def.kind).toBe('event');
|
||||||
const content = mockHost.readFile(refFileName) !;
|
const content = mockHost.readFile(refFileName)!;
|
||||||
const ref = `@Output('test') testEvent = new EventEmitter();`;
|
const ref = `@Output('test') testEvent = new EventEmitter();`;
|
||||||
expect(def.textSpan).toEqual({
|
expect(def.textSpan).toEqual({
|
||||||
start: content.indexOf(ref),
|
start: content.indexOf(ref),
|
||||||
@ -208,7 +210,7 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
// Get the marker for bounded text in the code added above
|
// Get the marker for bounded text in the code added above
|
||||||
const boundedText = mockHost.getLocationMarkerFor(fileName, 'my');
|
const boundedText = mockHost.getLocationMarkerFor(fileName, 'my');
|
||||||
@ -216,14 +218,14 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
// There should be exactly 1 definition
|
// There should be exactly 1 definition
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
|
|
||||||
const refFileName = '/app/parsing-cases.ts';
|
const refFileName = '/app/parsing-cases.ts';
|
||||||
expect(def.fileName).toBe(refFileName);
|
expect(def.fileName).toBe(refFileName);
|
||||||
expect(def.name).toBe('name');
|
expect(def.name).toBe('name');
|
||||||
expect(def.kind).toBe('property');
|
expect(def.kind).toBe('property');
|
||||||
const content = mockHost.readFile(refFileName) !;
|
const content = mockHost.readFile(refFileName)!;
|
||||||
const ref = `@Input('tcName') name = 'test';`;
|
const ref = `@Input('tcName') name = 'test';`;
|
||||||
expect(def.textSpan).toEqual({
|
expect(def.textSpan).toEqual({
|
||||||
start: content.indexOf(ref),
|
start: content.indexOf(ref),
|
||||||
@ -245,14 +247,14 @@ describe('definitions', () => {
|
|||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
|
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(4);
|
expect(definitions!.length).toBe(4);
|
||||||
|
|
||||||
const refFileName = '/node_modules/@angular/common/common.d.ts';
|
const refFileName = '/node_modules/@angular/common/common.d.ts';
|
||||||
for (const def of definitions !) {
|
for (const def of definitions!) {
|
||||||
expect(def.fileName).toBe(refFileName);
|
expect(def.fileName).toBe(refFileName);
|
||||||
expect(def.name).toBe('async');
|
expect(def.name).toBe('async');
|
||||||
expect(def.kind).toBe('pipe');
|
expect(def.kind).toBe('pipe');
|
||||||
@ -268,14 +270,14 @@ describe('definitions', () => {
|
|||||||
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
|
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
|
|
||||||
const refFileName = '/node_modules/@angular/common/common.d.ts';
|
const refFileName = '/node_modules/@angular/common/common.d.ts';
|
||||||
for (const def of definitions !) {
|
for (const def of definitions!) {
|
||||||
expect(def.fileName).toBe(refFileName);
|
expect(def.fileName).toBe(refFileName);
|
||||||
expect(def.name).toBe('date');
|
expect(def.name).toBe('date');
|
||||||
expect(def.kind).toBe('pipe');
|
expect(def.kind).toBe('pipe');
|
||||||
@ -292,17 +294,17 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
// Get the marker for bounded text in the code added above
|
// Get the marker for bounded text in the code added above
|
||||||
const boundedText = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'my');
|
const boundedText = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'my');
|
||||||
expect(textSpan).toEqual(boundedText);
|
expect(textSpan).toEqual(boundedText);
|
||||||
|
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
|
|
||||||
const refFileName = '/node_modules/@angular/common/common.d.ts';
|
const refFileName = '/node_modules/@angular/common/common.d.ts';
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
expect(def.fileName).toBe(refFileName);
|
expect(def.fileName).toBe(refFileName);
|
||||||
expect(def.name).toBe('NgForOf');
|
expect(def.name).toBe('NgForOf');
|
||||||
expect(def.kind).toBe('directive');
|
expect(def.kind).toBe('directive');
|
||||||
@ -317,17 +319,17 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
// Get the marker for bounded text in the code added above
|
// Get the marker for bounded text in the code added above
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
|
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
// The two definitions are setter and getter of 'ngForTrackBy'.
|
// The two definitions are setter and getter of 'ngForTrackBy'.
|
||||||
expect(definitions !.length).toBe(2);
|
expect(definitions!.length).toBe(2);
|
||||||
|
|
||||||
const refFileName = '/node_modules/@angular/common/common.d.ts';
|
const refFileName = '/node_modules/@angular/common/common.d.ts';
|
||||||
definitions !.forEach(def => {
|
definitions!.forEach(def => {
|
||||||
expect(def.fileName).toBe(refFileName);
|
expect(def.fileName).toBe(refFileName);
|
||||||
expect(def.name).toBe('ngForTrackBy');
|
expect(def.name).toBe('ngForTrackBy');
|
||||||
expect(def.kind).toBe('method');
|
expect(def.kind).toBe('method');
|
||||||
@ -343,19 +345,19 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
|
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
|
|
||||||
const refFileName = '/app/parsing-cases.ts';
|
const refFileName = '/app/parsing-cases.ts';
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
expect(def.fileName).toBe(refFileName);
|
expect(def.fileName).toBe(refFileName);
|
||||||
expect(def.name).toBe('heroes');
|
expect(def.name).toBe('heroes');
|
||||||
expect(def.kind).toBe('property');
|
expect(def.kind).toBe('property');
|
||||||
const content = mockHost.readFile(refFileName) !;
|
const content = mockHost.readFile(refFileName)!;
|
||||||
expect(content.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length))
|
expect(content.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length))
|
||||||
.toEqual(`heroes: Hero[] = [this.hero];`);
|
.toEqual(`heroes: Hero[] = [this.hero];`);
|
||||||
});
|
});
|
||||||
@ -370,28 +372,28 @@ describe('definitions', () => {
|
|||||||
|
|
||||||
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
// Get the marker for bounded text in the code added above
|
// Get the marker for bounded text in the code added above
|
||||||
const boundedText = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'my');
|
const boundedText = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'my');
|
||||||
expect(textSpan).toEqual(boundedText);
|
expect(textSpan).toEqual(boundedText);
|
||||||
|
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(2);
|
expect(definitions!.length).toBe(2);
|
||||||
const [def1, def2] = definitions !;
|
const [def1, def2] = definitions!;
|
||||||
|
|
||||||
const refFileName = '/app/parsing-cases.ts';
|
const refFileName = '/app/parsing-cases.ts';
|
||||||
expect(def1.fileName).toBe(refFileName);
|
expect(def1.fileName).toBe(refFileName);
|
||||||
expect(def1.name).toBe('model');
|
expect(def1.name).toBe('model');
|
||||||
expect(def1.kind).toBe('property');
|
expect(def1.kind).toBe('property');
|
||||||
let content = mockHost.readFile(refFileName) !;
|
let content = mockHost.readFile(refFileName)!;
|
||||||
expect(content.substring(def1.textSpan.start, def1.textSpan.start + def1.textSpan.length))
|
expect(content.substring(def1.textSpan.start, def1.textSpan.start + def1.textSpan.length))
|
||||||
.toEqual(`@Input() model: string = 'model';`);
|
.toEqual(`@Input() model: string = 'model';`);
|
||||||
|
|
||||||
expect(def2.fileName).toBe(refFileName);
|
expect(def2.fileName).toBe(refFileName);
|
||||||
expect(def2.name).toBe('modelChange');
|
expect(def2.name).toBe('modelChange');
|
||||||
expect(def2.kind).toBe('event');
|
expect(def2.kind).toBe('event');
|
||||||
content = mockHost.readFile(refFileName) !;
|
content = mockHost.readFile(refFileName)!;
|
||||||
expect(content.substring(def2.textSpan.start, def2.textSpan.start + def2.textSpan.length))
|
expect(content.substring(def2.textSpan.start, def2.textSpan.start + def2.textSpan.length))
|
||||||
.toEqual(`@Output() modelChange: EventEmitter<string> = new EventEmitter();`);
|
.toEqual(`@Output() modelChange: EventEmitter<string> = new EventEmitter();`);
|
||||||
});
|
});
|
||||||
@ -407,13 +409,13 @@ describe('definitions', () => {
|
|||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
expect(textSpan).toEqual({start: marker.start - 2, length: 9});
|
expect(textSpan).toEqual({start: marker.start - 2, length: 9});
|
||||||
|
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const [def] = definitions !;
|
const [def] = definitions!;
|
||||||
expect(def.fileName).toBe('/app/test.ng');
|
expect(def.fileName).toBe('/app/test.ng');
|
||||||
expect(def.textSpan).toEqual({start: 0, length: 0});
|
expect(def.textSpan).toEqual({start: 0, length: 0});
|
||||||
});
|
});
|
||||||
@ -430,13 +432,13 @@ describe('definitions', () => {
|
|||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
expect(textSpan).toEqual({start: marker.start - 2, length: 10});
|
expect(textSpan).toEqual({start: marker.start - 2, length: 10});
|
||||||
|
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const [def] = definitions !;
|
const [def] = definitions!;
|
||||||
expect(def.fileName).toBe('/app/test.css');
|
expect(def.fileName).toBe('/app/test.css');
|
||||||
expect(def.textSpan).toEqual({start: 0, length: 0});
|
expect(def.textSpan).toEqual({start: 0, length: 0});
|
||||||
});
|
});
|
||||||
@ -453,12 +455,12 @@ describe('definitions', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(fileName, 'name');
|
const marker = mockHost.getReferenceMarkerFor(fileName, 'name');
|
||||||
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
const {textSpan, definitions} = result !;
|
const {textSpan, definitions} = result!;
|
||||||
|
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(definitions).toBeDefined();
|
expect(definitions).toBeDefined();
|
||||||
expect(definitions !.length).toBe(1);
|
expect(definitions!.length).toBe(1);
|
||||||
const def = definitions ![0];
|
const def = definitions![0];
|
||||||
|
|
||||||
expect(def.fileName).toBe(fileName);
|
expect(def.fileName).toBe(fileName);
|
||||||
expect(def.name).toBe('name');
|
expect(def.name).toBe('name');
|
||||||
|
@ -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', () => {
|
||||||
|
@ -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);
|
||||||
@ -396,7 +401,7 @@ describe('diagnostics', () => {
|
|||||||
|
|
||||||
it('should reject it when not in an event binding', () => {
|
it('should reject it when not in an event binding', () => {
|
||||||
const content = mockHost.override(TEST_TEMPLATE, '<div [tabIndex]="$event"></div>');
|
const content = mockHost.override(TEST_TEMPLATE, '<div [tabIndex]="$event"></div>');
|
||||||
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE) !;
|
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE)!;
|
||||||
expect(diagnostics.length).toBe(1);
|
expect(diagnostics.length).toBe(1);
|
||||||
const {messageText, start, length} = diagnostics[0];
|
const {messageText, start, length} = diagnostics[0];
|
||||||
expect(messageText)
|
expect(messageText)
|
||||||
@ -410,7 +415,7 @@ describe('diagnostics', () => {
|
|||||||
it('should reject invalid properties on an event type', () => {
|
it('should reject invalid properties on an event type', () => {
|
||||||
const content = mockHost.override(
|
const content = mockHost.override(
|
||||||
TEST_TEMPLATE, '<div string-model (modelChange)="$event.notSubstring()"></div>');
|
TEST_TEMPLATE, '<div string-model (modelChange)="$event.notSubstring()"></div>');
|
||||||
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE) !;
|
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE)!;
|
||||||
expect(diagnostics.length).toBe(1);
|
expect(diagnostics.length).toBe(1);
|
||||||
const {messageText, start, length} = diagnostics[0];
|
const {messageText, start, length} = diagnostics[0];
|
||||||
expect(messageText)
|
expect(messageText)
|
||||||
@ -436,14 +441,14 @@ describe('diagnostics', () => {
|
|||||||
template: '<div></div>'
|
template: '<div></div>'
|
||||||
})
|
})
|
||||||
export class MyComponent {}`);
|
export class MyComponent {}`);
|
||||||
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
|
const diagnostics = ngLS.getSemanticDiagnostics(fileName)!;
|
||||||
expect(diagnostics.length).toBe(1);
|
expect(diagnostics.length).toBe(1);
|
||||||
const {messageText, start, length} = diagnostics[0];
|
const {messageText, start, length} = diagnostics[0];
|
||||||
expect(messageText)
|
expect(messageText)
|
||||||
.toBe(
|
.toBe(
|
||||||
`Component 'MyComponent' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration.`);
|
`Component 'MyComponent' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration.`);
|
||||||
const content = mockHost.readFile(fileName) !;
|
const content = mockHost.readFile(fileName)!;
|
||||||
expect(content.substring(start !, start ! + length !)).toBe('MyComponent');
|
expect(content.substring(start!, start! + length!)).toBe('MyComponent');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -495,7 +500,7 @@ describe('diagnostics', () => {
|
|||||||
}`);
|
}`);
|
||||||
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
|
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
|
||||||
expect(tsDiags).toEqual([]);
|
expect(tsDiags).toEqual([]);
|
||||||
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT) !;
|
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT)!;
|
||||||
expect(ngDiags.length).toBe(1);
|
expect(ngDiags.length).toBe(1);
|
||||||
const {messageText, start, length} = ngDiags[0];
|
const {messageText, start, length} = ngDiags[0];
|
||||||
const keyword = `() => 'foo'`;
|
const keyword = `() => 'foo'`;
|
||||||
@ -504,9 +509,9 @@ describe('diagnostics', () => {
|
|||||||
// messageText is a three-part chain
|
// messageText is a three-part chain
|
||||||
const firstPart = messageText as ts.DiagnosticMessageChain;
|
const firstPart = messageText as ts.DiagnosticMessageChain;
|
||||||
expect(firstPart.messageText).toBe(`Error during template compile of 'AppComponent'`);
|
expect(firstPart.messageText).toBe(`Error during template compile of 'AppComponent'`);
|
||||||
const secondPart = firstPart.next !;
|
const secondPart = firstPart.next!;
|
||||||
expect(secondPart[0].messageText).toBe('Function expressions are not supported in decorators');
|
expect(secondPart[0].messageText).toBe('Function expressions are not supported in decorators');
|
||||||
const thirdPart = secondPart[0].next !;
|
const thirdPart = secondPart[0].next!;
|
||||||
expect(thirdPart[0].messageText)
|
expect(thirdPart[0].messageText)
|
||||||
.toBe('Consider changing the function expression into an exported function');
|
.toBe('Consider changing the function expression into an exported function');
|
||||||
expect(thirdPart[0].next).toBeFalsy();
|
expect(thirdPart[0].next).toBeFalsy();
|
||||||
@ -568,7 +573,7 @@ describe('diagnostics', () => {
|
|||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
onClick() { }
|
onClick() { }
|
||||||
}`);
|
}`);
|
||||||
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT) !;
|
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT)!;
|
||||||
const {messageText, start, length} = diagnostics[0];
|
const {messageText, start, length} = diagnostics[0];
|
||||||
expect(messageText).toBe('Unexpected callable expression. Expected a method call');
|
expect(messageText).toBe('Unexpected callable expression. Expected a method call');
|
||||||
const keyword = `"onClick"`;
|
const keyword = `"onClick"`;
|
||||||
@ -682,7 +687,7 @@ describe('diagnostics', () => {
|
|||||||
.toBe(
|
.toBe(
|
||||||
'Invalid providers for "AppComponent in /app/app.component.ts" - only instances of Provider and Type are allowed, got: [?null?]');
|
'Invalid providers for "AppComponent in /app/app.component.ts" - only instances of Provider and Type are allowed, got: [?null?]');
|
||||||
// TODO: Looks like this is the wrong span. Should point to 'null' instead.
|
// TODO: Looks like this is the wrong span. Should point to 'null' instead.
|
||||||
expect(content.substring(start !, start ! + length !)).toBe('AppComponent');
|
expect(content.substring(start!, start! + length!)).toBe('AppComponent');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Issue #15768
|
// Issue #15768
|
||||||
@ -815,12 +820,12 @@ describe('diagnostics', () => {
|
|||||||
|
|
||||||
const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile');
|
const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile');
|
||||||
|
|
||||||
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
|
const diagnostics = ngLS.getSemanticDiagnostics(fileName)!;
|
||||||
const urlDiagnostic =
|
const urlDiagnostic =
|
||||||
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
|
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
|
||||||
expect(urlDiagnostic).toBeDefined();
|
expect(urlDiagnostic).toBeDefined();
|
||||||
|
|
||||||
const {start, length} = urlDiagnostic !;
|
const {start, length} = urlDiagnostic!;
|
||||||
expect(start).toBe(marker.start);
|
expect(start).toBe(marker.start);
|
||||||
expect(length).toBe(marker.length);
|
expect(length).toBe(marker.length);
|
||||||
});
|
});
|
||||||
@ -832,7 +837,7 @@ describe('diagnostics', () => {
|
|||||||
})
|
})
|
||||||
export class MyComponent {}`);
|
export class MyComponent {}`);
|
||||||
|
|
||||||
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
|
const diagnostics = ngLS.getSemanticDiagnostics(fileName)!;
|
||||||
const urlDiagnostic =
|
const urlDiagnostic =
|
||||||
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
|
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
|
||||||
expect(urlDiagnostic).toBeUndefined();
|
expect(urlDiagnostic).toBeUndefined();
|
||||||
@ -849,9 +854,9 @@ describe('diagnostics', () => {
|
|||||||
const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
|
const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
|
||||||
expect(diags.length).toBe(1);
|
expect(diags.length).toBe(1);
|
||||||
const {file, messageText, start, length} = diags[0];
|
const {file, messageText, start, length} = diags[0];
|
||||||
expect(file !.fileName).toBe(APP_COMPONENT);
|
expect(file!.fileName).toBe(APP_COMPONENT);
|
||||||
expect(messageText).toBe(`Component 'AppComponent' must have a template or templateUrl`);
|
expect(messageText).toBe(`Component 'AppComponent' must have a template or templateUrl`);
|
||||||
expect(content.substring(start !, start ! + length !)).toBe('AppComponent');
|
expect(content.substring(start!, start! + length!)).toBe('AppComponent');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should report diagnostic for both template and templateUrl', () => {
|
it('should report diagnostic for both template and templateUrl', () => {
|
||||||
@ -867,10 +872,10 @@ describe('diagnostics', () => {
|
|||||||
const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
|
const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
|
||||||
expect(diags.length).toBe(1);
|
expect(diags.length).toBe(1);
|
||||||
const {file, messageText, start, length} = diags[0];
|
const {file, messageText, start, length} = diags[0];
|
||||||
expect(file !.fileName).toBe(APP_COMPONENT);
|
expect(file!.fileName).toBe(APP_COMPONENT);
|
||||||
expect(messageText)
|
expect(messageText)
|
||||||
.toBe(`Component 'AppComponent' must not have both template and templateUrl`);
|
.toBe(`Component 'AppComponent' must not have both template and templateUrl`);
|
||||||
expect(content.substring(start !, start ! + length !)).toBe('AppComponent');
|
expect(content.substring(start!, start! + length!)).toBe('AppComponent');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should report errors for invalid styleUrls', () => {
|
it('should report errors for invalid styleUrls', () => {
|
||||||
@ -882,12 +887,12 @@ describe('diagnostics', () => {
|
|||||||
|
|
||||||
const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile');
|
const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile');
|
||||||
|
|
||||||
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
|
const diagnostics = ngLS.getSemanticDiagnostics(fileName)!;
|
||||||
const urlDiagnostic =
|
const urlDiagnostic =
|
||||||
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
|
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
|
||||||
expect(urlDiagnostic).toBeDefined();
|
expect(urlDiagnostic).toBeDefined();
|
||||||
|
|
||||||
const {start, length} = urlDiagnostic !;
|
const {start, length} = urlDiagnostic!;
|
||||||
expect(start).toBe(marker.start);
|
expect(start).toBe(marker.start);
|
||||||
expect(length).toBe(marker.length);
|
expect(length).toBe(marker.length);
|
||||||
});
|
});
|
||||||
@ -902,7 +907,7 @@ describe('diagnostics', () => {
|
|||||||
})
|
})
|
||||||
export class AppComponent {}`);
|
export class AppComponent {}`);
|
||||||
|
|
||||||
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT) !;
|
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT)!;
|
||||||
expect(diagnostics.length).toBe(0);
|
expect(diagnostics.length).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -926,14 +931,14 @@ describe('diagnostics', () => {
|
|||||||
`element references do not contain such a member`);
|
`element references do not contain such a member`);
|
||||||
// Assert that the span is actually highlight the bounded text. The span
|
// Assert that the span is actually highlight the bounded text. The span
|
||||||
// would be off if CRLF endings are not handled properly.
|
// would be off if CRLF endings are not handled properly.
|
||||||
expect(content.substring(start !, start ! + length !)).toBe(`line${i}`);
|
expect(content.substring(start!, start! + length!)).toBe(`line${i}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work correctly with CRLF endings in inline template', () => {
|
it('should work correctly with CRLF endings in inline template', () => {
|
||||||
const fileName = mockHost.addCode(
|
const fileName = mockHost.addCode(
|
||||||
'\n@Component({template:`\r\n\r\n{{line}}`})export class ComponentCRLF {}');
|
'\n@Component({template:`\r\n\r\n{{line}}`})export class ComponentCRLF {}');
|
||||||
const content = mockHost.readFile(fileName) !;
|
const content = mockHost.readFile(fileName)!;
|
||||||
const ngDiags = ngLS.getSemanticDiagnostics(fileName);
|
const ngDiags = ngLS.getSemanticDiagnostics(fileName);
|
||||||
expect(ngDiags.length).toBeGreaterThan(0);
|
expect(ngDiags.length).toBeGreaterThan(0);
|
||||||
const {messageText, start, length} = ngDiags[0];
|
const {messageText, start, length} = ngDiags[0];
|
||||||
@ -942,7 +947,7 @@ describe('diagnostics', () => {
|
|||||||
`Identifier 'line' is not defined. ` +
|
`Identifier 'line' is not defined. ` +
|
||||||
`The component declaration, template variable declarations, and ` +
|
`The component declaration, template variable declarations, and ` +
|
||||||
`element references do not contain such a member`);
|
`element references do not contain such a member`);
|
||||||
expect(content.substring(start !, start ! + length !)).toBe('line');
|
expect(content.substring(start!, start! + length!)).toBe('line');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not produce diagnostics for non-exported directives', () => {
|
it('should not produce diagnostics for non-exported directives', () => {
|
||||||
@ -972,8 +977,7 @@ describe('diagnostics', () => {
|
|||||||
`Consider using the safe navigation operator (optional?.toLowerCase) ` +
|
`Consider using the safe navigation operator (optional?.toLowerCase) ` +
|
||||||
`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', () => {
|
||||||
@ -987,7 +991,7 @@ describe('diagnostics', () => {
|
|||||||
`Consider using the safe navigation operator (optional?.length) ` +
|
`Consider using the safe navigation operator (optional?.length) ` +
|
||||||
`or non-null assertion operator (optional!.length).`);
|
`or non-null assertion operator (optional!.length).`);
|
||||||
expect(category).toBe(ts.DiagnosticCategory.Suggestion);
|
expect(category).toBe(ts.DiagnosticCategory.Suggestion);
|
||||||
expect(content.substring(start !, start ! + length !)).toBe('optional.length');
|
expect(content.substring(start!, start! + length!)).toBe('optional.length');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should report error if method is not found on non-nullable receiver', () => {
|
it('should report error if method is not found on non-nullable receiver', () => {
|
||||||
@ -1003,7 +1007,7 @@ describe('diagnostics', () => {
|
|||||||
expect(messageText)
|
expect(messageText)
|
||||||
.toBe(`Identifier 'someMethod' is not defined. 'string' does not contain such a member`);
|
.toBe(`Identifier 'someMethod' is not defined. 'string' does not contain such a member`);
|
||||||
expect(category).toBe(ts.DiagnosticCategory.Error);
|
expect(category).toBe(ts.DiagnosticCategory.Error);
|
||||||
expect(content.substring(start !, start ! + length !)).toBe(expression);
|
expect(content.substring(start!, start! + length!)).toBe(expression);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1020,7 +1024,7 @@ describe('diagnostics', () => {
|
|||||||
expect(messageText)
|
expect(messageText)
|
||||||
.toBe(`Identifier 'someProp' is not defined. 'string' does not contain such a member`);
|
.toBe(`Identifier 'someProp' is not defined. 'string' does not contain such a member`);
|
||||||
expect(category).toBe(ts.DiagnosticCategory.Error);
|
expect(category).toBe(ts.DiagnosticCategory.Error);
|
||||||
expect(content.substring(start !, start ! + length !)).toBe(expression);
|
expect(content.substring(start!, start! + length!)).toBe(expression);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1030,12 +1034,12 @@ describe('diagnostics', () => {
|
|||||||
<p>{{myClick()()}}</p>
|
<p>{{myClick()()}}</p>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const content = mockHost.readFile(TEST_TEMPLATE) !;
|
const content = mockHost.readFile(TEST_TEMPLATE)!;
|
||||||
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
|
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
|
||||||
expect(diags.length).toBe(1);
|
expect(diags.length).toBe(1);
|
||||||
const {messageText, start, length} = diags[0] !;
|
const {messageText, start, length} = diags[0]!;
|
||||||
expect(messageText).toBe(`Call target 'myClick()' has non-callable type 'void'.`);
|
expect(messageText).toBe(`Call target 'myClick()' has non-callable type 'void'.`);
|
||||||
expect(content.substring(start !, start ! + length !)).toBe('myClick()()');
|
expect(content.substring(start!, start! + length!)).toBe('myClick()()');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
@ -27,15 +27,15 @@ describe('expression diagnostics', () => {
|
|||||||
registry = ts.createDocumentRegistry(false, '/src');
|
registry = ts.createDocumentRegistry(false, '/src');
|
||||||
host = new MockLanguageServiceHost(['app/app.component.ts'], FILES, '/src');
|
host = new MockLanguageServiceHost(['app/app.component.ts'], FILES, '/src');
|
||||||
service = ts.createLanguageService(host, registry);
|
service = ts.createLanguageService(host, registry);
|
||||||
const program = service.getProgram() !;
|
const program = service.getProgram()!;
|
||||||
const checker = program.getTypeChecker();
|
const checker = program.getTypeChecker();
|
||||||
const symbolResolverHost = new ReflectorHost(() => program !, host);
|
const symbolResolverHost = new ReflectorHost(() => program!, host);
|
||||||
context = new DiagnosticContext(service, program !, checker, symbolResolverHost);
|
context = new DiagnosticContext(service, program!, checker, symbolResolverHost);
|
||||||
type = context.getStaticSymbol('app/app.component.ts', 'AppComponent');
|
type = context.getStaticSymbol('app/app.component.ts', 'AppComponent');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have no diagnostics in default app', () => {
|
it('should have no diagnostics in default app', () => {
|
||||||
function messageToString(messageText: string | ts.DiagnosticMessageChain): string {
|
function messageToString(messageText: string|ts.DiagnosticMessageChain): string {
|
||||||
if (typeof messageText == 'string') {
|
if (typeof messageText == 'string') {
|
||||||
return messageText;
|
return messageText;
|
||||||
} else {
|
} else {
|
||||||
@ -107,13 +107,14 @@ 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}}
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
'Identifier \'names\' is not defined'));
|
'Identifier \'names\' is not defined'));
|
||||||
it('should accept an async expression',
|
it('should accept an async expression',
|
||||||
() => accept('{{(promised_person | async)?.name.first || ""}}'));
|
() => accept('{{(promised_person | async)?.name.first || ""}}'));
|
||||||
it('should reject an async misspelled field',
|
it('should reject an async misspelled field',
|
||||||
@ -124,25 +125,27 @@ 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}}
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
'Identifier \'nume\' is not defined'));
|
'Identifier \'nume\' is not defined'));
|
||||||
it('should accept an async *ngIf', () => accept(`
|
it('should accept an async *ngIf', () => accept(`
|
||||||
<div *ngIf="promised_person | async as p">
|
<div *ngIf="promised_person | async as p">
|
||||||
{{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}}
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
'Identifier \'nume\' is not defined'));
|
'Identifier \'nume\' is not defined'));
|
||||||
it('should reject access to potentially undefined field',
|
it('should reject access to potentially undefined field',
|
||||||
() => reject(
|
() => reject(
|
||||||
`<div>{{maybe_person.name.first}}`,
|
`<div>{{maybe_person.name.first}}`,
|
||||||
|
@ -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';
|
||||||
@ -18,7 +18,7 @@ describe('GlobalSymbolTable', () => {
|
|||||||
const tsLS = ts.createLanguageService(mockHost);
|
const tsLS = ts.createLanguageService(mockHost);
|
||||||
|
|
||||||
it(`contains $any()`, () => {
|
it(`contains $any()`, () => {
|
||||||
const program = tsLS.getProgram() !;
|
const program = tsLS.getProgram()!;
|
||||||
const typeChecker = program.getTypeChecker();
|
const typeChecker = program.getTypeChecker();
|
||||||
const source = ts.createSourceFile('foo.ts', '', ts.ScriptTarget.ES2015);
|
const source = ts.createSourceFile('foo.ts', '', ts.ScriptTarget.ES2015);
|
||||||
const query = getSymbolQuery(program, typeChecker, source, () => EMPTY_SYMBOL_TABLE);
|
const query = getSymbolQuery(program, typeChecker, source, () => EMPTY_SYMBOL_TABLE);
|
||||||
|
@ -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', () => {
|
||||||
@ -30,7 +32,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
||||||
});
|
});
|
||||||
@ -40,7 +42,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
||||||
});
|
});
|
||||||
@ -50,7 +52,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
||||||
});
|
});
|
||||||
@ -60,7 +62,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
||||||
});
|
});
|
||||||
@ -70,7 +72,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
||||||
});
|
});
|
||||||
@ -80,7 +82,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'anyValue');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'anyValue');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TemplateReference.anyValue: any');
|
expect(toText(displayParts)).toBe('(property) TemplateReference.anyValue: any');
|
||||||
});
|
});
|
||||||
@ -92,7 +94,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeDefined();
|
expect(quickInfo).toBeDefined();
|
||||||
const documentation = toText(quickInfo !.documentation);
|
const documentation = toText(quickInfo!.documentation);
|
||||||
expect(documentation).toBe('This is the title of the `TemplateReference` Component.');
|
expect(documentation).toBe('This is the title of the `TemplateReference` Component.');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -102,7 +104,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
||||||
});
|
});
|
||||||
@ -112,7 +114,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(variable) hero: Hero');
|
expect(toText(displayParts)).toBe('(variable) hero: Hero');
|
||||||
});
|
});
|
||||||
@ -123,7 +125,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(variable) hero: Readonly<Hero>');
|
expect(toText(displayParts)).toBe('(variable) hero: Readonly<Hero>');
|
||||||
});
|
});
|
||||||
@ -133,7 +135,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'name');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'name');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(variable) name: { readonly name: "name"; }');
|
expect(toText(displayParts)).toBe('(variable) name: { readonly name: "name"; }');
|
||||||
});
|
});
|
||||||
@ -144,7 +146,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'myClick');
|
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'myClick');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(method) TemplateReference.myClick: (event: any) => void');
|
expect(toText(displayParts)).toBe('(method) TemplateReference.myClick: (event: any) => void');
|
||||||
});
|
});
|
||||||
@ -154,7 +156,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'trackBy');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'trackBy');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(method) NgForOf<T, U>.ngForTrackBy: TrackByFunction<T>');
|
expect(toText(displayParts)).toBe('(method) NgForOf<T, U>.ngForTrackBy: TrackByFunction<T>');
|
||||||
});
|
});
|
||||||
@ -164,7 +166,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'heroes');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'heroes');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TemplateReference.heroes: Hero[]');
|
expect(toText(displayParts)).toBe('(property) TemplateReference.heroes: Hero[]');
|
||||||
});
|
});
|
||||||
@ -175,7 +177,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'date');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'date');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts))
|
expect(toText(displayParts))
|
||||||
.toBe(
|
.toBe(
|
||||||
@ -187,7 +189,7 @@ describe('hover', () => {
|
|||||||
const position = content.indexOf('$any');
|
const position = content.indexOf('$any');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, position);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, position);
|
||||||
expect(quickInfo).toBeDefined();
|
expect(quickInfo).toBeDefined();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual({
|
expect(textSpan).toEqual({
|
||||||
start: position,
|
start: position,
|
||||||
length: '$any(title)'.length,
|
length: '$any(title)'.length,
|
||||||
@ -202,7 +204,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeDefined();
|
expect(quickInfo).toBeDefined();
|
||||||
const documentation = toText(quickInfo !.documentation);
|
const documentation = toText(quickInfo!.documentation);
|
||||||
expect(documentation).toBe('This Component provides the `test-comp` selector.');
|
expect(documentation).toBe('This Component provides the `test-comp` selector.');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -211,7 +213,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeDefined();
|
expect(quickInfo).toBeDefined();
|
||||||
const {displayParts, documentation} = quickInfo !;
|
const {displayParts, documentation} = quickInfo!;
|
||||||
expect(toText(displayParts))
|
expect(toText(displayParts))
|
||||||
.toBe('(component) AppModule.TestComponent: typeof TestComponent');
|
.toBe('(component) AppModule.TestComponent: typeof TestComponent');
|
||||||
expect(toText(documentation)).toBe('This Component provides the `test-comp` selector.');
|
expect(toText(documentation)).toBe('This Component provides the `test-comp` selector.');
|
||||||
@ -222,7 +224,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeDefined();
|
expect(quickInfo).toBeDefined();
|
||||||
const {displayParts, textSpan} = quickInfo !;
|
const {displayParts, textSpan} = quickInfo!;
|
||||||
expect(toText(displayParts)).toBe('(directive) AppModule.StringModel: typeof StringModel');
|
expect(toText(displayParts)).toBe('(directive) AppModule.StringModel: typeof StringModel');
|
||||||
expect(content.substring(textSpan.start, textSpan.start + textSpan.length))
|
expect(content.substring(textSpan.start, textSpan.start + textSpan.length))
|
||||||
.toBe('string-model');
|
.toBe('string-model');
|
||||||
@ -233,7 +235,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'test');
|
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'test');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(event) TestComponent.testEvent: EventEmitter<any>');
|
expect(toText(displayParts)).toBe('(event) TestComponent.testEvent: EventEmitter<any>');
|
||||||
});
|
});
|
||||||
@ -243,7 +245,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'tcName');
|
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'tcName');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TestComponent.name: string');
|
expect(toText(displayParts)).toBe('(property) TestComponent.name: string');
|
||||||
});
|
});
|
||||||
@ -254,7 +256,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'model');
|
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'model');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) StringModel.model: string');
|
expect(toText(displayParts)).toBe('(property) StringModel.model: string');
|
||||||
});
|
});
|
||||||
@ -264,7 +266,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'ngFor');
|
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'ngFor');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(directive) NgForOf: typeof NgForOf');
|
expect(toText(displayParts)).toBe('(directive) NgForOf: typeof NgForOf');
|
||||||
});
|
});
|
||||||
@ -272,12 +274,12 @@ describe('hover', () => {
|
|||||||
|
|
||||||
describe('hovering on TypeScript nodes', () => {
|
describe('hovering on TypeScript nodes', () => {
|
||||||
it('should work for component TypeScript declarations', () => {
|
it('should work for component TypeScript declarations', () => {
|
||||||
const content = mockHost.readFile(PARSING_CASES) !;
|
const content = mockHost.readFile(PARSING_CASES)!;
|
||||||
const position = content.indexOf('TemplateReference');
|
const position = content.indexOf('TemplateReference');
|
||||||
expect(position).toBeGreaterThan(0);
|
expect(position).toBeGreaterThan(0);
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(PARSING_CASES, position);
|
const quickInfo = ngLS.getQuickInfoAtPosition(PARSING_CASES, position);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual({
|
expect(textSpan).toEqual({
|
||||||
start: position,
|
start: position,
|
||||||
length: 'TemplateReference'.length,
|
length: 'TemplateReference'.length,
|
||||||
@ -286,12 +288,12 @@ describe('hover', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should work for directive TypeScript declarations', () => {
|
it('should work for directive TypeScript declarations', () => {
|
||||||
const content = mockHost.readFile(PARSING_CASES) !;
|
const content = mockHost.readFile(PARSING_CASES)!;
|
||||||
const position = content.indexOf('StringModel');
|
const position = content.indexOf('StringModel');
|
||||||
expect(position).toBeGreaterThan(0);
|
expect(position).toBeGreaterThan(0);
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(PARSING_CASES, position);
|
const quickInfo = ngLS.getQuickInfoAtPosition(PARSING_CASES, position);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual({
|
expect(textSpan).toEqual({
|
||||||
start: position,
|
start: position,
|
||||||
length: 'StringModel'.length,
|
length: 'StringModel'.length,
|
||||||
@ -313,7 +315,7 @@ describe('hover', () => {
|
|||||||
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
|
||||||
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
|
||||||
expect(quickInfo).toBeTruthy();
|
expect(quickInfo).toBeTruthy();
|
||||||
const {textSpan, displayParts} = quickInfo !;
|
const {textSpan, displayParts} = quickInfo!;
|
||||||
expect(textSpan).toEqual(marker);
|
expect(textSpan).toEqual(marker);
|
||||||
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,6 @@ describe('html_info', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function uniqueElements<T>(a: T[], b: T[]): T[] {
|
function uniqueElements<T>(a: T[], b: T[]): T[] {
|
||||||
|
@ -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');
|
||||||
|
@ -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;
|
||||||
@ -133,7 +159,7 @@ export class DiagnosticContext {
|
|||||||
if (!this._reflector) {
|
if (!this._reflector) {
|
||||||
const ssr = this.staticSymbolResolver;
|
const ssr = this.staticSymbolResolver;
|
||||||
const result = this._reflector = new StaticReflector(
|
const result = this._reflector = new StaticReflector(
|
||||||
summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath !));
|
summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath!));
|
||||||
this._reflector = result;
|
this._reflector = result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -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);
|
||||||
@ -198,7 +232,7 @@ function compileTemplate(context: DiagnosticContext, type: StaticSymbol, templat
|
|||||||
const config = new CompilerConfig();
|
const config = new CompilerConfig();
|
||||||
const parser = new TemplateParser(
|
const parser = new TemplateParser(
|
||||||
config, context.reflector, expressionParser, new DomElementSchemaRegistry(), htmlParser,
|
config, context.reflector, expressionParser, new DomElementSchemaRegistry(), htmlParser,
|
||||||
null !, []);
|
null!, []);
|
||||||
const htmlResult = htmlParser.parse(template, '', {tokenizeExpansionForms: true});
|
const htmlResult = htmlParser.parse(template, '', {tokenizeExpansionForms: true});
|
||||||
const analyzedModules = context.analyzedModules;
|
const analyzedModules = context.analyzedModules;
|
||||||
// let errors: Diagnostic[]|undefined = undefined;
|
// let errors: Diagnostic[]|undefined = undefined;
|
||||||
@ -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,
|
||||||
@ -246,6 +285,6 @@ export function getDiagnosticTemplateInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeMissing<T>(values: (T | null | undefined)[]): T[] {
|
function removeMissing<T>(values: (T|null|undefined)[]): T[] {
|
||||||
return values.filter(e => !!e) as T[];
|
return values.filter(e => !!e) as T[];
|
||||||
}
|
}
|
||||||
|
@ -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');
|
||||||
|
@ -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', `
|
||||||
@ -28,24 +27,24 @@ describe('getClassDeclFromTemplateNode', () => {
|
|||||||
}
|
}
|
||||||
const classDecl = sourceFile.forEachChild(visit);
|
const classDecl = sourceFile.forEachChild(visit);
|
||||||
expect(classDecl).toBeTruthy();
|
expect(classDecl).toBeTruthy();
|
||||||
expect(classDecl !.kind).toBe(ts.SyntaxKind.ClassDeclaration);
|
expect(classDecl!.kind).toBe(ts.SyntaxKind.ClassDeclaration);
|
||||||
expect((classDecl as ts.ClassDeclaration).name !.text).toBe('MyComponent');
|
expect((classDecl as ts.ClassDeclaration).name!.text).toBe('MyComponent');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return class declaration for AppComponent', () => {
|
it('should return class declaration for AppComponent', () => {
|
||||||
const host = new MockTypescriptHost(['/app/app.component.ts']);
|
const host = new MockTypescriptHost(['/app/app.component.ts']);
|
||||||
const tsLS = ts.createLanguageService(host);
|
const tsLS = ts.createLanguageService(host);
|
||||||
const sourceFile = tsLS.getProgram() !.getSourceFile('/app/app.component.ts');
|
const sourceFile = tsLS.getProgram()!.getSourceFile('/app/app.component.ts');
|
||||||
expect(sourceFile).toBeTruthy();
|
expect(sourceFile).toBeTruthy();
|
||||||
const classDecl = sourceFile !.forEachChild(function visit(node): ts.Node | undefined {
|
const classDecl = sourceFile!.forEachChild(function visit(node): ts.Node|undefined {
|
||||||
if (ts.isPropertyAssignment(node)) {
|
if (ts.isPropertyAssignment(node)) {
|
||||||
return getClassDeclFromDecoratorProp(node);
|
return getClassDeclFromDecoratorProp(node);
|
||||||
}
|
}
|
||||||
return node.forEachChild(visit);
|
return node.forEachChild(visit);
|
||||||
});
|
});
|
||||||
expect(classDecl).toBeTruthy();
|
expect(classDecl).toBeTruthy();
|
||||||
expect(ts.isClassDeclaration(classDecl !)).toBe(true);
|
expect(ts.isClassDeclaration(classDecl!)).toBe(true);
|
||||||
expect((classDecl as ts.ClassDeclaration).name !.text).toBe('AppComponent');
|
expect((classDecl as ts.ClassDeclaration).name!.text).toBe('AppComponent');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -57,11 +57,11 @@ function isFile(path: string) {
|
|||||||
function loadTourOfHeroes(): ReadonlyMap<string, string> {
|
function loadTourOfHeroes(): ReadonlyMap<string, string> {
|
||||||
const {TEST_SRCDIR} = process.env;
|
const {TEST_SRCDIR} = process.env;
|
||||||
const root =
|
const root =
|
||||||
path.join(TEST_SRCDIR !, 'angular', 'packages', 'language-service', 'test', 'project');
|
path.join(TEST_SRCDIR!, 'angular', 'packages', 'language-service', 'test', 'project');
|
||||||
const dirs = [root];
|
const dirs = [root];
|
||||||
const files = new Map<string, string>();
|
const files = new Map<string, string>();
|
||||||
while (dirs.length) {
|
while (dirs.length) {
|
||||||
const dirPath = dirs.pop() !;
|
const dirPath = dirs.pop()!;
|
||||||
for (const filePath of fs.readdirSync(dirPath)) {
|
for (const filePath of fs.readdirSync(dirPath)) {
|
||||||
const absPath = path.join(dirPath, filePath);
|
const absPath = path.join(dirPath, filePath);
|
||||||
if (isFile(absPath)) {
|
if (isFile(absPath)) {
|
||||||
@ -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);
|
||||||
@ -242,7 +254,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
|
|||||||
|
|
||||||
private pathExists(path: string): boolean {
|
private pathExists(path: string): boolean {
|
||||||
if (this.existsCache.has(path)) {
|
if (this.existsCache.has(path)) {
|
||||||
return this.existsCache.get(path) !;
|
return this.existsCache.get(path)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
const exists = fs.existsSync(path);
|
const exists = fs.existsSync(path);
|
||||||
@ -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;
|
||||||
|
@ -26,7 +26,7 @@ const mockProject = {
|
|||||||
describe('plugin', () => {
|
describe('plugin', () => {
|
||||||
const mockHost = new MockTypescriptHost(['/app/main.ts']);
|
const mockHost = new MockTypescriptHost(['/app/main.ts']);
|
||||||
const tsLS = ts.createLanguageService(mockHost);
|
const tsLS = ts.createLanguageService(mockHost);
|
||||||
const program = tsLS.getProgram() !;
|
const program = tsLS.getProgram()!;
|
||||||
const plugin = create({
|
const plugin = create({
|
||||||
languageService: tsLS,
|
languageService: tsLS,
|
||||||
languageServiceHost: mockHost,
|
languageServiceHost: mockHost,
|
||||||
@ -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';
|
||||||
@ -117,7 +119,7 @@ describe('plugin', () => {
|
|||||||
const marker = mockHost.getLocationMarkerFor(MY_COMPONENT, 'tree');
|
const marker = mockHost.getLocationMarkerFor(MY_COMPONENT, 'tree');
|
||||||
const completions = plugin.getCompletionsAtPosition(MY_COMPONENT, marker.start, undefined);
|
const completions = plugin.getCompletionsAtPosition(MY_COMPONENT, marker.start, undefined);
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
expect(completions !.entries).toEqual([
|
expect(completions!.entries).toEqual([
|
||||||
{
|
{
|
||||||
name: 'children',
|
name: 'children',
|
||||||
kind: CompletionKind.PROPERTY as any,
|
kind: CompletionKind.PROPERTY as any,
|
||||||
|
@ -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', () => {
|
||||||
@ -62,7 +62,7 @@ describe('TypeScriptServiceHost', () => {
|
|||||||
expect(oldModules.ngModules).toEqual([]);
|
expect(oldModules.ngModules).toEqual([]);
|
||||||
// Now add a script, this would change the program
|
// Now add a script, this would change the program
|
||||||
const fileName = '/app/main.ts';
|
const fileName = '/app/main.ts';
|
||||||
const content = tsLSHost.readFile(fileName) !;
|
const content = tsLSHost.readFile(fileName)!;
|
||||||
tsLSHost.addScript(fileName, content);
|
tsLSHost.addScript(fileName, content);
|
||||||
// If the caches are not cleared, we would get back an empty array.
|
// If the caches are not cleared, we would get back an empty array.
|
||||||
// But if the caches are cleared then the analyzed modules will be non-empty.
|
// But if the caches are cleared then the analyzed modules will be non-empty.
|
||||||
@ -121,8 +121,8 @@ describe('TypeScriptServiceHost', () => {
|
|||||||
expect(oldModules.symbolsMissingModule).toEqual([]);
|
expect(oldModules.symbolsMissingModule).toEqual([]);
|
||||||
// Expect to find AppComponent in the old modules
|
// Expect to find AppComponent in the old modules
|
||||||
const oldFile = oldModules.files.find(f => f.fileName === fileName);
|
const oldFile = oldModules.files.find(f => f.fileName === fileName);
|
||||||
expect(oldFile !.directives.length).toBe(1);
|
expect(oldFile!.directives.length).toBe(1);
|
||||||
const appComp = oldFile !.directives[0];
|
const appComp = oldFile!.directives[0];
|
||||||
expect(appComp.name).toBe('AppComponent');
|
expect(appComp.name).toBe('AppComponent');
|
||||||
expect(oldModules.ngModuleByPipeOrDirective.has(appComp)).toBe(true);
|
expect(oldModules.ngModuleByPipeOrDirective.has(appComp)).toBe(true);
|
||||||
|
|
||||||
@ -154,8 +154,8 @@ describe('TypeScriptServiceHost', () => {
|
|||||||
expect(newModules.symbolsMissingModule).toEqual([]);
|
expect(newModules.symbolsMissingModule).toEqual([]);
|
||||||
// Expect to find HelloComponent in the new modules
|
// Expect to find HelloComponent in the new modules
|
||||||
const newFile = newModules.files.find(f => f.fileName === fileName);
|
const newFile = newModules.files.find(f => f.fileName === fileName);
|
||||||
expect(newFile !.directives.length).toBe(1);
|
expect(newFile!.directives.length).toBe(1);
|
||||||
const helloComp = newFile !.directives[0];
|
const helloComp = newFile!.directives[0];
|
||||||
expect(helloComp.name).toBe('HelloComponent');
|
expect(helloComp.name).toBe('HelloComponent');
|
||||||
expect(newModules.ngModuleByPipeOrDirective.has(helloComp)).toBe(true);
|
expect(newModules.ngModuleByPipeOrDirective.has(helloComp)).toBe(true);
|
||||||
expect(newModules.ngModuleByPipeOrDirective.has(appComp)).toBe(false);
|
expect(newModules.ngModuleByPipeOrDirective.has(appComp)).toBe(false);
|
||||||
|
@ -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 [];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,9 +41,9 @@ describe('symbol query', () => {
|
|||||||
const host = new MockLanguageServiceHost(
|
const host = new MockLanguageServiceHost(
|
||||||
['/quickstart/app/app.component.ts'], QUICKSTART, '/quickstart');
|
['/quickstart/app/app.component.ts'], QUICKSTART, '/quickstart');
|
||||||
const service = ts.createLanguageService(host, registry);
|
const service = ts.createLanguageService(host, registry);
|
||||||
program = service.getProgram() !;
|
program = service.getProgram()!;
|
||||||
checker = program.getTypeChecker();
|
checker = program.getTypeChecker();
|
||||||
sourceFile = program.getSourceFile('/quickstart/app/app.component.ts') !;
|
sourceFile = program.getSourceFile('/quickstart/app/app.component.ts')!;
|
||||||
const symbolResolverHost = new ReflectorHost(() => program, host);
|
const symbolResolverHost = new ReflectorHost(() => program, host);
|
||||||
context = new DiagnosticContext(service, program, checker, symbolResolverHost);
|
context = new DiagnosticContext(service, program, checker, symbolResolverHost);
|
||||||
query = getSymbolQuery(program, checker, sourceFile, emptyPipes);
|
query = getSymbolQuery(program, checker, sourceFile, emptyPipes);
|
||||||
@ -67,7 +73,7 @@ describe('symbol query', () => {
|
|||||||
} else {
|
} else {
|
||||||
const symbol = query.getBuiltinType(builtinType);
|
const symbol = query.getBuiltinType(builtinType);
|
||||||
const got: ts.TypeFlags = (symbol as any).tsType.flags;
|
const got: ts.TypeFlags = (symbol as any).tsType.flags;
|
||||||
expect(got).toBe(want !);
|
expect(got).toBe(want!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -75,8 +81,8 @@ describe('symbol query', () => {
|
|||||||
|
|
||||||
describe('toSymbolTableFactory(tsVersion)', () => {
|
describe('toSymbolTableFactory(tsVersion)', () => {
|
||||||
it('should return a Map for versions of TypeScript >= 2.2', () => {
|
it('should return a Map for versions of TypeScript >= 2.2', () => {
|
||||||
const a = { name: 'a' } as ts.Symbol;
|
const a = {name: 'a'} as ts.Symbol;
|
||||||
const b = { name: 'b' } as ts.Symbol;
|
const b = {name: 'b'} as ts.Symbol;
|
||||||
expect(toSymbolTableFactory([a, b]) instanceof Map).toEqual(true);
|
expect(toSymbolTableFactory([a, b]) instanceof Map).toEqual(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -28,7 +28,7 @@ describe('getDirectiveClassLike', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
const {decoratorId, classId} = result !;
|
const {decoratorId, classId} = result!;
|
||||||
expect(decoratorId.kind).toBe(ts.SyntaxKind.Identifier);
|
expect(decoratorId.kind).toBe(ts.SyntaxKind.Identifier);
|
||||||
expect(decoratorId.text).toBe('NgModule');
|
expect(decoratorId.text).toBe('NgModule');
|
||||||
expect(classId.text).toBe('AppModule');
|
expect(classId.text).toBe('AppModule');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user