Skip to content

Commit

Permalink
ts sem
Browse files Browse the repository at this point in the history
  • Loading branch information
aeschli committed Jan 8, 2020
1 parent 2d752fc commit ed41737
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 0 deletions.
@@ -0,0 +1,210 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { ITypeScriptServiceClient, ExecConfig, ServerResponse } from '../typescriptService';
import * as Proto from '../protocol';

enum TokenType {
'class',
'enum',
'interface',
'namespace',
'typeParameter',
'type',
'parameter',
'variable',
'property',
'constant',
'function',
'member',
_sentinel
}


enum TokenModifier {
'declaration',
'static',
'async',
_sentinel
}

class SemanticTokensProvider implements vscode.SemanticTokensProvider {

constructor(
private readonly client: ITypeScriptServiceClient
) {
}

getLegend(): vscode.SemanticTokensLegend {
const tokenTypes = [];
for (let i = 0; i < TokenType._sentinel; i++) {
tokenTypes.push(TokenType[i]);
}
const tokenModifiers = [];
for (let i = 0; i < TokenModifier._sentinel; i++) {
tokenModifiers.push(TokenModifier[i]);
}
return new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers);
}

async provideSemanticTokens(document: vscode.TextDocument, _options: vscode.SemanticTokensRequestOptions, token: vscode.CancellationToken): Promise<vscode.SemanticTokens | null> {
const file = this.client.toOpenedFilePath(document);
if (!file) {
return null;
}

const args: ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs = {
file: file,
start: 0,
length: document.getText().length,
};

const versionBeforeRequest = document.version;

const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', args, token);
const versionAfterRequest = document.version;

if (versionBeforeRequest !== versionAfterRequest) {
// A new request will come in soon...
return null;

This comment has been minimized.

Copy link
@alexdima

alexdima Jan 16, 2020

Member

FYI @aeschli I will remove this because VS Code is written to handle this correctly.

i.e. in the core, the document version when the request is made is remembered and then when the result comes in, the result will be "adjusted" with the same adjusting code running when typing.

}

if (response.type !== 'response') {
return null;
}
if (!response.body) {
return null;
}

const builder = new vscode.SemanticTokensBuilder();

const tsTokens = response.body.spans;
for (let i = 0, len = Math.floor(tsTokens.length / 3); i < len; i++) {

const tokenType = tokenTypeMap[tsTokens[3 * i + 2]];
if (typeof tokenType === 'number') {
console.log(TokenType[tokenType]);
const offset = tsTokens[3 * i];
const length = tsTokens[3 * i + 1];

// we can use the document's range conversion methods because
// the result is at the same version as the document
const startPos = document.positionAt(offset);
const endPos = document.positionAt(offset + length);

for (let line = startPos.line; line <= endPos.line; line++) {
const startCharacter = (line === startPos.line ? startPos.character : 0);
const endCharacter = (line === endPos.line ? endPos.character : document.lineAt(line).text.length);
builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, 0);
}
}
}

return new vscode.SemanticTokens(builder.build());
}

}

export function register(
selector: vscode.DocumentSelector,
client: ITypeScriptServiceClient
) {
const provider = new SemanticTokensProvider(client);
return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend());
}

const tokenTypeMap: number[] = [];
tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = TokenType.class;
tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = TokenType.enum;
tokenTypeMap[ExperimentalProtocol.ClassificationType.interfaceName] = TokenType.interface;
tokenTypeMap[ExperimentalProtocol.ClassificationType.moduleName] = TokenType.namespace;
tokenTypeMap[ExperimentalProtocol.ClassificationType.typeParameterName] = TokenType.typeParameter;
tokenTypeMap[ExperimentalProtocol.ClassificationType.typeAliasName] = TokenType.type;
tokenTypeMap[ExperimentalProtocol.ClassificationType.parameterName] = TokenType.parameter;

export namespace ExperimentalProtocol {

export interface IExtendedTypeScriptServiceClient {
execute<K extends keyof ExperimentalProtocol.ExtendedTsServerRequests>(
command: K,
args: ExperimentalProtocol.ExtendedTsServerRequests[K][0],
token: vscode.CancellationToken,
config?: ExecConfig
): Promise<ServerResponse.Response<ExperimentalProtocol.ExtendedTsServerRequests[K][1]>>;
}

/**
* A request to get encoded semantic classifications for a span in the file
*/
export interface EncodedSemanticClassificationsRequest extends Proto.FileRequest {
arguments: EncodedSemanticClassificationsRequestArgs;
}

/**
* Arguments for EncodedSemanticClassificationsRequest request.
*/
export interface EncodedSemanticClassificationsRequestArgs extends Proto.FileRequestArgs {
/**
* Start position of the span.
*/
start: number;
/**
* Length of the span.
*/
length: number;
}

export const enum EndOfLineState {
None,
InMultiLineCommentTrivia,
InSingleQuoteStringLiteral,
InDoubleQuoteStringLiteral,
InTemplateHeadOrNoSubstitutionTemplate,
InTemplateMiddleOrTail,
InTemplateSubstitutionPosition,
}

export const enum ClassificationType {
comment = 1,
identifier = 2,
keyword = 3,
numericLiteral = 4,
operator = 5,
stringLiteral = 6,
regularExpressionLiteral = 7,
whiteSpace = 8,
text = 9,
punctuation = 10,
className = 11,
enumName = 12,
interfaceName = 13,
moduleName = 14,
typeParameterName = 15,
typeAliasName = 16,
parameterName = 17,
docCommentTagName = 18,
jsxOpenTagName = 19,
jsxCloseTagName = 20,
jsxSelfClosingTagName = 21,
jsxAttribute = 22,
jsxText = 23,
jsxAttributeStringLiteralValue = 24,
bigintLiteral = 25,
}

export interface EncodedSemanticClassificationsResponse extends Proto.Response {
body?: {
endOfLineState: EndOfLineState;
spans: number[];
};
}

export interface ExtendedTsServerRequests {
'encodedSemanticClassifications-full': [ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs, ExperimentalProtocol.EncodedSemanticClassificationsResponse];
}
}


Expand Up @@ -78,6 +78,7 @@ export default class LanguageProvider extends Disposable {
import('./features/signatureHelp').then(provider => this._register(provider.register(selector, this.client))),
import('./features/tagClosing').then(provider => this._register(provider.register(selector, this.description.id, this.client))),
import('./features/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))),
import('./features/semanticColoring').then(provider => this._register(provider.register(selector, this.client))),
]);
}

Expand Down

0 comments on commit ed41737

Please sign in to comment.