@theia/plugin-dev 1.45.0 → 1.46.0-next.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -31
- package/lib/browser/hosted-plugin-controller.d.ts +74 -74
- package/lib/browser/hosted-plugin-controller.js +352 -352
- package/lib/browser/hosted-plugin-frontend-contribution.d.ts +6 -6
- package/lib/browser/hosted-plugin-frontend-contribution.js +56 -56
- package/lib/browser/hosted-plugin-informer.d.ts +25 -25
- package/lib/browser/hosted-plugin-informer.js +102 -102
- package/lib/browser/hosted-plugin-log-viewer.d.ts +14 -14
- package/lib/browser/hosted-plugin-log-viewer.js +69 -69
- package/lib/browser/hosted-plugin-manager-client.d.ts +80 -80
- package/lib/browser/hosted-plugin-manager-client.js +410 -410
- package/lib/browser/hosted-plugin-preferences.d.ts +13 -13
- package/lib/browser/hosted-plugin-preferences.js +60 -60
- package/lib/browser/plugin-dev-frontend-module.d.ts +3 -3
- package/lib/browser/plugin-dev-frontend-module.js +42 -42
- package/lib/common/index.d.ts +2 -2
- package/lib/common/index.js +31 -31
- package/lib/common/plugin-dev-protocol.d.ts +24 -24
- package/lib/common/plugin-dev-protocol.js +20 -20
- package/lib/node/hosted-instance-manager.d.ts +104 -104
- package/lib/node/hosted-instance-manager.js +320 -320
- package/lib/node/hosted-instance-manager.js.map +1 -1
- package/lib/node/hosted-plugin-reader.d.ts +11 -11
- package/lib/node/hosted-plugin-reader.js +68 -68
- package/lib/node/hosted-plugin-reader.js.map +1 -1
- package/lib/node/hosted-plugin-uri-postprocessor.d.ts +6 -6
- package/lib/node/hosted-plugin-uri-postprocessor.js +27 -27
- package/lib/node/hosted-plugins-manager.d.ts +41 -41
- package/lib/node/hosted-plugins-manager.js +119 -119
- package/lib/node/plugin-dev-backend-module.d.ts +4 -4
- package/lib/node/plugin-dev-backend-module.js +53 -53
- package/lib/node/plugin-dev-service.d.ts +25 -25
- package/lib/node/plugin-dev-service.js +108 -108
- package/lib/node-electron/plugin-dev-electron-backend-module.d.ts +3 -3
- package/lib/node-electron/plugin-dev-electron-backend-module.js +28 -28
- package/lib/package.spec.js +25 -25
- package/package.json +9 -9
- package/src/browser/hosted-plugin-controller.ts +356 -356
- package/src/browser/hosted-plugin-frontend-contribution.ts +45 -45
- package/src/browser/hosted-plugin-informer.ts +93 -93
- package/src/browser/hosted-plugin-log-viewer.ts +52 -52
- package/src/browser/hosted-plugin-manager-client.ts +430 -430
- package/src/browser/hosted-plugin-preferences.ts +71 -71
- package/src/browser/plugin-dev-frontend-module.ts +45 -45
- package/src/common/index.ts +21 -21
- package/src/common/plugin-dev-protocol.ts +45 -45
- package/src/node/hosted-instance-manager.ts +382 -382
- package/src/node/hosted-plugin-reader.ts +58 -58
- package/src/node/hosted-plugin-uri-postprocessor.ts +32 -32
- package/src/node/hosted-plugins-manager.ts +146 -146
- package/src/node/plugin-dev-backend-module.ts +54 -54
- package/src/node/plugin-dev-service.ts +107 -107
- package/src/node-electron/plugin-dev-electron-backend-module.ts +29 -29
- package/src/package.spec.ts +28 -28
|
@@ -1,356 +1,356 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2018 Red Hat, Inc. and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
import { injectable, inject } from '@theia/core/shared/inversify';
|
|
18
|
-
import { StatusBar } from '@theia/core/lib/browser/status-bar/status-bar';
|
|
19
|
-
import { StatusBarAlignment, StatusBarEntry, FrontendApplicationContribution, PreferenceServiceImpl, PreferenceChange, codicon } from '@theia/core/lib/browser';
|
|
20
|
-
import { MessageService } from '@theia/core/lib/common';
|
|
21
|
-
import { CommandRegistry } from '@theia/core/shared/@phosphor/commands';
|
|
22
|
-
import { Menu } from '@theia/core/shared/@phosphor/widgets';
|
|
23
|
-
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
|
24
|
-
import { ConnectionStatusService, ConnectionStatus } from '@theia/core/lib/browser/connection-status-service';
|
|
25
|
-
import { PluginDevServer } from '../common/plugin-dev-protocol';
|
|
26
|
-
import { HostedPluginManagerClient, HostedInstanceState, HostedPluginCommands, HostedInstanceData } from './hosted-plugin-manager-client';
|
|
27
|
-
import { HostedPluginLogViewer } from './hosted-plugin-log-viewer';
|
|
28
|
-
import { HostedPluginPreferences } from './hosted-plugin-preferences';
|
|
29
|
-
import { nls } from '@theia/core/lib/common/nls';
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Adds a status bar element displaying the state of secondary Theia instance with hosted plugin and
|
|
33
|
-
* allows controlling the instance by simple clicking on the status bar element.
|
|
34
|
-
*/
|
|
35
|
-
@injectable()
|
|
36
|
-
export class HostedPluginController implements FrontendApplicationContribution {
|
|
37
|
-
|
|
38
|
-
public static readonly HOSTED_PLUGIN = 'hosted-plugin';
|
|
39
|
-
public static readonly HOSTED_PLUGIN_OFFLINE = 'hosted-plugin-offline';
|
|
40
|
-
public static readonly HOSTED_PLUGIN_FAILED = 'hosted-plugin-failed';
|
|
41
|
-
|
|
42
|
-
@inject(StatusBar)
|
|
43
|
-
protected readonly statusBar: StatusBar;
|
|
44
|
-
|
|
45
|
-
@inject(FrontendApplicationStateService)
|
|
46
|
-
protected readonly frontendApplicationStateService: FrontendApplicationStateService;
|
|
47
|
-
|
|
48
|
-
@inject(PluginDevServer)
|
|
49
|
-
protected readonly hostedPluginServer: PluginDevServer;
|
|
50
|
-
|
|
51
|
-
@inject(HostedPluginManagerClient)
|
|
52
|
-
protected readonly hostedPluginManagerClient: HostedPluginManagerClient;
|
|
53
|
-
|
|
54
|
-
@inject(ConnectionStatusService)
|
|
55
|
-
protected readonly connectionStatusService: ConnectionStatusService;
|
|
56
|
-
|
|
57
|
-
@inject(HostedPluginLogViewer)
|
|
58
|
-
protected readonly hostedPluginLogViewer: HostedPluginLogViewer;
|
|
59
|
-
|
|
60
|
-
@inject(HostedPluginPreferences)
|
|
61
|
-
protected readonly hostedPluginPreferences: HostedPluginPreferences;
|
|
62
|
-
|
|
63
|
-
@inject(PreferenceServiceImpl)
|
|
64
|
-
protected readonly preferenceService: PreferenceServiceImpl;
|
|
65
|
-
|
|
66
|
-
@inject(MessageService)
|
|
67
|
-
protected readonly messageService: MessageService;
|
|
68
|
-
|
|
69
|
-
private pluginState: HostedInstanceState = HostedInstanceState.STOPPED;
|
|
70
|
-
// used only for displaying Running instead of Watching in status bar if run of watcher fails
|
|
71
|
-
private watcherSuccess: boolean;
|
|
72
|
-
private entry: StatusBarEntry | undefined;
|
|
73
|
-
|
|
74
|
-
public initialize(): void {
|
|
75
|
-
this.hostedPluginServer.getHostedPlugin().then(pluginMetadata => {
|
|
76
|
-
if (!pluginMetadata) {
|
|
77
|
-
this.frontendApplicationStateService.reachedState('ready').then(() => {
|
|
78
|
-
// handles status bar item
|
|
79
|
-
this.hostedPluginManagerClient.onStateChanged(e => {
|
|
80
|
-
if (e.state === HostedInstanceState.STARTING) {
|
|
81
|
-
this.onHostedPluginStarting();
|
|
82
|
-
} else if (e.state === HostedInstanceState.RUNNING) {
|
|
83
|
-
this.onHostedPluginRunning();
|
|
84
|
-
} else if (e.state === HostedInstanceState.STOPPED) {
|
|
85
|
-
this.onHostedPluginStopped();
|
|
86
|
-
} else if (e.state === HostedInstanceState.FAILED) {
|
|
87
|
-
this.onHostedPluginFailed();
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// handles watch compilation
|
|
92
|
-
this.hostedPluginManagerClient.onStateChanged(e => this.handleWatchers(e));
|
|
93
|
-
|
|
94
|
-
// updates status bar if page is loading when hosted instance is already running
|
|
95
|
-
this.hostedPluginServer.isHostedPluginInstanceRunning().then(running => {
|
|
96
|
-
if (running) {
|
|
97
|
-
this.onHostedPluginRunning();
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
this.connectionStatusService.onStatusChange(() => this.onConnectionStatusChanged());
|
|
103
|
-
|
|
104
|
-
this.preferenceService.onPreferenceChanged(preference => this.onPreferencesChanged(preference));
|
|
105
|
-
} else {
|
|
106
|
-
console.error(`Need to load plugin ${pluginMetadata.model.id}`);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Display status bar element for stopped plugin.
|
|
113
|
-
*/
|
|
114
|
-
protected async onHostedPluginStopped(): Promise<void> {
|
|
115
|
-
this.pluginState = HostedInstanceState.STOPPED;
|
|
116
|
-
|
|
117
|
-
this.entry = {
|
|
118
|
-
text: `${nls.localize('theia/plugin-dev/hostedPluginStopped', 'Hosted Plugin: Stopped')} $(angle-up)`,
|
|
119
|
-
alignment: StatusBarAlignment.LEFT,
|
|
120
|
-
priority: 100,
|
|
121
|
-
onclick: e => {
|
|
122
|
-
this.showMenu(e.clientX, e.clientY);
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
this.entry.className = HostedPluginController.HOSTED_PLUGIN;
|
|
127
|
-
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Display status bar element for starting plugin.
|
|
132
|
-
*/
|
|
133
|
-
protected async onHostedPluginStarting(): Promise<void> {
|
|
134
|
-
this.pluginState = HostedInstanceState.STARTING;
|
|
135
|
-
|
|
136
|
-
this.hostedPluginLogViewer.showLogConsole();
|
|
137
|
-
|
|
138
|
-
this.entry = {
|
|
139
|
-
text: `$(cog~spin) ${nls.localize('theia/plugin-dev/hostedPluginStarting', 'Hosted Plugin: Starting')}`,
|
|
140
|
-
alignment: StatusBarAlignment.LEFT,
|
|
141
|
-
priority: 100
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
this.entry.className = HostedPluginController.HOSTED_PLUGIN;
|
|
145
|
-
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Display status bar element for running plugin.
|
|
150
|
-
*/
|
|
151
|
-
protected async onHostedPluginRunning(): Promise<void> {
|
|
152
|
-
this.pluginState = HostedInstanceState.RUNNING;
|
|
153
|
-
|
|
154
|
-
let entryText: string;
|
|
155
|
-
if (this.hostedPluginPreferences['hosted-plugin.watchMode'] && this.watcherSuccess) {
|
|
156
|
-
entryText = `$(cog~spin) ${nls.localize('theia/plugin-dev/hostedPluginWatching', 'Hosted Plugin: Watching')}$(angle-up)`;
|
|
157
|
-
} else {
|
|
158
|
-
entryText = `$(cog~spin) ${nls.localize('theia/plugin-dev/hostedPluginRunning', 'Hosted Plugin: Running')} $(angle-up)`;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
this.entry = {
|
|
162
|
-
text: entryText,
|
|
163
|
-
alignment: StatusBarAlignment.LEFT,
|
|
164
|
-
priority: 100,
|
|
165
|
-
onclick: e => {
|
|
166
|
-
this.showMenu(e.clientX, e.clientY);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
this.entry.className = HostedPluginController.HOSTED_PLUGIN;
|
|
171
|
-
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Display status bar element for failed plugin.
|
|
176
|
-
*/
|
|
177
|
-
protected async onHostedPluginFailed(): Promise<void> {
|
|
178
|
-
this.pluginState = HostedInstanceState.FAILED;
|
|
179
|
-
|
|
180
|
-
this.entry = {
|
|
181
|
-
text: `${nls.localize('theia/plugin-dev/hostedPluginStopped', 'Hosted Plugin: Stopped')} $(angle-up)`,
|
|
182
|
-
alignment: StatusBarAlignment.LEFT,
|
|
183
|
-
priority: 100,
|
|
184
|
-
onclick: e => {
|
|
185
|
-
this.showMenu(e.clientX, e.clientY);
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
this.entry.className = HostedPluginController.HOSTED_PLUGIN_FAILED;
|
|
190
|
-
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
protected async onPreferencesChanged(preference: PreferenceChange): Promise<void> {
|
|
194
|
-
if (preference.preferenceName === 'hosted-plugin.watchMode') {
|
|
195
|
-
if (await this.hostedPluginServer.isHostedPluginInstanceRunning()) {
|
|
196
|
-
const pluginLocation = await this.hostedPluginServer.getHostedPluginURI();
|
|
197
|
-
const isWatchCompilationRunning = await this.hostedPluginServer.isWatchCompilationRunning(pluginLocation);
|
|
198
|
-
if (preference.newValue === true) {
|
|
199
|
-
if (!isWatchCompilationRunning) {
|
|
200
|
-
await this.runWatchCompilation(pluginLocation.toString());
|
|
201
|
-
}
|
|
202
|
-
} else {
|
|
203
|
-
if (isWatchCompilationRunning) {
|
|
204
|
-
await this.hostedPluginServer.stopWatchCompilation(pluginLocation.toString());
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
// update status bar
|
|
208
|
-
this.onHostedPluginRunning();
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Starts / stops watchers on hosted instance state change.
|
|
215
|
-
*
|
|
216
|
-
* @param event hosted instance state change event
|
|
217
|
-
*/
|
|
218
|
-
protected async handleWatchers(event: HostedInstanceData): Promise<void> {
|
|
219
|
-
if (event.state === HostedInstanceState.RUNNING) {
|
|
220
|
-
if (this.hostedPluginPreferences['hosted-plugin.watchMode']) {
|
|
221
|
-
await this.runWatchCompilation(event.pluginLocation.toString());
|
|
222
|
-
// update status bar
|
|
223
|
-
this.onHostedPluginRunning();
|
|
224
|
-
}
|
|
225
|
-
} else if (event.state === HostedInstanceState.STOPPING) {
|
|
226
|
-
if (this.hostedPluginPreferences['hosted-plugin.watchMode']) {
|
|
227
|
-
const isRunning = await this.hostedPluginServer.isWatchCompilationRunning(event.pluginLocation.toString());
|
|
228
|
-
if (isRunning) {
|
|
229
|
-
try {
|
|
230
|
-
await this.hostedPluginServer.stopWatchCompilation(event.pluginLocation.toString());
|
|
231
|
-
} catch (error) {
|
|
232
|
-
this.messageService.error(this.getErrorMessage(error));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
private async runWatchCompilation(pluginLocation: string): Promise<void> {
|
|
240
|
-
try {
|
|
241
|
-
await this.hostedPluginServer.runWatchCompilation(pluginLocation);
|
|
242
|
-
this.watcherSuccess = true;
|
|
243
|
-
} catch (error) {
|
|
244
|
-
this.messageService.error(this.getErrorMessage(error));
|
|
245
|
-
this.watcherSuccess = false;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
250
|
-
private getErrorMessage(error: any): string {
|
|
251
|
-
return error?.message?.substring(error.message.indexOf(':') + 1) || '';
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Updating status bar element when changing connection status.
|
|
256
|
-
*/
|
|
257
|
-
private onConnectionStatusChanged(): void {
|
|
258
|
-
if (this.connectionStatusService.currentStatus === ConnectionStatus.OFFLINE) {
|
|
259
|
-
// Re-set the element only if it's visible on status bar
|
|
260
|
-
if (this.entry) {
|
|
261
|
-
const offlineElement = {
|
|
262
|
-
text: nls.localize('theia/plugin-dev/hostedPluginStopped', 'Hosted Plugin: Stopped'),
|
|
263
|
-
alignment: StatusBarAlignment.LEFT,
|
|
264
|
-
priority: 100
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
this.entry.className = HostedPluginController.HOSTED_PLUGIN_OFFLINE;
|
|
268
|
-
this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, offlineElement);
|
|
269
|
-
}
|
|
270
|
-
} else {
|
|
271
|
-
// ask state of hosted plugin when switching to Online
|
|
272
|
-
if (this.entry) {
|
|
273
|
-
this.hostedPluginServer.isHostedPluginInstanceRunning().then(running => {
|
|
274
|
-
if (running) {
|
|
275
|
-
this.onHostedPluginRunning();
|
|
276
|
-
} else {
|
|
277
|
-
this.onHostedPluginStopped();
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Show menu containing actions to start/stop/restart hosted plugin.
|
|
286
|
-
*/
|
|
287
|
-
protected showMenu(x: number, y: number): void {
|
|
288
|
-
const commands = new CommandRegistry();
|
|
289
|
-
const menu = new Menu({
|
|
290
|
-
commands
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
if (this.pluginState === HostedInstanceState.RUNNING) {
|
|
294
|
-
this.addCommandsForRunningPlugin(commands, menu);
|
|
295
|
-
} else if (this.pluginState === HostedInstanceState.STOPPED || this.pluginState === HostedInstanceState.FAILED) {
|
|
296
|
-
this.addCommandsForStoppedPlugin(commands, menu);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
menu.open(x, y);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Adds commands to the menu for running plugin.
|
|
304
|
-
*/
|
|
305
|
-
protected addCommandsForRunningPlugin(commands: CommandRegistry, menu: Menu): void {
|
|
306
|
-
commands.addCommand(HostedPluginCommands.STOP.id, {
|
|
307
|
-
label: nls.localize('theia/plugin-dev/stopInstance', 'Stop Instance'),
|
|
308
|
-
icon: codicon('debug-stop'),
|
|
309
|
-
execute: () => setTimeout(() => this.hostedPluginManagerClient.stop(), 100)
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
menu.addItem({
|
|
313
|
-
type: 'command',
|
|
314
|
-
command: HostedPluginCommands.STOP.id
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
commands.addCommand(HostedPluginCommands.RESTART.id, {
|
|
318
|
-
label: nls.localize('theia/plugin-dev/restartInstance', 'Restart Instance'),
|
|
319
|
-
icon: codicon('debug-restart'),
|
|
320
|
-
execute: () => setTimeout(() => this.hostedPluginManagerClient.restart(), 100)
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
menu.addItem({
|
|
324
|
-
type: 'command',
|
|
325
|
-
command: HostedPluginCommands.RESTART.id
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Adds command to the menu for stopped plugin.
|
|
331
|
-
*/
|
|
332
|
-
protected addCommandsForStoppedPlugin(commands: CommandRegistry, menu: Menu): void {
|
|
333
|
-
commands.addCommand(HostedPluginCommands.START.id, {
|
|
334
|
-
label: nls.localize('theia/plugin-dev/startInstance', 'Start Instance'),
|
|
335
|
-
icon: codicon('play'),
|
|
336
|
-
execute: () => setTimeout(() => this.hostedPluginManagerClient.start(), 100)
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
menu.addItem({
|
|
340
|
-
type: 'command',
|
|
341
|
-
command: HostedPluginCommands.START.id
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
commands.addCommand(HostedPluginCommands.DEBUG.id, {
|
|
345
|
-
label: nls.localize('theia/plugin-dev/debugInstance', 'Debug Instance'),
|
|
346
|
-
icon: codicon('debug'),
|
|
347
|
-
execute: () => setTimeout(() => this.hostedPluginManagerClient.debug(), 100)
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
menu.addItem({
|
|
351
|
-
type: 'command',
|
|
352
|
-
command: HostedPluginCommands.DEBUG.id
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
}
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2018 Red Hat, Inc. and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { injectable, inject } from '@theia/core/shared/inversify';
|
|
18
|
+
import { StatusBar } from '@theia/core/lib/browser/status-bar/status-bar';
|
|
19
|
+
import { StatusBarAlignment, StatusBarEntry, FrontendApplicationContribution, PreferenceServiceImpl, PreferenceChange, codicon } from '@theia/core/lib/browser';
|
|
20
|
+
import { MessageService } from '@theia/core/lib/common';
|
|
21
|
+
import { CommandRegistry } from '@theia/core/shared/@phosphor/commands';
|
|
22
|
+
import { Menu } from '@theia/core/shared/@phosphor/widgets';
|
|
23
|
+
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
|
24
|
+
import { ConnectionStatusService, ConnectionStatus } from '@theia/core/lib/browser/connection-status-service';
|
|
25
|
+
import { PluginDevServer } from '../common/plugin-dev-protocol';
|
|
26
|
+
import { HostedPluginManagerClient, HostedInstanceState, HostedPluginCommands, HostedInstanceData } from './hosted-plugin-manager-client';
|
|
27
|
+
import { HostedPluginLogViewer } from './hosted-plugin-log-viewer';
|
|
28
|
+
import { HostedPluginPreferences } from './hosted-plugin-preferences';
|
|
29
|
+
import { nls } from '@theia/core/lib/common/nls';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Adds a status bar element displaying the state of secondary Theia instance with hosted plugin and
|
|
33
|
+
* allows controlling the instance by simple clicking on the status bar element.
|
|
34
|
+
*/
|
|
35
|
+
@injectable()
|
|
36
|
+
export class HostedPluginController implements FrontendApplicationContribution {
|
|
37
|
+
|
|
38
|
+
public static readonly HOSTED_PLUGIN = 'hosted-plugin';
|
|
39
|
+
public static readonly HOSTED_PLUGIN_OFFLINE = 'hosted-plugin-offline';
|
|
40
|
+
public static readonly HOSTED_PLUGIN_FAILED = 'hosted-plugin-failed';
|
|
41
|
+
|
|
42
|
+
@inject(StatusBar)
|
|
43
|
+
protected readonly statusBar: StatusBar;
|
|
44
|
+
|
|
45
|
+
@inject(FrontendApplicationStateService)
|
|
46
|
+
protected readonly frontendApplicationStateService: FrontendApplicationStateService;
|
|
47
|
+
|
|
48
|
+
@inject(PluginDevServer)
|
|
49
|
+
protected readonly hostedPluginServer: PluginDevServer;
|
|
50
|
+
|
|
51
|
+
@inject(HostedPluginManagerClient)
|
|
52
|
+
protected readonly hostedPluginManagerClient: HostedPluginManagerClient;
|
|
53
|
+
|
|
54
|
+
@inject(ConnectionStatusService)
|
|
55
|
+
protected readonly connectionStatusService: ConnectionStatusService;
|
|
56
|
+
|
|
57
|
+
@inject(HostedPluginLogViewer)
|
|
58
|
+
protected readonly hostedPluginLogViewer: HostedPluginLogViewer;
|
|
59
|
+
|
|
60
|
+
@inject(HostedPluginPreferences)
|
|
61
|
+
protected readonly hostedPluginPreferences: HostedPluginPreferences;
|
|
62
|
+
|
|
63
|
+
@inject(PreferenceServiceImpl)
|
|
64
|
+
protected readonly preferenceService: PreferenceServiceImpl;
|
|
65
|
+
|
|
66
|
+
@inject(MessageService)
|
|
67
|
+
protected readonly messageService: MessageService;
|
|
68
|
+
|
|
69
|
+
private pluginState: HostedInstanceState = HostedInstanceState.STOPPED;
|
|
70
|
+
// used only for displaying Running instead of Watching in status bar if run of watcher fails
|
|
71
|
+
private watcherSuccess: boolean;
|
|
72
|
+
private entry: StatusBarEntry | undefined;
|
|
73
|
+
|
|
74
|
+
public initialize(): void {
|
|
75
|
+
this.hostedPluginServer.getHostedPlugin().then(pluginMetadata => {
|
|
76
|
+
if (!pluginMetadata) {
|
|
77
|
+
this.frontendApplicationStateService.reachedState('ready').then(() => {
|
|
78
|
+
// handles status bar item
|
|
79
|
+
this.hostedPluginManagerClient.onStateChanged(e => {
|
|
80
|
+
if (e.state === HostedInstanceState.STARTING) {
|
|
81
|
+
this.onHostedPluginStarting();
|
|
82
|
+
} else if (e.state === HostedInstanceState.RUNNING) {
|
|
83
|
+
this.onHostedPluginRunning();
|
|
84
|
+
} else if (e.state === HostedInstanceState.STOPPED) {
|
|
85
|
+
this.onHostedPluginStopped();
|
|
86
|
+
} else if (e.state === HostedInstanceState.FAILED) {
|
|
87
|
+
this.onHostedPluginFailed();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// handles watch compilation
|
|
92
|
+
this.hostedPluginManagerClient.onStateChanged(e => this.handleWatchers(e));
|
|
93
|
+
|
|
94
|
+
// updates status bar if page is loading when hosted instance is already running
|
|
95
|
+
this.hostedPluginServer.isHostedPluginInstanceRunning().then(running => {
|
|
96
|
+
if (running) {
|
|
97
|
+
this.onHostedPluginRunning();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this.connectionStatusService.onStatusChange(() => this.onConnectionStatusChanged());
|
|
103
|
+
|
|
104
|
+
this.preferenceService.onPreferenceChanged(preference => this.onPreferencesChanged(preference));
|
|
105
|
+
} else {
|
|
106
|
+
console.error(`Need to load plugin ${pluginMetadata.model.id}`);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Display status bar element for stopped plugin.
|
|
113
|
+
*/
|
|
114
|
+
protected async onHostedPluginStopped(): Promise<void> {
|
|
115
|
+
this.pluginState = HostedInstanceState.STOPPED;
|
|
116
|
+
|
|
117
|
+
this.entry = {
|
|
118
|
+
text: `${nls.localize('theia/plugin-dev/hostedPluginStopped', 'Hosted Plugin: Stopped')} $(angle-up)`,
|
|
119
|
+
alignment: StatusBarAlignment.LEFT,
|
|
120
|
+
priority: 100,
|
|
121
|
+
onclick: e => {
|
|
122
|
+
this.showMenu(e.clientX, e.clientY);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
this.entry.className = HostedPluginController.HOSTED_PLUGIN;
|
|
127
|
+
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Display status bar element for starting plugin.
|
|
132
|
+
*/
|
|
133
|
+
protected async onHostedPluginStarting(): Promise<void> {
|
|
134
|
+
this.pluginState = HostedInstanceState.STARTING;
|
|
135
|
+
|
|
136
|
+
this.hostedPluginLogViewer.showLogConsole();
|
|
137
|
+
|
|
138
|
+
this.entry = {
|
|
139
|
+
text: `$(cog~spin) ${nls.localize('theia/plugin-dev/hostedPluginStarting', 'Hosted Plugin: Starting')}`,
|
|
140
|
+
alignment: StatusBarAlignment.LEFT,
|
|
141
|
+
priority: 100
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
this.entry.className = HostedPluginController.HOSTED_PLUGIN;
|
|
145
|
+
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Display status bar element for running plugin.
|
|
150
|
+
*/
|
|
151
|
+
protected async onHostedPluginRunning(): Promise<void> {
|
|
152
|
+
this.pluginState = HostedInstanceState.RUNNING;
|
|
153
|
+
|
|
154
|
+
let entryText: string;
|
|
155
|
+
if (this.hostedPluginPreferences['hosted-plugin.watchMode'] && this.watcherSuccess) {
|
|
156
|
+
entryText = `$(cog~spin) ${nls.localize('theia/plugin-dev/hostedPluginWatching', 'Hosted Plugin: Watching')}$(angle-up)`;
|
|
157
|
+
} else {
|
|
158
|
+
entryText = `$(cog~spin) ${nls.localize('theia/plugin-dev/hostedPluginRunning', 'Hosted Plugin: Running')} $(angle-up)`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.entry = {
|
|
162
|
+
text: entryText,
|
|
163
|
+
alignment: StatusBarAlignment.LEFT,
|
|
164
|
+
priority: 100,
|
|
165
|
+
onclick: e => {
|
|
166
|
+
this.showMenu(e.clientX, e.clientY);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
this.entry.className = HostedPluginController.HOSTED_PLUGIN;
|
|
171
|
+
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Display status bar element for failed plugin.
|
|
176
|
+
*/
|
|
177
|
+
protected async onHostedPluginFailed(): Promise<void> {
|
|
178
|
+
this.pluginState = HostedInstanceState.FAILED;
|
|
179
|
+
|
|
180
|
+
this.entry = {
|
|
181
|
+
text: `${nls.localize('theia/plugin-dev/hostedPluginStopped', 'Hosted Plugin: Stopped')} $(angle-up)`,
|
|
182
|
+
alignment: StatusBarAlignment.LEFT,
|
|
183
|
+
priority: 100,
|
|
184
|
+
onclick: e => {
|
|
185
|
+
this.showMenu(e.clientX, e.clientY);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
this.entry.className = HostedPluginController.HOSTED_PLUGIN_FAILED;
|
|
190
|
+
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
protected async onPreferencesChanged(preference: PreferenceChange): Promise<void> {
|
|
194
|
+
if (preference.preferenceName === 'hosted-plugin.watchMode') {
|
|
195
|
+
if (await this.hostedPluginServer.isHostedPluginInstanceRunning()) {
|
|
196
|
+
const pluginLocation = await this.hostedPluginServer.getHostedPluginURI();
|
|
197
|
+
const isWatchCompilationRunning = await this.hostedPluginServer.isWatchCompilationRunning(pluginLocation);
|
|
198
|
+
if (preference.newValue === true) {
|
|
199
|
+
if (!isWatchCompilationRunning) {
|
|
200
|
+
await this.runWatchCompilation(pluginLocation.toString());
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
if (isWatchCompilationRunning) {
|
|
204
|
+
await this.hostedPluginServer.stopWatchCompilation(pluginLocation.toString());
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// update status bar
|
|
208
|
+
this.onHostedPluginRunning();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Starts / stops watchers on hosted instance state change.
|
|
215
|
+
*
|
|
216
|
+
* @param event hosted instance state change event
|
|
217
|
+
*/
|
|
218
|
+
protected async handleWatchers(event: HostedInstanceData): Promise<void> {
|
|
219
|
+
if (event.state === HostedInstanceState.RUNNING) {
|
|
220
|
+
if (this.hostedPluginPreferences['hosted-plugin.watchMode']) {
|
|
221
|
+
await this.runWatchCompilation(event.pluginLocation.toString());
|
|
222
|
+
// update status bar
|
|
223
|
+
this.onHostedPluginRunning();
|
|
224
|
+
}
|
|
225
|
+
} else if (event.state === HostedInstanceState.STOPPING) {
|
|
226
|
+
if (this.hostedPluginPreferences['hosted-plugin.watchMode']) {
|
|
227
|
+
const isRunning = await this.hostedPluginServer.isWatchCompilationRunning(event.pluginLocation.toString());
|
|
228
|
+
if (isRunning) {
|
|
229
|
+
try {
|
|
230
|
+
await this.hostedPluginServer.stopWatchCompilation(event.pluginLocation.toString());
|
|
231
|
+
} catch (error) {
|
|
232
|
+
this.messageService.error(this.getErrorMessage(error));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private async runWatchCompilation(pluginLocation: string): Promise<void> {
|
|
240
|
+
try {
|
|
241
|
+
await this.hostedPluginServer.runWatchCompilation(pluginLocation);
|
|
242
|
+
this.watcherSuccess = true;
|
|
243
|
+
} catch (error) {
|
|
244
|
+
this.messageService.error(this.getErrorMessage(error));
|
|
245
|
+
this.watcherSuccess = false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
250
|
+
private getErrorMessage(error: any): string {
|
|
251
|
+
return error?.message?.substring(error.message.indexOf(':') + 1) || '';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Updating status bar element when changing connection status.
|
|
256
|
+
*/
|
|
257
|
+
private onConnectionStatusChanged(): void {
|
|
258
|
+
if (this.connectionStatusService.currentStatus === ConnectionStatus.OFFLINE) {
|
|
259
|
+
// Re-set the element only if it's visible on status bar
|
|
260
|
+
if (this.entry) {
|
|
261
|
+
const offlineElement = {
|
|
262
|
+
text: nls.localize('theia/plugin-dev/hostedPluginStopped', 'Hosted Plugin: Stopped'),
|
|
263
|
+
alignment: StatusBarAlignment.LEFT,
|
|
264
|
+
priority: 100
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
this.entry.className = HostedPluginController.HOSTED_PLUGIN_OFFLINE;
|
|
268
|
+
this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, offlineElement);
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
// ask state of hosted plugin when switching to Online
|
|
272
|
+
if (this.entry) {
|
|
273
|
+
this.hostedPluginServer.isHostedPluginInstanceRunning().then(running => {
|
|
274
|
+
if (running) {
|
|
275
|
+
this.onHostedPluginRunning();
|
|
276
|
+
} else {
|
|
277
|
+
this.onHostedPluginStopped();
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Show menu containing actions to start/stop/restart hosted plugin.
|
|
286
|
+
*/
|
|
287
|
+
protected showMenu(x: number, y: number): void {
|
|
288
|
+
const commands = new CommandRegistry();
|
|
289
|
+
const menu = new Menu({
|
|
290
|
+
commands
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
if (this.pluginState === HostedInstanceState.RUNNING) {
|
|
294
|
+
this.addCommandsForRunningPlugin(commands, menu);
|
|
295
|
+
} else if (this.pluginState === HostedInstanceState.STOPPED || this.pluginState === HostedInstanceState.FAILED) {
|
|
296
|
+
this.addCommandsForStoppedPlugin(commands, menu);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
menu.open(x, y);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Adds commands to the menu for running plugin.
|
|
304
|
+
*/
|
|
305
|
+
protected addCommandsForRunningPlugin(commands: CommandRegistry, menu: Menu): void {
|
|
306
|
+
commands.addCommand(HostedPluginCommands.STOP.id, {
|
|
307
|
+
label: nls.localize('theia/plugin-dev/stopInstance', 'Stop Instance'),
|
|
308
|
+
icon: codicon('debug-stop'),
|
|
309
|
+
execute: () => setTimeout(() => this.hostedPluginManagerClient.stop(), 100)
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
menu.addItem({
|
|
313
|
+
type: 'command',
|
|
314
|
+
command: HostedPluginCommands.STOP.id
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
commands.addCommand(HostedPluginCommands.RESTART.id, {
|
|
318
|
+
label: nls.localize('theia/plugin-dev/restartInstance', 'Restart Instance'),
|
|
319
|
+
icon: codicon('debug-restart'),
|
|
320
|
+
execute: () => setTimeout(() => this.hostedPluginManagerClient.restart(), 100)
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
menu.addItem({
|
|
324
|
+
type: 'command',
|
|
325
|
+
command: HostedPluginCommands.RESTART.id
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Adds command to the menu for stopped plugin.
|
|
331
|
+
*/
|
|
332
|
+
protected addCommandsForStoppedPlugin(commands: CommandRegistry, menu: Menu): void {
|
|
333
|
+
commands.addCommand(HostedPluginCommands.START.id, {
|
|
334
|
+
label: nls.localize('theia/plugin-dev/startInstance', 'Start Instance'),
|
|
335
|
+
icon: codicon('play'),
|
|
336
|
+
execute: () => setTimeout(() => this.hostedPluginManagerClient.start(), 100)
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
menu.addItem({
|
|
340
|
+
type: 'command',
|
|
341
|
+
command: HostedPluginCommands.START.id
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
commands.addCommand(HostedPluginCommands.DEBUG.id, {
|
|
345
|
+
label: nls.localize('theia/plugin-dev/debugInstance', 'Debug Instance'),
|
|
346
|
+
icon: codicon('debug'),
|
|
347
|
+
execute: () => setTimeout(() => this.hostedPluginManagerClient.debug(), 100)
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
menu.addItem({
|
|
351
|
+
type: 'command',
|
|
352
|
+
command: HostedPluginCommands.DEBUG.id
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
}
|