Skip to content

Commit c35f348

Browse files
Faster getClassifications
1 parent c233073 commit c35f348

File tree

4 files changed

+149
-52
lines changed

4 files changed

+149
-52
lines changed

src/harness/harnessLanguageService.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,12 @@ module Harness.LanguageService {
300300
getSemanticClassifications(fileName: string, span: ts.TextSpan): ts.ClassifiedSpan[] {
301301
return unwrapJSONCallResult(this.shim.getSemanticClassifications(fileName, span.start, span.length));
302302
}
303+
getSyntacticClassifications2(fileName: string, span: ts.TextSpan): number[] {
304+
return unwrapJSONCallResult(this.shim.getSyntacticClassifications2(fileName, span.start, span.length));
305+
}
306+
getSemanticClassifications2(fileName: string, span: ts.TextSpan): number[] {
307+
return unwrapJSONCallResult(this.shim.getSemanticClassifications2(fileName, span.start, span.length));
308+
}
303309
getCompletionsAtPosition(fileName: string, position: number): ts.CompletionInfo {
304310
return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position));
305311
}

src/server/client.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,14 @@ module ts.server {
533533
throw new Error("Not Implemented Yet.");
534534
}
535535

536+
getSyntacticClassifications2(fileName: string, span: TextSpan): number[] {
537+
throw new Error("Not Implemented Yet.");
538+
}
539+
540+
getSemanticClassifications2(fileName: string, span: TextSpan): number[] {
541+
throw new Error("Not Implemented Yet.");
542+
}
543+
536544
getProgram(): Program {
537545
throw new Error("SourceFile objects are not serializable through the server protocol.");
538546
}

src/services/services.ts

