@theia/core 1.45.0 → 1.46.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 +4 -0
- package/i18n/nls.de.json +4 -0
- package/i18n/nls.es.json +4 -0
- package/i18n/nls.fr.json +4 -0
- package/i18n/nls.hu.json +4 -0
- package/i18n/nls.it.json +4 -0
- package/i18n/nls.ja.json +4 -0
- package/i18n/nls.json +4 -0
- package/i18n/nls.pl.json +4 -0
- package/i18n/nls.pt-br.json +4 -0
- package/i18n/nls.pt-pt.json +4 -0
- package/i18n/nls.ru.json +4 -0
- package/i18n/nls.zh-cn.json +4 -0
- package/lib/browser/browser.d.ts +3 -0
- package/lib/browser/browser.d.ts.map +1 -1
- package/lib/browser/browser.js +5 -1
- package/lib/browser/browser.js.map +1 -1
- package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/common-frontend-contribution.js +3 -3
- package/lib/browser/common-frontend-contribution.js.map +1 -1
- package/lib/browser/frontend-application-module.d.ts.map +1 -1
- package/lib/browser/frontend-application-module.js +2 -1
- package/lib/browser/frontend-application-module.js.map +1 -1
- package/lib/browser/messaging/ws-connection-source.d.ts +2 -0
- package/lib/browser/messaging/ws-connection-source.d.ts.map +1 -1
- package/lib/browser/messaging/ws-connection-source.js +38 -22
- package/lib/browser/messaging/ws-connection-source.js.map +1 -1
- package/lib/browser/tree/index.d.ts +1 -0
- package/lib/browser/tree/index.d.ts.map +1 -1
- package/lib/browser/tree/index.js +1 -0
- package/lib/browser/tree/index.js.map +1 -1
- package/lib/browser/tree/tree-preference.d.ts +11 -0
- package/lib/browser/tree/tree-preference.d.ts.map +1 -0
- package/lib/browser/tree/tree-preference.js +47 -0
- package/lib/browser/tree/tree-preference.js.map +1 -0
- package/lib/browser/tree/tree-widget.d.ts +6 -3
- package/lib/browser/tree/tree-widget.d.ts.map +1 -1
- package/lib/browser/tree/tree-widget.js +24 -3
- package/lib/browser/tree/tree-widget.js.map +1 -1
- package/lib/browser/tree/tree.d.ts.map +1 -1
- package/lib/browser/tree/tree.js +4 -1
- package/lib/browser/tree/tree.js.map +1 -1
- package/lib/browser/widget-manager.d.ts +9 -0
- package/lib/browser/widget-manager.d.ts.map +1 -1
- package/lib/browser/widget-manager.js +23 -0
- package/lib/browser/widget-manager.js.map +1 -1
- package/lib/browser/widgets/select-component.d.ts.map +1 -1
- package/lib/browser/widgets/select-component.js +1 -0
- package/lib/browser/widgets/select-component.js.map +1 -1
- package/lib/browser-only/frontend-only-application-module.d.ts +5 -0
- package/lib/browser-only/frontend-only-application-module.d.ts.map +1 -0
- package/lib/browser-only/frontend-only-application-module.js +115 -0
- package/lib/browser-only/frontend-only-application-module.js.map +1 -0
- package/lib/browser-only/i18n/i18n-frontend-only-module.d.ts +4 -0
- package/lib/browser-only/i18n/i18n-frontend-only-module.d.ts.map +1 -0
- package/lib/browser-only/i18n/i18n-frontend-only-module.js +35 -0
- package/lib/browser-only/i18n/i18n-frontend-only-module.js.map +1 -0
- package/lib/browser-only/logger-frontend-only-module.d.ts +3 -0
- package/lib/browser-only/logger-frontend-only-module.d.ts.map +1 -0
- package/lib/browser-only/logger-frontend-only-module.js +61 -0
- package/lib/browser-only/logger-frontend-only-module.js.map +1 -0
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.d.ts +14 -0
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.d.ts.map +1 -0
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.js +57 -0
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.js.map +1 -0
- package/lib/browser-only/messaging/messaging-frontend-only-module.d.ts +3 -0
- package/lib/browser-only/messaging/messaging-frontend-only-module.d.ts.map +1 -0
- package/lib/browser-only/messaging/messaging-frontend-only-module.js +48 -0
- package/lib/browser-only/messaging/messaging-frontend-only-module.js.map +1 -0
- package/lib/browser-only/preload/frontend-only-preload-module.d.ts +4 -0
- package/lib/browser-only/preload/frontend-only-preload-module.d.ts.map +1 -0
- package/lib/browser-only/preload/frontend-only-preload-module.js +52 -0
- package/lib/browser-only/preload/frontend-only-preload-module.js.map +1 -0
- package/lib/common/application-protocol.d.ts +1 -0
- package/lib/common/application-protocol.d.ts.map +1 -1
- package/lib/common/disposable.d.ts +9 -0
- package/lib/common/disposable.d.ts.map +1 -1
- package/lib/common/disposable.js +26 -1
- package/lib/common/disposable.js.map +1 -1
- package/lib/{node → common}/file-uri.d.ts +1 -1
- package/lib/common/file-uri.d.ts.map +1 -0
- package/lib/{node → common}/file-uri.js +2 -2
- package/lib/common/file-uri.js.map +1 -0
- package/lib/common/menu/menu-model-registry.d.ts +2 -2
- package/lib/common/menu/menu-model-registry.d.ts.map +1 -1
- package/lib/common/menu/menu-model-registry.js +16 -2
- package/lib/common/menu/menu-model-registry.js.map +1 -1
- package/lib/common/menu/menu.spec.js +28 -1
- package/lib/common/menu/menu.spec.js.map +1 -1
- package/lib/common/message-rpc/msg-pack-extension-manager.d.ts +1 -1
- package/lib/common/message-rpc/msg-pack-extension-manager.js +1 -1
- package/lib/common/message-rpc/rpc-protocol.d.ts +3 -1
- package/lib/common/message-rpc/rpc-protocol.d.ts.map +1 -1
- package/lib/common/message-rpc/rpc-protocol.js +19 -1
- package/lib/common/message-rpc/rpc-protocol.js.map +1 -1
- package/lib/common/messaging/proxy-factory.d.ts.map +1 -1
- package/lib/common/messaging/proxy-factory.js +1 -8
- package/lib/common/messaging/proxy-factory.js.map +1 -1
- package/lib/common/quick-pick-service.d.ts +1 -1
- package/lib/common/quick-pick-service.d.ts.map +1 -1
- package/lib/common/uuid.d.ts +6 -0
- package/lib/common/uuid.d.ts.map +1 -1
- package/lib/common/uuid.js +13 -1
- package/lib/common/uuid.js.map +1 -1
- package/lib/electron-main/electron-main-application.js +1 -1
- package/lib/electron-main/electron-main-application.js.map +1 -1
- package/lib/electron-main/theia-electron-window.js +1 -1
- package/lib/electron-main/theia-electron-window.js.map +1 -1
- package/lib/node/application-server.d.ts +1 -0
- package/lib/node/application-server.d.ts.map +1 -1
- package/lib/node/application-server.js +7 -1
- package/lib/node/application-server.js.map +1 -1
- package/lib/node/env-variables/env-variables-server.js +1 -1
- package/lib/node/env-variables/env-variables-server.js.map +1 -1
- package/lib/node/file-uri.spec.js +1 -1
- package/lib/node/file-uri.spec.js.map +1 -1
- package/lib/node/index.d.ts +1 -1
- package/lib/node/index.d.ts.map +1 -1
- package/lib/node/index.js +1 -1
- package/lib/node/index.js.map +1 -1
- package/lib/node/messaging/websocket-frontend-connection-service.d.ts +2 -1
- package/lib/node/messaging/websocket-frontend-connection-service.d.ts.map +1 -1
- package/lib/node/messaging/websocket-frontend-connection-service.js +8 -3
- package/lib/node/messaging/websocket-frontend-connection-service.js.map +1 -1
- package/package.json +10 -6
- package/src/browser/browser.ts +6 -1
- package/src/browser/common-frontend-contribution.ts +3 -3
- package/src/browser/frontend-application-module.ts +2 -1
- package/src/browser/messaging/ws-connection-source.ts +41 -21
- package/src/browser/style/select-component.css +12 -13
- package/src/browser/tree/index.ts +1 -0
- package/src/browser/tree/tree-preference.ts +50 -0
- package/src/browser/tree/tree-widget.tsx +24 -5
- package/src/browser/tree/tree.ts +2 -1
- package/src/browser/widget-manager.ts +25 -0
- package/src/browser/widgets/select-component.tsx +1 -0
- package/src/browser-only/frontend-only-application-module.ts +115 -0
- package/src/browser-only/i18n/i18n-frontend-only-module.ts +37 -0
- package/src/browser-only/logger-frontend-only-module.ts +63 -0
- package/src/browser-only/messaging/frontend-only-service-connection-provider.ts +39 -0
- package/src/browser-only/messaging/messaging-frontend-only-module.ts +42 -0
- package/src/browser-only/preload/frontend-only-preload-module.ts +49 -0
- package/src/common/application-protocol.ts +1 -0
- package/src/common/disposable.ts +25 -0
- package/src/{node → common}/file-uri.ts +2 -2
- package/src/common/menu/menu-model-registry.ts +22 -5
- package/src/common/menu/menu.spec.ts +30 -2
- package/src/common/message-rpc/msg-pack-extension-manager.ts +1 -1
- package/src/common/message-rpc/rpc-protocol.ts +22 -2
- package/src/common/messaging/proxy-factory.ts +1 -8
- package/src/common/quick-pick-service.ts +1 -1
- package/src/common/uuid.ts +13 -0
- package/src/electron-main/electron-main-application.ts +1 -1
- package/src/electron-main/theia-electron-window.ts +1 -1
- package/src/node/application-server.ts +8 -1
- package/src/node/env-variables/env-variables-server.ts +1 -1
- package/src/node/file-uri.spec.ts +1 -1
- package/src/node/index.ts +1 -1
- package/src/node/messaging/websocket-frontend-connection-service.ts +10 -5
- package/lib/node/file-uri.d.ts.map +0 -1
- package/lib/node/file-uri.js.map +0 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 STMicroelectronics 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 { interfaces } from 'inversify';
|
|
18
|
+
import { PreferenceProxy, PreferenceContribution, PreferenceSchema } from '../preferences';
|
|
19
|
+
import { PreferenceProxyFactory } from '../preferences/injectable-preference-proxy';
|
|
20
|
+
import { nls } from '../../common/nls';
|
|
21
|
+
|
|
22
|
+
export const PREFERENCE_NAME_TREE_INDENT = 'workbench.tree.indent';
|
|
23
|
+
|
|
24
|
+
export const treePreferencesSchema: PreferenceSchema = {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
[PREFERENCE_NAME_TREE_INDENT]: {
|
|
28
|
+
description: nls.localizeByDefault('Controls tree indentation in pixels.'),
|
|
29
|
+
type: 'number',
|
|
30
|
+
default: 8,
|
|
31
|
+
minimum: 4,
|
|
32
|
+
maximum: 40
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export class TreeConfiguration {
|
|
38
|
+
[PREFERENCE_NAME_TREE_INDENT]: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const TreePreferences = Symbol('treePreferences');
|
|
42
|
+
export type TreePreferences = PreferenceProxy<TreeConfiguration>;
|
|
43
|
+
|
|
44
|
+
export function bindTreePreferences(bind: interfaces.Bind): void {
|
|
45
|
+
bind(TreePreferences).toDynamicValue(ctx => {
|
|
46
|
+
const factory = ctx.container.get<PreferenceProxyFactory>(PreferenceProxyFactory);
|
|
47
|
+
return factory(treePreferencesSchema);
|
|
48
|
+
}).inSingletonScope();
|
|
49
|
+
bind(PreferenceContribution).toConstantValue({ schema: treePreferencesSchema });
|
|
50
|
+
}
|
|
@@ -43,6 +43,8 @@ import { LabelProvider } from '../label-provider';
|
|
|
43
43
|
import { CorePreferences } from '../core-preferences';
|
|
44
44
|
import { TreeFocusService } from './tree-focus-service';
|
|
45
45
|
import { useEffect } from 'react';
|
|
46
|
+
import { PreferenceService, PreferenceChange } from '../preferences';
|
|
47
|
+
import { PREFERENCE_NAME_TREE_INDENT } from './tree-preference';
|
|
46
48
|
|
|
47
49
|
const debounce = require('lodash.debounce');
|
|
48
50
|
|
|
@@ -73,8 +75,7 @@ export interface TreeProps {
|
|
|
73
75
|
readonly contextMenuPath?: MenuPath;
|
|
74
76
|
|
|
75
77
|
/**
|
|
76
|
-
* The size of the padding (in pixels)
|
|
77
|
-
* the padding for the children will be calculated as `leftPadding * hierarchyDepth` and so on.
|
|
78
|
+
* The size of the padding (in pixels) for the root node of the tree.
|
|
78
79
|
*/
|
|
79
80
|
readonly leftPadding: number;
|
|
80
81
|
|
|
@@ -174,6 +175,9 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
|
|
|
174
175
|
@inject(SelectionService)
|
|
175
176
|
protected readonly selectionService: SelectionService;
|
|
176
177
|
|
|
178
|
+
@inject(PreferenceService)
|
|
179
|
+
protected readonly preferenceService: PreferenceService;
|
|
180
|
+
|
|
177
181
|
@inject(LabelProvider)
|
|
178
182
|
protected readonly labelProvider: LabelProvider;
|
|
179
183
|
|
|
@@ -182,6 +186,8 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
|
|
|
182
186
|
|
|
183
187
|
protected shouldScrollToRow = true;
|
|
184
188
|
|
|
189
|
+
protected treeIndent: number = 8;
|
|
190
|
+
|
|
185
191
|
constructor(
|
|
186
192
|
@inject(TreeProps) readonly props: TreeProps,
|
|
187
193
|
@inject(TreeModel) readonly model: TreeModel,
|
|
@@ -198,6 +204,7 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
|
|
|
198
204
|
|
|
199
205
|
@postConstruct()
|
|
200
206
|
protected init(): void {
|
|
207
|
+
this.treeIndent = this.preferenceService.get(PREFERENCE_NAME_TREE_INDENT, this.treeIndent);
|
|
201
208
|
if (this.props.search) {
|
|
202
209
|
this.searchBox = this.searchBoxFactory({ ...SearchBoxProps.DEFAULT, showButtons: true, showFilter: true });
|
|
203
210
|
this.searchBox.node.addEventListener('focus', () => {
|
|
@@ -264,6 +271,12 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
|
|
|
264
271
|
return;
|
|
265
272
|
}
|
|
266
273
|
}
|
|
274
|
+
}),
|
|
275
|
+
this.preferenceService.onPreferenceChanged((event: PreferenceChange) => {
|
|
276
|
+
if (event.preferenceName === PREFERENCE_NAME_TREE_INDENT) {
|
|
277
|
+
this.treeIndent = event.newValue;
|
|
278
|
+
this.update();
|
|
279
|
+
}
|
|
267
280
|
})
|
|
268
281
|
]);
|
|
269
282
|
setTimeout(() => {
|
|
@@ -1227,12 +1240,15 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
|
|
|
1227
1240
|
|
|
1228
1241
|
/**
|
|
1229
1242
|
* Handle the `space key` keyboard event.
|
|
1230
|
-
* -
|
|
1243
|
+
* - If the element has a checkbox, it will be toggled.
|
|
1244
|
+
* - Otherwise, it should be similar to a single-click action.
|
|
1231
1245
|
* @param event the `space key` keyboard event.
|
|
1232
1246
|
*/
|
|
1233
1247
|
protected handleSpace(event: KeyboardEvent): void {
|
|
1234
1248
|
const { focusedNode } = this.focusService;
|
|
1235
|
-
if (
|
|
1249
|
+
if (focusedNode && focusedNode.checkboxInfo) {
|
|
1250
|
+
this.model.markAsChecked(focusedNode, !focusedNode.checkboxInfo.checked);
|
|
1251
|
+
} else if (!this.props.multiSelect || (!event.ctrlKey && !event.metaKey && !event.shiftKey)) {
|
|
1236
1252
|
this.tapNode(focusedNode);
|
|
1237
1253
|
}
|
|
1238
1254
|
}
|
|
@@ -1497,7 +1513,10 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
|
|
|
1497
1513
|
return this.labelProvider.getLongName(node);
|
|
1498
1514
|
}
|
|
1499
1515
|
protected getDepthPadding(depth: number): number {
|
|
1500
|
-
|
|
1516
|
+
if (depth === 1) {
|
|
1517
|
+
return this.props.leftPadding;
|
|
1518
|
+
}
|
|
1519
|
+
return depth * this.treeIndent;
|
|
1501
1520
|
}
|
|
1502
1521
|
}
|
|
1503
1522
|
export namespace TreeWidget {
|
package/src/browser/tree/tree.ts
CHANGED
|
@@ -397,9 +397,10 @@ export class TreeImpl implements Tree {
|
|
|
397
397
|
|
|
398
398
|
protected async doMarkAsBusy(node: Mutable<TreeNode>, ms: number, token: CancellationToken): Promise<void> {
|
|
399
399
|
try {
|
|
400
|
+
token.onCancellationRequested(() => this.doResetBusy(node));
|
|
400
401
|
await timeout(ms, token);
|
|
402
|
+
if (token.isCancellationRequested) { return; }
|
|
401
403
|
this.doSetBusy(node);
|
|
402
|
-
token.onCancellationRequested(() => this.doResetBusy(node));
|
|
403
404
|
} catch {
|
|
404
405
|
/* no-op */
|
|
405
406
|
}
|
|
@@ -194,6 +194,31 @@ export class WidgetManager {
|
|
|
194
194
|
return widget;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Finds a widget that matches the given test predicate.
|
|
199
|
+
* @param factoryId The widget factory id.
|
|
200
|
+
* @param predicate The test predicate.
|
|
201
|
+
*
|
|
202
|
+
* @returns a promise resolving to the widget if available, else `undefined`.
|
|
203
|
+
*/
|
|
204
|
+
async findWidget<T extends Widget>(factoryId: string, predicate: (options?: any) => boolean): Promise<T | undefined> {
|
|
205
|
+
for (const [key, widget] of this.widgets.entries()) {
|
|
206
|
+
if (this.testPredicate(key, factoryId, predicate)) {
|
|
207
|
+
return widget as T;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (const [key, widget] of this.pendingWidgetPromises.entries()) {
|
|
211
|
+
if (this.testPredicate(key, factoryId, predicate)) {
|
|
212
|
+
return widget as T;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
protected testPredicate(key: string, factoryId: string, predicate: (options?: any) => boolean): boolean {
|
|
218
|
+
const constructionOptions = this.fromKey(key);
|
|
219
|
+
return constructionOptions.factoryId === factoryId && predicate(constructionOptions.options);
|
|
220
|
+
}
|
|
221
|
+
|
|
197
222
|
protected doGetWidget<T extends Widget>(key: string): MaybePromise<T> | undefined {
|
|
198
223
|
const pendingWidget = this.widgets.get(key) ?? this.pendingWidgetPromises.get(key);
|
|
199
224
|
if (pendingWidget) {
|
|
@@ -77,6 +77,7 @@ export class SelectComponent extends React.Component<SelectComponentProps, Selec
|
|
|
77
77
|
if (!list) {
|
|
78
78
|
list = document.createElement('div');
|
|
79
79
|
list.id = SELECT_COMPONENT_CONTAINER;
|
|
80
|
+
list.className = 'theia-select-component-container';
|
|
80
81
|
document.body.appendChild(list);
|
|
81
82
|
}
|
|
82
83
|
this.dropdownElement = list;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 EclipseSource 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 { ContainerModule } from 'inversify';
|
|
18
|
+
import { BackendStopwatch, CommandRegistry, Emitter, MeasurementOptions, OS } from '../common';
|
|
19
|
+
import { ApplicationInfo, ApplicationServer, ExtensionInfo } from '../common/application-protocol';
|
|
20
|
+
import { EnvVariable, EnvVariablesServer } from './../common/env-variables';
|
|
21
|
+
import { bindMessageService } from '../browser/frontend-application-bindings';
|
|
22
|
+
import { KeyStoreService } from '../common/key-store';
|
|
23
|
+
import { QuickPickService } from '../common/quick-pick-service';
|
|
24
|
+
import { QuickPickServiceImpl } from '../browser/quick-input';
|
|
25
|
+
import { BackendRequestService, RequestService } from '@theia/request';
|
|
26
|
+
import { ConnectionStatus, ConnectionStatusService } from '../browser/connection-status-service';
|
|
27
|
+
|
|
28
|
+
export { bindMessageService };
|
|
29
|
+
|
|
30
|
+
// is loaded directly after the regular frontend module
|
|
31
|
+
export const frontendOnlyApplicationModule = new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
32
|
+
|
|
33
|
+
if (isBound(CommandRegistry)) {
|
|
34
|
+
rebind(CommandRegistry).toSelf().inSingletonScope();
|
|
35
|
+
} else {
|
|
36
|
+
bind(CommandRegistry).toSelf().inSingletonScope();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const stopwatch: BackendStopwatch = {
|
|
40
|
+
start: async (_name: string, _options?: MeasurementOptions | undefined): Promise<number> => -1,
|
|
41
|
+
stop: async (_measurement: number, _message: string, _messageArgs: unknown[]): Promise<void> => { }
|
|
42
|
+
};
|
|
43
|
+
if (isBound(BackendStopwatch)) {
|
|
44
|
+
rebind(BackendStopwatch).toConstantValue(stopwatch);
|
|
45
|
+
} else {
|
|
46
|
+
bind(BackendStopwatch).toConstantValue(stopwatch);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (isBound(CommandRegistry)) {
|
|
50
|
+
rebind(QuickPickService).to(QuickPickServiceImpl).inSingletonScope();
|
|
51
|
+
} else {
|
|
52
|
+
bind(QuickPickService).to(QuickPickServiceImpl).inSingletonScope();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const mockedApplicationServer: ApplicationServer = {
|
|
56
|
+
getExtensionsInfos: async (): Promise<ExtensionInfo[]> => [],
|
|
57
|
+
getApplicationInfo: async (): Promise<ApplicationInfo | undefined> => undefined,
|
|
58
|
+
getApplicationRoot: async (): Promise<string> => '',
|
|
59
|
+
getBackendOS: async (): Promise<OS.Type> => OS.Type.Linux
|
|
60
|
+
};
|
|
61
|
+
if (isBound(ApplicationServer)) {
|
|
62
|
+
rebind(ApplicationServer).toConstantValue(mockedApplicationServer);
|
|
63
|
+
} else {
|
|
64
|
+
bind(ApplicationServer).toConstantValue(mockedApplicationServer);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const varServer: EnvVariablesServer = {
|
|
68
|
+
getExecPath: async (): Promise<string> => '',
|
|
69
|
+
getVariables: async (): Promise<EnvVariable[]> => [],
|
|
70
|
+
getValue: async (_key: string): Promise<EnvVariable | undefined> => undefined,
|
|
71
|
+
getConfigDirUri: async (): Promise<string> => '',
|
|
72
|
+
getHomeDirUri: async (): Promise<string> => '',
|
|
73
|
+
getDrives: async (): Promise<string[]> => []
|
|
74
|
+
};
|
|
75
|
+
if (isBound(EnvVariablesServer)) {
|
|
76
|
+
rebind(EnvVariablesServer).toConstantValue(varServer);
|
|
77
|
+
} else {
|
|
78
|
+
bind(EnvVariablesServer).toConstantValue(varServer);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const keyStoreService: KeyStoreService = {
|
|
82
|
+
deletePassword: () => Promise.resolve(false),
|
|
83
|
+
findCredentials: () => Promise.resolve([]),
|
|
84
|
+
findPassword: () => Promise.resolve(undefined),
|
|
85
|
+
setPassword: () => Promise.resolve(),
|
|
86
|
+
getPassword: () => Promise.resolve(undefined)
|
|
87
|
+
};
|
|
88
|
+
if (isBound(KeyStoreService)) {
|
|
89
|
+
rebind<KeyStoreService>(KeyStoreService).toConstantValue(keyStoreService);
|
|
90
|
+
} else {
|
|
91
|
+
bind<KeyStoreService>(KeyStoreService).toConstantValue(keyStoreService);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const requestService: RequestService = {
|
|
95
|
+
configure: () => Promise.resolve(),
|
|
96
|
+
request: () => Promise.reject(),
|
|
97
|
+
resolveProxy: () => Promise.resolve(undefined)
|
|
98
|
+
};
|
|
99
|
+
if (isBound(BackendRequestService)) {
|
|
100
|
+
rebind<RequestService>(BackendRequestService).toConstantValue(requestService);
|
|
101
|
+
} else {
|
|
102
|
+
bind<RequestService>(BackendRequestService).toConstantValue(requestService);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const connectionStatusService: ConnectionStatusService = {
|
|
106
|
+
currentStatus: ConnectionStatus.ONLINE,
|
|
107
|
+
onStatusChange: new Emitter<ConnectionStatus>().event
|
|
108
|
+
};
|
|
109
|
+
if (isBound(ConnectionStatusService)) {
|
|
110
|
+
rebind<ConnectionStatusService>(ConnectionStatusService).toConstantValue(connectionStatusService);
|
|
111
|
+
} else {
|
|
112
|
+
bind<ConnectionStatusService>(ConnectionStatusService).toConstantValue(connectionStatusService);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 EclipseSource 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 { ContainerModule } from 'inversify';
|
|
18
|
+
import { AsyncLocalizationProvider, LanguageInfo, Localization } from '../../common/i18n/localization';
|
|
19
|
+
import { LanguageQuickPickService } from '../../browser/i18n/language-quick-pick-service';
|
|
20
|
+
|
|
21
|
+
export default new ContainerModule(bind => {
|
|
22
|
+
const i18nMock: AsyncLocalizationProvider = {
|
|
23
|
+
getCurrentLanguage: async (): Promise<string> => 'en',
|
|
24
|
+
setCurrentLanguage: async (_languageId: string): Promise<void> => {
|
|
25
|
+
|
|
26
|
+
},
|
|
27
|
+
getAvailableLanguages: async (): Promise<LanguageInfo[]> =>
|
|
28
|
+
[]
|
|
29
|
+
,
|
|
30
|
+
loadLocalization: async (_languageId: string): Promise<Localization> => ({
|
|
31
|
+
translations: {},
|
|
32
|
+
languageId: 'en'
|
|
33
|
+
})
|
|
34
|
+
};
|
|
35
|
+
bind(AsyncLocalizationProvider).toConstantValue(i18nMock);
|
|
36
|
+
bind(LanguageQuickPickService).toSelf().inSingletonScope();
|
|
37
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 EclipseSource 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 { ContainerModule, Container } from 'inversify';
|
|
18
|
+
import { ILoggerServer, ILoggerClient, LogLevel, ConsoleLogger } from '../common/logger-protocol';
|
|
19
|
+
import { ILogger, Logger, LoggerFactory, LoggerName } from '../common/logger';
|
|
20
|
+
|
|
21
|
+
// is loaded directly after the regular logger frontend module
|
|
22
|
+
export const loggerFrontendOnlyModule = new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
23
|
+
const logger: ILoggerServer = {
|
|
24
|
+
setLogLevel: async (_name: string, _logLevel: number): Promise<void> => { },
|
|
25
|
+
getLogLevel: async (_name: string): Promise<number> => LogLevel.INFO,
|
|
26
|
+
log: async (name: string, logLevel: number, message: string, params: unknown[]): Promise<void> => {
|
|
27
|
+
ConsoleLogger.log(name, logLevel, message, params);
|
|
28
|
+
|
|
29
|
+
},
|
|
30
|
+
child: async (_name: string): Promise<void> => { },
|
|
31
|
+
dispose: (): void => {
|
|
32
|
+
},
|
|
33
|
+
setClient: (_client: ILoggerClient | undefined): void => {
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
if (isBound(ILoggerServer)) {
|
|
37
|
+
rebind(ILoggerServer).toConstantValue(logger);
|
|
38
|
+
} else {
|
|
39
|
+
bind(ILoggerServer).toConstantValue(logger);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (isBound(ILoggerServer)) {
|
|
43
|
+
rebind(LoggerFactory).toFactory(ctx =>
|
|
44
|
+
(name: string) => {
|
|
45
|
+
const child = new Container({ defaultScope: 'Singleton' });
|
|
46
|
+
child.parent = ctx.container;
|
|
47
|
+
child.bind(ILogger).to(Logger).inTransientScope();
|
|
48
|
+
child.bind(LoggerName).toConstantValue(name);
|
|
49
|
+
return child.get(ILogger);
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
} else {
|
|
53
|
+
bind(LoggerFactory).toFactory(ctx =>
|
|
54
|
+
(name: string) => {
|
|
55
|
+
const child = new Container({ defaultScope: 'Singleton' });
|
|
56
|
+
child.parent = ctx.container;
|
|
57
|
+
child.bind(ILogger).to(Logger).inTransientScope();
|
|
58
|
+
child.bind(LoggerName).toConstantValue(name);
|
|
59
|
+
return child.get(ILogger);
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 EclipseSource 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
|
+
import { Event, RpcProxy, Channel, RpcProxyFactory, Emitter } from '../../common';
|
|
17
|
+
import { injectable } from 'inversify';
|
|
18
|
+
import { ServiceConnectionProvider } from '../../browser/messaging/service-connection-provider';
|
|
19
|
+
import { ConnectionSource } from '../../browser/messaging/connection-source';
|
|
20
|
+
|
|
21
|
+
@injectable()
|
|
22
|
+
export class FrontendOnlyConnectionSource implements ConnectionSource {
|
|
23
|
+
onConnectionDidOpen = new Emitter<Channel>().event;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@injectable()
|
|
27
|
+
export class FrontendOnlyServiceConnectionProvider extends ServiceConnectionProvider {
|
|
28
|
+
onSocketDidOpen = Event.None;
|
|
29
|
+
onSocketDidClose = Event.None;
|
|
30
|
+
onIncomingMessageActivity = Event.None;
|
|
31
|
+
override createProxy<T extends object>(path: unknown, target?: unknown): RpcProxy<T> {
|
|
32
|
+
console.debug(`[Frontend-Only Fallback] Created proxy connection for ${path}`);
|
|
33
|
+
const factory = target instanceof RpcProxyFactory ? target : new RpcProxyFactory<T>(target);
|
|
34
|
+
return factory.createProxy();
|
|
35
|
+
}
|
|
36
|
+
override listen(path: string, handler: ServiceConnectionProvider.ConnectionHandler, reconnect: boolean): void {
|
|
37
|
+
console.debug('[Frontend-Only Fallback] Listen to websocket connection requested');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 EclipseSource 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
|
+
import { ContainerModule } from 'inversify';
|
|
17
|
+
import { WebSocketConnectionSource } from '../../browser/messaging/ws-connection-source';
|
|
18
|
+
import { FrontendOnlyConnectionSource, FrontendOnlyServiceConnectionProvider } from './frontend-only-service-connection-provider';
|
|
19
|
+
import { ConnectionSource } from '../../browser/messaging/connection-source';
|
|
20
|
+
import { LocalConnectionProvider, RemoteConnectionProvider } from '../../browser/messaging/service-connection-provider';
|
|
21
|
+
|
|
22
|
+
// is loaded directly after the regular message frontend module
|
|
23
|
+
export const messagingFrontendOnlyModule = new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
24
|
+
unbind(WebSocketConnectionSource);
|
|
25
|
+
bind(FrontendOnlyConnectionSource).toSelf().inSingletonScope();
|
|
26
|
+
if (isBound(ConnectionSource)) {
|
|
27
|
+
rebind(ConnectionSource).toService(FrontendOnlyConnectionSource);
|
|
28
|
+
} else {
|
|
29
|
+
bind(ConnectionSource).toService(FrontendOnlyConnectionSource);
|
|
30
|
+
}
|
|
31
|
+
bind(FrontendOnlyServiceConnectionProvider).toSelf().inSingletonScope();
|
|
32
|
+
if (isBound(LocalConnectionProvider)) {
|
|
33
|
+
rebind(LocalConnectionProvider).toService(FrontendOnlyServiceConnectionProvider);
|
|
34
|
+
} else {
|
|
35
|
+
bind(LocalConnectionProvider).toService(FrontendOnlyServiceConnectionProvider);
|
|
36
|
+
}
|
|
37
|
+
if (isBound(RemoteConnectionProvider)) {
|
|
38
|
+
rebind(RemoteConnectionProvider).toService(FrontendOnlyServiceConnectionProvider);
|
|
39
|
+
} else {
|
|
40
|
+
bind(RemoteConnectionProvider).toService(FrontendOnlyServiceConnectionProvider);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 TypeFox 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 { ContainerModule } from 'inversify';
|
|
18
|
+
import { LocalizationServer } from '../../common/i18n/localization-server';
|
|
19
|
+
import { OS, OSBackendProvider } from '../../common/os';
|
|
20
|
+
import { Localization } from '../../common/i18n/localization';
|
|
21
|
+
|
|
22
|
+
// loaded after regular preload module
|
|
23
|
+
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
24
|
+
const frontendOnlyLocalizationServer: LocalizationServer = {
|
|
25
|
+
loadLocalization: async (languageId: string): Promise<Localization> => ({ translations: {}, languageId })
|
|
26
|
+
};
|
|
27
|
+
if (isBound(LocalizationServer)) {
|
|
28
|
+
rebind(LocalizationServer).toConstantValue(frontendOnlyLocalizationServer);
|
|
29
|
+
} else {
|
|
30
|
+
bind(LocalizationServer).toConstantValue(frontendOnlyLocalizationServer);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const frontendOnlyOSBackendProvider: OSBackendProvider = {
|
|
34
|
+
getBackendOS: async (): Promise<OS.Type> => {
|
|
35
|
+
if (window.navigator.platform.startsWith('Win')) {
|
|
36
|
+
return OS.Type.Windows;
|
|
37
|
+
} else if (window.navigator.platform.startsWith('Mac')) {
|
|
38
|
+
return OS.Type.OSX;
|
|
39
|
+
} else {
|
|
40
|
+
return OS.Type.Linux;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
if (isBound(OSBackendProvider)) {
|
|
45
|
+
rebind(OSBackendProvider).toConstantValue(frontendOnlyOSBackendProvider);
|
|
46
|
+
} else {
|
|
47
|
+
bind(OSBackendProvider).toConstantValue(frontendOnlyOSBackendProvider);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
@@ -23,6 +23,7 @@ export const ApplicationServer = Symbol('ApplicationServer');
|
|
|
23
23
|
export interface ApplicationServer {
|
|
24
24
|
getExtensionsInfos(): Promise<ExtensionInfo[]>;
|
|
25
25
|
getApplicationInfo(): Promise<ApplicationInfo | undefined>;
|
|
26
|
+
getApplicationRoot(): Promise<string>;
|
|
26
27
|
/**
|
|
27
28
|
* @deprecated since 1.25.0. Use `OS.backend.type()` instead.
|
|
28
29
|
*/
|
package/src/common/disposable.ts
CHANGED
|
@@ -133,3 +133,28 @@ export function disposableTimeout(...args: Parameters<typeof setTimeout>): Dispo
|
|
|
133
133
|
const handle = setTimeout(...args);
|
|
134
134
|
return { dispose: () => clearTimeout(handle) };
|
|
135
135
|
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Wrapper for a {@link Disposable} that is not available immediately.
|
|
139
|
+
*/
|
|
140
|
+
export class DisposableWrapper implements Disposable {
|
|
141
|
+
|
|
142
|
+
private disposed = false;
|
|
143
|
+
private disposable: Disposable | undefined = undefined;
|
|
144
|
+
|
|
145
|
+
set(disposable: Disposable): void {
|
|
146
|
+
if (this.disposed) {
|
|
147
|
+
disposable.dispose();
|
|
148
|
+
} else {
|
|
149
|
+
this.disposable = disposable;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
dispose(): void {
|
|
154
|
+
this.disposed = true;
|
|
155
|
+
if (this.disposable) {
|
|
156
|
+
this.disposable.dispose();
|
|
157
|
+
this.disposable = undefined;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
17
|
import { URI as Uri } from 'vscode-uri';
|
|
18
|
-
import URI from '
|
|
19
|
-
import { isWindows } from '
|
|
18
|
+
import URI from './uri';
|
|
19
|
+
import { isWindows } from './os';
|
|
20
20
|
|
|
21
21
|
export namespace FileUri {
|
|
22
22
|
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { CommandRegistry, Command } from '../command';
|
|
17
|
+
import { inject, injectable, named } from 'inversify';
|
|
18
|
+
import { Command, CommandRegistry } from '../command';
|
|
20
19
|
import { ContributionProvider } from '../contribution-provider';
|
|
21
|
-
import {
|
|
22
|
-
import { CompoundMenuNode, MenuAction, MenuNode, MenuPath, MutableCompoundMenuNode, SubMenuOptions } from './menu-types';
|
|
20
|
+
import { Disposable } from '../disposable';
|
|
23
21
|
import { ActionMenuNode } from './action-menu-node';
|
|
22
|
+
import { CompositeMenuNode, CompositeMenuNodeWrapper } from './composite-menu-node';
|
|
23
|
+
import { CompoundMenuNode, MenuAction, MenuNode, MenuNodeMetadata, MenuPath, MutableCompoundMenuNode, SubMenuOptions } from './menu-types';
|
|
24
24
|
|
|
25
25
|
export const MenuContribution = Symbol('MenuContribution');
|
|
26
26
|
|
|
@@ -157,6 +157,23 @@ export class MenuModelRegistry {
|
|
|
157
157
|
linkSubmenu(parentPath: MenuPath | string, childId: string | MenuPath, options?: SubMenuOptions, group?: string): Disposable {
|
|
158
158
|
const child = this.getMenuNode(childId);
|
|
159
159
|
const parent = this.getMenuNode(parentPath, group);
|
|
160
|
+
|
|
161
|
+
const isRecursive = (node: MenuNodeMetadata, childNode: MenuNodeMetadata): boolean => {
|
|
162
|
+
if (node.id === childNode.id) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
if (node.parent) {
|
|
166
|
+
return isRecursive(node.parent, childNode);
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// check for menu contribution recursion
|
|
172
|
+
if (isRecursive(parent, child)) {
|
|
173
|
+
console.warn(`Recursive menu contribution detected: ${child.id} is already in hierarchy of ${parent.id}.`);
|
|
174
|
+
return Disposable.NULL;
|
|
175
|
+
}
|
|
176
|
+
|
|
160
177
|
const wrapper = new CompositeMenuNodeWrapper(child, parent, options);
|
|
161
178
|
return parent.addNode(wrapper);
|
|
162
179
|
}
|