@theia/core 1.34.0 → 1.35.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.
Files changed (63) hide show
  1. package/README.md +6 -6
  2. package/i18n/nls.cs.json +3 -5
  3. package/i18n/nls.de.json +3 -5
  4. package/i18n/nls.es.json +3 -5
  5. package/i18n/nls.fr.json +3 -5
  6. package/i18n/nls.hu.json +3 -5
  7. package/i18n/nls.it.json +3 -5
  8. package/i18n/nls.ja.json +3 -5
  9. package/i18n/nls.json +3 -5
  10. package/i18n/nls.pl.json +3 -5
  11. package/i18n/nls.pt-br.json +3 -5
  12. package/i18n/nls.pt-pt.json +3 -5
  13. package/i18n/nls.ru.json +3 -5
  14. package/i18n/nls.zh-cn.json +3 -5
  15. package/lib/browser/common-frontend-contribution.d.ts +2 -1
  16. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  17. package/lib/browser/common-frontend-contribution.js +15 -7
  18. package/lib/browser/common-frontend-contribution.js.map +1 -1
  19. package/lib/browser/core-preferences.d.ts +1 -0
  20. package/lib/browser/core-preferences.d.ts.map +1 -1
  21. package/lib/browser/core-preferences.js +8 -11
  22. package/lib/browser/core-preferences.js.map +1 -1
  23. package/lib/browser/dialogs.d.ts +12 -0
  24. package/lib/browser/dialogs.d.ts.map +1 -1
  25. package/lib/browser/dialogs.js +70 -1
  26. package/lib/browser/dialogs.js.map +1 -1
  27. package/lib/browser/i18n/language-quick-pick-service.d.ts +2 -3
  28. package/lib/browser/i18n/language-quick-pick-service.d.ts.map +1 -1
  29. package/lib/browser/i18n/language-quick-pick-service.js +6 -4
  30. package/lib/browser/i18n/language-quick-pick-service.js.map +1 -1
  31. package/lib/browser/shell/application-shell.d.ts +4 -0
  32. package/lib/browser/shell/application-shell.d.ts.map +1 -1
  33. package/lib/browser/shell/application-shell.js +9 -0
  34. package/lib/browser/shell/application-shell.js.map +1 -1
  35. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +7 -2
  36. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -1
  37. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +33 -6
  38. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
  39. package/lib/browser/tree/search-box.js +1 -1
  40. package/lib/browser/tree/search-box.js.map +1 -1
  41. package/lib/common/encoding-service.d.ts +1 -0
  42. package/lib/common/encoding-service.d.ts.map +1 -1
  43. package/lib/common/severity.d.ts +1 -0
  44. package/lib/common/severity.d.ts.map +1 -1
  45. package/lib/common/severity.js +24 -5
  46. package/lib/common/severity.js.map +1 -1
  47. package/lib/electron-main/electron-main-application.d.ts.map +1 -1
  48. package/lib/electron-main/electron-main-application.js +3 -1
  49. package/lib/electron-main/electron-main-application.js.map +1 -1
  50. package/package.json +6 -6
  51. package/src/browser/common-frontend-contribution.ts +18 -9
  52. package/src/browser/core-preferences.ts +9 -11
  53. package/src/browser/dialogs.ts +72 -0
  54. package/src/browser/i18n/language-quick-pick-service.ts +8 -7
  55. package/src/browser/shell/application-shell.ts +13 -0
  56. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +39 -6
  57. package/src/browser/style/select-component.css +2 -3
  58. package/src/browser/style/sidepanel.css +1 -0
  59. package/src/browser/style/tree.css +2 -0
  60. package/src/browser/tree/search-box.ts +1 -1
  61. package/src/common/i18n/nls.metadata.json +14163 -13306
  62. package/src/common/severity.ts +21 -6
  63. package/src/electron-main/electron-main-application.ts +3 -1
@@ -401,6 +401,78 @@ export async function confirmExit(): Promise<boolean> {
401
401
  return safeToExit === true;
402
402
  }
403
403
 
