Skip to content

Fixed LS stops working after OS sleep/wakeup cycle #1021

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import { OutputContribution } from '@theia/output/lib/browser/output-contributio
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { FileChangeType } from '@theia/filesystem/lib/browser';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
Expand All @@ -75,6 +74,7 @@ import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-wi
import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog';
import { IDEUpdater } from '../common/protocol/ide-updater';
import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution';
import { HostedPluginEvents } from './hosted-plugin-events';

const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
export const SKIP_IDE_VERSION = 'skipIDEVersion';
Expand Down Expand Up @@ -147,8 +147,8 @@ export class ArduinoFrontendContribution
@inject(ConfigService)
protected readonly configService: ConfigService;

@inject(HostedPluginSupport)
protected hostedPluginSupport: HostedPluginSupport;
@inject(HostedPluginEvents)
protected hostedPluginEvents: HostedPluginEvents;

@inject(ExecutableService)
protected executableService: ExecutableService;
Expand Down Expand Up @@ -317,6 +317,12 @@ export class ArduinoFrontendContribution
}
};
this.boardsServiceClientImpl.onBoardsConfigChanged(start);
this.hostedPluginEvents.onPluginsDidStart(() =>
start(this.boardsServiceClientImpl.boardsConfig)
);
this.hostedPluginEvents.onPluginsWillUnload(
() => (this.languageServerFqbn = undefined)
);
this.arduinoPreferences.onPreferenceChanged((event) => {
if (event.newValue !== event.oldValue) {
switch (event.preferenceName) {
Expand Down Expand Up @@ -371,7 +377,7 @@ export class ArduinoFrontendContribution
): Promise<void> {
const release = await this.languageServerStartMutex.acquire();
try {
await this.hostedPluginSupport.didStart;
await this.hostedPluginEvents.didStart;
const details = await this.boardsService.getBoardDetails({ fqbn });
if (!details) {
// Core is not installed for the selected board.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,9 @@ import {
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
import { EditorManager as TheiaEditorManager } from '@theia/editor/lib/browser/editor-manager';
import { EditorManager } from './theia/editor/editor-manager';
import { HostedPluginEvents } from './hosted-plugin-events';
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';

const ElementQueries = require('css-element-queries/src/ElementQueries');

Expand Down Expand Up @@ -805,4 +808,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
);
})
.inSingletonScope();

bind(HostedPluginSupport).toSelf().inSingletonScope();
rebind(TheiaHostedPluginSupport).toService(HostedPluginSupport);
bind(HostedPluginEvents).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(HostedPluginEvents);
});
74 changes: 74 additions & 0 deletions arduino-ide-extension/src/browser/hosted-plugin-events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { DisposableCollection, Emitter, Event } from '@theia/core';
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
import { inject, injectable } from '@theia/core/shared/inversify';
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';

/**
* Frontend contribution to watch VS Code extension start/stop events from Theia.
*
* In Theia, there are no events when a VS Code extension is loaded, started, unloaded, and stopped.
* Currently, it's possible to `@inject` the `HostedPluginSupport` service from Theia and `await`
* for the `didStart` promise to resolve. But if the OS goes to sleep, the VS Code extensions will
* be unloaded and loaded and started again when the OS awakes. Theia reloads the VS Code extensions
* after the OS awake event, but the `didStart` promise was already resolved, so IDE2 cannot restart the LS.
* This service is meant to work around the limitation of Theia and fire an event every time the VS Code extensions
* loaded and started.
*/
@injectable()
export class HostedPluginEvents implements FrontendApplicationContribution {
@inject(HostedPluginSupport)
private readonly hostedPluginSupport: HostedPluginSupport;

private firstStart = true;
private readonly onPluginsDidStartEmitter = new Emitter<void>();
private readonly onPluginsWillUnloadEmitter = new Emitter<void>();
private readonly toDispose = new DisposableCollection(
this.onPluginsDidStartEmitter,
this.onPluginsWillUnloadEmitter
);

onStart(): void {
this.hostedPluginSupport.onDidLoad(() => {
// Fire the first event, when `didStart` resolves.
if (!this.firstStart) {
console.debug('HostedPluginEvents', "Received 'onDidLoad' event.");
this.onPluginsDidStartEmitter.fire();
} else {
console.debug(
'HostedPluginEvents',
"Received 'onDidLoad' event before the first start. Skipping."
);
}
});
this.hostedPluginSupport.didStart.then(() => {
console.debug('HostedPluginEvents', "Hosted plugins 'didStart'.");
if (!this.firstStart) {
throw new Error(
'Unexpectedly received a `didStart` event after the first start.'
);
}
this.firstStart = false;
this.onPluginsDidStartEmitter.fire();
});
this.hostedPluginSupport.onDidCloseConnection(() => {
console.debug('HostedPluginEvents', "Received 'onDidCloseConnection'.");
this.onPluginsWillUnloadEmitter.fire();
});
}

onStop(): void {
this.toDispose.dispose();
}

get onPluginsDidStart(): Event<void> {
return this.onPluginsDidStartEmitter.event;
}

get onPluginsWillUnload(): Event<void> {
return this.onPluginsWillUnloadEmitter.event;
}

get didStart(): Promise<void> {
return this.hostedPluginSupport.didStart;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Emitter, Event, JsonRpcProxy } from '@theia/core';
import { injectable, interfaces } from '@theia/core/shared/inversify';
import { HostedPluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
@injectable()
export class HostedPluginSupport extends TheiaHostedPluginSupport {
private readonly onDidLoadEmitter = new Emitter<void>();
private readonly onDidCloseConnectionEmitter = new Emitter<void>();

override onStart(container: interfaces.Container): void {
super.onStart(container);
this.hostedPluginServer.onDidCloseConnection(() =>
this.onDidCloseConnectionEmitter.fire()
);
}

protected override async doLoad(): Promise<void> {
await super.doLoad();
this.onDidLoadEmitter.fire(); // Unlike Theia, IDE2 fires an event after loading the VS Code extensions.
}

get onDidLoad(): Event<void> {
return this.onDidLoadEmitter.event;
}

get onDidCloseConnection(): Event<void> {
return this.onDidCloseConnectionEmitter.event;
}

private get hostedPluginServer(): JsonRpcProxy<HostedPluginServer> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (this as any).server;
}
}