refactor(language-service): Remove NgLSHost -> NgLS dependency (#31122)
``` NgLSHost: AngularLanguageServiceHost NgLS: AngularLanguageService ``` NgLSHost should not depend on NgLS, because it introduces circular dependency. Instead, the `getTemplateAst` and `getTemplatAstAtPosition` methods should be moved to NgLSHost and exposed as public methods. This removes the circular dependency, and also removes the need for the awkward 'setSite' method in NgLSHost. PR Close #31122
This commit is contained in:
parent
c34abf2cbc
commit
4ec50811d4
|
@ -7,49 +7,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {NgAnalyzedModules, StaticSymbol} from '@angular/compiler';
|
import {NgAnalyzedModules, StaticSymbol} from '@angular/compiler';
|
||||||
import {DiagnosticTemplateInfo, getTemplateExpressionDiagnostics} from '@angular/compiler-cli/src/language_services';
|
|
||||||
|
|
||||||
import {AstResult} from './common';
|
import {AstResult} from './common';
|
||||||
import {Declarations, Diagnostic, DiagnosticKind, DiagnosticMessageChain, Diagnostics, Span, TemplateSource} from './types';
|
import {Declarations, Diagnostic, DiagnosticKind, DiagnosticMessageChain, Diagnostics, Span, TemplateSource} from './types';
|
||||||
import {offsetSpan, spanOf} from './utils';
|
|
||||||
|
|
||||||
export interface AstProvider {
|
export interface AstProvider {
|
||||||
getTemplateAst(template: TemplateSource, fileName: string): AstResult;
|
getTemplateAst(template: TemplateSource, fileName: string): AstResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTemplateDiagnostics(
|
|
||||||
fileName: string, astProvider: AstProvider, templates: TemplateSource[]): Diagnostics {
|
|
||||||
const results: Diagnostics = [];
|
|
||||||
for (const template of templates) {
|
|
||||||
const ast = astProvider.getTemplateAst(template, fileName);
|
|
||||||
if (ast) {
|
|
||||||
if (ast.parseErrors && ast.parseErrors.length) {
|
|
||||||
results.push(...ast.parseErrors.map<Diagnostic>(
|
|
||||||
e => ({
|
|
||||||
kind: DiagnosticKind.Error,
|
|
||||||
span: offsetSpan(spanOf(e.span), template.span.start),
|
|
||||||
message: e.msg
|
|
||||||
})));
|
|
||||||
} else if (ast.templateAst && ast.htmlAst) {
|
|
||||||
const info: DiagnosticTemplateInfo = {
|
|
||||||
templateAst: ast.templateAst,
|
|
||||||
htmlAst: ast.htmlAst,
|
|
||||||
offset: template.span.start,
|
|
||||||
query: template.query,
|
|
||||||
members: template.members
|
|
||||||
};
|
|
||||||
const expressionDiagnostics = getTemplateExpressionDiagnostics(info);
|
|
||||||
results.push(...expressionDiagnostics);
|
|
||||||
}
|
|
||||||
if (ast.errors) {
|
|
||||||
results.push(...ast.errors.map<Diagnostic>(
|
|
||||||
e => ({kind: e.kind, span: e.span || template.span, message: e.message})));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDeclarationDiagnostics(
|
export function getDeclarationDiagnostics(
|
||||||
declarations: Declarations, modules: NgAnalyzedModules): Diagnostics {
|
declarations: Declarations, modules: NgAnalyzedModules): Diagnostics {
|
||||||
const results: Diagnostics = [];
|
const results: Diagnostics = [];
|
||||||
|
|
|
@ -6,14 +6,16 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompilerConfig, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, Lexer, NgAnalyzedModules, Parser, TemplateParser} from '@angular/compiler';
|
import {CompileMetadataResolver, CompilePipeSummary} from '@angular/compiler';
|
||||||
|
import {DiagnosticTemplateInfo, getTemplateExpressionDiagnostics} from '@angular/compiler-cli/src/language_services';
|
||||||
|
|
||||||
import {AstResult, TemplateInfo} from './common';
|
|
||||||
import {getTemplateCompletions} from './completions';
|
import {getTemplateCompletions} from './completions';
|
||||||
import {getDefinition} from './definitions';
|
import {getDefinition} from './definitions';
|
||||||
import {getDeclarationDiagnostics, getTemplateDiagnostics} from './diagnostics';
|
import {getDeclarationDiagnostics} from './diagnostics';
|
||||||
import {getHover} from './hover';
|
import {getHover} from './hover';
|
||||||
import {Completions, Definition, Diagnostic, DiagnosticKind, Diagnostics, Hover, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types';
|
import {Completions, Definition, Diagnostic, DiagnosticKind, Diagnostics, Hover, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types';
|
||||||
|
import {offsetSpan, spanOf} from './utils';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,7 +38,7 @@ class LanguageServiceImpl implements LanguageService {
|
||||||
let results: Diagnostics = [];
|
let results: Diagnostics = [];
|
||||||
let templates = this.host.getTemplates(fileName);
|
let templates = this.host.getTemplates(fileName);
|
||||||
if (templates && templates.length) {
|
if (templates && templates.length) {
|
||||||
results.push(...getTemplateDiagnostics(fileName, this, templates));
|
results.push(...this.getTemplateDiagnostics(fileName, templates));
|
||||||
}
|
}
|
||||||
|
|
||||||
let declarations = this.host.getDeclarations(fileName);
|
let declarations = this.host.getDeclarations(fileName);
|
||||||
|
@ -49,7 +51,7 @@ class LanguageServiceImpl implements LanguageService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getPipesAt(fileName: string, position: number): CompilePipeSummary[] {
|
getPipesAt(fileName: string, position: number): CompilePipeSummary[] {
|
||||||
let templateInfo = this.getTemplateAstAtPosition(fileName, position);
|
let templateInfo = this.host.getTemplateAstAtPosition(fileName, position);
|
||||||
if (templateInfo) {
|
if (templateInfo) {
|
||||||
return templateInfo.pipes;
|
return templateInfo.pipes;
|
||||||
}
|
}
|
||||||
|
@ -57,100 +59,59 @@ class LanguageServiceImpl implements LanguageService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompletionsAt(fileName: string, position: number): Completions {
|
getCompletionsAt(fileName: string, position: number): Completions {
|
||||||
let templateInfo = this.getTemplateAstAtPosition(fileName, position);
|
let templateInfo = this.host.getTemplateAstAtPosition(fileName, position);
|
||||||
if (templateInfo) {
|
if (templateInfo) {
|
||||||
return getTemplateCompletions(templateInfo);
|
return getTemplateCompletions(templateInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefinitionAt(fileName: string, position: number): Definition {
|
getDefinitionAt(fileName: string, position: number): Definition {
|
||||||
let templateInfo = this.getTemplateAstAtPosition(fileName, position);
|
let templateInfo = this.host.getTemplateAstAtPosition(fileName, position);
|
||||||
if (templateInfo) {
|
if (templateInfo) {
|
||||||
return getDefinition(templateInfo);
|
return getDefinition(templateInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getHoverAt(fileName: string, position: number): Hover|undefined {
|
getHoverAt(fileName: string, position: number): Hover|undefined {
|
||||||
let templateInfo = this.getTemplateAstAtPosition(fileName, position);
|
let templateInfo = this.host.getTemplateAstAtPosition(fileName, position);
|
||||||
if (templateInfo) {
|
if (templateInfo) {
|
||||||
return getHover(templateInfo);
|
return getHover(templateInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTemplateAstAtPosition(fileName: string, position: number): TemplateInfo|undefined {
|
private getTemplateDiagnostics(fileName: string, templates: TemplateSource[]): Diagnostics {
|
||||||
let template = this.host.getTemplateAt(fileName, position);
|
const results: Diagnostics = [];
|
||||||
if (template) {
|
for (const template of templates) {
|
||||||
let astResult = this.getTemplateAst(template, fileName);
|
const ast = this.host.getTemplateAst(template, fileName);
|
||||||
if (astResult && astResult.htmlAst && astResult.templateAst && astResult.directive &&
|
if (ast) {
|
||||||
astResult.directives && astResult.pipes && astResult.expressionParser)
|
if (ast.parseErrors && ast.parseErrors.length) {
|
||||||
return {
|
results.push(...ast.parseErrors.map<Diagnostic>(
|
||||||
position,
|
e => ({
|
||||||
fileName,
|
kind: DiagnosticKind.Error,
|
||||||
template,
|
span: offsetSpan(spanOf(e.span), template.span.start),
|
||||||
htmlAst: astResult.htmlAst,
|
message: e.msg
|
||||||
directive: astResult.directive,
|
})));
|
||||||
directives: astResult.directives,
|
} else if (ast.templateAst && ast.htmlAst) {
|
||||||
pipes: astResult.pipes,
|
const info: DiagnosticTemplateInfo = {
|
||||||
templateAst: astResult.templateAst,
|
templateAst: ast.templateAst,
|
||||||
expressionParser: astResult.expressionParser
|
htmlAst: ast.htmlAst,
|
||||||
};
|
offset: template.span.start,
|
||||||
}
|
query: template.query,
|
||||||
return undefined;
|
members: template.members
|
||||||
}
|
|
||||||
|
|
||||||
getTemplateAst(template: TemplateSource, contextFile: string): AstResult {
|
|
||||||
let result: AstResult|undefined = undefined;
|
|
||||||
try {
|
|
||||||
const resolvedMetadata =
|
|
||||||
this.metadataResolver.getNonNormalizedDirectiveMetadata(template.type as any);
|
|
||||||
const metadata = resolvedMetadata && resolvedMetadata.metadata;
|
|
||||||
if (metadata) {
|
|
||||||
const rawHtmlParser = new HtmlParser();
|
|
||||||
const htmlParser = new I18NHtmlParser(rawHtmlParser);
|
|
||||||
const expressionParser = new Parser(new Lexer());
|
|
||||||
const config = new CompilerConfig();
|
|
||||||
const parser = new TemplateParser(
|
|
||||||
config, this.host.resolver.getReflector(), expressionParser,
|
|
||||||
new DomElementSchemaRegistry(), htmlParser, null !, []);
|
|
||||||
const htmlResult = htmlParser.parse(template.source, '', {tokenizeExpansionForms: true});
|
|
||||||
const analyzedModules = this.host.getAnalyzedModules();
|
|
||||||
let errors: Diagnostic[]|undefined = undefined;
|
|
||||||
let ngModule = analyzedModules.ngModuleByPipeOrDirective.get(template.type);
|
|
||||||
if (!ngModule) {
|
|
||||||
// Reported by the the declaration diagnostics.
|
|
||||||
ngModule = findSuitableDefaultModule(analyzedModules);
|
|
||||||
}
|
|
||||||
if (ngModule) {
|
|
||||||
const resolvedDirectives = ngModule.transitiveModule.directives.map(
|
|
||||||
d => this.host.resolver.getNonNormalizedDirectiveMetadata(d.reference));
|
|
||||||
const directives = removeMissing(resolvedDirectives).map(d => d.metadata.toSummary());
|
|
||||||
const pipes = ngModule.transitiveModule.pipes.map(
|
|
||||||
p => this.host.resolver.getOrLoadPipeMetadata(p.reference).toSummary());
|
|
||||||
const schemas = ngModule.schemas;
|
|
||||||
const parseResult = parser.tryParseHtml(htmlResult, metadata, directives, pipes, schemas);
|
|
||||||
result = {
|
|
||||||
htmlAst: htmlResult.rootNodes,
|
|
||||||
templateAst: parseResult.templateAst,
|
|
||||||
directive: metadata, directives, pipes,
|
|
||||||
parseErrors: parseResult.errors, expressionParser, errors
|
|
||||||
};
|
};
|
||||||
|
const expressionDiagnostics = getTemplateExpressionDiagnostics(info);
|
||||||
|
results.push(...expressionDiagnostics);
|
||||||
|
}
|
||||||
|
if (ast.errors) {
|
||||||
|
results.push(...ast.errors.map<Diagnostic>(
|
||||||
|
e => ({kind: e.kind, span: e.span || template.span, message: e.message})));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
let span = template.span;
|
|
||||||
if (e.fileName == contextFile) {
|
|
||||||
span = template.query.getSpanAt(e.line, e.column) || span;
|
|
||||||
}
|
|
||||||
result = {errors: [{kind: DiagnosticKind.Error, message: e.message, span}]};
|
|
||||||
}
|
}
|
||||||
return result || {};
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeMissing<T>(values: (T | null | undefined)[]): T[] {
|
|
||||||
return values.filter(e => !!e) as T[];
|
|
||||||
}
|
|
||||||
|
|
||||||
function uniqueBySpan < T extends {
|
function uniqueBySpan < T extends {
|
||||||
span: Span;
|
span: Span;
|
||||||
}
|
}
|
||||||
|
@ -173,16 +134,3 @@ function uniqueBySpan < T extends {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function findSuitableDefaultModule(modules: NgAnalyzedModules): CompileNgModuleMetadata|undefined {
|
|
||||||
let result: CompileNgModuleMetadata|undefined = undefined;
|
|
||||||
let resultSize = 0;
|
|
||||||
for (const module of modules.ngModules) {
|
|
||||||
const moduleSize = module.transitiveModule.directives.length;
|
|
||||||
if (moduleSize > resultSize) {
|
|
||||||
result = module;
|
|
||||||
resultSize = moduleSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
|
@ -81,7 +81,6 @@ export function create(info: tss.server.PluginCreateInfo): ts.LanguageService {
|
||||||
|
|
||||||
const serviceHost = new TypeScriptServiceHost(info.languageServiceHost, oldLS);
|
const serviceHost = new TypeScriptServiceHost(info.languageServiceHost, oldLS);
|
||||||
const ls = createLanguageService(serviceHost);
|
const ls = createLanguageService(serviceHost);
|
||||||
serviceHost.setSite(ls);
|
|
||||||
projectHostMap.set(info.project, serviceHost);
|
projectHostMap.set(info.project, serviceHost);
|
||||||
|
|
||||||
proxy.getCompletionsAtPosition = function(
|
proxy.getCompletionsAtPosition = function(
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileMetadataResolver, CompilePipeSummary, NgAnalyzedModules, StaticSymbol} from '@angular/compiler';
|
import {CompileDirectiveMetadata, CompileMetadataResolver, CompilePipeSummary, NgAnalyzedModules, StaticSymbol} from '@angular/compiler';
|
||||||
import {BuiltinType, DeclarationKind, Definition, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from '@angular/compiler-cli/src/language_services';
|
import {BuiltinType, DeclarationKind, Definition, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from '@angular/compiler-cli/src/language_services';
|
||||||
|
import {AstResult, TemplateInfo} from './common';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
BuiltinType,
|
BuiltinType,
|
||||||
|
@ -203,6 +204,16 @@ export interface LanguageServiceHost {
|
||||||
* Return a list all the template files referenced by the project.
|
* Return a list all the template files referenced by the project.
|
||||||
*/
|
*/
|
||||||
getTemplateReferences(): string[];
|
getTemplateReferences(): string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the AST for both HTML and template for the contextFile.
|
||||||
|
*/
|
||||||
|
getTemplateAst(template: TemplateSource, contextFile: string): AstResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the template AST for the node that corresponds to the position.
|
||||||
|
*/
|
||||||
|
getTemplateAstAtPosition(fileName: string, position: number): TemplateInfo|undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,16 +6,18 @@
|
||||||
* 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, FormattedError, FormattedMessageChain, HtmlParser, JitSummaryResolver, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, analyzeNgModules, createOfflineCompileUrlResolver, isFormattedError} from '@angular/compiler';
|
import {AotSummaryResolver, 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 {CompilerOptions, getClassMembersFromDeclaration, getPipesTable, getSymbolQuery} from '@angular/compiler-cli/src/language_services';
|
import {CompilerOptions, getClassMembersFromDeclaration, getPipesTable, getSymbolQuery} from '@angular/compiler-cli/src/language_services';
|
||||||
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
import {AstResult, TemplateInfo} from './common';
|
||||||
import {createLanguageService} from './language_service';
|
import {createLanguageService} from './language_service';
|
||||||
import {ReflectorHost} from './reflector_host';
|
import {ReflectorHost} from './reflector_host';
|
||||||
import {Declaration, DeclarationError, Declarations, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, Symbol, SymbolQuery, TemplateSource, TemplateSources} from './types';
|
import {Declaration, DeclarationError, Declarations, Diagnostic, DiagnosticKind, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, Symbol, SymbolQuery, TemplateSource, TemplateSources} from './types';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,7 +27,6 @@ export function createLanguageServiceFromTypescript(
|
||||||
host: ts.LanguageServiceHost, service: ts.LanguageService): LanguageService {
|
host: ts.LanguageServiceHost, service: ts.LanguageService): LanguageService {
|
||||||
const ngHost = new TypeScriptServiceHost(host, service);
|
const ngHost = new TypeScriptServiceHost(host, service);
|
||||||
const ngServer = createLanguageService(ngHost);
|
const ngServer = createLanguageService(ngHost);
|
||||||
ngHost.setSite(ngServer);
|
|
||||||
return ngServer;
|
return ngServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +76,6 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
// TODO(issue/24571): remove '!'.
|
// TODO(issue/24571): remove '!'.
|
||||||
private analyzedModules !: NgAnalyzedModules | null;
|
private analyzedModules !: NgAnalyzedModules | null;
|
||||||
// TODO(issue/24571): remove '!'.
|
// TODO(issue/24571): remove '!'.
|
||||||
private service !: LanguageService;
|
|
||||||
// TODO(issue/24571): remove '!'.
|
|
||||||
private fileToComponent !: Map<string, StaticSymbol>| null;
|
private fileToComponent !: Map<string, StaticSymbol>| null;
|
||||||
// TODO(issue/24571): remove '!'.
|
// TODO(issue/24571): remove '!'.
|
||||||
private templateReferences !: string[] | null;
|
private templateReferences !: string[] | null;
|
||||||
|
@ -86,8 +85,6 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
|
|
||||||
constructor(private host: ts.LanguageServiceHost, private tsService: ts.LanguageService) {}
|
constructor(private host: ts.LanguageServiceHost, private tsService: ts.LanguageService) {}
|
||||||
|
|
||||||
setSite(service: LanguageService) { this.service = service; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Angular LanguageServiceHost implementation
|
* Angular LanguageServiceHost implementation
|
||||||
*/
|
*/
|
||||||
|
@ -323,7 +320,11 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
},
|
},
|
||||||
get query() {
|
get query() {
|
||||||
if (!queryCache) {
|
if (!queryCache) {
|
||||||
const pipes = t.service.getPipesAt(fileName, node.getStart());
|
let pipes: CompilePipeSummary[] = [];
|
||||||
|
const templateInfo = t.getTemplateAstAtPosition(fileName, node.getStart());
|
||||||
|
if (templateInfo) {
|
||||||
|
pipes = templateInfo.pipes;
|
||||||
|
}
|
||||||
queryCache = getSymbolQuery(
|
queryCache = getSymbolQuery(
|
||||||
t.program !, t.checker, sourceFile,
|
t.program !, t.checker, sourceFile,
|
||||||
() => getPipesTable(sourceFile, t.program !, t.checker, pipes));
|
() => getPipesTable(sourceFile, t.program !, t.checker, pipes));
|
||||||
|
@ -590,8 +591,91 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
|
|
||||||
return find(sourceFile);
|
return find(sourceFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTemplateAstAtPosition(fileName: string, position: number): TemplateInfo|undefined {
|
||||||
|
let template = this.getTemplateAt(fileName, position);
|
||||||
|
if (template) {
|
||||||
|
let astResult = this.getTemplateAst(template, fileName);
|
||||||
|
if (astResult && astResult.htmlAst && astResult.templateAst && astResult.directive &&
|
||||||
|
astResult.directives && astResult.pipes && astResult.expressionParser)
|
||||||
|
return {
|
||||||
|
position,
|
||||||
|
fileName,
|
||||||
|
template,
|
||||||
|
htmlAst: astResult.htmlAst,
|
||||||
|
directive: astResult.directive,
|
||||||
|
directives: astResult.directives,
|
||||||
|
pipes: astResult.pipes,
|
||||||
|
templateAst: astResult.templateAst,
|
||||||
|
expressionParser: astResult.expressionParser
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTemplateAst(template: TemplateSource, contextFile: string): AstResult {
|
||||||
|
let result: AstResult|undefined = undefined;
|
||||||
|
try {
|
||||||
|
const resolvedMetadata =
|
||||||
|
this.resolver.getNonNormalizedDirectiveMetadata(template.type as any);
|
||||||
|
const metadata = resolvedMetadata && resolvedMetadata.metadata;
|
||||||
|
if (metadata) {
|
||||||
|
const rawHtmlParser = new HtmlParser();
|
||||||
|
const htmlParser = new I18NHtmlParser(rawHtmlParser);
|
||||||
|
const expressionParser = new Parser(new Lexer());
|
||||||
|
const config = new CompilerConfig();
|
||||||
|
const parser = new TemplateParser(
|
||||||
|
config, this.resolver.getReflector(), expressionParser, new DomElementSchemaRegistry(),
|
||||||
|
htmlParser, null !, []);
|
||||||
|
const htmlResult = htmlParser.parse(template.source, '', {tokenizeExpansionForms: true});
|
||||||
|
const analyzedModules = this.getAnalyzedModules();
|
||||||
|
let errors: Diagnostic[]|undefined = undefined;
|
||||||
|
let ngModule = analyzedModules.ngModuleByPipeOrDirective.get(template.type);
|
||||||
|
if (!ngModule) {
|
||||||
|
// Reported by the the declaration diagnostics.
|
||||||
|
ngModule = findSuitableDefaultModule(analyzedModules);
|
||||||
|
}
|
||||||
|
if (ngModule) {
|
||||||
|
const directives =
|
||||||
|
ngModule.transitiveModule.directives
|
||||||
|
.map(d => this.resolver.getNonNormalizedDirectiveMetadata(d.reference))
|
||||||
|
.filter(d => d)
|
||||||
|
.map(d => d !.metadata.toSummary());
|
||||||
|
const pipes = ngModule.transitiveModule.pipes.map(
|
||||||
|
p => this.resolver.getOrLoadPipeMetadata(p.reference).toSummary());
|
||||||
|
const schemas = ngModule.schemas;
|
||||||
|
const parseResult = parser.tryParseHtml(htmlResult, metadata, directives, pipes, schemas);
|
||||||
|
result = {
|
||||||
|
htmlAst: htmlResult.rootNodes,
|
||||||
|
templateAst: parseResult.templateAst,
|
||||||
|
directive: metadata, directives, pipes,
|
||||||
|
parseErrors: parseResult.errors, expressionParser, errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
let span = template.span;
|
||||||
|
if (e.fileName == contextFile) {
|
||||||
|
span = template.query.getSpanAt(e.line, e.column) || span;
|
||||||
|
}
|
||||||
|
result = {errors: [{kind: DiagnosticKind.Error, message: e.message, span}]};
|
||||||
|
}
|
||||||
|
return result || {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findSuitableDefaultModule(modules: NgAnalyzedModules): CompileNgModuleMetadata|undefined {
|
||||||
|
let result: CompileNgModuleMetadata|undefined = undefined;
|
||||||
|
let resultSize = 0;
|
||||||
|
for (const module of modules.ngModules) {
|
||||||
|
const moduleSize = module.transitiveModule.directives.length;
|
||||||
|
if (moduleSize > resultSize) {
|
||||||
|
result = module;
|
||||||
|
resultSize = moduleSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function findTsConfig(fileName: string): string|undefined {
|
function findTsConfig(fileName: string): string|undefined {
|
||||||
let dir = path.dirname(fileName);
|
let dir = path.dirname(fileName);
|
||||||
|
|
|
@ -22,7 +22,6 @@ describe('completions', () => {
|
||||||
let service = ts.createLanguageService(mockHost, documentRegistry);
|
let service = ts.createLanguageService(mockHost, documentRegistry);
|
||||||
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||||
let ngService = createLanguageService(ngHost);
|
let ngService = createLanguageService(ngHost);
|
||||||
ngHost.setSite(ngService);
|
|
||||||
|
|
||||||
it('should be able to get entity completions',
|
it('should be able to get entity completions',
|
||||||
() => { contains('/app/test.ng', 'entity-amp', '&', '>', '<', 'ι'); });
|
() => { contains('/app/test.ng', 'entity-amp', '&', '>', '<', 'ι'); });
|
||||||
|
|
|
@ -22,7 +22,6 @@ describe('definitions', () => {
|
||||||
let service = ts.createLanguageService(mockHost, documentRegistry);
|
let service = ts.createLanguageService(mockHost, documentRegistry);
|
||||||
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||||
let ngService = createLanguageService(ngHost);
|
let ngService = createLanguageService(ngHost);
|
||||||
ngHost.setSite(ngService);
|
|
||||||
|
|
||||||
it('should be able to find field in an interpolation', () => {
|
it('should be able to find field in an interpolation', () => {
|
||||||
localReference(
|
localReference(
|
||||||
|
|
|
@ -26,7 +26,6 @@ describe('diagnostics', () => {
|
||||||
const service = ts.createLanguageService(mockHost, documentRegistry);
|
const service = ts.createLanguageService(mockHost, documentRegistry);
|
||||||
ngHost = new TypeScriptServiceHost(mockHost, service);
|
ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||||
ngService = createLanguageService(ngHost);
|
ngService = createLanguageService(ngHost);
|
||||||
ngHost.setSite(ngService);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be no diagnostics for test.ng',
|
it('should be no diagnostics for test.ng',
|
||||||
|
|
|
@ -23,7 +23,6 @@ describe('hover', () => {
|
||||||
let service = ts.createLanguageService(mockHost, documentRegistry);
|
let service = ts.createLanguageService(mockHost, documentRegistry);
|
||||||
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||||
let ngService = createLanguageService(ngHost);
|
let ngService = createLanguageService(ngHost);
|
||||||
ngHost.setSite(ngService);
|
|
||||||
|
|
||||||
|
|
||||||
it('should be able to find field in an interpolation', () => {
|
it('should be able to find field in an interpolation', () => {
|
||||||
|
|
|
@ -27,7 +27,6 @@ describe('references', () => {
|
||||||
service = ts.createLanguageService(mockHost, documentRegistry);
|
service = ts.createLanguageService(mockHost, documentRegistry);
|
||||||
ngHost = new TypeScriptServiceHost(mockHost, service);
|
ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||||
ngService = createLanguageService(ngHost);
|
ngService = createLanguageService(ngHost);
|
||||||
ngHost.setSite(ngService);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to get template references',
|
it('should be able to get template references',
|
||||||
|
|
Loading…
Reference in New Issue