404
+ export class ConfirmSaveDialogProps extends ConfirmDialogProps {
405
+ readonly save: string;
406
+ performSave: () => Promise<void>;
407
+ }
408
+
409
+ export class ConfirmSaveDialog extends ConfirmDialog {
410
+
411
+ protected saveButton: HTMLButtonElement | undefined;
412
+ constructor(
413
+ @inject(ConfirmSaveDialogProps) protected override readonly props: ConfirmSaveDialogProps
414
+ ) {
415
+ super(props);
416
+ this.contentNode.appendChild(this.createMessageNode(this.props.msg));
417
+ // reorder buttons
418
+ this.controlPanel.childNodes.forEach(child => this.controlPanel.removeChild(child));
419
+ [this.acceptButton, this.closeButton].forEach(child => {
420
+ if (typeof child !== 'undefined') {
421
+ this.controlPanel.appendChild(child);
422
+ }
423
+ });
424
+ this.appendSaveButton(props.save).addEventListener('click', async () => {
425
+ await props.performSave();
426
+ this.acceptButton?.click();
427
+ });
428
+ }
429
+
430
+ protected appendSaveButton(text: string = Dialog.OK): HTMLButtonElement {
431
+ this.saveButton = this.createButton(text);
432
+ this.controlPanel.appendChild(this.saveButton);
433
+ this.saveButton.classList.add('main');
434
+ return this.saveButton;
435
+ }
436
+
437
+ protected override onActivateRequest(msg: Message): void {
438
+ super.onActivateRequest(msg);
439
+ if (this.saveButton) {
440
+ this.saveButton.focus();
441
+ }
442
+ }
443
+
444
+ }
445
+
446
+ export async function confirmExitWithOrWithoutSaving(captionsToSave: string[], performSave: () => Promise<void>): Promise<boolean> {
447
+ const div: HTMLElement = document.createElement('div');
448
+ div.innerText = nls.localizeByDefault("Your changes will be lost if you don't save them.");
449
+
450
+ if (captionsToSave.length > 0) {
451
+ const span = document.createElement('span');
452
+ span.appendChild(document.createElement('br'));
453
+ captionsToSave.forEach(cap => {
454
+ const b = document.createElement('b');
455
+ b.innerText = cap;
456
+ span.appendChild(b);
457
+ span.appendChild(document.createElement('br'));
458
+ });
459
+ span.appendChild(document.createElement('br'));
460
+ div.appendChild(span);
461
+ const safeToExit = await new ConfirmSaveDialog({
462
+ title: nls.localizeByDefault('Do you want to save the changes to the following {0} files?', captionsToSave.length),
463
+ msg: div,
464
+ ok: nls.localizeByDefault("Don't Save"),
465
+ save: nls.localizeByDefault('Save All'),
466
+ cancel: Dialog.CANCEL,
467
+ performSave: performSave
468
+ }).open();
469
+ return safeToExit === true;
470
+ } else {
471
+ // fallback if not passed with an empty caption-list.
472
+ return confirmExit();
473
+ }
474
+
475
+ }
404
476
  @injectable()
