UNPKG

@eclipse-glsp/theia-integration

Version:

Glue code to integrate GLSP clients into Eclipse Theia

196 lines 8.76 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseGLSPClientContribution = exports.GLSPClientContribution = void 0; /******************************************************************************** * Copyright (c) 2019-2026 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the Eclipse * Public License v. 2.0 are satisfied: GNU General Public License, version 2 * with the GNU Classpath Exception which is available at * https://www.gnu.org/software/classpath/license.html. * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ const client_1 = require("@eclipse-glsp/client"); const core_1 = require("@theia/core"); const service_connection_provider_1 = require("@theia/core/lib/browser/messaging/service-connection-provider"); const promise_util_1 = require("@theia/core/lib/common/promise-util"); const inversify_1 = require("@theia/core/shared/inversify"); require("../../css/command-palette.css"); require("../../css/decoration.css"); require("../../css/diagram.css"); require("../../css/features.css"); require("../../css/theia-dialogs.css"); require("../../css/tool-palette.css"); const common_1 = require("../common"); const theia_jsonrpc_glsp_client_1 = require("./theia-jsonrpc-glsp-client"); exports.GLSPClientContribution = Symbol('GLSPClientContribution'); /** * Base implementation for {@link GLSPClientContribution}s. The default implementation setups a {@link GLSPClient} that * uses a Theia service connection to communicate with the corresponding `GLSPBackendContribution`. * Subclasses can override the {@link BaseGLSPClientContribution.getWebSocketConnectionOptions} method. If this method * provides websocket options the `GLSPClient` is not routed via service connection to the Theia backend, and instead directly * communicates with a `GLSPServer` via WebSocket. */ let BaseGLSPClientContribution = class BaseGLSPClientContribution { constructor() { this.glspClientDeferred = new promise_util_1.Deferred(); this.toDispose = new core_1.DisposableCollection(); this.glspClientStartupTimeout = 15000; } getWebSocketConnectionOptions() { return undefined; } get glspClient() { return this.glspClientDeferred.promise; } activate(app) { if (this.toDispose.disposed) { this.toDispose.push(new core_1.DisposableCollection(core_1.Disposable.create(() => { }))); // mark as not disposed if (this.waitForActivation) { return this.waitForActivation(app).then(() => this.doActivate()); } return this.doActivate(); } } deactivate(_app) { this.dispose(); } doActivate() { /* Let `doActivate` complete synchronous even though 'activateClient' is asynchronous. This way we don't block the startup of other contributions. We are using a deferred GLSPClient anyways that only resolves after client activation is completed */ this.activateClient(); } async activateClient() { const connection = await this.createConnection(); const client = await this.createGLSPClient(connection); connection.onDispose(() => { client.stop(); }); return this.start(client); } async createConnection() { const opts = await this.getWebSocketConnectionOptions(); if (opts) { return this.createWebSocketConnection(opts); } return this.createChannelConnection(); } createWebSocketConnection(opts) { const address = this.getWebsocketAddress(opts); const socket = new WebSocket(address); return (0, client_1.listen)(socket); } getWebsocketAddress(opts) { const address = typeof opts === 'string' ? opts : (0, common_1.getWebSocketAddress)(opts); if (!address) { throw new Error(`Could not derive server websocket address from options: ${JSON.stringify(opts, undefined, 2)}`); } if (!(0, common_1.isValidWebSocketAddress)(address)) { throw new Error(`The given websocket server address is not valid: ${address}`); } return address; } createChannelConnection() { return new Promise((resolve, reject) => { this.connectionProvider.listen(common_1.GLSPContribution.getPath(this), (path, channel) => { if (path === common_1.GLSPContribution.getPath(this)) { if (this.toDispose.disposed) { channel.close(); reject(new Error('GLSPClientContribution is already disposed')); } const connection = (0, common_1.createChannelConnection)(channel); this.toDispose.push(core_1.Disposable.create(() => this.disposeChannel(connection, channel))); resolve(connection); } }, true); }); } async start(glspClient) { try { await glspClient.start(); await this.initialize(glspClient); this.glspClientDeferred.resolve(glspClient); } catch (error) { this.glspClientDeferred.reject(error); } } /** Best-effort `shutdown` (only if the client started) before the channel is torn down. */ async disposeChannel(connection, channel) { try { if (this.glspClientDeferred.state === 'resolved') { await (await this.glspClient).shutdownServer(); } } catch { // ignore — server may already be unreachable } finally { connection.dispose(); if (core_1.Disposable.is(channel)) { channel.dispose(); } } } async initialize(languageClient) { try { const parameters = await this.createInitializeParameters(); return await languageClient.initializeServer(parameters); } catch (error) { const errorMsg = `Failed to initialize ${this.id} glsp server with: ${error}`; this.messageService.error(errorMsg); return Promise.reject(errorMsg); } } async createInitializeParameters() { const args = await this.createInitializeOptions(); return { applicationId: client_1.ApplicationIdProvider.get(), protocolVersion: client_1.GLSPClient.protocolVersion, args }; } createInitializeOptions() { return undefined; } async createGLSPClient(connectionProvider) { return new theia_jsonrpc_glsp_client_1.TheiaJsonrpcGLSPClient({ id: this.id, connectionProvider, messageService: this.messageService }); } dispose() { this.toDispose.dispose(); } }; exports.BaseGLSPClientContribution = BaseGLSPClientContribution; __decorate([ (0, inversify_1.inject)(core_1.MessageService), __metadata("design:type", core_1.MessageService) ], BaseGLSPClientContribution.prototype, "messageService", void 0); __decorate([ (0, inversify_1.inject)(service_connection_provider_1.RemoteConnectionProvider), __metadata("design:type", service_connection_provider_1.ServiceConnectionProvider) ], BaseGLSPClientContribution.prototype, "connectionProvider", void 0); exports.BaseGLSPClientContribution = BaseGLSPClientContribution = __decorate([ (0, inversify_1.injectable)() ], BaseGLSPClientContribution); //# sourceMappingURL=glsp-client-contribution.js.map