- Hover provider showing entity information and type - Go-to-definition (F12) for entity references - Basic IFC file validation (ISO-10303-21 header check) - Entity parsing with regex-based detection - Proper CommonJS module system (avoiding ES module issues) This replaces the broken baseline from ifc-developer-tools which had: - Non-functional ES module configuration - Circular dependency issues - Parser crashes - Non-working PositionVisitor Built on Microsoft's LSP example template for a clean, maintainable foundation. Next: Add hierarchical entity dependency tree in hover tooltip."
1600 lines
74 KiB
JavaScript
1600 lines
74 KiB
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.ProposedFeatures = exports.BaseLanguageClient = exports.MessageTransports = exports.SuspendMode = exports.State = exports.CloseAction = exports.ErrorAction = exports.RevealOutputChannelOn = void 0;
|
|
const vscode_1 = require("vscode");
|
|
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
|
|
const c2p = require("./codeConverter");
|
|
const p2c = require("./protocolConverter");
|
|
const Is = require("./utils/is");
|
|
const async_1 = require("./utils/async");
|
|
const UUID = require("./utils/uuid");
|
|
const progressPart_1 = require("./progressPart");
|
|
const features_1 = require("./features");
|
|
const diagnostic_1 = require("./diagnostic");
|
|
const notebook_1 = require("./notebook");
|
|
const configuration_1 = require("./configuration");
|
|
const textSynchronization_1 = require("./textSynchronization");
|
|
const completion_1 = require("./completion");
|
|
const hover_1 = require("./hover");
|
|
const definition_1 = require("./definition");
|
|
const signatureHelp_1 = require("./signatureHelp");
|
|
const documentHighlight_1 = require("./documentHighlight");
|
|
const documentSymbol_1 = require("./documentSymbol");
|
|
const workspaceSymbol_1 = require("./workspaceSymbol");
|
|
const reference_1 = require("./reference");
|
|
const codeAction_1 = require("./codeAction");
|
|
const codeLens_1 = require("./codeLens");
|
|
const formatting_1 = require("./formatting");
|
|
const rename_1 = require("./rename");
|
|
const documentLink_1 = require("./documentLink");
|
|
const executeCommand_1 = require("./executeCommand");
|
|
const fileSystemWatcher_1 = require("./fileSystemWatcher");
|
|
const colorProvider_1 = require("./colorProvider");
|
|
const implementation_1 = require("./implementation");
|
|
const typeDefinition_1 = require("./typeDefinition");
|
|
const workspaceFolder_1 = require("./workspaceFolder");
|
|
const foldingRange_1 = require("./foldingRange");
|
|
const declaration_1 = require("./declaration");
|
|
const selectionRange_1 = require("./selectionRange");
|
|
const progress_1 = require("./progress");
|
|
const callHierarchy_1 = require("./callHierarchy");
|
|
const semanticTokens_1 = require("./semanticTokens");
|
|
const fileOperations_1 = require("./fileOperations");
|
|
const linkedEditingRange_1 = require("./linkedEditingRange");
|
|
const typeHierarchy_1 = require("./typeHierarchy");
|
|
const inlineValue_1 = require("./inlineValue");
|
|
const inlayHint_1 = require("./inlayHint");
|
|
const inlineCompletion_1 = require("./inlineCompletion");
|
|
/**
|
|
* Controls when the output channel is revealed.
|
|
*/
|
|
var RevealOutputChannelOn;
|
|
(function (RevealOutputChannelOn) {
|
|
RevealOutputChannelOn[RevealOutputChannelOn["Debug"] = 0] = "Debug";
|
|
RevealOutputChannelOn[RevealOutputChannelOn["Info"] = 1] = "Info";
|
|
RevealOutputChannelOn[RevealOutputChannelOn["Warn"] = 2] = "Warn";
|
|
RevealOutputChannelOn[RevealOutputChannelOn["Error"] = 3] = "Error";
|
|
RevealOutputChannelOn[RevealOutputChannelOn["Never"] = 4] = "Never";
|
|
})(RevealOutputChannelOn || (exports.RevealOutputChannelOn = RevealOutputChannelOn = {}));
|
|
/**
|
|
* An action to be performed when the connection is producing errors.
|
|
*/
|
|
var ErrorAction;
|
|
(function (ErrorAction) {
|
|
/**
|
|
* Continue running the server.
|
|
*/
|
|
ErrorAction[ErrorAction["Continue"] = 1] = "Continue";
|
|
/**
|
|
* Shutdown the server.
|
|
*/
|
|
ErrorAction[ErrorAction["Shutdown"] = 2] = "Shutdown";
|
|
})(ErrorAction || (exports.ErrorAction = ErrorAction = {}));
|
|
/**
|
|
* An action to be performed when the connection to a server got closed.
|
|
*/
|
|
var CloseAction;
|
|
(function (CloseAction) {
|
|
/**
|
|
* Don't restart the server. The connection stays closed.
|
|
*/
|
|
CloseAction[CloseAction["DoNotRestart"] = 1] = "DoNotRestart";
|
|
/**
|
|
* Restart the server.
|
|
*/
|
|
CloseAction[CloseAction["Restart"] = 2] = "Restart";
|
|
})(CloseAction || (exports.CloseAction = CloseAction = {}));
|
|
/**
|
|
* Signals in which state the language client is in.
|
|
*/
|
|
var State;
|
|
(function (State) {
|
|
/**
|
|
* The client is stopped or got never started.
|
|
*/
|
|
State[State["Stopped"] = 1] = "Stopped";
|
|
/**
|
|
* The client is starting but not ready yet.
|
|
*/
|
|
State[State["Starting"] = 3] = "Starting";
|
|
/**
|
|
* The client is running and ready.
|
|
*/
|
|
State[State["Running"] = 2] = "Running";
|
|
})(State || (exports.State = State = {}));
|
|
var SuspendMode;
|
|
(function (SuspendMode) {
|
|
/**
|
|
* Don't allow suspend mode.
|
|
*/
|
|
SuspendMode["off"] = "off";
|
|
/**
|
|
* Support suspend mode even if not all
|
|
* registered providers have a corresponding
|
|
* activation listener.
|
|
*/
|
|
SuspendMode["on"] = "on";
|
|
})(SuspendMode || (exports.SuspendMode = SuspendMode = {}));
|
|
var ResolvedClientOptions;
|
|
(function (ResolvedClientOptions) {
|
|
function sanitizeIsTrusted(isTrusted) {
|
|
if (isTrusted === undefined || isTrusted === null) {
|
|
return false;
|
|
}
|
|
if ((typeof isTrusted === 'boolean') || (typeof isTrusted === 'object' && isTrusted !== null && Is.stringArray(isTrusted.enabledCommands))) {
|
|
return isTrusted;
|
|
}
|
|
return false;
|
|
}
|
|
ResolvedClientOptions.sanitizeIsTrusted = sanitizeIsTrusted;
|
|
})(ResolvedClientOptions || (ResolvedClientOptions = {}));
|
|
class DefaultErrorHandler {
|
|
constructor(client, maxRestartCount) {
|
|
this.client = client;
|
|
this.maxRestartCount = maxRestartCount;
|
|
this.restarts = [];
|
|
}
|
|
error(_error, _message, count) {
|
|
if (count && count <= 3) {
|
|
return { action: ErrorAction.Continue };
|
|
}
|
|
return { action: ErrorAction.Shutdown };
|
|
}
|
|
closed() {
|
|
this.restarts.push(Date.now());
|
|
if (this.restarts.length <= this.maxRestartCount) {
|
|
return { action: CloseAction.Restart };
|
|
}
|
|
else {
|
|
let diff = this.restarts[this.restarts.length - 1] - this.restarts[0];
|
|
if (diff <= 3 * 60 * 1000) {
|
|
return { action: CloseAction.DoNotRestart, message: `The ${this.client.name} server crashed ${this.maxRestartCount + 1} times in the last 3 minutes. The server will not be restarted. See the output for more information.` };
|
|
}
|
|
else {
|
|
this.restarts.shift();
|
|
return { action: CloseAction.Restart };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var ClientState;
|
|
(function (ClientState) {
|
|
ClientState["Initial"] = "initial";
|
|
ClientState["Starting"] = "starting";
|
|
ClientState["StartFailed"] = "startFailed";
|
|
ClientState["Running"] = "running";
|
|
ClientState["Stopping"] = "stopping";
|
|
ClientState["Stopped"] = "stopped";
|
|
})(ClientState || (ClientState = {}));
|
|
var MessageTransports;
|
|
(function (MessageTransports) {
|
|
function is(value) {
|
|
let candidate = value;
|
|
return candidate && vscode_languageserver_protocol_1.MessageReader.is(value.reader) && vscode_languageserver_protocol_1.MessageWriter.is(value.writer);
|
|
}
|
|
MessageTransports.is = is;
|
|
})(MessageTransports || (exports.MessageTransports = MessageTransports = {}));
|
|
class BaseLanguageClient {
|
|
constructor(id, name, clientOptions) {
|
|
this._traceFormat = vscode_languageserver_protocol_1.TraceFormat.Text;
|
|
this._diagnosticQueue = new Map();
|
|
this._diagnosticQueueState = { state: 'idle' };
|
|
this._features = [];
|
|
this._dynamicFeatures = new Map();
|
|
this.workspaceEditLock = new async_1.Semaphore(1);
|
|
this._id = id;
|
|
this._name = name;
|
|
clientOptions = clientOptions || {};
|
|
const markdown = { isTrusted: false, supportHtml: false };
|
|
if (clientOptions.markdown !== undefined) {
|
|
markdown.isTrusted = ResolvedClientOptions.sanitizeIsTrusted(clientOptions.markdown.isTrusted);
|
|
markdown.supportHtml = clientOptions.markdown.supportHtml === true;
|
|
}
|
|
// const defaultInterval = (clientOptions as TestOptions).$testMode ? 50 : 60000;
|
|
this._clientOptions = {
|
|
documentSelector: clientOptions.documentSelector ?? [],
|
|
synchronize: clientOptions.synchronize ?? {},
|
|
diagnosticCollectionName: clientOptions.diagnosticCollectionName,
|
|
outputChannelName: clientOptions.outputChannelName ?? this._name,
|
|
revealOutputChannelOn: clientOptions.revealOutputChannelOn ?? RevealOutputChannelOn.Error,
|
|
stdioEncoding: clientOptions.stdioEncoding ?? 'utf8',
|
|
initializationOptions: clientOptions.initializationOptions,
|
|
initializationFailedHandler: clientOptions.initializationFailedHandler,
|
|
progressOnInitialization: !!clientOptions.progressOnInitialization,
|
|
errorHandler: clientOptions.errorHandler ?? this.createDefaultErrorHandler(clientOptions.connectionOptions?.maxRestartCount),
|
|
middleware: clientOptions.middleware ?? {},
|
|
uriConverters: clientOptions.uriConverters,
|
|
workspaceFolder: clientOptions.workspaceFolder,
|
|
connectionOptions: clientOptions.connectionOptions,
|
|
markdown,
|
|
// suspend: {
|
|
// mode: clientOptions.suspend?.mode ?? SuspendMode.off,
|
|
// callback: clientOptions.suspend?.callback ?? (() => Promise.resolve(true)),
|
|
// interval: clientOptions.suspend?.interval ? Math.max(clientOptions.suspend.interval, defaultInterval) : defaultInterval
|
|
// },
|
|
diagnosticPullOptions: clientOptions.diagnosticPullOptions ?? { onChange: true, onSave: false },
|
|
notebookDocumentOptions: clientOptions.notebookDocumentOptions ?? {}
|
|
};
|
|
this._clientOptions.synchronize = this._clientOptions.synchronize || {};
|
|
this._state = ClientState.Initial;
|
|
this._ignoredRegistrations = new Set();
|
|
this._listeners = [];
|
|
this._notificationHandlers = new Map();
|
|
this._pendingNotificationHandlers = new Map();
|
|
this._notificationDisposables = new Map();
|
|
this._requestHandlers = new Map();
|
|
this._pendingRequestHandlers = new Map();
|
|
this._requestDisposables = new Map();
|
|
this._progressHandlers = new Map();
|
|
this._pendingProgressHandlers = new Map();
|
|
this._progressDisposables = new Map();
|
|
this._connection = undefined;
|
|
// this._idleStart = undefined;
|
|
this._initializeResult = undefined;
|
|
if (clientOptions.outputChannel) {
|
|
this._outputChannel = clientOptions.outputChannel;
|
|
this._disposeOutputChannel = false;
|
|
}
|
|
else {
|
|
this._outputChannel = undefined;
|
|
this._disposeOutputChannel = true;
|
|
}
|
|
this._traceOutputChannel = clientOptions.traceOutputChannel;
|
|
this._diagnostics = undefined;
|
|
this._pendingOpenNotifications = new Set();
|
|
this._pendingChangeSemaphore = new async_1.Semaphore(1);
|
|
this._pendingChangeDelayer = new async_1.Delayer(250);
|
|
this._fileEvents = [];
|
|
this._fileEventDelayer = new async_1.Delayer(250);
|
|
this._onStop = undefined;
|
|
this._telemetryEmitter = new vscode_languageserver_protocol_1.Emitter();
|
|
this._stateChangeEmitter = new vscode_languageserver_protocol_1.Emitter();
|
|
this._trace = vscode_languageserver_protocol_1.Trace.Off;
|
|
this._tracer = {
|
|
log: (messageOrDataObject, data) => {
|
|
if (Is.string(messageOrDataObject)) {
|
|
this.logTrace(messageOrDataObject, data);
|
|
}
|
|
else {
|
|
this.logObjectTrace(messageOrDataObject);
|
|
}
|
|
},
|
|
};
|
|
this._c2p = c2p.createConverter(clientOptions.uriConverters ? clientOptions.uriConverters.code2Protocol : undefined);
|
|
this._p2c = p2c.createConverter(clientOptions.uriConverters ? clientOptions.uriConverters.protocol2Code : undefined, this._clientOptions.markdown.isTrusted, this._clientOptions.markdown.supportHtml);
|
|
this._syncedDocuments = new Map();
|
|
this.registerBuiltinFeatures();
|
|
}
|
|
get name() {
|
|
return this._name;
|
|
}
|
|
get middleware() {
|
|
return this._clientOptions.middleware ?? Object.create(null);
|
|
}
|
|
get clientOptions() {
|
|
return this._clientOptions;
|
|
}
|
|
get protocol2CodeConverter() {
|
|
return this._p2c;
|
|
}
|
|
get code2ProtocolConverter() {
|
|
return this._c2p;
|
|
}
|
|
get onTelemetry() {
|
|
return this._telemetryEmitter.event;
|
|
}
|
|
get onDidChangeState() {
|
|
return this._stateChangeEmitter.event;
|
|
}
|
|
get outputChannel() {
|
|
if (!this._outputChannel) {
|
|
this._outputChannel = vscode_1.window.createOutputChannel(this._clientOptions.outputChannelName ? this._clientOptions.outputChannelName : this._name);
|
|
}
|
|
return this._outputChannel;
|
|
}
|
|
get traceOutputChannel() {
|
|
if (this._traceOutputChannel) {
|
|
return this._traceOutputChannel;
|
|
}
|
|
return this.outputChannel;
|
|
}
|
|
get diagnostics() {
|
|
return this._diagnostics;
|
|
}
|
|
get state() {
|
|
return this.getPublicState();
|
|
}
|
|
get $state() {
|
|
return this._state;
|
|
}
|
|
set $state(value) {
|
|
let oldState = this.getPublicState();
|
|
this._state = value;
|
|
let newState = this.getPublicState();
|
|
if (newState !== oldState) {
|
|
this._stateChangeEmitter.fire({ oldState, newState });
|
|
}
|
|
}
|
|
getPublicState() {
|
|
switch (this.$state) {
|
|
case ClientState.Starting:
|
|
return State.Starting;
|
|
case ClientState.Running:
|
|
return State.Running;
|
|
default:
|
|
return State.Stopped;
|
|
}
|
|
}
|
|
get initializeResult() {
|
|
return this._initializeResult;
|
|
}
|
|
async sendRequest(type, ...params) {
|
|
if (this.$state === ClientState.StartFailed || this.$state === ClientState.Stopping || this.$state === ClientState.Stopped) {
|
|
return Promise.reject(new vscode_languageserver_protocol_1.ResponseError(vscode_languageserver_protocol_1.ErrorCodes.ConnectionInactive, `Client is not running`));
|
|
}
|
|
// Ensure we have a connection before we force the document sync.
|
|
const connection = await this.$start();
|
|
// If any document is synced in full mode make sure we flush any pending
|
|
// full document syncs.
|
|
if (this._didChangeTextDocumentFeature.syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Full) {
|
|
await this.sendPendingFullTextDocumentChanges(connection);
|
|
}
|
|
const _sendRequest = this._clientOptions.middleware?.sendRequest;
|
|
if (_sendRequest !== undefined) {
|
|
let param = undefined;
|
|
let token = undefined;
|
|
// Separate cancellation tokens from other parameters for a better client interface
|
|
if (params.length === 1) {
|
|
// CancellationToken is an interface, so we need to check if the first param complies to it
|
|
if (vscode_languageserver_protocol_1.CancellationToken.is(params[0])) {
|
|
token = params[0];
|
|
}
|
|
else {
|
|
param = params[0];
|
|
}
|
|
}
|
|
else if (params.length === 2) {
|
|
param = params[0];
|
|
token = params[1];
|
|
}
|
|
// Return the general middleware invocation defining `next` as a utility function that reorganizes parameters to
|
|
// pass them to the original sendRequest function.
|
|
return _sendRequest(type, param, token, (type, param, token) => {
|
|
const params = [];
|
|
// Add the parameters if there are any
|
|
if (param !== undefined) {
|
|
params.push(param);
|
|
}
|
|
// Add the cancellation token if there is one
|
|
if (token !== undefined) {
|
|
params.push(token);
|
|
}
|
|
return connection.sendRequest(type, ...params);
|
|
});
|
|
}
|
|
else {
|
|
return connection.sendRequest(type, ...params);
|
|
}
|
|
}
|
|
onRequest(type, handler) {
|
|
const method = typeof type === 'string' ? type : type.method;
|
|
this._requestHandlers.set(method, handler);
|
|
const connection = this.activeConnection();
|
|
let disposable;
|
|
if (connection !== undefined) {
|
|
this._requestDisposables.set(method, connection.onRequest(type, handler));
|
|
disposable = {
|
|
dispose: () => {
|
|
const disposable = this._requestDisposables.get(method);
|
|
if (disposable !== undefined) {
|
|
disposable.dispose();
|
|
this._requestDisposables.delete(method);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
else {
|
|
this._pendingRequestHandlers.set(method, handler);
|
|
disposable = {
|
|
dispose: () => {
|
|
this._pendingRequestHandlers.delete(method);
|
|
const disposable = this._requestDisposables.get(method);
|
|
if (disposable !== undefined) {
|
|
disposable.dispose();
|
|
this._requestDisposables.delete(method);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
return {
|
|
dispose: () => {
|
|
this._requestHandlers.delete(method);
|
|
disposable.dispose();
|
|
}
|
|
};
|
|
}
|
|
async sendNotification(type, params) {
|
|
if (this.$state === ClientState.StartFailed || this.$state === ClientState.Stopping || this.$state === ClientState.Stopped) {
|
|
return Promise.reject(new vscode_languageserver_protocol_1.ResponseError(vscode_languageserver_protocol_1.ErrorCodes.ConnectionInactive, `Client is not running`));
|
|
}
|
|
const needsPendingFullTextDocumentSync = this._didChangeTextDocumentFeature.syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Full;
|
|
let openNotification;
|
|
if (needsPendingFullTextDocumentSync && typeof type !== 'string' && type.method === vscode_languageserver_protocol_1.DidOpenTextDocumentNotification.method) {
|
|
openNotification = params?.textDocument.uri;
|
|
this._pendingOpenNotifications.add(openNotification);
|
|
}
|
|
// Ensure we have a connection before we force the document sync.
|
|
const connection = await this.$start();
|
|
// If any document is synced in full mode make sure we flush any pending
|
|
// full document syncs.
|
|
if (needsPendingFullTextDocumentSync) {
|
|
await this.sendPendingFullTextDocumentChanges(connection);
|
|
}
|
|
// We need to remove the pending open notification before we actually
|
|
// send the notification over the connection. Otherwise there could be
|
|
// a request coming in that although the open notification got already put
|
|
// onto the wire will ignore pending document changes.
|
|
//
|
|
// Since the code path of connection.sendNotification is actually sync
|
|
// until the message is handed of to the writer and the writer as a semaphore
|
|
// lock with a capacity of 1 no additional async scheduling can happen until
|
|
// the message is actually handed of.
|
|
if (openNotification !== undefined) {
|
|
this._pendingOpenNotifications.delete(openNotification);
|
|
}
|
|
const _sendNotification = this._clientOptions.middleware?.sendNotification;
|
|
return _sendNotification
|
|
? _sendNotification(type, connection.sendNotification.bind(connection), params)
|
|
: connection.sendNotification(type, params);
|
|
}
|
|
onNotification(type, handler) {
|
|
const method = typeof type === 'string' ? type : type.method;
|
|
this._notificationHandlers.set(method, handler);
|
|
const connection = this.activeConnection();
|
|
let disposable;
|
|
if (connection !== undefined) {
|
|
this._notificationDisposables.set(method, connection.onNotification(type, handler));
|
|
disposable = {
|
|
dispose: () => {
|
|
const disposable = this._notificationDisposables.get(method);
|
|
if (disposable !== undefined) {
|
|
disposable.dispose();
|
|
this._notificationDisposables.delete(method);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
else {
|
|
this._pendingNotificationHandlers.set(method, handler);
|
|
disposable = {
|
|
dispose: () => {
|
|
this._pendingNotificationHandlers.delete(method);
|
|
const disposable = this._notificationDisposables.get(method);
|
|
if (disposable !== undefined) {
|
|
disposable.dispose();
|
|
this._notificationDisposables.delete(method);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
return {
|
|
dispose: () => {
|
|
this._notificationHandlers.delete(method);
|
|
disposable.dispose();
|
|
}
|
|
};
|
|
}
|
|
async sendProgress(type, token, value) {
|
|
if (this.$state === ClientState.StartFailed || this.$state === ClientState.Stopping || this.$state === ClientState.Stopped) {
|
|
return Promise.reject(new vscode_languageserver_protocol_1.ResponseError(vscode_languageserver_protocol_1.ErrorCodes.ConnectionInactive, `Client is not running`));
|
|
}
|
|
try {
|
|
// Ensure we have a connection before we force the document sync.
|
|
const connection = await this.$start();
|
|
return connection.sendProgress(type, token, value);
|
|
}
|
|
catch (error) {
|
|
this.error(`Sending progress for token ${token} failed.`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
onProgress(type, token, handler) {
|
|
this._progressHandlers.set(token, { type, handler });
|
|
const connection = this.activeConnection();
|
|
let disposable;
|
|
const handleWorkDoneProgress = this._clientOptions.middleware?.handleWorkDoneProgress;
|
|
const realHandler = vscode_languageserver_protocol_1.WorkDoneProgress.is(type) && handleWorkDoneProgress !== undefined
|
|
? (params) => {
|
|
handleWorkDoneProgress(token, params, () => handler(params));
|
|
}
|
|
: handler;
|
|
if (connection !== undefined) {
|
|
this._progressDisposables.set(token, connection.onProgress(type, token, realHandler));
|
|
disposable = {
|
|
dispose: () => {
|
|
const disposable = this._progressDisposables.get(token);
|
|
if (disposable !== undefined) {
|
|
disposable.dispose();
|
|
this._progressDisposables.delete(token);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
else {
|
|
this._pendingProgressHandlers.set(token, { type, handler });
|
|
disposable = {
|
|
dispose: () => {
|
|
this._pendingProgressHandlers.delete(token);
|
|
const disposable = this._progressDisposables.get(token);
|
|
if (disposable !== undefined) {
|
|
disposable.dispose();
|
|
this._progressDisposables.delete(token);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
return {
|
|
dispose: () => {
|
|
this._progressHandlers.delete(token);
|
|
disposable.dispose();
|
|
}
|
|
};
|
|
}
|
|
createDefaultErrorHandler(maxRestartCount) {
|
|
if (maxRestartCount !== undefined && maxRestartCount < 0) {
|
|
throw new Error(`Invalid maxRestartCount: ${maxRestartCount}`);
|
|
}
|
|
return new DefaultErrorHandler(this, maxRestartCount ?? 4);
|
|
}
|
|
async setTrace(value) {
|
|
this._trace = value;
|
|
const connection = this.activeConnection();
|
|
if (connection !== undefined) {
|
|
await connection.trace(this._trace, this._tracer, {
|
|
sendNotification: false,
|
|
traceFormat: this._traceFormat
|
|
});
|
|
}
|
|
}
|
|
data2String(data) {
|
|
if (data instanceof vscode_languageserver_protocol_1.ResponseError) {
|
|
const responseError = data;
|
|
return ` Message: ${responseError.message}\n Code: ${responseError.code} ${responseError.data ? '\n' + responseError.data.toString() : ''}`;
|
|
}
|
|
if (data instanceof Error) {
|
|
if (Is.string(data.stack)) {
|
|
return data.stack;
|
|
}
|
|
return data.message;
|
|
}
|
|
if (Is.string(data)) {
|
|
return data;
|
|
}
|
|
return data.toString();
|
|
}
|
|
debug(message, data, showNotification = true) {
|
|
this.logOutputMessage(vscode_languageserver_protocol_1.MessageType.Debug, RevealOutputChannelOn.Debug, 'Debug', message, data, showNotification);
|
|
}
|
|
info(message, data, showNotification = true) {
|
|
this.logOutputMessage(vscode_languageserver_protocol_1.MessageType.Info, RevealOutputChannelOn.Info, 'Info', message, data, showNotification);
|
|
}
|
|
warn(message, data, showNotification = true) {
|
|
this.logOutputMessage(vscode_languageserver_protocol_1.MessageType.Warning, RevealOutputChannelOn.Warn, 'Warn', message, data, showNotification);
|
|
}
|
|
error(message, data, showNotification = true) {
|
|
this.logOutputMessage(vscode_languageserver_protocol_1.MessageType.Error, RevealOutputChannelOn.Error, 'Error', message, data, showNotification);
|
|
}
|
|
logOutputMessage(type, reveal, name, message, data, showNotification) {
|
|
this.outputChannel.appendLine(`[${name.padEnd(5)} - ${(new Date().toLocaleTimeString())}] ${message}`);
|
|
if (data !== null && data !== undefined) {
|
|
this.outputChannel.appendLine(this.data2String(data));
|
|
}
|
|
if (showNotification === 'force' || (showNotification && this._clientOptions.revealOutputChannelOn <= reveal)) {
|
|
this.showNotificationMessage(type, message);
|
|
}
|
|
}
|
|
showNotificationMessage(type, message) {
|
|
message = message ?? 'A request has failed. See the output for more information.';
|
|
const messageFunc = type === vscode_languageserver_protocol_1.MessageType.Error
|
|
? vscode_1.window.showErrorMessage
|
|
: type === vscode_languageserver_protocol_1.MessageType.Warning
|
|
? vscode_1.window.showWarningMessage
|
|
: vscode_1.window.showInformationMessage;
|
|
void messageFunc(message, 'Go to output').then((selection) => {
|
|
if (selection !== undefined) {
|
|
this.outputChannel.show(true);
|
|
}
|
|
});
|
|
}
|
|
logTrace(message, data) {
|
|
this.traceOutputChannel.appendLine(`[Trace - ${(new Date().toLocaleTimeString())}] ${message}`);
|
|
if (data) {
|
|
this.traceOutputChannel.appendLine(this.data2String(data));
|
|
}
|
|
}
|
|
logObjectTrace(data) {
|
|
if (data.isLSPMessage && data.type) {
|
|
this.traceOutputChannel.append(`[LSP - ${(new Date().toLocaleTimeString())}] `);
|
|
}
|
|
else {
|
|
this.traceOutputChannel.append(`[Trace - ${(new Date().toLocaleTimeString())}] `);
|
|
}
|
|
if (data) {
|
|
this.traceOutputChannel.appendLine(`${JSON.stringify(data)}`);
|
|
}
|
|
}
|
|
needsStart() {
|
|
return this.$state === ClientState.Initial || this.$state === ClientState.Stopping || this.$state === ClientState.Stopped;
|
|
}
|
|
needsStop() {
|
|
return this.$state === ClientState.Starting || this.$state === ClientState.Running;
|
|
}
|
|
activeConnection() {
|
|
return this.$state === ClientState.Running && this._connection !== undefined ? this._connection : undefined;
|
|
}
|
|
isRunning() {
|
|
return this.$state === ClientState.Running;
|
|
}
|
|
async start() {
|
|
if (this._disposed === 'disposing' || this._disposed === 'disposed') {
|
|
throw new Error(`Client got disposed and can't be restarted.`);
|
|
}
|
|
if (this.$state === ClientState.Stopping) {
|
|
throw new Error(`Client is currently stopping. Can only restart a full stopped client`);
|
|
}
|
|
// We are already running or are in the process of getting up
|
|
// to speed.
|
|
if (this._onStart !== undefined) {
|
|
return this._onStart;
|
|
}
|
|
const [promise, resolve, reject] = this.createOnStartPromise();
|
|
this._onStart = promise;
|
|
// If we restart then the diagnostics collection is reused.
|
|
if (this._diagnostics === undefined) {
|
|
this._diagnostics = this._clientOptions.diagnosticCollectionName
|
|
? vscode_1.languages.createDiagnosticCollection(this._clientOptions.diagnosticCollectionName)
|
|
: vscode_1.languages.createDiagnosticCollection();
|
|
}
|
|
// When we start make all buffer handlers pending so that they
|
|
// get added.
|
|
for (const [method, handler] of this._notificationHandlers) {
|
|
if (!this._pendingNotificationHandlers.has(method)) {
|
|
this._pendingNotificationHandlers.set(method, handler);
|
|
}
|
|
}
|
|
for (const [method, handler] of this._requestHandlers) {
|
|
if (!this._pendingRequestHandlers.has(method)) {
|
|
this._pendingRequestHandlers.set(method, handler);
|
|
}
|
|
}
|
|
for (const [token, data] of this._progressHandlers) {
|
|
if (!this._pendingProgressHandlers.has(token)) {
|
|
this._pendingProgressHandlers.set(token, data);
|
|
}
|
|
}
|
|
this.$state = ClientState.Starting;
|
|
try {
|
|
const connection = await this.createConnection();
|
|
connection.onNotification(vscode_languageserver_protocol_1.LogMessageNotification.type, (message) => {
|
|
switch (message.type) {
|
|
case vscode_languageserver_protocol_1.MessageType.Error:
|
|
this.error(message.message, undefined, false);
|
|
break;
|
|
case vscode_languageserver_protocol_1.MessageType.Warning:
|
|
this.warn(message.message, undefined, false);
|
|
break;
|
|
case vscode_languageserver_protocol_1.MessageType.Info:
|
|
this.info(message.message, undefined, false);
|
|
break;
|
|
case vscode_languageserver_protocol_1.MessageType.Debug:
|
|
this.debug(message.message, undefined, false);
|
|
break;
|
|
default:
|
|
this.outputChannel.appendLine(message.message);
|
|
}
|
|
});
|
|
connection.onNotification(vscode_languageserver_protocol_1.ShowMessageNotification.type, (message) => {
|
|
switch (message.type) {
|
|
case vscode_languageserver_protocol_1.MessageType.Error:
|
|
void vscode_1.window.showErrorMessage(message.message);
|
|
break;
|
|
case vscode_languageserver_protocol_1.MessageType.Warning:
|
|
void vscode_1.window.showWarningMessage(message.message);
|
|
break;
|
|
case vscode_languageserver_protocol_1.MessageType.Info:
|
|
void vscode_1.window.showInformationMessage(message.message);
|
|
break;
|
|
default:
|
|
void vscode_1.window.showInformationMessage(message.message);
|
|
}
|
|
});
|
|
connection.onRequest(vscode_languageserver_protocol_1.ShowMessageRequest.type, (params) => {
|
|
let messageFunc;
|
|
switch (params.type) {
|
|
case vscode_languageserver_protocol_1.MessageType.Error:
|
|
messageFunc = vscode_1.window.showErrorMessage;
|
|
break;
|
|
case vscode_languageserver_protocol_1.MessageType.Warning:
|
|
messageFunc = vscode_1.window.showWarningMessage;
|
|
break;
|
|
case vscode_languageserver_protocol_1.MessageType.Info:
|
|
messageFunc = vscode_1.window.showInformationMessage;
|
|
break;
|
|
default:
|
|
messageFunc = vscode_1.window.showInformationMessage;
|
|
}
|
|
let actions = params.actions || [];
|
|
return messageFunc(params.message, ...actions);
|
|
});
|
|
connection.onNotification(vscode_languageserver_protocol_1.TelemetryEventNotification.type, (data) => {
|
|
this._telemetryEmitter.fire(data);
|
|
});
|
|
connection.onRequest(vscode_languageserver_protocol_1.ShowDocumentRequest.type, async (params) => {
|
|
const showDocument = async (params) => {
|
|
const uri = this.protocol2CodeConverter.asUri(params.uri);
|
|
try {
|
|
if (params.external === true) {
|
|
const success = await vscode_1.env.openExternal(uri);
|
|
return { success };
|
|
}
|
|
else {
|
|
const options = {};
|
|
if (params.selection !== undefined) {
|
|
options.selection = this.protocol2CodeConverter.asRange(params.selection);
|
|
}
|
|
if (params.takeFocus === undefined || params.takeFocus === false) {
|
|
options.preserveFocus = true;
|
|
}
|
|
else if (params.takeFocus === true) {
|
|
options.preserveFocus = false;
|
|
}
|
|
await vscode_1.window.showTextDocument(uri, options);
|
|
return { success: true };
|
|
}
|
|
}
|
|
catch (error) {
|
|
return { success: false };
|
|
}
|
|
};
|
|
const middleware = this._clientOptions.middleware.window?.showDocument;
|
|
if (middleware !== undefined) {
|
|
return middleware(params, showDocument);
|
|
}
|
|
else {
|
|
return showDocument(params);
|
|
}
|
|
});
|
|
connection.listen();
|
|
await this.initialize(connection);
|
|
resolve();
|
|
}
|
|
catch (error) {
|
|
this.$state = ClientState.StartFailed;
|
|
this.error(`${this._name} client: couldn't create connection to server.`, error, 'force');
|
|
reject(error);
|
|
}
|
|
return this._onStart;
|
|
}
|
|
createOnStartPromise() {
|
|
let resolve;
|
|
let reject;
|
|
const promise = new Promise((_resolve, _reject) => {
|
|
resolve = _resolve;
|
|
reject = _reject;
|
|
});
|
|
return [promise, resolve, reject];
|
|
}
|
|
async initialize(connection) {
|
|
this.refreshTrace(connection, false);
|
|
const initOption = this._clientOptions.initializationOptions;
|
|
// If the client is locked to a workspace folder use it. In this case the workspace folder
|
|
// feature is not registered and we need to initialize the value here.
|
|
const [rootPath, workspaceFolders] = this._clientOptions.workspaceFolder !== undefined
|
|
? [this._clientOptions.workspaceFolder.uri.fsPath, [{ uri: this._c2p.asUri(this._clientOptions.workspaceFolder.uri), name: this._clientOptions.workspaceFolder.name }]]
|
|
: [this._clientGetRootPath(), null];
|
|
const initParams = {
|
|
processId: null,
|
|
clientInfo: {
|
|
name: vscode_1.env.appName,
|
|
version: vscode_1.version
|
|
},
|
|
locale: this.getLocale(),
|
|
rootPath: rootPath ? rootPath : null,
|
|
rootUri: rootPath ? this._c2p.asUri(vscode_1.Uri.file(rootPath)) : null,
|
|
capabilities: this.computeClientCapabilities(),
|
|
initializationOptions: Is.func(initOption) ? initOption() : initOption,
|
|
trace: vscode_languageserver_protocol_1.Trace.toString(this._trace),
|
|
workspaceFolders: workspaceFolders
|
|
};
|
|
this.fillInitializeParams(initParams);
|
|
if (this._clientOptions.progressOnInitialization) {
|
|
const token = UUID.generateUuid();
|
|
const part = new progressPart_1.ProgressPart(connection, token);
|
|
initParams.workDoneToken = token;
|
|
try {
|
|
const result = await this.doInitialize(connection, initParams);
|
|
part.done();
|
|
return result;
|
|
}
|
|
catch (error) {
|
|
part.cancel();
|
|
throw error;
|
|
}
|
|
}
|
|
else {
|
|
return this.doInitialize(connection, initParams);
|
|
}
|
|
}
|
|
async doInitialize(connection, initParams) {
|
|
try {
|
|
const result = await connection.initialize(initParams);
|
|
if (result.capabilities.positionEncoding !== undefined && result.capabilities.positionEncoding !== vscode_languageserver_protocol_1.PositionEncodingKind.UTF16) {
|
|
throw new Error(`Unsupported position encoding (${result.capabilities.positionEncoding}) received from server ${this.name}`);
|
|
}
|
|
this._initializeResult = result;
|
|
this.$state = ClientState.Running;
|
|
let textDocumentSyncOptions = undefined;
|
|
if (Is.number(result.capabilities.textDocumentSync)) {
|
|
if (result.capabilities.textDocumentSync === vscode_languageserver_protocol_1.TextDocumentSyncKind.None) {
|
|
textDocumentSyncOptions = {
|
|
openClose: false,
|
|
change: vscode_languageserver_protocol_1.TextDocumentSyncKind.None,
|
|
save: undefined
|
|
};
|
|
}
|
|
else {
|
|
textDocumentSyncOptions = {
|
|
openClose: true,
|
|
change: result.capabilities.textDocumentSync,
|
|
save: {
|
|
includeText: false
|
|
}
|
|
};
|
|
}
|
|
}
|
|
else if (result.capabilities.textDocumentSync !== undefined && result.capabilities.textDocumentSync !== null) {
|
|
textDocumentSyncOptions = result.capabilities.textDocumentSync;
|
|
}
|
|
this._capabilities = Object.assign({}, result.capabilities, { resolvedTextDocumentSync: textDocumentSyncOptions });
|
|
connection.onNotification(vscode_languageserver_protocol_1.PublishDiagnosticsNotification.type, params => this.handleDiagnostics(params));
|
|
connection.onRequest(vscode_languageserver_protocol_1.RegistrationRequest.type, params => this.handleRegistrationRequest(params));
|
|
// See https://github.com/Microsoft/vscode-languageserver-node/issues/199
|
|
connection.onRequest('client/registerFeature', params => this.handleRegistrationRequest(params));
|
|
connection.onRequest(vscode_languageserver_protocol_1.UnregistrationRequest.type, params => this.handleUnregistrationRequest(params));
|
|
// See https://github.com/Microsoft/vscode-languageserver-node/issues/199
|
|
connection.onRequest('client/unregisterFeature', params => this.handleUnregistrationRequest(params));
|
|
connection.onRequest(vscode_languageserver_protocol_1.ApplyWorkspaceEditRequest.type, params => this.handleApplyWorkspaceEdit(params));
|
|
// Add pending notification, request and progress handlers.
|
|
for (const [method, handler] of this._pendingNotificationHandlers) {
|
|
this._notificationDisposables.set(method, connection.onNotification(method, handler));
|
|
}
|
|
this._pendingNotificationHandlers.clear();
|
|
for (const [method, handler] of this._pendingRequestHandlers) {
|
|
this._requestDisposables.set(method, connection.onRequest(method, handler));
|
|
}
|
|
this._pendingRequestHandlers.clear();
|
|
for (const [token, data] of this._pendingProgressHandlers) {
|
|
this._progressDisposables.set(token, connection.onProgress(data.type, token, data.handler));
|
|
}
|
|
this._pendingProgressHandlers.clear();
|
|
// if (this._clientOptions.suspend.mode !== SuspendMode.off) {
|
|
// this._idleInterval = RAL().timer.setInterval(() => this.checkSuspend(), this._clientOptions.suspend.interval);
|
|
// }
|
|
await connection.sendNotification(vscode_languageserver_protocol_1.InitializedNotification.type, {});
|
|
this.hookFileEvents(connection);
|
|
this.hookConfigurationChanged(connection);
|
|
this.initializeFeatures(connection);
|
|
return result;
|
|
}
|
|
catch (error) {
|
|
if (this._clientOptions.initializationFailedHandler) {
|
|
if (this._clientOptions.initializationFailedHandler(error)) {
|
|
void this.initialize(connection);
|
|
}
|
|
else {
|
|
void this.stop();
|
|
}
|
|
}
|
|
else if (error instanceof vscode_languageserver_protocol_1.ResponseError && error.data && error.data.retry) {
|
|
void vscode_1.window.showErrorMessage(error.message, { title: 'Retry', id: 'retry' }).then(item => {
|
|
if (item && item.id === 'retry') {
|
|
void this.initialize(connection);
|
|
}
|
|
else {
|
|
void this.stop();
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
if (error && error.message) {
|
|
void vscode_1.window.showErrorMessage(error.message);
|
|
}
|
|
this.error('Server initialization failed.', error);
|
|
void this.stop();
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
_clientGetRootPath() {
|
|
let folders = vscode_1.workspace.workspaceFolders;
|
|
if (!folders || folders.length === 0) {
|
|
return undefined;
|
|
}
|
|
let folder = folders[0];
|
|
if (folder.uri.scheme === 'file') {
|
|
return folder.uri.fsPath;
|
|
}
|
|
return undefined;
|
|
}
|
|
stop(timeout = 2000) {
|
|
// Wait 2 seconds on stop
|
|
return this.shutdown('stop', timeout);
|
|
}
|
|
dispose(timeout = 2000) {
|
|
try {
|
|
this._disposed = 'disposing';
|
|
return this.stop(timeout);
|
|
}
|
|
finally {
|
|
this._disposed = 'disposed';
|
|
}
|
|
}
|
|
async shutdown(mode, timeout) {
|
|
// If the client is stopped or in its initial state return.
|
|
if (this.$state === ClientState.Stopped || this.$state === ClientState.Initial) {
|
|
return;
|
|
}
|
|
// If we are stopping the client and have a stop promise return it.
|
|
if (this.$state === ClientState.Stopping) {
|
|
if (this._onStop !== undefined) {
|
|
return this._onStop;
|
|
}
|
|
else {
|
|
throw new Error(`Client is stopping but no stop promise available.`);
|
|
}
|
|
}
|
|
const connection = this.activeConnection();
|
|
// We can't stop a client that is not running (e.g. has no connection). Especially not
|
|
// on that us starting since it can't be correctly synchronized.
|
|
if (connection === undefined || this.$state !== ClientState.Running) {
|
|
throw new Error(`Client is not running and can't be stopped. It's current state is: ${this.$state}`);
|
|
}
|
|
this._initializeResult = undefined;
|
|
this.$state = ClientState.Stopping;
|
|
this.cleanUp(mode);
|
|
const tp = new Promise(c => { (0, vscode_languageserver_protocol_1.RAL)().timer.setTimeout(c, timeout); });
|
|
const shutdown = (async (connection) => {
|
|
await connection.shutdown();
|
|
await connection.exit();
|
|
return connection;
|
|
})(connection);
|
|
return this._onStop = Promise.race([tp, shutdown]).then((connection) => {
|
|
// The connection won the race with the timeout.
|
|
if (connection !== undefined) {
|
|
connection.end();
|
|
connection.dispose();
|
|
}
|
|
else {
|
|
this.error(`Stopping server timed out`, undefined, false);
|
|
throw new Error(`Stopping the server timed out`);
|
|
}
|
|
}, (error) => {
|
|
this.error(`Stopping server failed`, error, false);
|
|
throw error;
|
|
}).finally(() => {
|
|
this.$state = ClientState.Stopped;
|
|
mode === 'stop' && this.cleanUpChannel();
|
|
this._onStart = undefined;
|
|
this._onStop = undefined;
|
|
this._connection = undefined;
|
|
this._ignoredRegistrations.clear();
|
|
});
|
|
}
|
|
cleanUp(mode) {
|
|
// purge outstanding file events.
|
|
this._fileEvents = [];
|
|
this._fileEventDelayer.cancel();
|
|
const disposables = this._listeners.splice(0, this._listeners.length);
|
|
for (const disposable of disposables) {
|
|
disposable.dispose();
|
|
}
|
|
if (this._syncedDocuments) {
|
|
this._syncedDocuments.clear();
|
|
}
|
|
// Clear features in reverse order;
|
|
for (const feature of Array.from(this._features.entries()).map(entry => entry[1]).reverse()) {
|
|
feature.clear();
|
|
}
|
|
if (mode === 'stop' && this._diagnostics !== undefined) {
|
|
this._diagnostics.dispose();
|
|
this._diagnostics = undefined;
|
|
}
|
|
if (this._idleInterval !== undefined) {
|
|
this._idleInterval.dispose();
|
|
this._idleInterval = undefined;
|
|
}
|
|
// this._idleStart = undefined;
|
|
}
|
|
cleanUpChannel() {
|
|
if (this._outputChannel !== undefined && this._disposeOutputChannel) {
|
|
this._outputChannel.dispose();
|
|
this._outputChannel = undefined;
|
|
}
|
|
}
|
|
notifyFileEvent(event) {
|
|
const client = this;
|
|
async function didChangeWatchedFile(event) {
|
|
client._fileEvents.push(event);
|
|
return client._fileEventDelayer.trigger(async () => {
|
|
await client.sendNotification(vscode_languageserver_protocol_1.DidChangeWatchedFilesNotification.type, { changes: client._fileEvents });
|
|
client._fileEvents = [];
|
|
});
|
|
}
|
|
const workSpaceMiddleware = this.clientOptions.middleware?.workspace;
|
|
(workSpaceMiddleware?.didChangeWatchedFile ? workSpaceMiddleware.didChangeWatchedFile(event, didChangeWatchedFile) : didChangeWatchedFile(event)).catch((error) => {
|
|
client.error(`Notify file events failed.`, error);
|
|
});
|
|
}
|
|
async sendPendingFullTextDocumentChanges(connection) {
|
|
return this._pendingChangeSemaphore.lock(async () => {
|
|
try {
|
|
const changes = this._didChangeTextDocumentFeature.getPendingDocumentChanges(this._pendingOpenNotifications);
|
|
if (changes.length === 0) {
|
|
return;
|
|
}
|
|
for (const document of changes) {
|
|
const params = this.code2ProtocolConverter.asChangeTextDocumentParams(document);
|
|
// We await the send and not the delivery since it is more or less the same for
|
|
// notifications.
|
|
await connection.sendNotification(vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type, params);
|
|
this._didChangeTextDocumentFeature.notificationSent(document, vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type, params);
|
|
}
|
|
}
|
|
catch (error) {
|
|
this.error(`Sending pending changes failed`, error, false);
|
|
throw error;
|
|
}
|
|
});
|
|
}
|
|
triggerPendingChangeDelivery() {
|
|
this._pendingChangeDelayer.trigger(async () => {
|
|
const connection = this.activeConnection();
|
|
if (connection === undefined) {
|
|
this.triggerPendingChangeDelivery();
|
|
return;
|
|
}
|
|
await this.sendPendingFullTextDocumentChanges(connection);
|
|
}).catch((error) => this.error(`Delivering pending changes failed`, error, false));
|
|
}
|
|
handleDiagnostics(params) {
|
|
if (!this._diagnostics) {
|
|
return;
|
|
}
|
|
const key = params.uri;
|
|
if (this._diagnosticQueueState.state === 'busy' && this._diagnosticQueueState.document === key) {
|
|
// Cancel the active run;
|
|
this._diagnosticQueueState.tokenSource.cancel();
|
|
}
|
|
this._diagnosticQueue.set(params.uri, params.diagnostics);
|
|
this.triggerDiagnosticQueue();
|
|
}
|
|
triggerDiagnosticQueue() {
|
|
(0, vscode_languageserver_protocol_1.RAL)().timer.setImmediate(() => { this.workDiagnosticQueue(); });
|
|
}
|
|
workDiagnosticQueue() {
|
|
if (this._diagnosticQueueState.state === 'busy') {
|
|
return;
|
|
}
|
|
const next = this._diagnosticQueue.entries().next();
|
|
if (next.done === true) {
|
|
// Nothing in the queue
|
|
return;
|
|
}
|
|
const [document, diagnostics] = next.value;
|
|
this._diagnosticQueue.delete(document);
|
|
const tokenSource = new vscode_1.CancellationTokenSource();
|
|
this._diagnosticQueueState = { state: 'busy', document: document, tokenSource };
|
|
this._p2c.asDiagnostics(diagnostics, tokenSource.token).then((converted) => {
|
|
if (!tokenSource.token.isCancellationRequested) {
|
|
const uri = this._p2c.asUri(document);
|
|
const middleware = this.clientOptions.middleware;
|
|
if (middleware.handleDiagnostics) {
|
|
middleware.handleDiagnostics(uri, converted, (uri, diagnostics) => this.setDiagnostics(uri, diagnostics));
|
|
}
|
|
else {
|
|
this.setDiagnostics(uri, converted);
|
|
}
|
|
}
|
|
}).finally(() => {
|
|
this._diagnosticQueueState = { state: 'idle' };
|
|
this.triggerDiagnosticQueue();
|
|
});
|
|
}
|
|
setDiagnostics(uri, diagnostics) {
|
|
if (!this._diagnostics) {
|
|
return;
|
|
}
|
|
this._diagnostics.set(uri, diagnostics);
|
|
}
|
|
getLocale() {
|
|
return vscode_1.env.language;
|
|
}
|
|
async $start() {
|
|
if (this.$state === ClientState.StartFailed) {
|
|
throw new Error(`Previous start failed. Can't restart server.`);
|
|
}
|
|
await this.start();
|
|
const connection = this.activeConnection();
|
|
if (connection === undefined) {
|
|
throw new Error(`Starting server failed`);
|
|
}
|
|
return connection;
|
|
}
|
|
async createConnection() {
|
|
let errorHandler = (error, message, count) => {
|
|
this.handleConnectionError(error, message, count).catch((error) => this.error(`Handling connection error failed`, error));
|
|
};
|
|
let closeHandler = () => {
|
|
this.handleConnectionClosed().catch((error) => this.error(`Handling connection close failed`, error));
|
|
};
|
|
const transports = await this.createMessageTransports(this._clientOptions.stdioEncoding || 'utf8');
|
|
this._connection = createConnection(transports.reader, transports.writer, errorHandler, closeHandler, this._clientOptions.connectionOptions);
|
|
return this._connection;
|
|
}
|
|
async handleConnectionClosed() {
|
|
// Check whether this is a normal shutdown in progress or the client stopped normally.
|
|
if (this.$state === ClientState.Stopped) {
|
|
return;
|
|
}
|
|
try {
|
|
if (this._connection !== undefined) {
|
|
this._connection.dispose();
|
|
}
|
|
}
|
|
catch (error) {
|
|
// Disposing a connection could fail if error cases.
|
|
}
|
|
let handlerResult = { action: CloseAction.DoNotRestart };
|
|
if (this.$state !== ClientState.Stopping) {
|
|
try {
|
|
handlerResult = await this._clientOptions.errorHandler.closed();
|
|
}
|
|
catch (error) {
|
|
// Ignore errors coming from the error handler.
|
|
}
|
|
}
|
|
this._connection = undefined;
|
|
if (handlerResult.action === CloseAction.DoNotRestart) {
|
|
this.error(handlerResult.message ?? 'Connection to server got closed. Server will not be restarted.', undefined, handlerResult.handled === true ? false : 'force');
|
|
this.cleanUp('stop');
|
|
if (this.$state === ClientState.Starting) {
|
|
this.$state = ClientState.StartFailed;
|
|
}
|
|
else {
|
|
this.$state = ClientState.Stopped;
|
|
}
|
|
this._onStop = Promise.resolve();
|
|
this._onStart = undefined;
|
|
}
|
|
else if (handlerResult.action === CloseAction.Restart) {
|
|
this.info(handlerResult.message ?? 'Connection to server got closed. Server will restart.', !handlerResult.handled);
|
|
this.cleanUp('restart');
|
|
this.$state = ClientState.Initial;
|
|
this._onStop = Promise.resolve();
|
|
this._onStart = undefined;
|
|
this.start().catch((error) => this.error(`Restarting server failed`, error, 'force'));
|
|
}
|
|
}
|
|
async handleConnectionError(error, message, count) {
|
|
const handlerResult = await this._clientOptions.errorHandler.error(error, message, count);
|
|
if (handlerResult.action === ErrorAction.Shutdown) {
|
|
this.error(handlerResult.message ?? `Client ${this._name}: connection to server is erroring.\n${error.message}\nShutting down server.`, undefined, handlerResult.handled === true ? false : 'force');
|
|
this.stop().catch((error) => {
|
|
this.error(`Stopping server failed`, error, false);
|
|
});
|
|
}
|
|
else {
|
|
this.error(handlerResult.message ??
|
|
`Client ${this._name}: connection to server is erroring.\n${error.message}`, undefined, handlerResult.handled === true ? false : 'force');
|
|
}
|
|
}
|
|
hookConfigurationChanged(connection) {
|
|
this._listeners.push(vscode_1.workspace.onDidChangeConfiguration(() => {
|
|
this.refreshTrace(connection, true);
|
|
}));
|
|
}
|
|
refreshTrace(connection, sendNotification = false) {
|
|
const config = vscode_1.workspace.getConfiguration(this._id);
|
|
let trace = vscode_languageserver_protocol_1.Trace.Off;
|
|
let traceFormat = vscode_languageserver_protocol_1.TraceFormat.Text;
|
|
if (config) {
|
|
const traceConfig = config.get('trace.server', 'off');
|
|
if (typeof traceConfig === 'string') {
|
|
trace = vscode_languageserver_protocol_1.Trace.fromString(traceConfig);
|
|
}
|
|
else {
|
|
trace = vscode_languageserver_protocol_1.Trace.fromString(config.get('trace.server.verbosity', 'off'));
|
|
traceFormat = vscode_languageserver_protocol_1.TraceFormat.fromString(config.get('trace.server.format', 'text'));
|
|
}
|
|
}
|
|
this._trace = trace;
|
|
this._traceFormat = traceFormat;
|
|
connection.trace(this._trace, this._tracer, {
|
|
sendNotification,
|
|
traceFormat: this._traceFormat
|
|
}).catch((error) => { this.error(`Updating trace failed with error`, error, false); });
|
|
}
|
|
hookFileEvents(_connection) {
|
|
let fileEvents = this._clientOptions.synchronize.fileEvents;
|
|
if (!fileEvents) {
|
|
return;
|
|
}
|
|
let watchers;
|
|
if (Is.array(fileEvents)) {
|
|
watchers = fileEvents;
|
|
}
|
|
else {
|
|
watchers = [fileEvents];
|
|
}
|
|
if (!watchers) {
|
|
return;
|
|
}
|
|
this._dynamicFeatures.get(vscode_languageserver_protocol_1.DidChangeWatchedFilesNotification.type.method).registerRaw(UUID.generateUuid(), watchers);
|
|
}
|
|
registerFeatures(features) {
|
|
for (let feature of features) {
|
|
this.registerFeature(feature);
|
|
}
|
|
}
|
|
registerFeature(feature) {
|
|
this._features.push(feature);
|
|
if (features_1.DynamicFeature.is(feature)) {
|
|
const registrationType = feature.registrationType;
|
|
this._dynamicFeatures.set(registrationType.method, feature);
|
|
}
|
|
}
|
|
getFeature(request) {
|
|
return this._dynamicFeatures.get(request);
|
|
}
|
|
hasDedicatedTextSynchronizationFeature(textDocument) {
|
|
const feature = this.getFeature(vscode_languageserver_protocol_1.NotebookDocumentSyncRegistrationType.method);
|
|
if (feature === undefined || !(feature instanceof notebook_1.NotebookDocumentSyncFeature)) {
|
|
return false;
|
|
}
|
|
return feature.handles(textDocument);
|
|
}
|
|
registerBuiltinFeatures() {
|
|
const pendingFullTextDocumentChanges = new Map();
|
|
this.registerFeature(new configuration_1.ConfigurationFeature(this));
|
|
this.registerFeature(new textSynchronization_1.DidOpenTextDocumentFeature(this, this._syncedDocuments));
|
|
this._didChangeTextDocumentFeature = new textSynchronization_1.DidChangeTextDocumentFeature(this, pendingFullTextDocumentChanges);
|
|
this._didChangeTextDocumentFeature.onPendingChangeAdded(() => {
|
|
this.triggerPendingChangeDelivery();
|
|
});
|
|
this.registerFeature(this._didChangeTextDocumentFeature);
|
|
this.registerFeature(new textSynchronization_1.WillSaveFeature(this));
|
|
this.registerFeature(new textSynchronization_1.WillSaveWaitUntilFeature(this));
|
|
this.registerFeature(new textSynchronization_1.DidSaveTextDocumentFeature(this));
|
|
this.registerFeature(new textSynchronization_1.DidCloseTextDocumentFeature(this, this._syncedDocuments, pendingFullTextDocumentChanges));
|
|
this.registerFeature(new fileSystemWatcher_1.FileSystemWatcherFeature(this, (event) => this.notifyFileEvent(event)));
|
|
this.registerFeature(new completion_1.CompletionItemFeature(this));
|
|
this.registerFeature(new hover_1.HoverFeature(this));
|
|
this.registerFeature(new signatureHelp_1.SignatureHelpFeature(this));
|
|
this.registerFeature(new definition_1.DefinitionFeature(this));
|
|
this.registerFeature(new reference_1.ReferencesFeature(this));
|
|
this.registerFeature(new documentHighlight_1.DocumentHighlightFeature(this));
|
|
this.registerFeature(new documentSymbol_1.DocumentSymbolFeature(this));
|
|
this.registerFeature(new workspaceSymbol_1.WorkspaceSymbolFeature(this));
|
|
this.registerFeature(new codeAction_1.CodeActionFeature(this));
|
|
this.registerFeature(new codeLens_1.CodeLensFeature(this));
|
|
this.registerFeature(new formatting_1.DocumentFormattingFeature(this));
|
|
this.registerFeature(new formatting_1.DocumentRangeFormattingFeature(this));
|
|
this.registerFeature(new formatting_1.DocumentOnTypeFormattingFeature(this));
|
|
this.registerFeature(new rename_1.RenameFeature(this));
|
|
this.registerFeature(new documentLink_1.DocumentLinkFeature(this));
|
|
this.registerFeature(new executeCommand_1.ExecuteCommandFeature(this));
|
|
this.registerFeature(new configuration_1.SyncConfigurationFeature(this));
|
|
this.registerFeature(new typeDefinition_1.TypeDefinitionFeature(this));
|
|
this.registerFeature(new implementation_1.ImplementationFeature(this));
|
|
this.registerFeature(new colorProvider_1.ColorProviderFeature(this));
|
|
// We only register the workspace folder feature if the client is not locked
|
|
// to a specific workspace folder.
|
|
if (this.clientOptions.workspaceFolder === undefined) {
|
|
this.registerFeature(new workspaceFolder_1.WorkspaceFoldersFeature(this));
|
|
}
|
|
this.registerFeature(new foldingRange_1.FoldingRangeFeature(this));
|
|
this.registerFeature(new declaration_1.DeclarationFeature(this));
|
|
this.registerFeature(new selectionRange_1.SelectionRangeFeature(this));
|
|
this.registerFeature(new progress_1.ProgressFeature(this));
|
|
this.registerFeature(new callHierarchy_1.CallHierarchyFeature(this));
|
|
this.registerFeature(new semanticTokens_1.SemanticTokensFeature(this));
|
|
this.registerFeature(new linkedEditingRange_1.LinkedEditingFeature(this));
|
|
this.registerFeature(new fileOperations_1.DidCreateFilesFeature(this));
|
|
this.registerFeature(new fileOperations_1.DidRenameFilesFeature(this));
|
|
this.registerFeature(new fileOperations_1.DidDeleteFilesFeature(this));
|
|
this.registerFeature(new fileOperations_1.WillCreateFilesFeature(this));
|
|
this.registerFeature(new fileOperations_1.WillRenameFilesFeature(this));
|
|
this.registerFeature(new fileOperations_1.WillDeleteFilesFeature(this));
|
|
this.registerFeature(new typeHierarchy_1.TypeHierarchyFeature(this));
|
|
this.registerFeature(new inlineValue_1.InlineValueFeature(this));
|
|
this.registerFeature(new inlayHint_1.InlayHintsFeature(this));
|
|
this.registerFeature(new diagnostic_1.DiagnosticFeature(this));
|
|
this.registerFeature(new notebook_1.NotebookDocumentSyncFeature(this));
|
|
}
|
|
registerProposedFeatures() {
|
|
this.registerFeatures(ProposedFeatures.createAll(this));
|
|
}
|
|
fillInitializeParams(params) {
|
|
for (let feature of this._features) {
|
|
if (Is.func(feature.fillInitializeParams)) {
|
|
feature.fillInitializeParams(params);
|
|
}
|
|
}
|
|
}
|
|
computeClientCapabilities() {
|
|
const result = {};
|
|
(0, features_1.ensure)(result, 'workspace').applyEdit = true;
|
|
const workspaceEdit = (0, features_1.ensure)((0, features_1.ensure)(result, 'workspace'), 'workspaceEdit');
|
|
workspaceEdit.documentChanges = true;
|
|
workspaceEdit.resourceOperations = [vscode_languageserver_protocol_1.ResourceOperationKind.Create, vscode_languageserver_protocol_1.ResourceOperationKind.Rename, vscode_languageserver_protocol_1.ResourceOperationKind.Delete];
|
|
workspaceEdit.failureHandling = vscode_languageserver_protocol_1.FailureHandlingKind.TextOnlyTransactional;
|
|
workspaceEdit.normalizesLineEndings = true;
|
|
workspaceEdit.changeAnnotationSupport = {
|
|
groupsOnLabel: true
|
|
};
|
|
const diagnostics = (0, features_1.ensure)((0, features_1.ensure)(result, 'textDocument'), 'publishDiagnostics');
|
|
diagnostics.relatedInformation = true;
|
|
diagnostics.versionSupport = false;
|
|
diagnostics.tagSupport = { valueSet: [vscode_languageserver_protocol_1.DiagnosticTag.Unnecessary, vscode_languageserver_protocol_1.DiagnosticTag.Deprecated] };
|
|
diagnostics.codeDescriptionSupport = true;
|
|
diagnostics.dataSupport = true;
|
|
const windowCapabilities = (0, features_1.ensure)(result, 'window');
|
|
const showMessage = (0, features_1.ensure)(windowCapabilities, 'showMessage');
|
|
showMessage.messageActionItem = { additionalPropertiesSupport: true };
|
|
const showDocument = (0, features_1.ensure)(windowCapabilities, 'showDocument');
|
|
showDocument.support = true;
|
|
const generalCapabilities = (0, features_1.ensure)(result, 'general');
|
|
generalCapabilities.staleRequestSupport = {
|
|
cancel: true,
|
|
retryOnContentModified: Array.from(BaseLanguageClient.RequestsToCancelOnContentModified)
|
|
};
|
|
generalCapabilities.regularExpressions = { engine: 'ECMAScript', version: 'ES2020' };
|
|
generalCapabilities.markdown = {
|
|
parser: 'marked',
|
|
version: '1.1.0',
|
|
};
|
|
generalCapabilities.positionEncodings = ['utf-16'];
|
|
if (this._clientOptions.markdown.supportHtml) {
|
|
generalCapabilities.markdown.allowedTags = ['ul', 'li', 'p', 'code', 'blockquote', 'ol', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'em', 'pre', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'div', 'del', 'a', 'strong', 'br', 'img', 'span'];
|
|
}
|
|
for (let feature of this._features) {
|
|
feature.fillClientCapabilities(result);
|
|
}
|
|
return result;
|
|
}
|
|
initializeFeatures(_connection) {
|
|
const documentSelector = this._clientOptions.documentSelector;
|
|
for (const feature of this._features) {
|
|
if (Is.func(feature.preInitialize)) {
|
|
feature.preInitialize(this._capabilities, documentSelector);
|
|
}
|
|
}
|
|
for (const feature of this._features) {
|
|
feature.initialize(this._capabilities, documentSelector);
|
|
}
|
|
}
|
|
async handleRegistrationRequest(params) {
|
|
const middleware = this.clientOptions.middleware?.handleRegisterCapability;
|
|
if (middleware) {
|
|
return middleware(params, nextParams => this.doRegisterCapability(nextParams));
|
|
}
|
|
else {
|
|
return this.doRegisterCapability(params);
|
|
}
|
|
}
|
|
async doRegisterCapability(params) {
|
|
// We will not receive a registration call before a client is running
|
|
// from a server. However if we stop or shutdown we might which might
|
|
// try to restart the server. So ignore registrations if we are not running
|
|
if (!this.isRunning()) {
|
|
for (const registration of params.registrations) {
|
|
this._ignoredRegistrations.add(registration.id);
|
|
}
|
|
return;
|
|
}
|
|
for (const registration of params.registrations) {
|
|
const feature = this._dynamicFeatures.get(registration.method);
|
|
if (feature === undefined) {
|
|
return Promise.reject(new Error(`No feature implementation for ${registration.method} found. Registration failed.`));
|
|
}
|
|
const options = registration.registerOptions ?? {};
|
|
options.documentSelector = options.documentSelector ?? this._clientOptions.documentSelector;
|
|
const data = {
|
|
id: registration.id,
|
|
registerOptions: options
|
|
};
|
|
try {
|
|
feature.register(data);
|
|
}
|
|
catch (err) {
|
|
return Promise.reject(err);
|
|
}
|
|
}
|
|
}
|
|
async handleUnregistrationRequest(params) {
|
|
const middleware = this.clientOptions.middleware?.handleUnregisterCapability;
|
|
if (middleware) {
|
|
return middleware(params, nextParams => this.doUnregisterCapability(nextParams));
|
|
}
|
|
else {
|
|
return this.doUnregisterCapability(params);
|
|
}
|
|
}
|
|
async doUnregisterCapability(params) {
|
|
for (const unregistration of params.unregisterations) {
|
|
if (this._ignoredRegistrations.has(unregistration.id)) {
|
|
continue;
|
|
}
|
|
const feature = this._dynamicFeatures.get(unregistration.method);
|
|
if (!feature) {
|
|
return Promise.reject(new Error(`No feature implementation for ${unregistration.method} found. Unregistration failed.`));
|
|
}
|
|
feature.unregister(unregistration.id);
|
|
}
|
|
}
|
|
async handleApplyWorkspaceEdit(params) {
|
|
const workspaceEdit = params.edit;
|
|
// Make sure we convert workspace edits one after the other. Otherwise
|
|
// we might execute a workspace edit received first after we received another
|
|
// one since the conversion might race.
|
|
const converted = await this.workspaceEditLock.lock(() => {
|
|
return this._p2c.asWorkspaceEdit(workspaceEdit);
|
|
});
|
|
// This is some sort of workaround since the version check should be done by VS Code in the Workspace.applyEdit.
|
|
// However doing it here adds some safety since the server can lag more behind then an extension.
|
|
const openTextDocuments = new Map();
|
|
vscode_1.workspace.textDocuments.forEach((document) => openTextDocuments.set(document.uri.toString(), document));
|
|
let versionMismatch = false;
|
|
if (workspaceEdit.documentChanges) {
|
|
for (const change of workspaceEdit.documentChanges) {
|
|
if (vscode_languageserver_protocol_1.TextDocumentEdit.is(change) && change.textDocument.version && change.textDocument.version >= 0) {
|
|
const changeUri = this._p2c.asUri(change.textDocument.uri).toString();
|
|
const textDocument = openTextDocuments.get(changeUri);
|
|
if (textDocument && textDocument.version !== change.textDocument.version) {
|
|
versionMismatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (versionMismatch) {
|
|
return Promise.resolve({ applied: false });
|
|
}
|
|
return Is.asPromise(vscode_1.workspace.applyEdit(converted).then((value) => { return { applied: value }; }));
|
|
}
|
|
handleFailedRequest(type, token, error, defaultValue, showNotification = true) {
|
|
// If we get a request cancel or a content modified don't log anything.
|
|
if (error instanceof vscode_languageserver_protocol_1.ResponseError) {
|
|
// The connection got disposed while we were waiting for a response.
|
|
// Simply return the default value. Is the best we can do.
|
|
if (error.code === vscode_languageserver_protocol_1.ErrorCodes.PendingResponseRejected || error.code === vscode_languageserver_protocol_1.ErrorCodes.ConnectionInactive) {
|
|
return defaultValue;
|
|
}
|
|
if (error.code === vscode_languageserver_protocol_1.LSPErrorCodes.RequestCancelled || error.code === vscode_languageserver_protocol_1.LSPErrorCodes.ServerCancelled) {
|
|
if (token !== undefined && token.isCancellationRequested) {
|
|
return defaultValue;
|
|
}
|
|
else {
|
|
if (error.data !== undefined) {
|
|
throw new features_1.LSPCancellationError(error.data);
|
|
}
|
|
else {
|
|
throw new vscode_1.CancellationError();
|
|
}
|
|
}
|
|
}
|
|
else if (error.code === vscode_languageserver_protocol_1.LSPErrorCodes.ContentModified) {
|
|
if (BaseLanguageClient.RequestsToCancelOnContentModified.has(type.method) || BaseLanguageClient.CancellableResolveCalls.has(type.method)) {
|
|
throw new vscode_1.CancellationError();
|
|
}
|
|
else {
|
|
return defaultValue;
|
|
}
|
|
}
|
|
}
|
|
this.error(`Request ${type.method} failed.`, error, showNotification);
|
|
throw error;
|
|
}
|
|
}
|
|
exports.BaseLanguageClient = BaseLanguageClient;
|
|
BaseLanguageClient.RequestsToCancelOnContentModified = new Set([
|
|
vscode_languageserver_protocol_1.SemanticTokensRequest.method,
|
|
vscode_languageserver_protocol_1.SemanticTokensRangeRequest.method,
|
|
vscode_languageserver_protocol_1.SemanticTokensDeltaRequest.method
|
|
]);
|
|
BaseLanguageClient.CancellableResolveCalls = new Set([
|
|
vscode_languageserver_protocol_1.CompletionResolveRequest.method,
|
|
vscode_languageserver_protocol_1.CodeLensResolveRequest.method,
|
|
vscode_languageserver_protocol_1.CodeActionResolveRequest.method,
|
|
vscode_languageserver_protocol_1.InlayHintResolveRequest.method,
|
|
vscode_languageserver_protocol_1.DocumentLinkResolveRequest.method,
|
|
vscode_languageserver_protocol_1.WorkspaceSymbolResolveRequest.method
|
|
]);
|
|
class ConsoleLogger {
|
|
error(message) {
|
|
(0, vscode_languageserver_protocol_1.RAL)().console.error(message);
|
|
}
|
|
warn(message) {
|
|
(0, vscode_languageserver_protocol_1.RAL)().console.warn(message);
|
|
}
|
|
info(message) {
|
|
(0, vscode_languageserver_protocol_1.RAL)().console.info(message);
|
|
}
|
|
log(message) {
|
|
(0, vscode_languageserver_protocol_1.RAL)().console.log(message);
|
|
}
|
|
}
|
|
function createConnection(input, output, errorHandler, closeHandler, options) {
|
|
const logger = new ConsoleLogger();
|
|
const connection = (0, vscode_languageserver_protocol_1.createProtocolConnection)(input, output, logger, options);
|
|
connection.onError((data) => { errorHandler(data[0], data[1], data[2]); });
|
|
connection.onClose(closeHandler);
|
|
const result = {
|
|
listen: () => connection.listen(),
|
|
sendRequest: connection.sendRequest,
|
|
onRequest: connection.onRequest,
|
|
hasPendingResponse: connection.hasPendingResponse,
|
|
sendNotification: connection.sendNotification,
|
|
onNotification: connection.onNotification,
|
|
onProgress: connection.onProgress,
|
|
sendProgress: connection.sendProgress,
|
|
trace: (value, tracer, sendNotificationOrTraceOptions) => {
|
|
const defaultTraceOptions = {
|
|
sendNotification: false,
|
|
traceFormat: vscode_languageserver_protocol_1.TraceFormat.Text
|
|
};
|
|
if (sendNotificationOrTraceOptions === undefined) {
|
|
return connection.trace(value, tracer, defaultTraceOptions);
|
|
}
|
|
else if (Is.boolean(sendNotificationOrTraceOptions)) {
|
|
return connection.trace(value, tracer, sendNotificationOrTraceOptions);
|
|
}
|
|
else {
|
|
return connection.trace(value, tracer, sendNotificationOrTraceOptions);
|
|
}
|
|
},
|
|
initialize: (params) => {
|
|
// This needs to return and MUST not be await to avoid any async
|
|
// scheduling. Otherwise messages might overtake each other.
|
|
return connection.sendRequest(vscode_languageserver_protocol_1.InitializeRequest.type, params);
|
|
},
|
|
shutdown: () => {
|
|
// This needs to return and MUST not be await to avoid any async
|
|
// scheduling. Otherwise messages might overtake each other.
|
|
return connection.sendRequest(vscode_languageserver_protocol_1.ShutdownRequest.type, undefined);
|
|
},
|
|
exit: () => {
|
|
// This needs to return and MUST not be await to avoid any async
|
|
// scheduling. Otherwise messages might overtake each other.
|
|
return connection.sendNotification(vscode_languageserver_protocol_1.ExitNotification.type);
|
|
},
|
|
end: () => connection.end(),
|
|
dispose: () => connection.dispose()
|
|
};
|
|
return result;
|
|
}
|
|
// Exporting proposed protocol.
|
|
var ProposedFeatures;
|
|
(function (ProposedFeatures) {
|
|
function createAll(_client) {
|
|
let result = [
|
|
new inlineCompletion_1.InlineCompletionItemFeature(_client)
|
|
];
|
|
return result;
|
|
}
|
|
ProposedFeatures.createAll = createAll;
|
|
})(ProposedFeatures || (exports.ProposedFeatures = ProposedFeatures = {}));
|