[html] add semantic highlight

This commit is contained in:
Martin Aeschlimann
2019-12-02 23:40:22 +01:00
parent dbc7fa3c3e
commit b58c6fafad
4 changed files with 133 additions and 3 deletions

View File

@@ -6,10 +6,10 @@
import {
createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType,
DocumentRangeFormattingRequest, Disposable, DocumentSelector, TextDocumentPositionParams, ServerCapabilities,
Position, ConfigurationRequest, ConfigurationParams, DidChangeWorkspaceFoldersNotification,
Position, ConfigurationRequest, ConfigurationParams, DidChangeWorkspaceFoldersNotification, Range,
WorkspaceFolder, DocumentColorRequest, ColorInformation, ColorPresentationRequest, TextDocumentSyncKind
} from 'vscode-languageserver';
import { TextDocument, Diagnostic, DocumentLink, SymbolInformation } from 'vscode-html-languageservice';
import { TextDocument, Diagnostic, DocumentLink, SymbolInformation, TextDocumentIdentifier } from 'vscode-html-languageservice';
import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes';
import { format } from './modes/formatting';
@@ -29,6 +29,18 @@ namespace MatchingTagPositionRequest {
export const type: RequestType<TextDocumentPositionParams, Position | null, any, any> = new RequestType('html/matchingTagPosition');
}
// experimental: semantic tokens
interface SemanticTokenParams {
textDocument: TextDocumentIdentifier;
ranges?: Range[];
}
namespace SemanticTokenRequest {
export const type: RequestType<SemanticTokenParams, number[] | null, any, any> = new RequestType('html/semanticTokens');
}
namespace SemanticTokenLegendRequest {
export const type: RequestType<void, { types: string[]; modifiers: string[] } | null, any, any> = new RequestType('html/semanticTokenLegend');
}
// Create a connection for the server
const connection: IConnection = createConnection();
@@ -500,5 +512,45 @@ connection.onRequest(MatchingTagPositionRequest.type, (params, token) => {
}, null, `Error while computing matching tag position for ${params.textDocument.uri}`, token);
});
connection.onRequest(MatchingTagPositionRequest.type, (params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const pos = params.position;
if (pos.character > 0) {
const mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1));
if (mode && mode.findMatchingTagPosition) {
return mode.findMatchingTagPosition(document, pos);
}
}
}
return null;
}, null, `Error while computing matching tag position for ${params.textDocument.uri}`, token);
});
connection.onRequest(SemanticTokenRequest.type, (params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
if (document) {
const jsMode = languageModes.getMode('javascript');
if (jsMode && jsMode.getSemanticTokens) {
return jsMode.getSemanticTokens(document, params.ranges);
}
}
return null;
}, null, `Error while computing semantic tokens for ${params.textDocument.uri}`, token);
});
connection.onRequest(SemanticTokenLegendRequest.type, (_params, token) => {
return runSafe(() => {
const jsMode = languageModes.getMode('javascript');
if (jsMode && jsMode.getSemanticTokenLegend) {
return jsMode.getSemanticTokenLegend();
}
return null;
}, null, `Error while computing semantic tokens legend`, token);
});
// Listen on the connection
connection.listen();

View File

@@ -314,6 +314,44 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
onDocumentRemoved(document: TextDocument) {
jsDocuments.onDocumentRemoved(document);
},
getSemanticTokens(document: TextDocument, ranges: Range[] | undefined): number[] {
updateCurrentTextDocument(document);
if (!ranges) {
ranges = [Range.create(Position.create(0, 0), document.positionAt(document.getText().length))];
}
const result = [];
for (let range of ranges) {
const start = document.offsetAt(range.start), length = document.offsetAt(range.end) - start;
const tokens = jsLanguageService.getSemanticClassifications(FILE_NAME, { start, length });
let prefLine = 0;
let prevChar = 0;
for (let token of tokens) {
const m = tokenMapping[token.classificationType];
if (m) {
const startPos = document.positionAt(token.textSpan.start);
if (prefLine !== startPos.line) {
prevChar = 0;
}
result.push(startPos.line - prefLine); // line delta
result.push(startPos.character - prevChar); // line delta
result.push(token.textSpan.length); // lemgth
result.push(tokenTypes.indexOf(m)); // tokenType
result.push(0); // tokenModifier
prefLine = startPos.line;
prevChar = startPos.character;
}
}
}
return result;
},
getSemanticTokenLegend(): { types: string[], modifiers: string[] } {
return { types: tokenTypes, modifiers: tokenModifiers };
},
dispose() {
jsLanguageService.dispose();
jsDocuments.dispose();
@@ -321,6 +359,20 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
};
}
const tokenTypes: string[] = ['classes', 'enums', 'interfaces', 'namespaces', 'parameterTypes', 'types', 'parameters'];
const tokenModifiers: string[] = [];
const tokenMapping: { [name: string]: string } = {
[ts.ClassificationTypeNames.className]: 'classes',
[ts.ClassificationTypeNames.enumName]: 'enums',
[ts.ClassificationTypeNames.interfaceName]: 'interfaces',
[ts.ClassificationTypeNames.moduleName]: 'namespaces',
[ts.ClassificationTypeNames.typeParameterName]: 'parameterTypes',
[ts.ClassificationTypeNames.typeAliasName]: 'types',
[ts.ClassificationTypeNames.parameterName]: 'parameters'
};
function convertRange(document: TextDocument, span: { start: number | undefined, length: number | undefined }): Range {
if (typeof span.start === 'undefined') {
const pos = document.positionAt(0);

View File

@@ -51,6 +51,8 @@ export interface LanguageMode {
findMatchingTagPosition?: (document: TextDocument, position: Position) => Position | null;
getFoldingRanges?: (document: TextDocument) => FoldingRange[];
onDocumentRemoved(document: TextDocument): void;
getSemanticTokens?(document: TextDocument, ranges: Range[] | undefined): number[];
getSemanticTokenLegend?(): { types: string[], modifiers: string[] };
dispose(): void;
}