@theia/core 1.76.0-next.7 → 1.76.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 +8 -8
- package/i18n/nls.cs.json +187 -49
- package/i18n/nls.de.json +187 -49
- package/i18n/nls.es.json +187 -49
- package/i18n/nls.fr.json +187 -49
- package/i18n/nls.hu.json +187 -49
- package/i18n/nls.it.json +187 -49
- package/i18n/nls.ja.json +187 -49
- package/i18n/nls.json +187 -49
- package/i18n/nls.ko.json +187 -49
- package/i18n/nls.pl.json +187 -49
- package/i18n/nls.pt-br.json +187 -49
- package/i18n/nls.ru.json +187 -49
- package/i18n/nls.tr.json +187 -49
- package/i18n/nls.zh-cn.json +187 -49
- package/i18n/nls.zh-tw.json +187 -49
- package/lib/browser/catalog.json +175 -95
- package/lib/browser/dialogs.d.ts +2 -0
- package/lib/browser/dialogs.d.ts.map +1 -1
- package/lib/browser/dialogs.js +9 -5
- package/lib/browser/dialogs.js.map +1 -1
- package/lib/browser/frontend-application-module.d.ts.map +1 -1
- package/lib/browser/frontend-application-module.js +3 -0
- package/lib/browser/frontend-application-module.js.map +1 -1
- package/lib/browser/index.d.ts +1 -0
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +1 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/resource-context-key.d.ts +25 -0
- package/lib/browser/resource-context-key.d.ts.map +1 -1
- package/lib/browser/resource-context-key.js +41 -16
- package/lib/browser/resource-context-key.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts +7 -5
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js +13 -12
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts +11 -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 +38 -3
- 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.d.ts +2 -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 +4 -2
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js +123 -2
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts +12 -4
- package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js +2 -2
- package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js.map +1 -1
- package/lib/browser/tree/tree-widget.d.ts +20 -2
- package/lib/browser/tree/tree-widget.d.ts.map +1 -1
- package/lib/browser/tree/tree-widget.js +31 -13
- package/lib/browser/tree/tree-widget.js.map +1 -1
- package/lib/browser/tree/tree-widget.spec.d.ts +2 -0
- package/lib/browser/tree/tree-widget.spec.d.ts.map +1 -0
- package/lib/browser/tree/tree-widget.spec.js +192 -0
- package/lib/browser/tree/tree-widget.spec.js.map +1 -0
- package/lib/browser/view-container.d.ts.map +1 -1
- package/lib/browser/view-container.js +2 -2
- package/lib/browser/view-container.js.map +1 -1
- package/lib/browser/widget-context-key-contribution.d.ts +17 -0
- package/lib/browser/widget-context-key-contribution.d.ts.map +1 -0
- package/lib/browser/widget-context-key-contribution.js +20 -0
- package/lib/browser/widget-context-key-contribution.js.map +1 -0
- package/lib/browser/window/browser-window-module.d.ts.map +1 -1
- package/lib/browser/window/browser-window-module.js +3 -0
- package/lib/browser/window/browser-window-module.js.map +1 -1
- package/lib/browser/window/window-launch-args.d.ts +29 -0
- package/lib/browser/window/window-launch-args.d.ts.map +1 -0
- package/lib/browser/window/window-launch-args.js +39 -0
- package/lib/browser/window/window-launch-args.js.map +1 -0
- package/lib/browser-only/frontend-only-application-module.d.ts.map +1 -1
- package/lib/browser-only/frontend-only-application-module.js +5 -2
- package/lib/browser-only/frontend-only-application-module.js.map +1 -1
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.d.ts +3 -1
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.d.ts.map +1 -1
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.js +8 -7
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.js.map +1 -1
- package/lib/common/index.d.ts +2 -0
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +2 -0
- package/lib/common/index.js.map +1 -1
- package/lib/common/launch-arguments.d.ts +32 -0
- package/lib/common/launch-arguments.d.ts.map +1 -0
- package/lib/common/launch-arguments.js +44 -0
- package/lib/common/launch-arguments.js.map +1 -0
- package/lib/common/remote-cli-args-contribution.d.ts +22 -0
- package/lib/common/remote-cli-args-contribution.d.ts.map +1 -0
- package/lib/common/remote-cli-args-contribution.js +20 -0
- package/lib/common/remote-cli-args-contribution.js.map +1 -0
- package/lib/electron-browser/messaging/electron-ipc-connection-source.d.ts +6 -3
- package/lib/electron-browser/messaging/electron-ipc-connection-source.d.ts.map +1 -1
- package/lib/electron-browser/messaging/electron-ipc-connection-source.js +4 -3
- package/lib/electron-browser/messaging/electron-ipc-connection-source.js.map +1 -1
- package/lib/electron-browser/preload.d.ts.map +1 -1
- package/lib/electron-browser/preload.js +3 -1
- package/lib/electron-browser/preload.js.map +1 -1
- package/lib/electron-browser/window/electron-window-launch-args.d.ts +14 -0
- package/lib/electron-browser/window/electron-window-launch-args.d.ts.map +1 -0
- package/lib/electron-browser/window/electron-window-launch-args.js +41 -0
- package/lib/electron-browser/window/electron-window-launch-args.js.map +1 -0
- package/lib/electron-browser/window/electron-window-launch-args.spec.d.ts +2 -0
- package/lib/electron-browser/window/electron-window-launch-args.spec.d.ts.map +1 -0
- package/lib/electron-browser/window/electron-window-launch-args.spec.js +43 -0
- package/lib/electron-browser/window/electron-window-launch-args.spec.js.map +1 -0
- package/lib/electron-browser/window/electron-window-module.d.ts.map +1 -1
- package/lib/electron-browser/window/electron-window-module.js +7 -0
- package/lib/electron-browser/window/electron-window-module.js.map +1 -1
- package/lib/electron-common/electron-api.d.ts +10 -0
- package/lib/electron-common/electron-api.d.ts.map +1 -1
- package/lib/electron-common/electron-api.js.map +1 -1
- package/lib/electron-main/electron-api-main.d.ts.map +1 -1
- package/lib/electron-main/electron-api-main.js +7 -1
- package/lib/electron-main/electron-api-main.js.map +1 -1
- package/lib/electron-main/electron-main-application-module.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application-module.js +2 -0
- package/lib/electron-main/electron-main-application-module.js.map +1 -1
- package/lib/electron-main/electron-main-application.d.ts +57 -2
- package/lib/electron-main/electron-main-application.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application.js +68 -8
- package/lib/electron-main/electron-main-application.js.map +1 -1
- package/lib/electron-main/launch-args-store.d.ts +45 -0
- package/lib/electron-main/launch-args-store.d.ts.map +1 -0
- package/lib/electron-main/launch-args-store.js +76 -0
- package/lib/electron-main/launch-args-store.js.map +1 -0
- package/lib/electron-main/launch-args-store.spec.d.ts +2 -0
- package/lib/electron-main/launch-args-store.spec.d.ts.map +1 -0
- package/lib/electron-main/launch-args-store.spec.js +53 -0
- package/lib/electron-main/launch-args-store.spec.js.map +1 -0
- package/lib/electron-main/launch-argv-parser.d.ts +19 -0
- package/lib/electron-main/launch-argv-parser.d.ts.map +1 -0
- package/lib/electron-main/launch-argv-parser.js +71 -0
- package/lib/electron-main/launch-argv-parser.js.map +1 -0
- package/lib/electron-main/launch-argv-parser.spec.d.ts +2 -0
- package/lib/electron-main/launch-argv-parser.spec.d.ts.map +1 -0
- package/lib/electron-main/launch-argv-parser.spec.js +62 -0
- package/lib/electron-main/launch-argv-parser.spec.js.map +1 -0
- package/lib/electron-main/theia-electron-window.d.ts +2 -0
- package/lib/electron-main/theia-electron-window.d.ts.map +1 -1
- package/lib/electron-main/theia-electron-window.js +12 -0
- package/lib/electron-main/theia-electron-window.js.map +1 -1
- package/package.json +11 -11
- package/src/browser/dialogs.ts +10 -5
- package/src/browser/frontend-application-module.ts +5 -1
- package/src/browser/index.ts +1 -0
- package/src/browser/resource-context-key.ts +61 -16
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.tsx +13 -12
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +36 -4
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.ts +150 -3
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +6 -3
- package/src/browser/shell/tab-bar-toolbar/tab-toolbar-item.tsx +13 -5
- package/src/browser/style/tree.css +7 -0
- package/src/browser/tree/tree-widget.spec.tsx +245 -0
- package/src/browser/tree/tree-widget.tsx +36 -12
- package/src/browser/view-container.ts +3 -3
- package/src/browser/widget-context-key-contribution.ts +34 -0
- package/src/browser/window/browser-window-module.ts +3 -0
- package/src/browser/window/window-launch-args.ts +53 -0
- package/src/browser-only/frontend-only-application-module.ts +5 -2
- package/src/browser-only/messaging/frontend-only-service-connection-provider.ts +3 -1
- package/src/common/i18n/nls.metadata.json +5246 -1106
- package/src/common/index.ts +2 -0
- package/src/common/launch-arguments.ts +61 -0
- package/src/common/remote-cli-args-contribution.ts +39 -0
- package/src/electron-browser/messaging/electron-ipc-connection-source.ts +6 -3
- package/src/electron-browser/preload.ts +3 -1
- package/src/electron-browser/window/electron-window-launch-args.spec.ts +45 -0
- package/src/electron-browser/window/electron-window-launch-args.ts +38 -0
- package/src/electron-browser/window/electron-window-module.ts +7 -0
- package/src/electron-common/electron-api.ts +10 -0
- package/src/electron-main/electron-api-main.ts +7 -1
- package/src/electron-main/electron-main-application-module.ts +2 -0
- package/src/electron-main/electron-main-application.ts +101 -8
- package/src/electron-main/launch-args-store.spec.ts +59 -0
- package/src/electron-main/launch-args-store.ts +73 -0
- package/src/electron-main/launch-argv-parser.spec.ts +70 -0
- package/src/electron-main/launch-argv-parser.ts +66 -0
- package/src/electron-main/theia-electron-window.ts +9 -0
|
@@ -57,8 +57,8 @@ abstract class AbstractToolbarMenuWrapper {
|
|
|
57
57
|
}
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
60
|
-
render(widget: Widget): React.ReactNode {
|
|
61
|
-
return this.renderMenuItem(widget);
|
|
60
|
+
render(widget: Widget, contextMatcher: ContextMatcher = this.contextKeyService): React.ReactNode {
|
|
61
|
+
return this.renderMenuItem(widget, contextMatcher);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
abstract toMenuNode(): MenuNode | undefined;
|
|
@@ -90,14 +90,15 @@ abstract class AbstractToolbarMenuWrapper {
|
|
|
90
90
|
* chevron decoration that pops up a floating menu when clicked.
|
|
91
91
|
*
|
|
92
92
|
* @param item a toolbar item that is a menu item
|
|
93
|
+
* @param contextMatcher evaluates `when` clauses with the values that describe `widget`, so that the
|
|
94
|
+
* chevron and its popup resolve the same keys as the visibility check.
|
|
93
95
|
* @returns the rendered toolbar item
|
|
94
96
|
*/
|
|
95
|
-
protected renderMenuItem(widget: Widget): React.ReactNode {
|
|
97
|
+
protected renderMenuItem(widget: Widget, contextMatcher: ContextMatcher = this.contextKeyService): React.ReactNode {
|
|
96
98
|
const icon = this.icon || 'ellipsis';
|
|
97
|
-
const contextMatcher: ContextMatcher = this.contextKeyService;
|
|
98
99
|
const className = `${icon} ${ACTION_ITEM}`;
|
|
99
100
|
const itemClassName = TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM + ' enabled menu' + (this.isToggled(widget) ? ' toggled' : '');
|
|
100
|
-
if (CompoundMenuNode.is(this.menuNode) && !this.menuNode.isEmpty(this.effectiveMenuPath,
|
|
101
|
+
if (CompoundMenuNode.is(this.menuNode) && !this.menuNode.isEmpty(this.effectiveMenuPath, contextMatcher, widget.node, widget)) {
|
|
101
102
|
return <div key={this.id} className={itemClassName}>
|
|
102
103
|
<div id={this.id} className={className}
|
|
103
104
|
title={this.tooltip || this.text}
|
|
@@ -137,8 +138,8 @@ export class SubmenuAsToolbarItemWrapper extends AbstractToolbarMenuWrapper impl
|
|
|
137
138
|
executeCommand(widget: Widget, e: React.MouseEvent<HTMLDivElement, MouseEvent>): void {
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
isVisible(widget: Widget): boolean {
|
|
141
|
-
const menuNodeVisible = this.menuNode.isVisible(this.effectiveMenuPath,
|
|
141
|
+
isVisible(widget: Widget, contextMatcher: ContextMatcher): boolean {
|
|
142
|
+
const menuNodeVisible = this.menuNode.isVisible(this.effectiveMenuPath, contextMatcher, widget.node, widget);
|
|
142
143
|
return menuNodeVisible && !MenuModelRegistry.isEmpty(this.menuNode);
|
|
143
144
|
}
|
|
144
145
|
|
|
@@ -173,8 +174,8 @@ export class CommandMenuAsToolbarItemWrapper extends AbstractToolbarMenuWrapper
|
|
|
173
174
|
return this.menuNode.when;
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
isVisible(widget: Widget): boolean {
|
|
177
|
-
return this.menuNode.isVisible(this.effectiveMenuPath,
|
|
177
|
+
isVisible(widget: Widget, contextMatcher: ContextMatcher): boolean {
|
|
178
|
+
return this.menuNode.isVisible(this.effectiveMenuPath, contextMatcher, widget.node, widget);
|
|
178
179
|
}
|
|
179
180
|
|
|
180
181
|
executeCommand(widget: Widget, e: React.MouseEvent<HTMLDivElement, MouseEvent>): void {
|
|
@@ -229,19 +230,19 @@ export class ToolbarActionWrapper extends AbstractToolbarMenuWrapper implements
|
|
|
229
230
|
}
|
|
230
231
|
};
|
|
231
232
|
|
|
232
|
-
isVisible(widget: Widget): boolean {
|
|
233
|
+
isVisible(widget: Widget, contextMatcher: ContextMatcher): boolean {
|
|
233
234
|
const menuNode = this.menuNode;
|
|
234
235
|
if (this.toolbarItem.isVisible && !this.toolbarItem.isVisible(widget)) {
|
|
235
236
|
return false;
|
|
236
237
|
}
|
|
237
|
-
if (!menuNode?.isVisible(this.effectiveMenuPath,
|
|
238
|
+
if (!menuNode?.isVisible(this.effectiveMenuPath, contextMatcher, widget.node, widget)) {
|
|
238
239
|
return false;
|
|
239
240
|
}
|
|
240
241
|
if (this.toolbarItem.command) {
|
|
241
242
|
return true;
|
|
242
243
|
}
|
|
243
244
|
if (CompoundMenuNode.is(menuNode)) {
|
|
244
|
-
return !menuNode.isEmpty(this.effectiveMenuPath,
|
|
245
|
+
return !menuNode.isEmpty(this.effectiveMenuPath, contextMatcher, widget.node, widget);
|
|
245
246
|
}
|
|
246
247
|
return true;
|
|
247
248
|
}
|
|
@@ -18,9 +18,12 @@ import debounce = require('lodash.debounce');
|
|
|
18
18
|
import { inject, injectable, named } from 'inversify';
|
|
19
19
|
// eslint-disable-next-line max-len
|
|
20
20
|
import { CommandRegistry, ContributionProvider, Disposable, DisposableCollection, Emitter, Event, MenuModelRegistry, MenuPath, ILogger } from '../../../common';
|
|
21
|
-
import { ContextKeyService } from '../../context-key-service';
|
|
21
|
+
import { ContextKeyService, ContextMatcher } from '../../context-key-service';
|
|
22
22
|
import { FrontendApplicationContribution } from '../../frontend-application-contribution';
|
|
23
23
|
import { Widget } from '../../widgets';
|
|
24
|
+
import { Navigatable } from '../../navigatable-types';
|
|
25
|
+
import { ResourceContextKey } from '../../resource-context-key';
|
|
26
|
+
import { WidgetContextKeyContribution } from '../../widget-context-key-contribution';
|
|
24
27
|
import { ReactTabBarToolbarAction, RenderedToolbarAction } from './tab-bar-toolbar-types';
|
|
25
28
|
import { CommandMenuAsToolbarItemWrapper, SubmenuAsToolbarItemWrapper, ToolbarActionWrapper } from './tab-bar-toolbar-menu-adapters';
|
|
26
29
|
import { KeybindingRegistry } from '../../keybinding';
|
|
@@ -65,9 +68,14 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
|
|
|
65
68
|
@inject(LabelParser) protected readonly labelParser: LabelParser;
|
|
66
69
|
@inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer;
|
|
67
70
|
|
|
71
|
+
@inject(ResourceContextKey) protected readonly resourceContextKey: ResourceContextKey;
|
|
72
|
+
|
|
68
73
|
@inject(ContributionProvider) @named(TabBarToolbarContribution)
|
|
69
74
|
protected readonly contributionProvider: ContributionProvider<TabBarToolbarContribution>;
|
|
70
75
|
|
|
76
|
+
@inject(ContributionProvider) @named(WidgetContextKeyContribution)
|
|
77
|
+
protected readonly contextKeyContributionProvider: ContributionProvider<WidgetContextKeyContribution>;
|
|
78
|
+
|
|
71
79
|
@inject(ILogger) @named('core:TabBarToolbarRegistry')
|
|
72
80
|
protected readonly logger: ILogger;
|
|
73
81
|
|
|
@@ -133,8 +141,9 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
|
|
|
133
141
|
return [];
|
|
134
142
|
}
|
|
135
143
|
const result: Array<TabBarToolbarItem> = [];
|
|
144
|
+
const contextMatcher = this.contextMatcherFor(widget);
|
|
136
145
|
for (const item of this.items.values()) {
|
|
137
|
-
if (item.isVisible(widget)) {
|
|
146
|
+
if (item.isVisible(widget, contextMatcher)) {
|
|
138
147
|
result.push(item);
|
|
139
148
|
}
|
|
140
149
|
}
|
|
@@ -144,11 +153,11 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
|
|
|
144
153
|
const menu = this.menuRegistry.getMenu(delegate.menuPath);
|
|
145
154
|
if (menu) {
|
|
146
155
|
for (const child of menu.children) {
|
|
147
|
-
if (child.isVisible([...delegate.menuPath, child.id],
|
|
156
|
+
if (child.isVisible([...delegate.menuPath, child.id], contextMatcher, widget.node, widget)) {
|
|
148
157
|
if (CompoundMenuNode.is(child)) {
|
|
149
158
|
for (const grandchild of child.children) {
|
|
150
159
|
if (grandchild.isVisible([...delegate.menuPath, child.id, grandchild.id],
|
|
151
|
-
|
|
160
|
+
contextMatcher, widget.node, widget) && RenderedMenuNode.is(grandchild)) {
|
|
152
161
|
if (CommandMenu.is(grandchild)) {
|
|
153
162
|
result.push(new CommandMenuAsToolbarItemWrapper([...delegate.menuPath, child.id, grandchild.id], this.commandRegistry,
|
|
154
163
|
this.menuRegistry, this.contextKeyService, this.contextMenuRenderer, grandchild, child.id));
|
|
@@ -171,6 +180,29 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
|
|
|
171
180
|
return result;
|
|
172
181
|
}
|
|
173
182
|
|
|
183
|
+
/**
|
|
184
|
+
* A matcher that evaluates `when` clauses against the given widget rather than against whichever widget
|
|
185
|
+
* currently holds the focus. Keys that describe no aspect of the widget keep their ambient value, and the
|
|
186
|
+
* context scoped to the widget's DOM node is still consulted for the keys the overlay does not define.
|
|
187
|
+
*/
|
|
188
|
+
contextMatcherFor(widget: Widget): ContextMatcher {
|
|
189
|
+
const overlay = new Map<string, unknown>();
|
|
190
|
+
if (Navigatable.is(widget)) {
|
|
191
|
+
for (const [key, value] of Object.entries(this.resourceContextKey.toValues(widget.getResourceUri()))) {
|
|
192
|
+
overlay.set(key, value);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
for (const contribution of this.contextKeyContributionProvider.getContributions()) {
|
|
196
|
+
const entries = contribution.getContextKeyValues(widget);
|
|
197
|
+
if (entries) {
|
|
198
|
+
for (const [key, value] of entries) {
|
|
199
|
+
overlay.set(key, value);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return overlay.size ? this.contextKeyService.createOverlay(overlay) : this.contextKeyService;
|
|
204
|
+
}
|
|
205
|
+
|
|
174
206
|
/**
|
|
175
207
|
* Collects all context keys referenced by toolbar items and delegated menu nodes
|
|
176
208
|
* that may participate in rendering for the given widget.
|
|
@@ -18,13 +18,18 @@ import { enableJSDOM } from '../../test/jsdom';
|
|
|
18
18
|
|
|
19
19
|
let disableJSDOM = enableJSDOM();
|
|
20
20
|
import { expect } from 'chai';
|
|
21
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
21
22
|
import {
|
|
22
23
|
CommandMenu, CommandRegistry, CompoundMenuNode, Group, GroupImpl, MenuAction, MenuModelRegistry, MenuNode, MenuNodeFactory, MutableCompoundMenuNode,
|
|
23
24
|
Submenu, SubmenuImpl, SubMenuLink
|
|
24
25
|
} from '../../../common';
|
|
25
|
-
import { ContextKeyServiceDummyImpl } from '../../context-key-service';
|
|
26
|
+
import { ContextKeyServiceDummyImpl, ContextMatcher } from '../../context-key-service';
|
|
26
27
|
import { ContextMenuRenderer } from '../../context-menu-renderer';
|
|
27
28
|
import { Widget } from '../../widgets';
|
|
29
|
+
import URI from '../../../common/uri';
|
|
30
|
+
import { ResourceContextKey } from '../../resource-context-key';
|
|
31
|
+
import { WidgetContextKeyContribution } from '../../widget-context-key-contribution';
|
|
32
|
+
import { TabBarToolbar } from './tab-bar-toolbar';
|
|
28
33
|
import { TOOLBAR_WRAPPER_ID_SUFFIX } from './tab-bar-toolbar-menu-adapters';
|
|
29
34
|
import { TabBarToolbarRegistry } from './tab-bar-toolbar-registry';
|
|
30
35
|
import { TAB_BAR_TOOLBAR_CONTEXT_MENU, TabBarToolbarAction } from './tab-bar-toolbar-types';
|
|
@@ -135,6 +140,106 @@ describe('tab-bar-toolbar', () => {
|
|
|
135
140
|
testWidget.dispose();
|
|
136
141
|
});
|
|
137
142
|
|
|
143
|
+
it('evaluates delegated items against the resource of the widget that owns the toolbar', () => {
|
|
144
|
+
const testWidget = new TestToolbarWidget(new URI('probe:/a.probe'));
|
|
145
|
+
const commands = createCommandRegistry();
|
|
146
|
+
commands.registerCommand({ id: TEST_COMMAND, label: 'Test Command' }, { execute: () => { } });
|
|
147
|
+
const menuRegistry = createMenuRegistry(commands);
|
|
148
|
+
menuRegistry.registerMenuAction([...TEST_MENU_PATH, 'other'], { commandId: TEST_COMMAND, when: 'resourceScheme == probe' });
|
|
149
|
+
const registry = createToolbarRegistry(commands, menuRegistry, new TestContextKeyService());
|
|
150
|
+
registry.registerMenuDelegate(TEST_MENU_PATH, TestToolbarWidget.is);
|
|
151
|
+
|
|
152
|
+
const commandItem = registry.visibleItems(testWidget).find(item => item.id === `${TEST_COMMAND}${TOOLBAR_WRAPPER_ID_SUFFIX}`);
|
|
153
|
+
|
|
154
|
+
const otherWidget = new TestToolbarWidget(new URI('file:/a.txt'));
|
|
155
|
+
|
|
156
|
+
expect(commandItem).to.exist;
|
|
157
|
+
expect(registry.visibleItems(otherWidget)).to.be.empty;
|
|
158
|
+
testWidget.dispose();
|
|
159
|
+
otherWidget.dispose();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('evaluates registered items against the keys contributed for the widget that owns the toolbar', () => {
|
|
163
|
+
const testWidget = new TestToolbarWidget(new URI('probe:/a.probe'));
|
|
164
|
+
const otherWidget = new TestToolbarWidget(new URI('probe:/b.probe'));
|
|
165
|
+
const commands = createCommandRegistry();
|
|
166
|
+
commands.registerCommand({ id: TEST_COMMAND, label: 'Test Command' }, { execute: () => { } });
|
|
167
|
+
const menuRegistry = createMenuRegistry(commands);
|
|
168
|
+
const contribution: WidgetContextKeyContribution = {
|
|
169
|
+
getContextKeyValues: widget => [['activeCustomEditorId', widget === testWidget ? 'test.probeEditor' : '']]
|
|
170
|
+
};
|
|
171
|
+
const registry = createToolbarRegistry(commands, menuRegistry, new TestContextKeyService(), [contribution]);
|
|
172
|
+
registry.registerItem({ id: 'test-item', command: TEST_COMMAND, when: 'activeCustomEditorId == test.probeEditor' });
|
|
173
|
+
|
|
174
|
+
expect(registry.visibleItems(testWidget).map(item => item.id)).to.deep.equal(['test-item']);
|
|
175
|
+
expect(registry.visibleItems(otherWidget)).to.be.empty;
|
|
176
|
+
testWidget.dispose();
|
|
177
|
+
otherWidget.dispose();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('renders the submenu chevron against the widget that owns the toolbar', () => {
|
|
181
|
+
const testWidget = new TestToolbarWidget();
|
|
182
|
+
const otherWidget = new TestToolbarWidget();
|
|
183
|
+
const commands = createCommandRegistry();
|
|
184
|
+
commands.registerCommand({ id: TEST_SUBMENU_COMMAND, label: 'Test Submenu Command' }, { execute: () => { } });
|
|
185
|
+
const menuRegistry = createMenuRegistry(commands);
|
|
186
|
+
menuRegistry.registerSubmenu([...TEST_MENU_PATH, 'other', 'test-submenu'], 'Test Submenu');
|
|
187
|
+
menuRegistry.registerMenuAction([...TEST_MENU_PATH, 'other', 'test-submenu'],
|
|
188
|
+
{ commandId: TEST_SUBMENU_COMMAND, when: 'activeCustomEditorId == test.probeEditor' });
|
|
189
|
+
const contribution: WidgetContextKeyContribution = {
|
|
190
|
+
getContextKeyValues: widget => [['activeCustomEditorId', widget === testWidget ? 'test.probeEditor' : '']]
|
|
191
|
+
};
|
|
192
|
+
const registry = createToolbarRegistry(commands, menuRegistry, new TestContextKeyService(), [contribution]);
|
|
193
|
+
registry.registerMenuDelegate(TEST_MENU_PATH, TestToolbarWidget.is);
|
|
194
|
+
const submenuItem = registry.visibleItems(testWidget).find(item => item.id === `test-submenu${TOOLBAR_WRAPPER_ID_SUFFIX}`);
|
|
195
|
+
|
|
196
|
+
expect(submenuItem).to.exist;
|
|
197
|
+
expect(renderToStaticMarkup(submenuItem!.render(testWidget, registry.contextMatcherFor(testWidget)))).to.contain('chevron');
|
|
198
|
+
expect(renderToStaticMarkup(submenuItem!.render(testWidget, registry.contextMatcherFor(otherWidget)))).to.not.contain('chevron');
|
|
199
|
+
testWidget.dispose();
|
|
200
|
+
otherWidget.dispose();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('opens the More Actions menu against the widget that owns the toolbar', () => {
|
|
204
|
+
const testWidget = new TestToolbarWidget();
|
|
205
|
+
const otherWidget = new TestToolbarWidget();
|
|
206
|
+
const commands = createCommandRegistry();
|
|
207
|
+
commands.registerCommand({ id: TEST_SUBMENU_COMMAND, label: 'Test Submenu Command' }, { execute: () => { } });
|
|
208
|
+
const menuRegistry = createMenuRegistry(commands);
|
|
209
|
+
menuRegistry.registerSubmenu([...TEST_MENU_PATH, 'other', 'test-submenu'], 'Test Submenu');
|
|
210
|
+
menuRegistry.registerMenuAction([...TEST_MENU_PATH, 'other', 'test-submenu'],
|
|
211
|
+
{ commandId: TEST_SUBMENU_COMMAND, when: 'activeCustomEditorId == test.probeEditor' });
|
|
212
|
+
const contribution: WidgetContextKeyContribution = {
|
|
213
|
+
getContextKeyValues: widget => [['activeCustomEditorId', widget === testWidget ? 'test.probeEditor' : '']]
|
|
214
|
+
};
|
|
215
|
+
const ambient = new TestContextKeyService();
|
|
216
|
+
const registry = createToolbarRegistry(commands, menuRegistry, ambient, [contribution]);
|
|
217
|
+
registry.registerMenuDelegate(TEST_MENU_PATH, TestToolbarWidget.is);
|
|
218
|
+
|
|
219
|
+
let rendered: { contextKeyService?: ContextMatcher } | undefined;
|
|
220
|
+
const toolbar = new TabBarToolbar();
|
|
221
|
+
Reflect.set(toolbar, 'contextKeyService', ambient);
|
|
222
|
+
Reflect.set(toolbar, 'toolbarRegistry', registry);
|
|
223
|
+
Reflect.set(toolbar, 'contextMenuRenderer', {
|
|
224
|
+
render: (options: { contextKeyService?: ContextMatcher }) => {
|
|
225
|
+
rendered = options;
|
|
226
|
+
return { dispose: () => { } };
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
const setCurrent = Reflect.get(toolbar, 'setCurrent') as (widget: Widget) => void;
|
|
230
|
+
|
|
231
|
+
setCurrent.call(toolbar, testWidget);
|
|
232
|
+
toolbar.renderMoreContextMenu({ x: 0, y: 0 });
|
|
233
|
+
expect(rendered?.contextKeyService?.match('activeCustomEditorId == test.probeEditor')).to.be.true;
|
|
234
|
+
|
|
235
|
+
setCurrent.call(toolbar, otherWidget);
|
|
236
|
+
toolbar.renderMoreContextMenu({ x: 0, y: 0 });
|
|
237
|
+
expect(rendered?.contextKeyService?.match('activeCustomEditorId == test.probeEditor')).to.be.false;
|
|
238
|
+
|
|
239
|
+
testWidget.dispose();
|
|
240
|
+
otherWidget.dispose();
|
|
241
|
+
});
|
|
242
|
+
|
|
138
243
|
it('preserves the widget for wrapped submenu emptiness checks', () => {
|
|
139
244
|
const testWidget = new TestToolbarWidget();
|
|
140
245
|
const commands = createCommandRegistry();
|
|
@@ -166,6 +271,38 @@ class TestToolbarWidget extends Widget {
|
|
|
166
271
|
static is(candidate?: Widget): candidate is TestToolbarWidget {
|
|
167
272
|
return candidate instanceof TestToolbarWidget;
|
|
168
273
|
}
|
|
274
|
+
|
|
275
|
+
constructor(protected readonly resourceUri?: URI) {
|
|
276
|
+
super();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
getResourceUri(): URI | undefined {
|
|
280
|
+
return this.resourceUri;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
createMoveToUri(resourceUri: URI): URI | undefined {
|
|
284
|
+
return resourceUri;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Evaluates `key == value` clauses against the overlay alone, so that a visible item can only come from the
|
|
290
|
+
* values the registry attributed to the widget.
|
|
291
|
+
*/
|
|
292
|
+
class TestContextKeyService extends ContextKeyServiceDummyImpl {
|
|
293
|
+
override match(expression: string): boolean {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
override createOverlay(overlay: Iterable<[string, unknown]>): ContextMatcher {
|
|
298
|
+
const values = new Map(overlay);
|
|
299
|
+
return {
|
|
300
|
+
match: (expression: string) => {
|
|
301
|
+
const [key, value] = expression.split(' == ');
|
|
302
|
+
return values.get(key) === value;
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
169
306
|
}
|
|
170
307
|
|
|
171
308
|
class TestMenuNodeFactory implements MenuNodeFactory {
|
|
@@ -187,7 +324,8 @@ class TestMenuNodeFactory implements MenuNodeFactory {
|
|
|
187
324
|
|
|
188
325
|
createCommandMenu(item: MenuAction): CommandMenu {
|
|
189
326
|
return {
|
|
190
|
-
isVisible: (_path,
|
|
327
|
+
isVisible: (_path, contextMatcher, context, ...args) =>
|
|
328
|
+
(!item.when || contextMatcher.match(item.when, context)) && this.commands.isVisible(item.commandId, ...args),
|
|
191
329
|
isEnabled: (_path, ...args) => this.commands.isEnabled(item.commandId, ...args),
|
|
192
330
|
isToggled: (_path, ...args) => this.commands.isToggled(item.commandId, ...args),
|
|
193
331
|
id: item.commandId,
|
|
@@ -208,7 +346,12 @@ function createMenuRegistry(commands: CommandRegistry): MenuModelRegistry {
|
|
|
208
346
|
return new MenuModelRegistry({ getContributions: () => [] }, commands, new TestMenuNodeFactory(commands));
|
|
209
347
|
}
|
|
210
348
|
|
|
211
|
-
function createToolbarRegistry(
|
|
349
|
+
function createToolbarRegistry(
|
|
350
|
+
commands: CommandRegistry,
|
|
351
|
+
menuRegistry: MenuModelRegistry,
|
|
352
|
+
contextKeyService: ContextKeyServiceDummyImpl,
|
|
353
|
+
contextKeyContributions: WidgetContextKeyContribution[] = []
|
|
354
|
+
): TabBarToolbarRegistry {
|
|
212
355
|
const registry = new TabBarToolbarRegistry();
|
|
213
356
|
Reflect.set(registry, 'commandRegistry', commands);
|
|
214
357
|
Reflect.set(registry, 'contextKeyService', contextKeyService);
|
|
@@ -216,5 +359,9 @@ function createToolbarRegistry(commands: CommandRegistry, menuRegistry: MenuMode
|
|
|
216
359
|
Reflect.set(registry, 'keybindingRegistry', {});
|
|
217
360
|
Reflect.set(registry, 'labelParser', {});
|
|
218
361
|
Reflect.set(registry, 'contextMenuRenderer', { render: () => undefined } as unknown as ContextMenuRenderer);
|
|
362
|
+
const resourceContextKey = new ResourceContextKey();
|
|
363
|
+
Reflect.set(resourceContextKey, 'languages', { languages: [] });
|
|
364
|
+
Reflect.set(registry, 'resourceContextKey', resourceContextKey);
|
|
365
|
+
Reflect.set(registry, 'contextKeyContributionProvider', { getContributions: () => contextKeyContributions });
|
|
219
366
|
return registry;
|
|
220
367
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { inject, injectable, postConstruct } from 'inversify';
|
|
18
18
|
import * as React from 'react';
|
|
19
19
|
import { buttonKeyboardProps, isActivationKey } from '../../keyboard/keyboard-utils';
|
|
20
|
-
import { ContextKeyService } from '../../context-key-service';
|
|
20
|
+
import { ContextKeyService, ContextMatcher } from '../../context-key-service';
|
|
21
21
|
import { CommandRegistry, Disposable, DisposableCollection, nls } from '../../../common';
|
|
22
22
|
import { Anchor, ContextMenuAccess, ContextMenuRenderer } from '../../context-menu-renderer';
|
|
23
23
|
import { LabelParser } from '../../label-parser';
|
|
@@ -48,6 +48,7 @@ export function toAnchor(event: React.MouseEvent): Anchor {
|
|
|
48
48
|
export class TabBarToolbar extends ReactWidget {
|
|
49
49
|
|
|
50
50
|
protected current: Widget | undefined;
|
|
51
|
+
protected contextMatcher: ContextMatcher;
|
|
51
52
|
protected inline = new Map<string, TabBarToolbarItem>();
|
|
52
53
|
protected more = new Map<string, TabBarToolbarItem>();
|
|
53
54
|
|
|
@@ -73,6 +74,7 @@ export class TabBarToolbar extends ReactWidget {
|
|
|
73
74
|
|
|
74
75
|
@postConstruct()
|
|
75
76
|
protected init(): void {
|
|
77
|
+
this.contextMatcher = this.contextKeyService;
|
|
76
78
|
this.toDispose.pushAll([
|
|
77
79
|
this.keybindings.onKeybindingsChanged(() => this.maybeUpdate()),
|
|
78
80
|
this.contextKeyService.onDidChange(e => {
|
|
@@ -129,6 +131,7 @@ export class TabBarToolbar extends ReactWidget {
|
|
|
129
131
|
this.toDisposeOnSetCurrent.dispose();
|
|
130
132
|
this.toDispose.push(this.toDisposeOnSetCurrent);
|
|
131
133
|
this.current = current;
|
|
134
|
+
this.contextMatcher = current ? this.toolbarRegistry.contextMatcherFor(current) : this.contextKeyService;
|
|
132
135
|
if (current) {
|
|
133
136
|
const resetCurrent = () => {
|
|
134
137
|
this.setCurrent(undefined);
|
|
@@ -145,7 +148,7 @@ export class TabBarToolbar extends ReactWidget {
|
|
|
145
148
|
this.keybindingContextKeys.clear();
|
|
146
149
|
return <React.Fragment>
|
|
147
150
|
{this.renderMore()}
|
|
148
|
-
{[...this.inline.values()].map(item => item.render(this.current))}
|
|
151
|
+
{[...this.inline.values()].map(item => item.render(this.current, this.contextMatcher))}
|
|
149
152
|
</React.Fragment>;
|
|
150
153
|
}
|
|
151
154
|
|
|
@@ -199,7 +202,7 @@ export class TabBarToolbar extends ReactWidget {
|
|
|
199
202
|
args: [this.current],
|
|
200
203
|
anchor,
|
|
201
204
|
context: this.current?.node || this.node,
|
|
202
|
-
contextKeyService: this.
|
|
205
|
+
contextKeyService: this.contextMatcher,
|
|
203
206
|
includeAnchorArg: false,
|
|
204
207
|
onHide: () => toDisposeOnHide.dispose()
|
|
205
208
|
});
|
|
@@ -14,7 +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 { ContextKeyService } from '../../context-key-service';
|
|
17
|
+
import { ContextKeyService, ContextMatcher } from '../../context-key-service';
|
|
18
18
|
import { ReactTabBarToolbarAction, RenderedToolbarAction, TabBarToolbarActionBase } from './tab-bar-toolbar-types';
|
|
19
19
|
import { Widget } from '@lumino/widgets';
|
|
20
20
|
import { LabelIcon, LabelParser } from '../../label-parser';
|
|
@@ -29,10 +29,18 @@ import { ActionMenuNode, GroupImpl, MenuNode } from '../../../common/menu';
|
|
|
29
29
|
export interface TabBarToolbarItem {
|
|
30
30
|
id: string;
|
|
31
31
|
when?: string;
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* @param contextMatcher evaluates `when` clauses with the values that describe the given widget. Keys
|
|
34
|
+
* scoped to a DOM node keep resolving against the focused element, as no context node is passed here.
|
|
35
|
+
*/
|
|
36
|
+
isVisible(widget: Widget, contextMatcher: ContextMatcher): boolean;
|
|
33
37
|
isEnabled(widget: Widget): boolean;
|
|
34
38
|
isToggled(widget: Widget): boolean;
|
|
35
|
-
|
|
39
|
+
/**
|
|
40
|
+
* @param contextMatcher evaluates `when` clauses with the values that describe the given widget. Defaults
|
|
41
|
+
* to the ambient context, for toolbars that render items with no owning widget.
|
|
42
|
+
*/
|
|
43
|
+
render(widget?: Widget, contextMatcher?: ContextMatcher): React.ReactNode;
|
|
36
44
|
onDidChange?: Event<void>;
|
|
37
45
|
group?: string;
|
|
38
46
|
priority?: number;
|
|
@@ -68,12 +76,12 @@ class AbstractToolbarItemImpl<T extends TabBarToolbarActionBase> {
|
|
|
68
76
|
return this.action.onDidChange;
|
|
69
77
|
}
|
|
70
78
|
|
|
71
|
-
isVisible(widget: Widget): boolean {
|
|
79
|
+
isVisible(widget: Widget, contextMatcher: ContextMatcher): boolean {
|
|
72
80
|
if (this.action.isVisible) {
|
|
73
81
|
return this.action.isVisible(widget);
|
|
74
82
|
}
|
|
75
83
|
const actionVisible = !this.action.command || this.commandRegistry.isVisible(this.action.command, widget);
|
|
76
|
-
const contextMatches = !this.action.when ||
|
|
84
|
+
const contextMatches = !this.action.when || contextMatcher.match(this.action.when);
|
|
77
85
|
|
|
78
86
|
return actionVisible && contextMatches;
|
|
79
87
|
}
|
|
@@ -219,10 +219,17 @@
|
|
|
219
219
|
padding: 0 var(--theia-ui-padding);
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
/* Centered as a flex line rather than by text alignment: a codicon is a 16px
|
|
223
|
+
inline-block on the same baseline as the 13px label, so in normal flow it
|
|
224
|
+
rides above the text and stretches the line box. */
|
|
222
225
|
.theia-TreeContainer .theia-WelcomeView .theia-WelcomeViewButton {
|
|
223
226
|
width: 100%;
|
|
224
227
|
max-width: var(--theia-welcomeView-button-maxWidth);
|
|
225
228
|
margin: auto;
|
|
229
|
+
display: flex;
|
|
230
|
+
align-items: center;
|
|
231
|
+
justify-content: center;
|
|
232
|
+
gap: calc(var(--theia-ui-padding) / 2);
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
.theia-TreeContainer .theia-WelcomeView .theia-WelcomeViewCommandLink {
|