From 9560895ec21d597332b27540ea5a0932d857d24a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Jun 2015 18:18:37 -0700 Subject: [PATCH 1/4] Don't bother trying to semantically classify names that could never be typenames. --- src/compiler/binder.ts | 7 +++++++ src/compiler/program.ts | 16 ++++++++++++++++ src/compiler/types.ts | 3 +++ src/services/services.ts | 14 +++++++++----- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d411b840dc..f672a94844 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -90,10 +90,12 @@ module ts { let lastContainer: Node; let symbolCount = 0; let Symbol = objectAllocator.getSymbolConstructor(); + let typeNames: Map = {}; if (!file.locals) { bind(file); file.symbolCount = symbolCount; + file.typeNames = typeNames; } return; @@ -194,6 +196,11 @@ module ts { symbol = hasProperty(symbolTable, name) ? symbolTable[name] : (symbolTable[name] = createSymbol(SymbolFlags.None, name)); + + if (name && (includes & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.TypeAlias | SymbolFlags.Interface | SymbolFlags.TypeParameter | SymbolFlags.Module))) { + typeNames[name] = name; + } + if (symbol.flags & excludes) { if (node.name) { node.name.parent = node; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index c09b70a0c6..ceebb1db11 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -148,6 +148,7 @@ module ts { let commonSourceDirectory: string; let diagnosticsProducingTypeChecker: TypeChecker; let noDiagnosticsTypeChecker: TypeChecker; + let typeNames: Map; let start = new Date().getTime(); @@ -172,6 +173,7 @@ module ts { getDeclarationDiagnostics, getCompilerOptionsDiagnostics, getTypeChecker, + getTypeNames, getDiagnosticsProducingTypeChecker, getCommonSourceDirectory: () => commonSourceDirectory, emit, @@ -183,6 +185,20 @@ module ts { }; return program; + function getTypeNames() { + if (!typeNames) { + // Initialize a checker so that all our files are bound. + getTypeChecker(); + typeNames = {}; + + for (let sourceFile of files) { + copyMap(sourceFile.typeNames, typeNames); + } + } + + return typeNames; + } + function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost { return { getCanonicalFileName: fileName => host.getCanonicalFileName(fileName), diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 70962ea13c..3607c11d2b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -422,6 +422,7 @@ module ts { /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) /* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding) /* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) + /* @internal */ typeNames?: Map; } export interface NodeArray extends Array, TextRange { @@ -1223,6 +1224,8 @@ module ts { // language service). /* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker; + /* @internal */ getTypeNames(): Map; + /* @internal */ getNodeCount(): number; /* @internal */ getIdentifierCount(): number; /* @internal */ getSymbolCount(): number; diff --git a/src/services/services.ts b/src/services/services.ts index 5e8392fa9d..eda3a255e4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -5992,6 +5992,7 @@ module ts { let typeChecker = program.getTypeChecker(); let result: number[] = []; + let typeNames = program.getTypeNames(); processNode(sourceFile); return { spans: result, endOfLineState: EndOfLineState.None }; @@ -6048,11 +6049,14 @@ module ts { // Only walk into nodes that intersect the requested span. if (node && textSpanIntersectsWith(span, node.getFullStart(), node.getFullWidth())) { if (node.kind === SyntaxKind.Identifier && !nodeIsMissing(node)) { - let symbol = typeChecker.getSymbolAtLocation(node); - if (symbol) { - let type = classifySymbol(symbol, getMeaningFromLocation(node)); - if (type) { - pushClassification(node.getStart(), node.getWidth(), type); + let identifier = node; + if (typeNames[identifier.text]) { + let symbol = typeChecker.getSymbolAtLocation(node); + if (symbol) { + let type = classifySymbol(symbol, getMeaningFromLocation(node)); + if (type) { + pushClassification(node.getStart(), node.getWidth(), type); + } } } } From dbfdb96f3956419d0040399757a4d7e6cef6c299 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Jun 2015 18:24:34 -0700 Subject: [PATCH 2/4] Add explanatory comment --- src/services/services.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/services/services.ts b/src/services/services.ts index eda3a255e4..255b1f80e5 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6050,6 +6050,10 @@ module ts { if (node && textSpanIntersectsWith(span, node.getFullStart(), node.getFullWidth())) { if (node.kind === SyntaxKind.Identifier && !nodeIsMissing(node)) { let identifier = node; + + // Only bother calling into the typechecker if this is an identifier that + // could possibly resolve to a type name. This makes classification run + // in a third of the time it would normally take. if (typeNames[identifier.text]) { let symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { From 5b7ca78c92463a9162856c178f7a644e6e292a65 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 12 Jun 2015 12:53:24 -0700 Subject: [PATCH 3/4] PR feedback. --- src/compiler/binder.ts | 8 ++++---- src/compiler/program.ts | 14 +++++++------- src/compiler/types.ts | 10 ++++++++-- src/services/services.ts | 7 +++++-- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 61791390e1..e3cd0da60d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -90,12 +90,12 @@ namespace ts { let lastContainer: Node; let symbolCount = 0; let Symbol = objectAllocator.getSymbolConstructor(); - let typeNames: Map = {}; + let classifiableNames: Map = {}; if (!file.locals) { bind(file); file.symbolCount = symbolCount; - file.typeNames = typeNames; + file.classifiableNames = classifiableNames; } return; @@ -197,8 +197,8 @@ namespace ts { ? symbolTable[name] : (symbolTable[name] = createSymbol(SymbolFlags.None, name)); - if (name && (includes & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.TypeAlias | SymbolFlags.Interface | SymbolFlags.TypeParameter | SymbolFlags.Module))) { - typeNames[name] = name; + if (name && (includes & SymbolFlags.Classifiable)) { + classifiableNames[name] = name; } if (symbol.flags & excludes) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 469c5b4cde..8ab3e73858 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -148,7 +148,7 @@ namespace ts { let commonSourceDirectory: string; let diagnosticsProducingTypeChecker: TypeChecker; let noDiagnosticsTypeChecker: TypeChecker; - let typeNames: Map; + let classifiableNames: Map; let start = new Date().getTime(); @@ -173,7 +173,7 @@ namespace ts { getDeclarationDiagnostics, getCompilerOptionsDiagnostics, getTypeChecker, - getTypeNames, + getClassifiableNames, getDiagnosticsProducingTypeChecker, getCommonSourceDirectory: () => commonSourceDirectory, emit, @@ -185,18 +185,18 @@ namespace ts { }; return program; - function getTypeNames() { - if (!typeNames) { + function getClassifiableNames() { + if (!classifiableNames) { // Initialize a checker so that all our files are bound. getTypeChecker(); - typeNames = {}; + classifiableNames = {}; for (let sourceFile of files) { - copyMap(sourceFile.typeNames, typeNames); + copyMap(sourceFile.classifiableNames, classifiableNames); } } - return typeNames; + return classifiableNames; } function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f88e877e4c..8be7c1792a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -422,7 +422,6 @@ namespace ts { /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) /* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding) /* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) - /* @internal */ typeNames?: Map; } export interface NodeArray extends Array, TextRange { @@ -1173,6 +1172,8 @@ namespace ts { // Stores a line map for the file. // This field should never be used directly to obtain line map, use getLineMap function instead. /* @internal */ lineMap: number[]; + + /* @internal */ classifiableNames?: Map; } export interface ScriptReferenceHost { @@ -1224,7 +1225,7 @@ namespace ts { // language service). /* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker; - /* @internal */ getTypeNames(): Map; + /* @internal */ getClassifiableNames(): Map; /* @internal */ getNodeCount(): number; /* @internal */ getIdentifierCount(): number; @@ -1522,6 +1523,11 @@ namespace ts { PropertyOrAccessor = Property | Accessor, Export = ExportNamespace | ExportType | ExportValue, + + /* @internal */ + // The set of things we consider semantically classifiable. Used to speed up the LS during + // classification. + Classifiable = Class | Enum | TypeAlias | Interface | TypeParameter | Module, } export interface Symbol { diff --git a/src/services/services.ts b/src/services/services.ts index 1ae735ac20..f8fe0038af 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -5992,7 +5992,7 @@ namespace ts { let typeChecker = program.getTypeChecker(); let result: number[] = []; - let typeNames = program.getTypeNames(); + let classifiableNames = program.getClassifiableNames(); processNode(sourceFile); return { spans: result, endOfLineState: EndOfLineState.None }; @@ -6005,6 +6005,9 @@ namespace ts { function classifySymbol(symbol: Symbol, meaningAtPosition: SemanticMeaning): ClassificationType { let flags = symbol.getFlags(); + if ((flags & SymbolFlags.Classifiable) === 0) { + return; + } if (flags & SymbolFlags.Class) { return ClassificationType.className; @@ -6054,7 +6057,7 @@ namespace ts { // Only bother calling into the typechecker if this is an identifier that // could possibly resolve to a type name. This makes classification run // in a third of the time it would normally take. - if (typeNames[identifier.text]) { + if (classifiableNames[identifier.text]) { let symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { let type = classifySymbol(symbol, getMeaningFromLocation(node)); From 804b976c737b7dabc5fb8093abc7eae9d58ac538 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 12 Jun 2015 13:13:45 -0700 Subject: [PATCH 4/4] PR feedback. --- src/services/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index f8fe0038af..3939e62cfd 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6005,7 +6005,7 @@ namespace ts { function classifySymbol(symbol: Symbol, meaningAtPosition: SemanticMeaning): ClassificationType { let flags = symbol.getFlags(); - if ((flags & SymbolFlags.Classifiable) === 0) { + if ((flags & SymbolFlags.Classifiable) === SymbolFlags.None) { return; }