405
477
  export class SingleTextInputDialogProps extends DialogProps {
406
478
  readonly confirmButtonLabel?: string;
@@ -20,8 +20,7 @@ import { AsyncLocalizationProvider, LanguageInfo } from '../../common/i18n/local
20
20
  import { QuickInputService, QuickPickItem, QuickPickSeparator } from '../quick-input';
21
21
  import { WindowService } from '../window/window-service';
22
22
 
23
- export interface LanguageQuickPickItem extends QuickPickItem {
24
- languageId: string
23
+ export interface LanguageQuickPickItem extends QuickPickItem, LanguageInfo {
25
24
  execute?(): Promise<void>
26
25
  }
27
26
 
@@ -32,13 +31,13 @@ export class LanguageQuickPickService {
32
31
  @inject(AsyncLocalizationProvider) protected readonly localizationProvider: AsyncLocalizationProvider;
33
32
  @inject(WindowService) protected readonly windowService: WindowService;
34
33
 
35
- async pickDisplayLanguage(): Promise<string | undefined> {
34
+ async pickDisplayLanguage(): Promise<LanguageInfo | undefined> {
36
35
  const quickInput = this.quickInputService.createQuickPick<LanguageQuickPickItem>();
37
36
  const installedItems = await this.getInstalledLanguages();
38
37
  const quickInputItems: (LanguageQuickPickItem | QuickPickSeparator)[] = [
39
38
  {
40
39
  type: 'separator',
41
- label: nls.localizeByDefault('Installed languages')
40
+ label: nls.localizeByDefault('Installed')
42
41
  },
43
42
  ...installedItems
44
43
  ];
@@ -55,7 +54,7 @@ export class LanguageQuickPickService {
55
54
  if (availableItems.length > 0) {
56
55
  quickInputItems.push({
57
56
  type: 'separator',
58
- label: nls.localizeByDefault('Available languages')
57
+ label: nls.localizeByDefault('Available')
59
58
  });
60
59
  const installed = new Set(installedItems.map(e => e.languageId));
61
60
  for (const available of availableItems) {
@@ -77,7 +76,7 @@ export class LanguageQuickPickService {
77
76
  // Some language quick pick items want to install additional languages
78
77
  // We have to await that before returning the selected locale
79
78
  await selectedItem.execute?.();
80
- resolve(selectedItem.languageId);
79
+ resolve(selectedItem);
81
80
  } else {
82
81
  resolve(undefined);
83
82
  }
@@ -122,7 +121,9 @@ export class LanguageQuickPickService {
122
121
  return {
123
122
  label,
124
123
  description,
125
- languageId: id
124
+ languageId: id,
125
+ languageName: language.languageName,
126
+ localizedLanguageName: language.localizedLanguageName
126
127
  };
127
128
  }
128
129
  }
@@ -89,6 +89,8 @@ export class DockPanelRenderer implements DockLayout.IRenderer {
89
89
 
90
90
  readonly tabBarClasses: string[] = [];
91
91
 
92
+ private readonly onDidCreateTabBarEmitter = new Emitter<TabBar<Widget>>();
93
+
92
94
  constructor(
93
95
  @inject(TabBarRendererFactory) protected readonly tabBarRendererFactory: TabBarRendererFactory,
94
96
  @inject(TabBarToolbarRegistry) protected readonly tabBarToolbarRegistry: TabBarToolbarRegistry,
@@ -96,6 +98,10 @@ export class DockPanelRenderer implements DockLayout.IRenderer {
96
98
  @inject(BreadcrumbsRendererFactory) protected readonly breadcrumbsRendererFactory: BreadcrumbsRendererFactory,
97
99
  ) { }
98
100
 
101
+ get onDidCreateTabBar(): CommonEvent<TabBar<Widget>> {
102
+ return this.onDidCreateTabBarEmitter.event;
103
+ }
104
+
99
105
  createTabBar(): TabBar<Widget> {
100
106
  const renderer = this.tabBarRendererFactory();
101
107
  const tabBar = new ToolbarAwareTabBar(
@@ -115,6 +121,7 @@ export class DockPanelRenderer implements DockLayout.IRenderer {
115
121
  tabBar.disposed.connect(() => renderer.dispose());
116
122
  renderer.contextMenuPath = SHELL_TABBAR_CONTEXT_MENU;
117
123
  tabBar.currentChanged.connect(this.onCurrentTabChanged, this);
124
+ this.onDidCreateTabBarEmitter.fire(tabBar);
118
125
  return tabBar;
119
126
  }
120
127
 
@@ -221,6 +228,11 @@ export class ApplicationShell extends Widget {
221
228
  @inject(TheiaDockPanel.Factory)
222
229
  protected readonly dockPanelFactory: TheiaDockPanel.Factory;
223
230
 
231
+ private _mainPanelRenderer: DockPanelRenderer;
232
+ get mainPanelRenderer(): DockPanelRenderer {
233
+ return this._mainPanelRenderer;
234
+ }
235
+
224
236
  /**
225
237
  * Construct a new application shell.
226
238
  */
@@ -496,6 +508,7 @@ export class ApplicationShell extends Widget {
496
508
  const renderer = this.dockPanelRendererFactory();
497
509
  renderer.tabBarClasses.push(MAIN_BOTTOM_AREA_CLASS);
498
510
  renderer.tabBarClasses.push(MAIN_AREA_CLASS);
511
+ this._mainPanelRenderer = renderer;
499
512
  const dockPanel = this.dockPanelFactory({
500
513
  mode: 'multiple-document',
501
514
  renderer,
@@ -16,6 +16,7 @@
16
16
 
17
17
  import { inject, injectable } from 'inversify';
18
18
  import * as React from 'react';
19
+ import { ContextKeyService } from '../../context-key-service';
19
20
  import { CommandRegistry, Disposable, DisposableCollection, MenuCommandExecutor, MenuModelRegistry, MenuPath, nls } from '../../../common';
20
21
  import { Anchor, ContextMenuAccess, ContextMenuRenderer } from '../../context-menu-renderer';
21
22
  import { LabelIcon, LabelParser } from '../../label-parser';
@@ -41,12 +42,15 @@ export class TabBarToolbar extends ReactWidget {
41
42
  protected inline = new Map<string, TabBarToolbarItem | ReactTabBarToolbarItem>();
42
43
  protected more = new Map<string, TabBarToolbarItem>();
43
44
 
45
+ protected contextKeyListener: Disposable | undefined;
46
+
44
47
  @inject(CommandRegistry) protected readonly commands: CommandRegistry;
45
48
  @inject(LabelParser) protected readonly labelParser: LabelParser;
46
49
  @inject(MenuModelRegistry) protected readonly menus: MenuModelRegistry;
47
50
  @inject(MenuCommandExecutor) protected readonly menuCommandExecutor: MenuCommandExecutor;
48
51
  @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer;
49
52
  @inject(TabBarToolbarRegistry) protected readonly toolbarRegistry: TabBarToolbarRegistry;
53
+ @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService;
50
54
 
51
55
  constructor() {
52
56
  super();
@@ -60,13 +64,22 @@ export class TabBarToolbar extends ReactWidget {
60
64
  updateItems(items: Array<TabBarToolbarItem | ReactTabBarToolbarItem>, current: Widget | undefined): void {
61
65
  this.inline.clear();
62
66
  this.more.clear();
67
+
68
+ const contextKeys = new Set<string>();
63
69
  for (const item of items.sort(TabBarToolbarItem.PRIORITY_COMPARATOR).reverse()) {
64
70
  if ('render' in item || item.group === undefined || item.group === 'navigation') {
65
71
  this.inline.set(item.id, item);
66
72
  } else {
67
73
  this.more.set(item.id, item);
68
74
  }
75
+
76
+ if (item.when) {
77
+ this.contextKeyService.parseKeys(item.when)?.forEach(key => contextKeys.add(key));
78
+ }
69
79
  }
80
+
81
+ this.updateContextKeyListener(contextKeys);
82
+
70
83
  this.setCurrent(current);
71
84
  if (!items.length) {
72
85
  this.hide();
@@ -97,6 +110,17 @@ export class TabBarToolbar extends ReactWidget {
97
110
  }
98
111
  }
99
112
 
113
+ protected updateContextKeyListener(contextKeys: Set<string>): void {
114
+ this.contextKeyListener?.dispose();
115
+ if (contextKeys.size > 0) {
116
+ this.contextKeyListener = this.contextKeyService.onDidChange(event => {
117
+ if (event.affects(contextKeys)) {
118
+ this.update();
119
+ }
120
+ });
121
+ }
122
+ }
123
+
100
124
  protected render(): React.ReactNode {
101
125
  return <React.Fragment>
102
126
  {this.renderMore()}
@@ -124,7 +148,8 @@ export class TabBarToolbar extends ReactWidget {
124
148
  classNames.push(iconClass);
125
149
  }
126
150
  const tooltip = item.tooltip || (command && command.label);
127
- const toolbarItemClassNames = this.getToolbarItemClassNames(command?.id ?? item.command);
151
+
152
+ const toolbarItemClassNames = this.getToolbarItemClassNames(item);
128
153
  if (item.menuPath && !item.command) { toolbarItemClassNames.push('enabled'); }
129
154
  return <div key={item.id}
130
155
  ref={this.onRender}
@@ -139,13 +164,13 @@ export class TabBarToolbar extends ReactWidget {
139
164
  </div>;
140
165
  }
141
166
 
142
- protected getToolbarItemClassNames(commandId: string | undefined): string[] {
167
+ protected getToolbarItemClassNames(item: AnyToolbarItem): string[] {
143
168
  const classNames = [TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM];
144
- if (commandId) {
145
- if (this.commandIsEnabled(commandId)) {
169
+ if (item.command) {
170
+ if (this.commandIsEnabled(item.command) && this.evaluateWhenClause(item.when)) {
146
171
  classNames.push('enabled');
147
172
  }
148
- if (this.commandIsToggled(commandId)) {
173
+ if (this.commandIsToggled(item.command)) {
149
174
  classNames.push('toggled');
150
175
  }
151
176
  }
@@ -221,11 +246,20 @@ export class TabBarToolbar extends ReactWidget {
221
246
  return this.commands.isToggled(command, this.current);
222
247
  }
223
248
 
249
+ protected evaluateWhenClause(whenClause: string | undefined): boolean {
250
+ return whenClause ? this.contextKeyService.match(whenClause) : true;
251
+ }
252
+
224
253
  protected executeCommand = (e: React.MouseEvent<HTMLElement>) => {
225
254
  e.preventDefault();
226
255
  e.stopPropagation();
227
256
 
228
257
  const item: AnyToolbarItem | undefined = this.inline.get(e.currentTarget.id);
258
+
259
+ if (!this.evaluateWhenClause(item?.when)) {
260
+ return;
261
+ }
262
+
229
263
  if (item?.command && item.menuPath) {
230
264
  this.menuCommandExecutor.executeCommand(item.menuPath, item.command, this.current);
231
265
  } else if (item?.command) {
@@ -245,7 +279,6 @@ export class TabBarToolbar extends ReactWidget {
245
279
  protected onMouseUpEvent = (e: React.MouseEvent<HTMLElement>) => {
246
280
  e.currentTarget.classList.remove('active');
247
281
  };
248
-
249
282
  }
250
283
 
251
284
  export namespace TabBarToolbar {
@@ -54,7 +54,8 @@
54
54
  text-overflow: ellipsis;
55
55
  overflow: hidden;
56
56
  display: flex;
57
- padding: 2px 5px;
57
+ padding: 0px 5px;
58
+ line-height: 22px;
58
59
  }
59
60
 
60
61
  .theia-select-component-dropdown .theia-select-component-description {
@@ -87,8 +88,6 @@
87
88
  color: var(--theia-list-activeSelectionForeground);
88
89
  cursor: pointer;
89
90
  background: var(--theia-list-activeSelectionBackground);
90
- outline: var(--theia-focusBorder) solid 1px;
91
- outline-offset: -1px;
92
91
  }
93
92
 
94
93
  .theia-select-component-dropdown .theia-select-component-separator {
@@ -318,6 +318,7 @@
318
318
  margin-left: 14px;
319
319
  text-transform: uppercase;
320
320
  font-size: var(--theia-ui-font-size0);
321
+ min-width: 1rem;
321
322
  }
322
323
 
323
324
  .theia-sidepanel-toolbar .p-TabBar-toolbar .item {
@@ -100,6 +100,8 @@
100
100
  .theia-Tree:focus-within .theia-TreeNode.theia-mod-selected {
101
101
  background: var(--theia-list-activeSelectionBackground);
102
102
  color: var(--theia-list-activeSelectionForeground) !important;
103
+ outline: var(--theia-focusBorder) solid 1px;
104
+ outline-offset: -1px;
103
105
  }
104
106
 
105
107
  .theia-Tree:focus-within .theia-TreeNode.theia-mod-selected .theia-TreeNodeTail,
@@ -254,7 +254,7 @@ export class SearchBox extends BaseWidget {
254
254
  SearchBox.Styles.BUTTON,
255
255
  ...SearchBox.Styles.FILTER,
256
256
  );
257
- filter.title = nls.localizeByDefault('Enable Filter on Type');
257
+ filter.title = nls.localizeByDefault('Filter on Type');
258
258
  buttons.appendChild(filter);
259
259
  filter.onclick = this.fireFilterToggle.bind(this);
260
260
  }