polish
This commit is contained in:
parent
900100b745
commit
4032ce7241
1 changed files with 73 additions and 79 deletions
|
@ -7,35 +7,19 @@ 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
|
||||
export function register(selector: vscode.DocumentSelector, client: ITypeScriptServiceClient) {
|
||||
const provider = new SemanticTokensProvider(client);
|
||||
return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend());
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Prototype of a SemanticTokensProvider, relying on the experimental `encodedSemanticClassifications-full` request from the TypeScript server.
|
||||
* As the results retured by the TypeScript server are limited, we also add a Typescript plugin (typescript-vscode-sh-plugin) to enrich the returned token.
|
||||
*/
|
||||
class SemanticTokensProvider implements vscode.SemanticTokensProvider {
|
||||
|
||||
constructor(
|
||||
private readonly client: ITypeScriptServiceClient
|
||||
) {
|
||||
constructor(private readonly client: ITypeScriptServiceClient) {
|
||||
}
|
||||
|
||||
getLegend(): vscode.SemanticTokensLegend {
|
||||
|
@ -58,79 +42,91 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider {
|
|||
|
||||
const versionBeforeRequest = document.version;
|
||||
|
||||
if (_options.ranges) {
|
||||
const allTokenSpans: number[][] = [];
|
||||
|
||||
// const allArgs = _options.ranges.map(r => ({file, start: document.offsetAt(r.start), length: document.offsetAt(r.end) - document.offsetAt(r.start)}));
|
||||
let requestArgs: ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs[] = [];
|
||||
if (_options.ranges) {
|
||||
requestArgs = _options.ranges.map(r => { const start = document.offsetAt(r.start); const length = document.offsetAt(r.end) - start; return { file, start, length }; });
|
||||
requestArgs = requestArgs.sort((a1, a2) => a1.start - a2.start);
|
||||
} else {
|
||||
requestArgs = [{ file, start: 0, length: document.getText().length }]; // full file
|
||||
}
|
||||
for (const requestArg of requestArgs) {
|
||||
const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', requestArg, token);
|
||||
if (response.type === 'response' && response.body) {
|
||||
allTokenSpans.push(response.body.spans);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const args: ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs = {
|
||||
file: file,
|
||||
start: 0,
|
||||
length: document.getText().length,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (response.type !== 'response') {
|
||||
return null;
|
||||
}
|
||||
if (!response.body) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const builder = new vscode.SemanticTokensBuilder();
|
||||
for (const tokenSpan of allTokenSpans) {
|
||||
for (let i = 0, len = Math.floor(tokenSpan.length / 3); i < len; i++) {
|
||||
|
||||
const tsTokens = response.body.spans;
|
||||
for (let i = 0, len = Math.floor(tsTokens.length / 3); i < len; i++) {
|
||||
const tsClassification = tokenSpan[3 * i + 2];
|
||||
let tokenType = 0;
|
||||
let tokenModifiers = 0;
|
||||
if (tsClassification >= 0x100) {
|
||||
// exendend classifications as returned by the typescript-vscode-sh-plugin
|
||||
tokenType = (tsClassification >> 8) - 1;
|
||||
tokenModifiers = tsClassification & 0xFF;
|
||||
} else {
|
||||
tokenType = tokenTypeMap[tsClassification];
|
||||
if (tokenType === undefined) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const tsClassification = tsTokens[3 * i + 2];
|
||||
let tokenType = 0;
|
||||
let tokenModifiers = 0;
|
||||
if (tsClassification > 0xFF) {
|
||||
// classifications as returned by the typescript-vscode-sh-plugin
|
||||
tokenType = (tsClassification >> 8) - 1;
|
||||
tokenModifiers = tsClassification & 0xFF;
|
||||
} else {
|
||||
tokenType = tokenTypeMap[tsClassification];
|
||||
if (tokenType === undefined) {
|
||||
continue;
|
||||
const offset = tokenSpan[3 * i];
|
||||
const length = tokenSpan[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, tokenModifiers);
|
||||
}
|
||||
}
|
||||
|
||||
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, tokenModifiers);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
enum TokenType {
|
||||
'class',
|
||||
'enum',
|
||||
'interface',
|
||||
'namespace',
|
||||
'typeParameter',
|
||||
'type',
|
||||
'parameter',
|
||||
'variable',
|
||||
'property',
|
||||
'constant',
|
||||
'function',
|
||||
'member',
|
||||
_sentinel
|
||||
}
|
||||
|
||||
enum TokenModifier {
|
||||
'declaration',
|
||||
'static',
|
||||
'async',
|
||||
_sentinel
|
||||
}
|
||||
|
||||
// mapping for the original ExperimentalProtocol.ClassificationType from TypeScript (only used when plugin is not available)
|
||||
|
||||
const tokenTypeMap: number[] = [];
|
||||
tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = TokenType.class;
|
||||
tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = TokenType.enum;
|
||||
|
@ -221,5 +217,3 @@ export namespace ExperimentalProtocol {
|
|||
'encodedSemanticClassifications-full': [ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs, ExperimentalProtocol.EncodedSemanticClassificationsResponse];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue