@theia/core 1.30.0-next.7 → 1.30.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 (129) hide show
  1. package/README.md +6 -6
  2. package/i18n/nls.cs.json +16 -0
  3. package/i18n/nls.de.json +16 -0
  4. package/i18n/nls.es.json +16 -0
  5. package/i18n/nls.fr.json +16 -0
  6. package/i18n/nls.hu.json +16 -0
  7. package/i18n/nls.it.json +16 -0
  8. package/i18n/nls.ja.json +16 -0
  9. package/i18n/nls.json +16 -0
  10. package/i18n/nls.pl.json +16 -0
  11. package/i18n/nls.pt-br.json +16 -0
  12. package/i18n/nls.pt-pt.json +16 -0
  13. package/i18n/nls.ru.json +16 -0
  14. package/i18n/nls.zh-cn.json +20 -4
  15. package/lib/browser/about-dialog.d.ts +9 -0
  16. package/lib/browser/about-dialog.d.ts.map +1 -1
  17. package/lib/browser/about-dialog.js +34 -4
  18. package/lib/browser/about-dialog.js.map +1 -1
  19. package/lib/browser/core-preferences.d.ts +3 -1
  20. package/lib/browser/core-preferences.d.ts.map +1 -1
  21. package/lib/browser/core-preferences.js +38 -2
  22. package/lib/browser/core-preferences.js.map +1 -1
  23. package/lib/browser/dialogs/react-dialog.d.ts +1 -0
  24. package/lib/browser/dialogs/react-dialog.d.ts.map +1 -1
  25. package/lib/browser/dialogs/react-dialog.js +10 -2
  26. package/lib/browser/dialogs/react-dialog.js.map +1 -1
  27. package/lib/browser/frontend-application-config-provider.spec.js +1 -1
  28. package/lib/browser/frontend-application-config-provider.spec.js.map +1 -1
  29. package/lib/browser/frontend-application-module.d.ts.map +1 -1
  30. package/lib/browser/frontend-application-module.js +5 -0
  31. package/lib/browser/frontend-application-module.js.map +1 -1
  32. package/lib/browser/icon-theme-service.d.ts +5 -1
  33. package/lib/browser/icon-theme-service.d.ts.map +1 -1
  34. package/lib/browser/icon-theme-service.js +21 -1
  35. package/lib/browser/icon-theme-service.js.map +1 -1
  36. package/lib/browser/label-provider.d.ts +13 -1
  37. package/lib/browser/label-provider.d.ts.map +1 -1
  38. package/lib/browser/label-provider.js +34 -28
  39. package/lib/browser/label-provider.js.map +1 -1
  40. package/lib/browser/preferences/preference-contribution.d.ts +2 -0
  41. package/lib/browser/preferences/preference-contribution.d.ts.map +1 -1
  42. package/lib/browser/preferences/preference-contribution.js +7 -0
  43. package/lib/browser/preferences/preference-contribution.js.map +1 -1
  44. package/lib/browser/preloader.d.ts.map +1 -1
  45. package/lib/browser/preloader.js +4 -1
  46. package/lib/browser/preloader.js.map +1 -1
  47. package/lib/browser/shell/application-shell.d.ts +1 -0
  48. package/lib/browser/shell/application-shell.d.ts.map +1 -1
  49. package/lib/browser/shell/application-shell.js +8 -0
  50. package/lib/browser/shell/application-shell.js.map +1 -1
  51. package/lib/browser/shell/tab-bars.d.ts +10 -2
  52. package/lib/browser/shell/tab-bars.d.ts.map +1 -1
  53. package/lib/browser/shell/tab-bars.js +14 -5
  54. package/lib/browser/shell/tab-bars.js.map +1 -1
  55. package/lib/browser/shell/theia-dock-panel.d.ts +4 -2
  56. package/lib/browser/shell/theia-dock-panel.d.ts.map +1 -1
  57. package/lib/browser/shell/theia-dock-panel.js +5 -0
  58. package/lib/browser/shell/theia-dock-panel.js.map +1 -1
  59. package/lib/browser/theming.d.ts +5 -1
  60. package/lib/browser/theming.d.ts.map +1 -1
  61. package/lib/browser/theming.js +19 -1
  62. package/lib/browser/theming.js.map +1 -1
  63. package/lib/browser/view-container.js +1 -1
  64. package/lib/browser/view-container.js.map +1 -1
  65. package/lib/browser/window/window-title-service.d.ts +32 -0
  66. package/lib/browser/window/window-title-service.d.ts.map +1 -0
  67. package/lib/browser/window/window-title-service.js +121 -0
  68. package/lib/browser/window/window-title-service.js.map +1 -0
  69. package/lib/browser/window/window-title-updater.d.ts +19 -0
  70. package/lib/browser/window/window-title-updater.d.ts.map +1 -0
  71. package/lib/browser/window/window-title-updater.js +108 -0
  72. package/lib/browser/window/window-title-updater.js.map +1 -0
  73. package/lib/common/event.d.ts +8 -3
  74. package/lib/common/event.d.ts.map +1 -1
  75. package/lib/common/event.js +3 -1
  76. package/lib/common/event.js.map +1 -1
  77. package/lib/common/json-schema.d.ts +1 -0
  78. package/lib/common/json-schema.d.ts.map +1 -1
  79. package/lib/common/message-rpc/rpc-message-encoder.d.ts.map +1 -1
  80. package/lib/common/message-rpc/rpc-message-encoder.js +1 -1
  81. package/lib/common/message-rpc/rpc-message-encoder.js.map +1 -1
  82. package/lib/common/message-rpc/rpc-protocol.d.ts +0 -1
  83. package/lib/common/message-rpc/rpc-protocol.d.ts.map +1 -1
  84. package/lib/common/message-rpc/rpc-protocol.js +6 -13
  85. package/lib/common/message-rpc/rpc-protocol.js.map +1 -1
  86. package/lib/common/progress-service.d.ts +1 -1
  87. package/lib/common/progress-service.d.ts.map +1 -1
  88. package/lib/common/progress-service.js +2 -2
  89. package/lib/common/progress-service.js.map +1 -1
  90. package/lib/common/quick-pick-service.d.ts +2 -2
  91. package/lib/common/quick-pick-service.d.ts.map +1 -1
  92. package/lib/electron-browser/menu/electron-menu-contribution.d.ts +17 -1
  93. package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
  94. package/lib/electron-browser/menu/electron-menu-contribution.js +87 -1
  95. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  96. package/lib/electron-browser/menu/electron-menu-module.d.ts.map +1 -1
  97. package/lib/electron-browser/menu/electron-menu-module.js +2 -0
  98. package/lib/electron-browser/menu/electron-menu-module.js.map +1 -1
  99. package/lib/electron-main/electron-main-application-module.d.ts.map +1 -1
  100. package/lib/electron-main/electron-main-application-module.js +3 -0
  101. package/lib/electron-main/electron-main-application-module.js.map +1 -1
  102. package/package.json +6 -6
  103. package/src/browser/about-dialog.tsx +45 -2
  104. package/src/browser/core-preferences.ts +43 -4
  105. package/src/browser/dialogs/react-dialog.tsx +10 -2
  106. package/src/browser/frontend-application-config-provider.spec.ts +1 -1
  107. package/src/browser/frontend-application-module.ts +5 -0
  108. package/src/browser/icon-theme-service.ts +21 -2
  109. package/src/browser/label-provider.ts +38 -29
  110. package/src/browser/preferences/preference-contribution.ts +9 -0
  111. package/src/browser/preloader.ts +4 -1
  112. package/src/browser/shell/application-shell.ts +9 -0
  113. package/src/browser/shell/tab-bars.ts +14 -5
  114. package/src/browser/shell/theia-dock-panel.ts +6 -1
  115. package/src/browser/style/about.css +13 -4
  116. package/src/browser/theming.ts +21 -3
  117. package/src/browser/view-container.ts +2 -2
  118. package/src/browser/window/window-title-service.ts +107 -0
  119. package/src/browser/window/window-title-updater.ts +94 -0
  120. package/src/common/event.ts +13 -4
  121. package/src/common/json-schema.ts +1 -0
  122. package/src/common/message-rpc/rpc-message-encoder.ts +1 -1
  123. package/src/common/message-rpc/rpc-protocol.ts +8 -14
  124. package/src/common/progress-service.ts +2 -2
  125. package/src/common/quick-pick-service.ts +2 -2
  126. package/src/electron-browser/menu/electron-menu-contribution.ts +87 -7
  127. package/src/electron-browser/menu/electron-menu-module.ts +3 -1
  128. package/src/electron-browser/menu/electron-menu-style.css +25 -0
  129. package/src/electron-main/electron-main-application-module.ts +4 -0
@@ -68,6 +68,11 @@ export interface LabelProviderContribution {
68
68
  */
69
69
  getLongName?(element: object): string | undefined;
70
70
 
71
+ /**
72
+ * A compromise between {@link getName} and {@link getLongName}. Can be used to supplement getName in contexts that allow both a primary display field and extra detail.
73
+ */
74
+ getDetails?(element: object): string | undefined;
75
+
71
76
  /**
72
77
  * Emit when something has changed that may result in this label provider returning a different
73
78
  * value for one or more properties (name, icon etc).
@@ -168,6 +173,14 @@ export class DefaultUriLabelProviderContribution implements LabelProviderContrib
168
173
  return uri && uri.path.fsPath();
169
174
  }
170
175
 
176
+ getDetails(element: URI | URIIconReference): string | undefined {
177
+ const uri = this.getUri(element);
178
+ if (uri) {
179
+ return this.getLongName(uri.parent);
180
+ }
181
+ return this.getLongName(element);
182
+ }
183
+
171
184
  protected getUri(element: URI | URIIconReference): URI | undefined {
172
185
  return URIIconReference.is(element) ? element.uri : element;
173
186
  }
@@ -325,15 +338,7 @@ export class LabelProvider implements FrontendApplicationContribution {
325
338
  * @return the icon class
326
339
  */
327
340
  getIcon(element: object): string {
328
- const contributions = this.findContribution(element);
329
- for (const contribution of contributions) {
330
- const value = contribution.getIcon && contribution.getIcon(element);
331
- if (value === undefined) {
332
- continue;
333
- }
334
- return value;
335
- }
336
- return '';
341
+ return this.handleRequest(element, 'getIcon') ?? '';
337
342
  }
338
343
 
339
344
  /**
@@ -341,15 +346,7 @@ export class LabelProvider implements FrontendApplicationContribution {
341
346
  * @return the short name
342
347
  */
343
348
  getName(element: object): string {
344
- const contributions = this.findContribution(element);
345
- for (const contribution of contributions) {
346
- const value = contribution.getName && contribution.getName(element);
347
- if (value === undefined) {
348
- continue;
349
- }
350
- return value;
351
- }
352
- return '<unknown>';
349
+ return this.handleRequest(element, 'getName') ?? '<unknown>';
353
350
  }
354
351
 
355
352
  /**
@@ -357,21 +354,33 @@ export class LabelProvider implements FrontendApplicationContribution {
357
354
  * @return the long name
358
355
  */
359
356
  getLongName(element: object): string {
360
- const contributions = this.findContribution(element);
361
- for (const contribution of contributions) {
362
- const value = contribution.getLongName && contribution.getLongName(element);
363
- if (value === undefined) {
364
- continue;
357
+ return this.handleRequest(element, 'getLongName') ?? '';
358
+ }
359
+
360
+ /**
361
+ * Get details from the list of available {@link LabelProviderContribution} for the given element.
362
+ * @return the details
363
+ * Can be used to supplement {@link getName} in contexts that allow both a primary display field and extra detail.
364
+ */
365
+ getDetails(element: object): string {
366
+ return this.handleRequest(element, 'getDetails') ?? '';
367
+ }
368
+
369
+ protected handleRequest(element: object, method: keyof Omit<LabelProviderContribution, 'canHandle' | 'onDidChange' | 'affects'>): string | undefined {
370
+ for (const contribution of this.findContribution(element, method)) {
371
+ const value = contribution[method]?.(element);
372
+ if (value !== undefined) {
373
+ return value;
365
374
  }
366
- return value;
367
375
  }
368
- return '';
369
376
  }
370
377
 
371
- protected findContribution(element: object): LabelProviderContribution[] {
372
- const prioritized = Prioritizeable.prioritizeAllSync(this.contributionProvider.getContributions(), contrib =>
378
+ protected findContribution(element: object, method?: keyof Omit<LabelProviderContribution, 'canHandle' | 'onDidChange' | 'affects'>): LabelProviderContribution[] {
379
+ const candidates = method
380
+ ? this.contributionProvider.getContributions().filter(candidate => candidate[method])
381
+ : this.contributionProvider.getContributions();
382
+ return Prioritizeable.prioritizeAllSync(candidates, contrib =>
373
383
  contrib.canHandle(element)
374
- );
375
- return prioritized.map(c => c.value);
384
+ ).map(entry => entry.value);
376
385
  }
377
386
  }
@@ -356,6 +356,15 @@ export class PreferenceSchemaProvider extends PreferenceProvider {
356
356
  return [][Symbol.iterator]();
357
357
  }
358
358
 
359
+ getSchemaProperty(key: string): PreferenceDataProperty | undefined {
360
+ return this.combinedSchema.properties[key];
361
+ }
362
+
363
+ updateSchemaProperty(key: string, property: PreferenceDataProperty): void {
364
+ this.updateSchemaProps(key, property);
365
+ this.fireDidPreferenceSchemaChanged();
366
+ }
367
+
359
368
  protected updateSchemaProps(key: string, property: PreferenceDataProperty): void {
360
369
  this.combinedSchema.properties[key] = property;
361
370
 
@@ -58,7 +58,10 @@ async function loadBackendOS(): Promise<void> {
58
58
  }
59
59
 
60
60
  function initBackground(): void {
61
- const value = window.localStorage.getItem(DEFAULT_BACKGROUND_COLOR_STORAGE_KEY) || '#1d1d1d';
61
+ // The default light background color is based on the `colors#editor.background` value from
62
+ // `packages/monaco/data/monaco-themes/vscode/dark_vs.json` and the dark background comes from the `light_vs.json`.
63
+ const dark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
64
+ const value = window.localStorage.getItem(DEFAULT_BACKGROUND_COLOR_STORAGE_KEY) || (dark ? '#1E1E1E' : '#FFFFFF');
62
65
  const documentElement = document.documentElement;
63
66
  documentElement.style.setProperty('--theia-editor-background', value);
64
67
  }
@@ -1957,6 +1957,15 @@ export namespace ApplicationShell {
1957
1957
  */
1958
1958
  export type Area = 'main' | 'top' | 'left' | 'right' | 'bottom' | 'secondaryWindow';
1959
1959
 
1960
+ export const areaLabels: Record<Area, string> = {
1961
+ main: nls.localizeByDefault('Main'),
1962
+ top: nls.localize('theia/shell-area/top', 'Top'),
1963
+ left: nls.localize('theia/shell-area/left', 'Left'),
1964
+ right: nls.localize('theia/shell-area/right', 'Right'),
1965
+ bottom: nls.localize('theia/shell-area/bottom', 'Bottom'),
1966
+ secondaryWindow: nls.localize('theia/shell-area/secondary', 'Secondary Window'),
1967
+ };
1968
+
1960
1969
  /**
1961
1970
  * The _side areas_ are those shell areas that can be collapsed and expanded,
1962
1971
  * i.e. `left`, `right`, and `bottom`.
@@ -141,11 +141,12 @@ export class TabBarRenderer extends TabBar.Renderer {
141
141
  * Render tabs with the default DOM structure, but additionally register a context menu listener.
142
142
  * @param {SideBarRenderData} data Data used to render the tab.
143
143
  * @param {boolean} isInSidePanel An optional check which determines if the tab is in the side-panel.
144
+ * @param {boolean} isPartOfHiddenTabBar An optional check which determines if the tab is in the hidden horizontal tab bar.
144
145
  * @returns {VirtualElement} The virtual element of the rendered tab.
145
146
  */
146
- override renderTab(data: SideBarRenderData, isInSidePanel?: boolean): VirtualElement {
147
+ override renderTab(data: SideBarRenderData, isInSidePanel?: boolean, isPartOfHiddenTabBar?: boolean): VirtualElement {
147
148
  const title = data.title;
148
- const id = this.createTabId(data.title);
149
+ const id = this.createTabId(data.title, isPartOfHiddenTabBar);
149
150
  const key = this.createTabKey(data);
150
151
  const style = this.createTabStyle(data);
151
152
  const className = this.createTabClass(data);
@@ -177,8 +178,15 @@ export class TabBarRenderer extends TabBar.Renderer {
177
178
  );
178
179
  }
179
180
 
180
- createTabId(title: Title<Widget>): string {
181
- return 'shell-tab-' + title.owner.id;
181
+ /**
182
+ * Generate ID for an entry in the tab bar
183
+ * @param {Title<Widget>} title Title of the widget controlled by this tab bar
184
+ * @param {boolean} isPartOfHiddenTabBar Tells us if this entry is part of the hidden horizontal tab bar.
185
+ * If yes, add a suffix to differentiate it's ID from the entry in the visible tab bar
186
+ * @returns {string} DOM element ID
187
+ */
188
+ createTabId(title: Title<Widget>, isPartOfHiddenTabBar = false): string {
189
+ return 'shell-tab-' + title.owner.id + (isPartOfHiddenTabBar ? '-hidden' : '');
182
190
  }
183
191
 
184
192
  /**
@@ -904,7 +912,8 @@ export class SideTabBar extends ScrollableTabBar {
904
912
  } else {
905
913
  rd = { title, current, zIndex };
906
914
  }
907
- content[i] = renderer.renderTab(rd, true);
915
+ // Based on how renderTabs() is called, assume renderData will be undefined when invoked for this.hiddenContentNode
916
+ content[i] = renderer.renderTab(rd, true, renderData === undefined);
908
917
  }
909
918
  VirtualDOM.render(content, host);
910
919
  }
@@ -20,7 +20,7 @@ import { Signal } from '@phosphor/signaling';
20
20
  import { Disposable, DisposableCollection } from '../../common/disposable';
21
21
  import { UnsafeWidgetUtilities } from '../widgets';
22
22
  import { CorePreferences } from '../core-preferences';
23
- import { Emitter, environment } from '../../common';
23
+ import { Emitter, Event, environment } from '../../common';
24
24
 
25
25
  export const MAXIMIZED_CLASS = 'theia-maximized';
26
26
  export const ACTIVE_TABBAR_CLASS = 'theia-tabBar-active';
@@ -50,6 +50,10 @@ export class TheiaDockPanel extends DockPanel {
50
50
 
51
51
  protected readonly onDidToggleMaximizedEmitter = new Emitter<Widget>();
52
52
  readonly onDidToggleMaximized = this.onDidToggleMaximizedEmitter.event;
53
+ protected readonly onDidChangeCurrentEmitter = new Emitter<Title<Widget> | undefined>();
54
+ get onDidChangeCurrent(): Event<Title<Widget> | undefined> {
55
+ return this.onDidChangeCurrentEmitter.event;
56
+ }
53
57
 
54
58
  constructor(options?: DockPanel.IOptions,
55
59
  protected readonly preferences?: CorePreferences
@@ -114,6 +118,7 @@ export class TheiaDockPanel extends DockPanel {
114
118
  title.owner.disposed.disconnect(resetCurrent)
115
119
  ));
116
120
  }
121
+ this.onDidChangeCurrentEmitter.fire(title);
117
122
  }
118
123
 
119
124
  markActiveTabBar(title?: Title<Widget>): void {
@@ -14,14 +14,23 @@
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
16
 
17
- ul.theia-aboutDialog {
17
+ .theia-aboutDialog .about-details {
18
+ padding-left: 10px;
19
+ }
20
+
21
+ .theia-aboutDialog .about-details a {
22
+ cursor: pointer;
23
+ }
24
+
25
+ ul.theia-aboutDialog {
18
26
  flex: 1 100%;
19
27
  padding-bottom: calc(var(--theia-ui-padding) * 3);
20
- }
21
- ul.theia-aboutExtensions {
28
+ }
29
+
30
+ ul.theia-aboutExtensions {
22
31
  height: 200px;
23
32
  overflow: hidden;
24
33
  overflow-y: scroll;
25
34
  list-style-type: none;
26
35
  padding: 0;
27
- }
36
+ }
@@ -17,11 +17,12 @@
17
17
  import { Emitter, Event } from '../common/event';
18
18
  import { Disposable } from '../common/disposable';
19
19
  import { FrontendApplicationConfigProvider } from './frontend-application-config-provider';
20
- import { ApplicationProps } from '@theia/application-package/lib/application-props';
20
+ import { ApplicationProps, DefaultTheme } from '@theia/application-package/lib/application-props';
21
21
  import { Theme, ThemeChangeEvent } from '../common/theme';
22
22
  import { inject, injectable, postConstruct } from 'inversify';
23
23
  import { Deferred } from '../common/promise-util';
24
- import { PreferenceService } from './preferences';
24
+ import { PreferenceSchemaProvider, PreferenceService } from './preferences';
25
+ import debounce = require('lodash.debounce');
25
26
 
26
27
  const COLOR_THEME_PREFERENCE_KEY = 'workbench.colorTheme';
27
28
  const NO_THEME = { id: 'no-theme', label: 'Not a real theme.', type: 'dark' } as const;
@@ -31,6 +32,7 @@ export class ThemeService {
31
32
  static readonly STORAGE_KEY = 'theme';
32
33
 
33
34
  @inject(PreferenceService) protected readonly preferences: PreferenceService;
35
+ @inject(PreferenceSchemaProvider) protected readonly schemaProvider: PreferenceSchemaProvider;
34
36
 
35
37
  protected themes: { [id: string]: Theme } = {};
36
38
  protected activeTheme: Theme = NO_THEME;
@@ -48,6 +50,7 @@ export class ThemeService {
48
50
  this.loadUserTheme();
49
51
  this.preferences.ready.then(() => {
50
52
  this.validateActiveTheme();
53
+ this.updateColorThemePreference();
51
54
  this.preferences.onPreferencesChanged(changes => {
52
55
  if (COLOR_THEME_PREFERENCE_KEY in changes) {
53
56
  this.validateActiveTheme();
@@ -61,6 +64,7 @@ export class ThemeService {
61
64
  this.themes[theme.id] = theme;
62
65
  }
63
66
  this.validateActiveTheme();
67
+ this.updateColorThemePreference();
64
68
  return Disposable.create(() => {
65
69
  for (const theme of themes) {
66
70
  delete this.themes[theme.id];
@@ -68,6 +72,7 @@ export class ThemeService {
68
72
  this.setCurrentTheme(this.defaultTheme.id, false);
69
73
  }
70
74
  }
75
+ this.updateColorThemePreference();
71
76
  });
72
77
  }
73
78
 
@@ -80,6 +85,18 @@ export class ThemeService {
80
85
  }
81
86
  }
82
87
 
88
+ protected updateColorThemePreference = debounce(() => this.doUpdateColorThemePreference(), 500);
89
+
90
+ protected doUpdateColorThemePreference(): void {
91
+ const preference = this.schemaProvider.getSchemaProperty(COLOR_THEME_PREFERENCE_KEY);
92
+ if (preference) {
93
+ const sortedThemes = this.getThemes().sort((a, b) => a.label.localeCompare(b.label));
94
+ preference.enum = sortedThemes.map(e => e.id);
95
+ preference.enumItemLabels = sortedThemes.map(e => e.label);
96
+ this.schemaProvider.updateSchemaProperty(COLOR_THEME_PREFERENCE_KEY, preference);
97
+ }
98
+ }
99
+
83
100
  getThemes(): Theme[] {
84
101
  const result = [];
85
102
  for (const o in this.themes) {
@@ -136,7 +153,8 @@ export class ThemeService {
136
153
  * The default theme. If that is not applicable, returns with the fallback theme.
137
154
  */
138
155
  get defaultTheme(): Theme {
139
- return this.tryGetTheme(FrontendApplicationConfigProvider.get().defaultTheme) ?? this.getTheme(ApplicationProps.DEFAULT.frontend.config.defaultTheme);
156
+ return this.tryGetTheme(DefaultTheme.defaultForOSTheme(FrontendApplicationConfigProvider.get().defaultTheme))
157
+ ?? this.getTheme(DefaultTheme.defaultForOSTheme(ApplicationProps.DEFAULT.frontend.config.defaultTheme));
140
158
  }
141
159
 
142
160
  /**
@@ -30,7 +30,7 @@ import { FrontendApplicationStateService } from './frontend-application-state';
30
30
  import { ContextMenuRenderer, Anchor } from './context-menu-renderer';
31
31
  import { parseCssMagnitude } from './browser';
32
32
  import { TabBarToolbarRegistry, TabBarToolbarFactory, TabBarToolbar, TabBarDelegator, TabBarToolbarItem } from './shell/tab-bar-toolbar';
33
- import { isEmpty } from '../common';
33
+ import { isEmpty, nls } from '../common';
34
34
  import { WidgetManager } from './widget-manager';
35
35
  import { Key } from './keys';
36
36
  import { ProgressBarFactory } from './progress-bar-factory';
@@ -175,7 +175,7 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
175
175
  }),
176
176
  menuRegistry.registerMenuAction([...this.contextMenuPath, '0_global'], {
177
177
  commandId: this.globalHideCommandId,
178
- label: 'Hide'
178
+ label: nls.localize('theia/core/hideViewContainer', 'Hide')
179
179
  }),
180
180
  this.onDidChangeTrackableWidgetsEmitter,
181
181
  this.onDidChangeTrackableWidgets(() => this.decoratorService.fireDidChangeDecorations())
@@ -0,0 +1,107 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 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 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { inject, injectable, postConstruct } from 'inversify';
18
+ import { escapeRegExpCharacters } from '../../common/strings';
19
+ import { Emitter, Event } from '../../common/event';
20
+ import { CorePreferences } from '../core-preferences';
21
+ import { FrontendApplicationConfigProvider } from '../frontend-application-config-provider';
22
+
23
+ export const InitialWindowTitleParts = {
24
+ activeEditorShort: undefined,
25
+ activeEditorMedium: undefined,
26
+ activeEditorLong: undefined,
27
+ activeFolderShort: undefined,
28
+ activeFolderMedium: undefined,
29
+ activeFolderLong: undefined,
30
+ folderName: undefined,
31
+ folderPath: undefined,
32
+ rootName: undefined,
33
+ rootPath: undefined,
34
+ appName: FrontendApplicationConfigProvider.get().applicationName,
35
+ remoteName: undefined,
36
+ dirty: undefined,
37
+ developmentHost: undefined
38
+ };
39
+
40
+ @injectable()
41
+ export class WindowTitleService {
42
+
43
+ @inject(CorePreferences)
44
+ protected readonly preferences: CorePreferences;
45
+
46
+ protected _title = '';
47
+ protected titleTemplate?: string;
48
+
49
+ protected onDidChangeTitleEmitter = new Emitter<string>();
50
+ protected titleParts = new Map<string, string | undefined>(Object.entries(InitialWindowTitleParts));
51
+ protected separator = ' - ';
52
+
53
+ @postConstruct()
54
+ protected init(): void {
55
+ this.titleTemplate = this.preferences['window.title'];
56
+ this.separator = this.preferences['window.titleSeparator'];
57
+ this.updateTitle();
58
+ this.preferences.onPreferenceChanged(e => {
59
+ if (e.preferenceName === 'window.title') {
60
+ this.titleTemplate = e.newValue;
61
+ this.updateTitle();
62
+ } else if (e.preferenceName === 'window.titleSeparator') {
63
+ this.separator = e.newValue;
64
+ this.updateTitle();
65
+ }
66
+ });
67
+ }
68
+
69
+ get onDidChangeTitle(): Event<string> {
70
+ return this.onDidChangeTitleEmitter.event;
71
+ }
72
+
73
+ get title(): string {
74
+ return this._title;
75
+ }
76
+
77
+ update(parts: Record<string, string | undefined>): void {
78
+ for (const [key, value] of Object.entries(parts)) {
79
+ this.titleParts.set(key, value);
80
+ }
81
+ this.updateTitle();
82
+ }
83
+
84
+ protected updateTitle(): void {
85
+ if (!this.titleTemplate) {
86
+ this._title = '';
87
+ } else {
88
+ let title = this.titleTemplate;
89
+ for (const [key, value] of this.titleParts.entries()) {
90
+ if (key !== 'developmentHost') {
91
+ const label = `$\{${key}\}`;
92
+ const regex = new RegExp(escapeRegExpCharacters(label), 'g');
93
+ title = title.replace(regex, value ?? '');
94
+ }
95
+ }
96
+ const separatedTitle = title.split('${separator}').filter(e => e.trim().length > 0);
97
+ this._title = separatedTitle.join(this.separator);
98
+ }
99
+ const developmentHost = this.titleParts.get('developmentHost');
100
+ if (developmentHost) {
101
+ this._title = developmentHost + this.separator + this._title;
102
+ }
103
+ document.title = this._title;
104
+ this.onDidChangeTitleEmitter.fire(this._title);
105
+ }
106
+
107
+ }
@@ -0,0 +1,94 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 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 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { Widget } from '../widgets';
18
+ import { FrontendApplication, FrontendApplicationContribution } from '../frontend-application';
19
+ import { NavigatableWidget } from '../navigatable-types';
20
+ import { inject, injectable } from 'inversify';
21
+ import { WindowTitleService } from './window-title-service';
22
+ import { LabelProvider } from '../label-provider';
23
+ import { Saveable } from '../saveable';
24
+ import { Disposable } from '../../common';
25
+
26
+ @injectable()
27
+ export class WindowTitleUpdater implements FrontendApplicationContribution {
28
+
29
+ @inject(WindowTitleService)
30
+ protected readonly windowTitleService: WindowTitleService;
31
+
32
+ @inject(LabelProvider)
33
+ protected readonly labelProvider: LabelProvider;
34
+
35
+ onStart(app: FrontendApplication): void {
36
+ app.shell.mainPanel.onDidChangeCurrent(title => this.handleWidgetChange(title?.owner));
37
+ this.handleWidgetChange(app.shell.getCurrentWidget('main'));
38
+ }
39
+
40
+ protected toDisposeOnWidgetChanged: Disposable = Disposable.NULL;
41
+ protected handleWidgetChange(widget?: Widget): void {
42
+ this.toDisposeOnWidgetChanged.dispose();
43
+ const saveable = Saveable.get(widget);
44
+ if (saveable) {
45
+ this.toDisposeOnWidgetChanged = saveable.onDirtyChanged(() => this.windowTitleService.update({ dirty: saveable.dirty ? '●' : '' }));
46
+ } else {
47
+ this.toDisposeOnWidgetChanged = Disposable.NULL;
48
+ }
49
+ this.updateTitleWidget(widget);
50
+ }
51
+
52
+ /**
53
+ * Updates the title of the application based on the currently opened widget.
54
+ *
55
+ * @param widget The current widget in the `main` application area. `undefined` if no widget is currently open in that area.
56
+ */
57
+ protected updateTitleWidget(widget?: Widget): void {
58
+ let activeEditorLong: string | undefined;
59
+ let activeEditorMedium: string | undefined;
60
+ let activeEditorShort: string | undefined;
61
+ let activeFolderLong: string | undefined;
62
+ let activeFolderMedium: string | undefined;
63
+ let activeFolderShort: string | undefined;
64
+ let dirty: string | undefined;
65
+ const uri = NavigatableWidget.getUri(widget);
66
+ if (uri) {
67
+ activeEditorLong = uri.path.fsPath();
68
+ activeEditorMedium = this.labelProvider.getLongName(uri);
69
+ activeEditorShort = this.labelProvider.getName(uri);
70
+ const parent = uri.parent;
71
+ activeFolderLong = parent.path.fsPath();
72
+ activeFolderMedium = this.labelProvider.getLongName(parent);
73
+ activeFolderShort = this.labelProvider.getName(parent);
74
+ } else if (widget) {
75
+ const widgetTitle = widget.title.label;
76
+ activeEditorLong = widgetTitle;
77
+ activeEditorMedium = widgetTitle;
78
+ activeEditorShort = widgetTitle;
79
+ }
80
+ if (Saveable.isDirty(widget)) {
81
+ dirty = '●';
82
+ }
83
+ this.windowTitleService.update({
84
+ activeEditorLong,
85
+ activeEditorMedium,
86
+ activeEditorShort,
87
+ activeFolderLong,
88
+ activeFolderMedium,
89
+ activeFolderShort,
90
+ dirty
91
+ });
92
+ }
93
+
94
+ }
@@ -307,7 +307,13 @@ export class Emitter<T = any> {
307
307
  }
308
308
  }
309
309
 
310
+ export type WaitUntilData<T> = Omit<T, 'waitUntil' | 'token'>;
311
+
310
312
  export interface WaitUntilEvent {
313
+ /**
314
+ * A cancellation token.
315
+ */
316
+ token: CancellationToken;
311
317
  /**
312
318
  * Allows to pause the event loop until the provided thenable resolved.
313
319
  *
@@ -325,11 +331,13 @@ export namespace WaitUntilEvent {
325
331
  */
326
332
  export async function fire<T extends WaitUntilEvent>(
327
333
  emitter: Emitter<T>,
328
- event: Omit<T, 'waitUntil'>,
329
- timeout: number | undefined = undefined
334
+ event: WaitUntilData<T>,
335
+ timeout?: number,
336
+ token = CancellationToken.None
330
337
  ): Promise<void> {
331
338
  const waitables: Promise<void>[] = [];
332
339
  const asyncEvent = Object.assign(event, {
340
+ token,
333
341
  waitUntil: (thenable: Promise<any>) => {
334
342
  if (Object.isFrozen(waitables)) {
335
343
  throw new Error('waitUntil cannot be called asynchronously.');
@@ -364,7 +372,7 @@ export class AsyncEmitter<T extends WaitUntilEvent> extends Emitter<T> {
364
372
  /**
365
373
  * Fire listeners async one after another.
366
374
  */
367
- override fire(event: Omit<T, 'waitUntil'>, token: CancellationToken = CancellationToken.None,
375
+ override fire(event: WaitUntilData<T>, token: CancellationToken = CancellationToken.None,
368
376
  promiseJoin?: (p: Promise<any>, listener: Function) => Promise<any>): Promise<void> {
369
377
  const callbacks = this._callbacks;
370
378
  if (!callbacks) {
@@ -377,7 +385,7 @@ export class AsyncEmitter<T extends WaitUntilEvent> extends Emitter<T> {
377
385
  return this.deliveryQueue = this.deliver(listeners, event, token, promiseJoin);
378
386
  }
379
387
 
380
- protected async deliver(listeners: Callback[], event: Omit<T, 'waitUntil'>, token: CancellationToken,
388
+ protected async deliver(listeners: Callback[], event: WaitUntilData<T>, token: CancellationToken,
381
389
  promiseJoin?: (p: Promise<any>, listener: Function) => Promise<any>): Promise<void> {
382
390
  for (const listener of listeners) {
383
391
  if (token.isCancellationRequested) {
@@ -385,6 +393,7 @@ export class AsyncEmitter<T extends WaitUntilEvent> extends Emitter<T> {
385
393
  }
386
394
  const waitables: Promise<void>[] = [];
387
395
  const asyncEvent = Object.assign(event, {
396
+ token,
388
397
  waitUntil: (thenable: Promise<any>) => {
389
398
  if (Object.isFrozen(waitables)) {
390
399
  throw new Error('waitUntil cannot be called asynchronously.');
@@ -85,6 +85,7 @@ export interface IJSONSchema {
85
85
  errorMessage?: string; // VSCode extension
86
86
  patternErrorMessage?: string; // VSCode extension
87
87
  deprecationMessage?: string; // VSCode extension
88
+ enumItemLabels?: string[]; // VSCode extension
88
89
  enumDescriptions?: string[]; // VSCode extension
89
90
  markdownEnumDescriptions?: string[]; // VSCode extension
90
91
  markdownDescription?: string; // VSCode extension
@@ -120,7 +120,7 @@ export interface RpcMessageEncoder {
120
120
 
121
121
  }
122
122
 
123
- export const defaultMsgPack = new MsgPack({ moreTypes: true, encodeUndefinedAsNil: false, bundleStrings: true });
123
+ export const defaultMsgPack = new MsgPack({ moreTypes: true, encodeUndefinedAsNil: false, bundleStrings: false });
124
124
  // Add custom msgpackR extension for ResponseErrors.
125
125
  addExtension({
126
126
  Class: ResponseError,
@@ -132,23 +132,23 @@ export class RpcProtocol {
132
132
  // The last element of the request args might be a cancellation token. As these tokens are not serializable we have to remove it from the
133
133
  // args array and the `CANCELLATION_TOKEN_KEY` string instead.
134
134
  const cancellationToken: CancellationToken | undefined = args.length && CancellationToken.is(args[args.length - 1]) ? args.pop() : undefined;
135
- if (cancellationToken && cancellationToken.isCancellationRequested) {
136
- return Promise.reject(this.cancelError());
137
- }
138
135
 
139
136
  if (cancellationToken) {
140
137
  args.push(RpcProtocol.CANCELLATION_TOKEN_KEY);
141
- cancellationToken.onCancellationRequested(() => {
142
- this.sendCancel(id);
143
- this.pendingRequests.get(id)?.reject(this.cancelError());
144
- }
145
- );
146
138
  }
139
+
147
140
  this.pendingRequests.set(id, reply);
148
141
 
149
142
  const output = this.channel.getWriteBuffer();
150
143
  this.encoder.request(output, id, method, args);
151
144
  output.commit();
145
+
146
+ if (cancellationToken?.isCancellationRequested) {
147
+ this.sendCancel(id);
148
+ } else {
149
+ cancellationToken?.onCancellationRequested(() => this.sendCancel(id));
150
+ }
151
+
152
152
  return reply.promise;
153
153
  }
154
154
 
@@ -164,12 +164,6 @@ export class RpcProtocol {
164
164
  output.commit();
165
165
  }
166
166
 
167
- cancelError(): Error {
168
- const error = new Error('"Request has already been canceled by the sender"');
169
- error.name = 'Cancel';
170
- return error;
171
- }
172
-
173
167
  protected handleCancel(id: number): void {
174
168
  const cancellationTokenSource = this.cancellationTokenSources.get(id);
175
169
  if (cancellationTokenSource) {