Import the semantic highlighter from typescript-vscode-sh-plugin (#39119)

* Initial import of the vscode semantic highlight code

* Adds the ability to test modern semantic classification via strings instead of numbers

* Adds existing tests

* Port over the semantic classification tests

* Update baselines

* Update src/harness/fourslashImpl.ts

Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>

* Handle feedback from #39119

* Consistent formatting in the 2020 classifier

* Update baselines

* Apply suggestions from code review

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>

* Update src/harness/fourslashImpl.ts

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>

* Reafactor after comments

* Use 2020 everywhere

* Handle feedback

* WIP - don't provide a breaking change

* Fix all build errors

* Update baselines

* Update src/services/classifier2020.ts

Co-authored-by: Sheetal Nandi <shkamat@microsoft.com>

* Addresses Ron's feedback

Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
Co-authored-by: Sheetal Nandi <shkamat@microsoft.com>
This commit is contained in:
Orta Therox
2020-09-11 15:10:55 -04:00
committed by GitHub
co-authored by Nathan Shively-Sanders Daniel Rosenwasser Sheetal Nandi
parent a36f17c1f8
commit db5368dc1d
81 changed files with 1169 additions and 194 deletions
+1 -1
View File
@@ -747,7 +747,7 @@ namespace ts.server {
return notImplemented();
}
getEncodedSemanticClassifications(_fileName: string, _span: TextSpan): Classifications {
getEncodedSemanticClassifications(_fileName: string, _span: TextSpan, _format?: SemanticClassificationFormat): Classifications {
return notImplemented();
}
+70 -6
View File
@@ -2492,7 +2492,49 @@ 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 classificationToIdentifier(classification: number){
const tokenTypes: string[] = [];
tokenTypes[ts.classifier.v2020.TokenType.class] = "class";
tokenTypes[ts.classifier.v2020.TokenType.enum] = "enum";
tokenTypes[ts.classifier.v2020.TokenType.interface] = "interface";
tokenTypes[ts.classifier.v2020.TokenType.namespace] = "namespace";
tokenTypes[ts.classifier.v2020.TokenType.typeParameter] = "typeParameter";
tokenTypes[ts.classifier.v2020.TokenType.type] = "type";
tokenTypes[ts.classifier.v2020.TokenType.parameter] = "parameter";
tokenTypes[ts.classifier.v2020.TokenType.variable] = "variable";
tokenTypes[ts.classifier.v2020.TokenType.enumMember] = "enumMember";
tokenTypes[ts.classifier.v2020.TokenType.property] = "property";
tokenTypes[ts.classifier.v2020.TokenType.function] = "function";
tokenTypes[ts.classifier.v2020.TokenType.member] = "member";
const tokenModifiers: string[] = [];
tokenModifiers[ts.classifier.v2020.TokenModifier.async] = "async";
tokenModifiers[ts.classifier.v2020.TokenModifier.declaration] = "declaration";
tokenModifiers[ts.classifier.v2020.TokenModifier.readonly] = "readonly";
tokenModifiers[ts.classifier.v2020.TokenModifier.static] = "static";
tokenModifiers[ts.classifier.v2020.TokenModifier.local] = "local";
tokenModifiers[ts.classifier.v2020.TokenModifier.defaultLibrary] = "defaultLibrary";
function getTokenTypeFromClassification(tsClassification: number): number | undefined {
if (tsClassification > ts.classifier.v2020.TokenEncodingConsts.modifierMask) {
return (tsClassification >> ts.classifier.v2020.TokenEncodingConsts.typeOffset) - 1;
}
return undefined;
}
function getTokenModifierFromClassification(tsClassification: number) {
return tsClassification & ts.classifier.v2020.TokenEncodingConsts.modifierMask;
}
const typeIdx = getTokenTypeFromClassification(classification) || 0;
const modSet = getTokenModifierFromClassification(classification);
return [tokenTypes[typeIdx], ...tokenModifiers.filter((_, i) => modSet & 1 << i)].join(".");
}
private verifyClassifications(expected: { classificationType: string | number, text?: string; textSpan?: TextSpan }[], actual: (ts.ClassifiedSpan | ts.ClassifiedSpan2020)[] , sourceFileText: string) {
if (actual.length !== expected.length) {
this.raiseError("verifyClassifications failed - expected total classifications to be " + expected.length +
", but was " + actual.length +
@@ -2501,10 +2543,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());
}
@@ -2555,10 +2599,30 @@ namespace FourSlash {
}
}
public verifySemanticClassifications(expected: { classificationType: string; text: string }[]) {
public replaceWithSemanticClassifications(format: ts.SemanticClassificationFormat.TwentyTwenty) {
const actual = this.languageService.getSemanticClassifications(this.activeFile.fileName,
ts.createTextSpan(0, this.activeFile.content.length));
ts.createTextSpan(0, this.activeFile.content.length), format);
const replacement = [`const c2 = classification("2020");`,`verify.semanticClassificationsAre("2020",`];
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}"), `);
};
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);
this.verifyClassifications(expected, actual, this.activeFile.content);
}
@@ -3859,7 +3923,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`.
+85 -27
View File
@@ -524,8 +524,12 @@ 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: ts.SemanticClassificationFormat, ...classifications: Classification[]) {
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) {
@@ -768,101 +772,127 @@ namespace FourSlashInterface {
}
}
interface Classification {
interface OlderClassification {
classificationType: ts.ClassificationTypeNames;
text: string;
textSpan?: FourSlash.TextSpan;
}
export namespace Classification {
export function comment(text: string, position?: number): Classification {
// The VS Code LSP
interface ModernClassification {
classificationType: string;
text?: string;
textSpan?: FourSlash.TextSpan;
}
type Classification = OlderClassification | ModernClassification;
export function classification(format: ts.SemanticClassificationFormat) {
function semanticToken(identifier: string, text: string, _position: number): Classification {
return {
classificationType: identifier,
text
};
}
if (format === ts.SemanticClassificationFormat.TwentyTwenty) {
return {
semanticToken
};
}
// Defaults to the previous semantic classifier factory functions
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);
}
@@ -870,7 +900,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;
+5 -4
View File
@@ -461,14 +461,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?: 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): ts.Classifications {
return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length));
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 {
return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, preferences));
+249
View File
@@ -0,0 +1,249 @@
/** @internal */
namespace ts.classifier.v2020 {
export const enum TokenEncodingConsts {
typeOffset = 8,
modifierMask = (1 << typeOffset) - 1
}
export const enum TokenType {
class, enum, interface, namespace, typeParameter, type, parameter, variable, enumMember, property, function, member
}
export const 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): ClassifiedSpan2020[] {
const classifications = getEncodedSemanticClassifications(program, cancellationToken, sourceFile, span);
Debug.assert(classifications.spans.length % 3 === 0);
const dense = classifications.spans;
const result: ClassifiedSpan2020[] = [];
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, cancellationToken),
endOfLineState: EndOfLineState.None
};
}
function getSemanticTokens(program: Program, sourceFile: SourceFile, span: TextSpan, cancellationToken: CancellationToken): number[] {
const resultTokens: number[] = [];
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, cancellationToken);
}
return resultTokens;
}
function collectTokens(program: Program, sourceFile: SourceFile, span: TextSpan, collector: (node: Node, tokenType: number, tokenModifier: number) => void, cancellationToken: CancellationToken) {
const typeChecker = program.getTypeChecker();
let inJSXElement = false;
function visit(node: Node) {
switch(node.kind) {
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
cancellationToken.throwIfCancellationRequested();
}
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.get(node.parent.kind) === typeIdx);
if (parentIsDeclaration && (<NamedDeclaration>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.get(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 tokenFromDeclarationMapping = new Map<SyntaxKind, 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]
]);
}
+19 -4
View File
@@ -1827,22 +1827,37 @@ namespace ts {
return kind === ScriptKind.TS || kind === ScriptKind.TSX;
}
function getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
function getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
function getSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[] | ClassifiedSpan2020[] {
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 || SemanticClassificationFormat.Original;
if (responseFormat === SemanticClassificationFormat.TwentyTwenty) {
return classifier.v2020.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span);
}
else {
return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
}
}
function getEncodedSemanticClassifications(fileName: string, span: TextSpan): 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();
return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
const responseFormat = format || SemanticClassificationFormat.Original;
if (responseFormat === SemanticClassificationFormat.Original) {
return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
}
else {
return classifier.v2020.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span);
}
}
function getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
+2 -2
View File
@@ -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?: SemanticClassificationFormat): 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?: 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;
+1
View File
@@ -12,6 +12,7 @@
"types.ts",
"utilities.ts",
"classifier.ts",
"classifier2020.ts",
"stringCompletions.ts",
"completions.ts",
"documentHighlights.ts",
+24 -2
View File
@@ -319,6 +319,11 @@ namespace ts {
export type WithMetadata<T> = 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
@@ -382,13 +387,25 @@ namespace ts {
/** @deprecated Use getEncodedSyntacticClassifications instead. */
getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
getSyntacticClassifications(fileName: string, span: TextSpan, format: SemanticClassificationFormat): ClassifiedSpan[] | ClassifiedSpan2020[];
/** @deprecated Use getEncodedSemanticClassifications instead. */
getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
getSemanticClassifications(fileName: string, span: TextSpan, format: SemanticClassificationFormat): ClassifiedSpan[] | ClassifiedSpan2020[];
// Encoded as triples of [start, length, ClassificationType].
/** 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.
@@ -603,6 +620,11 @@ namespace ts {
classificationType: ClassificationTypeNames;
}
export interface ClassifiedSpan2020 {
textSpan: TextSpan;
classificationType: number;
}
/**
* Navigation bar interface designed for visual studio's dual-column layout.
* This does not form a proper tree.