UNPKG

@jupyterlab/lsp

Version:
240 lines 7.83 kB
/* * Copyright (c) Jupyter Development Team. * Distributed under the terms of the Modified BSD License. */ // Disclaimer/acknowledgement: Fragments are based on https://github.com/wylieconlon/lsp-editor-adapter, // which is copyright of wylieconlon and contributors and ISC licenced. // ISC licence is, quote, "functionally equivalent to the simplified BSD and MIT licenses, // but without language deemed unnecessary following the Berne Convention." (Wikipedia). // Introduced modifications are BSD licenced, copyright JupyterLab development team. import { ConsoleLogger, listen } from 'vscode-ws-jsonrpc'; import { Signal } from '@lumino/signaling'; import { registerServerCapability, unregisterServerCapability } from './server-capability-registration'; export class LspWsConnection { constructor(options) { /** * Map to track opened virtual documents.. */ this.openedUris = new Map(); /** * The connection is connected? */ this._isConnected = false; /** * The connection is initialized? */ this._isInitialized = false; /** * Array of LSP callback disposables, it is used to * clear the callbacks when the connection is disposed. */ this._disposables = []; this._disposed = new Signal(this); this._isDisposed = false; this._rootUri = options.rootUri; } /** * Is the language server is connected? */ get isConnected() { return this._isConnected; } /** * Is the language server is initialized? */ get isInitialized() { return this._isInitialized; } /** * Is the language server is connected and initialized? */ get isReady() { return this._isConnected && this._isInitialized; } /** * A signal emitted when the connection is disposed. */ get disposed() { return this._disposed; } /** * Check if the connection is disposed */ get isDisposed() { return this._isDisposed; } /** * Initialize a connection over a web socket that speaks the LSP protocol */ connect(socket) { this.socket = socket; listen({ webSocket: this.socket, logger: new ConsoleLogger(), onConnection: (connection) => { connection.listen(); this._isConnected = true; this.connection = connection; this.sendInitialize(); const registerCapabilityDisposable = this.connection.onRequest('client/registerCapability', (params) => { params.registrations.forEach((capabilityRegistration) => { try { this.serverCapabilities = registerServerCapability(this.serverCapabilities, capabilityRegistration); } catch (err) { console.error(err); } }); }); this._disposables.push(registerCapabilityDisposable); const unregisterCapabilityDisposable = this.connection.onRequest('client/unregisterCapability', (params) => { params.unregisterations.forEach((capabilityUnregistration) => { this.serverCapabilities = unregisterServerCapability(this.serverCapabilities, capabilityUnregistration); }); }); this._disposables.push(unregisterCapabilityDisposable); const disposable = this.connection.onClose(() => { this._isConnected = false; }); this._disposables.push(disposable); } }); } /** * Close the connection */ close() { if (this.connection) { this.connection.dispose(); } this.openedUris.clear(); this.socket.close(); } /** * The initialize request telling the server which options the client supports */ sendInitialize() { if (!this._isConnected) { return; } this.openedUris.clear(); const message = this.initializeParams(); this.connection .sendRequest('initialize', message) .then(params => { this.onServerInitialized(params); }, e => { console.warn('LSP websocket connection initialization failure', e); }); } /** * Inform the server that the document was opened */ sendOpen(documentInfo) { const textDocumentMessage = { textDocument: { uri: documentInfo.uri, languageId: documentInfo.languageId, text: documentInfo.text, version: documentInfo.version } }; this.connection .sendNotification('textDocument/didOpen', textDocumentMessage) .catch(console.error); this.openedUris.set(documentInfo.uri, true); this.sendChange(documentInfo); } /** * Sends the full text of the document to the server */ sendChange(documentInfo) { if (!this.isReady) { return; } if (!this.openedUris.get(documentInfo.uri)) { this.sendOpen(documentInfo); return; } const textDocumentChange = { textDocument: { uri: documentInfo.uri, version: documentInfo.version }, contentChanges: [{ text: documentInfo.text }] }; this.connection .sendNotification('textDocument/didChange', textDocumentChange) .catch(console.error); documentInfo.version++; } /** * Send save notification to the server. */ sendSaved(documentInfo) { if (!this.isReady) { return; } const textDocumentChange = { textDocument: { uri: documentInfo.uri, version: documentInfo.version }, text: documentInfo.text }; this.connection .sendNotification('textDocument/didSave', textDocumentChange) .catch(console.error); } /** * Send configuration change to the server. */ sendConfigurationChange(settings) { if (!this.isReady) { return; } this.connection .sendNotification('workspace/didChangeConfiguration', settings) .catch(console.error); } /** * Dispose the connection. */ dispose() { if (this._isDisposed) { return; } this._isDisposed = true; this._disposables.forEach(disposable => { disposable.dispose(); }); this._disposed.emit(); Signal.clearData(this); } /** * Callback called when the server is initialized. */ onServerInitialized(params) { this._isInitialized = true; this.serverCapabilities = params.capabilities; this.connection.sendNotification('initialized', {}).catch(console.error); this.connection .sendNotification('workspace/didChangeConfiguration', { settings: {} }) .catch(console.error); } /** * Initialization parameters to be sent to the language server. * Subclasses should override this when adding more features. */ initializeParams() { return { capabilities: {}, processId: null, rootUri: this._rootUri, workspaceFolders: null }; } } //# sourceMappingURL=ws-connection.js.map