Skip to content

Commit d780961

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
Fixed LS stops working after OS sleep/wakeup cycle
Signed-off-by: Akos Kitta <[email protected]>
1 parent 5b486b1 commit d780961

File tree

4 files changed

+126
-4
lines changed

4 files changed

+126
-4
lines changed

Diff for: arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

+10-4
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ import { OutputContribution } from '@theia/output/lib/browser/output-contributio
5454
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
5555
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
5656
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
57-
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
5857
import { FileService } from '@theia/filesystem/lib/browser/file-service';
5958
import { FileChangeType } from '@theia/filesystem/lib/browser';
6059
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
@@ -75,6 +74,7 @@ import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-wi
7574
import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog';
7675
import { IDEUpdater } from '../common/protocol/ide-updater';
7776
import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution';
77+
import { HostedPluginEvents } from './hosted-plugin-events';
7878

7979
const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
8080
export const SKIP_IDE_VERSION = 'skipIDEVersion';
@@ -147,8 +147,8 @@ export class ArduinoFrontendContribution
147147
@inject(ConfigService)
148148
protected readonly configService: ConfigService;
149149

150-
@inject(HostedPluginSupport)
151-
protected hostedPluginSupport: HostedPluginSupport;
150+
@inject(HostedPluginEvents)
151+
protected hostedPluginEvents: HostedPluginEvents;
152152

