@theia/core 1.40.1 → 1.41.0
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 +6 -6
- package/i18n/nls.cs.json +5 -1
- package/i18n/nls.de.json +5 -1
- package/i18n/nls.es.json +5 -1
- package/i18n/nls.fr.json +5 -1
- package/i18n/nls.hu.json +5 -1
- package/i18n/nls.it.json +5 -1
- package/i18n/nls.ja.json +5 -1
- package/i18n/nls.json +5 -1
- package/i18n/nls.pl.json +5 -1
- package/i18n/nls.pt-br.json +5 -1
- package/i18n/nls.pt-pt.json +5 -1
- package/i18n/nls.ru.json +5 -1
- package/i18n/nls.zh-cn.json +5 -1
- package/lib/browser/common-frontend-contribution.d.ts +11 -0
- package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/common-frontend-contribution.js +97 -14
- package/lib/browser/common-frontend-contribution.js.map +1 -1
- package/lib/browser/context-menu-renderer.d.ts +5 -0
- package/lib/browser/context-menu-renderer.d.ts.map +1 -1
- package/lib/browser/label-parser.d.ts +8 -0
- package/lib/browser/label-parser.d.ts.map +1 -1
- package/lib/browser/label-parser.js +14 -0
- package/lib/browser/label-parser.js.map +1 -1
- package/lib/browser/label-parser.spec.js +33 -0
- package/lib/browser/label-parser.spec.js.map +1 -1
- package/lib/browser/menu/browser-context-menu-renderer.d.ts +1 -1
- package/lib/browser/menu/browser-context-menu-renderer.d.ts.map +1 -1
- package/lib/browser/menu/browser-context-menu-renderer.js +2 -2
- package/lib/browser/menu/browser-context-menu-renderer.js.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts +1 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.js +2 -2
- package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
- package/lib/browser/performance/frontend-stopwatch.d.ts.map +1 -1
- package/lib/browser/performance/frontend-stopwatch.js +6 -2
- package/lib/browser/performance/frontend-stopwatch.js.map +1 -1
- package/lib/browser/preferences/preference-contribution.d.ts +2 -0
- package/lib/browser/preferences/preference-contribution.d.ts.map +1 -1
- package/lib/browser/preferences/preference-contribution.js +48 -24
- package/lib/browser/preferences/preference-contribution.js.map +1 -1
- package/lib/browser/saveable.d.ts +15 -1
- package/lib/browser/saveable.d.ts.map +1 -1
- package/lib/browser/saveable.js +34 -1
- package/lib/browser/saveable.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts +46 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js +87 -6
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts +20 -2
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js +28 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +25 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +79 -7
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
- package/lib/browser/shell/tab-bars.d.ts.map +1 -1
- package/lib/browser/shell/tab-bars.js +9 -1
- package/lib/browser/shell/tab-bars.js.map +1 -1
- package/lib/browser/tree/tree-model.d.ts +2 -0
- package/lib/browser/tree/tree-model.d.ts.map +1 -1
- package/lib/browser/tree/tree-model.js +6 -0
- package/lib/browser/tree/tree-model.js.map +1 -1
- package/lib/browser/tree/tree-widget.d.ts +18 -0
- package/lib/browser/tree/tree-widget.d.ts.map +1 -1
- package/lib/browser/tree/tree-widget.js +57 -0
- package/lib/browser/tree/tree-widget.js.map +1 -1
- package/lib/browser/tree/tree.d.ts +18 -0
- package/lib/browser/tree/tree.d.ts.map +1 -1
- package/lib/browser/tree/tree.js +6 -0
- package/lib/browser/tree/tree.js.map +1 -1
- package/lib/browser/widgets/enhanced-preview-widget.d.ts +7 -0
- package/lib/browser/widgets/enhanced-preview-widget.d.ts.map +1 -0
- package/lib/browser/widgets/enhanced-preview-widget.js +27 -0
- package/lib/browser/widgets/enhanced-preview-widget.js.map +1 -0
- package/lib/browser/window-contribution.js +1 -1
- package/lib/browser/window-contribution.js.map +1 -1
- package/lib/common/array-utils.d.ts +7 -0
- package/lib/common/array-utils.d.ts.map +1 -1
- package/lib/common/array-utils.js +21 -0
- package/lib/common/array-utils.js.map +1 -1
- package/lib/common/event.d.ts +5 -0
- package/lib/common/event.d.ts.map +1 -1
- package/lib/common/event.js +5 -1
- package/lib/common/event.js.map +1 -1
- package/lib/common/menu/menu-model-registry.d.ts +12 -1
- package/lib/common/menu/menu-model-registry.d.ts.map +1 -1
- package/lib/common/menu/menu-model-registry.js +46 -0
- package/lib/common/menu/menu-model-registry.js.map +1 -1
- package/lib/common/performance/measurement.d.ts +21 -0
- package/lib/common/performance/measurement.d.ts.map +1 -1
- package/lib/common/performance/stopwatch.d.ts +10 -2
- package/lib/common/performance/stopwatch.d.ts.map +1 -1
- package/lib/common/performance/stopwatch.js +34 -11
- package/lib/common/performance/stopwatch.js.map +1 -1
- package/lib/common/promise-util.d.ts +4 -0
- package/lib/common/promise-util.d.ts.map +1 -1
- package/lib/common/promise-util.js +11 -1
- package/lib/common/promise-util.js.map +1 -1
- package/lib/common/promise-util.spec.js +26 -12
- package/lib/common/promise-util.spec.js.map +1 -1
- package/lib/common/types.d.ts +4 -0
- package/lib/common/types.d.ts.map +1 -1
- package/lib/common/types.js +16 -1
- package/lib/common/types.js.map +1 -1
- package/lib/common/uri.d.ts +1 -0
- package/lib/common/uri.d.ts.map +1 -1
- package/lib/common/uri.js +3 -0
- package/lib/common/uri.js.map +1 -1
- package/lib/electron-browser/menu/electron-context-menu-renderer.js +2 -2
- package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.js +2 -2
- package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
- package/lib/electron-main/electron-main-application.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application.js +3 -0
- package/lib/electron-main/electron-main-application.js.map +1 -1
- package/lib/node/backend-application.d.ts +5 -1
- package/lib/node/backend-application.d.ts.map +1 -1
- package/lib/node/backend-application.js +28 -5
- package/lib/node/backend-application.js.map +1 -1
- package/lib/node/logger-cli-contribution.spec.js +1 -1
- package/lib/node/logger-cli-contribution.spec.js.map +1 -1
- package/lib/node/main.d.ts +2 -5
- package/lib/node/main.d.ts.map +1 -1
- package/lib/node/main.js.map +1 -1
- package/lib/node/performance/node-stopwatch.js +1 -1
- package/lib/node/performance/node-stopwatch.js.map +1 -1
- package/package.json +7 -7
- package/src/browser/common-frontend-contribution.ts +107 -17
- package/src/browser/context-menu-renderer.ts +5 -0
- package/src/browser/label-parser.spec.ts +38 -0
- package/src/browser/label-parser.ts +15 -0
- package/src/browser/menu/browser-context-menu-renderer.ts +2 -2
- package/src/browser/menu/browser-menu-plugin.ts +2 -2
- package/src/browser/performance/frontend-stopwatch.ts +5 -2
- package/src/browser/preferences/preference-contribution.ts +49 -24
- package/src/browser/saveable.ts +41 -2
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +94 -8
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts +28 -1
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +87 -8
- package/src/browser/shell/tab-bars.ts +8 -1
- package/src/browser/style/tabs.css +32 -2
- package/src/browser/tree/tree-model.ts +8 -0
- package/src/browser/tree/tree-widget.tsx +66 -0
- package/src/browser/tree/tree.ts +27 -0
- package/src/browser/widgets/enhanced-preview-widget.ts +27 -0
- package/src/browser/window-contribution.ts +1 -1
- package/src/common/array-utils.ts +20 -0
- package/src/common/event.ts +12 -2
- package/src/common/i18n/nls.metadata.json +5616 -5359
- package/src/common/menu/menu-model-registry.ts +50 -0
- package/src/common/performance/measurement.ts +26 -0
- package/src/common/performance/stopwatch.ts +38 -12
- package/src/common/promise-util.spec.ts +43 -12
- package/src/common/promise-util.ts +12 -0
- package/src/common/types.ts +17 -0
- package/src/common/uri.ts +4 -0
- package/src/electron-browser/menu/electron-context-menu-renderer.ts +2 -2
- package/src/electron-browser/menu/electron-main-menu-factory.ts +2 -2
- package/src/electron-main/electron-main-application.ts +3 -0
- package/src/node/backend-application.ts +30 -5
- package/src/node/logger-cli-contribution.spec.ts +1 -1
- package/src/node/main.ts +1 -6
- package/src/node/performance/node-stopwatch.ts +1 -1
|
@@ -254,6 +254,56 @@ export class MenuModelRegistry {
|
|
|
254
254
|
return this.findGroup(menuPath);
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Checks the given menu model whether it will show a menu with a single submenu.
|
|
259
|
+
*
|
|
260
|
+
* @param fullMenuModel the menu model to analyze
|
|
261
|
+
* @param menuPath the menu's path
|
|
262
|
+
* @returns if the menu will show a single submenu this returns a menu that will show the child elements of the submenu,
|
|
263
|
+
* otherwise the given `fullMenuModel` is return
|
|
264
|
+
*/
|
|
265
|
+
removeSingleRootNode(fullMenuModel: MutableCompoundMenuNode, menuPath: MenuPath): CompoundMenuNode {
|
|
266
|
+
// check whether all children are compound menus and that there is only one child that has further children
|
|
267
|
+
if (!this.allChildrenCompound(fullMenuModel.children)) {
|
|
268
|
+
return fullMenuModel;
|
|
269
|
+
}
|
|
270
|
+
let nonEmptyNode = undefined;
|
|
271
|
+
for (const child of fullMenuModel.children) {
|
|
272
|
+
if (!this.isEmpty(child.children || [])) {
|
|
273
|
+
if (nonEmptyNode === undefined) {
|
|
274
|
+
nonEmptyNode = child;
|
|
275
|
+
} else {
|
|
276
|
+
return fullMenuModel;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (CompoundMenuNode.is(nonEmptyNode) && nonEmptyNode.children.length === 1 && CompoundMenuNode.is(nonEmptyNode.children[0])) {
|
|
282
|
+
nonEmptyNode = nonEmptyNode.children[0];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return CompoundMenuNode.is(nonEmptyNode) ? nonEmptyNode : fullMenuModel;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
protected allChildrenCompound(children: ReadonlyArray<MenuNode>): boolean {
|
|
289
|
+
return children.every(CompoundMenuNode.is);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
protected isEmpty(children: ReadonlyArray<MenuNode>): boolean {
|
|
293
|
+
if (children.length === 0) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
if (!this.allChildrenCompound(children)) {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
for (const child of children) {
|
|
300
|
+
if (!this.isEmpty(child.children || [])) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
|
|
257
307
|
/**
|
|
258
308
|
* Returns the {@link MenuPath path} at which a given menu node can be accessed from this registry, if it can be determined.
|
|
259
309
|
* Returns `undefined` if the `parent` of any node in the chain is unknown.
|
|
@@ -101,4 +101,30 @@ export interface MeasurementOptions {
|
|
|
101
101
|
* @see {@link thresholdLogLevel}
|
|
102
102
|
*/
|
|
103
103
|
thresholdMillis?: number;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Flag to indicate whether the stopwatch should store measurement results for later retrieval.
|
|
107
|
+
* For example the cache can be used to retrieve measurements which were taken during startup before a listener had a chance to register.
|
|
108
|
+
*/
|
|
109
|
+
storeResults?: boolean
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Captures the result of a {@link Measurement} in a serializable format.
|
|
114
|
+
*/
|
|
115
|
+
export interface MeasurementResult {
|
|
116
|
+
/** The measurement name. This may show up in the performance measurement framework appropriate to the application context. */
|
|
117
|
+
name: string;
|
|
118
|
+
|
|
119
|
+
/** The time when the measurement recording has been started */
|
|
120
|
+
startTime: number;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The elapsed time measured, if it has been {@link stop stopped} and measured, or `NaN` if the platform disabled
|
|
124
|
+
* performance measurement.
|
|
125
|
+
*/
|
|
126
|
+
elapsed: number;
|
|
127
|
+
|
|
128
|
+
/** An optional label for the application the start of which (in real time) is the basis of all measurements. */
|
|
129
|
+
owner?: string;
|
|
104
130
|
}
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
import { inject, injectable } from 'inversify';
|
|
20
20
|
import { ILogger, LogLevel } from '../logger';
|
|
21
21
|
import { MaybePromise } from '../types';
|
|
22
|
-
import { Measurement, MeasurementOptions } from './measurement';
|
|
22
|
+
import { Measurement, MeasurementOptions, MeasurementResult } from './measurement';
|
|
23
|
+
import { Emitter, Event } from '../event';
|
|
23
24
|
|
|
24
25
|
/** The default log level for measurements that are not otherwise configured with a default. */
|
|
25
26
|
const DEFAULT_LOG_LEVEL = LogLevel.INFO;
|
|
@@ -50,10 +51,20 @@ export abstract class Stopwatch {
|
|
|
50
51
|
@inject(ILogger)
|
|
51
52
|
protected readonly logger: ILogger;
|
|
52
53
|
|
|
54
|
+
protected _storedMeasurements: MeasurementResult[] = [];
|
|
55
|
+
|
|
56
|
+
protected onDidAddMeasurementResultEmitter = new Emitter<MeasurementResult>();
|
|
57
|
+
get onDidAddMeasurementResult(): Event<MeasurementResult> {
|
|
58
|
+
return this.onDidAddMeasurementResultEmitter.event;
|
|
59
|
+
}
|
|
60
|
+
|
|
53
61
|
constructor(protected readonly defaultLogOptions: LogOptions) {
|
|
54
62
|
if (!defaultLogOptions.defaultLogLevel) {
|
|
55
63
|
defaultLogOptions.defaultLogLevel = DEFAULT_LOG_LEVEL;
|
|
56
64
|
}
|
|
65
|
+
if (defaultLogOptions.storeResults === undefined) {
|
|
66
|
+
defaultLogOptions.storeResults = true;
|
|
67
|
+
}
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
/**
|
|
@@ -91,25 +102,36 @@ export abstract class Stopwatch {
|
|
|
91
102
|
return result;
|
|
92
103
|
}
|
|
93
104
|
|
|
94
|
-
protected createMeasurement(name: string,
|
|
105
|
+
protected createMeasurement(name: string, measure: () => { startTime: number, duration: number }, options?: MeasurementOptions): Measurement {
|
|
95
106
|
const logOptions = this.mergeLogOptions(options);
|
|
96
107
|
|
|
97
|
-
const
|
|
108
|
+
const measurement: Measurement = {
|
|
98
109
|
name,
|
|
99
110
|
stop: () => {
|
|
100
|
-
if (
|
|
101
|
-
|
|
111
|
+
if (measurement.elapsed === undefined) {
|
|
112
|
+
const { startTime, duration } = measure();
|
|
113
|
+
measurement.elapsed = duration;
|
|
114
|
+
const result: MeasurementResult = {
|
|
115
|
+
name,
|
|
116
|
+
elapsed: duration,
|
|
117
|
+
startTime,
|
|
118
|
+
owner: logOptions.owner
|
|
119
|
+
};
|
|
120
|
+
if (logOptions.storeResults) {
|
|
121
|
+
this._storedMeasurements.push(result);
|
|
122
|
+
}
|
|
123
|
+
this.onDidAddMeasurementResultEmitter.fire(result);
|
|
102
124
|
}
|
|
103
|
-
return
|
|
125
|
+
return measurement.elapsed;
|
|
104
126
|
},
|
|
105
|
-
log: (activity: string, ...optionalArgs: any[]) => this.log(
|
|
106
|
-
debug: (activity: string, ...optionalArgs: any[]) => this.log(
|
|
107
|
-
info: (activity: string, ...optionalArgs: any[]) => this.log(
|
|
108
|
-
warn: (activity: string, ...optionalArgs: any[]) => this.log(
|
|
109
|
-
error: (activity: string, ...optionalArgs: any[]) => this.log(
|
|
127
|
+
log: (activity: string, ...optionalArgs: any[]) => this.log(measurement, activity, this.atLevel(logOptions, undefined, optionalArgs)),
|
|
128
|
+
debug: (activity: string, ...optionalArgs: any[]) => this.log(measurement, activity, this.atLevel(logOptions, LogLevel.DEBUG, optionalArgs)),
|
|
129
|
+
info: (activity: string, ...optionalArgs: any[]) => this.log(measurement, activity, this.atLevel(logOptions, LogLevel.INFO, optionalArgs)),
|
|
130
|
+
warn: (activity: string, ...optionalArgs: any[]) => this.log(measurement, activity, this.atLevel(logOptions, LogLevel.WARN, optionalArgs)),
|
|
131
|
+
error: (activity: string, ...optionalArgs: any[]) => this.log(measurement, activity, this.atLevel(logOptions, LogLevel.ERROR, optionalArgs)),
|
|
110
132
|
};
|
|
111
133
|
|
|
112
|
-
return
|
|
134
|
+
return measurement;
|
|
113
135
|
}
|
|
114
136
|
|
|
115
137
|
protected mergeLogOptions(logOptions?: Partial<LogOptions>): LogOptions {
|
|
@@ -154,4 +176,8 @@ export abstract class Stopwatch {
|
|
|
154
176
|
this.logger.log(level, `${whatWasMeasured}: ${elapsed.toFixed(1)} ms [${timeFromStart}]`, ...(options.arguments ?? []));
|
|
155
177
|
}
|
|
156
178
|
|
|
179
|
+
get storedMeasurements(): ReadonlyArray<MeasurementResult> {
|
|
180
|
+
return this._storedMeasurements;
|
|
181
|
+
}
|
|
182
|
+
|
|
157
183
|
}
|
|
@@ -13,22 +13,19 @@
|
|
|
13
13
|
//
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
|
-
import * as assert from 'assert';
|
|
17
|
-
import { waitForEvent } from './promise-util';
|
|
16
|
+
import * as assert from 'assert/strict';
|
|
17
|
+
import { firstTrue, waitForEvent } from './promise-util';
|
|
18
18
|
import { Emitter } from './event';
|
|
19
|
+
import { CancellationError } from './cancellation';
|
|
19
20
|
|
|
20
21
|
describe('promise-util', () => {
|
|
21
|
-
it('should time out', async () => {
|
|
22
|
-
const emitter = new Emitter<string>();
|
|
23
|
-
try {
|
|
24
|
-
await waitForEvent(emitter.event, 1000);
|
|
25
|
-
assert.fail('did not time out');
|
|
26
|
-
} catch (e) {
|
|
27
|
-
// OK
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
22
|
|
|
31
|
-
describe('
|
|
23
|
+
describe('waitForEvent', () => {
|
|
24
|
+
it('should time out', async () => {
|
|
25
|
+
const emitter = new Emitter<string>();
|
|
26
|
+
await assert.rejects(waitForEvent(emitter.event, 1000), reason => reason instanceof CancellationError);
|
|
27
|
+
});
|
|
28
|
+
|
|
32
29
|
it('should get event', async () => {
|
|
33
30
|
const emitter = new Emitter<string>();
|
|
34
31
|
setTimeout(() => {
|
|
@@ -38,4 +35,38 @@ describe('promise-util', () => {
|
|
|
38
35
|
});
|
|
39
36
|
});
|
|
40
37
|
|
|
38
|
+
describe('firstTrue', () => {
|
|
39
|
+
it('should resolve to false when the promises arg is empty', async () => {
|
|
40
|
+
const actual = await firstTrue();
|
|
41
|
+
assert.strictEqual(actual, false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should resolve to true when the first promise resolves to true', async () => {
|
|
45
|
+
const signals: string[] = [];
|
|
46
|
+
const createPromise = (signal: string, timeout: number, result: boolean) =>
|
|
47
|
+
new Promise<boolean>(resolve => setTimeout(() => {
|
|
48
|
+
signals.push(signal);
|
|
49
|
+
resolve(result);
|
|
50
|
+
}, timeout));
|
|
51
|
+
const actual = await firstTrue(
|
|
52
|
+
createPromise('a', 10, false),
|
|
53
|
+
createPromise('b', 20, false),
|
|
54
|
+
createPromise('c', 30, true),
|
|
55
|
+
createPromise('d', 40, false),
|
|
56
|
+
createPromise('e', 50, true)
|
|
57
|
+
);
|
|
58
|
+
assert.strictEqual(actual, true);
|
|
59
|
+
assert.deepStrictEqual(signals, ['a', 'b', 'c']);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should reject when one of the promises rejects', async () => {
|
|
63
|
+
await assert.rejects(firstTrue(
|
|
64
|
+
new Promise<boolean>(resolve => setTimeout(() => resolve(false), 10)),
|
|
65
|
+
new Promise<boolean>(resolve => setTimeout(() => resolve(false), 20)),
|
|
66
|
+
new Promise<boolean>((_, reject) => setTimeout(() => reject(new Error('my test error')), 30)),
|
|
67
|
+
new Promise<boolean>(resolve => setTimeout(() => resolve(true), 40)),
|
|
68
|
+
), /Error: my test error/);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
41
72
|
});
|
|
@@ -129,3 +129,15 @@ export function waitForEvent<T>(event: Event<T>, ms: number, thisArg?: any, disp
|
|
|
129
129
|
export function isThenable<T>(obj: unknown): obj is Promise<T> {
|
|
130
130
|
return isObject<Promise<unknown>>(obj) && isFunction(obj.then);
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Returns with a promise that waits until the first promise resolves to `true`.
|
|
135
|
+
*/
|
|
136
|
+
// Based on https://stackoverflow.com/a/51160727/5529090
|
|
137
|
+
export function firstTrue(...promises: readonly Promise<boolean>[]): Promise<boolean> {
|
|
138
|
+
const newPromises = promises.map(promise => new Promise<boolean>(
|
|
139
|
+
(resolve, reject) => promise.then(result => result && resolve(true), reject)
|
|
140
|
+
));
|
|
141
|
+
newPromises.push(Promise.all(promises).then(() => false));
|
|
142
|
+
return Promise.race(newPromises);
|
|
143
|
+
}
|
package/src/common/types.ts
CHANGED
|
@@ -56,6 +56,23 @@ export function isFunction<T extends (...args: unknown[]) => unknown>(value: unk
|
|
|
56
56
|
return typeof value === 'function';
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @returns whether the provided parameter is an empty JavaScript Object or not.
|
|
61
|
+
*/
|
|
62
|
+
export function isEmptyObject(obj: unknown): obj is object {
|
|
63
|
+
if (!isObject(obj)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const key in obj) {
|
|
68
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
export function isObject<T extends object>(value: unknown): value is UnknownObject<T> {
|
|
60
77
|
// eslint-disable-next-line no-null/no-null
|
|
61
78
|
return typeof value === 'object' && value !== null;
|
package/src/common/uri.ts
CHANGED
|
@@ -100,8 +100,8 @@ export class ElectronContextMenuRenderer extends BrowserContextMenuRenderer {
|
|
|
100
100
|
|
|
101
101
|
protected override doRender(options: RenderContextMenuOptions): ContextMenuAccess {
|
|
102
102
|
if (this.useNativeStyle) {
|
|
103
|
-
const { menuPath, anchor, args, onHide, context, contextKeyService } = options;
|
|
104
|
-
const menu = this.electronMenuFactory.createElectronContextMenu(menuPath, args, context, contextKeyService);
|
|
103
|
+
const { menuPath, anchor, args, onHide, context, contextKeyService, skipSingleRootNode } = options;
|
|
104
|
+
const menu = this.electronMenuFactory.createElectronContextMenu(menuPath, args, context, contextKeyService, skipSingleRootNode);
|
|
105
105
|
const { x, y } = coordinateFromAnchor(anchor);
|
|
106
106
|
|
|
107
107
|
const menuHandle = window.electronTheiaCore.popup(menu, x, y, () => {
|
|
@@ -125,8 +125,8 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory {
|
|
|
125
125
|
return undefined;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
createElectronContextMenu(menuPath: MenuPath, args?: any[], context?: HTMLElement, contextKeyService?: ContextMatcher): MenuDto[] {
|
|
129
|
-
const menuModel = this.menuProvider.getMenu(menuPath);
|
|
128
|
+
createElectronContextMenu(menuPath: MenuPath, args?: any[], context?: HTMLElement, contextKeyService?: ContextMatcher, skipSingleRootNode?: boolean): MenuDto[] {
|
|
129
|
+
const menuModel = skipSingleRootNode ? this.menuProvider.removeSingleRootNode(this.menuProvider.getMenu(menuPath), menuPath) : this.menuProvider.getMenu(menuPath);
|
|
130
130
|
return this.fillMenuTemplate([], menuModel, args, { showDisabled: true, context, rootMenuPath: menuPath, contextKeyService });
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -507,6 +507,9 @@ export class ElectronMainApplication {
|
|
|
507
507
|
backendProcess.on('error', error => {
|
|
508
508
|
reject(error);
|
|
509
509
|
});
|
|
510
|
+
backendProcess.on('exit', () => {
|
|
511
|
+
reject(new Error('backend process exited'));
|
|
512
|
+
});
|
|
510
513
|
app.on('quit', () => {
|
|
511
514
|
// Only issue a kill signal if the backend process is running.
|
|
512
515
|
// eslint-disable-next-line no-null/no-null
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
+
import * as dns from 'dns';
|
|
17
18
|
import * as path from 'path';
|
|
18
19
|
import * as http from 'http';
|
|
19
20
|
import * as https from 'https';
|
|
@@ -29,6 +30,8 @@ import { AddressInfo } from 'net';
|
|
|
29
30
|
import { ApplicationPackage } from '@theia/application-package';
|
|
30
31
|
import { ProcessUtils } from './process-utils';
|
|
31
32
|
|
|
33
|
+
export type DnsResultOrder = 'ipv4first' | 'verbatim' | 'nodeDefault';
|
|
34
|
+
|
|
32
35
|
const APP_PROJECT_PATH = 'app-project-path';
|
|
33
36
|
|
|
34
37
|
const TIMER_WARNING_THRESHOLD = 50;
|
|
@@ -36,6 +39,7 @@ const TIMER_WARNING_THRESHOLD = 50;
|
|
|
36
39
|
const DEFAULT_PORT = environment.electron.is() ? 0 : 3000;
|
|
37
40
|
const DEFAULT_HOST = 'localhost';
|
|
38
41
|
const DEFAULT_SSL = false;
|
|
42
|
+
const DEFAULT_DNS_DEFAULT_RESULT_ORDER: DnsResultOrder = 'ipv4first';
|
|
39
43
|
|
|
40
44
|
export const BackendApplicationServer = Symbol('BackendApplicationServer');
|
|
41
45
|
/**
|
|
@@ -107,6 +111,7 @@ export class BackendApplicationCliContribution implements CliContribution {
|
|
|
107
111
|
|
|
108
112
|
port: number;
|
|
109
113
|
hostname: string | undefined;
|
|
114
|
+
dnsDefaultResultOrder: DnsResultOrder = DEFAULT_DNS_DEFAULT_RESULT_ORDER;
|
|
110
115
|
ssl: boolean | undefined;
|
|
111
116
|
cert: string | undefined;
|
|
112
117
|
certkey: string | undefined;
|
|
@@ -119,6 +124,12 @@ export class BackendApplicationCliContribution implements CliContribution {
|
|
|
119
124
|
conf.option('cert', { description: 'Path to SSL certificate.', type: 'string' });
|
|
120
125
|
conf.option('certkey', { description: 'Path to SSL certificate key.', type: 'string' });
|
|
121
126
|
conf.option(APP_PROJECT_PATH, { description: 'Sets the application project directory', default: this.appProjectPath() });
|
|
127
|
+
conf.option('dnsDefaultResultOrder', {
|
|
128
|
+
type: 'string',
|
|
129
|
+
description: 'Configure Node\'s DNS resolver default behavior, see https://nodejs.org/docs/latest-v18.x/api/dns.html#dnssetdefaultresultorderorder',
|
|
130
|
+
choices: ['ipv4first', 'verbatim', 'nodeDefault'],
|
|
131
|
+
default: DEFAULT_DNS_DEFAULT_RESULT_ORDER
|
|
132
|
+
});
|
|
122
133
|
}
|
|
123
134
|
|
|
124
135
|
setArguments(args: yargs.Arguments): void {
|
|
@@ -128,6 +139,7 @@ export class BackendApplicationCliContribution implements CliContribution {
|
|
|
128
139
|
this.cert = args.cert as string;
|
|
129
140
|
this.certkey = args.certkey as string;
|
|
130
141
|
this.projectPath = args[APP_PROJECT_PATH] as string;
|
|
142
|
+
this.dnsDefaultResultOrder = args.dnsDefaultResultOrder as DnsResultOrder;
|
|
131
143
|
}
|
|
132
144
|
|
|
133
145
|
protected appProjectPath(): string {
|
|
@@ -234,9 +246,13 @@ export class BackendApplication {
|
|
|
234
246
|
this.app.use(...handlers);
|
|
235
247
|
}
|
|
236
248
|
|
|
237
|
-
async start(
|
|
238
|
-
|
|
239
|
-
|
|
249
|
+
async start(port?: number, hostname?: string): Promise<http.Server | https.Server> {
|
|
250
|
+
hostname ??= this.cliParams.hostname;
|
|
251
|
+
port ??= this.cliParams.port;
|
|
252
|
+
|
|
253
|
+
if (this.cliParams.dnsDefaultResultOrder !== 'nodeDefault') {
|
|
254
|
+
dns.setDefaultResultOrder(this.cliParams.dnsDefaultResultOrder);
|
|
255
|
+
}
|
|
240
256
|
|
|
241
257
|
const deferred = new Deferred<http.Server | https.Server>();
|
|
242
258
|
let server: http.Server | https.Server;
|
|
@@ -279,8 +295,10 @@ export class BackendApplication {
|
|
|
279
295
|
});
|
|
280
296
|
|
|
281
297
|
server.listen(port, hostname, () => {
|
|
282
|
-
|
|
283
|
-
|
|
298
|
+
// address should be defined at this point
|
|
299
|
+
const address = server.address()!;
|
|
300
|
+
const url = typeof address === 'string' ? address : this.getHttpUrl(address, this.cliParams.ssl);
|
|
301
|
+
console.info(`Theia app listening on ${url}.`);
|
|
284
302
|
deferred.resolve(server);
|
|
285
303
|
});
|
|
286
304
|
|
|
@@ -301,6 +319,13 @@ export class BackendApplication {
|
|
|
301
319
|
return this.stopwatch.startAsync('server', 'Finished starting backend application', () => deferred.promise);
|
|
302
320
|
}
|
|
303
321
|
|
|
322
|
+
protected getHttpUrl({ address, port, family }: AddressInfo, ssl?: boolean): string {
|
|
323
|
+
const scheme = ssl ? 'https' : 'http';
|
|
324
|
+
return family.toLowerCase() === 'ipv6'
|
|
325
|
+
? `${scheme}://[${address}]:${port}`
|
|
326
|
+
: `${scheme}://${address}:${port}`;
|
|
327
|
+
}
|
|
328
|
+
|
|
304
329
|
protected onStop(): void {
|
|
305
330
|
console.info('>>> Stopping backend contributions...');
|
|
306
331
|
for (const contrib of this.contributionsProvider.getContributions()) {
|
|
@@ -137,7 +137,7 @@ describe('log-level-cli-contribution', () => {
|
|
|
137
137
|
|
|
138
138
|
const args: yargs.Arguments = yargs.parse(['--log-config', file.path]);
|
|
139
139
|
await cli.setArguments(args);
|
|
140
|
-
sinon.assert.calledWithMatch(consoleErrorSpy, '
|
|
140
|
+
sinon.assert.calledWithMatch(consoleErrorSpy, 'Error reading log config file');
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
// Skip this test because it is flaky, sometimes we don't receive the event.
|
package/src/node/main.ts
CHANGED
|
@@ -26,12 +26,7 @@ process.on('unhandledRejection', (reason, promise) => {
|
|
|
26
26
|
throw reason;
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
export
|
|
30
|
-
readonly port: number;
|
|
31
|
-
readonly address: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function start(serverModule: MaybePromise<http.Server | https.Server>): Promise<Address> {
|
|
29
|
+
export async function start(serverModule: MaybePromise<http.Server | https.Server>): Promise<AddressInfo> {
|
|
35
30
|
const server = await serverModule;
|
|
36
31
|
return server.address() as AddressInfo;
|
|
37
32
|
}
|