"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.WorkspaceFeature = exports.TextDocumentLanguageFeature = exports.TextDocumentEventFeature = exports.DynamicDocumentFeature = exports.DynamicFeature = exports.StaticFeature = exports.ensure = exports.LSPCancellationError = void 0; const vscode_1 = require("vscode"); const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol"); const Is = require("./utils/is"); const UUID = require("./utils/uuid"); class LSPCancellationError extends vscode_1.CancellationError { constructor(data) { super(); this.data = data; } } exports.LSPCancellationError = LSPCancellationError; function ensure(target, key) { if (target[key] === undefined) { target[key] = {}; } return target[key]; } exports.ensure = ensure; var StaticFeature; (function (StaticFeature) { function is(value) { const candidate = value; return candidate !== undefined && candidate !== null && Is.func(candidate.fillClientCapabilities) && Is.func(candidate.initialize) && Is.func(candidate.getState) && Is.func(candidate.clear) && (candidate.fillInitializeParams === undefined || Is.func(candidate.fillInitializeParams)); } StaticFeature.is = is; })(StaticFeature || (exports.StaticFeature = StaticFeature = {})); var DynamicFeature; (function (DynamicFeature) { function is(value) { const candidate = value; return candidate !== undefined && candidate !== null && Is.func(candidate.fillClientCapabilities) && Is.func(candidate.initialize) && Is.func(candidate.getState) && Is.func(candidate.clear) && (candidate.fillInitializeParams === undefined || Is.func(candidate.fillInitializeParams)) && Is.func(candidate.register) && Is.func(candidate.unregister) && candidate.registrationType !== undefined; } DynamicFeature.is = is; })(DynamicFeature || (exports.DynamicFeature = DynamicFeature = {})); /** * An abstract dynamic feature implementation that operates on documents (e.g. text * documents or notebooks). */ class DynamicDocumentFeature { constructor(client) { this._client = client; } /** * Returns the state the feature is in. */ getState() { const selectors = this.getDocumentSelectors(); let count = 0; for (const selector of selectors) { count++; for (const document of vscode_1.workspace.textDocuments) { if (vscode_1.languages.match(selector, document) > 0) { return { kind: 'document', id: this.registrationType.method, registrations: true, matches: true }; } } } const registrations = count > 0; return { kind: 'document', id: this.registrationType.method, registrations, matches: false }; } } exports.DynamicDocumentFeature = DynamicDocumentFeature; /** * An abstract base class to implement features that react to events * emitted from text documents. */ class TextDocumentEventFeature extends DynamicDocumentFeature { static textDocumentFilter(selectors, textDocument) { for (const selector of selectors) { if (vscode_1.languages.match(selector, textDocument) > 0) { return true; } } return false; } constructor(client, event, type, middleware, createParams, textDocument, selectorFilter) { super(client); this._event = event; this._type = type; this._middleware = middleware; this._createParams = createParams; this._textDocument = textDocument; this._selectorFilter = selectorFilter; this._selectors = new Map(); this._onNotificationSent = new vscode_1.EventEmitter(); } getStateInfo() { return [this._selectors.values(), false]; } getDocumentSelectors() { return this._selectors.values(); } register(data) { if (!data.registerOptions.documentSelector) { return; } if (!this._listener) { this._listener = this._event((data) => { this.callback(data).catch((error) => { this._client.error(`Sending document notification ${this._type.method} failed.`, error); }); }); } this._selectors.set(data.id, this._client.protocol2CodeConverter.asDocumentSelector(data.registerOptions.documentSelector)); } async callback(data) { const doSend = async (data) => { const params = this._createParams(data); await this._client.sendNotification(this._type, params); this.notificationSent(this.getTextDocument(data), this._type, params); }; if (this.matches(data)) { const middleware = this._middleware(); return middleware ? middleware(data, (data) => doSend(data)) : doSend(data); } } matches(data) { if (this._client.hasDedicatedTextSynchronizationFeature(this._textDocument(data))) { return false; } return !this._selectorFilter || this._selectorFilter(this._selectors.values(), data); } get onNotificationSent() { return this._onNotificationSent.event; } notificationSent(textDocument, type, params) { this._onNotificationSent.fire({ textDocument, type, params }); } unregister(id) { this._selectors.delete(id); if (this._selectors.size === 0 && this._listener) { this._listener.dispose(); this._listener = undefined; } } clear() { this._selectors.clear(); this._onNotificationSent.dispose(); if (this._listener) { this._listener.dispose(); this._listener = undefined; } } getProvider(document) { for (const selector of this._selectors.values()) { if (vscode_1.languages.match(selector, document) > 0) { return { send: (data) => { return this.callback(data); } }; } } return undefined; } } exports.TextDocumentEventFeature = TextDocumentEventFeature; /** * A abstract feature implementation that registers language providers * for text documents using a given document selector. */ class TextDocumentLanguageFeature extends DynamicDocumentFeature { constructor(client, registrationType) { super(client); this._registrationType = registrationType; this._registrations = new Map(); } *getDocumentSelectors() { for (const registration of this._registrations.values()) { const selector = registration.data.registerOptions.documentSelector; if (selector === null) { continue; } yield this._client.protocol2CodeConverter.asDocumentSelector(selector); } } get registrationType() { return this._registrationType; } register(data) { if (!data.registerOptions.documentSelector) { return; } let registration = this.registerLanguageProvider(data.registerOptions, data.id); this._registrations.set(data.id, { disposable: registration[0], data, provider: registration[1] }); } unregister(id) { let registration = this._registrations.get(id); if (registration !== undefined) { registration.disposable.dispose(); } } clear() { this._registrations.forEach((value) => { value.disposable.dispose(); }); this._registrations.clear(); } getRegistration(documentSelector, capability) { if (!capability) { return [undefined, undefined]; } else if (vscode_languageserver_protocol_1.TextDocumentRegistrationOptions.is(capability)) { const id = vscode_languageserver_protocol_1.StaticRegistrationOptions.hasId(capability) ? capability.id : UUID.generateUuid(); const selector = capability.documentSelector ?? documentSelector; if (selector) { return [id, Object.assign({}, capability, { documentSelector: selector })]; } } else if (Is.boolean(capability) && capability === true || vscode_languageserver_protocol_1.WorkDoneProgressOptions.is(capability)) { if (!documentSelector) { return [undefined, undefined]; } const options = (Is.boolean(capability) && capability === true ? { documentSelector } : Object.assign({}, capability, { documentSelector })); return [UUID.generateUuid(), options]; } return [undefined, undefined]; } getRegistrationOptions(documentSelector, capability) { if (!documentSelector || !capability) { return undefined; } return (Is.boolean(capability) && capability === true ? { documentSelector } : Object.assign({}, capability, { documentSelector })); } getProvider(textDocument) { for (const registration of this._registrations.values()) { let selector = registration.data.registerOptions.documentSelector; if (selector !== null && vscode_1.languages.match(this._client.protocol2CodeConverter.asDocumentSelector(selector), textDocument) > 0) { return registration.provider; } } return undefined; } getAllProviders() { const result = []; for (const item of this._registrations.values()) { result.push(item.provider); } return result; } } exports.TextDocumentLanguageFeature = TextDocumentLanguageFeature; class WorkspaceFeature { constructor(client, registrationType) { this._client = client; this._registrationType = registrationType; this._registrations = new Map(); } getState() { const registrations = this._registrations.size > 0; return { kind: 'workspace', id: this._registrationType.method, registrations }; } get registrationType() { return this._registrationType; } register(data) { const registration = this.registerLanguageProvider(data.registerOptions); this._registrations.set(data.id, { disposable: registration[0], provider: registration[1] }); } unregister(id) { let registration = this._registrations.get(id); if (registration !== undefined) { registration.disposable.dispose(); } } clear() { this._registrations.forEach((registration) => { registration.disposable.dispose(); }); this._registrations.clear(); } getProviders() { const result = []; for (const registration of this._registrations.values()) { result.push(registration.provider); } return result; } } exports.WorkspaceFeature = WorkspaceFeature;