UNPKG

@jupyterlab/lsp

Version:
248 lines 7.95 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { PageConfig, URLExt } from '@jupyterlab/coreutils'; import { ServerConnection } from '@jupyterlab/services'; import { Signal } from '@lumino/signaling'; import { ILanguageServerManager } from './tokens'; import { PromiseDelegate } from '@lumino/coreutils'; export class LanguageServerManager { constructor(options) { /** * map of language server sessions. */ this._sessions = new Map(); /** * Map of language server specs. */ this._specs = new Map(); /** * Set of emitted warning message, message in this set will not be warned again. */ this._warningsEmitted = new Set(); /** * A promise resolved when this server manager is ready. */ this._ready = new PromiseDelegate(); /** * Signal emitted when a language server session is changed */ this._sessionsChanged = new Signal(this); this._isDisposed = false; /** * Check if the manager is enabled or disabled */ this._enabled = true; this._settings = options.settings || ServerConnection.makeSettings(); this._baseUrl = options.baseUrl || PageConfig.getBaseUrl(); this._retries = options.retries || 2; this._retriesInterval = options.retriesInterval || 10000; this._statusCode = -1; this._configuration = {}; this.fetchSessions().catch(e => console.log(e)); } /** * Check if the manager is enabled or disabled */ get isEnabled() { return this._enabled; } /** * Check if the manager is disposed. */ get isDisposed() { return this._isDisposed; } /** * Get server connection settings. */ get settings() { return this._settings; } /** * Get the language server specs. */ get specs() { return this._specs; } /** * Get the status end point. */ get statusUrl() { return URLExt.join(this._baseUrl, ILanguageServerManager.URL_NS, 'status'); } /** * Signal emitted when a language server session is changed */ get sessionsChanged() { return this._sessionsChanged; } /** * Get the map of language server sessions. */ get sessions() { return this._sessions; } /** * A promise resolved when this server manager is ready. */ get ready() { return this._ready.promise; } /** * Get the status code of server's responses. */ get statusCode() { return this._statusCode; } /** * Enable the language server services */ async enable() { this._enabled = true; await this.fetchSessions(); } /** * Disable the language server services */ disable() { this._enabled = false; this._sessions = new Map(); this._sessionsChanged.emit(void 0); } /** * Dispose the manager. */ dispose() { if (this._isDisposed) { return; } this._isDisposed = true; Signal.clearData(this); } /** * Update the language server configuration. */ setConfiguration(configuration) { this._configuration = configuration; } /** * Get matching language server for input language option. */ getMatchingServers(options) { if (!options.language) { console.error('Cannot match server by language: language not available; ensure that kernel and specs provide language and MIME type'); return []; } const matchingSessionsKeys = []; for (const [key, session] of this._sessions.entries()) { if (this.isMatchingSpec(options, session.spec)) { matchingSessionsKeys.push(key); } } return matchingSessionsKeys.sort(this.compareRanks.bind(this)); } /** * Get matching language server spec for input language option. */ getMatchingSpecs(options) { const result = new Map(); for (const [key, specification] of this._specs.entries()) { if (this.isMatchingSpec(options, specification)) { result.set(key, specification); } } return result; } /** * Fetch the server session list from the status endpoint. The server * manager is ready once this method finishes. */ async fetchSessions() { if (!this._enabled) { return; } let response = await ServerConnection.makeRequest(this.statusUrl, { method: 'GET' }, this._settings); this._statusCode = response.status; if (!response.ok) { if (this._retries > 0) { this._retries -= 1; setTimeout(this.fetchSessions.bind(this), this._retriesInterval); } else { this._ready.resolve(undefined); console.log('Missing jupyter_lsp server extension, skipping.'); } return; } let sessions; try { const data = await response.json(); sessions = data.sessions; try { this.version = data.version; this._specs = new Map(Object.entries(data.specs)); } catch (err) { console.warn(err); } } catch (err) { console.warn(err); this._ready.resolve(undefined); return; } for (let key of Object.keys(sessions)) { let id = key; if (this._sessions.has(id)) { Object.assign(this._sessions.get(id) || {}, sessions[key]); } else { this._sessions.set(id, sessions[key]); } } const oldKeys = this._sessions.keys(); for (const oldKey in oldKeys) { if (!sessions[oldKey]) { let oldId = oldKey; this._sessions.delete(oldId); } } this._sessionsChanged.emit(void 0); this._ready.resolve(undefined); } /** * Check if input language option maths the language server spec. */ isMatchingSpec(options, spec) { // most things speak language // if language is not known, it is guessed based on MIME type earlier // so some language should be available by now (which can be not so obvious, e.g. "plain" for txt documents) const lowerCaseLanguage = options.language.toLocaleLowerCase(); return spec.languages.some((language) => language.toLocaleLowerCase() == lowerCaseLanguage); } /** * Helper function to warn a message only once. */ warnOnce(arg) { if (!this._warningsEmitted.has(arg)) { this._warningsEmitted.add(arg); console.warn(arg); } } /** * Compare the rank of two servers with the same language. */ compareRanks(a, b) { var _a, _b, _c, _d; const DEFAULT_RANK = 50; const aRank = (_b = (_a = this._configuration[a]) === null || _a === void 0 ? void 0 : _a.rank) !== null && _b !== void 0 ? _b : DEFAULT_RANK; const bRank = (_d = (_c = this._configuration[b]) === null || _c === void 0 ? void 0 : _c.rank) !== null && _d !== void 0 ? _d : DEFAULT_RANK; if (aRank == bRank) { this.warnOnce(`Two matching servers: ${a} and ${b} have the same rank; choose which one to use by changing the rank in Advanced Settings Editor`); return a.localeCompare(b); } // higher rank = higher in the list (descending order) return bRank - aRank; } } //# sourceMappingURL=manager.js.map