Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,12 @@ namespace ts {
let lastContainer: Node;
let symbolCount = 0;
let Symbol = objectAllocator.getSymbolConstructor();
let classifiableNames: Map<string> = {};

if (!file.locals) {
bind(file);
file.symbolCount = symbolCount;
file.classifiableNames = classifiableNames;
}

return;
Expand Down Expand Up @@ -194,6 +196,11 @@ namespace ts {
symbol = hasProperty(symbolTable, name)
? symbolTable[name]
: (symbolTable[name] = createSymbol(SymbolFlags.None, name));

if (name && (includes & SymbolFlags.Classifiable)) {
classifiableNames[name] = name;
}

if (symbol.flags & excludes) {
if (node.name) {
node.name.parent = node;
Expand Down
16 changes: 16 additions & 0 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ namespace ts {
let commonSourceDirectory: string;
let diagnosticsProducingTypeChecker: TypeChecker;
let noDiagnosticsTypeChecker: TypeChecker;
let classifiableNames: Map<string>;

let start = new Date().getTime();

Expand All @@ -172,6 +173,7 @@ namespace ts {
getDeclarationDiagnostics,
getCompilerOptionsDiagnostics,
getTypeChecker,
getClassifiableNames,
getDiagnosticsProducingTypeChecker,
getCommonSourceDirectory: () => commonSourceDirectory,
emit,
Expand All @@ -183,6 +185,20 @@ namespace ts {
};
return program;

function getClassifiableNames() {
if (!classifiableNames) {
// Initialize a checker so that all our files are bound.
getTypeChecker();
classifiableNames = {};

for (let sourceFile of files) {
copyMap(sourceFile.classifiableNames, classifiableNames);
}
}

return classifiableNames;
}

function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
return {
getCanonicalFileName: fileName => host.getCanonicalFileName(fileName),
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1172,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<string>;
}

export interface ScriptReferenceHost {
Expand Down Expand Up @@ -1223,6 +1225,8 @@ namespace ts {
// language service).
/* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker;

/* @internal */ getClassifiableNames(): Map<string>;

/* @internal */ getNodeCount(): number;
/* @internal */ getIdentifierCount(): number;
/* @internal */ getSymbolCount(): number;
Expand Down Expand Up @@ -1519,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 {
Expand Down
21 changes: 16 additions & 5 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5992,6 +5992,7 @@ namespace ts {
let typeChecker = program.getTypeChecker();

let result: number[] = [];
let classifiableNames = program.getClassifiableNames();
processNode(sourceFile);

return { spans: result, endOfLineState: EndOfLineState.None };
Expand All @@ -6004,6 +6005,9 @@ namespace ts {

function classifySymbol(symbol: Symbol, meaningAtPosition: SemanticMeaning): ClassificationType {
let flags = symbol.getFlags();
if ((flags & SymbolFlags.Classifiable) === SymbolFlags.None) {
return;
}

if (flags & SymbolFlags.Class) {
return ClassificationType.className;
Expand Down Expand Up @@ -6048,11 +6052,18 @@ namespace 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 = <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 (classifiableNames[identifier.text]) {
let symbol = typeChecker.getSymbolAtLocation(node);
if (symbol) {
let type = classifySymbol(symbol, getMeaningFromLocation(node));
if (type) {
pushClassification(node.getStart(), node.getWidth(), type);
}
}
}
}
Expand Down