vscode-languageserver
Version:
Language server implementation for node
252 lines (251 loc) • 9.82 kB
JavaScript
"use strict";
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
Object.defineProperty(exports, "__esModule", { value: true });
exports.SemanticTokensBuilder = exports.SemanticTokensDiff = exports.SemanticTokensFeature = void 0;
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
const SemanticTokensFeature = (Base) => {
return class extends Base {
get semanticTokens() {
return {
refresh: () => {
return this.connection.sendRequest(vscode_languageserver_protocol_1.SemanticTokensRefreshRequest.type);
},
on: (handler) => {
const type = vscode_languageserver_protocol_1.SemanticTokensRequest.type;
return this.connection.onRequest(type, (params, cancel) => {
return handler(params, cancel, this.attachWorkDoneProgress(params), this.attachPartialResultProgress(type, params));
});
},
onDelta: (handler) => {
const type = vscode_languageserver_protocol_1.SemanticTokensDeltaRequest.type;
return this.connection.onRequest(type, (params, cancel) => {
return handler(params, cancel, this.attachWorkDoneProgress(params), this.attachPartialResultProgress(type, params));
});
},
onRange: (handler) => {
const type = vscode_languageserver_protocol_1.SemanticTokensRangeRequest.type;
return this.connection.onRequest(type, (params, cancel) => {
return handler(params, cancel, this.attachWorkDoneProgress(params), this.attachPartialResultProgress(type, params));
});
}
};
}
};
};
exports.SemanticTokensFeature = SemanticTokensFeature;
class SemanticTokensDiff {
originalSequence;
modifiedSequence;
constructor(originalSequence, modifiedSequence) {
this.originalSequence = originalSequence;
this.modifiedSequence = modifiedSequence;
}
computeDiff() {
const originalLength = this.originalSequence.length;
const modifiedLength = this.modifiedSequence.length;
let startIndex = 0;
while (startIndex < modifiedLength && startIndex < originalLength && this.originalSequence[startIndex] === this.modifiedSequence[startIndex]) {
startIndex++;
}
if (startIndex < modifiedLength && startIndex < originalLength) {
let originalEndIndex = originalLength - 1;
let modifiedEndIndex = modifiedLength - 1;
while (originalEndIndex >= startIndex && modifiedEndIndex >= startIndex && this.originalSequence[originalEndIndex] === this.modifiedSequence[modifiedEndIndex]) {
originalEndIndex--;
modifiedEndIndex--;
}
// if one moved behind the start index move them forward again
if (originalEndIndex < startIndex || modifiedEndIndex < startIndex) {
originalEndIndex++;
modifiedEndIndex++;
}
const deleteCount = originalEndIndex - startIndex + 1;
const newData = this.modifiedSequence.slice(startIndex, modifiedEndIndex + 1);
// If we moved behind the start index we could have missed a simple delete.
if (newData.length === 1 && newData[0] === this.originalSequence[originalEndIndex]) {
return [
{ start: startIndex, deleteCount: deleteCount - 1 }
];
}
else {
return [
{ start: startIndex, deleteCount, data: newData }
];
}
}
else if (startIndex < modifiedLength) {
return [
{ start: startIndex, deleteCount: 0, data: this.modifiedSequence.slice(startIndex) }
];
}
else if (startIndex < originalLength) {
return [
{ start: startIndex, deleteCount: originalLength - startIndex }
];
}
else {
// The two arrays are the same.
return [];
}
}
}
exports.SemanticTokensDiff = SemanticTokensDiff;
class SemanticTokensBuilder {
_id;
_prevLine;
_prevChar;
_dataIsSortedAndDeltaEncoded;
_data;
_dataNonDelta;
_dataLen;
_prevData;
constructor() {
this._prevData = undefined;
this.initialize();
}
initialize() {
this._id = Date.now();
this._prevLine = 0;
this._prevChar = 0;
this._data = [];
this._dataNonDelta = [];
this._dataLen = 0;
this._dataIsSortedAndDeltaEncoded = true;
}
push(line, char, length, tokenType, tokenModifiers) {
if (this._dataIsSortedAndDeltaEncoded && (line < this._prevLine || (line === this._prevLine && char < this._prevChar))) {
// push calls were ordered and are no longer ordered
this._dataIsSortedAndDeltaEncoded = false;
this._dataNonDelta = SemanticTokensBuilder._deltaDecode(this._data);
}
let pushLine = line;
let pushChar = char;
if (this._dataIsSortedAndDeltaEncoded && this._dataLen > 0) {
pushLine -= this._prevLine;
if (pushLine === 0) {
pushChar -= this._prevChar;
}
}
const dataSource = this._dataIsSortedAndDeltaEncoded ? this._data : this._dataNonDelta;
dataSource[this._dataLen++] = pushLine;
dataSource[this._dataLen++] = pushChar;
dataSource[this._dataLen++] = length;
dataSource[this._dataLen++] = tokenType;
dataSource[this._dataLen++] = tokenModifiers;
this._prevLine = line;
this._prevChar = char;
}
get id() {
return this._id.toString();
}
static _deltaDecode(data) {
// Remove delta encoding from data
const tokenCount = (data.length / 5) | 0;
let prevLine = 0;
let prevChar = 0;
const result = [];
for (let i = 0; i < tokenCount; i++) {
const dstOffset = 5 * i;
let line = data[dstOffset];
let char = data[dstOffset + 1];
if (line === 0) {
// on the same line as previous token
line = prevLine;
char += prevChar;
}
else {
// on a different line than previous token
line += prevLine;
}
const length = data[dstOffset + 2];
const tokenType = data[dstOffset + 3];
const tokenModifiers = data[dstOffset + 4];
result[dstOffset + 0] = line;
result[dstOffset + 1] = char;
result[dstOffset + 2] = length;
result[dstOffset + 3] = tokenType;
result[dstOffset + 4] = tokenModifiers;
prevLine = line;
prevChar = char;
}
return result;
}
static _sortAndDeltaEncode(data) {
const pos = [];
const tokenCount = (data.length / 5) | 0;
for (let i = 0; i < tokenCount; i++) {
pos[i] = i;
}
pos.sort((a, b) => {
const aLine = data[5 * a];
const bLine = data[5 * b];
if (aLine === bLine) {
const aChar = data[5 * a + 1];
const bChar = data[5 * b + 1];
return aChar - bChar;
}
return aLine - bLine;
});
const result = [];
let prevLine = 0;
let prevChar = 0;
for (let i = 0; i < tokenCount; i++) {
const srcOffset = 5 * pos[i];
const line = data[srcOffset + 0];
const char = data[srcOffset + 1];
const length = data[srcOffset + 2];
const tokenType = data[srcOffset + 3];
const tokenModifiers = data[srcOffset + 4];
const pushLine = line - prevLine;
const pushChar = (pushLine === 0 ? char - prevChar : char);
const dstOffset = 5 * i;
result[dstOffset + 0] = pushLine;
result[dstOffset + 1] = pushChar;
result[dstOffset + 2] = length;
result[dstOffset + 3] = tokenType;
result[dstOffset + 4] = tokenModifiers;
prevLine = line;
prevChar = char;
}
return result;
}
getFinalDataDelta() {
if (this._dataIsSortedAndDeltaEncoded) {
return this._data;
}
else {
return SemanticTokensBuilder._sortAndDeltaEncode(this._dataNonDelta);
}
}
previousResult(id) {
if (this.id === id) {
this._prevData = this.getFinalDataDelta();
}
this.initialize();
}
build() {
this._prevData = undefined;
return {
resultId: this.id,
data: this.getFinalDataDelta()
};
}
canBuildEdits() {
return this._prevData !== undefined;
}
buildEdits() {
if (this._prevData !== undefined) {
return {
resultId: this.id,
edits: (new SemanticTokensDiff(this._prevData, this.getFinalDataDelta())).computeDiff()
};
}
else {
return this.build();
}
}
}
exports.SemanticTokensBuilder = SemanticTokensBuilder;