153153
@inject(ExecutableService)
154154
protected executableService: ExecutableService;
@@ -317,6 +317,12 @@ export class ArduinoFrontendContribution
317317
}
318318
};
319319
this.boardsServiceClientImpl.onBoardsConfigChanged(start);
320+
this.hostedPluginEvents.onPluginsDidStart(() =>
321+
start(this.boardsServiceClientImpl.boardsConfig)
322+
);
323+
this.hostedPluginEvents.onPluginsWillUnload(
324+
() => (this.languageServerFqbn = undefined)
325+
);
320326
this.arduinoPreferences.onPreferenceChanged((event) => {
321327
if (event.newValue !== event.oldValue) {
322328
switch (event.preferenceName) {
@@ -371,7 +377,7 @@ export class ArduinoFrontendContribution
371377
): Promise<void> {
372378
const release = await this.languageServerStartMutex.acquire();
373379
try {
374-
await this.hostedPluginSupport.didStart;
380+
await this.hostedPluginEvents.didStart;
375381
const details = await this.boardsService.getBoardDetails({ fqbn });
376382
if (!details) {
377383
// Core is not installed for the selected board.

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+8
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ import {
277277
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
278278
import { EditorManager as TheiaEditorManager } from '@theia/editor/lib/browser/editor-manager';
279279
import { EditorManager } from './theia/editor/editor-manager';
280+
import { HostedPluginEvents } from './hosted-plugin-events';
281+
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';
282+
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
280283

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

@@ -805,4 +808,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
805808
);
806809
})
807810
.inSingletonScope();
811+
812+
bind(HostedPluginSupport).toSelf().inSingletonScope();
813+
rebind(TheiaHostedPluginSupport).toService(HostedPluginSupport);
814+
bind(HostedPluginEvents).toSelf().inSingletonScope();
815+
bind(FrontendApplicationContribution).toService(HostedPluginEvents);
808816
});
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { DisposableCollection, Emitter, Event } from '@theia/core';
2+
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
3+
import { inject, injectable } from '@theia/core/shared/inversify';
4+
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';
5+
6+
/**
7+
* Frontend contribution to watch VS Code extension start/stop events from Theia.
8+
*
9+
* In Theia, there are no events when a VS Code extension is loaded, started, unloaded, and stopped.
10+
* Currently, it's possible to `@inject` the `HostedPluginSupport` service from Theia and `await`
11+
* for the `didStart` promise to resolve. But if the OS goes to sleep, the VS Code extensions will
12+
* be unloaded and loaded and started again when the OS awakes. Theia reloads the VS Code extensions
13+
* after the OS awake event, but the `didStart` promise was already resolved, so IDE2 cannot restart the LS.
14+
* This service is meant to work around the limitation of Theia and fire an event every time the VS Code extensions
15+
* loaded and started.
16+
*/
17+
@injectable()
18+
export class HostedPluginEvents implements FrontendApplicationContribution {
19+
@inject(HostedPluginSupport)
20+
private readonly hostedPluginSupport: HostedPluginSupport;
21+
22+
private firstStart = true;
23+
private readonly onPluginsDidStartEmitter = new Emitter<void>();
24+
private readonly onPluginsWillUnloadEmitter = new Emitter<void>();
25+
private readonly toDispose = new DisposableCollection(
26+
this.onPluginsDidStartEmitter,
27+
this.onPluginsWillUnloadEmitter
28+
);
29+
30+
onStart(): void {
31+
this.hostedPluginSupport.onDidLoad(() => {
32+
// Fire the first event, when `didStart` resolves.
33+
if (!this.firstStart) {
34+
console.debug('HostedPluginEvents', "Received 'onDidLoad' event.");
35+
this.onPluginsDidStartEmitter.fire();
36+
} else {
37+
console.debug(
38+
'HostedPluginEvents',
39+
"Received 'onDidLoad' event before the first start. Skipping."
40+
);
41+
}
42+
});
43+
this.hostedPluginSupport.didStart.then(() => {
44+
console.debug('HostedPluginEvents', "Hosted plugins 'didStart'.");
45+
if (!this.firstStart) {
46+
throw new Error(
47+
'Unexpectedly received a `didStart` event after the first start.'
48+
);
49+
}
50+
this.firstStart = false;
51+
this.onPluginsDidStartEmitter.fire();
52+
});
53+
this.hostedPluginSupport.onDidCloseConnection(() => {
54+
console.debug('HostedPluginEvents', "Received 'onDidCloseConnection'.");
55+
this.onPluginsWillUnloadEmitter.fire();
56+
});
57+
}
58+
59+
onStop(): void {
60+
this.toDispose.dispose();
61+
}
62+
63+
get onPluginsDidStart(): Event<void> {
64+
return this.onPluginsDidStartEmitter.event;
65+
}
66+
67+
get onPluginsWillUnload(): Event<void> {
68+
return this.onPluginsWillUnloadEmitter.event;
69+
}
70+
71+
get didStart(): Promise<void> {
72+
return this.hostedPluginSupport.didStart;
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Emitter, Event, JsonRpcProxy } from '@theia/core';
2+
import { injectable, interfaces } from '@theia/core/shared/inversify';
3+
import { HostedPluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
4+
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
5+
@injectable()
6+
export class HostedPluginSupport extends TheiaHostedPluginSupport {
7+
private readonly onDidLoadEmitter = new Emitter<void>();
8+
private readonly onDidCloseConnectionEmitter = new Emitter<void>();
9+
10+
override onStart(container: interfaces.Container): void {
11+
super.onStart(container);
12+
this.hostedPluginServer.onDidCloseConnection(() =>
13+
this.onDidCloseConnectionEmitter.fire()
14+
);
15+
}
16+
17+
protected override async doLoad(): Promise<void> {
18+
await super.doLoad();
19+
this.onDidLoadEmitter.fire(); // Unlike Theia, IDE2 fires an event after loading the VS Code extensions.
20+
}
21+
22+
get onDidLoad(): Event<void> {
23+
return this.onDidLoadEmitter.event;
24+
}
25+
26+
get onDidCloseConnection(): Event<void> {
27+
return this.onDidCloseConnectionEmitter.event;
28+
}
29+
30+
private get hostedPluginServer(): JsonRpcProxy<HostedPluginServer> {
31+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
32+
return (this as any).server;
33+
}
34+
}

0 commit comments

Comments
 (0)