UNPKG

vscode-languageserver

Version:
252 lines (251 loc) 9.82 kB
"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;