diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a3129b2bc79d..a4ec6233eff31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ [1.22.0 Milestone](https://github.com/eclipse-theia/theia/milestone/30) +- [plugin-ext] add --fastStartup flag to wait for frontend start to deploy the plugins. [#](https://github.com/eclipse-theia/theia/pull/) - Contributed on behalf of STMicroelectronics + [Breaking Changes:](#breaking_changes_1.22.0) - [core] Removed `MarkdownRenderer` class [#10589](https://github.com/eclipse-theia/theia/pull/10589) - [core] `ContextKeyService` is now an interface. Extenders should extend `ContextKeyServiceDummyImpl` [#10546](https://github.com/eclipse-theia/theia/pull/10546) diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index fb2a485fb0d56..b42dcbd7a62af 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -30,7 +30,8 @@ import { MessageClient, InMemoryResources, messageServicePath, - InMemoryTextResourceResolver + InMemoryTextResourceResolver, + ClientConnectionNotifier } from '../common'; import { KeybindingRegistry, KeybindingContext, KeybindingContribution } from './keybinding'; import { FrontendApplication, FrontendApplicationContribution, DefaultFrontendApplicationContribution } from './frontend-application'; @@ -390,4 +391,8 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo child.bind(Coordinate).toConstantValue(position); return child.get(BreadcrumbPopupContainer); }); + bind(ClientConnectionNotifier).toDynamicValue(ctx => { + const connection = ctx.container.get(WebSocketConnectionProvider); + return connection.createProxy('clientConnected'); + }).inSingletonScope(); }); diff --git a/packages/core/src/browser/frontend-application.ts b/packages/core/src/browser/frontend-application.ts index 9d2e4ad62f6cc..d0c0fee855d39 100644 --- a/packages/core/src/browser/frontend-application.ts +++ b/packages/core/src/browser/frontend-application.ts @@ -15,7 +15,7 @@ ********************************************************************************/ import { inject, injectable, named } from 'inversify'; -import { ContributionProvider, CommandRegistry, MenuModelRegistry, isOSX } from '../common'; +import { ContributionProvider, CommandRegistry, MenuModelRegistry, isOSX, ClientConnectionNotifier } from '../common'; import { MaybePromise } from '../common/types'; import { KeybindingRegistry } from './keybinding'; import { Widget } from './widgets'; @@ -129,7 +129,8 @@ export class FrontendApplication { @inject(ContributionProvider) @named(FrontendApplicationContribution) protected readonly contributions: ContributionProvider, @inject(ApplicationShell) protected readonly _shell: ApplicationShell, - @inject(FrontendApplicationStateService) protected readonly stateService: FrontendApplicationStateService + @inject(FrontendApplicationStateService) protected readonly stateService: FrontendApplicationStateService, + @inject(ClientConnectionNotifier) protected readonly connectionNotifier: ClientConnectionNotifier ) { } get shell(): ApplicationShell { @@ -258,9 +259,10 @@ export class FrontendApplication { const startupElem = this.getStartupIndicator(host); if (startupElem) { return new Promise(resolve => { - window.requestAnimationFrame(() => { + window.requestAnimationFrame(async () => { startupElem.classList.add('theia-hidden'); console.log(`Finished loading frontend application after ${(performance.now() / 1000).toFixed(3)} seconds`); + await this.connectionNotifier.clientConnected(); const preloadStyle = window.getComputedStyle(startupElem); const transitionDuration = parseCssTime(preloadStyle.transitionDuration, 0); window.setTimeout(() => { diff --git a/packages/core/src/common/connection-notifier.ts b/packages/core/src/common/connection-notifier.ts new file mode 100644 index 0000000000000..ce6af4e8eb701 --- /dev/null +++ b/packages/core/src/common/connection-notifier.ts @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (C) 2022 STMicroelectronics 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 + ********************************************************************************/ +import { injectable } from 'inversify'; +import * as events from 'events'; + +export const ConnectionTimeout = Symbol('ConnectionTimeout'); + +@injectable() +export class ClientConnectionNotifier { + + static readonly CLIENT_CONNECTED = 'clientConnected'; + + readonly connectionEvent = new events.EventEmitter(); + + currentlyConnected = false; + + async clientConnected(): Promise { + if (!this.currentlyConnected) { + this.currentlyConnected = true; + this.connectionEvent.emit(ClientConnectionNotifier.CLIENT_CONNECTED); + } + } + +} diff --git a/packages/core/src/common/index.ts b/packages/core/src/common/index.ts index 3d041d03e68d9..2341edd7ca775 100644 --- a/packages/core/src/common/index.ts +++ b/packages/core/src/common/index.ts @@ -40,6 +40,7 @@ export * from './lsp-types'; export * from './contribution-filter'; export * from './nls'; export * from './numbers'; +export * from './connection-notifier'; import { environment } from '@theia/application-package/lib/environment'; export { environment }; diff --git a/packages/core/src/node/backend-application-module.ts b/packages/core/src/node/backend-application-module.ts index 555d6974d4c25..f12188490ccbb 100644 --- a/packages/core/src/node/backend-application-module.ts +++ b/packages/core/src/node/backend-application-module.ts @@ -18,7 +18,7 @@ import { ContainerModule, decorate, injectable } from 'inversify'; import { ApplicationPackage } from '@theia/application-package'; import { bindContributionProvider, MessageService, MessageClient, ConnectionHandler, JsonRpcConnectionHandler, - CommandService, commandServicePath, messageServicePath + CommandService, commandServicePath, messageServicePath, ClientConnectionNotifier } from '../common'; import { BackendApplication, BackendApplicationContribution, BackendApplicationCliContribution, BackendApplicationServer } from './backend-application'; import { CliManager, CliContribution } from './cli'; @@ -109,4 +109,8 @@ export const backendApplicationModule = new ContainerModule(bind => { bind(EnvironmentUtils).toSelf().inSingletonScope(); bind(ProcessUtils).toSelf().inSingletonScope(); + bind(ConnectionHandler).toDynamicValue(({container}) => + new JsonRpcConnectionHandler('clientConnected', () => container.get(ClientConnectionNotifier)) + ).inSingletonScope(); + bind(ClientConnectionNotifier).toSelf().inSingletonScope(); }); diff --git a/packages/core/src/node/backend-application.ts b/packages/core/src/node/backend-application.ts index 8e50774a19181..83e8a0d8f638c 100644 --- a/packages/core/src/node/backend-application.ts +++ b/packages/core/src/node/backend-application.ts @@ -37,6 +37,7 @@ const TIMER_WARNING_THRESHOLD = 50; const DEFAULT_PORT = environment.electron.is() ? 0 : 3000; const DEFAULT_HOST = 'localhost'; const DEFAULT_SSL = false; +const DEFAULT_FAST_STARTUP = false; export const BackendApplicationServer = Symbol('BackendApplicationServer'); /** @@ -107,6 +108,7 @@ export class BackendApplicationCliContribution implements CliContribution { cert: string | undefined; certkey: string | undefined; projectPath: string; + fastStartup: boolean; configure(conf: yargs.Argv): void { conf.option('port', { alias: 'p', description: 'The port the backend server listens on.', type: 'number', default: DEFAULT_PORT }); @@ -115,6 +117,7 @@ export class BackendApplicationCliContribution implements CliContribution { conf.option('cert', { description: 'Path to SSL certificate.', type: 'string' }); conf.option('certkey', { description: 'Path to SSL certificate key.', type: 'string' }); conf.option(APP_PROJECT_PATH, { description: 'Sets the application project directory', default: this.appProjectPath() }); + conf.option('fastStartup', {description: 'delay plugin deployment to decrease startup time', type: 'boolean', default: DEFAULT_FAST_STARTUP}); } setArguments(args: yargs.Arguments): void { @@ -124,6 +127,7 @@ export class BackendApplicationCliContribution implements CliContribution { this.cert = args.cert as string; this.certkey = args.certkey as string; this.projectPath = args[APP_PROJECT_PATH] as string; + this.fastStartup = args['fastStartup'] as boolean; } protected appProjectPath(): string { diff --git a/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts b/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts index 1a0adc3f22377..6a53ff74df2b7 100644 --- a/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts +++ b/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts @@ -14,21 +14,30 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { BackendApplicationContribution } from '@theia/core/lib/node'; +import { BackendApplicationCliContribution, BackendApplicationContribution } from '@theia/core/lib/node'; +import { ClientConnectionNotifier } from '@theia/core/lib/common'; import { injectable, inject } from '@theia/core/shared/inversify'; import { PluginDeployer } from '../../common/plugin-protocol'; -import { ILogger } from '@theia/core'; @injectable() export class PluginDeployerContribution implements BackendApplicationContribution { - @inject(ILogger) - protected readonly logger: ILogger; - @inject(PluginDeployer) protected pluginDeployer: PluginDeployer; + @inject(ClientConnectionNotifier) + protected readonly connectionNotifier: ClientConnectionNotifier; + + @inject(BackendApplicationCliContribution) + protected readonly cliParams: BackendApplicationCliContribution; + initialize(): void { - this.pluginDeployer.start(); + if (this.cliParams.fastStartup) { + this.connectionNotifier.connectionEvent.on(ClientConnectionNotifier.CLIENT_CONNECTED, async () => { + this.pluginDeployer.start(); + }); + } else { + this.pluginDeployer.start(); + } } }