Skip to content

Commit 3c8a61e

Browse files
vicbtbosch
authored andcommitted
feat: add support for TS 2.2
1 parent bccfaa4 commit 3c8a61e

4 files changed

Lines changed: 95 additions & 50 deletions

File tree

‎packages/language-service/src/ts_plugin.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,25 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
7171

7272
proxy.getQuickInfoAtPosition = function(fileName: string, position: number): ts.QuickInfo {
7373
let base = oldLS.getQuickInfoAtPosition(fileName, position);
74+
// TODO(vicb): the tags property has been removed in TS 2.2
75+
const tags = (<any>base).tags;
7476
tryOperation('get quick info', () => {
7577
const ours = ls.getHoverAt(fileName, position);
7678
if (ours) {
7779
const displayParts: typeof base.displayParts = [];
7880
for (const part of ours.text) {
7981
displayParts.push({kind: part.language !, text: part.text});
8082
}
81-
base = {
83+
base = <any>{
8284
displayParts,
8385
documentation: [],
8486
kind: 'angular',
8587
kindModifiers: 'what does this do?',
8688
textSpan: {start: ours.span.start, length: ours.span.end - ours.span.start},
87-
tags: [],
8889
};
90+
if (tags) {
91+
(<any>base).tags = tags;
92+
}
8993
}
9094
});
9195

‎packages/language-service/src/typescript_host.ts‎

Lines changed: 75 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {AotSummaryResolver, CompileDirectiveMetadata, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, InterpolationConfig, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticAndDynamicReflectionCapabilities, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, SummaryResolver, UrlResolver, analyzeNgModules, componentModuleUrl, createOfflineCompileUrlResolver, extractProgramSymbols} from '@angular/compiler';
9+
import {AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, InterpolationConfig, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, PipeResolver, ResourceLoader, StaticAndDynamicReflectionCapabilities, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, SummaryResolver, analyzeNgModules, componentModuleUrl, createOfflineCompileUrlResolver, extractProgramSymbols} from '@angular/compiler';
1010
import {AngularCompilerOptions} from '@angular/compiler-cli';
11-
import {Type, ViewEncapsulation, ɵConsole as Console} from '@angular/core';
11+
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
1212
import * as fs from 'fs';
1313
import * as path from 'path';
1414
import * as ts from 'typescript';
1515

1616
import {createLanguageService} from './language_service';
1717
import {ReflectorHost} from './reflector_host';
1818
import {BuiltinType, CompletionKind, Declaration, DeclarationError, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types';
19+
import {isTypescriptVersion} from './utils';
1920

2021

2122

@@ -25,6 +26,7 @@ const isPrivate = (ts as any).ModifierFlags ?
2526
((node: ts.Node) =>
2627
!!((ts as any).getCombinedModifierFlags(node) & (ts as any).ModifierFlags.Private)) :
2728
((node: ts.Node) => !!(node.flags & (ts as any).NodeFlags.Private));
29+
2830
const isReferenceType = (ts as any).ObjectFlags ?
2931
((type: ts.Type) =>
3032
!!(type.flags & (ts as any).TypeFlags.Object &&
@@ -46,11 +48,9 @@ export function createLanguageServiceFromTypescript(
4648
* The language service never needs the normalized versions of the metadata. To avoid parsing
4749
* the content and resolving references, return an empty file. This also allows normalizing
4850
* template that are syntatically incorrect which is required to provide completions in
49-
* syntatically incorrect templates.
51+
* syntactically incorrect templates.
5052
*/
5153
export class DummyHtmlParser extends HtmlParser {
52-
constructor() { super(); }
53-
5454
parse(
5555
source: string, url: string, parseExpansionForms: boolean = false,
5656
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ParseTreeResult {
@@ -71,7 +71,7 @@ export class DummyResourceLoader extends ResourceLoader {
7171
* The `TypeScriptServiceHost` implements the Angular `LanguageServiceHost` using
7272
* the TypeScript language services.
7373
*
74-
* @expermental
74+
* @experimental
7575
*/
7676
export class TypeScriptServiceHost implements LanguageServiceHost {
7777
private _resolver: CompileMetadataResolver;
@@ -342,7 +342,6 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
342342
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
343343
case ts.SyntaxKind.StringLiteral:
344344
let [declaration, decorator] = this.getTemplateClassDeclFromNode(node);
345-
let queryCache: SymbolQuery|undefined = undefined;
346345
if (declaration && declaration.name) {
347346
const sourceFile = this.getSourceFile(fileName);
348347
return this.getSourceFromDeclaration(
@@ -564,8 +563,6 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
564563
}
565564

566565
private findNode(sourceFile: ts.SourceFile, position: number): ts.Node|undefined {
567-
let _this = this;
568-
569566
function find(node: ts.Node): ts.Node|undefined {
570567
if (position >= node.getStart() && position < node.getEnd()) {
571568
return ts.forEachChild(node, find) || node;
@@ -634,8 +631,6 @@ class TypeScriptSymbolQuery implements SymbolQuery {
634631

635632
getTypeUnion(...types: Symbol[]): Symbol {
636633
// TODO: Replace with typeChecker API when available
637-
const checker = this.checker;
638-
639634
// No API exists so the cheat is to just return the last type any if no types are given.
640635
return types.length ? types[types.length - 1] : this.getBuiltinType(BuiltinType.Any);
641636
}
@@ -708,7 +703,9 @@ class TypeScriptSymbolQuery implements SymbolQuery {
708703

709704
private getTemplateRefContextType(typeSymbol: ts.Symbol): ts.Symbol|undefined {
710705
const type = this.checker.getTypeOfSymbolAtLocation(typeSymbol, this.source);
711-
const constructor = type.symbol && type.symbol.members && type.symbol.members['__constructor'];
706+
const constructor = type.symbol && type.symbol.members &&
707+
getFromSymbolTable(type.symbol.members !, '__constructor');
708+
712709
if (constructor) {
713710
const constructorDeclaration = constructor.declarations ![0] as ts.ConstructorTypeNode;
714711
for (const parameter of constructorDeclaration.parameters) {
@@ -719,7 +716,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
719716
return typeReference.typeArguments[0].symbol;
720717
}
721718
}
722-
};
719+
}
723720
}
724721
}
725722

@@ -761,29 +758,6 @@ function selectSignature(type: ts.Type, context: TypeContext, types: Symbol[]):
761758
return signatures.length ? new SignatureWrapper(signatures[0], context) : undefined;
762759
}
763760

764-
function toSymbolTable(symbols: ts.Symbol[]): ts.SymbolTable {
765-
const result: ts.SymbolTable = <any>{};
766-
for (const symbol of symbols) {
767-
result[symbol.name] = symbol;
768-
}
769-
return result;
770-
}
771-
772-
function toSymbols(
773-
symbolTable: ts.SymbolTable | undefined, filter?: (symbol: ts.Symbol) => boolean) {
774-
const result: ts.Symbol[] = [];
775-
if (!symbolTable) return result;
776-
const own = typeof symbolTable.hasOwnProperty === 'function' ?
777-
(name: string) => symbolTable.hasOwnProperty(name) :
778-
(name: string) => !!symbolTable[name];
779-
for (const name in symbolTable) {
780-
if (own(name) && (!filter || filter(symbolTable[name]))) {
781-
result.push(symbolTable[name]);
782-
}
783-
}
784-
return result;
785-
}
786-
787761
class TypeWrapper implements Symbol {
788762
constructor(public tsType: ts.Type, public context: TypeContext) {
789763
if (!tsType) {
@@ -932,31 +906,72 @@ class SignatureResultOverride implements Signature {
932906
get result(): Symbol { return this.resultType; }
933907
}
934908

909+
function toSymbolTable(symbols: ts.Symbol[]): ts.SymbolTable {
910+
if (isTypescriptVersion('2.2')) {
911+
const result = new Map<string, ts.Symbol>();
912+
for (const symbol of symbols) {
913+
result.set(symbol.name, symbol);
914+
}
915+
return <ts.SymbolTable>(result as any);
916+
}
917+
918+
const result = <any>{};
919+
for (const symbol of symbols) {
920+
result[symbol.name] = symbol;
921+
}
922+
return result as ts.SymbolTable;
923+
}
924+
925+
function toSymbols(symbolTable: ts.SymbolTable | undefined): ts.Symbol[] {
926+
if (!symbolTable) return [];
927+
928+
const table = symbolTable as any;
929+
930+
if (typeof table.values === 'function') {
931+
return Array.from(table.values()) as ts.Symbol[];
932+
}
933+
934+
const result: ts.Symbol[] = [];
935+
936+
const own = typeof table.hasOwnProperty === 'function' ?
937+
(name: string) => table.hasOwnProperty(name) :
938+
(name: string) => !!table[name];
939+
940+
for (const name in table) {
941+
if (own(name)) {
942+
result.push(table[name]);
943+
}
944+
}
945+
return result;
946+
}
947+
935948
class SymbolTableWrapper implements SymbolTable {
936949
private symbols: ts.Symbol[];
937950
private symbolTable: ts.SymbolTable;
938951

939-
constructor(
940-
symbols: ts.SymbolTable|ts.Symbol[]|undefined, private context: TypeContext,
941-
filter?: (symbol: ts.Symbol) => boolean) {
952+
constructor(symbols: ts.SymbolTable|ts.Symbol[]|undefined, private context: TypeContext) {
942953
symbols = symbols || [];
954+
943955
if (Array.isArray(symbols)) {
944-
this.symbols = filter ? symbols.filter(filter) : symbols;
956+
this.symbols = symbols;
945957
this.symbolTable = toSymbolTable(symbols);
946958
} else {
947-
this.symbols = toSymbols(symbols, filter);
948-
this.symbolTable = filter ? toSymbolTable(this.symbols) : symbols;
959+
this.symbols = toSymbols(symbols);
960+
this.symbolTable = symbols;
949961
}
950962
}
951963

952964
get size(): number { return this.symbols.length; }
953965

954966
get(key: string): Symbol|undefined {
955-
const symbol = this.symbolTable[key];
967+
const symbol = getFromSymbolTable(this.symbolTable, key);
956968
return symbol ? new SymbolWrapper(symbol, this.context) : undefined;
957969
}
958970

959-
has(key: string): boolean { return this.symbolTable[key] != null; }
971+
has(key: string): boolean {
972+
const table: any = this.symbolTable;
973+
return (typeof table.has === 'function') ? table.has(key) : table[key] != null;
974+
}
960975

961976
values(): Symbol[] { return this.symbols.map(s => new SymbolWrapper(s, this.context)); }
962977
}
@@ -1320,3 +1335,19 @@ function typeKindOf(type: ts.Type): BuiltinType {
13201335
}
13211336
return BuiltinType.Other;
13221337
}
1338+
1339+
1340+
function getFromSymbolTable(symbolTable: ts.SymbolTable, key: string): ts.Symbol|undefined {
1341+
const table = symbolTable as any;
1342+
let symbol: ts.Symbol|undefined;
1343+
1344+
if (typeof table.get === 'function') {
1345+
// TS 2.2 uses a Map
1346+
symbol = table.get(key);
1347+
} else {
1348+
// TS pre-2.2 uses an object
1349+
symbol = table[key];
1350+
}
1351+
1352+
return symbol;
1353+
}

‎packages/language-service/src/utils.ts‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {CompileDirectiveSummary, CompileTypeMetadata, CssSelector, ParseSourceSpan, SelectorMatcher, identifierName} from '@angular/compiler';
10-
9+
import {CompileDirectiveSummary, CompileTypeMetadata, CssSelector, ParseSourceSpan, identifierName} from '@angular/compiler';
10+
import * as ts from 'typescript';
1111
import {SelectorInfo, TemplateInfo} from './common';
1212
import {Span} from './types';
1313

@@ -96,3 +96,13 @@ export function uniqueByName < T extends {
9696
return result;
9797
}
9898
}
99+
100+
export function isTypescriptVersion(low: string, high?: string) {
101+
const version = ts.version;
102+
103+
if (version.substring(0, low.length) < low) return false;
104+
105+
if (high && (version.substring(0, high.length) > high)) return false;
106+
107+
return true;
108+
}

‎tools/@angular/tsc-wrapped/test/typescript.mocks.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as fs from 'fs';
2-
import * as path from 'path';
32
import * as ts from 'typescript';
43

54
export interface Directory { [name: string]: (Directory|string); }
@@ -127,7 +126,8 @@ export class MockSymbol implements ts.Symbol {
127126
getName(): string { return this.name; }
128127
getDeclarations(): ts.Declaration[] { return [this.node]; }
129128
getDocumentationComment(): ts.SymbolDisplayPart[] { return []; }
130-
getJsDocTags(): ts.JSDocTagInfo[]{return []};
129+
// TODO(vicb): removed in TS 2.2
130+
getJsDocTags(): any[]{return []};
131131

132132
static of (name: string): MockSymbol { return new MockSymbol(name); }
133133
}

0 commit comments

Comments
 (0)