From adc6c9f979b7dc5f95741ada023750356fcffd6a Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Mon, 1 Jun 2020 15:58:08 -0400 Subject: [PATCH 01/14] Initial import of the vscode semantic highlight code --- src/harness/client.ts | 2 +- src/harness/fourslashImpl.ts | 10 +- src/harness/fourslashInterfaceImpl.ts | 102 ++++++-- src/harness/harnessLanguageService.ts | 9 +- src/services/classifierVscode.ts | 246 ++++++++++++++++++ src/services/services.ts | 20 +- src/services/shims.ts | 4 +- src/services/tsconfig.json | 1 + src/services/types.ts | 21 +- tests/cases/fourslash/fourslash.ts | 64 ++--- .../fourslash/semanticClassification1.ts | 23 +- .../fourslash/semanticClassification2.ts | 2 +- .../fourslash/semanticClassificationAlias.ts | 2 +- .../semanticClassificationClassExpression.ts | 4 +- ...nticClassificationInTemplateExpressions.ts | 2 +- ...stantiatedModuleWithVariableOfSameName1.ts | 2 +- ...stantiatedModuleWithVariableOfSameName2.ts | 2 +- .../fourslash/semanticClassificationJs.ts | 2 +- .../semanticClassificationModules.ts | 4 +- ...stantiatedModuleWithVariableOfSameName1.ts | 4 +- ...stantiatedModuleWithVariableOfSameName2.ts | 2 +- .../semanticClassificationWithUnionTypes.ts | 4 +- .../semanticClassificationsCancellation1.ts | 4 +- .../semanticClassificatonTypeAlias.ts | 4 +- .../shims-pp/getSemanticClassifications.ts | 2 +- .../shims/getSemanticClassifications.ts | 2 +- 26 files changed, 443 insertions(+), 101 deletions(-) create mode 100644 src/services/classifierVscode.ts diff --git a/src/harness/client.ts b/src/harness/client.ts index 32b9b7f4e16e3..8a18c3ce25ebf 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -747,7 +747,7 @@ namespace ts.server { return notImplemented(); } - getEncodedSemanticClassifications(_fileName: string, _span: TextSpan): Classifications { + getEncodedSemanticClassifications(_fileName: string, _span: TextSpan, _format?: "original" | "2020"): Classifications { return notImplemented(); } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 08ed8709fb0e9..7b5a32b7daf20 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2448,7 +2448,9 @@ namespace FourSlash { Harness.IO.log(this.spanInfoToString(this.getNameOrDottedNameSpan(pos)!, "**")); } - private verifyClassifications(expected: { classificationType: string; text: string; textSpan?: TextSpan }[], actual: ts.ClassifiedSpan[], sourceFileText: string) { + private verifyClassifications(expected: { classificationType: string | number, text?: string; textSpan?: TextSpan }[], actual: ts.ClassifiedSpan[], sourceFileText: string) { + console.log("expected:", expected); + console.log("actual:", actual); if (actual.length !== expected.length) { this.raiseError("verifyClassifications failed - expected total classifications to be " + expected.length + ", but was " + actual.length + @@ -2511,9 +2513,9 @@ namespace FourSlash { } } - public verifySemanticClassifications(expected: { classificationType: string; text: string }[]) { + public verifySemanticClassifications(format: "original" | "2020", expected: { classificationType: string | number; text?: string }[]) { const actual = this.languageService.getSemanticClassifications(this.activeFile.fileName, - ts.createTextSpan(0, this.activeFile.content.length)); + ts.createTextSpan(0, this.activeFile.content.length), format); this.verifyClassifications(expected, actual, this.activeFile.content); } @@ -3766,7 +3768,7 @@ namespace FourSlash { const cancellation = new FourSlashInterface.Cancellation(state); // eslint-disable-next-line no-eval const f = eval(wrappedCode); - f(test, goTo, plugins, verify, edit, debug, format, cancellation, FourSlashInterface.Classification, FourSlashInterface.Completion, verifyOperationIsCancelled); + f(test, goTo, plugins, verify, edit, debug, format, cancellation, FourSlashInterface.classification, FourSlashInterface.Completion, verifyOperationIsCancelled); } catch (err) { // ensure 'source-map-support' is triggered while we still have the handler attached by accessing `error.stack`. diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 876a5bc37b02b..2a42980b9822f 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -504,8 +504,8 @@ namespace FourSlashInterface { /** * This method *requires* an ordered stream of classifications for a file, and spans are highly recommended. */ - public semanticClassificationsAre(...classifications: Classification[]) { - this.state.verifySemanticClassifications(classifications); + public semanticClassificationsAre(format: "original" | "2020", ...classifications: Classification[]) { + this.state.verifySemanticClassifications(format, classifications); } public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, expectedRange?: FourSlash.Range, options?: ts.RenameInfoOptions) { @@ -749,100 +749,118 @@ namespace FourSlashInterface { } interface Classification { - classificationType: ts.ClassificationTypeNames; - text: string; + classificationType: ts.ClassificationTypeNames | number; + text?: string; textSpan?: FourSlash.TextSpan; } - export namespace Classification { - export function comment(text: string, position?: number): Classification { + + export function classification(format: "original" | "2020") { + function token(identifier: number, text: string, _position: number): Classification { + return { + classificationType: identifier, + // textSpan: { + // start: position, + // end: -1 + // }, + text + } + } + + if (format === "2020") { + return { + token + } + } + + function comment(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.comment, text, position); } - export function identifier(text: string, position?: number): Classification { + function identifier(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.identifier, text, position); } - export function keyword(text: string, position?: number): Classification { + function keyword(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.keyword, text, position); } - export function numericLiteral(text: string, position?: number): Classification { + function numericLiteral(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.numericLiteral, text, position); } - export function operator(text: string, position?: number): Classification { + function operator(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.operator, text, position); } - export function stringLiteral(text: string, position?: number): Classification { + function stringLiteral(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.stringLiteral, text, position); } - export function whiteSpace(text: string, position?: number): Classification { + function whiteSpace(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.whiteSpace, text, position); } - export function text(text: string, position?: number): Classification { + function text(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.text, text, position); } - export function punctuation(text: string, position?: number): Classification { + function punctuation(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.punctuation, text, position); } - export function docCommentTagName(text: string, position?: number): Classification { + function docCommentTagName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.docCommentTagName, text, position); } - export function className(text: string, position?: number): Classification { + function className(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.className, text, position); } - export function enumName(text: string, position?: number): Classification { + function enumName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.enumName, text, position); } - export function interfaceName(text: string, position?: number): Classification { + function interfaceName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.interfaceName, text, position); } - export function moduleName(text: string, position?: number): Classification { + function moduleName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.moduleName, text, position); } - export function typeParameterName(text: string, position?: number): Classification { + function typeParameterName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.typeParameterName, text, position); } - export function parameterName(text: string, position?: number): Classification { + function parameterName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.parameterName, text, position); } - export function typeAliasName(text: string, position?: number): Classification { + function typeAliasName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.typeAliasName, text, position); } - export function jsxOpenTagName(text: string, position?: number): Classification { + function jsxOpenTagName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.jsxOpenTagName, text, position); } - export function jsxCloseTagName(text: string, position?: number): Classification { + function jsxCloseTagName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.jsxCloseTagName, text, position); } - export function jsxSelfClosingTagName(text: string, position?: number): Classification { + function jsxSelfClosingTagName(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.jsxSelfClosingTagName, text, position); } - export function jsxAttribute(text: string, position?: number): Classification { + function jsxAttribute(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.jsxAttribute, text, position); } - export function jsxText(text: string, position?: number): Classification { + function jsxText(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.jsxText, text, position); } - export function jsxAttributeStringLiteralValue(text: string, position?: number): Classification { + function jsxAttributeStringLiteralValue(text: string, position?: number): Classification { return getClassification(ts.ClassificationTypeNames.jsxAttributeStringLiteralValue, text, position); } @@ -850,7 +868,35 @@ namespace FourSlashInterface { const textSpan = position === undefined ? undefined : { start: position, end: position + text.length }; return { classificationType, text, textSpan }; } + + return { + comment, + identifier, + keyword, + numericLiteral, + operator, + stringLiteral, + whiteSpace, + text, + punctuation, + docCommentTagName, + className, + enumName, + interfaceName, + moduleName, + typeParameterName, + parameterName, + typeAliasName, + jsxOpenTagName, + jsxCloseTagName, + jsxSelfClosingTagName, + jsxAttribute, + jsxText, + jsxAttributeStringLiteralValue, + getClassification + } } + export namespace Completion { export import SortText = ts.Completions.SortText; export import CompletionSource = ts.Completions.CompletionSource; diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 78acc177b8cc4..fce4847ef3f96 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -448,14 +448,15 @@ namespace Harness.LanguageService { getSyntacticClassifications(fileName: string, span: ts.TextSpan): ts.ClassifiedSpan[] { return unwrapJSONCallResult(this.shim.getSyntacticClassifications(fileName, span.start, span.length)); } - getSemanticClassifications(fileName: string, span: ts.TextSpan): ts.ClassifiedSpan[] { - return unwrapJSONCallResult(this.shim.getSemanticClassifications(fileName, span.start, span.length)); + getSemanticClassifications(fileName: string, span: ts.TextSpan, format?: "original" | "2020"): ts.ClassifiedSpan[] { + return unwrapJSONCallResult(this.shim.getSemanticClassifications(fileName, span.start, span.length, format)); } getEncodedSyntacticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications { return unwrapJSONCallResult(this.shim.getEncodedSyntacticClassifications(fileName, span.start, span.length)); } - getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications { - return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length)); + getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan, format?: "original" | "2020"): ts.Classifications { + const responseFormat = format || "original"; + return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length, responseFormat)); } getCompletionsAtPosition(fileName: string, position: number, preferences: ts.UserPreferences | undefined): ts.CompletionInfo { return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, preferences)); diff --git a/src/services/classifierVscode.ts b/src/services/classifierVscode.ts new file mode 100644 index 0000000000000..4660960f15e81 --- /dev/null +++ b/src/services/classifierVscode.ts @@ -0,0 +1,246 @@ +namespace ts.classifier.vscode { + + enum TokenType { + class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member, _ + } + + enum TokenModifier { + declaration, static, async, readonly, defaultLibrary, local, _ + } + + enum TokenEncodingConsts { + typeOffset = 8, + modifierMask = (1 << typeOffset) - 1 + } + + /** This is mainly used internally for testing */ + export function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[] { + const classifications = getEncodedSemanticClassifications(program, _cancellationToken, sourceFile, span) + + Debug.assert(classifications.spans.length % 3 === 0); + const dense = classifications.spans; + const result: ClassifiedSpan[] = []; + for (let i = 0; i < dense.length; i += 3) { + result.push({ + textSpan: createTextSpan(dense[i], dense[i + 1]), + classificationType: dense[i + 2] as any // getClassificationTypeName(dense[i + 2]) + }); + } + + return result; + } + + export function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications { + return { + spans: getSemanticTokens(program, sourceFile, span), + endOfLineState: ts.EndOfLineState.None + } + } + + function getSemanticTokens(program: Program, sourceFile:SourceFile, span: TextSpan): number[] { + let resultTokens: number[] = []; + + const collector = (node: Node, typeIdx: number, modifierSet: number) => { + resultTokens.push(node.getStart(), node.getWidth(), ((typeIdx + 1) << TokenEncodingConsts.typeOffset) + modifierSet); + }; + + if (program && sourceFile) { + collectTokens(program, sourceFile, span, collector); + } + return resultTokens; + } + + function collectTokens(program: Program, sourceFile: SourceFile, span: TextSpan, collector: (node: Node, tokenType: number, tokenModifier: number) => void) { + const typeChecker = program.getTypeChecker(); + + let inJSXElement = false; + + function visit(node: Node) { + if (!node || !textSpanIntersectsWith(span, node.pos, node.getFullWidth()) || node.getFullWidth() === 0) { + return; + } + const prevInJSXElement = inJSXElement; + if (isJsxElement(node) || isJsxSelfClosingElement(node)) { + inJSXElement = true; + } + if (isJsxExpression(node)) { + inJSXElement = false; + } + + if (isIdentifier(node) && !inJSXElement && !inImportClause(node)) { + let symbol = typeChecker.getSymbolAtLocation(node); + if (symbol) { + if (symbol.flags & SymbolFlags.Alias) { + symbol = typeChecker.getAliasedSymbol(symbol); + } + let typeIdx = classifySymbol(symbol, getMeaningFromLocation(node)); + if (typeIdx !== undefined) { + let modifierSet = 0; + if (node.parent) { + const parentIsDeclaration = (isBindingElement(node.parent) || tokenFromDeclarationMapping[node.parent.kind] === typeIdx); + if (parentIsDeclaration && (node.parent).name === node) { + modifierSet = 1 << TokenModifier.declaration; + } + } + + // property declaration in constructor + if (typeIdx === TokenType.parameter && isRightSideOfQualifiedNameOrPropertyAccess(node)) { + typeIdx = TokenType.property; + } + + typeIdx = reclassifyByType(typeChecker, node, typeIdx); + + const decl = symbol.valueDeclaration; + if (decl) { + const modifiers = getCombinedModifierFlags(decl); + const nodeFlags = getCombinedNodeFlags(decl); + if (modifiers & ModifierFlags.Static) { + modifierSet |= 1 << TokenModifier.static; + } + if (modifiers & ModifierFlags.Async) { + modifierSet |= 1 << TokenModifier.async; + } + if (typeIdx !== TokenType.class && typeIdx !== TokenType.interface) { + if ((modifiers & ModifierFlags.Readonly) || (nodeFlags & NodeFlags.Const) || (symbol.getFlags() & SymbolFlags.EnumMember)) { + modifierSet |= 1 << TokenModifier.readonly; + } + } + if ((typeIdx === TokenType.variable || typeIdx === TokenType.function) && isLocalDeclaration(decl, sourceFile)) { + modifierSet |= 1 << TokenModifier.local; + } + if (program.isSourceFileDefaultLibrary(decl.getSourceFile())) { + modifierSet |= 1 << TokenModifier.defaultLibrary; + } + } else if (symbol.declarations && symbol.declarations.some(d => program.isSourceFileDefaultLibrary(d.getSourceFile()))) { + modifierSet |= 1 << TokenModifier.defaultLibrary; + } + + collector(node, typeIdx, modifierSet); + + } + } + } + forEachChild(node, visit); + + inJSXElement = prevInJSXElement; + } + visit(sourceFile); + } + + function classifySymbol(symbol: Symbol, meaning: SemanticMeaning): TokenType | undefined { + const flags = symbol.getFlags(); + if (flags & SymbolFlags.Class) { + return TokenType.class; + } else if (flags & SymbolFlags.Enum) { + return TokenType.enum; + } else if (flags & SymbolFlags.TypeAlias) { + return TokenType.type; + } else if (flags & SymbolFlags.Interface) { + if (meaning & SemanticMeaning.Type) { + return TokenType.interface; + } + } else if (flags & SymbolFlags.TypeParameter) { + return TokenType.typeParameter; + } + let decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; + if (decl && isBindingElement(decl)) { + decl = getDeclarationForBindingElement(decl); + } + return decl && tokenFromDeclarationMapping[decl.kind]; + } + + function reclassifyByType(typeChecker: TypeChecker, node: Node, typeIdx: TokenType): TokenType { + // type based classifications + if (typeIdx === TokenType.variable || typeIdx === TokenType.property || typeIdx === TokenType.parameter) { + const type = typeChecker.getTypeAtLocation(node); + if (type) { + const test = (condition: (type: Type) => boolean) => { + return condition(type) || type.isUnion() && type.types.some(condition); + } + if (typeIdx !== TokenType.parameter && test(t => t.getConstructSignatures().length > 0)) { + return TokenType.class; + } + if (test(t => t.getCallSignatures().length > 0) && !test(t => t.getProperties().length > 0) || isExpressionInCallExpression(node)) { + return typeIdx === TokenType.property ? TokenType.member : TokenType.function; + } + } + } + return typeIdx; + } + + function isLocalDeclaration(decl: Declaration, sourceFile: SourceFile): boolean { + if (isBindingElement(decl)) { + decl = getDeclarationForBindingElement(decl); + } + if (isVariableDeclaration(decl)) { + return (!isSourceFile(decl.parent.parent.parent) || isCatchClause(decl.parent)) && decl.getSourceFile() === sourceFile; + } else if (isFunctionDeclaration(decl)) { + return !isSourceFile(decl.parent) && decl.getSourceFile() === sourceFile; + } + return false; + } + + function getDeclarationForBindingElement(element: BindingElement): VariableDeclaration | ParameterDeclaration { + while (true) { + if (isBindingElement(element.parent.parent)) { + element = element.parent.parent; + } else { + return element.parent.parent; + } + } + } + + function inImportClause(node: Node): boolean { + const parent = node.parent; + return parent && (isImportClause(parent) || isImportSpecifier(parent) || isNamespaceImport(parent)); + } + + function isExpressionInCallExpression(node: Node): boolean { + while (isRightSideOfQualifiedNameOrPropertyAccess(node)) { + node = node.parent + } + return isCallExpression(node.parent) && node.parent.expression === node; + } + + function isRightSideOfQualifiedNameOrPropertyAccess(node: Node): boolean { + return (isQualifiedName(node.parent) && node.parent.right === node) || (isPropertyAccessExpression(node.parent) && node.parent.name === node); + } + + const enum SemanticMeaning { + None = 0x0, + Value = 0x1, + Type = 0x2, + Namespace = 0x4, + All = Value | Type | Namespace + } + + function getMeaningFromLocation(node: Node): SemanticMeaning { + const f = (ts).getMeaningFromLocation; + if (typeof f === 'function') { + return f(node); + } + return SemanticMeaning.All; + } + + const tokenFromDeclarationMapping: { [name: string]: TokenType } = { + [SyntaxKind.VariableDeclaration]: TokenType.variable, + [SyntaxKind.Parameter]: TokenType.parameter, + [SyntaxKind.PropertyDeclaration]: TokenType.property, + [SyntaxKind.ModuleDeclaration]: TokenType.namespace, + [SyntaxKind.EnumDeclaration]: TokenType.enum, + [SyntaxKind.EnumMember]: TokenType.enumMember, + [SyntaxKind.ClassDeclaration]: TokenType.class, + [SyntaxKind.MethodDeclaration]: TokenType.member, + [SyntaxKind.FunctionDeclaration]: TokenType.function, + [SyntaxKind.FunctionExpression]: TokenType.function, + [SyntaxKind.MethodSignature]: TokenType.member, + [SyntaxKind.GetAccessor]: TokenType.property, + [SyntaxKind.SetAccessor]: TokenType.property, + [SyntaxKind.PropertySignature]: TokenType.property, + [SyntaxKind.InterfaceDeclaration]: TokenType.interface, + [SyntaxKind.TypeAliasDeclaration]: TokenType.type, + [SyntaxKind.TypeParameter]: TokenType.typeParameter, + [SyntaxKind.PropertyAssignment]: TokenType.property, + [SyntaxKind.ShorthandPropertyAssignment]: TokenType.property + }; +} diff --git a/src/services/services.ts b/src/services/services.ts index 75c9bfdba71df..551578ff58454 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1797,22 +1797,34 @@ namespace ts { return kind === ScriptKind.TS || kind === ScriptKind.TSX; } - function getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] { + function getSemanticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): ClassifiedSpan[] { if (!isTsOrTsxFile(fileName)) { // do not run semantic classification on non-ts-or-tsx files return []; } synchronizeHostData(); - return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); + + const responseFormat = format || "original" + if (responseFormat === "original") { + return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); + } else { + return ts.classifier.vscode.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); + } } - function getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications { + function getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): Classifications { if (!isTsOrTsxFile(fileName)) { // do not run semantic classification on non-ts-or-tsx files return { spans: [], endOfLineState: EndOfLineState.None }; } synchronizeHostData(); - return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); + + const responseFormat = format || "original" + if (responseFormat === "original") { + return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); + } else { + return ts.classifier.vscode.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); + } } function getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] { diff --git a/src/services/shims.ts b/src/services/shims.ts index 4c60639bdab88..94708a03c2806 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -145,9 +145,9 @@ namespace ts { getCompilerOptionsDiagnostics(): string; getSyntacticClassifications(fileName: string, start: number, length: number): string; - getSemanticClassifications(fileName: string, start: number, length: number): string; + getSemanticClassifications(fileName: string, start: number, length: number, format?: "original" | "2020"): string; getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string; - getEncodedSemanticClassifications(fileName: string, start: number, length: number): string; + getEncodedSemanticClassifications(fileName: string, start: number, length: number, format?: "original" | "2020"): string; getCompletionsAtPosition(fileName: string, position: number, preferences: UserPreferences | undefined): string; getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined): string; diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 9567185c7a9ac..fb62e2a953e81 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -12,6 +12,7 @@ "types.ts", "utilities.ts", "classifier.ts", + "classifierVscode.ts", "stringCompletions.ts", "completions.ts", "documentHighlights.ts", diff --git a/src/services/types.ts b/src/services/types.ts index bc1223272faf8..ed9578b4efdfb 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -375,14 +375,23 @@ namespace ts { getCompilerOptionsDiagnostics(): Diagnostic[]; /** @deprecated Use getEncodedSyntacticClassifications instead. */ - getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; + getSyntacticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): ClassifiedSpan[]; /** @deprecated Use getEncodedSemanticClassifications instead. */ - getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; - - // Encoded as triples of [start, length, ClassificationType]. + getSemanticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): ClassifiedSpan[]; + /** Encoded as triples of [start, length, ClassificationType]. */ getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; - getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; + + /** + * Gets semantic highlights information for a particular file. Has two formats, an older + * version used by VS and a format used by VS Code. + * + * @param fileName The path to the file + * @param position A text span to return results within + * @param format Which format to use, defaults to "original" + * @returns a number array encoded as triples of [start, length, ClassificationType, ...]. + */ + getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): Classifications; /** * Gets completion entries at a particular position in a file. @@ -589,7 +598,7 @@ namespace ts { export interface ClassifiedSpan { textSpan: TextSpan; - classificationType: ClassificationTypeNames; + classificationType: ClassificationTypeNames | number; } /** diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index a1f91482309c6..aca17470ebcf5 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -348,14 +348,14 @@ declare namespace FourSlashInterface { */ syntacticClassificationsAre(...classifications: { classificationType: string; - text: string; + text?: string; }[]): void; /** * This method *requires* an ordered stream of classifications for a file, and spans are highly recommended. */ - semanticClassificationsAre(...classifications: { - classificationType: string; - text: string; + semanticClassificationsAre(format: "original" | "2020", ...classifications: { + classificationType: string | number; + text?: string; textSpan?: TextSpan; }[]): void; renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, range?: Range, allowRenameOfImportPath?: boolean): void; @@ -454,118 +454,123 @@ declare namespace FourSlashInterface { resetCancelled(): void; setCancelled(numberOfCalls?: number): void; } - module classification { - function comment(text: string, position?: number): { + + interface ModernClassificationFactory { + token(identifier: number, name: string, position: number) + } + + interface ClassificationFactory { + comment(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function identifier(text: string, position?: number): { + identifier(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function keyword(text: string, position?: number): { + keyword(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function numericLiteral(text: string, position?: number): { + numericLiteral(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function operator(text: string, position?: number): { + operator(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function stringLiteral(text: string, position?: number): { + stringLiteral(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function whiteSpace(text: string, position?: number): { + whiteSpace(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function text(text: string, position?: number): { + text(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function punctuation(text: string, position?: number): { + punctuation(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function docCommentTagName(text: string, position?: number): { + docCommentTagName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function className(text: string, position?: number): { + className(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function enumName(text: string, position?: number): { + enumName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function interfaceName(text: string, position?: number): { + interfaceName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function moduleName(text: string, position?: number): { + moduleName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function typeParameterName(text: string, position?: number): { + typeParameterName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function parameterName(text: string, position?: number): { + parameterName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function typeAliasName(text: string, position?: number): { + typeAliasName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function jsxOpenTagName(text: string, position?: number): { + jsxOpenTagName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function jsxCloseTagName(text: string, position?: number): { + jsxCloseTagName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function jsxSelfClosingTagName(text: string, position?: number): { + jsxSelfClosingTagName(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function jsxAttribute(text: string, position?: number): { + jsxAttribute(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function jsxText(text: string, position?: number): { + jsxText(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; }; - function jsxAttributeStringLiteralValue(text: string, position?: number): { + jsxAttributeStringLiteralValue(text: string, position?: number): { classificationType: string; text: string; textSpan?: TextSpan; @@ -749,7 +754,8 @@ declare var edit: FourSlashInterface.edit; declare var debug: FourSlashInterface.debug; declare var format: FourSlashInterface.format; declare var cancellation: FourSlashInterface.cancellation; -declare var classification: typeof FourSlashInterface.classification; +declare function classification(format: "original"): FourSlashInterface.ClassificationFactory; +declare function classification(format: "2020"): FourSlashInterface.ModernClassificationFactory; declare namespace completion { type Entry = FourSlashInterface.ExpectedCompletionEntryObject; export const enum SortText { diff --git a/tests/cases/fourslash/semanticClassification1.ts b/tests/cases/fourslash/semanticClassification1.ts index 4eb7f2a32edab..9fa7a7e284c7a 100644 --- a/tests/cases/fourslash/semanticClassification1.ts +++ b/tests/cases/fourslash/semanticClassification1.ts @@ -6,10 +6,29 @@ //// } //// interface /*2*/X extends /*3*/M./*4*/I { } -var c = classification; -verify.semanticClassificationsAre( +var c = classification("original"); +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.interfaceName("X", test.marker("2").position), c.moduleName("M", test.marker("3").position), c.interfaceName("I", test.marker("4").position)); + + // // TODO: test like: + // // c2.token("module", "M", test.marker("0").position)) + +var c2 = classification("2020") +verify.semanticClassificationsAre("2020", + c2.token(1025, "M", test.marker("0").position), + c2.token(769, "I", test.marker("1").position), + c2.token(769, "X", test.marker("2").position), + c2.token(1024, "M", test.marker("3").position), + c2.token(768, "I", test.marker("4").position), +) +// { textSpan: { start: 7, end: 1 }, classificationType: 1025 }, +// { textSpan: { start: 32, end: 1 }, classificationType: 769 }, +// { textSpan: { start: 54, end: 1 }, classificationType: 769 }, +// { textSpan: { start: 64, end: 1 }, classificationType: 1024 }, +// { textSpan: { start: 66, end: 1 }, classificationType: 768 } ); + + diff --git a/tests/cases/fourslash/semanticClassification2.ts b/tests/cases/fourslash/semanticClassification2.ts index f7f6340e5bd6c..84cdb8fc766ed 100644 --- a/tests/cases/fourslash/semanticClassification2.ts +++ b/tests/cases/fourslash/semanticClassification2.ts @@ -8,4 +8,4 @@ //// Thing.toExponential(); var c = classification; -verify.semanticClassificationsAre(c.interfaceName("Thing", test.marker("0").position)); \ No newline at end of file +verify.semanticClassificationsAre("original", c.interfaceName("Thing", test.marker("0").position)); diff --git a/tests/cases/fourslash/semanticClassificationAlias.ts b/tests/cases/fourslash/semanticClassificationAlias.ts index 799674a211dae..7e34940cc00ca 100644 --- a/tests/cases/fourslash/semanticClassificationAlias.ts +++ b/tests/cases/fourslash/semanticClassificationAlias.ts @@ -12,7 +12,7 @@ goTo.file("/b.ts"); const [m0, m1, m2, m3] = test.markers(); const c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.typeAliasName("x", m0.position), c.className("y", m1.position), c.typeAliasName("x", m2.position), diff --git a/tests/cases/fourslash/semanticClassificationClassExpression.ts b/tests/cases/fourslash/semanticClassificationClassExpression.ts index f067265df2994..98da5a8418d3e 100644 --- a/tests/cases/fourslash/semanticClassificationClassExpression.ts +++ b/tests/cases/fourslash/semanticClassificationClassExpression.ts @@ -4,9 +4,9 @@ //// class /*1*/C {} //// class /*2*/D extends class /*3*/B{} { } var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.className("C", test.marker("0").position), c.className("C", test.marker("1").position), c.className("D", test.marker("2").position), c.className("B", test.marker("3").position) -); \ No newline at end of file +); diff --git a/tests/cases/fourslash/semanticClassificationInTemplateExpressions.ts b/tests/cases/fourslash/semanticClassificationInTemplateExpressions.ts index 7ac8bec28207f..15f822de0a82d 100644 --- a/tests/cases/fourslash/semanticClassificationInTemplateExpressions.ts +++ b/tests/cases/fourslash/semanticClassificationInTemplateExpressions.ts @@ -11,7 +11,7 @@ ////`abcd${ /*3*/M./*4*/C.x + /*5*/M./*6*/E.E1}efg` var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.className("C", test.marker("1").position), c.enumName("E", test.marker("2").position), diff --git a/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName1.ts b/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName1.ts index 2061c218eac7a..cfb43136fe859 100644 --- a/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName1.ts +++ b/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName1.ts @@ -16,7 +16,7 @@ ////var x = /*5*/M; var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.moduleName("M", test.marker("3").position), diff --git a/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName2.ts b/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName2.ts index f1f4fa8e67014..4b1745479a19a 100644 --- a/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName2.ts +++ b/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName2.ts @@ -19,7 +19,7 @@ ////var x = /*6*/M; var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.moduleName("M", test.marker("2").position), diff --git a/tests/cases/fourslash/semanticClassificationJs.ts b/tests/cases/fourslash/semanticClassificationJs.ts index 8105202a23fed..4232c962012f7 100644 --- a/tests/cases/fourslash/semanticClassificationJs.ts +++ b/tests/cases/fourslash/semanticClassificationJs.ts @@ -6,4 +6,4 @@ //// let x = 1; // no semantic classification in js file -verify.semanticClassificationsAre(); \ No newline at end of file +verify.semanticClassificationsAre("original", ); diff --git a/tests/cases/fourslash/semanticClassificationModules.ts b/tests/cases/fourslash/semanticClassificationModules.ts index 6992639e4c3a5..ba2798dfaf8fc 100644 --- a/tests/cases/fourslash/semanticClassificationModules.ts +++ b/tests/cases/fourslash/semanticClassificationModules.ts @@ -10,10 +10,10 @@ ////var y = /*5*/M; var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.moduleName("M", test.marker("2").position), c.interfaceName("I", test.marker("3").position), c.moduleName("M", test.marker("4").position), - c.moduleName("M", test.marker("5").position)); \ No newline at end of file + c.moduleName("M", test.marker("5").position)); diff --git a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts index 6c98c8340c38f..9b291b933d0cc 100644 --- a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts +++ b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts @@ -9,6 +9,6 @@ ////var M = { I: 10 }; var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), - c.interfaceName("I", test.marker("1").position)); \ No newline at end of file + c.interfaceName("I", test.marker("1").position)); diff --git a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts index 419692b98ce7d..2c88b59b9b40e 100644 --- a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts +++ b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts @@ -15,7 +15,7 @@ ////var x = /*5*/M; var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.moduleName("M", test.marker("3").position), diff --git a/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts b/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts index bc55c5d8253cc..4d90b6b46c520 100644 --- a/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts +++ b/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts @@ -12,7 +12,7 @@ ////var I: typeof M | typeof /*8*/C; var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.interfaceName("I", test.marker("2").position), @@ -21,4 +21,4 @@ verify.semanticClassificationsAre( c.interfaceName("I", test.marker("5").position), c.interfaceName("I", test.marker("6").position), c.className("C", test.marker("7").position), - c.className("C", test.marker("8").position)); \ No newline at end of file + c.className("C", test.marker("8").position)); diff --git a/tests/cases/fourslash/semanticClassificationsCancellation1.ts b/tests/cases/fourslash/semanticClassificationsCancellation1.ts index 9381bef5e86ce..95a63db68c74e 100644 --- a/tests/cases/fourslash/semanticClassificationsCancellation1.ts +++ b/tests/cases/fourslash/semanticClassificationsCancellation1.ts @@ -7,9 +7,9 @@ var c = classification; cancellation.setCancelled(1); -verifyOperationIsCancelled(() => verify.semanticClassificationsAre()); +verifyOperationIsCancelled(() => verify.semanticClassificationsAre("original", )); cancellation.resetCancelled(); -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M"), c.moduleName("N")); diff --git a/tests/cases/fourslash/semanticClassificatonTypeAlias.ts b/tests/cases/fourslash/semanticClassificatonTypeAlias.ts index 188a09afcbc76..d021cbe827687 100644 --- a/tests/cases/fourslash/semanticClassificatonTypeAlias.ts +++ b/tests/cases/fourslash/semanticClassificatonTypeAlias.ts @@ -6,10 +6,10 @@ ////function f(x: /*3*/Alias): /*4*/Alias { return undefined; } var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.typeAliasName("Alias", test.marker("0").position), c.typeAliasName("Alias", test.marker("1").position), c.typeAliasName("Alias", test.marker("2").position), c.typeAliasName("Alias", test.marker("3").position), c.typeAliasName("Alias", test.marker("4").position) - ); \ No newline at end of file + ); diff --git a/tests/cases/fourslash/shims-pp/getSemanticClassifications.ts b/tests/cases/fourslash/shims-pp/getSemanticClassifications.ts index 4eb7f2a32edab..106e7171cc6d4 100644 --- a/tests/cases/fourslash/shims-pp/getSemanticClassifications.ts +++ b/tests/cases/fourslash/shims-pp/getSemanticClassifications.ts @@ -7,7 +7,7 @@ //// interface /*2*/X extends /*3*/M./*4*/I { } var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.interfaceName("X", test.marker("2").position), diff --git a/tests/cases/fourslash/shims/getSemanticClassifications.ts b/tests/cases/fourslash/shims/getSemanticClassifications.ts index 4eb7f2a32edab..106e7171cc6d4 100644 --- a/tests/cases/fourslash/shims/getSemanticClassifications.ts +++ b/tests/cases/fourslash/shims/getSemanticClassifications.ts @@ -7,7 +7,7 @@ //// interface /*2*/X extends /*3*/M./*4*/I { } var c = classification; -verify.semanticClassificationsAre( +verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.interfaceName("X", test.marker("2").position), From fff50d6dd0260f354bff04b02387415cb8c44bc7 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Mon, 15 Jun 2020 16:42:22 -0400 Subject: [PATCH 02/14] Adds the ability to test modern semantic classification via strings instead of numbers --- src/compiler/types.ts | 5 + src/harness/client.ts | 2 +- src/harness/fourslashImpl.ts | 2 +- src/harness/fourslashInterfaceImpl.ts | 71 ++- src/harness/harnessLanguageService.ts | 6 +- src/services/classifierVscode.ts | 502 +++++++++--------- src/services/services.ts | 18 +- src/services/shims.ts | 4 +- src/services/types.ts | 10 +- tests/cases/fourslash/fourslash.ts | 2 +- .../fourslash/semanticClassification1.ts | 19 +- 11 files changed, 350 insertions(+), 291 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 05ad94c7599f1..ffb1d1ae00407 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7766,6 +7766,11 @@ namespace ts { newLength: number; } + export const enum SemanticClassificationFormat { + Original = "orginal", + TwentyTwenty = "2020" + } + /* @internal */ export interface DiagnosticCollection { // Adds a diagnostic to this diagnostic collection. diff --git a/src/harness/client.ts b/src/harness/client.ts index 8a18c3ce25ebf..b8d7d7ee48294 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -747,7 +747,7 @@ namespace ts.server { return notImplemented(); } - getEncodedSemanticClassifications(_fileName: string, _span: TextSpan, _format?: "original" | "2020"): Classifications { + getEncodedSemanticClassifications(_fileName: string, _span: TextSpan, _format?: SemanticClassificationFormat): Classifications { return notImplemented(); } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 7b5a32b7daf20..736f233017879 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2513,7 +2513,7 @@ namespace FourSlash { } } - public verifySemanticClassifications(format: "original" | "2020", expected: { classificationType: string | number; text?: string }[]) { + public verifySemanticClassifications(format: ts.SemanticClassificationFormat, expected: { classificationType: string | number; text?: string }[]) { const actual = this.languageService.getSemanticClassifications(this.activeFile.fileName, ts.createTextSpan(0, this.activeFile.content.length), format); diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 2a42980b9822f..55c552b1f42b2 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -504,7 +504,7 @@ namespace FourSlashInterface { /** * This method *requires* an ordered stream of classifications for a file, and spans are highly recommended. */ - public semanticClassificationsAre(format: "original" | "2020", ...classifications: Classification[]) { + public semanticClassificationsAre(format: ts.SemanticClassificationFormat, ...classifications: Classification[]) { this.state.verifySemanticClassifications(format, classifications); } @@ -754,22 +754,73 @@ namespace FourSlashInterface { textSpan?: FourSlash.TextSpan; } - export function classification(format: "original" | "2020") { - function token(identifier: number, text: string, _position: number): Classification { + + export function classification(format: ts.SemanticClassificationFormat) { + + function semanticToken(identifier: string, text: string, _position: number): Classification { + + const tokenTypes = { + class: ts.classifier.vscode.TokenType.class, + enum: ts.classifier.vscode.TokenType.enum, + interface: ts.classifier.vscode.TokenType.interface, + namespace: ts.classifier.vscode.TokenType.namespace, + typeParameter: ts.classifier.vscode.TokenType.typeParameter, + type: ts.classifier.vscode.TokenType.type, + parameter: ts.classifier.vscode.TokenType.parameter, + variable: ts.classifier.vscode.TokenType.variable, + enumMember: ts.classifier.vscode.TokenType.enumMember, + property: ts.classifier.vscode.TokenType.property, + function: ts.classifier.vscode.TokenType.function, + member: ts.classifier.vscode.TokenType.member + }; + + const tokenModifiers = { + async: ts.classifier.vscode.TokenModifier.async, + declaration: ts.classifier.vscode.TokenModifier.declaration, + readonly: ts.classifier.vscode.TokenModifier.readonly, + static: ts.classifier.vscode.TokenModifier.static, + local: ts.classifier.vscode.TokenModifier.local, + defaultLibrary: ts.classifier.vscode.TokenModifier.defaultLibrary, + }; + + function identifierToClassificationID(identifier: string): number { + const [tokenType, ...modifiers] = identifier.split("."); + // @ts-expect-error + const tokenValue = tokenTypes[tokenType]; + if (tokenValue === undefined) { + throw new Error(`Did not find ${tokenType} in tokenTypes for classifiers.`); + } + + let classification = (tokenValue + 1) << 8; + ts.forEach(modifiers, (modifier) => { + // @ts-expect-error + const modifierValue = tokenModifiers[modifiers]; + if (tokenValue === undefined) { + throw new Error(`Did not find ${modifier} in tokenModifiers for classifiers.`); + } + classification += modifierValue + 1; + console.log("adding: ", modifierValue); + }); + + // debugger; + + return classification; + } + return { - classificationType: identifier, - // textSpan: { + classificationType: identifierToClassificationID(identifier), + // textSpan: { // start: position, // end: -1 // }, text - } + }; } - if (format === "2020") { + if (format === ts.SemanticClassificationFormat.TwentyTwenty) { return { - token - } + semanticToken + }; } function comment(text: string, position?: number): Classification { @@ -894,7 +945,7 @@ namespace FourSlashInterface { jsxText, jsxAttributeStringLiteralValue, getClassification - } + }; } export namespace Completion { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index fce4847ef3f96..3f4cd6233ed34 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -448,14 +448,14 @@ namespace Harness.LanguageService { getSyntacticClassifications(fileName: string, span: ts.TextSpan): ts.ClassifiedSpan[] { return unwrapJSONCallResult(this.shim.getSyntacticClassifications(fileName, span.start, span.length)); } - getSemanticClassifications(fileName: string, span: ts.TextSpan, format?: "original" | "2020"): ts.ClassifiedSpan[] { + getSemanticClassifications(fileName: string, span: ts.TextSpan, format?: ts.SemanticClassificationFormat): ts.ClassifiedSpan[] { return unwrapJSONCallResult(this.shim.getSemanticClassifications(fileName, span.start, span.length, format)); } getEncodedSyntacticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications { return unwrapJSONCallResult(this.shim.getEncodedSyntacticClassifications(fileName, span.start, span.length)); } - getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan, format?: "original" | "2020"): ts.Classifications { - const responseFormat = format || "original"; + getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan, format?: ts.SemanticClassificationFormat): ts.Classifications { + const responseFormat = format || ts.SemanticClassificationFormat.Original; return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length, responseFormat)); } getCompletionsAtPosition(fileName: string, position: number, preferences: ts.UserPreferences | undefined): ts.CompletionInfo { diff --git a/src/services/classifierVscode.ts b/src/services/classifierVscode.ts index 4660960f15e81..0e972870e7e60 100644 --- a/src/services/classifierVscode.ts +++ b/src/services/classifierVscode.ts @@ -1,246 +1,256 @@ -namespace ts.classifier.vscode { - - enum TokenType { - class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member, _ - } - - enum TokenModifier { - declaration, static, async, readonly, defaultLibrary, local, _ - } - - enum TokenEncodingConsts { - typeOffset = 8, - modifierMask = (1 << typeOffset) - 1 - } - - /** This is mainly used internally for testing */ - export function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[] { - const classifications = getEncodedSemanticClassifications(program, _cancellationToken, sourceFile, span) - - Debug.assert(classifications.spans.length % 3 === 0); - const dense = classifications.spans; - const result: ClassifiedSpan[] = []; - for (let i = 0; i < dense.length; i += 3) { - result.push({ - textSpan: createTextSpan(dense[i], dense[i + 1]), - classificationType: dense[i + 2] as any // getClassificationTypeName(dense[i + 2]) - }); - } - - return result; - } - - export function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications { - return { - spans: getSemanticTokens(program, sourceFile, span), - endOfLineState: ts.EndOfLineState.None - } - } - - function getSemanticTokens(program: Program, sourceFile:SourceFile, span: TextSpan): number[] { - let resultTokens: number[] = []; - - const collector = (node: Node, typeIdx: number, modifierSet: number) => { - resultTokens.push(node.getStart(), node.getWidth(), ((typeIdx + 1) << TokenEncodingConsts.typeOffset) + modifierSet); - }; - - if (program && sourceFile) { - collectTokens(program, sourceFile, span, collector); - } - return resultTokens; - } - - function collectTokens(program: Program, sourceFile: SourceFile, span: TextSpan, collector: (node: Node, tokenType: number, tokenModifier: number) => void) { - const typeChecker = program.getTypeChecker(); - - let inJSXElement = false; - - function visit(node: Node) { - if (!node || !textSpanIntersectsWith(span, node.pos, node.getFullWidth()) || node.getFullWidth() === 0) { - return; - } - const prevInJSXElement = inJSXElement; - if (isJsxElement(node) || isJsxSelfClosingElement(node)) { - inJSXElement = true; - } - if (isJsxExpression(node)) { - inJSXElement = false; - } - - if (isIdentifier(node) && !inJSXElement && !inImportClause(node)) { - let symbol = typeChecker.getSymbolAtLocation(node); - if (symbol) { - if (symbol.flags & SymbolFlags.Alias) { - symbol = typeChecker.getAliasedSymbol(symbol); - } - let typeIdx = classifySymbol(symbol, getMeaningFromLocation(node)); - if (typeIdx !== undefined) { - let modifierSet = 0; - if (node.parent) { - const parentIsDeclaration = (isBindingElement(node.parent) || tokenFromDeclarationMapping[node.parent.kind] === typeIdx); - if (parentIsDeclaration && (node.parent).name === node) { - modifierSet = 1 << TokenModifier.declaration; - } - } - - // property declaration in constructor - if (typeIdx === TokenType.parameter && isRightSideOfQualifiedNameOrPropertyAccess(node)) { - typeIdx = TokenType.property; - } - - typeIdx = reclassifyByType(typeChecker, node, typeIdx); - - const decl = symbol.valueDeclaration; - if (decl) { - const modifiers = getCombinedModifierFlags(decl); - const nodeFlags = getCombinedNodeFlags(decl); - if (modifiers & ModifierFlags.Static) { - modifierSet |= 1 << TokenModifier.static; - } - if (modifiers & ModifierFlags.Async) { - modifierSet |= 1 << TokenModifier.async; - } - if (typeIdx !== TokenType.class && typeIdx !== TokenType.interface) { - if ((modifiers & ModifierFlags.Readonly) || (nodeFlags & NodeFlags.Const) || (symbol.getFlags() & SymbolFlags.EnumMember)) { - modifierSet |= 1 << TokenModifier.readonly; - } - } - if ((typeIdx === TokenType.variable || typeIdx === TokenType.function) && isLocalDeclaration(decl, sourceFile)) { - modifierSet |= 1 << TokenModifier.local; - } - if (program.isSourceFileDefaultLibrary(decl.getSourceFile())) { - modifierSet |= 1 << TokenModifier.defaultLibrary; - } - } else if (symbol.declarations && symbol.declarations.some(d => program.isSourceFileDefaultLibrary(d.getSourceFile()))) { - modifierSet |= 1 << TokenModifier.defaultLibrary; - } - - collector(node, typeIdx, modifierSet); - - } - } - } - forEachChild(node, visit); - - inJSXElement = prevInJSXElement; - } - visit(sourceFile); - } - - function classifySymbol(symbol: Symbol, meaning: SemanticMeaning): TokenType | undefined { - const flags = symbol.getFlags(); - if (flags & SymbolFlags.Class) { - return TokenType.class; - } else if (flags & SymbolFlags.Enum) { - return TokenType.enum; - } else if (flags & SymbolFlags.TypeAlias) { - return TokenType.type; - } else if (flags & SymbolFlags.Interface) { - if (meaning & SemanticMeaning.Type) { - return TokenType.interface; - } - } else if (flags & SymbolFlags.TypeParameter) { - return TokenType.typeParameter; - } - let decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; - if (decl && isBindingElement(decl)) { - decl = getDeclarationForBindingElement(decl); - } - return decl && tokenFromDeclarationMapping[decl.kind]; - } - - function reclassifyByType(typeChecker: TypeChecker, node: Node, typeIdx: TokenType): TokenType { - // type based classifications - if (typeIdx === TokenType.variable || typeIdx === TokenType.property || typeIdx === TokenType.parameter) { - const type = typeChecker.getTypeAtLocation(node); - if (type) { - const test = (condition: (type: Type) => boolean) => { - return condition(type) || type.isUnion() && type.types.some(condition); - } - if (typeIdx !== TokenType.parameter && test(t => t.getConstructSignatures().length > 0)) { - return TokenType.class; - } - if (test(t => t.getCallSignatures().length > 0) && !test(t => t.getProperties().length > 0) || isExpressionInCallExpression(node)) { - return typeIdx === TokenType.property ? TokenType.member : TokenType.function; - } - } - } - return typeIdx; - } - - function isLocalDeclaration(decl: Declaration, sourceFile: SourceFile): boolean { - if (isBindingElement(decl)) { - decl = getDeclarationForBindingElement(decl); - } - if (isVariableDeclaration(decl)) { - return (!isSourceFile(decl.parent.parent.parent) || isCatchClause(decl.parent)) && decl.getSourceFile() === sourceFile; - } else if (isFunctionDeclaration(decl)) { - return !isSourceFile(decl.parent) && decl.getSourceFile() === sourceFile; - } - return false; - } - - function getDeclarationForBindingElement(element: BindingElement): VariableDeclaration | ParameterDeclaration { - while (true) { - if (isBindingElement(element.parent.parent)) { - element = element.parent.parent; - } else { - return element.parent.parent; - } - } - } - - function inImportClause(node: Node): boolean { - const parent = node.parent; - return parent && (isImportClause(parent) || isImportSpecifier(parent) || isNamespaceImport(parent)); - } - - function isExpressionInCallExpression(node: Node): boolean { - while (isRightSideOfQualifiedNameOrPropertyAccess(node)) { - node = node.parent - } - return isCallExpression(node.parent) && node.parent.expression === node; - } - - function isRightSideOfQualifiedNameOrPropertyAccess(node: Node): boolean { - return (isQualifiedName(node.parent) && node.parent.right === node) || (isPropertyAccessExpression(node.parent) && node.parent.name === node); - } - - const enum SemanticMeaning { - None = 0x0, - Value = 0x1, - Type = 0x2, - Namespace = 0x4, - All = Value | Type | Namespace - } - - function getMeaningFromLocation(node: Node): SemanticMeaning { - const f = (ts).getMeaningFromLocation; - if (typeof f === 'function') { - return f(node); - } - return SemanticMeaning.All; - } - - const tokenFromDeclarationMapping: { [name: string]: TokenType } = { - [SyntaxKind.VariableDeclaration]: TokenType.variable, - [SyntaxKind.Parameter]: TokenType.parameter, - [SyntaxKind.PropertyDeclaration]: TokenType.property, - [SyntaxKind.ModuleDeclaration]: TokenType.namespace, - [SyntaxKind.EnumDeclaration]: TokenType.enum, - [SyntaxKind.EnumMember]: TokenType.enumMember, - [SyntaxKind.ClassDeclaration]: TokenType.class, - [SyntaxKind.MethodDeclaration]: TokenType.member, - [SyntaxKind.FunctionDeclaration]: TokenType.function, - [SyntaxKind.FunctionExpression]: TokenType.function, - [SyntaxKind.MethodSignature]: TokenType.member, - [SyntaxKind.GetAccessor]: TokenType.property, - [SyntaxKind.SetAccessor]: TokenType.property, - [SyntaxKind.PropertySignature]: TokenType.property, - [SyntaxKind.InterfaceDeclaration]: TokenType.interface, - [SyntaxKind.TypeAliasDeclaration]: TokenType.type, - [SyntaxKind.TypeParameter]: TokenType.typeParameter, - [SyntaxKind.PropertyAssignment]: TokenType.property, - [SyntaxKind.ShorthandPropertyAssignment]: TokenType.property - }; -} +namespace ts.classifier.vscode { + + /** @internal */ + export enum TokenType { + class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member, _ + } + + /** @internal */ + export enum TokenModifier { + declaration, static, async, readonly, defaultLibrary, local, _ + } + + /** @internal */ + export enum TokenEncodingConsts { + typeOffset = 8, + modifierMask = (1 << typeOffset) - 1 + } + + /** This is mainly used internally for testing */ + export function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[] { + const classifications = getEncodedSemanticClassifications(program, _cancellationToken, sourceFile, span); + + Debug.assert(classifications.spans.length % 3 === 0); + const dense = classifications.spans; + const result: ClassifiedSpan[] = []; + for (let i = 0; i < dense.length; i += 3) { + result.push({ + textSpan: createTextSpan(dense[i], dense[i + 1]), + classificationType: dense[i + 2] + }); + } + + return result; + } + + export function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications { + return { + spans: getSemanticTokens(program, sourceFile, span), + endOfLineState: EndOfLineState.None + }; + } + + function getSemanticTokens(program: Program, sourceFile: SourceFile, span: TextSpan): number[] { + const resultTokens: number[] = []; + + const collector = (node: Node, typeIdx: number, modifierSet: number) => { + resultTokens.push(node.getStart(), node.getWidth(), ((typeIdx + 1) << TokenEncodingConsts.typeOffset) + modifierSet); + }; + + if (program && sourceFile) { + collectTokens(program, sourceFile, span, collector); + } + return resultTokens; + } + + function collectTokens(program: Program, sourceFile: SourceFile, span: TextSpan, collector: (node: Node, tokenType: number, tokenModifier: number) => void) { + const typeChecker = program.getTypeChecker(); + + let inJSXElement = false; + + function visit(node: Node) { + if (!node || !textSpanIntersectsWith(span, node.pos, node.getFullWidth()) || node.getFullWidth() === 0) { + return; + } + const prevInJSXElement = inJSXElement; + if (isJsxElement(node) || isJsxSelfClosingElement(node)) { + inJSXElement = true; + } + if (isJsxExpression(node)) { + inJSXElement = false; + } + + if (isIdentifier(node) && !inJSXElement && !inImportClause(node)) { + let symbol = typeChecker.getSymbolAtLocation(node); + if (symbol) { + if (symbol.flags & SymbolFlags.Alias) { + symbol = typeChecker.getAliasedSymbol(symbol); + } + let typeIdx = classifySymbol(symbol, getMeaningFromLocation(node)); + if (typeIdx !== undefined) { + let modifierSet = 0; + if (node.parent) { + const parentIsDeclaration = (isBindingElement(node.parent) || tokenFromDeclarationMapping[node.parent.kind] === typeIdx); + if (parentIsDeclaration && (node.parent).name === node) { + modifierSet = 1 << TokenModifier.declaration; + } + } + + // property declaration in constructor + if (typeIdx === TokenType.parameter && isRightSideOfQualifiedNameOrPropertyAccess(node)) { + typeIdx = TokenType.property; + } + + typeIdx = reclassifyByType(typeChecker, node, typeIdx); + + const decl = symbol.valueDeclaration; + if (decl) { + const modifiers = getCombinedModifierFlags(decl); + const nodeFlags = getCombinedNodeFlags(decl); + if (modifiers & ModifierFlags.Static) { + modifierSet |= 1 << TokenModifier.static; + } + if (modifiers & ModifierFlags.Async) { + modifierSet |= 1 << TokenModifier.async; + } + if (typeIdx !== TokenType.class && typeIdx !== TokenType.interface) { + if ((modifiers & ModifierFlags.Readonly) || (nodeFlags & NodeFlags.Const) || (symbol.getFlags() & SymbolFlags.EnumMember)) { + modifierSet |= 1 << TokenModifier.readonly; + } + } + if ((typeIdx === TokenType.variable || typeIdx === TokenType.function) && isLocalDeclaration(decl, sourceFile)) { + modifierSet |= 1 << TokenModifier.local; + } + if (program.isSourceFileDefaultLibrary(decl.getSourceFile())) { + modifierSet |= 1 << TokenModifier.defaultLibrary; + } + } + else if (symbol.declarations && symbol.declarations.some(d => program.isSourceFileDefaultLibrary(d.getSourceFile()))) { + modifierSet |= 1 << TokenModifier.defaultLibrary; + } + + collector(node, typeIdx, modifierSet); + + } + } + } + forEachChild(node, visit); + + inJSXElement = prevInJSXElement; + } + visit(sourceFile); + } + + function classifySymbol(symbol: Symbol, meaning: SemanticMeaning): TokenType | undefined { + const flags = symbol.getFlags(); + if (flags & SymbolFlags.Class) { + return TokenType.class; + } + else if (flags & SymbolFlags.Enum) { + return TokenType.enum; + } + else if (flags & SymbolFlags.TypeAlias) { + return TokenType.type; + } + else if (flags & SymbolFlags.Interface) { + if (meaning & SemanticMeaning.Type) { + return TokenType.interface; + } + } + else if (flags & SymbolFlags.TypeParameter) { + return TokenType.typeParameter; + } + let decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; + if (decl && isBindingElement(decl)) { + decl = getDeclarationForBindingElement(decl); + } + return decl && tokenFromDeclarationMapping[decl.kind]; + } + + function reclassifyByType(typeChecker: TypeChecker, node: Node, typeIdx: TokenType): TokenType { + // type based classifications + if (typeIdx === TokenType.variable || typeIdx === TokenType.property || typeIdx === TokenType.parameter) { + const type = typeChecker.getTypeAtLocation(node); + if (type) { + const test = (condition: (type: Type) => boolean) => { + return condition(type) || type.isUnion() && type.types.some(condition); + }; + if (typeIdx !== TokenType.parameter && test(t => t.getConstructSignatures().length > 0)) { + return TokenType.class; + } + if (test(t => t.getCallSignatures().length > 0) && !test(t => t.getProperties().length > 0) || isExpressionInCallExpression(node)) { + return typeIdx === TokenType.property ? TokenType.member : TokenType.function; + } + } + } + return typeIdx; + } + + function isLocalDeclaration(decl: Declaration, sourceFile: SourceFile): boolean { + if (isBindingElement(decl)) { + decl = getDeclarationForBindingElement(decl); + } + if (isVariableDeclaration(decl)) { + return (!isSourceFile(decl.parent.parent.parent) || isCatchClause(decl.parent)) && decl.getSourceFile() === sourceFile; + } + else if (isFunctionDeclaration(decl)) { + return !isSourceFile(decl.parent) && decl.getSourceFile() === sourceFile; + } + return false; + } + + function getDeclarationForBindingElement(element: BindingElement): VariableDeclaration | ParameterDeclaration { + while (true) { + if (isBindingElement(element.parent.parent)) { + element = element.parent.parent; + } + else { + return element.parent.parent; + } + } + } + + function inImportClause(node: Node): boolean { + const parent = node.parent; + return parent && (isImportClause(parent) || isImportSpecifier(parent) || isNamespaceImport(parent)); + } + + function isExpressionInCallExpression(node: Node): boolean { + while (isRightSideOfQualifiedNameOrPropertyAccess(node)) { + node = node.parent; + } + return isCallExpression(node.parent) && node.parent.expression === node; + } + + function isRightSideOfQualifiedNameOrPropertyAccess(node: Node): boolean { + return (isQualifiedName(node.parent) && node.parent.right === node) || (isPropertyAccessExpression(node.parent) && node.parent.name === node); + } + + const enum SemanticMeaning { + None = 0x0, + Value = 0x1, + Type = 0x2, + Namespace = 0x4, + All = Value | Type | Namespace + } + + function getMeaningFromLocation(node: Node): SemanticMeaning { + const f = (ts).getMeaningFromLocation; + if (typeof f === "function") { + return f(node); + } + return SemanticMeaning.All; + } + + const tokenFromDeclarationMapping: { [name: string]: TokenType } = { + [SyntaxKind.VariableDeclaration]: TokenType.variable, + [SyntaxKind.Parameter]: TokenType.parameter, + [SyntaxKind.PropertyDeclaration]: TokenType.property, + [SyntaxKind.ModuleDeclaration]: TokenType.namespace, + [SyntaxKind.EnumDeclaration]: TokenType.enum, + [SyntaxKind.EnumMember]: TokenType.enumMember, + [SyntaxKind.ClassDeclaration]: TokenType.class, + [SyntaxKind.MethodDeclaration]: TokenType.member, + [SyntaxKind.FunctionDeclaration]: TokenType.function, + [SyntaxKind.FunctionExpression]: TokenType.function, + [SyntaxKind.MethodSignature]: TokenType.member, + [SyntaxKind.GetAccessor]: TokenType.property, + [SyntaxKind.SetAccessor]: TokenType.property, + [SyntaxKind.PropertySignature]: TokenType.property, + [SyntaxKind.InterfaceDeclaration]: TokenType.interface, + [SyntaxKind.TypeAliasDeclaration]: TokenType.type, + [SyntaxKind.TypeParameter]: TokenType.typeParameter, + [SyntaxKind.PropertyAssignment]: TokenType.property, + [SyntaxKind.ShorthandPropertyAssignment]: TokenType.property + }; +} diff --git a/src/services/services.ts b/src/services/services.ts index 551578ff58454..0e6e58dd5825e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1797,33 +1797,35 @@ namespace ts { return kind === ScriptKind.TS || kind === ScriptKind.TSX; } - function getSemanticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): ClassifiedSpan[] { + function getSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[] { if (!isTsOrTsxFile(fileName)) { // do not run semantic classification on non-ts-or-tsx files return []; } synchronizeHostData(); - const responseFormat = format || "original" + const responseFormat = format || "original"; if (responseFormat === "original") { return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); - } else { - return ts.classifier.vscode.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); + } + else { + return classifier.vscode.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); } } - function getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): Classifications { + function getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): Classifications { if (!isTsOrTsxFile(fileName)) { // do not run semantic classification on non-ts-or-tsx files return { spans: [], endOfLineState: EndOfLineState.None }; } synchronizeHostData(); - const responseFormat = format || "original" + const responseFormat = format || "original"; if (responseFormat === "original") { return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); - } else { - return ts.classifier.vscode.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); + } + else { + return classifier.vscode.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); } } diff --git a/src/services/shims.ts b/src/services/shims.ts index 94708a03c2806..4d79258ba2065 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -145,9 +145,9 @@ namespace ts { getCompilerOptionsDiagnostics(): string; getSyntacticClassifications(fileName: string, start: number, length: number): string; - getSemanticClassifications(fileName: string, start: number, length: number, format?: "original" | "2020"): string; + getSemanticClassifications(fileName: string, start: number, length: number, format?: SemanticClassificationFormat): string; getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string; - getEncodedSemanticClassifications(fileName: string, start: number, length: number, format?: "original" | "2020"): string; + getEncodedSemanticClassifications(fileName: string, start: number, length: number, format?: SemanticClassificationFormat): string; getCompletionsAtPosition(fileName: string, position: number, preferences: UserPreferences | undefined): string; getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined): string; diff --git a/src/services/types.ts b/src/services/types.ts index ed9578b4efdfb..3fbaa2ea0d1ad 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -375,23 +375,23 @@ namespace ts { getCompilerOptionsDiagnostics(): Diagnostic[]; /** @deprecated Use getEncodedSyntacticClassifications instead. */ - getSyntacticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): ClassifiedSpan[]; + getSyntacticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[]; /** @deprecated Use getEncodedSemanticClassifications instead. */ - getSemanticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): ClassifiedSpan[]; + getSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[]; /** Encoded as triples of [start, length, ClassificationType]. */ getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; /** * Gets semantic highlights information for a particular file. Has two formats, an older * version used by VS and a format used by VS Code. - * + * * @param fileName The path to the file * @param position A text span to return results within * @param format Which format to use, defaults to "original" - * @returns a number array encoded as triples of [start, length, ClassificationType, ...]. + * @returns a number array encoded as triples of [start, length, ClassificationType, ...]. */ - getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: "original" | "2020"): Classifications; + getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): Classifications; /** * Gets completion entries at a particular position in a file. diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index aca17470ebcf5..9b9eea4839cd9 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -456,7 +456,7 @@ declare namespace FourSlashInterface { } interface ModernClassificationFactory { - token(identifier: number, name: string, position: number) + semanticToken(identifier: string, name: string, position: number) } interface ClassificationFactory { diff --git a/tests/cases/fourslash/semanticClassification1.ts b/tests/cases/fourslash/semanticClassification1.ts index 9fa7a7e284c7a..de96daee2091a 100644 --- a/tests/cases/fourslash/semanticClassification1.ts +++ b/tests/cases/fourslash/semanticClassification1.ts @@ -14,21 +14,12 @@ verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("3").position), c.interfaceName("I", test.marker("4").position)); - // // TODO: test like: - // // c2.token("module", "M", test.marker("0").position)) - var c2 = classification("2020") verify.semanticClassificationsAre("2020", - c2.token(1025, "M", test.marker("0").position), - c2.token(769, "I", test.marker("1").position), - c2.token(769, "X", test.marker("2").position), - c2.token(1024, "M", test.marker("3").position), - c2.token(768, "I", test.marker("4").position), + c2.semanticToken("namespace.declaration", "M", test.marker("0").position), + c2.semanticToken("interface.declaration", "I", test.marker("1").position), + c2.semanticToken("interface.declaration", "X", test.marker("2").position), + c2.semanticToken("namespace", "M", test.marker("3").position), + c2.semanticToken("interface", "I", test.marker("4").position), ) -// { textSpan: { start: 7, end: 1 }, classificationType: 1025 }, -// { textSpan: { start: 32, end: 1 }, classificationType: 769 }, -// { textSpan: { start: 54, end: 1 }, classificationType: 769 }, -// { textSpan: { start: 64, end: 1 }, classificationType: 1024 }, -// { textSpan: { start: 66, end: 1 }, classificationType: 768 } ); - From a95eb3d2eb2845d197fdaaf83078ec9e37980de9 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Tue, 16 Jun 2020 16:29:38 -0400 Subject: [PATCH 03/14] Adds existing tests --- src/compiler/types.ts | 2 +- src/harness/fourslashImpl.ts | 74 ++++++++++++++++++- src/harness/fourslashInterfaceImpl.ts | 61 ++------------- src/services/classifierVscode.ts | 12 +-- src/services/services.ts | 12 +-- .../cases/fourslash/classifyThisParameter.ts | 2 +- tests/cases/fourslash/fourslash.ts | 4 +- .../incrementalJsDocAdjustsLengthsRight.ts | 2 +- .../fourslash/semanticClassification1.ts | 12 +-- .../fourslash/semanticClassification2.ts | 11 ++- .../fourslash/semanticClassificationAlias.ts | 10 ++- .../semanticClassificationClassExpression.ts | 11 ++- ...nticClassificationInTemplateExpressions.ts | 17 ++++- ...stantiatedModuleWithVariableOfSameName1.ts | 18 ++++- ...stantiatedModuleWithVariableOfSameName2.ts | 18 ++++- .../fourslash/semanticClassificationJs.ts | 2 + .../semanticClassificationModules.ts | 16 +++- ...stantiatedModuleWithVariableOfSameName1.ts | 11 ++- ...stantiatedModuleWithVariableOfSameName2.ts | 17 ++++- .../semanticClassificationWithUnionTypes.ts | 18 ++++- .../semanticClassificationsCancellation1.ts | 11 ++- .../semanticClassificatonTypeAlias.ts | 15 +++- .../shims-pp/getSemanticClassifications.ts | 2 +- .../shims-pp/getSyntacticClassifications.ts | 4 +- .../shims/getSemanticClassifications.ts | 2 +- .../shims/getSyntacticClassifications.ts | 4 +- ...tacticClassificationForJSDocTemplateTag.ts | 9 ++- .../syntacticClassificationWithErrors.ts | 11 ++- .../fourslash/syntacticClassifications1.ts | 18 ++++- .../syntacticClassificationsCancellation1.ts | 8 +- ...ticClassificationsConflictDiff3Markers1.ts | 10 ++- ...ticClassificationsConflictDiff3Markers2.ts | 9 ++- ...yntacticClassificationsConflictMarkers1.ts | 10 ++- ...yntacticClassificationsConflictMarkers2.ts | 9 ++- .../syntacticClassificationsDocComment1.ts | 8 +- .../syntacticClassificationsDocComment2.ts | 7 +- .../syntacticClassificationsDocComment3.ts | 8 +- .../syntacticClassificationsDocComment4.ts | 8 +- .../syntacticClassificationsForOfKeyword.ts | 10 ++- .../syntacticClassificationsForOfKeyword2.ts | 10 ++- .../syntacticClassificationsForOfKeyword3.ts | 11 ++- ...cticClassificationsFunctionWithComments.ts | 14 +++- .../fourslash/syntacticClassificationsJsx1.ts | 10 ++- .../fourslash/syntacticClassificationsJsx2.ts | 10 ++- ...cticClassificationsMergeConflictMarker1.ts | 8 +- .../syntacticClassificationsObjectLiteral.ts | 19 ++++- .../syntacticClassificationsTemplates1.ts | 12 ++- .../syntacticClassificationsTemplates2.ts | 9 ++- .../syntacticClassificationsTripleSlash1.ts | 6 +- .../syntacticClassificationsTripleSlash10.ts | 4 +- .../syntacticClassificationsTripleSlash11.ts | 4 +- .../syntacticClassificationsTripleSlash12.ts | 4 +- .../syntacticClassificationsTripleSlash13.ts | 4 +- .../syntacticClassificationsTripleSlash14.ts | 4 +- .../syntacticClassificationsTripleSlash15.ts | 4 +- .../syntacticClassificationsTripleSlash16.ts | 4 +- .../syntacticClassificationsTripleSlash17.ts | 4 +- .../syntacticClassificationsTripleSlash18.ts | 4 +- .../syntacticClassificationsTripleSlash2.ts | 6 +- .../syntacticClassificationsTripleSlash3.ts | 6 +- .../syntacticClassificationsTripleSlash4.ts | 6 +- .../syntacticClassificationsTripleSlash5.ts | 6 +- .../syntacticClassificationsTripleSlash6.ts | 6 +- .../syntacticClassificationsTripleSlash7.ts | 6 +- .../syntacticClassificationsTripleSlash8.ts | 6 +- .../syntacticClassificationsTripleSlash9.ts | 6 +- 66 files changed, 513 insertions(+), 173 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ffb1d1ae00407..557845466cf2e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7767,7 +7767,7 @@ namespace ts { } export const enum SemanticClassificationFormat { - Original = "orginal", + Original = "original", TwentyTwenty = "2020" } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 736f233017879..9bcc59667514c 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2448,9 +2448,50 @@ namespace FourSlash { Harness.IO.log(this.spanInfoToString(this.getNameOrDottedNameSpan(pos)!, "**")); } + private classificationToIdentifier(classification: number){ + + const tokenTypes: string[] = []; + tokenTypes[ts.classifier.vscode.TokenType.class] = "class"; + tokenTypes[ts.classifier.vscode.TokenType.enum] = "enum"; + tokenTypes[ts.classifier.vscode.TokenType.interface] = "interface"; + tokenTypes[ts.classifier.vscode.TokenType.namespace] = "namespace"; + tokenTypes[ts.classifier.vscode.TokenType.typeParameter] = "typeParameter"; + tokenTypes[ts.classifier.vscode.TokenType.type] = "type"; + tokenTypes[ts.classifier.vscode.TokenType.parameter] = "parameter"; + tokenTypes[ts.classifier.vscode.TokenType.variable] = "variable"; + tokenTypes[ts.classifier.vscode.TokenType.enumMember] = "enumMember"; + tokenTypes[ts.classifier.vscode.TokenType.property] = "property"; + tokenTypes[ts.classifier.vscode.TokenType.function] = "function"; + tokenTypes[ts.classifier.vscode.TokenType.member] = "member"; + + const tokenModifiers: string[] = []; + tokenModifiers[ts.classifier.vscode.TokenModifier.async] = "async"; + tokenModifiers[ts.classifier.vscode.TokenModifier.declaration] = "declaration"; + tokenModifiers[ts.classifier.vscode.TokenModifier.readonly] = "readonly"; + tokenModifiers[ts.classifier.vscode.TokenModifier.static] = "static"; + tokenModifiers[ts.classifier.vscode.TokenModifier.local] = "local"; + tokenModifiers[ts.classifier.vscode.TokenModifier.defaultLibrary] = "defaultLibrary"; + + + function getTokenTypeFromClassification(tsClassification: number): number | undefined { + if (tsClassification > ts.classifier.vscode.TokenEncodingConsts.modifierMask) { + return (tsClassification >> ts.classifier.vscode.TokenEncodingConsts.typeOffset) - 1; + } + return undefined; + } + + function getTokenModifierFromClassification(tsClassification: number) { + return tsClassification & ts.classifier.vscode.TokenEncodingConsts.modifierMask; + } + + const typeIdx = getTokenTypeFromClassification(classification) || 0; + const modSet = getTokenModifierFromClassification(classification); + + const tokenClassifiction = [tokenTypes[typeIdx], ...tokenModifiers.filter((_, i) => modSet & 1 << i)].join("."); + return tokenClassifiction; + } + private verifyClassifications(expected: { classificationType: string | number, text?: string; textSpan?: TextSpan }[], actual: ts.ClassifiedSpan[], sourceFileText: string) { - console.log("expected:", expected); - console.log("actual:", actual); if (actual.length !== expected.length) { this.raiseError("verifyClassifications failed - expected total classifications to be " + expected.length + ", but was " + actual.length + @@ -2459,10 +2500,12 @@ namespace FourSlash { ts.zipWith(expected, actual, (expectedClassification, actualClassification) => { const expectedType = expectedClassification.classificationType; - if (expectedType !== actualClassification.classificationType) { + const actualType = typeof actualClassification.classificationType === "number" ? this.classificationToIdentifier(actualClassification.classificationType) : actualClassification.classificationType; + + if (expectedType !== actualType) { this.raiseError("verifyClassifications failed - expected classifications type to be " + expectedType + ", but was " + - actualClassification.classificationType + + actualType + jsonMismatchString()); } @@ -2513,6 +2556,29 @@ namespace FourSlash { } } + public replaceWithSemanticClassifications(format: ts.SemanticClassificationFormat.TwentyTwenty) { + const actual = this.languageService.getSemanticClassifications(this.activeFile.fileName, + ts.createTextSpan(0, this.activeFile.content.length), format); + const replacement = [`const c2 = classification("2020");`,`verify.semanticClassificationsAre("2020",`]; + actual.forEach(a => { + const identifier = this.classificationToIdentifier(a.classificationType as number); + const text = this.activeFile.content.slice(a.textSpan.start, a.textSpan.start + a.textSpan.length); + replacement.push(` c2.semanticToken("${identifier}", "${text}"), `); + }); + replacement.push(");"); + + throw new Error("You need to change the source code of fourslash test to use replaceWithSemanticClassifications"); + + /** + const fs = require("fs"); + const testfilePath = this.originalInputFileName.slice(1); + const testfile = fs.readFileSync(testfilePath, "utf8"); + const newfile = testfile.replace("verify.replaceWithSemanticClassifications(\"2020\")", replacement.join("\n")); + fs.writeFileSync(testfilePath, newfile); + */ + } + + public verifySemanticClassifications(format: ts.SemanticClassificationFormat, expected: { classificationType: string | number; text?: string }[]) { const actual = this.languageService.getSemanticClassifications(this.activeFile.fileName, ts.createTextSpan(0, this.activeFile.content.length), format); diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 55c552b1f42b2..f3fc93cfc65bc 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -508,6 +508,10 @@ namespace FourSlashInterface { this.state.verifySemanticClassifications(format, classifications); } + public replaceWithSemanticClassifications(format: ts.SemanticClassificationFormat.TwentyTwenty) { + this.state.replaceWithSemanticClassifications(format); + } + public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, expectedRange?: FourSlash.Range, options?: ts.RenameInfoOptions) { this.state.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers, fileToRename, expectedRange, options); } @@ -749,7 +753,7 @@ namespace FourSlashInterface { } interface Classification { - classificationType: ts.ClassificationTypeNames | number; + classificationType: ts.ClassificationTypeNames | string; text?: string; textSpan?: FourSlash.TextSpan; } @@ -758,61 +762,8 @@ namespace FourSlashInterface { export function classification(format: ts.SemanticClassificationFormat) { function semanticToken(identifier: string, text: string, _position: number): Classification { - - const tokenTypes = { - class: ts.classifier.vscode.TokenType.class, - enum: ts.classifier.vscode.TokenType.enum, - interface: ts.classifier.vscode.TokenType.interface, - namespace: ts.classifier.vscode.TokenType.namespace, - typeParameter: ts.classifier.vscode.TokenType.typeParameter, - type: ts.classifier.vscode.TokenType.type, - parameter: ts.classifier.vscode.TokenType.parameter, - variable: ts.classifier.vscode.TokenType.variable, - enumMember: ts.classifier.vscode.TokenType.enumMember, - property: ts.classifier.vscode.TokenType.property, - function: ts.classifier.vscode.TokenType.function, - member: ts.classifier.vscode.TokenType.member - }; - - const tokenModifiers = { - async: ts.classifier.vscode.TokenModifier.async, - declaration: ts.classifier.vscode.TokenModifier.declaration, - readonly: ts.classifier.vscode.TokenModifier.readonly, - static: ts.classifier.vscode.TokenModifier.static, - local: ts.classifier.vscode.TokenModifier.local, - defaultLibrary: ts.classifier.vscode.TokenModifier.defaultLibrary, - }; - - function identifierToClassificationID(identifier: string): number { - const [tokenType, ...modifiers] = identifier.split("."); - // @ts-expect-error - const tokenValue = tokenTypes[tokenType]; - if (tokenValue === undefined) { - throw new Error(`Did not find ${tokenType} in tokenTypes for classifiers.`); - } - - let classification = (tokenValue + 1) << 8; - ts.forEach(modifiers, (modifier) => { - // @ts-expect-error - const modifierValue = tokenModifiers[modifiers]; - if (tokenValue === undefined) { - throw new Error(`Did not find ${modifier} in tokenModifiers for classifiers.`); - } - classification += modifierValue + 1; - console.log("adding: ", modifierValue); - }); - - // debugger; - - return classification; - } - return { - classificationType: identifierToClassificationID(identifier), - // textSpan: { - // start: position, - // end: -1 - // }, + classificationType: identifier, text }; } diff --git a/src/services/classifierVscode.ts b/src/services/classifierVscode.ts index 0e972870e7e60..2d3a382cc9574 100644 --- a/src/services/classifierVscode.ts +++ b/src/services/classifierVscode.ts @@ -1,5 +1,11 @@ namespace ts.classifier.vscode { + /** @internal */ + export enum TokenEncodingConsts { + typeOffset = 8, + modifierMask = (1 << typeOffset) - 1 + } + /** @internal */ export enum TokenType { class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member, _ @@ -10,12 +16,6 @@ namespace ts.classifier.vscode { declaration, static, async, readonly, defaultLibrary, local, _ } - /** @internal */ - export enum TokenEncodingConsts { - typeOffset = 8, - modifierMask = (1 << typeOffset) - 1 - } - /** This is mainly used internally for testing */ export function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[] { const classifications = getEncodedSemanticClassifications(program, _cancellationToken, sourceFile, span); diff --git a/src/services/services.ts b/src/services/services.ts index 0e6e58dd5825e..04502be4b4fbf 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1804,11 +1804,11 @@ namespace ts { } synchronizeHostData(); - const responseFormat = format || "original"; - if (responseFormat === "original") { + const responseFormat = format || SemanticClassificationFormat.Original; + if (responseFormat === SemanticClassificationFormat.Original) { return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); } - else { + else { return classifier.vscode.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); } } @@ -1820,11 +1820,11 @@ namespace ts { } synchronizeHostData(); - const responseFormat = format || "original"; - if (responseFormat === "original") { + const responseFormat = format || SemanticClassificationFormat.Original; + if (responseFormat === SemanticClassificationFormat.Original) { return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); } - else { + else { return classifier.vscode.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); } } diff --git a/tests/cases/fourslash/classifyThisParameter.ts b/tests/cases/fourslash/classifyThisParameter.ts index 5e0c9a9d4b666..e28e5c2e67c9d 100644 --- a/tests/cases/fourslash/classifyThisParameter.ts +++ b/tests/cases/fourslash/classifyThisParameter.ts @@ -2,7 +2,7 @@ ////function f(this){} -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("function"), c.identifier("f"), diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9b9eea4839cd9..8f2e85021d93d 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -358,6 +358,8 @@ declare namespace FourSlashInterface { text?: string; textSpan?: TextSpan; }[]): void; + /** Edits the current testfile and replaces with the semantic classifications */ + replaceWithSemanticClassifications(format: "2020") renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string, fileToRename?: string, range?: Range, allowRenameOfImportPath?: boolean): void; renameInfoFailed(message?: string, allowRenameOfImportPath?: boolean): void; renameLocations(startRanges: ArrayOrSingle, options: RenameLocationsOptions): void; @@ -456,7 +458,7 @@ declare namespace FourSlashInterface { } interface ModernClassificationFactory { - semanticToken(identifier: string, name: string, position: number) + semanticToken(identifier: string, name: string) } interface ClassificationFactory { diff --git a/tests/cases/fourslash/incrementalJsDocAdjustsLengthsRight.ts b/tests/cases/fourslash/incrementalJsDocAdjustsLengthsRight.ts index 626a1e372588a..e4d438171a964 100644 --- a/tests/cases/fourslash/incrementalJsDocAdjustsLengthsRight.ts +++ b/tests/cases/fourslash/incrementalJsDocAdjustsLengthsRight.ts @@ -9,7 +9,7 @@ //// * @param {Number} wid/*1*/ goTo.marker('1'); edit.insert("th\n@"); -const c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/**\n * Pad `str` to `width`.\n *\n * "), c.punctuation("@"), diff --git a/tests/cases/fourslash/semanticClassification1.ts b/tests/cases/fourslash/semanticClassification1.ts index de96daee2091a..25f5557542a07 100644 --- a/tests/cases/fourslash/semanticClassification1.ts +++ b/tests/cases/fourslash/semanticClassification1.ts @@ -6,7 +6,7 @@ //// } //// interface /*2*/X extends /*3*/M./*4*/I { } -var c = classification("original"); +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), @@ -16,10 +16,10 @@ verify.semanticClassificationsAre("original", var c2 = classification("2020") verify.semanticClassificationsAre("2020", - c2.semanticToken("namespace.declaration", "M", test.marker("0").position), - c2.semanticToken("interface.declaration", "I", test.marker("1").position), - c2.semanticToken("interface.declaration", "X", test.marker("2").position), - c2.semanticToken("namespace", "M", test.marker("3").position), - c2.semanticToken("interface", "I", test.marker("4").position), + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("interface.declaration", "X"), + c2.semanticToken("namespace", "M"), + c2.semanticToken("interface", "I"), ) diff --git a/tests/cases/fourslash/semanticClassification2.ts b/tests/cases/fourslash/semanticClassification2.ts index 84cdb8fc766ed..f250b7d690047 100644 --- a/tests/cases/fourslash/semanticClassification2.ts +++ b/tests/cases/fourslash/semanticClassification2.ts @@ -7,5 +7,14 @@ //// var Thing = 0; //// Thing.toExponential(); -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.interfaceName("Thing", test.marker("0").position)); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("interface.declaration", "Thing"), + c2.semanticToken("member.declaration", "toExponential"), + c2.semanticToken("variable.declaration", "Thing"), + c2.semanticToken("variable", "Thing"), + c2.semanticToken("member.defaultLibrary", "toExponential") +); diff --git a/tests/cases/fourslash/semanticClassificationAlias.ts b/tests/cases/fourslash/semanticClassificationAlias.ts index 7e34940cc00ca..eef56f91830d5 100644 --- a/tests/cases/fourslash/semanticClassificationAlias.ts +++ b/tests/cases/fourslash/semanticClassificationAlias.ts @@ -11,10 +11,18 @@ goTo.file("/b.ts"); const [m0, m1, m2, m3] = test.markers(); -const c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.typeAliasName("x", m0.position), c.className("y", m1.position), c.typeAliasName("x", m2.position), c.className("y", m3.position), ); + +const c2 = classification("2020"); + verify.semanticClassificationsAre("2020", + + c2.semanticToken("variable.declaration.readonly", "v"), + c2.semanticToken("type", "x"), + c2.semanticToken("class", "y"), +); diff --git a/tests/cases/fourslash/semanticClassificationClassExpression.ts b/tests/cases/fourslash/semanticClassificationClassExpression.ts index 98da5a8418d3e..eda79ce8f3d39 100644 --- a/tests/cases/fourslash/semanticClassificationClassExpression.ts +++ b/tests/cases/fourslash/semanticClassificationClassExpression.ts @@ -3,10 +3,19 @@ //// var x = class /*0*/C {} //// class /*1*/C {} //// class /*2*/D extends class /*3*/B{} { } -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.className("C", test.marker("0").position), c.className("C", test.marker("1").position), c.className("D", test.marker("2").position), c.className("B", test.marker("3").position) ); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "x"), + c2.semanticToken("class", "C"), + c2.semanticToken("class.declaration", "C"), + c2.semanticToken("class.declaration", "D"), + c2.semanticToken("class", "B"), +); diff --git a/tests/cases/fourslash/semanticClassificationInTemplateExpressions.ts b/tests/cases/fourslash/semanticClassificationInTemplateExpressions.ts index 15f822de0a82d..5866529de3da8 100644 --- a/tests/cases/fourslash/semanticClassificationInTemplateExpressions.ts +++ b/tests/cases/fourslash/semanticClassificationInTemplateExpressions.ts @@ -10,7 +10,7 @@ ////} ////`abcd${ /*3*/M./*4*/C.x + /*5*/M./*6*/E.E1}efg` -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.className("C", test.marker("1").position), @@ -19,3 +19,18 @@ verify.semanticClassificationsAre("original", c.className("C", test.marker("4").position), c.moduleName("M", test.marker("5").position), c.enumName("E", test.marker("6").position)); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("class.declaration", "C"), + c2.semanticToken("property.declaration.static", "x"), + c2.semanticToken("enum.declaration", "E"), + c2.semanticToken("enumMember.declaration.readonly", "E1"), + c2.semanticToken("namespace", "M"), + c2.semanticToken("class", "C"), + c2.semanticToken("property.static", "x"), + c2.semanticToken("namespace", "M"), + c2.semanticToken("enum", "E"), + c2.semanticToken("enumMember.readonly", "E1"), +); diff --git a/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName1.ts b/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName1.ts index cfb43136fe859..ab5de2ba6f175 100644 --- a/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName1.ts +++ b/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName1.ts @@ -15,10 +15,26 @@ //// ////var x = /*5*/M; -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.moduleName("M", test.marker("3").position), c.interfaceName("I", test.marker("4").position), c.moduleName("M", test.marker("5").position)); + + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("variable.declaration.local", "x"), + c2.semanticToken("variable.declaration", "M"), + c2.semanticToken("property.declaration", "foo"), + c2.semanticToken("property.declaration", "bar"), + c2.semanticToken("variable.declaration", "v"), + c2.semanticToken("namespace", "M"), + c2.semanticToken("interface", "I"), + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("namespace", "M"), +); diff --git a/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName2.ts b/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName2.ts index 4b1745479a19a..5f9de7227d9b3 100644 --- a/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName2.ts +++ b/tests/cases/fourslash/semanticClassificationInstantiatedModuleWithVariableOfSameName2.ts @@ -18,7 +18,7 @@ //// ////var x = /*6*/M; -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), @@ -26,3 +26,19 @@ verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("4").position), c.interfaceName("I", test.marker("5").position), c.moduleName("M", test.marker("6").position)); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("variable.declaration.local", "x"), + c2.semanticToken("variable.declaration", "M"), + c2.semanticToken("property.declaration", "foo"), + c2.semanticToken("property.declaration", "bar"), + c2.semanticToken("variable.declaration", "v"), + c2.semanticToken("namespace", "M"), + c2.semanticToken("interface", "I"), + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("namespace", "M"), +); diff --git a/tests/cases/fourslash/semanticClassificationJs.ts b/tests/cases/fourslash/semanticClassificationJs.ts index 4232c962012f7..69683d6669511 100644 --- a/tests/cases/fourslash/semanticClassificationJs.ts +++ b/tests/cases/fourslash/semanticClassificationJs.ts @@ -7,3 +7,5 @@ // no semantic classification in js file verify.semanticClassificationsAre("original", ); + + diff --git a/tests/cases/fourslash/semanticClassificationModules.ts b/tests/cases/fourslash/semanticClassificationModules.ts index ba2798dfaf8fc..5a57251b3a599 100644 --- a/tests/cases/fourslash/semanticClassificationModules.ts +++ b/tests/cases/fourslash/semanticClassificationModules.ts @@ -9,7 +9,7 @@ ////var x: /*2*/M./*3*/I = /*4*/M.v; ////var y = /*5*/M; -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), @@ -17,3 +17,17 @@ verify.semanticClassificationsAre("original", c.interfaceName("I", test.marker("3").position), c.moduleName("M", test.marker("4").position), c.moduleName("M", test.marker("5").position)); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("variable.declaration.local", "v"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("namespace", "M"), + c2.semanticToken("interface", "I"), + c2.semanticToken("namespace", "M"), + c2.semanticToken("variable.local", "v"), + c2.semanticToken("variable.declaration", "y"), + c2.semanticToken("namespace", "M"), +); diff --git a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts index 9b291b933d0cc..6c1e24e2d94f0 100644 --- a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts +++ b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts @@ -8,7 +8,16 @@ //// ////var M = { I: 10 }; -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position)); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable", "M"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("variable.declaration", "M"), + c2.semanticToken("property.declaration", "I"), +); + \ No newline at end of file diff --git a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts index 2c88b59b9b40e..1566d333c4e37 100644 --- a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts +++ b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts @@ -14,9 +14,24 @@ //// ////var x = /*5*/M; -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), c.moduleName("M", test.marker("3").position), c.interfaceName("I", test.marker("4").position)); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable", "M"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("variable.declaration", "M"), + c2.semanticToken("property.declaration", "foo"), + c2.semanticToken("property.declaration", "bar"), + c2.semanticToken("variable.declaration", "v"), + c2.semanticToken("variable", "M"), + c2.semanticToken("interface", "I"), + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("variable", "M"), +); + \ No newline at end of file diff --git a/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts b/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts index 4d90b6b46c520..257130392df1f 100644 --- a/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts +++ b/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts @@ -11,7 +11,7 @@ ////var M: /*4*/M./*5*/I | /*6*/I | /*7*/C; ////var I: typeof M | typeof /*8*/C; -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), @@ -22,3 +22,19 @@ verify.semanticClassificationsAre("original", c.interfaceName("I", test.marker("6").position), c.className("C", test.marker("7").position), c.className("C", test.marker("8").position)); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable", "M"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("class.declaration", "C"), + c2.semanticToken("variable.declaration", "M"), + c2.semanticToken("variable", "M"), + c2.semanticToken("interface", "I"), + c2.semanticToken("interface", "I"), + c2.semanticToken("class", "C"), + c2.semanticToken("class.declaration", "I"), + c2.semanticToken("variable", "M"), + c2.semanticToken("class", "C"), +); diff --git a/tests/cases/fourslash/semanticClassificationsCancellation1.ts b/tests/cases/fourslash/semanticClassificationsCancellation1.ts index 95a63db68c74e..84a0dafe17681 100644 --- a/tests/cases/fourslash/semanticClassificationsCancellation1.ts +++ b/tests/cases/fourslash/semanticClassificationsCancellation1.ts @@ -5,7 +5,7 @@ ////module N { ////} -var c = classification; +const c = classification("original"); cancellation.setCancelled(1); verifyOperationIsCancelled(() => verify.semanticClassificationsAre("original", )); cancellation.resetCancelled(); @@ -13,3 +13,12 @@ cancellation.resetCancelled(); verify.semanticClassificationsAre("original", c.moduleName("M"), c.moduleName("N")); + + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("namespace.declaration", "N"), +); + + \ No newline at end of file diff --git a/tests/cases/fourslash/semanticClassificatonTypeAlias.ts b/tests/cases/fourslash/semanticClassificatonTypeAlias.ts index d021cbe827687..01e390b33a85d 100644 --- a/tests/cases/fourslash/semanticClassificatonTypeAlias.ts +++ b/tests/cases/fourslash/semanticClassificatonTypeAlias.ts @@ -5,7 +5,7 @@ ////var y = {}; ////function f(x: /*3*/Alias): /*4*/Alias { return undefined; } -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.typeAliasName("Alias", test.marker("0").position), c.typeAliasName("Alias", test.marker("1").position), @@ -13,3 +13,16 @@ verify.semanticClassificationsAre("original", c.typeAliasName("Alias", test.marker("3").position), c.typeAliasName("Alias", test.marker("4").position) ); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("type.declaration", "Alias"), + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("type", "Alias"), + c2.semanticToken("variable.declaration", "y"), + c2.semanticToken("type", "Alias"), + c2.semanticToken("function.declaration", "f"), + c2.semanticToken("parameter.declaration", "x"), + c2.semanticToken("type", "Alias"), + c2.semanticToken("type", "Alias"), +); diff --git a/tests/cases/fourslash/shims-pp/getSemanticClassifications.ts b/tests/cases/fourslash/shims-pp/getSemanticClassifications.ts index 106e7171cc6d4..1bbc4eec21a3e 100644 --- a/tests/cases/fourslash/shims-pp/getSemanticClassifications.ts +++ b/tests/cases/fourslash/shims-pp/getSemanticClassifications.ts @@ -6,7 +6,7 @@ //// } //// interface /*2*/X extends /*3*/M./*4*/I { } -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), diff --git a/tests/cases/fourslash/shims-pp/getSyntacticClassifications.ts b/tests/cases/fourslash/shims-pp/getSyntacticClassifications.ts index 1dbe2944b8ae5..897d42cbea729 100644 --- a/tests/cases/fourslash/shims-pp/getSyntacticClassifications.ts +++ b/tests/cases/fourslash/shims-pp/getSyntacticClassifications.ts @@ -18,7 +18,7 @@ //// } //// } -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("// comment"), c.keyword("module"), c.moduleName("M"), c.punctuation("{"), @@ -32,4 +32,4 @@ verify.syntacticClassificationsAre( c.punctuation("}"), c.keyword("module"), c.moduleName("M1"), c.punctuation("."), c.moduleName("M2"), c.punctuation("{"), c.punctuation("}"), - c.punctuation("}")); \ No newline at end of file + c.punctuation("}")); diff --git a/tests/cases/fourslash/shims/getSemanticClassifications.ts b/tests/cases/fourslash/shims/getSemanticClassifications.ts index 106e7171cc6d4..1bbc4eec21a3e 100644 --- a/tests/cases/fourslash/shims/getSemanticClassifications.ts +++ b/tests/cases/fourslash/shims/getSemanticClassifications.ts @@ -6,7 +6,7 @@ //// } //// interface /*2*/X extends /*3*/M./*4*/I { } -var c = classification; +const c = classification("original"); verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position), diff --git a/tests/cases/fourslash/shims/getSyntacticClassifications.ts b/tests/cases/fourslash/shims/getSyntacticClassifications.ts index 1dbe2944b8ae5..897d42cbea729 100644 --- a/tests/cases/fourslash/shims/getSyntacticClassifications.ts +++ b/tests/cases/fourslash/shims/getSyntacticClassifications.ts @@ -18,7 +18,7 @@ //// } //// } -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("// comment"), c.keyword("module"), c.moduleName("M"), c.punctuation("{"), @@ -32,4 +32,4 @@ verify.syntacticClassificationsAre( c.punctuation("}"), c.keyword("module"), c.moduleName("M1"), c.punctuation("."), c.moduleName("M2"), c.punctuation("{"), c.punctuation("}"), - c.punctuation("}")); \ No newline at end of file + c.punctuation("}")); diff --git a/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts b/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts index c3368207d2cc2..0f8169828b30d 100644 --- a/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts +++ b/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts @@ -4,7 +4,7 @@ ////function ident: T { ////} -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/** "), c.punctuation("@"), @@ -20,3 +20,10 @@ verify.syntacticClassificationsAre( c.identifier("T"), c.punctuation("{"), c.punctuation("}")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("function.declaration", "ident"), + c2.semanticToken("typeParameter.declaration", "T"), + c2.semanticToken("typeParameter", "T"), +); diff --git a/tests/cases/fourslash/syntacticClassificationWithErrors.ts b/tests/cases/fourslash/syntacticClassificationWithErrors.ts index 166a22a1a5127..d21f8f9955ba7 100644 --- a/tests/cases/fourslash/syntacticClassificationWithErrors.ts +++ b/tests/cases/fourslash/syntacticClassificationWithErrors.ts @@ -5,10 +5,17 @@ ////} ////c = -let c = classification +const c = classification("original") + verify.syntacticClassificationsAre( c.keyword("class"), c.className("A"), c.punctuation("{"), c.identifier("a"), c.punctuation(":"), c.punctuation("}"), c.identifier("c"), c.operator("=") - ); \ No newline at end of file + ); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "A"), + c2.semanticToken("property.declaration", "a"), +); diff --git a/tests/cases/fourslash/syntacticClassifications1.ts b/tests/cases/fourslash/syntacticClassifications1.ts index 1dbe2944b8ae5..23c1460dcff3a 100644 --- a/tests/cases/fourslash/syntacticClassifications1.ts +++ b/tests/cases/fourslash/syntacticClassifications1.ts @@ -18,7 +18,7 @@ //// } //// } -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("// comment"), c.keyword("module"), c.moduleName("M"), c.punctuation("{"), @@ -32,4 +32,18 @@ verify.syntacticClassificationsAre( c.punctuation("}"), c.keyword("module"), c.moduleName("M1"), c.punctuation("."), c.moduleName("M2"), c.punctuation("{"), c.punctuation("}"), - c.punctuation("}")); \ No newline at end of file + c.punctuation("}")); + + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("variable.declaration.local", "v"), + c2.semanticToken("variable.declaration.local", "s"), + c2.semanticToken("class.declaration", "C"), + c2.semanticToken("typeParameter.declaration", "T"), + c2.semanticToken("enum.declaration", "E"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("namespace.declaration", "M1"), + c2.semanticToken("namespace.declaration", "M2"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsCancellation1.ts b/tests/cases/fourslash/syntacticClassificationsCancellation1.ts index f15ce5f9984c8..9b483463bec84 100644 --- a/tests/cases/fourslash/syntacticClassificationsCancellation1.ts +++ b/tests/cases/fourslash/syntacticClassificationsCancellation1.ts @@ -5,7 +5,7 @@ ////module N { ////} -var c = classification; +const c = classification("original"); cancellation.setCancelled(1); verifyOperationIsCancelled(() => verify.syntacticClassificationsAre()); cancellation.resetCancelled(); @@ -19,3 +19,9 @@ verify.syntacticClassificationsAre( c.moduleName("N"), c.punctuation("{"), c.punctuation("}")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("namespace.declaration", "N"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsConflictDiff3Markers1.ts b/tests/cases/fourslash/syntacticClassificationsConflictDiff3Markers1.ts index 669705307a96a..b63271f096b0f 100644 --- a/tests/cases/fourslash/syntacticClassificationsConflictDiff3Markers1.ts +++ b/tests/cases/fourslash/syntacticClassificationsConflictDiff3Markers1.ts @@ -10,7 +10,7 @@ ////>>>>>>> Branch - a ////} -const c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("class"), c.className("C"), c.punctuation("{"), c.comment("<<<<<<< HEAD"), @@ -20,4 +20,10 @@ verify.syntacticClassificationsAre( c.comment("======="), c.identifier("v"), c.punctuation("="), c.numericLiteral("2"), c.punctuation(";"), c.comment(">>>>>>> Branch - a"), - c.punctuation("}")); \ No newline at end of file + c.punctuation("}")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "C"), + c2.semanticToken("property.declaration", "v"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsConflictDiff3Markers2.ts b/tests/cases/fourslash/syntacticClassificationsConflictDiff3Markers2.ts index 17144397184c6..c75bb2130f5b9 100644 --- a/tests/cases/fourslash/syntacticClassificationsConflictDiff3Markers2.ts +++ b/tests/cases/fourslash/syntacticClassificationsConflictDiff3Markers2.ts @@ -8,7 +8,7 @@ ////class D { } ////>>>>>>> Branch - a -const c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("<<<<<<< HEAD"), c.keyword("class"), c.className("C"), c.punctuation("{"), c.punctuation("}"), @@ -16,4 +16,9 @@ verify.syntacticClassificationsAre( c.keyword("class"), c.identifier("E"), c.punctuation("{"), c.punctuation("}"), c.comment("======="), c.keyword("class"), c.identifier("D"), c.punctuation("{"), c.punctuation("}"), - c.comment(">>>>>>> Branch - a")); \ No newline at end of file + c.comment(">>>>>>> Branch - a")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "C"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsConflictMarkers1.ts b/tests/cases/fourslash/syntacticClassificationsConflictMarkers1.ts index 99db7443a4c73..54f40b2cc61ab 100644 --- a/tests/cases/fourslash/syntacticClassificationsConflictMarkers1.ts +++ b/tests/cases/fourslash/syntacticClassificationsConflictMarkers1.ts @@ -8,7 +8,7 @@ ////>>>>>>> Branch - a ////} -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("class"), c.className("C"), c.punctuation("{"), c.comment("<<<<<<< HEAD"), @@ -16,4 +16,10 @@ verify.syntacticClassificationsAre( c.comment("======="), c.identifier("v"), c.punctuation("="), c.numericLiteral("2"), c.punctuation(";"), c.comment(">>>>>>> Branch - a"), - c.punctuation("}")); \ No newline at end of file + c.punctuation("}")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "C"), + c2.semanticToken("property.declaration", "v"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsConflictMarkers2.ts b/tests/cases/fourslash/syntacticClassificationsConflictMarkers2.ts index 18363c0389e04..e787c832fa72a 100644 --- a/tests/cases/fourslash/syntacticClassificationsConflictMarkers2.ts +++ b/tests/cases/fourslash/syntacticClassificationsConflictMarkers2.ts @@ -6,10 +6,15 @@ ////class D { } ////>>>>>>> Branch - a -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("<<<<<<< HEAD"), c.keyword("class"), c.className("C"), c.punctuation("{"), c.punctuation("}"), c.comment("======="), c.keyword("class"), c.identifier("D"), c.punctuation("{"), c.punctuation("}"), - c.comment(">>>>>>> Branch - a")); \ No newline at end of file + c.comment(">>>>>>> Branch - a")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "C"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsDocComment1.ts b/tests/cases/fourslash/syntacticClassificationsDocComment1.ts index 1ea3bf9684a61..5a0194e8c5ad2 100644 --- a/tests/cases/fourslash/syntacticClassificationsDocComment1.ts +++ b/tests/cases/fourslash/syntacticClassificationsDocComment1.ts @@ -3,7 +3,7 @@ //// /** @type {number} */ //// var v; -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/** "), c.punctuation("@"), @@ -15,3 +15,9 @@ verify.syntacticClassificationsAre( c.keyword("var"), c.identifier("v"), c.punctuation(";")); + + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "v"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsDocComment2.ts b/tests/cases/fourslash/syntacticClassificationsDocComment2.ts index 38dca4edcf20d..d0e3eeba83645 100644 --- a/tests/cases/fourslash/syntacticClassificationsDocComment2.ts +++ b/tests/cases/fourslash/syntacticClassificationsDocComment2.ts @@ -4,7 +4,7 @@ //// var v; -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/** "), c.punctuation("@"), @@ -24,3 +24,8 @@ verify.syntacticClassificationsAre( c.keyword("var"), c.identifier("v"), c.punctuation(";")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "v"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsDocComment3.ts b/tests/cases/fourslash/syntacticClassificationsDocComment3.ts index d7996fbbc54e5..94009e799224b 100644 --- a/tests/cases/fourslash/syntacticClassificationsDocComment3.ts +++ b/tests/cases/fourslash/syntacticClassificationsDocComment3.ts @@ -3,7 +3,7 @@ //// /** @param foo { number /* } */ //// var v; -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/** "), c.punctuation("@"), @@ -17,3 +17,9 @@ verify.syntacticClassificationsAre( c.keyword("var"), c.identifier("v"), c.punctuation(";")); + + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "v"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsDocComment4.ts b/tests/cases/fourslash/syntacticClassificationsDocComment4.ts index d8ac2f12d8b32..ee8eaa933635c 100644 --- a/tests/cases/fourslash/syntacticClassificationsDocComment4.ts +++ b/tests/cases/fourslash/syntacticClassificationsDocComment4.ts @@ -3,7 +3,7 @@ //// /** @param {number} p1 */ //// function foo(p1) {} -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/** "), c.punctuation("@"), @@ -22,3 +22,9 @@ verify.syntacticClassificationsAre( c.punctuation(")"), c.punctuation("{"), c.punctuation("}")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("function.declaration", "foo"), + c2.semanticToken("parameter.declaration", "p1"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsForOfKeyword.ts b/tests/cases/fourslash/syntacticClassificationsForOfKeyword.ts index 22d7aa8ba946d..5a2354b380582 100644 --- a/tests/cases/fourslash/syntacticClassificationsForOfKeyword.ts +++ b/tests/cases/fourslash/syntacticClassificationsForOfKeyword.ts @@ -2,7 +2,7 @@ //// for (var of of of) { } -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("for"), c.punctuation("("), @@ -13,4 +13,10 @@ verify.syntacticClassificationsAre( c.punctuation(")"), c.punctuation("{"), c.punctuation("}") - ); \ No newline at end of file + ); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "of"), + c2.semanticToken("variable", "of"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsForOfKeyword2.ts b/tests/cases/fourslash/syntacticClassificationsForOfKeyword2.ts index 190663caa995e..1d8ba78579b93 100644 --- a/tests/cases/fourslash/syntacticClassificationsForOfKeyword2.ts +++ b/tests/cases/fourslash/syntacticClassificationsForOfKeyword2.ts @@ -2,7 +2,7 @@ //// for (var of in of) { } -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("for"), c.punctuation("("), @@ -13,4 +13,10 @@ verify.syntacticClassificationsAre( c.punctuation(")"), c.punctuation("{"), c.punctuation("}") - ); \ No newline at end of file + ); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "of"), + c2.semanticToken("variable", "of"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsForOfKeyword3.ts b/tests/cases/fourslash/syntacticClassificationsForOfKeyword3.ts index ef96c99fed45b..1e5f8481e5891 100644 --- a/tests/cases/fourslash/syntacticClassificationsForOfKeyword3.ts +++ b/tests/cases/fourslash/syntacticClassificationsForOfKeyword3.ts @@ -2,7 +2,7 @@ //// for (var of; of; of) { } -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("for"), c.punctuation("("), @@ -15,4 +15,11 @@ verify.syntacticClassificationsAre( c.punctuation(")"), c.punctuation("{"), c.punctuation("}") - ); \ No newline at end of file + ); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "of"), + c2.semanticToken("variable", "of"), + c2.semanticToken("variable", "of"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsFunctionWithComments.ts b/tests/cases/fourslash/syntacticClassificationsFunctionWithComments.ts index 05a5aaa173e0e..b9bde2d5df321 100644 --- a/tests/cases/fourslash/syntacticClassificationsFunctionWithComments.ts +++ b/tests/cases/fourslash/syntacticClassificationsFunctionWithComments.ts @@ -16,10 +16,20 @@ var firstCommentText = * There are many like it, but this one is mine.\n\ */"; -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment(firstCommentText), c.keyword("function"), c.identifier("myFunction"), c.punctuation("("), c.comment("/* x */"), c.parameterName("x"), c.punctuation(":"), c.keyword("any"), c.punctuation(")"), c.punctuation("{"), c.keyword("var"), c.identifier("y"), c.operator("="), c.identifier("x"), c.operator("?"), c.identifier("x"), c.operator("++"), c.operator(":"), c.operator("++"), c.identifier("x"), c.punctuation(";"), c.punctuation("}"), - c.comment("// end of file")); \ No newline at end of file + c.comment("// end of file")); + + const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("function.declaration", "myFunction"), + c2.semanticToken("parameter.declaration", "x"), + c2.semanticToken("variable.declaration.local", "y"), + c2.semanticToken("parameter", "x"), + c2.semanticToken("parameter", "x"), + c2.semanticToken("parameter", "x"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsJsx1.ts b/tests/cases/fourslash/syntacticClassificationsJsx1.ts index e9de07c759b7b..8e6bfc6462ec1 100644 --- a/tests/cases/fourslash/syntacticClassificationsJsx1.ts +++ b/tests/cases/fourslash/syntacticClassificationsJsx1.ts @@ -7,7 +7,7 @@ //// ////let y = -const c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("let"), c.identifier("x"), c.operator("="), c.punctuation("<"), @@ -24,4 +24,10 @@ verify.syntacticClassificationsAre( c.jsxSelfClosingTagName("element"), c.jsxAttribute("attr"), c.operator("="), c.jsxAttributeStringLiteralValue(`"123"`), c.punctuation("/"), c.punctuation(">") -) \ No newline at end of file +) + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("variable.declaration", "y"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsJsx2.ts b/tests/cases/fourslash/syntacticClassificationsJsx2.ts index 9110c6ce4b934..6c244ef4b6598 100644 --- a/tests/cases/fourslash/syntacticClassificationsJsx2.ts +++ b/tests/cases/fourslash/syntacticClassificationsJsx2.ts @@ -7,7 +7,7 @@ //// ////let y = -const c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("let"), c.identifier("x"), c.operator("="), c.punctuation("<"), @@ -24,4 +24,10 @@ verify.syntacticClassificationsAre( c.jsxSelfClosingTagName("element.name"), c.jsxAttribute("attr"), c.operator("="), c.jsxAttributeStringLiteralValue(`"123"`), c.punctuation("/"), c.punctuation(">") -) \ No newline at end of file +) + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("variable.declaration", "y"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsMergeConflictMarker1.ts b/tests/cases/fourslash/syntacticClassificationsMergeConflictMarker1.ts index b78a6f35982a6..49610740bc266 100644 --- a/tests/cases/fourslash/syntacticClassificationsMergeConflictMarker1.ts +++ b/tests/cases/fourslash/syntacticClassificationsMergeConflictMarker1.ts @@ -7,10 +7,14 @@ //// >>>>>>> Feature -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("<<<<<<< HEAD"), c.stringLiteral("\"AAAA\""), c.comment("======="), c.stringLiteral("\"BBBB\""), - c.comment(">>>>>>> Feature")); \ No newline at end of file + c.comment(">>>>>>> Feature")); + +const c2 = classification("2020"); + verify.semanticClassificationsAre("2020", +); diff --git a/tests/cases/fourslash/syntacticClassificationsObjectLiteral.ts b/tests/cases/fourslash/syntacticClassificationsObjectLiteral.ts index c5bab5181a3a7..6d5690b4d17bc 100644 --- a/tests/cases/fourslash/syntacticClassificationsObjectLiteral.ts +++ b/tests/cases/fourslash/syntacticClassificationsObjectLiteral.ts @@ -11,7 +11,7 @@ //// v: v += v, ////}; -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("var"), c.identifier("v"), c.operator("="), c.numericLiteral("10e0"), c.punctuation(";"), c.keyword("var"), c.identifier("x"), c.operator("="), c.punctuation("{"), @@ -22,4 +22,19 @@ verify.syntacticClassificationsAre( c.identifier("var"), c.punctuation(":"), c.numericLiteral("5"), c.punctuation(","), c.identifier("void"), c.punctuation(":"), c.keyword("void"), c.numericLiteral("0"), c.punctuation(","), c.identifier("v"), c.punctuation(":"), c.identifier("v"), c.operator("+="), c.identifier("v"), c.punctuation(","), - c.punctuation("}"), c.punctuation(";")); \ No newline at end of file + c.punctuation("}"), c.punctuation(";")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "v"), + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("property.declaration", "p1"), + c2.semanticToken("property.declaration", "p2"), + c2.semanticToken("property.declaration", "any"), + c2.semanticToken("property.declaration", "function"), + c2.semanticToken("property.declaration", "var"), + c2.semanticToken("property.declaration", "void"), + c2.semanticToken("property.declaration", "v"), + c2.semanticToken("variable", "v"), + c2.semanticToken("variable", "v"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsTemplates1.ts b/tests/cases/fourslash/syntacticClassificationsTemplates1.ts index 6c27aab3f40e8..f4864d56e0b21 100644 --- a/tests/cases/fourslash/syntacticClassificationsTemplates1.ts +++ b/tests/cases/fourslash/syntacticClassificationsTemplates1.ts @@ -6,10 +6,18 @@ //// p2: `goodbye ${0} cruel ${0} world`, ////}; -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("var"), c.identifier("v"), c.operator("="), c.numericLiteral("10e0"), c.punctuation(";"), c.keyword("var"), c.identifier("x"), c.operator("="), c.punctuation("{"), c.identifier("p1"), c.punctuation(":"), c.stringLiteral("`hello world`"), c.punctuation(","), c.identifier("p2"), c.punctuation(":"), c.stringLiteral("`goodbye ${"), c.numericLiteral("0"), c.stringLiteral("} cruel ${"), c.numericLiteral("0"), c.stringLiteral("} world`"), c.punctuation(","), - c.punctuation("}"), c.punctuation(";")); \ No newline at end of file + c.punctuation("}"), c.punctuation(";")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "v"), + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("property.declaration", "p1"), + c2.semanticToken("property.declaration", "p2"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsTemplates2.ts b/tests/cases/fourslash/syntacticClassificationsTemplates2.ts index 25bb64e78ba37..fc3b655d0257c 100644 --- a/tests/cases/fourslash/syntacticClassificationsTemplates2.ts +++ b/tests/cases/fourslash/syntacticClassificationsTemplates2.ts @@ -4,8 +4,13 @@ ////`goodbye "${ `hello world` }" ////and ${ `good${ " " }riddance` }`; -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.keyword("var"), c.identifier("tiredOfCanonicalExamples"), c.operator("="), c.stringLiteral("`goodbye \"${"), c.stringLiteral("`hello world`"), - c.stringLiteral("}\" \nand ${"), c.stringLiteral("`good${"), c.stringLiteral("\" \""), c.stringLiteral("}riddance`"), c.stringLiteral("}`"), c.punctuation(";")); \ No newline at end of file + c.stringLiteral("}\" \nand ${"), c.stringLiteral("`good${"), c.stringLiteral("\" \""), c.stringLiteral("}riddance`"), c.stringLiteral("}`"), c.punctuation(";")); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "tiredOfCanonicalExamples"), +); diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash1.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash1.ts index 13c27669ad5e6..daebae06adc73 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash1.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash1.ts @@ -2,7 +2,7 @@ //// /// -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/// "), c.punctuation("<"), @@ -12,4 +12,6 @@ verify.syntacticClassificationsAre( c.operator("="), c.jsxAttributeStringLiteralValue("\"./module.ts\""), c.comment(" "), - c.punctuation("/>")); \ No newline at end of file + c.punctuation("/>")); + + diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash10.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash10.ts index d1bb681b92ee9..25fd4cf0380cc 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash10.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash10.ts @@ -2,7 +2,7 @@ //// /// -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/// "), c.punctuation("<"), @@ -16,4 +16,4 @@ verify.syntacticClassificationsAre( c.operator("="), c.jsxAttributeStringLiteralValue("\"node\""), c.comment(" "), - c.punctuation("/>")); \ No newline at end of file + c.punctuation("/>")); diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash13.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash13.ts index 182b01f756017..877827e9753da 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash13.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash13.ts @@ -2,7 +2,7 @@ //// /// trailing -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/// "), c.punctuation("<"), @@ -13,4 +13,4 @@ verify.syntacticClassificationsAre( c.jsxAttributeStringLiteralValue("\"./module.ts\""), c.comment(" "), c.punctuation("/>"), - c.comment(" trailing")); \ No newline at end of file + c.comment(" trailing")); diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash14.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash14.ts index a7d16fea02ceb..197bc0d1579e7 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash14.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash14.ts @@ -2,6 +2,6 @@ //// /// nonElement -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( - c.comment("/// nonElement")); \ No newline at end of file + c.comment("/// nonElement")); diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash15.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash15.ts index 8ec5ebbea5d08..382fc3054a44a 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash15.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash15.ts @@ -3,7 +3,7 @@ //// /// //// /// -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/// "), c.punctuation("<"), @@ -22,4 +22,4 @@ verify.syntacticClassificationsAre( c.operator("="), c.jsxAttributeStringLiteralValue("\"./module2.ts\""), c.comment(" "), - c.punctuation("/>")); \ No newline at end of file + c.punctuation("/>")); diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash16.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash16.ts index 1dbee22cb594f..6315f74101341 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash16.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash16.ts @@ -3,7 +3,7 @@ //// /// //// 1 -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/// "), c.punctuation("<"), @@ -14,4 +14,4 @@ verify.syntacticClassificationsAre( c.jsxAttributeStringLiteralValue("\"./module.ts\""), c.comment(" "), c.punctuation("/>"), - c.numericLiteral("1")); \ No newline at end of file + c.numericLiteral("1")); diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash17.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash17.ts index b95cdd6b6c0f0..3b11eec9df11f 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash17.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash17.ts @@ -2,6 +2,6 @@ //// /// Text -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( - c.comment("/// Text")); \ No newline at end of file + c.comment("/// Text")); diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash18.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash18.ts index 148b17f6e4565..90dbd2b1d7678 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash18.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash18.ts @@ -2,6 +2,6 @@ //// /// Text -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( - c.comment("/// Text")); \ No newline at end of file + c.comment("/// Text")); diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash2.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash2.ts index 278d66dcb4a7f..618a5216537ba 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash2.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash2.ts @@ -2,7 +2,7 @@ //// /// -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("///"), c.punctuation("<"), @@ -13,4 +13,6 @@ verify.syntacticClassificationsAre( c.operator("="), c.comment(" "), c.jsxAttributeStringLiteralValue("\"./module.ts\""), - c.punctuation("/>")); \ No newline at end of file + c.punctuation("/>")); + + diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash3.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash3.ts index 11d3b2462be5b..1dea4b6968012 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash3.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash3.ts @@ -2,7 +2,7 @@ //// /// -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( c.comment("/// "), c.punctuation("<"), @@ -16,4 +16,6 @@ verify.syntacticClassificationsAre( c.operator("="), c.jsxAttributeStringLiteralValue("\"node\""), c.comment(" "), - c.punctuation("/>")); \ No newline at end of file + c.punctuation("/>")); + + diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash4.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash4.ts index d407bcee5032b..9d6267ecbcda1 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash4.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash4.ts @@ -2,6 +2,8 @@ //// /// < -var c = classification; +const c = classification("original"); verify.syntacticClassificationsAre( - c.comment("/// <")); // Don't classify until we recognize the element name \ No newline at end of file + c.comment("/// <")); // Don't classify until we recognize the element name + + diff --git a/tests/cases/fourslash/syntacticClassificationsTripleSlash5.ts b/tests/cases/fourslash/syntacticClassificationsTripleSlash5.ts index c6f1de38f2890..b3bab4bde6497 100644 --- a/tests/cases/fourslash/syntacticClassificationsTripleSlash5.ts +++ b/tests/cases/fourslash/syntacticClassificationsTripleSlash5.ts @@ -2,8 +2,10 @@ //// /// Date: Wed, 17 Jun 2020 12:39:39 -0400 Subject: [PATCH 04/14] Port over the semantic classification tests --- src/harness/fourslashImpl.ts | 6 ++-- ...icModernClassificationCallableVariables.ts | 31 +++++++++++++++++++ ...cModernClassificationCallableVariables2.ts | 25 +++++++++++++++ ...nticModernClassificationClassProperties.ts | 21 +++++++++++++ ...ticModernClassificationConstructorTypes.ts | 13 ++++++++ .../semanticModernClassificationFunctions.ts | 19 ++++++++++++ .../semanticModernClassificationInterfaces.ts | 21 +++++++++++++ .../semanticModernClassificationMembers.ts | 24 ++++++++++++++ ...ticModernClassificationObjectProperties.ts | 13 ++++++++ .../semanticModernClassificationVariables.ts | 19 ++++++++++++ 10 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/semanticModernClassificationCallableVariables.ts create mode 100644 tests/cases/fourslash/semanticModernClassificationCallableVariables2.ts create mode 100644 tests/cases/fourslash/semanticModernClassificationClassProperties.ts create mode 100644 tests/cases/fourslash/semanticModernClassificationConstructorTypes.ts create mode 100644 tests/cases/fourslash/semanticModernClassificationFunctions.ts create mode 100644 tests/cases/fourslash/semanticModernClassificationInterfaces.ts create mode 100644 tests/cases/fourslash/semanticModernClassificationMembers.ts create mode 100644 tests/cases/fourslash/semanticModernClassificationObjectProperties.ts create mode 100644 tests/cases/fourslash/semanticModernClassificationVariables.ts diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 9bcc59667514c..4bfd8a644aaa6 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2567,15 +2567,15 @@ namespace FourSlash { }); replacement.push(");"); - throw new Error("You need to change the source code of fourslash test to use replaceWithSemanticClassifications"); + // throw new Error("You need to change the source code of fourslash test to use replaceWithSemanticClassifications"); + - /** const fs = require("fs"); const testfilePath = this.originalInputFileName.slice(1); const testfile = fs.readFileSync(testfilePath, "utf8"); const newfile = testfile.replace("verify.replaceWithSemanticClassifications(\"2020\")", replacement.join("\n")); fs.writeFileSync(testfilePath, newfile); - */ + } diff --git a/tests/cases/fourslash/semanticModernClassificationCallableVariables.ts b/tests/cases/fourslash/semanticModernClassificationCallableVariables.ts new file mode 100644 index 0000000000000..c98204baf8c75 --- /dev/null +++ b/tests/cases/fourslash/semanticModernClassificationCallableVariables.ts @@ -0,0 +1,31 @@ +//// class A { onEvent: () => void; } +//// const x = new A().onEvent; +//// const match = (s: any) => x(); +//// const other = match; +//// match({ other }); +//// interface B = { (): string; }; var b: B +//// var s: String; +//// var t: { (): string; foo: string}; + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "A"), + c2.semanticToken("member.declaration", "onEvent"), + c2.semanticToken("function.declaration.readonly", "x"), + c2.semanticToken("class", "A"), + c2.semanticToken("member", "onEvent"), + c2.semanticToken("function.declaration.readonly", "match"), + c2.semanticToken("parameter.declaration", "s"), + c2.semanticToken("function.readonly", "x"), + c2.semanticToken("function.declaration.readonly", "other"), + c2.semanticToken("function.readonly", "match"), + c2.semanticToken("function.readonly", "match"), + c2.semanticToken("member.declaration", "other"), + c2.semanticToken("interface.declaration", "B"), + c2.semanticToken("variable.declaration", "b"), + c2.semanticToken("interface", "B"), + c2.semanticToken("variable.declaration", "s"), + c2.semanticToken("interface.defaultLibrary", "String"), + c2.semanticToken("variable.declaration", "t"), + c2.semanticToken("property.declaration", "foo"), +);; \ No newline at end of file diff --git a/tests/cases/fourslash/semanticModernClassificationCallableVariables2.ts b/tests/cases/fourslash/semanticModernClassificationCallableVariables2.ts new file mode 100644 index 0000000000000..d108398c02327 --- /dev/null +++ b/tests/cases/fourslash/semanticModernClassificationCallableVariables2.ts @@ -0,0 +1,25 @@ +//// import "node"; +//// var fs = require("fs") +//// require.resolve('react'); +//// require.resolve.paths; +//// interface LanguageMode { getFoldingRanges?: (d: string) => number[]; }; +//// function (mode: LanguageMode | undefined) { if (mode && mode.getFoldingRanges) { return mode.getFoldingRanges('a'); }}; +//// function b(a: () => void) { a(); }; + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "fs"), + c2.semanticToken("interface.declaration", "LanguageMode"), + c2.semanticToken("member.declaration", "getFoldingRanges"), + c2.semanticToken("parameter.declaration", "d"), + c2.semanticToken("parameter.declaration", "mode"), + c2.semanticToken("interface", "LanguageMode"), + c2.semanticToken("parameter", "mode"), + c2.semanticToken("parameter", "mode"), + c2.semanticToken("member", "getFoldingRanges"), + c2.semanticToken("parameter", "mode"), + c2.semanticToken("member", "getFoldingRanges"), + c2.semanticToken("function.declaration", "b"), + c2.semanticToken("function.declaration", "a"), + c2.semanticToken("function", "a"), +);; \ No newline at end of file diff --git a/tests/cases/fourslash/semanticModernClassificationClassProperties.ts b/tests/cases/fourslash/semanticModernClassificationClassProperties.ts new file mode 100644 index 0000000000000..e2c3318dc2e74 --- /dev/null +++ b/tests/cases/fourslash/semanticModernClassificationClassProperties.ts @@ -0,0 +1,21 @@ +//// class A { +//// private y: number; +//// constructor(public x : number, _y : number) { this.y = _y; } +//// get z() : number { return this.x + this.y; } +//// set a(v: number) { } +//// } + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "A"), + c2.semanticToken("property.declaration", "y"), + c2.semanticToken("parameter.declaration", "x"), + c2.semanticToken("parameter.declaration", "_y"), + c2.semanticToken("property", "y"), + c2.semanticToken("parameter", "_y"), + c2.semanticToken("property.declaration", "z"), + c2.semanticToken("property", "x"), + c2.semanticToken("property", "y"), + c2.semanticToken("property.declaration", "a"), + c2.semanticToken("parameter.declaration", "v"), +);; \ No newline at end of file diff --git a/tests/cases/fourslash/semanticModernClassificationConstructorTypes.ts b/tests/cases/fourslash/semanticModernClassificationConstructorTypes.ts new file mode 100644 index 0000000000000..25ef45b29b628 --- /dev/null +++ b/tests/cases/fourslash/semanticModernClassificationConstructorTypes.ts @@ -0,0 +1,13 @@ +//// Object.create(null); +//// const x = Promise.resolve(Number.MAX_VALUE); +//// if (x instanceof Promise) {} + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.defaultLibrary", "Object"), + c2.semanticToken("member.defaultLibrary", "create"), + c2.semanticToken("variable.declaration.readonly", "x"), + c2.semanticToken("class.defaultLibrary", "Number"), + c2.semanticToken("property.readonly.defaultLibrary", "MAX_VALUE"), + c2.semanticToken("variable.readonly", "x"), +);; \ No newline at end of file diff --git a/tests/cases/fourslash/semanticModernClassificationFunctions.ts b/tests/cases/fourslash/semanticModernClassificationFunctions.ts new file mode 100644 index 0000000000000..730fe9aad206c --- /dev/null +++ b/tests/cases/fourslash/semanticModernClassificationFunctions.ts @@ -0,0 +1,19 @@ +//// function foo(p1) { +//// return foo(Math.abs(p1)) +//// } +//// `/${window.location}`.split("/").forEach(s => foo(s)); + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("function.declaration", "foo"), + c2.semanticToken("parameter.declaration", "p1"), + c2.semanticToken("function", "foo"), + c2.semanticToken("variable.defaultLibrary", "Math"), + c2.semanticToken("member.defaultLibrary", "abs"), + c2.semanticToken("parameter", "p1"), + c2.semanticToken("member.defaultLibrary", "split"), + c2.semanticToken("member.defaultLibrary", "forEach"), + c2.semanticToken("parameter.declaration", "s"), + c2.semanticToken("function", "foo"), + c2.semanticToken("parameter", "s"), +);; \ No newline at end of file diff --git a/tests/cases/fourslash/semanticModernClassificationInterfaces.ts b/tests/cases/fourslash/semanticModernClassificationInterfaces.ts new file mode 100644 index 0000000000000..a2967fd213a59 --- /dev/null +++ b/tests/cases/fourslash/semanticModernClassificationInterfaces.ts @@ -0,0 +1,21 @@ +//// interface Pos { x: number, y: number }; +//// const p = { x: 1, y: 2 } as Pos; +//// const foo = (o: Pos) => o.x + o.y; + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("interface.declaration", "Pos"), + c2.semanticToken("property.declaration", "x"), + c2.semanticToken("property.declaration", "y"), + c2.semanticToken("variable.declaration.readonly", "p"), + c2.semanticToken("property.declaration", "x"), + c2.semanticToken("property.declaration", "y"), + c2.semanticToken("interface", "Pos"), + c2.semanticToken("function.declaration.readonly", "foo"), + c2.semanticToken("parameter.declaration", "o"), + c2.semanticToken("interface", "Pos"), + c2.semanticToken("parameter", "o"), + c2.semanticToken("property", "x"), + c2.semanticToken("parameter", "o"), + c2.semanticToken("property", "y"), +);; \ No newline at end of file diff --git a/tests/cases/fourslash/semanticModernClassificationMembers.ts b/tests/cases/fourslash/semanticModernClassificationMembers.ts new file mode 100644 index 0000000000000..0492b24955f67 --- /dev/null +++ b/tests/cases/fourslash/semanticModernClassificationMembers.ts @@ -0,0 +1,24 @@ +//// class A { +//// static x = 9; +//// f = 9; +//// async m() { return A.x + await this.m(); }; +//// get s() { return this.f; +//// static t() { return new A().f; }; +//// constructor() {} +//// } + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "A"), + c2.semanticToken("property.declaration.static", "x"), + c2.semanticToken("property.declaration", "f"), + c2.semanticToken("member.declaration.async", "m"), + c2.semanticToken("class", "A"), + c2.semanticToken("property.static", "x"), + c2.semanticToken("member.async", "m"), + c2.semanticToken("property.declaration", "s"), + c2.semanticToken("property", "f"), + c2.semanticToken("member.declaration.static", "t"), + c2.semanticToken("class", "A"), + c2.semanticToken("property", "f"), +);; \ No newline at end of file diff --git a/tests/cases/fourslash/semanticModernClassificationObjectProperties.ts b/tests/cases/fourslash/semanticModernClassificationObjectProperties.ts new file mode 100644 index 0000000000000..31dc8f30f10f4 --- /dev/null +++ b/tests/cases/fourslash/semanticModernClassificationObjectProperties.ts @@ -0,0 +1,13 @@ +//// let x = 1, y = 1; +//// const a1 = { e: 1 }; +//// var a2 = { x }; + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("variable.declaration", "y"), + c2.semanticToken("variable.declaration.readonly", "a1"), + c2.semanticToken("property.declaration", "e"), + c2.semanticToken("variable.declaration", "a2"), + c2.semanticToken("property.declaration", "x"), +);; \ No newline at end of file diff --git a/tests/cases/fourslash/semanticModernClassificationVariables.ts b/tests/cases/fourslash/semanticModernClassificationVariables.ts new file mode 100644 index 0000000000000..6c4e9c1567187 --- /dev/null +++ b/tests/cases/fourslash/semanticModernClassificationVariables.ts @@ -0,0 +1,19 @@ +//// var x = 9, y1 = [x]; +//// try { +//// for (const s of y1) { x = s } +//// } catch (e) { +//// throw y1; +//// } + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("variable.declaration", "y1"), + c2.semanticToken("variable", "x"), + c2.semanticToken("variable.declaration.readonly.local", "s"), + c2.semanticToken("variable", "y1"), + c2.semanticToken("variable", "x"), + c2.semanticToken("variable.readonly.local", "s"), + c2.semanticToken("variable.declaration.local", "e"), + c2.semanticToken("variable", "y1"), +);; \ No newline at end of file From de080b63bf008e6f1b2fcab6cbe730adfe81fdcf Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Fri, 19 Jun 2020 07:52:41 -0400 Subject: [PATCH 05/14] Update baselines --- .../reference/api/tsserverlibrary.d.ts | 27 ++++++++++++++++--- tests/baselines/reference/api/typescript.d.ts | 27 ++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 72100cfc28940..dceffc5c1d5af 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3723,6 +3723,10 @@ declare namespace ts { span: TextSpan; newLength: number; } + export enum SemanticClassificationFormat { + Original = "original", + TwentyTwenty = "2020" + } export interface SyntaxList extends Node { kind: SyntaxKind.SyntaxList; _children: Node[]; @@ -5389,11 +5393,21 @@ declare namespace ts { */ getCompilerOptionsDiagnostics(): Diagnostic[]; /** @deprecated Use getEncodedSyntacticClassifications instead. */ - getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; + getSyntacticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[]; /** @deprecated Use getEncodedSemanticClassifications instead. */ - getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; + getSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[]; + /** Encoded as triples of [start, length, ClassificationType]. */ getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; - getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; + /** + * Gets semantic highlights information for a particular file. Has two formats, an older + * version used by VS and a format used by VS Code. + * + * @param fileName The path to the file + * @param position A text span to return results within + * @param format Which format to use, defaults to "original" + * @returns a number array encoded as triples of [start, length, ClassificationType, ...]. + */ + getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): Classifications; /** * Gets completion entries at a particular position in a file. * @@ -5546,7 +5560,7 @@ declare namespace ts { } interface ClassifiedSpan { textSpan: TextSpan; - classificationType: ClassificationTypeNames; + classificationType: ClassificationTypeNames | number; } /** * Navigation bar interface designed for visual studio's dual-column layout. @@ -6208,6 +6222,11 @@ declare namespace ts { /** The classifier is used for syntactic highlighting in editors via the TSServer */ function createClassifier(): Classifier; } +declare namespace ts.classifier.vscode { + /** This is mainly used internally for testing */ + function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[]; + function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications; +} declare namespace ts { interface DocumentHighlights { fileName: string; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8ed116b6b1d38..b3ef5679c060f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3723,6 +3723,10 @@ declare namespace ts { span: TextSpan; newLength: number; } + export enum SemanticClassificationFormat { + Original = "original", + TwentyTwenty = "2020" + } export interface SyntaxList extends Node { kind: SyntaxKind.SyntaxList; _children: Node[]; @@ -5389,11 +5393,21 @@ declare namespace ts { */ getCompilerOptionsDiagnostics(): Diagnostic[]; /** @deprecated Use getEncodedSyntacticClassifications instead. */ - getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; + getSyntacticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[]; /** @deprecated Use getEncodedSemanticClassifications instead. */ - getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; + getSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[]; + /** Encoded as triples of [start, length, ClassificationType]. */ getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; - getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; + /** + * Gets semantic highlights information for a particular file. Has two formats, an older + * version used by VS and a format used by VS Code. + * + * @param fileName The path to the file + * @param position A text span to return results within + * @param format Which format to use, defaults to "original" + * @returns a number array encoded as triples of [start, length, ClassificationType, ...]. + */ + getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): Classifications; /** * Gets completion entries at a particular position in a file. * @@ -5546,7 +5560,7 @@ declare namespace ts { } interface ClassifiedSpan { textSpan: TextSpan; - classificationType: ClassificationTypeNames; + classificationType: ClassificationTypeNames | number; } /** * Navigation bar interface designed for visual studio's dual-column layout. @@ -6208,6 +6222,11 @@ declare namespace ts { /** The classifier is used for syntactic highlighting in editors via the TSServer */ function createClassifier(): Classifier; } +declare namespace ts.classifier.vscode { + /** This is mainly used internally for testing */ + function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[]; + function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications; +} declare namespace ts { interface DocumentHighlights { fileName: string; From 455ed22d945038f6be8fb469a63a1092ec875a96 Mon Sep 17 00:00:00 2001 From: Orta Date: Mon, 22 Jun 2020 15:06:26 -0400 Subject: [PATCH 06/14] Update src/harness/fourslashImpl.ts Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> --- src/harness/fourslashImpl.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 4bfd8a644aaa6..54f7405a88873 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2487,8 +2487,7 @@ namespace FourSlash { const typeIdx = getTokenTypeFromClassification(classification) || 0; const modSet = getTokenModifierFromClassification(classification); - const tokenClassifiction = [tokenTypes[typeIdx], ...tokenModifiers.filter((_, i) => modSet & 1 << i)].join("."); - return tokenClassifiction; + return [tokenTypes[typeIdx], ...tokenModifiers.filter((_, i) => modSet & 1 << i)].join("."); } private verifyClassifications(expected: { classificationType: string | number, text?: string; textSpan?: TextSpan }[], actual: ts.ClassifiedSpan[], sourceFileText: string) { From a25badbcbc0e6c91b0b00b723d29ae0f404f6eae Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Mon, 22 Jun 2020 15:33:19 -0400 Subject: [PATCH 07/14] Handle feedback from #39119 --- src/compiler/types.ts | 5 -- src/harness/fourslashImpl.ts | 56 +++++++++---------- ...{classifierVscode.ts => classifier2020.ts} | 3 +- src/services/services.ts | 4 +- src/services/tsconfig.json | 2 +- src/services/types.ts | 5 ++ 6 files changed, 37 insertions(+), 38 deletions(-) rename src/services/{classifierVscode.ts => classifier2020.ts} (96%) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 557845466cf2e..05ad94c7599f1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7766,11 +7766,6 @@ namespace ts { newLength: number; } - export const enum SemanticClassificationFormat { - Original = "original", - TwentyTwenty = "2020" - } - /* @internal */ export interface DiagnosticCollection { // Adds a diagnostic to this diagnostic collection. diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 54f7405a88873..7603dfb58602a 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2451,37 +2451,37 @@ namespace FourSlash { private classificationToIdentifier(classification: number){ const tokenTypes: string[] = []; - tokenTypes[ts.classifier.vscode.TokenType.class] = "class"; - tokenTypes[ts.classifier.vscode.TokenType.enum] = "enum"; - tokenTypes[ts.classifier.vscode.TokenType.interface] = "interface"; - tokenTypes[ts.classifier.vscode.TokenType.namespace] = "namespace"; - tokenTypes[ts.classifier.vscode.TokenType.typeParameter] = "typeParameter"; - tokenTypes[ts.classifier.vscode.TokenType.type] = "type"; - tokenTypes[ts.classifier.vscode.TokenType.parameter] = "parameter"; - tokenTypes[ts.classifier.vscode.TokenType.variable] = "variable"; - tokenTypes[ts.classifier.vscode.TokenType.enumMember] = "enumMember"; - tokenTypes[ts.classifier.vscode.TokenType.property] = "property"; - tokenTypes[ts.classifier.vscode.TokenType.function] = "function"; - tokenTypes[ts.classifier.vscode.TokenType.member] = "member"; + tokenTypes[ts.classifier.modern.TokenType.class] = "class"; + tokenTypes[ts.classifier.modern.TokenType.enum] = "enum"; + tokenTypes[ts.classifier.modern.TokenType.interface] = "interface"; + tokenTypes[ts.classifier.modern.TokenType.namespace] = "namespace"; + tokenTypes[ts.classifier.modern.TokenType.typeParameter] = "typeParameter"; + tokenTypes[ts.classifier.modern.TokenType.type] = "type"; + tokenTypes[ts.classifier.modern.TokenType.parameter] = "parameter"; + tokenTypes[ts.classifier.modern.TokenType.variable] = "variable"; + tokenTypes[ts.classifier.modern.TokenType.enumMember] = "enumMember"; + tokenTypes[ts.classifier.modern.TokenType.property] = "property"; + tokenTypes[ts.classifier.modern.TokenType.function] = "function"; + tokenTypes[ts.classifier.modern.TokenType.member] = "member"; const tokenModifiers: string[] = []; - tokenModifiers[ts.classifier.vscode.TokenModifier.async] = "async"; - tokenModifiers[ts.classifier.vscode.TokenModifier.declaration] = "declaration"; - tokenModifiers[ts.classifier.vscode.TokenModifier.readonly] = "readonly"; - tokenModifiers[ts.classifier.vscode.TokenModifier.static] = "static"; - tokenModifiers[ts.classifier.vscode.TokenModifier.local] = "local"; - tokenModifiers[ts.classifier.vscode.TokenModifier.defaultLibrary] = "defaultLibrary"; + tokenModifiers[ts.classifier.modern.TokenModifier.async] = "async"; + tokenModifiers[ts.classifier.modern.TokenModifier.declaration] = "declaration"; + tokenModifiers[ts.classifier.modern.TokenModifier.readonly] = "readonly"; + tokenModifiers[ts.classifier.modern.TokenModifier.static] = "static"; + tokenModifiers[ts.classifier.modern.TokenModifier.local] = "local"; + tokenModifiers[ts.classifier.modern.TokenModifier.defaultLibrary] = "defaultLibrary"; function getTokenTypeFromClassification(tsClassification: number): number | undefined { - if (tsClassification > ts.classifier.vscode.TokenEncodingConsts.modifierMask) { - return (tsClassification >> ts.classifier.vscode.TokenEncodingConsts.typeOffset) - 1; + if (tsClassification > ts.classifier.modern.TokenEncodingConsts.modifierMask) { + return (tsClassification >> ts.classifier.modern.TokenEncodingConsts.typeOffset) - 1; } return undefined; } function getTokenModifierFromClassification(tsClassification: number) { - return tsClassification & ts.classifier.vscode.TokenEncodingConsts.modifierMask; + return tsClassification & ts.classifier.modern.TokenEncodingConsts.modifierMask; } const typeIdx = getTokenTypeFromClassification(classification) || 0; @@ -2566,15 +2566,13 @@ namespace FourSlash { }); replacement.push(");"); - // throw new Error("You need to change the source code of fourslash test to use replaceWithSemanticClassifications"); - - - const fs = require("fs"); - const testfilePath = this.originalInputFileName.slice(1); - const testfile = fs.readFileSync(testfilePath, "utf8"); - const newfile = testfile.replace("verify.replaceWithSemanticClassifications(\"2020\")", replacement.join("\n")); - fs.writeFileSync(testfilePath, newfile); + throw new Error("You need to change the source code of fourslash test to use replaceWithSemanticClassifications"); + // const fs = require("fs"); + // const testfilePath = this.originalInputFileName.slice(1); + // const testfile = fs.readFileSync(testfilePath, "utf8"); + // const newfile = testfile.replace("verify.replaceWithSemanticClassifications(\"2020\")", replacement.join("\n")); + // fs.writeFileSync(testfilePath, newfile); } diff --git a/src/services/classifierVscode.ts b/src/services/classifier2020.ts similarity index 96% rename from src/services/classifierVscode.ts rename to src/services/classifier2020.ts index 2d3a382cc9574..f8f02c13b2bcb 100644 --- a/src/services/classifierVscode.ts +++ b/src/services/classifier2020.ts @@ -1,4 +1,5 @@ -namespace ts.classifier.vscode { +/** @internal */ +namespace ts.classifier.modern { /** @internal */ export enum TokenEncodingConsts { diff --git a/src/services/services.ts b/src/services/services.ts index 04502be4b4fbf..f4b300059aea9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1809,7 +1809,7 @@ namespace ts { return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); } else { - return classifier.vscode.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); + return classifier.modern.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); } } @@ -1825,7 +1825,7 @@ namespace ts { return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); } else { - return classifier.vscode.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); + return classifier.modern.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); } } diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index fb62e2a953e81..8e660d9f2854e 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -12,7 +12,7 @@ "types.ts", "utilities.ts", "classifier.ts", - "classifierVscode.ts", + "classifier2020.ts", "stringCompletions.ts", "completions.ts", "documentHighlights.ts", diff --git a/src/services/types.ts b/src/services/types.ts index 3fbaa2ea0d1ad..401b0ac202830 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -313,6 +313,11 @@ namespace ts { export type WithMetadata = T & { metadata?: unknown; }; + export const enum SemanticClassificationFormat { + Original = "original", + TwentyTwenty = "2020" + } + // // Public services of a language service instance associated // with a language service host instance From 8c9a4f545b98af1cfc4c67acba97aca9cb1a97fb Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Mon, 22 Jun 2020 15:34:03 -0400 Subject: [PATCH 08/14] Consistent formatting in the 2020 classifier --- src/services/classifier2020.ts | 426 ++++++++++++++++----------------- 1 file changed, 213 insertions(+), 213 deletions(-) diff --git a/src/services/classifier2020.ts b/src/services/classifier2020.ts index f8f02c13b2bcb..5a482f47fe216 100644 --- a/src/services/classifier2020.ts +++ b/src/services/classifier2020.ts @@ -1,257 +1,257 @@ /** @internal */ namespace ts.classifier.modern { - /** @internal */ - export enum TokenEncodingConsts { - typeOffset = 8, - modifierMask = (1 << typeOffset) - 1 - } + /** @internal */ + export enum TokenEncodingConsts { + typeOffset = 8, + modifierMask = (1 << typeOffset) - 1 + } - /** @internal */ - export enum TokenType { - class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member, _ - } + /** @internal */ + export enum TokenType { + class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member, _ + } - /** @internal */ - export enum TokenModifier { - declaration, static, async, readonly, defaultLibrary, local, _ - } + /** @internal */ + export enum TokenModifier { + declaration, static, async, readonly, defaultLibrary, local, _ + } - /** This is mainly used internally for testing */ - export function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[] { - const classifications = getEncodedSemanticClassifications(program, _cancellationToken, sourceFile, span); + /** This is mainly used internally for testing */ + export function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[] { + const classifications = getEncodedSemanticClassifications(program, _cancellationToken, sourceFile, span); - Debug.assert(classifications.spans.length % 3 === 0); - const dense = classifications.spans; - const result: ClassifiedSpan[] = []; - for (let i = 0; i < dense.length; i += 3) { - result.push({ - textSpan: createTextSpan(dense[i], dense[i + 1]), - classificationType: dense[i + 2] - }); - } + Debug.assert(classifications.spans.length % 3 === 0); + const dense = classifications.spans; + const result: ClassifiedSpan[] = []; + for (let i = 0; i < dense.length; i += 3) { + result.push({ + textSpan: createTextSpan(dense[i], dense[i + 1]), + classificationType: dense[i + 2] + }); + } - return result; - } + return result; + } - export function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications { - return { - spans: getSemanticTokens(program, sourceFile, span), - endOfLineState: EndOfLineState.None - }; - } + export function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications { + return { + spans: getSemanticTokens(program, sourceFile, span), + endOfLineState: EndOfLineState.None + }; + } - function getSemanticTokens(program: Program, sourceFile: SourceFile, span: TextSpan): number[] { - const resultTokens: number[] = []; + function getSemanticTokens(program: Program, sourceFile: SourceFile, span: TextSpan): number[] { + const resultTokens: number[] = []; - const collector = (node: Node, typeIdx: number, modifierSet: number) => { - resultTokens.push(node.getStart(), node.getWidth(), ((typeIdx + 1) << TokenEncodingConsts.typeOffset) + modifierSet); - }; + const collector = (node: Node, typeIdx: number, modifierSet: number) => { + resultTokens.push(node.getStart(), node.getWidth(), ((typeIdx + 1) << TokenEncodingConsts.typeOffset) + modifierSet); + }; - if (program && sourceFile) { - collectTokens(program, sourceFile, span, collector); + if (program && sourceFile) { + collectTokens(program, sourceFile, span, collector); + } + return resultTokens; } - return resultTokens; - } - - function collectTokens(program: Program, sourceFile: SourceFile, span: TextSpan, collector: (node: Node, tokenType: number, tokenModifier: number) => void) { - const typeChecker = program.getTypeChecker(); - let inJSXElement = false; + function collectTokens(program: Program, sourceFile: SourceFile, span: TextSpan, collector: (node: Node, tokenType: number, tokenModifier: number) => void) { + const typeChecker = program.getTypeChecker(); - function visit(node: Node) { - if (!node || !textSpanIntersectsWith(span, node.pos, node.getFullWidth()) || node.getFullWidth() === 0) { - return; - } - const prevInJSXElement = inJSXElement; - if (isJsxElement(node) || isJsxSelfClosingElement(node)) { - inJSXElement = true; - } - if (isJsxExpression(node)) { - inJSXElement = false; - } + let inJSXElement = false; - if (isIdentifier(node) && !inJSXElement && !inImportClause(node)) { - let symbol = typeChecker.getSymbolAtLocation(node); - if (symbol) { - if (symbol.flags & SymbolFlags.Alias) { - symbol = typeChecker.getAliasedSymbol(symbol); - } - let typeIdx = classifySymbol(symbol, getMeaningFromLocation(node)); - if (typeIdx !== undefined) { - let modifierSet = 0; - if (node.parent) { - const parentIsDeclaration = (isBindingElement(node.parent) || tokenFromDeclarationMapping[node.parent.kind] === typeIdx); - if (parentIsDeclaration && (node.parent).name === node) { - modifierSet = 1 << TokenModifier.declaration; - } + function visit(node: Node) { + if (!node || !textSpanIntersectsWith(span, node.pos, node.getFullWidth()) || node.getFullWidth() === 0) { + return; } - - // property declaration in constructor - if (typeIdx === TokenType.parameter && isRightSideOfQualifiedNameOrPropertyAccess(node)) { - typeIdx = TokenType.property; + const prevInJSXElement = inJSXElement; + if (isJsxElement(node) || isJsxSelfClosingElement(node)) { + inJSXElement = true; + } + if (isJsxExpression(node)) { + inJSXElement = false; } - typeIdx = reclassifyByType(typeChecker, node, typeIdx); + if (isIdentifier(node) && !inJSXElement && !inImportClause(node)) { + let symbol = typeChecker.getSymbolAtLocation(node); + if (symbol) { + if (symbol.flags & SymbolFlags.Alias) { + symbol = typeChecker.getAliasedSymbol(symbol); + } + let typeIdx = classifySymbol(symbol, getMeaningFromLocation(node)); + if (typeIdx !== undefined) { + let modifierSet = 0; + if (node.parent) { + const parentIsDeclaration = (isBindingElement(node.parent) || tokenFromDeclarationMapping[node.parent.kind] === typeIdx); + if (parentIsDeclaration && (node.parent).name === node) { + modifierSet = 1 << TokenModifier.declaration; + } + } + + // property declaration in constructor + if (typeIdx === TokenType.parameter && isRightSideOfQualifiedNameOrPropertyAccess(node)) { + typeIdx = TokenType.property; + } + + typeIdx = reclassifyByType(typeChecker, node, typeIdx); - const decl = symbol.valueDeclaration; - if (decl) { - const modifiers = getCombinedModifierFlags(decl); - const nodeFlags = getCombinedNodeFlags(decl); - if (modifiers & ModifierFlags.Static) { - modifierSet |= 1 << TokenModifier.static; - } - if (modifiers & ModifierFlags.Async) { - modifierSet |= 1 << TokenModifier.async; - } - if (typeIdx !== TokenType.class && typeIdx !== TokenType.interface) { - if ((modifiers & ModifierFlags.Readonly) || (nodeFlags & NodeFlags.Const) || (symbol.getFlags() & SymbolFlags.EnumMember)) { - modifierSet |= 1 << TokenModifier.readonly; + const decl = symbol.valueDeclaration; + if (decl) { + const modifiers = getCombinedModifierFlags(decl); + const nodeFlags = getCombinedNodeFlags(decl); + if (modifiers & ModifierFlags.Static) { + modifierSet |= 1 << TokenModifier.static; + } + if (modifiers & ModifierFlags.Async) { + modifierSet |= 1 << TokenModifier.async; + } + if (typeIdx !== TokenType.class && typeIdx !== TokenType.interface) { + if ((modifiers & ModifierFlags.Readonly) || (nodeFlags & NodeFlags.Const) || (symbol.getFlags() & SymbolFlags.EnumMember)) { + modifierSet |= 1 << TokenModifier.readonly; + } + } + if ((typeIdx === TokenType.variable || typeIdx === TokenType.function) && isLocalDeclaration(decl, sourceFile)) { + modifierSet |= 1 << TokenModifier.local; + } + if (program.isSourceFileDefaultLibrary(decl.getSourceFile())) { + modifierSet |= 1 << TokenModifier.defaultLibrary; + } + } + else if (symbol.declarations && symbol.declarations.some(d => program.isSourceFileDefaultLibrary(d.getSourceFile()))) { + modifierSet |= 1 << TokenModifier.defaultLibrary; + } + + collector(node, typeIdx, modifierSet); + + } } - } - if ((typeIdx === TokenType.variable || typeIdx === TokenType.function) && isLocalDeclaration(decl, sourceFile)) { - modifierSet |= 1 << TokenModifier.local; - } - if (program.isSourceFileDefaultLibrary(decl.getSourceFile())) { - modifierSet |= 1 << TokenModifier.defaultLibrary; - } - } - else if (symbol.declarations && symbol.declarations.some(d => program.isSourceFileDefaultLibrary(d.getSourceFile()))) { - modifierSet |= 1 << TokenModifier.defaultLibrary; } + forEachChild(node, visit); - collector(node, typeIdx, modifierSet); - - } + inJSXElement = prevInJSXElement; } - } - forEachChild(node, visit); - - inJSXElement = prevInJSXElement; + visit(sourceFile); } - visit(sourceFile); - } - function classifySymbol(symbol: Symbol, meaning: SemanticMeaning): TokenType | undefined { - const flags = symbol.getFlags(); - if (flags & SymbolFlags.Class) { - return TokenType.class; - } - else if (flags & SymbolFlags.Enum) { - return TokenType.enum; - } - else if (flags & SymbolFlags.TypeAlias) { - return TokenType.type; - } - else if (flags & SymbolFlags.Interface) { - if (meaning & SemanticMeaning.Type) { - return TokenType.interface; - } - } - else if (flags & SymbolFlags.TypeParameter) { - return TokenType.typeParameter; + function classifySymbol(symbol: Symbol, meaning: SemanticMeaning): TokenType | undefined { + const flags = symbol.getFlags(); + if (flags & SymbolFlags.Class) { + return TokenType.class; + } + else if (flags & SymbolFlags.Enum) { + return TokenType.enum; + } + else if (flags & SymbolFlags.TypeAlias) { + return TokenType.type; + } + else if (flags & SymbolFlags.Interface) { + if (meaning & SemanticMeaning.Type) { + return TokenType.interface; + } + } + else if (flags & SymbolFlags.TypeParameter) { + return TokenType.typeParameter; + } + let decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; + if (decl && isBindingElement(decl)) { + decl = getDeclarationForBindingElement(decl); + } + return decl && tokenFromDeclarationMapping[decl.kind]; } - let decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; - if (decl && isBindingElement(decl)) { - decl = getDeclarationForBindingElement(decl); + + function reclassifyByType(typeChecker: TypeChecker, node: Node, typeIdx: TokenType): TokenType { + // type based classifications + if (typeIdx === TokenType.variable || typeIdx === TokenType.property || typeIdx === TokenType.parameter) { + const type = typeChecker.getTypeAtLocation(node); + if (type) { + const test = (condition: (type: Type) => boolean) => { + return condition(type) || type.isUnion() && type.types.some(condition); + }; + if (typeIdx !== TokenType.parameter && test(t => t.getConstructSignatures().length > 0)) { + return TokenType.class; + } + if (test(t => t.getCallSignatures().length > 0) && !test(t => t.getProperties().length > 0) || isExpressionInCallExpression(node)) { + return typeIdx === TokenType.property ? TokenType.member : TokenType.function; + } + } + } + return typeIdx; } - return decl && tokenFromDeclarationMapping[decl.kind]; - } - function reclassifyByType(typeChecker: TypeChecker, node: Node, typeIdx: TokenType): TokenType { - // type based classifications - if (typeIdx === TokenType.variable || typeIdx === TokenType.property || typeIdx === TokenType.parameter) { - const type = typeChecker.getTypeAtLocation(node); - if (type) { - const test = (condition: (type: Type) => boolean) => { - return condition(type) || type.isUnion() && type.types.some(condition); - }; - if (typeIdx !== TokenType.parameter && test(t => t.getConstructSignatures().length > 0)) { - return TokenType.class; + function isLocalDeclaration(decl: Declaration, sourceFile: SourceFile): boolean { + if (isBindingElement(decl)) { + decl = getDeclarationForBindingElement(decl); } - if (test(t => t.getCallSignatures().length > 0) && !test(t => t.getProperties().length > 0) || isExpressionInCallExpression(node)) { - return typeIdx === TokenType.property ? TokenType.member : TokenType.function; + if (isVariableDeclaration(decl)) { + return (!isSourceFile(decl.parent.parent.parent) || isCatchClause(decl.parent)) && decl.getSourceFile() === sourceFile; } - } + else if (isFunctionDeclaration(decl)) { + return !isSourceFile(decl.parent) && decl.getSourceFile() === sourceFile; + } + return false; } - return typeIdx; - } - function isLocalDeclaration(decl: Declaration, sourceFile: SourceFile): boolean { - if (isBindingElement(decl)) { - decl = getDeclarationForBindingElement(decl); - } - if (isVariableDeclaration(decl)) { - return (!isSourceFile(decl.parent.parent.parent) || isCatchClause(decl.parent)) && decl.getSourceFile() === sourceFile; - } - else if (isFunctionDeclaration(decl)) { - return !isSourceFile(decl.parent) && decl.getSourceFile() === sourceFile; + function getDeclarationForBindingElement(element: BindingElement): VariableDeclaration | ParameterDeclaration { + while (true) { + if (isBindingElement(element.parent.parent)) { + element = element.parent.parent; + } + else { + return element.parent.parent; + } + } } - return false; - } - function getDeclarationForBindingElement(element: BindingElement): VariableDeclaration | ParameterDeclaration { - while (true) { - if (isBindingElement(element.parent.parent)) { - element = element.parent.parent; - } - else { - return element.parent.parent; - } + function inImportClause(node: Node): boolean { + const parent = node.parent; + return parent && (isImportClause(parent) || isImportSpecifier(parent) || isNamespaceImport(parent)); } - } - function inImportClause(node: Node): boolean { - const parent = node.parent; - return parent && (isImportClause(parent) || isImportSpecifier(parent) || isNamespaceImport(parent)); - } - - function isExpressionInCallExpression(node: Node): boolean { - while (isRightSideOfQualifiedNameOrPropertyAccess(node)) { - node = node.parent; + function isExpressionInCallExpression(node: Node): boolean { + while (isRightSideOfQualifiedNameOrPropertyAccess(node)) { + node = node.parent; + } + return isCallExpression(node.parent) && node.parent.expression === node; } - return isCallExpression(node.parent) && node.parent.expression === node; - } - function isRightSideOfQualifiedNameOrPropertyAccess(node: Node): boolean { - return (isQualifiedName(node.parent) && node.parent.right === node) || (isPropertyAccessExpression(node.parent) && node.parent.name === node); - } + function isRightSideOfQualifiedNameOrPropertyAccess(node: Node): boolean { + return (isQualifiedName(node.parent) && node.parent.right === node) || (isPropertyAccessExpression(node.parent) && node.parent.name === node); + } - const enum SemanticMeaning { - None = 0x0, - Value = 0x1, - Type = 0x2, - Namespace = 0x4, - All = Value | Type | Namespace - } + const enum SemanticMeaning { + None = 0x0, + Value = 0x1, + Type = 0x2, + Namespace = 0x4, + All = Value | Type | Namespace + } - function getMeaningFromLocation(node: Node): SemanticMeaning { - const f = (ts).getMeaningFromLocation; - if (typeof f === "function") { - return f(node); + function getMeaningFromLocation(node: Node): SemanticMeaning { + const f = (ts).getMeaningFromLocation; + if (typeof f === "function") { + return f(node); + } + return SemanticMeaning.All; } - return SemanticMeaning.All; - } - const tokenFromDeclarationMapping: { [name: string]: TokenType } = { - [SyntaxKind.VariableDeclaration]: TokenType.variable, - [SyntaxKind.Parameter]: TokenType.parameter, - [SyntaxKind.PropertyDeclaration]: TokenType.property, - [SyntaxKind.ModuleDeclaration]: TokenType.namespace, - [SyntaxKind.EnumDeclaration]: TokenType.enum, - [SyntaxKind.EnumMember]: TokenType.enumMember, - [SyntaxKind.ClassDeclaration]: TokenType.class, - [SyntaxKind.MethodDeclaration]: TokenType.member, - [SyntaxKind.FunctionDeclaration]: TokenType.function, - [SyntaxKind.FunctionExpression]: TokenType.function, - [SyntaxKind.MethodSignature]: TokenType.member, - [SyntaxKind.GetAccessor]: TokenType.property, - [SyntaxKind.SetAccessor]: TokenType.property, - [SyntaxKind.PropertySignature]: TokenType.property, - [SyntaxKind.InterfaceDeclaration]: TokenType.interface, - [SyntaxKind.TypeAliasDeclaration]: TokenType.type, - [SyntaxKind.TypeParameter]: TokenType.typeParameter, - [SyntaxKind.PropertyAssignment]: TokenType.property, - [SyntaxKind.ShorthandPropertyAssignment]: TokenType.property - }; + const tokenFromDeclarationMapping: { [name: string]: TokenType } = { + [SyntaxKind.VariableDeclaration]: TokenType.variable, + [SyntaxKind.Parameter]: TokenType.parameter, + [SyntaxKind.PropertyDeclaration]: TokenType.property, + [SyntaxKind.ModuleDeclaration]: TokenType.namespace, + [SyntaxKind.EnumDeclaration]: TokenType.enum, + [SyntaxKind.EnumMember]: TokenType.enumMember, + [SyntaxKind.ClassDeclaration]: TokenType.class, + [SyntaxKind.MethodDeclaration]: TokenType.member, + [SyntaxKind.FunctionDeclaration]: TokenType.function, + [SyntaxKind.FunctionExpression]: TokenType.function, + [SyntaxKind.MethodSignature]: TokenType.member, + [SyntaxKind.GetAccessor]: TokenType.property, + [SyntaxKind.SetAccessor]: TokenType.property, + [SyntaxKind.PropertySignature]: TokenType.property, + [SyntaxKind.InterfaceDeclaration]: TokenType.interface, + [SyntaxKind.TypeAliasDeclaration]: TokenType.type, + [SyntaxKind.TypeParameter]: TokenType.typeParameter, + [SyntaxKind.PropertyAssignment]: TokenType.property, + [SyntaxKind.ShorthandPropertyAssignment]: TokenType.property + }; } From d425ab5c2c3d8698f51126eb24f876bd336e8b7f Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Mon, 22 Jun 2020 15:46:46 -0400 Subject: [PATCH 09/14] Update baselines --- tests/baselines/reference/api/tsserverlibrary.d.ts | 13 ++++--------- tests/baselines/reference/api/typescript.d.ts | 13 ++++--------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index dceffc5c1d5af..8d40708e09930 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3723,10 +3723,6 @@ declare namespace ts { span: TextSpan; newLength: number; } - export enum SemanticClassificationFormat { - Original = "original", - TwentyTwenty = "2020" - } export interface SyntaxList extends Node { kind: SyntaxKind.SyntaxList; _children: Node[]; @@ -5343,6 +5339,10 @@ declare namespace ts { type WithMetadata = T & { metadata?: unknown; }; + enum SemanticClassificationFormat { + Original = "original", + TwentyTwenty = "2020" + } interface LanguageService { /** This is used as a part of restarting the language service. */ cleanupSemanticCache(): void; @@ -6222,11 +6222,6 @@ declare namespace ts { /** The classifier is used for syntactic highlighting in editors via the TSServer */ function createClassifier(): Classifier; } -declare namespace ts.classifier.vscode { - /** This is mainly used internally for testing */ - function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[]; - function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications; -} declare namespace ts { interface DocumentHighlights { fileName: string; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index b3ef5679c060f..35b2e1f98dbf1 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3723,10 +3723,6 @@ declare namespace ts { span: TextSpan; newLength: number; } - export enum SemanticClassificationFormat { - Original = "original", - TwentyTwenty = "2020" - } export interface SyntaxList extends Node { kind: SyntaxKind.SyntaxList; _children: Node[]; @@ -5343,6 +5339,10 @@ declare namespace ts { type WithMetadata = T & { metadata?: unknown; }; + enum SemanticClassificationFormat { + Original = "original", + TwentyTwenty = "2020" + } interface LanguageService { /** This is used as a part of restarting the language service. */ cleanupSemanticCache(): void; @@ -6222,11 +6222,6 @@ declare namespace ts { /** The classifier is used for syntactic highlighting in editors via the TSServer */ function createClassifier(): Classifier; } -declare namespace ts.classifier.vscode { - /** This is mainly used internally for testing */ - function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[]; - function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications; -} declare namespace ts { interface DocumentHighlights { fileName: string; From 8849fea4b3cdddba1fcf9ee4cec9fb29af494018 Mon Sep 17 00:00:00 2001 From: Orta Date: Tue, 23 Jun 2020 10:57:19 -0400 Subject: [PATCH 10/14] Apply suggestions from code review Co-authored-by: Daniel Rosenwasser --- src/services/classifier2020.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/services/classifier2020.ts b/src/services/classifier2020.ts index 5a482f47fe216..2df3cf3e0a243 100644 --- a/src/services/classifier2020.ts +++ b/src/services/classifier2020.ts @@ -2,18 +2,19 @@ namespace ts.classifier.modern { /** @internal */ - export enum TokenEncodingConsts { + export const enum TokenEncodingConsts { + typeOffset = 8, modifierMask = (1 << typeOffset) - 1 } /** @internal */ - export enum TokenType { + export const enum TokenType { class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member, _ } /** @internal */ - export enum TokenModifier { + export const enum TokenModifier { declaration, static, async, readonly, defaultLibrary, local, _ } @@ -49,7 +50,7 @@ namespace ts.classifier.modern { }; if (program && sourceFile) { - collectTokens(program, sourceFile, span, collector); + collectTokens(program, sourceFile, span, collector); } return resultTokens; } From 2a8766cc1b04fc50e23cec6bdd48bbe67dcbf1b8 Mon Sep 17 00:00:00 2001 From: Orta Date: Tue, 23 Jun 2020 11:01:25 -0400 Subject: [PATCH 11/14] Update src/harness/fourslashImpl.ts Co-authored-by: Daniel Rosenwasser --- src/harness/fourslashImpl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 7603dfb58602a..7677e42abe39e 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2559,7 +2559,7 @@ namespace FourSlash { const actual = this.languageService.getSemanticClassifications(this.activeFile.fileName, ts.createTextSpan(0, this.activeFile.content.length), format); const replacement = [`const c2 = classification("2020");`,`verify.semanticClassificationsAre("2020",`]; - actual.forEach(a => { + for (const a of actual) { const identifier = this.classificationToIdentifier(a.classificationType as number); const text = this.activeFile.content.slice(a.textSpan.start, a.textSpan.start + a.textSpan.length); replacement.push(` c2.semanticToken("${identifier}", "${text}"), `); From 9c1f39667dd8e680a98b13fde1ca31ca2eb6ad3f Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Tue, 23 Jun 2020 11:36:11 -0400 Subject: [PATCH 12/14] Reafactor after comments --- src/harness/fourslashImpl.ts | 2 +- src/services/classifier2020.ts | 15 +++++++-------- src/services/services.ts | 6 +++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 7677e42abe39e..bc9927436d994 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2563,7 +2563,7 @@ namespace FourSlash { const identifier = this.classificationToIdentifier(a.classificationType as number); const text = this.activeFile.content.slice(a.textSpan.start, a.textSpan.start + a.textSpan.length); replacement.push(` c2.semanticToken("${identifier}", "${text}"), `); - }); + }; replacement.push(");"); throw new Error("You need to change the source code of fourslash test to use replaceWithSemanticClassifications"); diff --git a/src/services/classifier2020.ts b/src/services/classifier2020.ts index 2df3cf3e0a243..adbac20b3e7e6 100644 --- a/src/services/classifier2020.ts +++ b/src/services/classifier2020.ts @@ -3,19 +3,18 @@ namespace ts.classifier.modern { /** @internal */ export const enum TokenEncodingConsts { - typeOffset = 8, modifierMask = (1 << typeOffset) - 1 } /** @internal */ export const enum TokenType { - class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member, _ + class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member } /** @internal */ export const enum TokenModifier { - declaration, static, async, readonly, defaultLibrary, local, _ + declaration, static, async, readonly, defaultLibrary, local } /** This is mainly used internally for testing */ @@ -26,10 +25,10 @@ namespace ts.classifier.modern { const dense = classifications.spans; const result: ClassifiedSpan[] = []; for (let i = 0; i < dense.length; i += 3) { - result.push({ - textSpan: createTextSpan(dense[i], dense[i + 1]), - classificationType: dense[i + 2] - }); + result.push({ + textSpan: createTextSpan(dense[i], dense[i + 1]), + classificationType: dense[i + 2] + }); } return result; @@ -46,7 +45,7 @@ namespace ts.classifier.modern { const resultTokens: number[] = []; const collector = (node: Node, typeIdx: number, modifierSet: number) => { - resultTokens.push(node.getStart(), node.getWidth(), ((typeIdx + 1) << TokenEncodingConsts.typeOffset) + modifierSet); + resultTokens.push(node.getStart(sourceFile), node.getWidth(sourceFile), ((typeIdx + 1) << TokenEncodingConsts.typeOffset) + modifierSet); }; if (program && sourceFile) { diff --git a/src/services/services.ts b/src/services/services.ts index f4b300059aea9..25e9e2ec6ef66 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1805,11 +1805,11 @@ namespace ts { synchronizeHostData(); const responseFormat = format || SemanticClassificationFormat.Original; - if (responseFormat === SemanticClassificationFormat.Original) { - return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); + if (responseFormat === SemanticClassificationFormat.TwentyTwenty) { + return classifier.modern.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); } else { - return classifier.modern.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); + return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); } } From 58e830c3d6a5bbceaecd599ebad5255e0cde5e62 Mon Sep 17 00:00:00 2001 From: Orta Date: Mon, 29 Jun 2020 15:09:18 -0400 Subject: [PATCH 13/14] Switch to a faster semantic classifier --- src/server/project.ts | 2 +- src/services/classifier2020.ts | 2242 +++++++++++++++++++++++++++++--- src/services/services.ts | 5 +- 3 files changed, 2080 insertions(+), 169 deletions(-) diff --git a/src/server/project.ts b/src/server/project.ts index 9bdbb194472f1..f0d89ebcc7ec3 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1819,7 +1819,7 @@ namespace ts.server { packageJson.dependencies?.forEach((_, dependenyName) => addDependency(dependenyName)); packageJson.peerDependencies?.forEach((_, dependencyName) => addDependency(dependencyName)); if (dependencySelection === PackageJsonAutoImportPreference.All) { - packageJson.devDependencies?.forEach((_, dependencyName) => addDependency(dependencyName)); + packageJson.devDependencies?.forEach((_gulp, dependencyName) => addDependency(dependencyName)); } } diff --git a/src/services/classifier2020.ts b/src/services/classifier2020.ts index adbac20b3e7e6..005c985416a75 100644 --- a/src/services/classifier2020.ts +++ b/src/services/classifier2020.ts @@ -14,15 +14,15 @@ namespace ts.classifier.modern { /** @internal */ export const enum TokenModifier { - declaration, static, async, readonly, defaultLibrary, local + declaration, static, async, readonly, defaultLibrary, local, _ } /** This is mainly used internally for testing */ export function getSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[] { const classifications = getEncodedSemanticClassifications(program, _cancellationToken, sourceFile, span); - Debug.assert(classifications.spans.length % 3 === 0); - const dense = classifications.spans; + Debug.assert(classifications.length % 3 === 0); + const dense = classifications; const result: ClassifiedSpan[] = []; for (let i = 0; i < dense.length; i += 3) { result.push({ @@ -34,224 +34,2132 @@ namespace ts.classifier.modern { return result; } - export function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications { - return { - spans: getSemanticTokens(program, sourceFile, span), - endOfLineState: EndOfLineState.None - }; + export function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, _span: TextSpan) { + const context = new Context(program, sourceFile); + visitSourceFile(context, sourceFile); + return context.result; } - function getSemanticTokens(program: Program, sourceFile: SourceFile, span: TextSpan): number[] { - const resultTokens: number[] = []; + // export function getEncodedSemanticClassifications(program: Program, _cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications { + // return { + // spans: getSemanticTokens(program, sourceFile, span), + // endOfLineState: EndOfLineState.None + // }; + // } - const collector = (node: Node, typeIdx: number, modifierSet: number) => { - resultTokens.push(node.getStart(sourceFile), node.getWidth(sourceFile), ((typeIdx + 1) << TokenEncodingConsts.typeOffset) + modifierSet); - }; + // function getSemanticTokens(program: Program, sourceFile: SourceFile, span: TextSpan): number[] { + // const resultTokens: number[] = []; - if (program && sourceFile) { - collectTokens(program, sourceFile, span, collector); - } - return resultTokens; + // const collector = (node: Node, typeIdx: number, modifierSet: number) => { + // resultTokens.push(node.getStart(sourceFile), node.getWidth(sourceFile), ((typeIdx + 1) << TokenEncodingConsts.typeOffset) + modifierSet); + // }; + + // if (program && sourceFile) { + // collectTokens(program, sourceFile, span, collector); + // } + // return resultTokens; + // } + + // function collectTokens(program: Program, sourceFile: SourceFile, span: TextSpan, collector: (node: Node, tokenType: number, tokenModifier: number) => void) { + // const typeChecker = program.getTypeChecker(); + + // let inJSXElement = false; + + // function visit(node: Node) { + // if (!node || !textSpanIntersectsWith(span, node.pos, node.getFullWidth()) || node.getFullWidth() === 0) { + // return; + // } + // const prevInJSXElement = inJSXElement; + // if (isJsxElement(node) || isJsxSelfClosingElement(node)) { + // inJSXElement = true; + // } + // if (isJsxExpression(node)) { + // inJSXElement = false; + // } + + // if (isIdentifier(node) && !inJSXElement && !inImportClause(node)) { + // let symbol = typeChecker.getSymbolAtLocation(node); + // if (symbol) { + // if (symbol.flags & SymbolFlags.Alias) { + // symbol = typeChecker.getAliasedSymbol(symbol); + // } + // let typeIdx = classifySymbol(symbol, getMeaningFromLocation(node)); + // if (typeIdx !== undefined) { + // let modifierSet = 0; + // if (node.parent) { + // const parentIsDeclaration = (isBindingElement(node.parent) || tokenFromDeclarationMapping[node.parent.kind] === typeIdx); + // if (parentIsDeclaration && (node.parent).name === node) { + // modifierSet = 1 << TokenModifier.declaration; + // } + // } + + // // property declaration in constructor + // if (typeIdx === TokenType.parameter && isRightSideOfQualifiedNameOrPropertyAccess(node)) { + // typeIdx = TokenType.property; + // } + + // typeIdx = reclassifyByType(typeChecker, node, typeIdx); + + // const decl = symbol.valueDeclaration; + // if (decl) { + // const modifiers = getCombinedModifierFlags(decl); + // const nodeFlags = getCombinedNodeFlags(decl); + // if (modifiers & ModifierFlags.Static) { + // modifierSet |= 1 << TokenModifier.static; + // } + // if (modifiers & ModifierFlags.Async) { + // modifierSet |= 1 << TokenModifier.async; + // } + // if (typeIdx !== TokenType.class && typeIdx !== TokenType.interface) { + // if ((modifiers & ModifierFlags.Readonly) || (nodeFlags & NodeFlags.Const) || (symbol.getFlags() & SymbolFlags.EnumMember)) { + // modifierSet |= 1 << TokenModifier.readonly; + // } + // } + // if ((typeIdx === TokenType.variable || typeIdx === TokenType.function) && isLocalDeclaration(decl, sourceFile)) { + // modifierSet |= 1 << TokenModifier.local; + // } + // if (program.isSourceFileDefaultLibrary(decl.getSourceFile())) { + // modifierSet |= 1 << TokenModifier.defaultLibrary; + // } + // } + // else if (symbol.declarations && symbol.declarations.some(d => program.isSourceFileDefaultLibrary(d.getSourceFile()))) { + // modifierSet |= 1 << TokenModifier.defaultLibrary; + // } + + // collector(node, typeIdx, modifierSet); + + // } + // } + // } + // forEachChild(node, visit); + + // inJSXElement = prevInJSXElement; + // } + // visit(sourceFile); + // } + + // function classifySymbol(symbol: Symbol, meaning: SemanticMeaning): TokenType | undefined { + // const flags = symbol.getFlags(); + // if (flags & SymbolFlags.Class) { + // return TokenType.class; + // } + // else if (flags & SymbolFlags.Enum) { + // return TokenType.enum; + // } + // else if (flags & SymbolFlags.TypeAlias) { + // return TokenType.type; + // } + // else if (flags & SymbolFlags.Interface) { + // if (meaning & SemanticMeaning.Type) { + // return TokenType.interface; + // } + // } + // else if (flags & SymbolFlags.TypeParameter) { + // return TokenType.typeParameter; + // } + // let decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; + // if (decl && isBindingElement(decl)) { + // decl = getDeclarationForBindingElement(decl); + // } + // return decl && tokenFromDeclarationMapping[decl.kind]; + // } + + // function reclassifyByType(typeChecker: TypeChecker, node: Node, typeIdx: TokenType): TokenType { + // // type based classifications + // if (typeIdx === TokenType.variable || typeIdx === TokenType.property || typeIdx === TokenType.parameter) { + // const type = typeChecker.getTypeAtLocation(node); + // if (type) { + // const test = (condition: (type: Type) => boolean) => { + // return condition(type) || type.isUnion() && type.types.some(condition); + // }; + // if (typeIdx !== TokenType.parameter && test(t => t.getConstructSignatures().length > 0)) { + // return TokenType.class; + // } + // if (test(t => t.getCallSignatures().length > 0) && !test(t => t.getProperties().length > 0) || isExpressionInCallExpression(node)) { + // return typeIdx === TokenType.property ? TokenType.member : TokenType.function; + // } + // } + // } + // return typeIdx; + // } + + // function isLocalDeclaration(decl: Declaration, sourceFile: SourceFile): boolean { + // if (isBindingElement(decl)) { + // decl = getDeclarationForBindingElement(decl); + // } + // if (isVariableDeclaration(decl)) { + // return (!isSourceFile(decl.parent.parent.parent) || isCatchClause(decl.parent)) && decl.getSourceFile() === sourceFile; + // } + // else if (isFunctionDeclaration(decl)) { + // return !isSourceFile(decl.parent) && decl.getSourceFile() === sourceFile; + // } + // return false; + // } + + // function getDeclarationForBindingElement(element: BindingElement): VariableDeclaration | ParameterDeclaration { + // while (true) { + // if (isBindingElement(element.parent.parent)) { + // element = element.parent.parent; + // } + // else { + // return element.parent.parent; + // } + // } + // } + + // function inImportClause(node: Node): boolean { + // const parent = node.parent; + // return parent && (isImportClause(parent) || isImportSpecifier(parent) || isNamespaceImport(parent)); + // } + + // function isExpressionInCallExpression(node: Node): boolean { + // while (isRightSideOfQualifiedNameOrPropertyAccess(node)) { + // node = node.parent; + // } + // return isCallExpression(node.parent) && node.parent.expression === node; + // } + + // function isRightSideOfQualifiedNameOrPropertyAccess(node: Node): boolean { + // return (isQualifiedName(node.parent) && node.parent.right === node) || (isPropertyAccessExpression(node.parent) && node.parent.name === node); + // } + + // function getMeaningFromLocation(node: Node): SemanticMeaning { + // const f = (ts).getMeaningFromLocation; + // if (typeof f === "function") { + // return f(node); + // } + // return SemanticMeaning.All; + // } + + /// CUT + + const enum SemanticMeaning { + None = 0x0, + Value = 0x1, + Type = 0x2, + Namespace = 0x4, + All = Value | Type | Namespace } - function collectTokens(program: Program, sourceFile: SourceFile, span: TextSpan, collector: (node: Node, tokenType: number, tokenModifier: number) => void) { - const typeChecker = program.getTypeChecker(); + const enum CanUseLocals { + False = 0, + True = 1, + } - let inJSXElement = false; + const enum IsCallExpression { + False = 0, + True = 1 + } - function visit(node: Node) { - if (!node || !textSpanIntersectsWith(span, node.pos, node.getFullWidth()) || node.getFullWidth() === 0) { - return; + const enum IsRightSideOfExpression { + False = 0, + True = 1 + } + + const valueSymbolFlags = ( + SymbolFlags.FunctionScopedVariable + | SymbolFlags.BlockScopedVariable + | SymbolFlags.Property + | SymbolFlags.Function + ); + + const emptyLocals: SymbolTable = new Map<__String, Symbol>(); + + // Using internal TS API: + interface NodeWithLocals { + locals: SymbolTable | undefined; + } + interface NodeWithSymbol { + symbol: Symbol | undefined; + } + interface ProgramWithDiagnosticsProducingTypeChecker { + getDiagnosticsProducingTypeChecker(): TypeChecker; + } + interface SymbolWithExportSymbol { + exportSymbol: Symbol | undefined; + } + + class Context { + + private readonly program: Program; + private readonly typeChecker: TypeChecker; + public readonly sourceFile: SourceFile; + + private readonly locals: SymbolTable[]; + + private readonly scopeSymbolCacheStack: Map[]; + private scopeSymbolCache: Map; + + private readonly thisSymbolStack: (Symbol | undefined)[]; + private thisSymbol: Symbol | undefined; + + private readonly symbolToEncodedTokenType: Map; + + public readonly result: number[]; + public resultLen: number; + + constructor(program: Program, sourceFile: SourceFile) { + this.program = program; + this.typeChecker = (this.program).getDiagnosticsProducingTypeChecker(); + this.sourceFile = sourceFile; + this.locals = []; + this.scopeSymbolCacheStack = [new Map()]; + this.scopeSymbolCache = this.scopeSymbolCacheStack[this.scopeSymbolCacheStack.length - 1]; + this.thisSymbolStack = [undefined]; + this.thisSymbol = this.thisSymbolStack[this.thisSymbolStack.length - 1]; + this.symbolToEncodedTokenType = new Map(); + this.result = []; + this.resultLen = 0; + } + + public findSymbolInLocals(escapedText: __String, meaning: SemanticMeaning): Symbol | undefined { + const locals = this.locals; + for (let i = locals.length - 1; i >= 0; i--) { + const symbolTable = locals[i]; + const symbol = symbolTable.get(escapedText); + if (symbol) { + if (((meaning & SemanticMeaning.Value) === 0) && (symbol.getFlags() & valueSymbolFlags)) { + // we are not looking for Value + continue; + } + const symbolWithExportSymbol = (symbol); + if (symbolWithExportSymbol.exportSymbol) { + return symbolWithExportSymbol.exportSymbol; + } + return symbol; + } } - const prevInJSXElement = inJSXElement; - if (isJsxElement(node) || isJsxSelfClosingElement(node)) { - inJSXElement = true; + return undefined; + } + + public pushResult(node: Identifier | PrivateIdentifier, tokenType: TokenType, modifiers: number): void { + const nodeWidth = node.getWidth(); + if (nodeWidth === 0) { + return; } - if (isJsxExpression(node)) { - inJSXElement = false; + const result = this.result; + let resultLen = this.resultLen; + result[resultLen++] = node.getStart(); + result[resultLen++] = nodeWidth; + result[resultLen++] = ((tokenType + 1) << TokenEncodingConsts.typeOffset) + modifiers; + this.resultLen = resultLen; + } + + public pushEncodedResult(node: Identifier | PrivateIdentifier, encodedToken: number): void { + const nodeWidth = node.getWidth(); + if (nodeWidth === 0) { + return; } + const result = this.result; + let resultLen = this.resultLen; + result[resultLen++] = node.getStart(); + result[resultLen++] = nodeWidth; + result[resultLen++] = encodedToken; + this.resultLen = resultLen; + } - if (isIdentifier(node) && !inJSXElement && !inImportClause(node)) { - let symbol = typeChecker.getSymbolAtLocation(node); - if (symbol) { - if (symbol.flags & SymbolFlags.Alias) { - symbol = typeChecker.getAliasedSymbol(symbol); - } - let typeIdx = classifySymbol(symbol, getMeaningFromLocation(node)); - if (typeIdx !== undefined) { - let modifierSet = 0; - if (node.parent) { - const parentIsDeclaration = (isBindingElement(node.parent) || tokenFromDeclarationMapping[node.parent.kind] === typeIdx); - if (parentIsDeclaration && (node.parent).name === node) { - modifierSet = 1 << TokenModifier.declaration; - } + public pushResultFromSymbolCache(symbol: Symbol, node: Identifier | PrivateIdentifier, extraTokenModifiers: number, isCallExpression: IsCallExpression, isRightSideOfExpression: IsRightSideOfExpression): boolean { + const cachedEncodedTokenType = this.symbolToEncodedTokenType.get(symbol); + if (typeof cachedEncodedTokenType !== "undefined") { + let tokenType = (cachedEncodedTokenType >>> TokenEncodingConsts.typeOffset) - 1; + let tokenModifiers = (cachedEncodedTokenType & TokenEncodingConsts.modifierMask); + if (symbol.valueDeclaration && symbol.valueDeclaration.kind === SyntaxKind.Parameter && symbol.valueDeclaration.parent.kind === SyntaxKind.Constructor) { + // handle the case of a property declaration in constructor + if (isRightSideOfExpression === IsRightSideOfExpression.True) { + if (tokenType === TokenType.parameter) { + tokenType = TokenType.property; } - - // property declaration in constructor - if (typeIdx === TokenType.parameter && isRightSideOfQualifiedNameOrPropertyAccess(node)) { - typeIdx = TokenType.property; + else if (tokenType === TokenType.function) { + tokenType = TokenType.member; } - - typeIdx = reclassifyByType(typeChecker, node, typeIdx); - - const decl = symbol.valueDeclaration; - if (decl) { - const modifiers = getCombinedModifierFlags(decl); - const nodeFlags = getCombinedNodeFlags(decl); - if (modifiers & ModifierFlags.Static) { - modifierSet |= 1 << TokenModifier.static; - } - if (modifiers & ModifierFlags.Async) { - modifierSet |= 1 << TokenModifier.async; - } - if (typeIdx !== TokenType.class && typeIdx !== TokenType.interface) { - if ((modifiers & ModifierFlags.Readonly) || (nodeFlags & NodeFlags.Const) || (symbol.getFlags() & SymbolFlags.EnumMember)) { - modifierSet |= 1 << TokenModifier.readonly; - } - } - if ((typeIdx === TokenType.variable || typeIdx === TokenType.function) && isLocalDeclaration(decl, sourceFile)) { - modifierSet |= 1 << TokenModifier.local; - } - if (program.isSourceFileDefaultLibrary(decl.getSourceFile())) { - modifierSet |= 1 << TokenModifier.defaultLibrary; - } + } + else { + if (tokenType === TokenType.property) { + tokenType = TokenType.parameter; } - else if (symbol.declarations && symbol.declarations.some(d => program.isSourceFileDefaultLibrary(d.getSourceFile()))) { - modifierSet |= 1 << TokenModifier.defaultLibrary; + else if (tokenType === TokenType.member) { + tokenType = TokenType.function; } - - collector(node, typeIdx, modifierSet); - } } + if (isCallExpression === IsCallExpression.True && (tokenType === TokenType.variable || tokenType === TokenType.property || tokenType === TokenType.parameter)) { + tokenType = tokenType === TokenType.property ? TokenType.member : TokenType.function; + } + tokenModifiers = tokenModifiers | extraTokenModifiers; + this.pushResult(node, tokenType, tokenModifiers); + return true; } - forEachChild(node, visit); + return false; + } - inJSXElement = prevInJSXElement; + public saveResultInSymbolCache(symbol: Symbol, encodedTokenType: number): void { + this.symbolToEncodedTokenType.set(symbol, encodedTokenType); } - visit(sourceFile); - } - function classifySymbol(symbol: Symbol, meaning: SemanticMeaning): TokenType | undefined { - const flags = symbol.getFlags(); - if (flags & SymbolFlags.Class) { - return TokenType.class; + public pushThisSymbol(nodeScopeThis: Symbol | undefined): void { + this.thisSymbolStack.push(nodeScopeThis); + this.thisSymbol = this.thisSymbolStack[this.thisSymbolStack.length - 1]; } - else if (flags & SymbolFlags.Enum) { - return TokenType.enum; + + public popThisSymbol(): void { + this.thisSymbolStack.pop(); + this.thisSymbol = this.thisSymbolStack[this.thisSymbolStack.length - 1]; } - else if (flags & SymbolFlags.TypeAlias) { - return TokenType.type; + + public getThisSymbol(): Symbol | undefined { + return this.thisSymbol; } - else if (flags & SymbolFlags.Interface) { - if (meaning & SemanticMeaning.Type) { - return TokenType.interface; + + public pushLocals(nodeLocals: SymbolTable | undefined): void { + if (!nodeLocals) { + this.locals.push(emptyLocals); + return; + } + // if any aliases are present, make a new symbol table + let hasAlias = false; + nodeLocals.forEach((symbol) => { + if (hasAlias) { + return; + } + hasAlias = (symbol.flags & SymbolFlags.Alias) ? true : false; + }); + + if (hasAlias) { + // must recreate symbol table with resolved aliases + const newSymbolTable = new Map<__String, Symbol>(); + nodeLocals.forEach((symbol, key) => { + if (symbol.flags & SymbolFlags.Alias) { + newSymbolTable.set(key, this.typeChecker.getAliasedSymbol(symbol)); + } + else { + newSymbolTable.set(key, symbol); + } + }); + this.locals.push(newSymbolTable); + } + else { + this.locals.push(nodeLocals); } } - else if (flags & SymbolFlags.TypeParameter) { - return TokenType.typeParameter; + + public popLocals(): void { + this.locals.pop(); } - let decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; - if (decl && isBindingElement(decl)) { - decl = getDeclarationForBindingElement(decl); + + public pushScope(nodeLocals: SymbolTable | undefined): void { + this.scopeSymbolCacheStack.push(new Map()); + this.scopeSymbolCache = this.scopeSymbolCacheStack[this.scopeSymbolCacheStack.length - 1]; + this.pushLocals(nodeLocals); + } + + public popScope(): void { + this.scopeSymbolCacheStack.pop(); + this.scopeSymbolCache = this.scopeSymbolCacheStack[this.scopeSymbolCacheStack.length - 1]; + this.popLocals(); + } + + public getCachedScopeSymbol(cacheKey: string): Symbol | undefined { + return this.scopeSymbolCache.get(cacheKey); + } + + public setCachedScopeSymbol(cacheKey: string, symbol: Symbol | undefined): void { + this.scopeSymbolCache.set(cacheKey, symbol); + } + + public getTypeCheckerSymbolAtLocation(node: Node): Symbol | undefined { + const symbol = this.typeChecker.getSymbolAtLocation(node); + if (symbol && symbol.flags & SymbolFlags.Alias) { + return this.typeChecker.getAliasedSymbol(symbol); + } + return symbol; } - return decl && tokenFromDeclarationMapping[decl.kind]; - } - function reclassifyByType(typeChecker: TypeChecker, node: Node, typeIdx: TokenType): TokenType { - // type based classifications - if (typeIdx === TokenType.variable || typeIdx === TokenType.property || typeIdx === TokenType.parameter) { - const type = typeChecker.getTypeAtLocation(node); + public reclassifyWithType(node: Node, tokenType: TokenType.variable | TokenType.property | TokenType.parameter): TokenType { + // type based classifications + const type = this.typeChecker.getTypeAtLocation(node); if (type) { - const test = (condition: (type: Type) => boolean) => { - return condition(type) || type.isUnion() && type.types.some(condition); - }; - if (typeIdx !== TokenType.parameter && test(t => t.getConstructSignatures().length > 0)) { + if (tokenType !== TokenType.parameter && typeHasConstructSignatures(type)) { return TokenType.class; } - if (test(t => t.getCallSignatures().length > 0) && !test(t => t.getProperties().length > 0) || isExpressionInCallExpression(node)) { - return typeIdx === TokenType.property ? TokenType.member : TokenType.function; + if (typeHasCallSignatures(type) && !typeHasProperties(type)) { + return tokenType === TokenType.property ? TokenType.member : TokenType.function; } } + return tokenType; } - return typeIdx; - } - function isLocalDeclaration(decl: Declaration, sourceFile: SourceFile): boolean { - if (isBindingElement(decl)) { - decl = getDeclarationForBindingElement(decl); + public isSourceFileDefaultLibrary(sourceFile: SourceFile): boolean { + return this.program.isSourceFileDefaultLibrary(sourceFile); } - if (isVariableDeclaration(decl)) { - return (!isSourceFile(decl.parent.parent.parent) || isCatchClause(decl.parent)) && decl.getSourceFile() === sourceFile; + } + + function getSymbolFast(ctx: Context, node: Identifier, meaning: SemanticMeaning, canUseLocals: CanUseLocals): Symbol | undefined { + const escapedText = node.escapedText; + if (canUseLocals === CanUseLocals.True) { + const symbolInLocals = ctx.findSymbolInLocals(escapedText, meaning); + if (symbolInLocals) { + return symbolInLocals; + } } - else if (isFunctionDeclaration(decl)) { - return !isSourceFile(decl.parent) && decl.getSourceFile() === sourceFile; + const cacheKey = `${meaning}${escapedText}`; + const symbolInCache = ctx.getCachedScopeSymbol(cacheKey); + if (typeof symbolInCache !== "undefined") { + return symbolInCache; } - return false; + const symbol = ctx.getTypeCheckerSymbolAtLocation(node) || undefined; + ctx.setCachedScopeSymbol(cacheKey, symbol); + return symbol; } - function getDeclarationForBindingElement(element: BindingElement): VariableDeclaration | ParameterDeclaration { - while (true) { - if (isBindingElement(element.parent.parent)) { - element = element.parent.parent; + function nodeModifiersToTokenModifiers(modifiers: ModifiersArray | undefined): number { + if (!modifiers) { + return 0; + } + let result = 0; + for (const modifier of modifiers) { + if (modifier.kind === SyntaxKind.StaticKeyword) { + result |= 1 << TokenModifier.static; } - else { - return element.parent.parent; + else if (modifier.kind === SyntaxKind.AsyncKeyword) { + result |= 1 << TokenModifier.async; + } + else if (modifier.kind === SyntaxKind.ReadonlyKeyword) { + result |= 1 << TokenModifier.readonly; } } + return result; } - function inImportClause(node: Node): boolean { - const parent = node.parent; - return parent && (isImportClause(parent) || isImportSpecifier(parent) || isNamespaceImport(parent)); + function canCacheSymbol(symbol: Symbol | undefined): boolean { + // can only cache symbols that have 1 declaration or that have multiple declarations of the same kind + if (!symbol || !symbol.declarations || symbol.declarations.length <= 1) { + return true; + } + const kind = symbol.declarations[0].kind; + for (let i = 1, len = symbol.declarations.length; i < len; i++) { + if (symbol.declarations[i].kind !== kind) { + return false; + } + } + return true; } - function isExpressionInCallExpression(node: Node): boolean { - while (isRightSideOfQualifiedNameOrPropertyAccess(node)) { - node = node.parent; + function visitIdentifierInDeclaration(ctx: Context, node: Identifier, symbol: Symbol | undefined, _meaning: SemanticMeaning, tokenType: TokenType, tokenModifiers: number): void { + const canCache = canCacheSymbol(symbol); + if (canCache && symbol && ctx.pushResultFromSymbolCache(symbol, node, (1 << TokenModifier.declaration), IsCallExpression.False, IsRightSideOfExpression.False)) { + return; } - return isCallExpression(node.parent) && node.parent.expression === node; + + if (tokenType === TokenType.variable || tokenType === TokenType.property || tokenType === TokenType.parameter) { + tokenType = ctx.reclassifyWithType(node, tokenType); + if (tokenType !== TokenType.variable && tokenType !== TokenType.function) { + // the token has changed its type, be sure to remove local + tokenModifiers &= ~(1 << TokenModifier.local); + } + } + + const encodedTokenType = ((tokenType + 1) << TokenEncodingConsts.typeOffset) | tokenModifiers; + if (canCache && symbol) { + ctx.saveResultInSymbolCache(symbol, encodedTokenType); + } + ctx.pushEncodedResult(node, encodedTokenType | (1 << TokenModifier.declaration)); } - function isRightSideOfQualifiedNameOrPropertyAccess(node: Node): boolean { - return (isQualifiedName(node.parent) && node.parent.right === node) || (isPropertyAccessExpression(node.parent) && node.parent.name === node); + function visitIdentifierWithSymbol(ctx: Context, node: Identifier | PrivateIdentifier, symbol: Symbol, meaning: SemanticMeaning, isCallExpression: IsCallExpression, isRightSideOfExpression: IsRightSideOfExpression): void { + let canCache = canCacheSymbol(symbol); + if (canCache && ctx.pushResultFromSymbolCache(symbol, node, 0, isCallExpression, isRightSideOfExpression)) { + return; + } + + const flags = symbol.getFlags(); + let decl = symbol.valueDeclaration || (symbol.declarations && symbol.declarations[0]); + let tokenType: TokenType; + if (flags & SymbolFlags.Class) { + tokenType = TokenType.class; + } + else if (flags & SymbolFlags.Enum) { + tokenType = TokenType.enum; + } + else if (flags & SymbolFlags.TypeAlias) { + tokenType = TokenType.type; + } + else if ((flags & SymbolFlags.Interface) && (meaning & SemanticMeaning.Type)) { + tokenType = TokenType.interface; + } + else if (flags & SymbolFlags.TypeParameter) { + tokenType = TokenType.typeParameter; + } + else if (decl) { + if (decl.kind === SyntaxKind.BindingElement) { + decl = findBindingElementParentDeclaration(decl); + } + switch (decl.kind) { + case SyntaxKind.VariableDeclaration: tokenType = TokenType.variable; break; + case SyntaxKind.Parameter: + // handle the case of a property declaration in constructor + if (isRightSideOfExpression === IsRightSideOfExpression.True) { + tokenType = TokenType.property; + } + else { + tokenType = TokenType.parameter; + } + break; + case SyntaxKind.PropertyDeclaration: tokenType = TokenType.property; break; + case SyntaxKind.ModuleDeclaration: tokenType = TokenType.namespace; break; + case SyntaxKind.EnumDeclaration: tokenType = TokenType.enum; break; + case SyntaxKind.EnumMember: tokenType = TokenType.enumMember; break; + case SyntaxKind.ClassDeclaration: tokenType = TokenType.class; break; + case SyntaxKind.MethodDeclaration: tokenType = TokenType.member; break; + case SyntaxKind.FunctionDeclaration: tokenType = TokenType.function; break; + case SyntaxKind.FunctionExpression: tokenType = TokenType.function; break; + case SyntaxKind.MethodSignature: tokenType = TokenType.member; break; + case SyntaxKind.GetAccessor: tokenType = TokenType.property; break; + case SyntaxKind.PropertySignature: tokenType = TokenType.property; break; + case SyntaxKind.InterfaceDeclaration: tokenType = TokenType.interface; break; + case SyntaxKind.TypeAliasDeclaration: tokenType = TokenType.type; break; + case SyntaxKind.TypeParameter: tokenType = TokenType.typeParameter; break; + case SyntaxKind.PropertyAssignment: tokenType = TokenType.property; break; + case SyntaxKind.ShorthandPropertyAssignment: tokenType = TokenType.property; break; + default: + return; + } + } + else { + return; + } + + const isDefaultLibrary = ctx.isSourceFileDefaultLibrary(decl.getSourceFile()); + + if (tokenType === TokenType.variable || tokenType === TokenType.property || tokenType === TokenType.parameter) { + if (isCallExpression === IsCallExpression.True && !isDefaultLibrary) { + // normally, classes cannot be called, but there are default library symbols like String which can appear in a call expression + tokenType = tokenType === TokenType.property ? TokenType.member : TokenType.function; + canCache = false; + } + else { + tokenType = ctx.reclassifyWithType(node, tokenType); + } + } + + let modifierSet = 0; + const valueDecl = symbol.valueDeclaration; + if (valueDecl) { + const modifiers = getCombinedModifierFlags(valueDecl); + const nodeFlags = getCombinedNodeFlags(valueDecl); + if (modifiers & ModifierFlags.Static) { + modifierSet |= 1 << TokenModifier.static; + } + if (modifiers & ModifierFlags.Async) { + modifierSet |= 1 << TokenModifier.async; + } + if (tokenType !== TokenType.interface) { + if ((modifiers & ModifierFlags.Readonly) || (nodeFlags & NodeFlags.Const) || (flags & SymbolFlags.EnumMember)) { + modifierSet |= 1 << TokenModifier.readonly; + } + } + if ((tokenType === TokenType.variable || tokenType === TokenType.function) && isLocalDeclaration(valueDecl, ctx.sourceFile)) { + modifierSet |= 1 << TokenModifier.local; + } + } + + if (isDefaultLibrary) { + modifierSet |= 1 << TokenModifier.defaultLibrary; + } + + const encodedTokenType = ((tokenType + 1) << TokenEncodingConsts.typeOffset) | modifierSet; + if (canCache) { + ctx.saveResultInSymbolCache(symbol, encodedTokenType); + } + ctx.pushEncodedResult(node, encodedTokenType); + } + function visitIdentifier(ctx: Context, node: Identifier, meaning: SemanticMeaning, canUseLocals: CanUseLocals, isCallExpression: IsCallExpression): Symbol | undefined { + const symbol = getSymbolFast(ctx, node, meaning, canUseLocals); + if (!symbol) { + return undefined; + } + visitIdentifierWithSymbol(ctx, node, symbol, meaning, isCallExpression, IsRightSideOfExpression.False); + return symbol; + } + function visitPropertyName(ctx: Context, node: StringLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier): void { + if (node.kind === SyntaxKind.ComputedPropertyName) { + visitExpression(ctx, node.expression); + } + } + function visitQualifiedName(ctx: Context, node: QualifiedName, meaning: SemanticMeaning): Symbol | undefined { + return visitQualifiedNameWithSplitMeaning(ctx, node, meaning, meaning); + } + function visitQualifiedNameWithSplitMeaning(ctx: Context, node: QualifiedName, meaningLeft: SemanticMeaning, meaningRight: SemanticMeaning): Symbol | undefined { + const leftSymbol = visitEntityName(ctx, node.left, meaningLeft); + if (!leftSymbol || !leftSymbol.exports) { + return undefined; + } + const symbol = leftSymbol.exports.get(node.right.escapedText); + if (!symbol) { + return undefined; + } + visitIdentifierWithSymbol(ctx, node.right, symbol, meaningRight, IsCallExpression.False, IsRightSideOfExpression.False); + return symbol; + } + function visitEntityName(ctx: Context, node: EntityName, meaning: SemanticMeaning): Symbol | undefined { + switch (node.kind) { + case SyntaxKind.Identifier: + return visitIdentifier(ctx, node, meaning, CanUseLocals.True, IsCallExpression.False); + case SyntaxKind.QualifiedName: + return visitQualifiedName(ctx, node, meaning); + } + } + function visitParameterDeclaration(ctx: Context, node: ParameterDeclaration): Symbol | null | void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + visitBindingName(ctx, node.name, (node).symbol, TokenType.parameter, tokenModifiers); + let typeSymbol: Symbol | null | void; + if (node.type) { + typeSymbol = visitTypeNode(ctx, node.type); + } + if (node.initializer) { + visitExpression(ctx, node.initializer); + } + return typeSymbol; + } + function visitParameters(ctx: Context, nodes: NodeArray): void { + for (const parameter of nodes) { + visitParameterDeclaration(ctx, parameter); + } + } + function visitFunctionTypeNode(ctx: Context, node: FunctionTypeNode): Symbol | undefined | void { + visitDecorators(ctx, node.decorators); + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + visitTypeNode(ctx, node.type); + return (node).symbol; + } + function visitConstructorTypeNode(ctx: Context, node: ConstructorTypeNode): Symbol | undefined | void { + visitDecorators(ctx, node.decorators); + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + visitTypeNode(ctx, node.type); + return (node).symbol; + } + function visitImportTypeNode(ctx: Context, node: ImportTypeNode): void { + visitTypeNode(ctx, node.argument); + if (node.qualifier) { + visitEntityName(ctx, node.qualifier, (node.isTypeOf ? SemanticMeaning.Value : SemanticMeaning.Type)); + } + visitTypeArguments(ctx, node.typeArguments); + } + function visitTypeReference(ctx: Context, node: TypeReferenceNode): Symbol | undefined { + let symbol: Symbol | undefined; + if (node.typeName.kind === SyntaxKind.Identifier) { + symbol = visitIdentifier(ctx, node.typeName, SemanticMeaning.Type, CanUseLocals.True, IsCallExpression.False); + } + else { + symbol = visitQualifiedNameWithSplitMeaning(ctx, node.typeName, SemanticMeaning.Namespace, SemanticMeaning.Type); + } + visitTypeArguments(ctx, node.typeArguments); + return symbol; + } + function visitExpressionWithTypeArguments(ctx: Context, node: ExpressionWithTypeArguments): void { + if (node.parent.kind === SyntaxKind.HeritageClause && (node.parent.token === SyntaxKind.ExtendsKeyword || node.parent.token === SyntaxKind.ImplementsKeyword)) { + if (node.expression.kind === SyntaxKind.Identifier) { + visitIdentifier(ctx, node.expression, SemanticMeaning.Type, CanUseLocals.True, IsCallExpression.False); + } + else { + visitExpression(ctx, node.expression); + } + } + else { + visitExpression(ctx, node.expression); + } + visitTypeArguments(ctx, node.typeArguments); + } + function visitTypePredicateNode(ctx: Context, node: TypePredicateNode): void { + if (node.parameterName.kind === SyntaxKind.Identifier) { + visitIdentifier(ctx, node.parameterName, SemanticMeaning.Value, CanUseLocals.True, IsCallExpression.False); + } + if (node.type) { + visitTypeNode(ctx, node.type); + } + } + function visitTypeQueryNode(ctx: Context, node: TypeQueryNode): void { + visitEntityName(ctx, node.exprName, SemanticMeaning.Value); + } + function visitTypeLiteralNode(ctx: Context, node: TypeLiteralNode): void { + for (const member of node.members) { + switch (member.kind) { + case SyntaxKind.CallSignature: + visitCallSignatureDeclaration(ctx, member); + break; + case SyntaxKind.ConstructSignature: + visitConstructSignatureDeclaration(ctx, member); + break; + case SyntaxKind.PropertySignature: + visitPropertySignature(ctx, member); + break; + case SyntaxKind.MethodSignature: + visitMethodSignature(ctx, member); + break; + case SyntaxKind.IndexSignature: + visitIndexSignatureDeclaration(ctx, member); + break; + } + } + } + function visitArrayTypeNode(ctx: Context, node: ArrayTypeNode): void { + visitTypeNode(ctx, node.elementType); + } + function visitTupleTypeNode(ctx: Context, node: TupleTypeNode): void { + for (const elementType of node.elements) { + visitTypeNode(ctx, elementType); + } + } + function visitOptionalTypeNode(ctx: Context, node: OptionalTypeNode): void { + visitTypeNode(ctx, node.type); + } + function visitRestTypeNode(ctx: Context, node: RestTypeNode): void { + visitTypeNode(ctx, node.type); + } + function visitUnionTypeNode(ctx: Context, node: UnionTypeNode): void { + for (const type of node.types) { + visitTypeNode(ctx, type); + } + } + function visitIntersectionTypeNode(ctx: Context, node: IntersectionTypeNode): void { + for (const type of node.types) { + visitTypeNode(ctx, type); + } + } + function visitConditionalTypeNode(ctx: Context, node: ConditionalTypeNode): void { + visitTypeNode(ctx, node.checkType); + visitTypeNode(ctx, node.extendsType); + visitTypeNode(ctx, node.trueType); + visitTypeNode(ctx, node.falseType); + } + function visitInferTypeNode(ctx: Context, node: InferTypeNode): void { + visitTypeParameterDeclaration(ctx, node.typeParameter); + } + function visitParenthesizedTypeNode(ctx: Context, node: ParenthesizedTypeNode): Symbol | undefined | void { + return visitTypeNode(ctx, node.type); + } + function visitTypeOperatorNode(ctx: Context, node: TypeOperatorNode): void { + visitTypeNode(ctx, node.type); + } + function visitIndexedAccessTypeNode(ctx: Context, node: IndexedAccessTypeNode): void { + visitTypeNode(ctx, node.objectType); + visitTypeNode(ctx, node.indexType); } + function visitMappedTypeNode(ctx: Context, node: MappedTypeNode): void { + visitTypeParameterDeclaration(ctx, node.typeParameter); + if (node.type) { + visitTypeNode(ctx, node.type); + } + } + function visitTypeNode(ctx: Context, node: TypeNode): Symbol | undefined | void { + switch (node.kind) { + case SyntaxKind.FunctionType: + return visitFunctionTypeNode(ctx, node); + case SyntaxKind.ConstructorType: + return visitConstructorTypeNode(ctx, node); + case SyntaxKind.ImportType: + return visitImportTypeNode(ctx, node); + case SyntaxKind.TypeReference: + return visitTypeReference(ctx, node); + case SyntaxKind.ExpressionWithTypeArguments: + return visitExpressionWithTypeArguments(ctx, node); + case SyntaxKind.TypePredicate: + return visitTypePredicateNode(ctx, node); + case SyntaxKind.TypeQuery: + return visitTypeQueryNode(ctx, node); + case SyntaxKind.TypeLiteral: + return visitTypeLiteralNode(ctx, node); + case SyntaxKind.ArrayType: + return visitArrayTypeNode(ctx, node); + case SyntaxKind.TupleType: + return visitTupleTypeNode(ctx, node); + case SyntaxKind.OptionalType: + return visitOptionalTypeNode(ctx, node); + case SyntaxKind.RestType: + return visitRestTypeNode(ctx, node); + case SyntaxKind.UnionType: + return visitUnionTypeNode(ctx, node); + case SyntaxKind.IntersectionType: + return visitIntersectionTypeNode(ctx, node); + case SyntaxKind.ConditionalType: + return visitConditionalTypeNode(ctx, node); + case SyntaxKind.InferType: + return visitInferTypeNode(ctx, node); + case SyntaxKind.ParenthesizedType: + return visitParenthesizedTypeNode(ctx, node); + case SyntaxKind.TypeOperator: + return visitTypeOperatorNode(ctx, node); + case SyntaxKind.IndexedAccessType: + return visitIndexedAccessTypeNode(ctx, node); + case SyntaxKind.MappedType: + return visitMappedTypeNode(ctx, node); + // ignoring: - const enum SemanticMeaning { - None = 0x0, - Value = 0x1, - Type = 0x2, - Namespace = 0x4, - All = Value | Type | Namespace + // TODO: This was commented in importing + // case SyntaxKind.ThisKeyword: + // return ctx.getThisSymbol(); + + + // case SyntaxKind.JSDocTypeExpression: + // case SyntaxKind.JSDocAllType: + // case SyntaxKind.JSDocUnknownType: + // case SyntaxKind.JSDocNonNullableType: + // case SyntaxKind.JSDocNullableType: + // case SyntaxKind.JSDocOptionalType: + // case SyntaxKind.JSDocFunctionType: + // case SyntaxKind.JSDocVariadicType: + // case SyntaxKind.JSDocNamepathType: + // case SyntaxKind.JSDocSignature: + // case SyntaxKind.JSDocTypeLiteral: + // case SyntaxKind.LiteralType: + // case SyntaxKind.ThisType: + // case SyntaxKind.NullKeyword: + // case SyntaxKind.TrueKeyword + // case SyntaxKind.FalseKeyword: + // case SyntaxKind.AnyKeyword: + // case SyntaxKind.UnknownKeyword: + // case SyntaxKind.NumberKeyword: + // case SyntaxKind.BigIntKeyword: + // case SyntaxKind.ObjectKeyword: + // case SyntaxKind.BooleanKeyword: + // case SyntaxKind.StringKeyword: + // case SyntaxKind.SymbolKeyword: + // case SyntaxKind.ThisKeyword: + // case SyntaxKind.VoidKeyword: + // case SyntaxKind.UndefinedKeyword: + // case SyntaxKind.NullKeyword: + // case SyntaxKind.NeverKeyword: + } + } + function visitTypeArguments(ctx: Context, nodes: NodeArray | undefined): void { + if (nodes) { + for (const node of nodes) { + visitTypeNode(ctx, node); + } + } + } + function visitPrefixUnaryExpression(ctx: Context, node: PrefixUnaryExpression): void { + visitExpression(ctx, node.operand); + } + function visitPostfixUnaryExpression(ctx: Context, node: PostfixUnaryExpression): void { + visitExpression(ctx, node.operand); + } + function visitPartiallyEmittedExpression(ctx: Context, node: PartiallyEmittedExpression): void { + visitExpression(ctx, node.expression); + } + function visitFunctionExpression(ctx: Context, node: FunctionExpression): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name) { + ctx.pushResult(node.name, TokenType.function, tokenModifiers | (1 << TokenModifier.declaration)); + } + visitTypeParameters(ctx, node.typeParameters); + const newScopeThis = visitParametersAndGetThisSymbol(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + if (node.body) { + ctx.pushThisSymbol(newScopeThis); + ctx.pushScope((node).locals); + visitBlock(ctx, node.body); + ctx.popScope(); + ctx.popThisSymbol(); + } + } + function visitTemplateSpan(ctx: Context, node: TemplateSpan): void { + visitExpression(ctx, node.expression); + } + function visitTemplateExpression(ctx: Context, node: TemplateExpression): void { + // ignoring node.head + for (const templateSpan of node.templateSpans) { + visitTemplateSpan(ctx, templateSpan); + } + } + function visitParenthesizedExpression(ctx: Context, node: ParenthesizedExpression): Symbol | null | void { + return visitExpression(ctx, node.expression); + } + function visitArrayLiteralExpression(ctx: Context, node: ArrayLiteralExpression): void { + for (const element of node.elements) { + visitExpression(ctx, element); + } + } + function visitMethodDeclaration(ctx: Context, node: MethodDeclaration): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name.kind === SyntaxKind.Identifier) { + ctx.pushResult(node.name, TokenType.member, tokenModifiers | (1 << TokenModifier.declaration)); + } + else { + visitPropertyName(ctx, node.name); + } + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + if (node.body) { + ctx.pushScope((node).locals); + visitBlock(ctx, node.body); + ctx.popScope(); + } + } + function visitGetAccessorDeclaration(ctx: Context, node: GetAccessorDeclaration): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name.kind === SyntaxKind.Identifier) { + visitIdentifierInDeclaration(ctx, node.name, (node).symbol, SemanticMeaning.Value, TokenType.property, tokenModifiers); + } + else { + visitPropertyName(ctx, node.name); + } + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + if (node.body) { + ctx.pushScope((node).locals); + visitBlock(ctx, node.body); + ctx.popScope(); + } + } + function visitSetAccessorDeclaration(ctx: Context, node: SetAccessorDeclaration): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name.kind === SyntaxKind.Identifier) { + visitIdentifierInDeclaration(ctx, node.name, (node).symbol, SemanticMeaning.Value, TokenType.property, tokenModifiers); + } + else { + visitPropertyName(ctx, node.name); + } + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + if (node.body) { + ctx.pushScope((node).locals); + visitBlock(ctx, node.body); + ctx.popScope(); + } + } + function visitPropertyAssignment(ctx: Context, node: PropertyAssignment): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name.kind === SyntaxKind.Identifier) { + visitIdentifierInDeclaration(ctx, node.name, (node).symbol, SemanticMeaning.Value, TokenType.property, tokenModifiers); + } + else { + visitPropertyName(ctx, node.name); + } + visitExpression(ctx, node.initializer); + } + function visitShorthandPropertyAssignment(ctx: Context, node: ShorthandPropertyAssignment): void { + visitDecorators(ctx, node.decorators); + const tokenType = ctx.reclassifyWithType(node.name, TokenType.property); + ctx.pushResult(node.name, tokenType, (1 << TokenModifier.declaration)); + if (node.objectAssignmentInitializer) { + visitExpression(ctx, node.objectAssignmentInitializer); + } + } + function visitSpreadAssignment(ctx: Context, node: SpreadAssignment): void { + visitExpression(ctx, node.expression); } + function visitObjectLiteralExpression(ctx: Context, node: ObjectLiteralExpression): Symbol | null | void { + for (const property of node.properties) { + switch (property.kind) { + case SyntaxKind.MethodDeclaration: + visitMethodDeclaration(ctx, property); + break; + case SyntaxKind.GetAccessor: + visitGetAccessorDeclaration(ctx, property); + break; + case SyntaxKind.SetAccessor: + visitSetAccessorDeclaration(ctx, property); + break; + case SyntaxKind.PropertyAssignment: + visitPropertyAssignment(ctx, property); + break; + case SyntaxKind.ShorthandPropertyAssignment: + visitShorthandPropertyAssignment(ctx, property); + break; + case SyntaxKind.SpreadAssignment: + visitSpreadAssignment(ctx, property); + break; + } + } + return (node).symbol; + } + function visitNewExpression(ctx: Context, node: NewExpression): void { + visitExpression(ctx, node.expression); + visitTypeArguments(ctx, node.typeArguments); + if (node.arguments) { + for (const argument of node.arguments) { + visitExpression(ctx, argument); + } + } + } + function visitMetaProperty(ctx: Context, node: MetaProperty): void { + visitIdentifier(ctx, node.name, SemanticMeaning.Value, CanUseLocals.False, IsCallExpression.False); + } + function visitPropertyDeclaration(ctx: Context, node: PropertyDeclaration): void { + visitDecorators(ctx, node.decorators); + const modifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name.kind === SyntaxKind.Identifier) { + visitIdentifierInDeclaration(ctx, node.name, (node).symbol, SemanticMeaning.Value, TokenType.property, modifiers); + } + else { + visitPropertyName(ctx, node.name); + } + if (node.type) { + visitTypeNode(ctx, node.type); + } + if (node.initializer) { + visitExpression(ctx, node.initializer); + } + } + function visitConstructorDeclaration(ctx: Context, node: ConstructorDeclaration): void { + visitDecorators(ctx, node.decorators); + visitParameters(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + if (node.body) { + ctx.pushScope((node).locals); + visitBlock(ctx, node.body); + ctx.popScope(); + } + } + function visitClassElement(ctx: Context, node: ClassElement): void { + switch (node.kind) { + case SyntaxKind.PropertyDeclaration: + return visitPropertyDeclaration(ctx, node); + case SyntaxKind.MethodDeclaration: + return visitMethodDeclaration(ctx, node); + case SyntaxKind.Constructor: + return visitConstructorDeclaration(ctx, node); + case SyntaxKind.GetAccessor: + return visitGetAccessorDeclaration(ctx, node); + case SyntaxKind.SetAccessor: + return visitSetAccessorDeclaration(ctx, node); + case SyntaxKind.IndexSignature: + return visitIndexSignatureDeclaration(ctx, node); + // ignoring: + // case SyntaxKind.SemicolonClassElement: + } + } + function visitClassExpression(ctx: Context, node: ClassExpression): void { + visitDecorators(ctx, node.decorators); + if (node.name) { + visitIdentifierInDeclaration(ctx, node.name, (node).symbol, SemanticMeaning.Value, TokenType.class, 0); + } + visitTypeParameters(ctx, node.typeParameters); + if (node.heritageClauses) { + for (const heritageClause of node.heritageClauses) { + visitHeritageClause(ctx, heritageClause); + } + } + ctx.pushThisSymbol((node).symbol); + for (const member of node.members) { + visitClassElement(ctx, member); + } + ctx.popThisSymbol(); + } + function visitPropertyAccessExpression(ctx: Context, node: PropertyAccessExpression, isCallExpression: IsCallExpression): Symbol | null | void { + const leftSymbol = visitExpression(ctx, node.expression); + if (leftSymbol && (leftSymbol.getFlags() & valueSymbolFlags) === 0 && (leftSymbol.exports || leftSymbol.members)) { + let symbol: Symbol | undefined; + if (leftSymbol.exports) { + symbol = leftSymbol.exports.get(node.name.escapedText); + } + if (!symbol && leftSymbol.members) { + symbol = leftSymbol.members.get(node.name.escapedText); + } + if (symbol) { + // found it! + visitIdentifierWithSymbol(ctx, node.name, symbol, SemanticMeaning.Value, isCallExpression, IsRightSideOfExpression.True); + return symbol; + } + } + const symbol = ctx.getTypeCheckerSymbolAtLocation(node.name); + if (symbol) { + visitIdentifierWithSymbol(ctx, node.name, symbol, SemanticMeaning.Value, isCallExpression, IsRightSideOfExpression.True); + } + return symbol; + } + function visitElementAccessExpression(ctx: Context, node: ElementAccessExpression): void { + visitExpression(ctx, node.expression); + visitExpression(ctx, node.argumentExpression); + } + function visitTaggedTemplateExpression(ctx: Context, node: TaggedTemplateExpression): void { + visitExpression(ctx, node.tag); + visitTypeArguments(ctx, node.typeArguments); + visitExpression(ctx, node.template); + } + function visitCallExpression(ctx: Context, node: CallExpression): void { + if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { + visitPropertyAccessExpression(ctx, (node.expression), IsCallExpression.True); + } + else if (node.expression.kind === SyntaxKind.Identifier) { + visitIdentifier(ctx, node.expression, SemanticMeaning.Value, CanUseLocals.True, IsCallExpression.True); + } + else { + visitExpression(ctx, node.expression); + } + visitTypeArguments(ctx, node.typeArguments); + for (const argument of node.arguments) { + visitExpression(ctx, argument); + } + } + function visitNonNullExpression(ctx: Context, node: NonNullExpression): void { + visitExpression(ctx, node.expression); + } + function visitDeleteExpression(ctx: Context, node: DeleteExpression): void { + visitExpression(ctx, node.expression); + } + function visitTypeOfExpression(ctx: Context, node: TypeOfExpression): void { + visitExpression(ctx, node.expression); + } + function visitVoidExpression(ctx: Context, node: VoidExpression): void { + visitExpression(ctx, node.expression); + } + function visitAwaitExpression(ctx: Context, node: AwaitExpression): void { + visitExpression(ctx, node.expression); + } + function visitTypeAssertion(ctx: Context, node: TypeAssertion): Symbol | null | void { + const symbol = visitTypeNode(ctx, node.type); + visitExpression(ctx, node.expression); + return symbol; + } + function visitYieldExpression(ctx: Context, node: YieldExpression): void { + if (node.expression) { + visitExpression(ctx, node.expression); + } + } + function visitBinaryExpression(ctx: Context, node: BinaryExpression): void { + visitExpression(ctx, node.left); + visitExpression(ctx, node.right); + } + function visitConditionalExpression(ctx: Context, node: ConditionalExpression): void { + visitExpression(ctx, node.condition); + visitExpression(ctx, node.whenTrue); + visitExpression(ctx, node.whenFalse); + } + function visitArrowFunction(ctx: Context, node: ArrowFunction): void { + visitDecorators(ctx, node.decorators); + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + + ctx.pushScope((node).locals); + if (node.body.kind === SyntaxKind.Block) { + visitBlock(ctx, node.body); + } + else { + // must be expression + visitExpression(ctx, node.body); + } + ctx.popScope(); + } + function visitSpreadElement(ctx: Context, node: SpreadElement): void { + visitExpression(ctx, node.expression); + } + function visitAsExpression(ctx: Context, node: AsExpression): Symbol | null | void { + visitExpression(ctx, node.expression); + return visitTypeNode(ctx, node.type); + } + function visitCommaListExpression(ctx: Context, node: CommaListExpression): void { + for (const element of node.elements) { + visitExpression(ctx, element); + } + } + function visitJsxAttributes(ctx: Context, node: JsxAttributes): void { + for (const property of node.properties) { + if (property.kind === SyntaxKind.JsxAttribute) { + // ignoring property.name + if (property.initializer && property.initializer.kind === SyntaxKind.JsxExpression) { + visitJsxExpression(ctx, property.initializer); + } + } + // ignoring JsxSpreadAttribute + } + } + function visitJsxChild(ctx: Context, node: JsxChild): void { + switch (node.kind) { + case SyntaxKind.JsxElement: + return visitJsxElement(ctx, node); + case SyntaxKind.JsxSelfClosingElement: + return visitJsxSelfClosingElement(ctx, node); + case SyntaxKind.JsxFragment: + return visitJsxFragment(ctx, node); + case SyntaxKind.JsxExpression: + return visitJsxExpression(ctx, node); + // ignoring: + // case SyntaxKind.JsxText: + } + } + function visitJsxElement(ctx: Context, node: JsxElement): void { + visitJsxOpeningElement(ctx, node.openingElement); + for (const child of node.children) { + visitJsxChild(ctx, child); + } + // ignoring node.closingElement + } + function visitJsxSelfClosingElement(ctx: Context, node: JsxSelfClosingElement): void { + // ignoring node.tagName; + // ignoring node.typeArguments; + visitJsxAttributes(ctx, node.attributes); + } + function visitJsxFragment(ctx: Context, node: JsxFragment): void { + for (const child of node.children) { + visitJsxChild(ctx, child); + } + } + function visitJsxOpeningElement(ctx: Context, node: JsxOpeningElement): void { + // ignoring node.tagName; + // ignoring node.typeArguments; + visitJsxAttributes(ctx, node.attributes); + } + function visitJsxExpression(ctx: Context, node: JsxExpression): void { + if (node.expression) { + visitExpression(ctx, node.expression); + } + } + function visitExpression(ctx: Context, node: Expression): Symbol | null | void { + switch (node.kind) { + case SyntaxKind.PrefixUnaryExpression: + return visitPrefixUnaryExpression(ctx, node); + case SyntaxKind.PostfixUnaryExpression: + return visitPostfixUnaryExpression(ctx, node); + case SyntaxKind.PartiallyEmittedExpression: + return visitPartiallyEmittedExpression(ctx, node); + case SyntaxKind.Identifier: + return visitIdentifier(ctx, node, SemanticMeaning.Value, CanUseLocals.True, IsCallExpression.False); + case SyntaxKind.FunctionExpression: + return visitFunctionExpression(ctx, node); + case SyntaxKind.TemplateExpression: + return visitTemplateExpression(ctx, node); + case SyntaxKind.ParenthesizedExpression: + return visitParenthesizedExpression(ctx, node); + case SyntaxKind.ArrayLiteralExpression: + return visitArrayLiteralExpression(ctx, node); + case SyntaxKind.ObjectLiteralExpression: + return visitObjectLiteralExpression(ctx, node); + case SyntaxKind.NewExpression: + return visitNewExpression(ctx, node); + case SyntaxKind.MetaProperty: + return visitMetaProperty(ctx, node); + case SyntaxKind.ClassExpression: + return visitClassExpression(ctx, node); + case SyntaxKind.PropertyAccessExpression: + return visitPropertyAccessExpression(ctx, node, IsCallExpression.False); + case SyntaxKind.ElementAccessExpression: + return visitElementAccessExpression(ctx, node); + case SyntaxKind.TaggedTemplateExpression: + return visitTaggedTemplateExpression(ctx, node); + case SyntaxKind.CallExpression: + return visitCallExpression(ctx, node); + case SyntaxKind.NonNullExpression: + return visitNonNullExpression(ctx, node); + case SyntaxKind.DeleteExpression: + return visitDeleteExpression(ctx, node); + case SyntaxKind.TypeOfExpression: + return visitTypeOfExpression(ctx, node); + case SyntaxKind.VoidExpression: + return visitVoidExpression(ctx, node); + case SyntaxKind.AwaitExpression: + return visitAwaitExpression(ctx, node); + case SyntaxKind.TypeAssertionExpression: + return visitTypeAssertion(ctx, node); + case SyntaxKind.YieldExpression: + return visitYieldExpression(ctx, node); + case SyntaxKind.BinaryExpression: + return visitBinaryExpression(ctx, node); + case SyntaxKind.ConditionalExpression: + return visitConditionalExpression(ctx, node); + case SyntaxKind.ArrowFunction: + return visitArrowFunction(ctx, node); + case SyntaxKind.SpreadElement: + return visitSpreadElement(ctx, node); + case SyntaxKind.AsExpression: + return visitAsExpression(ctx, node); + case SyntaxKind.CommaListExpression: + return visitCommaListExpression(ctx, node); + case SyntaxKind.JsxAttributes: + return visitJsxAttributes(ctx, node); + case SyntaxKind.JsxElement: + return visitJsxElement(ctx, node); + case SyntaxKind.JsxSelfClosingElement: + return visitJsxSelfClosingElement(ctx, node); + case SyntaxKind.JsxFragment: + return visitJsxFragment(ctx, node); + case SyntaxKind.JsxOpeningElement: + return visitJsxOpeningElement(ctx, node); + case SyntaxKind.JsxExpression: + return visitJsxExpression(ctx, node); + // ignoring: + case SyntaxKind.ThisKeyword: + return ctx.getThisSymbol(); + // case SyntaxKind.JsxOpeningFragment: + // case SyntaxKind.JsxClosingFragment: + // case SyntaxKind.OmittedExpression: + // case SyntaxKind.SyntheticExpression: + // case SyntaxKind.NullKeyword: + // case SyntaxKind.TrueKeyword: + // case SyntaxKind.FalseKeyword: + // case SyntaxKind.SuperKeyword: + // case SyntaxKind.ImportKeyword: + // case SyntaxKind.StringLiteral: + // case SyntaxKind.RegularExpressionLiteral: + // case SyntaxKind.NoSubstitutionTemplateLiteral: + // case SyntaxKind.NumericLiteral: + // case SyntaxKind.BigIntLiteral: + } + } + function visitDecorator(ctx: Context, node: Decorator): void { + visitExpression(ctx, node.expression); + } + function visitDecorators(ctx: Context, decorators: NodeArray | undefined): void { + if (decorators) { + for (const decorator of decorators) { + visitDecorator(ctx, decorator); + } + } + } + function visitParametersAndGetThisSymbol(ctx: Context, parameters: NodeArray): Symbol | undefined { + let isFirst = true; + let result: Symbol | undefined; + for (const parameter of parameters) { + if (isFirst) { + isFirst = false; + if (parameter.name.kind === SyntaxKind.Identifier && parameter.name.getText() === "this") { + result = visitParameterDeclaration(ctx, parameter) || undefined; + } + else { + visitParameterDeclaration(ctx, parameter); + } + } + else { + visitParameterDeclaration(ctx, parameter); + } + } + return result; + } + function visitFunctionDeclaration(ctx: Context, node: FunctionDeclaration, parentKind: SyntaxKind): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name) { + ctx.pushResult(node.name, TokenType.function, tokenModifiers | (1 << TokenModifier.declaration) | (parentKind !== SyntaxKind.SourceFile ? (1 << TokenModifier.local) : 0)); + } + visitTypeParameters(ctx, node.typeParameters); + const newScopeThis = visitParametersAndGetThisSymbol(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + if (node.body) { + ctx.pushThisSymbol(newScopeThis); + ctx.pushScope((node).locals); + visitBlock(ctx, node.body); + ctx.popScope(); + ctx.popThisSymbol(); + } + } + function visitClassDeclaration(ctx: Context, node: ClassDeclaration): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name) { + visitIdentifierInDeclaration(ctx, node.name, (node).symbol, SemanticMeaning.Value | SemanticMeaning.Type, TokenType.class, tokenModifiers); + } + visitTypeParameters(ctx, node.typeParameters); + if (node.heritageClauses) { + for (const heritageClause of node.heritageClauses) { + visitHeritageClause(ctx, heritageClause); + } + } + ctx.pushThisSymbol((node).symbol); + for (const member of node.members) { + visitClassElement(ctx, member); + } + ctx.popThisSymbol(); + } + function visitMissingDeclaration(ctx: Context, node: MissingDeclaration): void { + visitDecorators(ctx, node.decorators); + } + function visitTypeParameterDeclaration(ctx: Context, node: TypeParameterDeclaration): void { + visitIdentifierInDeclaration(ctx, node.name, (node).symbol, SemanticMeaning.Type, TokenType.typeParameter, 0); + if (node.constraint) { + visitTypeNode(ctx, node.constraint); + } + if (node.default) { + visitTypeNode(ctx, node.default); + } + if (node.expression) { + visitExpression(ctx, node.expression); + } + } + function visitTypeParameters(ctx: Context, nodes: NodeArray | undefined): void { + if (nodes) { + for (const typeParameter of nodes) { + visitTypeParameterDeclaration(ctx, typeParameter); + } + } + } + function visitHeritageClause(ctx: Context, node: HeritageClause): void { + for (const type of node.types) { + visitExpressionWithTypeArguments(ctx, type); + } + } + function visitCallSignatureDeclaration(ctx: Context, node: CallSignatureDeclaration): void { + visitDecorators(ctx, node.decorators); + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + } + function visitConstructSignatureDeclaration(ctx: Context, node: ConstructSignatureDeclaration): void { + visitDecorators(ctx, node.decorators); + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + } + function visitPropertySignature(ctx: Context, node: PropertySignature): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name.kind === SyntaxKind.Identifier) { + visitIdentifierInDeclaration(ctx, node.name, (node).symbol, SemanticMeaning.Value, TokenType.property, tokenModifiers); + } + else { + visitPropertyName(ctx, node.name); + } + if (node.type) { + visitTypeNode(ctx, node.type); + } + if (node.initializer) { + visitExpression(ctx, node.initializer); + } + } + function visitMethodSignature(ctx: Context, node: MethodSignature): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name.kind === SyntaxKind.Identifier) { + visitIdentifierInDeclaration(ctx, node.name, (node).symbol, SemanticMeaning.Value, TokenType.member, tokenModifiers); + } + else { + visitPropertyName(ctx, node.name); + } + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + } + function visitIndexSignatureDeclaration(ctx: Context, node: IndexSignatureDeclaration): void { + visitDecorators(ctx, node.decorators); + visitTypeParameters(ctx, node.typeParameters); + visitParameters(ctx, node.parameters); + if (node.type) { + visitTypeNode(ctx, node.type); + } + } + function visitInterfaceDeclaration(ctx: Context, node: InterfaceDeclaration): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + ctx.pushResult(node.name, TokenType.interface, tokenModifiers | (1 << TokenModifier.declaration)); + visitTypeParameters(ctx, node.typeParameters); + if (node.heritageClauses) { + for (const heritageClause of node.heritageClauses) { + visitHeritageClause(ctx, heritageClause); + } + } + for (const member of node.members) { + switch (member.kind) { + case SyntaxKind.CallSignature: + visitCallSignatureDeclaration(ctx, member); + break; + case SyntaxKind.ConstructSignature: + visitConstructSignatureDeclaration(ctx, member); + break; + case SyntaxKind.PropertySignature: + visitPropertySignature(ctx, member); + break; + case SyntaxKind.MethodSignature: + visitMethodSignature(ctx, member); + break; + case SyntaxKind.IndexSignature: + visitIndexSignatureDeclaration(ctx, member); + break; + } + } + } + function visitTypeAliasDeclaration(ctx: Context, node: TypeAliasDeclaration): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + ctx.pushResult(node.name, TokenType.type, tokenModifiers | (1 << TokenModifier.declaration)); + visitTypeParameters(ctx, node.typeParameters); + visitTypeNode(ctx, node.type); + } + function visitEnumDeclaration(ctx: Context, node: EnumDeclaration): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + ctx.pushResult(node.name, TokenType.enum, tokenModifiers | (1 << TokenModifier.declaration)); + + const symbol = (node).symbol; + ctx.pushLocals(symbol ? symbol.exports : undefined); + for (const enumMember of node.members) { + if (enumMember.name.kind === SyntaxKind.Identifier) { + ctx.pushResult(enumMember.name, TokenType.enumMember, (1 << TokenModifier.declaration) | (1 << TokenModifier.readonly)); + } + else { + visitPropertyName(ctx, enumMember.name); + } + if (enumMember.initializer) { + visitExpression(ctx, enumMember.initializer); + } + } + ctx.popLocals(); + } + function visitModuleBlock(ctx: Context, node: ModuleBlock): void { + for (const statement of node.statements) { + visitStatement(ctx, statement, SyntaxKind.ModuleBlock); + } + } + function visitModuleDeclaration(ctx: Context, node: ModuleDeclaration): void { + visitDecorators(ctx, node.decorators); + const tokenModifiers = nodeModifiersToTokenModifiers(node.modifiers); + if (node.name.kind === SyntaxKind.Identifier) { + ctx.pushResult(node.name, TokenType.namespace, tokenModifiers | (1 << TokenModifier.declaration)); + } + if (node.body) { + ctx.pushScope((node).locals); + switch (node.body.kind) { + case SyntaxKind.ModuleBlock: + visitModuleBlock(ctx, node.body); + break; + case SyntaxKind.ModuleDeclaration: + visitModuleDeclaration(ctx, node.body); + break; + // ignoring: + // case SyntaxKind.Identifier: + } + ctx.popScope(); + } + } + function visitExternalModuleReference(ctx: Context, node: ExternalModuleReference): void { + if (node.expression.kind === SyntaxKind.Identifier) { + visitIdentifier(ctx, node.expression, SemanticMeaning.All, CanUseLocals.True, IsCallExpression.False); + } + else { + visitExpression(ctx, node.expression); + } + } + function visitImportEqualsDeclaration(ctx: Context, node: ImportEqualsDeclaration): void { + visitDecorators(ctx, node.decorators); + ctx.pushResult(node.name, TokenType.namespace, 0); + if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) { + visitExternalModuleReference(ctx, node.moduleReference); + } + else { + // isInternalModuleImportEqualsDeclaration + // import a = |b|; // Namespace + // import a = |b.c|; // Value, type, namespace + // import a = |b.c|.d; // Namespace + if (node.moduleReference.kind === SyntaxKind.Identifier) { + visitIdentifier(ctx, node.moduleReference, SemanticMeaning.Namespace, CanUseLocals.True, IsCallExpression.False); + } + else { + visitQualifiedNameWithSplitMeaning(ctx, node.moduleReference, SemanticMeaning.Namespace, SemanticMeaning.All); + } + } + } + function visitNamespaceExportDeclaration(ctx: Context, node: NamespaceExportDeclaration): void { + if (node.name) { + visitIdentifier(ctx, node.name, SemanticMeaning.Namespace | SemanticMeaning.Value, CanUseLocals.True, IsCallExpression.False); + } + } + function visitExportSpecifier(ctx: Context, node: ExportSpecifier): void { + if (node.propertyName) { + visitIdentifier(ctx, node.propertyName, SemanticMeaning.Value, CanUseLocals.True, IsCallExpression.False); + } + visitIdentifier(ctx, node.name, SemanticMeaning.Value, CanUseLocals.True, IsCallExpression.False); + } + function visitNamedExports(ctx: Context, node: NamedExportBindings): void { + if (isNamedExports(node)) { + for (const element of node.elements) { + visitExportSpecifier(ctx, element); + } + } + else if(isNamespaceExport(node)) { + // TODO: Noop probably seems right here, double check + } + } + function visitExportDeclaration(ctx: Context, node: ExportDeclaration): void { + visitDecorators(ctx, node.decorators); + if (node.exportClause) { + visitNamedExports(ctx, node.exportClause); + } + if (node.moduleSpecifier) { + visitExpression(ctx, node.moduleSpecifier); + } + } + function visitExportAssignment(ctx: Context, node: ExportAssignment): void { + visitDecorators(ctx, node.decorators); + if (node.expression.kind === SyntaxKind.Identifier) { + visitIdentifier(ctx, node.expression, SemanticMeaning.All, CanUseLocals.True, IsCallExpression.False); + } + else { + visitExpression(ctx, node.expression); + } + } + function visitBlock(ctx: Context, node: Block): void { + ctx.pushScope((node).locals); + for (const statement of node.statements) { + visitStatement(ctx, statement, SyntaxKind.Block); + } + ctx.popScope(); + } + function visitBindingName(ctx: Context, node: BindingName, symbol: Symbol | undefined, tokenType: TokenType.variable | TokenType.parameter, tokenModifiers: number): void { + switch (node.kind) { + case SyntaxKind.Identifier: + visitIdentifierInDeclaration(ctx, node, symbol, SemanticMeaning.Value, tokenType, tokenModifiers); + break; + case SyntaxKind.ObjectBindingPattern: + visitObjectBindingPattern(ctx, node, tokenType, tokenModifiers); + break; + case SyntaxKind.ArrayBindingPattern: + visitArrayBindingPattern(ctx, node, tokenType, tokenModifiers); + break; + } + + } + function visitBindingElement(ctx: Context, node: BindingElement, tokenType: TokenType.variable | TokenType.parameter, tokenModifiers: number): void { + visitDecorators(ctx, node.decorators); + if (node.propertyName) { + if (node.propertyName.kind === SyntaxKind.Identifier) { + visitIdentifier(ctx, node.propertyName, SemanticMeaning.Value, CanUseLocals.False, IsCallExpression.False); + } + else { + visitPropertyName(ctx, node.propertyName); + } + } + if (node.name) { + visitBindingName(ctx, node.name, (node).symbol, tokenType, tokenModifiers); + } + if (node.initializer) { + visitExpression(ctx, node.initializer); + } + } + function visitObjectBindingPattern(ctx: Context, node: ObjectBindingPattern, tokenType: TokenType.variable | TokenType.parameter, tokenModifiers: number): void { + for (const element of node.elements) { + visitBindingElement(ctx, element, tokenType, tokenModifiers); + } + } + function visitArrayBindingPattern(ctx: Context, node: ArrayBindingPattern, tokenType: TokenType.variable | TokenType.parameter, tokenModifiers: number): void { + for (const element of node.elements) { + if (element.kind === SyntaxKind.BindingElement) { + visitBindingElement(ctx, element, tokenType, tokenModifiers); + } + } + } + function visitVariableDeclaration(ctx: Context, node: VariableDeclaration, tokenModifiers: number): void { + visitDecorators(ctx, node.decorators); + visitBindingName(ctx, node.name, (node).symbol, TokenType.variable, tokenModifiers); + if (node.type) { + visitTypeNode(ctx, node.type); + } + if (node.initializer) { + visitExpression(ctx, node.initializer); + } + } + function visitVariableDeclarationList(ctx: Context, node: VariableDeclarationList, tokenModifiers: number): void { + if (node.flags & NodeFlags.Const) { + tokenModifiers |= (1 << TokenModifier.readonly); + } + for (const decl of node.declarations) { + visitVariableDeclaration(ctx, decl, tokenModifiers); + } + } + function visitVariableStatement(ctx: Context, node: VariableStatement, parentKind: SyntaxKind): void { + visitDecorators(ctx, node.decorators); + let tokenModifiers = 0; + if (parentKind !== SyntaxKind.SourceFile) { + tokenModifiers |= (1 << TokenModifier.local); + } + visitVariableDeclarationList(ctx, node.declarationList, tokenModifiers); + } + function visitExpressionStatement(ctx: Context, node: ExpressionStatement): void { + visitExpression(ctx, node.expression); + } + function visitIfStatement(ctx: Context, node: IfStatement): void { + visitExpression(ctx, node.expression); + visitStatement(ctx, node.thenStatement, SyntaxKind.IfStatement); + if (node.elseStatement) { + visitStatement(ctx, node.elseStatement, SyntaxKind.IfStatement); + } + } + function visitDoStatement(ctx: Context, node: DoStatement): void { + visitStatement(ctx, node.statement, SyntaxKind.DoStatement); + visitExpression(ctx, node.expression); + } + function visitWhileStatement(ctx: Context, node: WhileStatement): void { + visitExpression(ctx, node.expression); + visitStatement(ctx, node.statement, SyntaxKind.WhileStatement); + } + function visitForInitializer(ctx: Context, node: ForInitializer): void { + if (node.kind === SyntaxKind.VariableDeclarationList) { + visitVariableDeclarationList(ctx, node, 1 << TokenModifier.local); + } + else { + visitExpression(ctx, node); + } + } + function visitForStatement(ctx: Context, node: ForStatement): void { + ctx.pushScope((node).locals); + if (node.initializer) { + visitForInitializer(ctx, node.initializer); + } + if (node.condition) { + visitExpression(ctx, node.condition); + } + if (node.incrementor) { + visitExpression(ctx, node.incrementor); + } + visitStatement(ctx, node.statement, SyntaxKind.ForOfStatement); + ctx.popScope(); + } + function visitForInStatement(ctx: Context, node: ForInStatement): void { + ctx.pushScope((node).locals); + visitForInitializer(ctx, node.initializer); + visitExpression(ctx, node.expression); + visitStatement(ctx, node.statement, SyntaxKind.ForOfStatement); + ctx.popScope(); + } + function visitForOfStatement(ctx: Context, node: ForOfStatement): void { + ctx.pushScope((node).locals); + visitForInitializer(ctx, node.initializer); + visitExpression(ctx, node.expression); + visitStatement(ctx, node.statement, SyntaxKind.ForOfStatement); + ctx.popScope(); + } + function visitCaseClause(ctx: Context, node: CaseClause): void { + visitExpression(ctx, node.expression); + for (const statement of node.statements) { + visitStatement(ctx, statement, SyntaxKind.CaseClause); + } + } + function visitDefaultClause(ctx: Context, node: DefaultClause): void { + for (const statement of node.statements) { + visitStatement(ctx, statement, SyntaxKind.CaseClause); + } + } + function visitCaseBlock(ctx: Context, node: CaseBlock): void { + ctx.pushScope((node).locals); + for (const clause of node.clauses) { + if (clause.kind === SyntaxKind.CaseClause) { + visitCaseClause(ctx, clause); + } + else { + visitDefaultClause(ctx, clause); + } + } + ctx.popScope(); + } + function visitSwitchStatement(ctx: Context, node: SwitchStatement): void { + visitExpression(ctx, node.expression); + visitCaseBlock(ctx, node.caseBlock); + } + function visitLabeledStatement(ctx: Context, node: LabeledStatement): void { + // ignoring node.label + visitStatement(ctx, node.statement, SyntaxKind.LabeledStatement); + } + function visitThrowStatement(ctx: Context, node: ThrowStatement): void { + if (node.expression) { + visitExpression(ctx, node.expression); + } + } + function visitCatchClause(ctx: Context, node: CatchClause): void { + if (node.variableDeclaration) { + visitVariableDeclaration(ctx, node.variableDeclaration, (1 << TokenModifier.local)); + } + visitBlock(ctx, node.block); + } + function visitTryStatement(ctx: Context, node: TryStatement): void { + visitBlock(ctx, node.tryBlock); + if (node.catchClause) { + ctx.pushScope((node).locals); + visitCatchClause(ctx, node.catchClause); + ctx.popScope(); + } + if (node.finallyBlock) { + visitBlock(ctx, node.finallyBlock); + } + } + function visitReturnStatement(ctx: Context, node: ReturnStatement): void { + if (node.expression) { + visitExpression(ctx, node.expression); + } + } + function visitStatement(ctx: Context, node: Statement, parentKind: SyntaxKind): void { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(ctx, node, parentKind); + case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(ctx, node); + case SyntaxKind.MissingDeclaration: + return visitMissingDeclaration(ctx, node); + case SyntaxKind.InterfaceDeclaration: + return visitInterfaceDeclaration(ctx, node); + case SyntaxKind.TypeAliasDeclaration: + return visitTypeAliasDeclaration(ctx, node); + case SyntaxKind.EnumDeclaration: + return visitEnumDeclaration(ctx, node); + case SyntaxKind.ModuleDeclaration: + return visitModuleDeclaration(ctx, node); + case SyntaxKind.ImportEqualsDeclaration: + return visitImportEqualsDeclaration(ctx, node); + case SyntaxKind.NamespaceExportDeclaration: + return visitNamespaceExportDeclaration(ctx, node); + case SyntaxKind.ExportDeclaration: + return visitExportDeclaration(ctx, node); + case SyntaxKind.ExportAssignment: + return visitExportAssignment(ctx, node); + case SyntaxKind.Block: + return visitBlock(ctx, node); + case SyntaxKind.VariableStatement: + return visitVariableStatement(ctx, node, parentKind); + case SyntaxKind.ExpressionStatement: + return visitExpressionStatement(ctx, node); + case SyntaxKind.IfStatement: + return visitIfStatement(ctx, node); + case SyntaxKind.DoStatement: + return visitDoStatement(ctx, node); + case SyntaxKind.WhileStatement: + return visitWhileStatement(ctx, node); + case SyntaxKind.ForStatement: + return visitForStatement(ctx, node); + case SyntaxKind.ForInStatement: + return visitForInStatement(ctx, node); + case SyntaxKind.ForOfStatement: + return visitForOfStatement(ctx, node); + case SyntaxKind.SwitchStatement: + return visitSwitchStatement(ctx, node); + case SyntaxKind.LabeledStatement: + return visitLabeledStatement(ctx, node); + case SyntaxKind.ThrowStatement: + return visitThrowStatement(ctx, node); + case SyntaxKind.TryStatement: + return visitTryStatement(ctx, node); + case SyntaxKind.ReturnStatement: + return visitReturnStatement(ctx, node); + + // ignoring + // case SyntaxKind.ImportDeclaration: + // case SyntaxKind.BreakStatement: + // case SyntaxKind.ContinueStatement: + // case SyntaxKind.WithStatement: + // case SyntaxKind.NotEmittedStatement: + // case SyntaxKind.EmptyStatement: + // case SyntaxKind.DebuggerStatement: + } + } + function visitSourceFile(ctx: Context, node: SourceFile): void { + ctx.pushScope((node).locals); + for (const statement of node.statements) { + visitStatement(ctx, statement, SyntaxKind.SourceFile); + } + ctx.popScope(); + } + + function findBindingElementParentDeclaration(element: BindingElement): VariableDeclaration | ParameterDeclaration { + while (true) { + if (element.parent.parent.kind === SyntaxKind.BindingElement) { + element = element.parent.parent; + } + else { + return element.parent.parent; + } + } + } + + function isLocalDeclaration(decl: Declaration, sourceFile: SourceFile): boolean { + if (isBindingElement(decl)) { + decl = findBindingElementParentDeclaration(decl); + } + if (isVariableDeclaration(decl) || isBindingElement(decl)) { + return (!isSourceFile(decl.parent.parent.parent) || isCatchClause(decl.parent)) && decl.getSourceFile() === sourceFile; + } + else if (isFunctionDeclaration(decl)) { + return !isSourceFile(decl.parent) && decl.getSourceFile() === sourceFile; + } + return false; + } + + function typeHasConstructSignatures(type: Type): boolean { + if (type.isUnion()) { + for (const t of type.types) { + if (t.getConstructSignatures().length > 0) { + return true; + } + } + return false; + } + return (type.getConstructSignatures().length > 0); + } + + function typeHasCallSignatures(type: Type): boolean { + if (type.isUnion()) { + for (const t of type.types) { + if (t.getCallSignatures().length > 0) { + return true; + } + } + return false; + } + return (type.getCallSignatures().length > 0); + } + + function typeHasProperties(type: Type): boolean { + if (type.isUnion()) { + for (const t of type.types) { + if (t.getProperties().length > 0) { + return true; + } + } + return false; + } + return (type.getProperties().length > 0); + } + + + + export function compareTokens(sourceFile: SourceFile, actualTokens: number[], oracleTokens: number[]): string[] { + const result: string[] = []; + for (let i = 0, len = Math.max(oracleTokens.length / 3, actualTokens.length / 3); i < len; i++) { + const offset = 3 * i; + const oracleOffset = (offset < oracleTokens.length ? oracleTokens[offset] : -1); + const oracleLength = (offset < oracleTokens.length ? oracleTokens[offset + 1] : -1); + const oracleType = (offset < oracleTokens.length ? oracleTokens[offset + 2] : -1); + const actualOffset = (offset < actualTokens.length ? actualTokens[offset] : -1); + const actualLength = (offset < actualTokens.length ? actualTokens[offset + 1] : -1); + const actualType = (offset < actualTokens.length ? actualTokens[offset + 2] : -1); + if (oracleOffset !== actualOffset || oracleLength !== actualLength || oracleType !== actualType) { + let debugStr = ``; + debugStr += `THEIRS: ${printSemanticToken(sourceFile, oracleOffset, oracleLength, oracleType)}\n`; + debugStr += `OURS: ${printSemanticToken(sourceFile, actualOffset, actualLength, actualType)}\n`; + // const oracleNode = findNodeEncompassingRange(sourceFile, oracleOffset, oracleLength); + // const actualNode = findNodeEncompassingRange(sourceFile, actualOffset, actualLength); + result.push(debugStr); + if (oracleOffset !== actualOffset) { + break; + } + } + } + return result; + + function printSemanticToken(sourceFile: SourceFile, offset: number, length: number, type: number): string { + if (offset === -1) { + return ``; + } + const node = findNodeEncompassingRange(sourceFile, offset, length); + const chain: string[] = []; + let tmp: Node | undefined = node; + while (tmp) { + chain.push((tmp).__debugKind); + tmp = tmp.parent; + } + chain.reverse(); + const tokenType = (type >>> TokenEncodingConsts.typeOffset) - 1; + const tokenModifiers = (type & TokenEncodingConsts.modifierMask); + const lineChar = sourceFile.getLineAndCharacterOfPosition(node.pos + node.getLeadingTriviaWidth()); + + return `<<<${node.getText()}>>> @ ${lineChar.line + 1}:${lineChar.character} --- ${chain.join(">")} :::: ${tokenTypeToString(tokenType)} - [${tokenModifiersToString(tokenModifiers)}]`; + } + function findNodeEncompassingRange(sourceFile: SourceFile, offset: number, length: number): Node { + const searchStart = offset; + const searchEnd = offset + length; + let current: Node = sourceFile; + outer: while (true) { + // find the child that contains 'position' + for (const child of current.getChildren(sourceFile)) { + const childStart = child.getStart(sourceFile, /* includeJsDocComment */ true); + const childEnd = child.getEnd(); + if (childStart <= searchStart && searchEnd <= childEnd) { + // Good child + current = child; + continue outer; + } + } + return current; + } + } + function tokenTypeToString(tokenType: TokenType): string { + switch (tokenType) { + case TokenType.class: return "class"; + case TokenType.enum: return "enum"; + case TokenType.interface: return "interface"; + case TokenType.namespace: return "namespace"; + case TokenType.typeParameter: return "typeParameter"; + case TokenType.type: return "type"; + case TokenType.parameter: return "parameter"; + case TokenType.variable: return "variable"; + case TokenType.enumMember: return "enumMember"; + case TokenType.property: return "property"; + case TokenType.function: return "function"; + case TokenType.member: return "member"; + } + return ""; + } + function tokenModifierToString(tokenModifier: TokenModifier): string { + switch (tokenModifier) { + case TokenModifier.declaration: return "declaration"; + case TokenModifier.static: return "static"; + case TokenModifier.async: return "async"; + case TokenModifier.readonly: return "readonly"; + case TokenModifier.defaultLibrary: return "defaultLibrary"; + case TokenModifier.local: return "local"; + } + return ""; + } + function tokenModifiersToString(tokenModifiers: number): string[] { + const result: string[] = []; + for (let i = 0; i < TokenModifier._; i++) { + const mask = ((1 << i) >>> 0); + if (tokenModifiers & mask) { + result.push(tokenModifierToString(i)); + } + } + return result; + } + } + + + /// CUT - function getMeaningFromLocation(node: Node): SemanticMeaning { - const f = (ts).getMeaningFromLocation; - if (typeof f === "function") { - return f(node); - } - return SemanticMeaning.All; - } - - const tokenFromDeclarationMapping: { [name: string]: TokenType } = { - [SyntaxKind.VariableDeclaration]: TokenType.variable, - [SyntaxKind.Parameter]: TokenType.parameter, - [SyntaxKind.PropertyDeclaration]: TokenType.property, - [SyntaxKind.ModuleDeclaration]: TokenType.namespace, - [SyntaxKind.EnumDeclaration]: TokenType.enum, - [SyntaxKind.EnumMember]: TokenType.enumMember, - [SyntaxKind.ClassDeclaration]: TokenType.class, - [SyntaxKind.MethodDeclaration]: TokenType.member, - [SyntaxKind.FunctionDeclaration]: TokenType.function, - [SyntaxKind.FunctionExpression]: TokenType.function, - [SyntaxKind.MethodSignature]: TokenType.member, - [SyntaxKind.GetAccessor]: TokenType.property, - [SyntaxKind.SetAccessor]: TokenType.property, - [SyntaxKind.PropertySignature]: TokenType.property, - [SyntaxKind.InterfaceDeclaration]: TokenType.interface, - [SyntaxKind.TypeAliasDeclaration]: TokenType.type, - [SyntaxKind.TypeParameter]: TokenType.typeParameter, - [SyntaxKind.PropertyAssignment]: TokenType.property, - [SyntaxKind.ShorthandPropertyAssignment]: TokenType.property - }; + // const tokenFromDeclarationMapping: { [name: string]: TokenType } = { + // [SyntaxKind.VariableDeclaration]: TokenType.variable, + // [SyntaxKind.Parameter]: TokenType.parameter, + // [SyntaxKind.PropertyDeclaration]: TokenType.property, + // [SyntaxKind.ModuleDeclaration]: TokenType.namespace, + // [SyntaxKind.EnumDeclaration]: TokenType.enum, + // [SyntaxKind.EnumMember]: TokenType.enumMember, + // [SyntaxKind.ClassDeclaration]: TokenType.class, + // [SyntaxKind.MethodDeclaration]: TokenType.member, + // [SyntaxKind.FunctionDeclaration]: TokenType.function, + // [SyntaxKind.FunctionExpression]: TokenType.function, + // [SyntaxKind.MethodSignature]: TokenType.member, + // [SyntaxKind.GetAccessor]: TokenType.property, + // [SyntaxKind.SetAccessor]: TokenType.property, + // [SyntaxKind.PropertySignature]: TokenType.property, + // [SyntaxKind.InterfaceDeclaration]: TokenType.interface, + // [SyntaxKind.TypeAliasDeclaration]: TokenType.type, + // [SyntaxKind.TypeParameter]: TokenType.typeParameter, + // [SyntaxKind.PropertyAssignment]: TokenType.property, + // [SyntaxKind.ShorthandPropertyAssignment]: TokenType.property + // }; } diff --git a/src/services/services.ts b/src/services/services.ts index 25e9e2ec6ef66..eba1dc21efb2c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1825,7 +1825,10 @@ namespace ts { return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span); } else { - return classifier.modern.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span); + return { + endOfLineState: EndOfLineState.None, + spans: classifier.modern.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span) + }; } } From 7d13c29c45f6ab519550306927cf1866a7c6f273 Mon Sep 17 00:00:00 2001 From: Orta Date: Mon, 29 Jun 2020 15:51:20 -0400 Subject: [PATCH 14/14] Gets the tests green --- src/harness/fourslashImpl.ts | 6 ++-- .../semanticClassificationClassExpression.ts | 14 +++++----- ...stantiatedModuleWithVariableOfSameName1.ts | 4 +-- ...stantiatedModuleWithVariableOfSameName2.ts | 25 ++++++++--------- .../semanticClassificationWithUnionTypes.ts | 28 +++++++++---------- .../syntacticClassificationsForOfKeyword.ts | 10 +++---- .../syntacticClassificationsForOfKeyword2.ts | 4 +-- .../syntacticClassificationsForOfKeyword3.ts | 10 +++---- 8 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index bc9927436d994..511a03c69a0a5 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2497,15 +2497,15 @@ namespace FourSlash { jsonMismatchString()); } - ts.zipWith(expected, actual, (expectedClassification, actualClassification) => { + ts.zipWith(expected, actual, (expectedClassification, actualClassification, index) => { const expectedType = expectedClassification.classificationType; const actualType = typeof actualClassification.classificationType === "number" ? this.classificationToIdentifier(actualClassification.classificationType) : actualClassification.classificationType; if (expectedType !== actualType) { - this.raiseError("verifyClassifications failed - expected classifications type to be " + + this.raiseError(`verifyClassifications failed - expected classifications at index ${index} type to be ` + expectedType + ", but was " + actualType + - jsonMismatchString()); + displayExpectedAndActualString(JSON.stringify(expectedType, undefined, 4), JSON.stringify(actualType, undefined, 5))); } const expectedSpan = expectedClassification.textSpan; diff --git a/tests/cases/fourslash/semanticClassificationClassExpression.ts b/tests/cases/fourslash/semanticClassificationClassExpression.ts index eda79ce8f3d39..6616f61beb0b0 100644 --- a/tests/cases/fourslash/semanticClassificationClassExpression.ts +++ b/tests/cases/fourslash/semanticClassificationClassExpression.ts @@ -11,11 +11,11 @@ verify.semanticClassificationsAre("original", c.className("B", test.marker("3").position) ); -const c2 = classification("2020"); -verify.semanticClassificationsAre("2020", - c2.semanticToken("class.declaration", "x"), - c2.semanticToken("class", "C"), - c2.semanticToken("class.declaration", "C"), - c2.semanticToken("class.declaration", "D"), - c2.semanticToken("class", "B"), +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("class.declaration", "x"), + c2.semanticToken("class.declaration", "C"), + c2.semanticToken("class.declaration", "C"), + c2.semanticToken("class.declaration", "D"), + c2.semanticToken("class.declaration", "B"), ); diff --git a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts index 6c1e24e2d94f0..6a495a279bf5f 100644 --- a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts +++ b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName1.ts @@ -13,11 +13,11 @@ verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("0").position), c.interfaceName("I", test.marker("1").position)); + const c2 = classification("2020"); verify.semanticClassificationsAre("2020", - c2.semanticToken("variable", "M"), + c2.semanticToken("namespace.declaration", "M"), c2.semanticToken("interface.declaration", "I"), c2.semanticToken("variable.declaration", "M"), c2.semanticToken("property.declaration", "I"), ); - \ No newline at end of file diff --git a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts index 1566d333c4e37..9d45176096a4f 100644 --- a/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts +++ b/tests/cases/fourslash/semanticClassificationUninstantiatedModuleWithVariableOfSameName2.ts @@ -21,17 +21,16 @@ verify.semanticClassificationsAre("original", c.moduleName("M", test.marker("3").position), c.interfaceName("I", test.marker("4").position)); -const c2 = classification("2020"); -verify.semanticClassificationsAre("2020", - c2.semanticToken("variable", "M"), - c2.semanticToken("interface.declaration", "I"), - c2.semanticToken("variable.declaration", "M"), - c2.semanticToken("property.declaration", "foo"), - c2.semanticToken("property.declaration", "bar"), - c2.semanticToken("variable.declaration", "v"), - c2.semanticToken("variable", "M"), - c2.semanticToken("interface", "I"), - c2.semanticToken("variable.declaration", "x"), - c2.semanticToken("variable", "M"), +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("variable.declaration", "M"), + c2.semanticToken("property.declaration", "foo"), + c2.semanticToken("property.declaration", "bar"), + c2.semanticToken("variable.declaration", "v"), + c2.semanticToken("variable", "M"), + c2.semanticToken("interface", "I"), + c2.semanticToken("variable.declaration", "x"), + c2.semanticToken("variable", "M"), ); - \ No newline at end of file diff --git a/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts b/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts index 257130392df1f..a56a0ce888431 100644 --- a/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts +++ b/tests/cases/fourslash/semanticClassificationWithUnionTypes.ts @@ -23,18 +23,18 @@ verify.semanticClassificationsAre("original", c.className("C", test.marker("7").position), c.className("C", test.marker("8").position)); -const c2 = classification("2020"); -verify.semanticClassificationsAre("2020", - c2.semanticToken("variable", "M"), - c2.semanticToken("interface.declaration", "I"), - c2.semanticToken("interface.declaration", "I"), - c2.semanticToken("class.declaration", "C"), - c2.semanticToken("variable.declaration", "M"), - c2.semanticToken("variable", "M"), - c2.semanticToken("interface", "I"), - c2.semanticToken("interface", "I"), - c2.semanticToken("class", "C"), - c2.semanticToken("class.declaration", "I"), - c2.semanticToken("variable", "M"), - c2.semanticToken("class", "C"), +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("namespace.declaration", "M"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("interface.declaration", "I"), + c2.semanticToken("class.declaration", "C"), + c2.semanticToken("variable.declaration", "M"), + c2.semanticToken("variable", "M"), + c2.semanticToken("interface", "I"), + c2.semanticToken("interface", "I"), + c2.semanticToken("class", "C"), + c2.semanticToken("class.declaration", "I"), + c2.semanticToken("variable", "M"), + c2.semanticToken("class", "C"), ); diff --git a/tests/cases/fourslash/syntacticClassificationsForOfKeyword.ts b/tests/cases/fourslash/syntacticClassificationsForOfKeyword.ts index 5a2354b380582..6220f77c6fce6 100644 --- a/tests/cases/fourslash/syntacticClassificationsForOfKeyword.ts +++ b/tests/cases/fourslash/syntacticClassificationsForOfKeyword.ts @@ -14,9 +14,9 @@ verify.syntacticClassificationsAre( c.punctuation("{"), c.punctuation("}") ); - -const c2 = classification("2020"); -verify.semanticClassificationsAre("2020", - c2.semanticToken("variable.declaration", "of"), - c2.semanticToken("variable", "of"), + +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration.local", "of"), + c2.semanticToken("variable.local", "of"), ); diff --git a/tests/cases/fourslash/syntacticClassificationsForOfKeyword2.ts b/tests/cases/fourslash/syntacticClassificationsForOfKeyword2.ts index 1d8ba78579b93..db49945e1a73f 100644 --- a/tests/cases/fourslash/syntacticClassificationsForOfKeyword2.ts +++ b/tests/cases/fourslash/syntacticClassificationsForOfKeyword2.ts @@ -17,6 +17,6 @@ verify.syntacticClassificationsAre( const c2 = classification("2020"); verify.semanticClassificationsAre("2020", - c2.semanticToken("variable.declaration", "of"), - c2.semanticToken("variable", "of"), + c2.semanticToken("variable.declaration.local", "of"), + c2.semanticToken("variable.local", "of"), ); diff --git a/tests/cases/fourslash/syntacticClassificationsForOfKeyword3.ts b/tests/cases/fourslash/syntacticClassificationsForOfKeyword3.ts index 1e5f8481e5891..3393a4737f3b0 100644 --- a/tests/cases/fourslash/syntacticClassificationsForOfKeyword3.ts +++ b/tests/cases/fourslash/syntacticClassificationsForOfKeyword3.ts @@ -17,9 +17,9 @@ verify.syntacticClassificationsAre( c.punctuation("}") ); -const c2 = classification("2020"); -verify.semanticClassificationsAre("2020", - c2.semanticToken("variable.declaration", "of"), - c2.semanticToken("variable", "of"), - c2.semanticToken("variable", "of"), +const c2 = classification("2020"); +verify.semanticClassificationsAre("2020", + c2.semanticToken("variable.declaration.local", "of"), + c2.semanticToken("variable.local", "of"), + c2.semanticToken("variable.local", "of"), );