Lines changed: 113 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ module ts {
197197
let list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, NodeFlags.Synthetic, this);
198198
list._children = [];
199199
let pos = nodes.pos;
200+
201+
202+
200203
for (let node of nodes) {
201204
if (pos < node.pos) {
202205
pos = this.addSyntheticNodes(list._children, pos, node.pos);
@@ -972,6 +975,10 @@ module ts {
972975
getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
973976
getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
974977

978+
// Encoded as triples of [start, length, ClassificationType].
979+
getSyntacticClassifications2(fileName: string, span: TextSpan): number[];
980+
getSemanticClassifications2(fileName: string, span: TextSpan): number[];
981+
975982
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo;
976983
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails;
977984

@@ -1487,6 +1494,24 @@ module ts {
14871494
public static typeAlias = "type alias name";
14881495
}
14891496

1497+
export const enum ClassificationType {
1498+
comment = 1,
1499+
identifier = 2,
1500+
keyword = 3,
1501+
numericLiteral = 4,
1502+
operator = 5,
1503+
stringLiteral = 6,
1504+
whiteSpace = 7,
1505+
text = 8,
1506+
punctuation = 9,
1507+
className = 10,
1508+
enumName = 11,
1509+
interfaceName = 12,
1510+
moduleName = 13,
1511+
typeParameterName = 14,
1512+
typeAlias = 15,
1513+
}
1514+
14901515
/// Language Service
14911516

14921517
interface FormattingOptions {
@@ -5801,35 +5826,45 @@ module ts {
58015826
return NavigationBar.getNavigationBarItems(sourceFile);
58025827
}
58035828

5804-
function getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
5829+
function getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]{
5830+
return convertClassifications(getSemanticClassifications2(fileName, span));
5831+
}
5832+
5833+
function getSemanticClassifications2(fileName: string, span: TextSpan): number[] {
58055834
synchronizeHostData();
58065835

58075836
let sourceFile = getValidSourceFile(fileName);
58085837
let typeChecker = program.getTypeChecker();
58095838

5810-
let result: ClassifiedSpan[] = [];
5839+
let result: number[] = [];
58115840
processNode(sourceFile);
58125841

58135842
return result;
58145843

5815-
function classifySymbol(symbol: Symbol, meaningAtPosition: SemanticMeaning) {
5844+
function pushClassification(start: number, length: number, type: ClassificationType) {
5845+
result.push(start);
5846+
result.push(length);
5847+
result.push(type);
5848+
}
5849+
5850+
function classifySymbol(symbol: Symbol, meaningAtPosition: SemanticMeaning): ClassificationType {
58165851
let flags = symbol.getFlags();
58175852

58185853
if (flags & SymbolFlags.Class) {
5819-
return ClassificationTypeNames.className;
5854+
return ClassificationType.className;
58205855
}
58215856
else if (flags & SymbolFlags.Enum) {
5822-
return ClassificationTypeNames.enumName;
5857+
return ClassificationType.enumName;
58235858
}
58245859
else if (flags & SymbolFlags.TypeAlias) {
5825-
return ClassificationTypeNames.typeAlias;
5860+
return ClassificationType.typeAlias;
58265861
}
58275862
else if (meaningAtPosition & SemanticMeaning.Type) {
58285863
if (flags & SymbolFlags.Interface) {
5829-
return ClassificationTypeNames.interfaceName;
5864+
return ClassificationType.interfaceName;
58305865
}
58315866
else if (flags & SymbolFlags.TypeParameter) {
5832-
return ClassificationTypeNames.typeParameterName;
5867+
return ClassificationType.typeParameterName;
58335868
}
58345869
}
58355870
else if (flags & SymbolFlags.Module) {
@@ -5838,7 +5873,7 @@ module ts {
58385873
// - There exists a module declaration which actually impacts the value side.
58395874
if (meaningAtPosition & SemanticMeaning.Namespace ||
58405875
(meaningAtPosition & SemanticMeaning.Value && hasValueSideModule(symbol))) {
5841-
return ClassificationTypeNames.moduleName;
5876+
return ClassificationType.moduleName;
58425877
}
58435878
}
58445879

@@ -5862,10 +5897,7 @@ module ts {
58625897
if (symbol) {
58635898
let type = classifySymbol(symbol, getMeaningFromLocation(node));
58645899
if (type) {
5865-
result.push({
5866-
textSpan: createTextSpan(node.getStart(), node.getWidth()),
5867-
classificationType: type
5868-
});
5900+
pushClassification(node.getStart(), node.getWidth(), type);
58695901
}
58705902
}
58715903
}
@@ -5875,19 +5907,62 @@ module ts {
58755907
}
58765908
}
58775909

5878-
function getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
5910+
function getClassificationTypeName(type: ClassificationType) {
5911+
switch (type) {
5912+
case ClassificationType.comment: return ClassificationTypeNames.comment;
5913+
case ClassificationType.identifier: return ClassificationTypeNames.identifier;
5914+
case ClassificationType.keyword: return ClassificationTypeNames.keyword;
5915+
case ClassificationType.numericLiteral: return ClassificationTypeNames.numericLiteral;
5916+
case ClassificationType.operator: return ClassificationTypeNames.operator;
5917+
case ClassificationType.stringLiteral: return ClassificationTypeNames.stringLiteral;
5918+
case ClassificationType.whiteSpace: return ClassificationTypeNames.whiteSpace;
5919+
case ClassificationType.text: return ClassificationTypeNames.text;
5920+
case ClassificationType.punctuation: return ClassificationTypeNames.punctuation;
5921+
case ClassificationType.className: return ClassificationTypeNames.className;
5922+
case ClassificationType.enumName: return ClassificationTypeNames.enumName;
5923+
case ClassificationType.interfaceName: return ClassificationTypeNames.interfaceName;
5924+
case ClassificationType.moduleName: return ClassificationTypeNames.moduleName;
5925+
case ClassificationType.typeParameterName: return ClassificationTypeNames.typeParameterName;
5926+
case ClassificationType.typeAlias: return ClassificationTypeNames.typeAlias;
5927+
}
5928+
}
5929+
5930+
function convertClassifications(dense: number[]): ClassifiedSpan[] {
5931+
Debug.assert(dense.length % 3 === 0);
5932+
let result: ClassifiedSpan[] = [];
5933+
for (let i = 0, n = dense.length; i < n; i += 3) {
5934+
result.push({
5935+
textSpan: createTextSpan(dense[i], dense[i + 1]),
5936+
classificationType: getClassificationTypeName(dense[i + 2])
5937+
});
5938+
}
5939+
5940+
return result;
5941+
}
5942+
5943+
function getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]{
5944+
return convertClassifications(getSyntacticClassifications2(fileName, span));
5945+
}
5946+
5947+
function getSyntacticClassifications2(fileName: string, span: TextSpan): number[] {
58795948
// doesn't use compiler - no need to synchronize with host
58805949
let sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
58815950

58825951
// Make a scanner we can get trivia from.
58835952
let triviaScanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ false, sourceFile.text);
58845953
let mergeConflictScanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ false, sourceFile.text);
58855954

5886-
let result: ClassifiedSpan[] = [];
5955+
let result: number[] = [];
58875956
processElement(sourceFile);
58885957

58895958
return result;
58905959

5960+
function pushClassification(start: number, length: number, type: ClassificationType) {
5961+
result.push(start);
5962+
result.push(length);
5963+
result.push(type);
5964+
}
5965+
58915966
function classifyLeadingTrivia(token: Node): void {
58925967
let tokenStart = skipTrivia(sourceFile.text, token.pos, /*stopAfterLineBreak:*/ false);
58935968
if (tokenStart === token.pos) {
@@ -5909,10 +5984,7 @@ module ts {
59095984

59105985
if (isComment(kind)) {
59115986
// Simple comment. Just add as is.
5912-
result.push({
5913-
textSpan: createTextSpan(start, width),
5914-
classificationType: ClassificationTypeNames.comment
5915-
})
5987+
pushClassification(start, width, ClassificationType.comment);
59165988
continue;
59175989
}
59185990

@@ -5923,10 +5995,7 @@ module ts {
59235995
// for the <<<<<<< and >>>>>>> markers, we just add them in as comments
59245996
// in the classification stream.
59255997
if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan) {
5926-
result.push({
5927-
textSpan: createTextSpan(start, width),
5928-
classificationType: ClassificationTypeNames.comment
5929-
});
5998+
pushClassification(start, width, ClassificationType.comment);
59305999
continue;
59316000
}
59326001

@@ -5947,11 +6016,7 @@ module ts {
59476016
break;
59486017
}
59496018
}
5950-
result.push({
5951-
textSpan: createTextSpanFromBounds(start, i),
5952-
classificationType: ClassificationTypeNames.comment
5953-
});
5954-
6019+
pushClassification(start, i - start, ClassificationType.comment);
59556020
mergeConflictScanner.setTextPos(i);
59566021

59576022
while (mergeConflictScanner.getTextPos() < end) {
@@ -5966,10 +6031,7 @@ module ts {
59666031

59676032
let type = classifyTokenType(tokenKind);
59686033
if (type) {
5969-
result.push({
5970-
textSpan: createTextSpanFromBounds(start, end),
5971-
classificationType: type
5972-
});
6034+
pushClassification(start, end - start, type);
59736035
}
59746036
}
59756037

@@ -5979,20 +6041,17 @@ module ts {
59796041
if (token.getWidth() > 0) {
59806042
let type = classifyTokenType(token.kind, token);
59816043
if (type) {
5982-
result.push({
5983-
textSpan: createTextSpan(token.getStart(), token.getWidth()),
5984-
classificationType: type
5985-
});
6044+
pushClassification(token.getStart(), token.getWidth(), type);
59866045
}
59876046
}
59886047
}
59896048

59906049
// for accurate classification, the actual token should be passed in. however, for
59916050
// cases like 'disabled merge code' classification, we just get the token kind and
59926051
// classify based on that instead.
5993-
function classifyTokenType(tokenKind: SyntaxKind, token?: Node): string {
6052+
function classifyTokenType(tokenKind: SyntaxKind, token?: Node): ClassificationType {
59946053
if (isKeyword(tokenKind)) {
5995-
return ClassificationTypeNames.keyword;
6054+
return ClassificationType.keyword;
59966055
}
59976056

59986057
// Special case < and > If they appear in a generic context they are punctuation,
@@ -6001,7 +6060,7 @@ module ts {
60016060
// If the node owning the token has a type argument list or type parameter list, then
60026061
// we can effectively assume that a '<' and '>' belong to those lists.
60036062
if (token && getTypeArgumentOrTypeParameterList(token.parent)) {
6004-
return ClassificationTypeNames.punctuation;
6063+
return ClassificationType.punctuation;
60056064
}
60066065
}
60076066

@@ -6012,66 +6071,66 @@ module ts {
60126071
if (token.parent.kind === SyntaxKind.VariableDeclaration ||
60136072
token.parent.kind === SyntaxKind.PropertyDeclaration ||
60146073
token.parent.kind === SyntaxKind.Parameter) {
6015-
return ClassificationTypeNames.operator;
6074+
return ClassificationType.operator;
60166075
}
60176076
}
60186077

60196078
if (token.parent.kind === SyntaxKind.BinaryExpression ||
60206079
token.parent.kind === SyntaxKind.PrefixUnaryExpression ||
60216080
token.parent.kind === SyntaxKind.PostfixUnaryExpression ||
60226081
token.parent.kind === SyntaxKind.ConditionalExpression) {
6023-
return ClassificationTypeNames.operator;
6082+
return ClassificationType.operator;
60246083
}
60256084
}
60266085

6027-
return ClassificationTypeNames.punctuation;
6086+
return ClassificationType.punctuation;
60286087
}
60296088
else if (tokenKind === SyntaxKind.NumericLiteral) {
6030-
return ClassificationTypeNames.numericLiteral;
6089+
return ClassificationType.numericLiteral;
60316090
}
60326091
else if (tokenKind === SyntaxKind.StringLiteral) {
6033-
return ClassificationTypeNames.stringLiteral;
6092+
return ClassificationType.stringLiteral;
60346093
}
60356094
else if (tokenKind === SyntaxKind.RegularExpressionLiteral) {
60366095
// TODO: we should get another classification type for these literals.
6037-
return ClassificationTypeNames.stringLiteral;
6096+
return ClassificationType.stringLiteral;
60386097
}
60396098
else if (isTemplateLiteralKind(tokenKind)) {
60406099
// TODO (drosen): we should *also* get another classification type for these literals.
6041-
return ClassificationTypeNames.stringLiteral;
6100+
return ClassificationType.stringLiteral;
60426101
}
60436102
else if (tokenKind === SyntaxKind.Identifier) {
60446103
if (token) {
60456104
switch (token.parent.kind) {
60466105
case SyntaxKind.ClassDeclaration:
60476106
if ((<ClassDeclaration>token.parent).name === token) {
6048-
return ClassificationTypeNames.className;
6107+
return ClassificationType.className;
60496108
}
60506109
return;
60516110
case SyntaxKind.TypeParameter:
60526111
if ((<TypeParameterDeclaration>token.parent).name === token) {
6053-
return ClassificationTypeNames.typeParameterName;
6112+
return ClassificationType.typeParameterName;
60546113
}
60556114
return;
60566115
case SyntaxKind.InterfaceDeclaration:
60576116
if ((<InterfaceDeclaration>token.parent).name === token) {
6058-
return ClassificationTypeNames.interfaceName;
6117+
return ClassificationType.interfaceName;
60596118
}
60606119
return;
60616120
case SyntaxKind.EnumDeclaration:
60626121
if ((<EnumDeclaration>token.parent).name === token) {
6063-
return ClassificationTypeNames.enumName;
6122+
return ClassificationType.enumName;
60646123
}
60656124
return;
60666125
case SyntaxKind.ModuleDeclaration:
60676126
if ((<ModuleDeclaration>token.parent).name === token) {
6068-
return ClassificationTypeNames.moduleName;
6127+
return ClassificationType.moduleName;
60696128
}
60706129
return;
60716130
}
60726131
}
60736132

6074-
return ClassificationTypeNames.text;
6133+
return ClassificationType.text;
60756134
}
60766135
}
60776136

@@ -6402,6 +6461,8 @@ module ts {
64026461
getCompilerOptionsDiagnostics,
64036462
getSyntacticClassifications,
64046463
getSemanticClassifications,
6464+
getSyntacticClassifications2,
6465+
getSemanticClassifications2,
64056466
getCompletionsAtPosition,
64066467
getCompletionEntryDetails,
64076468
getSignatureHelpItems,

0 commit comments

Comments
 (0)