@theia/core 1.40.1 → 1.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +6 -6
  2. package/i18n/nls.cs.json +5 -1
  3. package/i18n/nls.de.json +5 -1
  4. package/i18n/nls.es.json +5 -1
  5. package/i18n/nls.fr.json +5 -1
  6. package/i18n/nls.hu.json +5 -1
  7. package/i18n/nls.it.json +5 -1
  8. package/i18n/nls.ja.json +5 -1
  9. package/i18n/nls.json +5 -1
  10. package/i18n/nls.pl.json +5 -1
  11. package/i18n/nls.pt-br.json +5 -1
  12. package/i18n/nls.pt-pt.json +5 -1
  13. package/i18n/nls.ru.json +5 -1
  14. package/i18n/nls.zh-cn.json +5 -1
  15. package/lib/browser/common-frontend-contribution.d.ts +11 -0
  16. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  17. package/lib/browser/common-frontend-contribution.js +97 -14
  18. package/lib/browser/common-frontend-contribution.js.map +1 -1
  19. package/lib/browser/context-menu-renderer.d.ts +5 -0
  20. package/lib/browser/context-menu-renderer.d.ts.map +1 -1
  21. package/lib/browser/label-parser.d.ts +8 -0
  22. package/lib/browser/label-parser.d.ts.map +1 -1
  23. package/lib/browser/label-parser.js +14 -0
  24. package/lib/browser/label-parser.js.map +1 -1
  25. package/lib/browser/label-parser.spec.js +33 -0
  26. package/lib/browser/label-parser.spec.js.map +1 -1
  27. package/lib/browser/menu/browser-context-menu-renderer.d.ts +1 -1
  28. package/lib/browser/menu/browser-context-menu-renderer.d.ts.map +1 -1
  29. package/lib/browser/menu/browser-context-menu-renderer.js +2 -2
  30. package/lib/browser/menu/browser-context-menu-renderer.js.map +1 -1
  31. package/lib/browser/menu/browser-menu-plugin.d.ts +1 -1
  32. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  33. package/lib/browser/menu/browser-menu-plugin.js +2 -2
  34. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  35. package/lib/browser/performance/frontend-stopwatch.d.ts.map +1 -1
  36. package/lib/browser/performance/frontend-stopwatch.js +6 -2
  37. package/lib/browser/performance/frontend-stopwatch.js.map +1 -1
  38. package/lib/browser/preferences/preference-contribution.d.ts +2 -0
  39. package/lib/browser/preferences/preference-contribution.d.ts.map +1 -1
  40. package/lib/browser/preferences/preference-contribution.js +48 -24
  41. package/lib/browser/preferences/preference-contribution.js.map +1 -1
  42. package/lib/browser/saveable.d.ts +15 -1
  43. package/lib/browser/saveable.d.ts.map +1 -1
  44. package/lib/browser/saveable.js +34 -1
  45. package/lib/browser/saveable.js.map +1 -1
  46. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts +46 -1
  47. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts.map +1 -1
  48. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js +87 -6
  49. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js.map +1 -1
  50. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts +20 -2
  51. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts.map +1 -1
  52. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js +28 -1
  53. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js.map +1 -1
  54. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +25 -1
  55. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -1
  56. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +79 -7
  57. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
  58. package/lib/browser/shell/tab-bars.d.ts.map +1 -1
  59. package/lib/browser/shell/tab-bars.js +9 -1
  60. package/lib/browser/shell/tab-bars.js.map +1 -1
  61. package/lib/browser/tree/tree-model.d.ts +2 -0
  62. package/lib/browser/tree/tree-model.d.ts.map +1 -1
  63. package/lib/browser/tree/tree-model.js +6 -0
  64. package/lib/browser/tree/tree-model.js.map +1 -1
  65. package/lib/browser/tree/tree-widget.d.ts +18 -0
  66. package/lib/browser/tree/tree-widget.d.ts.map +1 -1
  67. package/lib/browser/tree/tree-widget.js +57 -0
  68. package/lib/browser/tree/tree-widget.js.map +1 -1
  69. package/lib/browser/tree/tree.d.ts +18 -0
  70. package/lib/browser/tree/tree.d.ts.map +1 -1
  71. package/lib/browser/tree/tree.js +6 -0
  72. package/lib/browser/tree/tree.js.map +1 -1
  73. package/lib/browser/widgets/enhanced-preview-widget.d.ts +7 -0
  74. package/lib/browser/widgets/enhanced-preview-widget.d.ts.map +1 -0
  75. package/lib/browser/widgets/enhanced-preview-widget.js +27 -0
  76. package/lib/browser/widgets/enhanced-preview-widget.js.map +1 -0
  77. package/lib/browser/window-contribution.js +1 -1
  78. package/lib/browser/window-contribution.js.map +1 -1
  79. package/lib/common/array-utils.d.ts +7 -0
  80. package/lib/common/array-utils.d.ts.map +1 -1
  81. package/lib/common/array-utils.js +21 -0
  82. package/lib/common/array-utils.js.map +1 -1
  83. package/lib/common/event.d.ts +5 -0
  84. package/lib/common/event.d.ts.map +1 -1
  85. package/lib/common/event.js +5 -1
  86. package/lib/common/event.js.map +1 -1
  87. package/lib/common/menu/menu-model-registry.d.ts +12 -1
  88. package/lib/common/menu/menu-model-registry.d.ts.map +1 -1
  89. package/lib/common/menu/menu-model-registry.js +46 -0
  90. package/lib/common/menu/menu-model-registry.js.map +1 -1
  91. package/lib/common/performance/measurement.d.ts +21 -0
  92. package/lib/common/performance/measurement.d.ts.map +1 -1
  93. package/lib/common/performance/stopwatch.d.ts +10 -2
  94. package/lib/common/performance/stopwatch.d.ts.map +1 -1
  95. package/lib/common/performance/stopwatch.js +34 -11
  96. package/lib/common/performance/stopwatch.js.map +1 -1
  97. package/lib/common/promise-util.d.ts +4 -0
  98. package/lib/common/promise-util.d.ts.map +1 -1
  99. package/lib/common/promise-util.js +11 -1
  100. package/lib/common/promise-util.js.map +1 -1
  101. package/lib/common/promise-util.spec.js +26 -12
  102. package/lib/common/promise-util.spec.js.map +1 -1
  103. package/lib/common/types.d.ts +4 -0
  104. package/lib/common/types.d.ts.map +1 -1
  105. package/lib/common/types.js +16 -1
  106. package/lib/common/types.js.map +1 -1
  107. package/lib/common/uri.d.ts +1 -0
  108. package/lib/common/uri.d.ts.map +1 -1
  109. package/lib/common/uri.js +3 -0
  110. package/lib/common/uri.js.map +1 -1
  111. package/lib/electron-browser/menu/electron-context-menu-renderer.js +2 -2
  112. package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
  113. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts +1 -1
  114. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  115. package/lib/electron-browser/menu/electron-main-menu-factory.js +2 -2
  116. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  117. package/lib/electron-main/electron-main-application.d.ts.map +1 -1
  118. package/lib/electron-main/electron-main-application.js +3 -0
  119. package/lib/electron-main/electron-main-application.js.map +1 -1
  120. package/lib/node/backend-application.d.ts +5 -1
  121. package/lib/node/backend-application.d.ts.map +1 -1
  122. package/lib/node/backend-application.js +28 -5
  123. package/lib/node/backend-application.js.map +1 -1
  124. package/lib/node/logger-cli-contribution.spec.js +1 -1
  125. package/lib/node/logger-cli-contribution.spec.js.map +1 -1
  126. package/lib/node/main.d.ts +2 -5
  127. package/lib/node/main.d.ts.map +1 -1
  128. package/lib/node/main.js.map +1 -1
  129. package/lib/node/performance/node-stopwatch.js +1 -1
  130. package/lib/node/performance/node-stopwatch.js.map +1 -1
  131. package/package.json +7 -7
  132. package/src/browser/common-frontend-contribution.ts +107 -17
  133. package/src/browser/context-menu-renderer.ts +5 -0
  134. package/src/browser/label-parser.spec.ts +38 -0
  135. package/src/browser/label-parser.ts +15 -0
  136. package/src/browser/menu/browser-context-menu-renderer.ts +2 -2
  137. package/src/browser/menu/browser-menu-plugin.ts +2 -2
  138. package/src/browser/performance/frontend-stopwatch.ts +5 -2
  139. package/src/browser/preferences/preference-contribution.ts +49 -24
  140. package/src/browser/saveable.ts +41 -2
  141. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +94 -8
  142. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts +28 -1
  143. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +87 -8
  144. package/src/browser/shell/tab-bars.ts +8 -1
  145. package/src/browser/style/tabs.css +32 -2
  146. package/src/browser/tree/tree-model.ts +8 -0
  147. package/src/browser/tree/tree-widget.tsx +66 -0
  148. package/src/browser/tree/tree.ts +27 -0
  149. package/src/browser/widgets/enhanced-preview-widget.ts +27 -0
  150. package/src/browser/window-contribution.ts +1 -1
  151. package/src/common/array-utils.ts +20 -0
  152. package/src/common/event.ts +12 -2
  153. package/src/common/i18n/nls.metadata.json +5616 -5359
  154. package/src/common/menu/menu-model-registry.ts +50 -0
  155. package/src/common/performance/measurement.ts +26 -0
  156. package/src/common/performance/stopwatch.ts +38 -12
  157. package/src/common/promise-util.spec.ts +43 -12
  158. package/src/common/promise-util.ts +12 -0
  159. package/src/common/types.ts +17 -0
  160. package/src/common/uri.ts +4 -0
  161. package/src/electron-browser/menu/electron-context-menu-renderer.ts +2 -2
  162. package/src/electron-browser/menu/electron-main-menu-factory.ts +2 -2
  163. package/src/electron-main/electron-main-application.ts +3 -0
  164. package/src/node/backend-application.ts +30 -5
  165. package/src/node/logger-cli-contribution.spec.ts +1 -1
  166. package/src/node/main.ts +1 -6
  167. package/src/node/performance/node-stopwatch.ts +1 -1
@@ -36,7 +36,7 @@ import { OS, isOSX, isWindows, EOL } from '../common/os';
36
36
  import { ResourceContextKey } from './resource-context-key';
37
37
  import { UriSelection } from '../common/selection';
38
38
  import { StorageService } from './storage-service';
39
- import { Navigatable } from './navigatable';
39
+ import { Navigatable, NavigatableWidget } from './navigatable';
40
40
  import { QuickViewService } from './quick-input/quick-view-service';
41
41
  import { environment } from '@theia/application-package/lib/environment';
42
42
  import { IconTheme, IconThemeService } from './icon-theme-service';
@@ -52,7 +52,7 @@ import { UTF8 } from '../common/encodings';
52
52
  import { EnvVariablesServer } from '../common/env-variables';
53
53
  import { AuthenticationService } from './authentication-service';
54
54
  import { FormatType, Saveable, SaveOptions } from './saveable';
55
- import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator } from './quick-input';
55
+ import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator, QuickPickSeparator } from './quick-input';
56
56
  import { AsyncLocalizationProvider } from '../common/i18n/localization';
57
57
  import { nls } from '../common/nls';
58
58
  import { CurrentWidgetCommandAdapter } from './shell/current-widget-command-adapter';
@@ -63,12 +63,13 @@ import { DecorationStyle } from './decoration-style';
63
63
  import { isPinned, Title, togglePinned, Widget } from './widgets';
64
64
  import { SaveResourceService } from './save-resource-service';
65
65
  import { UserWorkingDirectoryProvider } from './user-working-directory-provider';
66
- import { UntitledResourceResolver } from '../common';
66
+ import { UNTITLED_SCHEME, UntitledResourceResolver } from '../common';
67
67
  import { LanguageQuickPickService } from './i18n/language-quick-pick-service';
68
68
 
69
69
  export namespace CommonMenus {
70
70
 
71
71
  export const FILE = [...MAIN_MENU_BAR, '1_file'];
72
+ export const FILE_NEW_TEXT = [...FILE, '1_new_text'];
72
73
  export const FILE_NEW = [...FILE, '1_new'];
73
74
  export const FILE_OPEN = [...FILE, '2_open'];
74
75
  export const FILE_SAVE = [...FILE, '3_save'];
@@ -79,6 +80,8 @@ export namespace CommonMenus {
79
80
  export const FILE_SETTINGS_SUBMENU_THEME = [...FILE_SETTINGS_SUBMENU, '2_settings_submenu_theme'];
80
81
  export const FILE_CLOSE = [...FILE, '6_close'];
81
82
 
83
+ export const FILE_NEW_CONTRIBUTIONS = 'file/newFile';
84
+
82
85
  export const EDIT = [...MAIN_MENU_BAR, '2_edit'];
83
86
  export const EDIT_UNDO = [...EDIT, '1_undo'];
84
87
  export const EDIT_CLIPBOARD = [...EDIT, '2_clipboard'];
@@ -108,6 +111,7 @@ export namespace CommonCommands {
108
111
 
109
112
  export const FILE_CATEGORY = 'File';
110
113
  export const VIEW_CATEGORY = 'View';
114
+ export const CREATE_CATEGORY = 'Create';
111
115
  export const PREFERENCES_CATEGORY = 'Preferences';
112
116
  export const FILE_CATEGORY_KEY = nls.getDefaultKey(FILE_CATEGORY);
113
117
  export const VIEW_CATEGORY_KEY = nls.getDefaultKey(VIEW_CATEGORY);
@@ -272,11 +276,16 @@ export namespace CommonCommands {
272
276
  category: VIEW_CATEGORY,
273
277
  label: 'Toggle Menu Bar'
274
278
  });
275
- export const NEW_UNTITLED_FILE = Command.toDefaultLocalizedCommand({
276
- id: 'workbench.action.files.newUntitledFile',
279
+ export const NEW_UNTITLED_TEXT_FILE = Command.toDefaultLocalizedCommand({
280
+ id: 'workbench.action.files.newUntitledTextFile',
277
281
  category: FILE_CATEGORY,
278
282
  label: 'New Untitled Text File'
279
283
  });
284
+ export const NEW_UNTITLED_FILE = Command.toDefaultLocalizedCommand({
285
+ id: 'workbench.action.files.newUntitledFile',
286
+ category: CREATE_CATEGORY,
287
+ label: 'New File...'
288
+ });
280
289
  export const SAVE = Command.toDefaultLocalizedCommand({
281
290
  id: 'core.save',
282
291
  category: FILE_CATEGORY,
@@ -371,6 +380,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
371
380
  @inject(CommandRegistry)
372
381
  protected readonly commandRegistry: CommandRegistry;
373
382
 
383
+ @inject(MenuModelRegistry)
384
+ protected readonly menuRegistry: MenuModelRegistry;
385
+
374
386
  @inject(StorageService)
375
387
  protected readonly storageService: StorageService;
376
388
 
@@ -545,6 +557,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
545
557
  registry.registerSubmenu(CommonMenus.VIEW, nls.localizeByDefault('View'));
546
558
  registry.registerSubmenu(CommonMenus.HELP, nls.localizeByDefault('Help'));
547
559
 
560
+ // For plugins contributing create new file commands/menu-actions
561
+ registry.registerIndependentSubmenu(CommonMenus.FILE_NEW_CONTRIBUTIONS, nls.localizeByDefault('New File...'));
562
+
548
563
  registry.registerMenuAction(CommonMenus.FILE_SAVE, {
549
564
  commandId: CommonCommands.SAVE.id
550
565
  });
@@ -693,10 +708,16 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
693
708
 
694
709
  registry.registerSubmenu(CommonMenus.VIEW_APPEARANCE_SUBMENU, nls.localizeByDefault('Appearance'));
695
710
 
696
- registry.registerMenuAction(CommonMenus.FILE_NEW, {
711
+ registry.registerMenuAction(CommonMenus.FILE_NEW_TEXT, {
712
+ commandId: CommonCommands.NEW_UNTITLED_TEXT_FILE.id,
713
+ label: nls.localizeByDefault('New Text File'),
714
+ order: 'a'
715
+ });
716
+
717
+ registry.registerMenuAction(CommonMenus.FILE_NEW_TEXT, {
697
718
  commandId: CommonCommands.NEW_UNTITLED_FILE.id,
698
719
  label: nls.localizeByDefault('New File...'),
699
- order: 'a'
720
+ order: 'a1'
700
721
  });
701
722
  }
702
723
 
@@ -941,13 +962,17 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
941
962
  execute: () => this.toggleBreadcrumbs(),
942
963
  isToggled: () => this.isBreadcrumbsEnabled(),
943
964
  });
944
- commandRegistry.registerCommand(CommonCommands.NEW_UNTITLED_FILE, {
965
+ commandRegistry.registerCommand(CommonCommands.NEW_UNTITLED_TEXT_FILE, {
945
966
  execute: async () => {
946
967
  const untitledUri = this.untitledResourceResolver.createUntitledURI('', await this.workingDirProvider.getUserWorkingDir());
947
968
  this.untitledResourceResolver.resolve(untitledUri);
948
969
  return open(this.openerService, untitledUri);
949
970
  }
950
971
  });
972
+ commandRegistry.registerCommand(CommonCommands.NEW_UNTITLED_FILE, {
973
+ execute: async () => this.showNewFilePicker()
974
+ });
975
+
951
976
  for (const [index, ordinal] of this.getOrdinalNumbers().entries()) {
952
977
  commandRegistry.registerCommand({ id: `workbench.action.focus${ordinal}EditorGroup`, label: index === 0 ? nls.localizeByDefault('Focus First Editor Group') : '', category: nls.localize(CommonCommands.VIEW_CATEGORY_KEY, CommonCommands.VIEW_CATEGORY) }, {
953
978
  isEnabled: () => this.shell.mainAreaTabBars.length > index,
@@ -1097,8 +1122,12 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
1097
1122
  when: 'activeEditorIsPinned'
1098
1123
  },
1099
1124
  {
1100
- command: CommonCommands.NEW_UNTITLED_FILE.id,
1125
+ command: CommonCommands.NEW_UNTITLED_TEXT_FILE.id,
1101
1126
  keybinding: this.isElectron() ? 'ctrlcmd+n' : 'alt+n',
1127
+ },
1128
+ {
1129
+ command: CommonCommands.NEW_UNTITLED_FILE.id,
1130
+ keybinding: 'ctrlcmd+alt+n'
1102
1131
  }
1103
1132
  );
1104
1133
  for (const [index, ordinal] of this.getOrdinalNumbers().entries()) {
@@ -1142,14 +1171,26 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
1142
1171
  }
1143
1172
 
1144
1173
  onWillStop(): OnWillStopAction | undefined {
1145
- try {
1146
- if (this.shouldPreventClose || this.shell.canSaveAll()) {
1147
- const captionsToSave = this.unsavedTabsCaptions();
1174
+ if (this.shouldPreventClose || this.shell.canSaveAll()) {
1175
+ return {
1176
+ reason: 'Dirty editors present',
1177
+ action: async () => {
1178
+ const captionsToSave = this.unsavedTabsCaptions();
1179
+ const untitledCaptionsToSave = this.unsavedUntitledTabsCaptions();
1180
+ const result = await confirmExitWithOrWithoutSaving(captionsToSave, async () => {
1181
+ await this.saveDirty(untitledCaptionsToSave);
1182
+ await this.shell.saveAll();
1183
+ });
1184
+ if (this.shell.canSaveAll()) {
1185
+ this.shouldPreventClose = true;
1186
+ return false;
1187
+ } else {
1188
+ this.shouldPreventClose = false;
1189
+ return result;
1190
+ }
1148
1191
 
1149
- return { reason: 'Dirty editors present', action: async () => confirmExitWithOrWithoutSaving(captionsToSave, async () => this.shell.saveAll()) };
1150
- }
1151
- } finally {
1152
- this.shouldPreventClose = false;
1192
+ }
1193
+ };
1153
1194
  }
1154
1195
  }
1155
1196
  protected unsavedTabsCaptions(): string[] {
@@ -1157,6 +1198,11 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
1157
1198
  .filter(widget => this.saveResourceService.canSave(widget))
1158
1199
  .map(widget => widget.title.label);
1159
1200
  }
1201
+ protected unsavedUntitledTabsCaptions(): Widget[] {
1202
+ return this.shell.widgets.filter(widget =>
1203
+ NavigatableWidget.getUri(widget)?.scheme === UNTITLED_SCHEME && this.saveResourceService.canSaveAs(widget)
1204
+ );
1205
+ }
1160
1206
  protected async configureDisplayLanguage(): Promise<void> {
1161
1207
  const languageInfo = await this.languageQuickPickService.pickDisplayLanguage();
1162
1208
  if (languageInfo && !nls.isSelectedLocale(languageInfo.languageId) && await this.confirmRestart(
@@ -1167,7 +1213,14 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
1167
1213
  this.windowService.reload();
1168
1214
  }
1169
1215
  }
1170
-
1216
+ protected async saveDirty(toSave: Widget[]): Promise<void> {
1217
+ for (const widget of toSave) {
1218
+ const saveable = Saveable.get(widget);
1219
+ if (saveable?.dirty) {
1220
+ await this.saveResourceService.save(widget);
1221
+ }
1222
+ }
1223
+ }
1171
1224
  protected toggleBreadcrumbs(): void {
1172
1225
  const value: boolean | undefined = this.preferenceService.get('breadcrumbs.enabled');
1173
1226
  this.preferenceService.set('breadcrumbs.enabled', !value, PreferenceScope.User);
@@ -1294,6 +1347,43 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
1294
1347
  });
1295
1348
  }
1296
1349
 
1350
+ /**
1351
+ * @todo https://github.com/eclipse-theia/theia/issues/12824
1352
+ */
1353
+ protected async showNewFilePicker(): Promise<void> {
1354
+ const newFileContributions = this.menuRegistry.getMenuNode(CommonMenus.FILE_NEW_CONTRIBUTIONS); // Add menus
1355
+ const items: QuickPickItemOrSeparator[] = [
1356
+ {
1357
+ label: nls.localizeByDefault('New Text File'),
1358
+ execute: async () => this.commandRegistry.executeCommand(CommonCommands.NEW_UNTITLED_TEXT_FILE.id)
1359
+ },
1360
+ ...newFileContributions.children
1361
+ .flatMap(node => {
1362
+ if (node.children && node.children.length > 0) {
1363
+ return node.children;
1364
+ }
1365
+ return node;
1366
+ })
1367
+ .filter(node => node.role || node.command)
1368
+ .map(node => {
1369
+ if (node.role) {
1370
+ return { type: 'separator' } as QuickPickSeparator;
1371
+ }
1372
+ const command = this.commandRegistry.getCommand(node.command!);
1373
+ return {
1374
+ label: command!.label!,
1375
+ execute: async () => this.commandRegistry.executeCommand(command!.id!)
1376
+ };
1377
+
1378
+ })
1379
+ ];
1380
+ this.quickInputService.showQuickPick(items, {
1381
+ title: nls.localizeByDefault('New File...'),
1382
+ placeholder: nls.localizeByDefault('Select File Type or Enter File Name...'),
1383
+ canSelectMany: false
1384
+ });
1385
+ }
1386
+
1297
1387
  registerColors(colors: ColorRegistry): void {
1298
1388
  colors.register(
1299
1389
  // Base Colors should be aligned with https://code.visualstudio.com/api/references/theme-color#base-colors
@@ -120,4 +120,9 @@ export interface RenderContextMenuOptions {
120
120
  context?: HTMLElement;
121
121
  contextKeyService?: ContextMatcher;
122
122
  onHide?: () => void;
123
+ /**
124
+ * If true a single submenu in the context menu is not rendered but its children are rendered on the top level.
125
+ * Default is `false`.
126
+ */
127
+ skipSingleRootNode?: boolean;
123
128
  }
@@ -124,4 +124,42 @@ describe('StatusBarEntryUtility', () => {
124
124
  expect((iconArr[3] as LabelIcon).name).equals('icon3');
125
125
  });
126
126
 
127
+ it('should strip nothing from an empty string', () => {
128
+ text = '';
129
+ const stripped: string = statusBarEntryUtility.stripIcons(text);
130
+ expect(stripped).to.be.equal(text);
131
+ });
132
+
133
+ it('should strip nothing from an string containing no icons', () => {
134
+ // Deliberate double space to verify not concatenating these words
135
+ text = 'foo bar';
136
+ const stripped: string = statusBarEntryUtility.stripIcons(text);
137
+ expect(stripped).to.be.equal(text);
138
+ });
139
+
140
+ it("should strip a medial '$(icon)' from a string", () => {
141
+ text = 'foo $(icon) bar';
142
+ const stripped: string = statusBarEntryUtility.stripIcons(text);
143
+ expect(stripped).to.be.equal('foo bar');
144
+ });
145
+
146
+ it("should strip a terminal '$(icon)' from a string", () => {
147
+ // Deliberate double space to verify not concatenating these words
148
+ text = 'foo bar $(icon)';
149
+ const stripped: string = statusBarEntryUtility.stripIcons(text);
150
+ expect(stripped).to.be.equal('foo bar');
151
+ });
152
+
153
+ it("should strip an initial '$(icon)' from a string", () => {
154
+ // Deliberate double space to verify not concatenating these words
155
+ text = '$(icon) foo bar';
156
+ const stripped: string = statusBarEntryUtility.stripIcons(text);
157
+ expect(stripped).to.be.equal('foo bar');
158
+ });
159
+
160
+ it("should strip multiple '$(icon)' specifiers from a string", () => {
161
+ text = '$(icon1) foo $(icon2)$(icon3) bar $(icon4) $(icon5)';
162
+ const stripped: string = statusBarEntryUtility.stripIcons(text);
163
+ expect(stripped).to.be.equal('foo bar');
164
+ });
127
165
  });
@@ -90,4 +90,19 @@ export class LabelParser {
90
90
  return parserArray;
91
91
  }
92
92
 
93
+ /**
94
+ * Strips icon specifiers from the given `text`, leaving only a
95
+ * space-separated concatenation of the non-icon segments.
96
+ *
97
+ * @param text text to be stripped of icon specifiers
98
+ * @returns the `text` with icon specifiers stripped out
99
+ */
100
+ stripIcons(text: string): string {
101
+ return this.parse(text)
102
+ .filter(item => !LabelIcon.is(item))
103
+ .map(s => (s as string).trim())
104
+ .filter(s => s.length)
105
+ .join(' ');
106
+ }
107
+
93
108
  }
@@ -34,8 +34,8 @@ export class BrowserContextMenuRenderer extends ContextMenuRenderer {
34
34
  super();
35
35
  }
36
36
 
37
- protected doRender({ menuPath, anchor, args, onHide, context, contextKeyService }: RenderContextMenuOptions): ContextMenuAccess {
38
- const contextMenu = this.menuFactory.createContextMenu(menuPath, args, context, contextKeyService);
37
+ protected doRender({ menuPath, anchor, args, onHide, context, contextKeyService, skipSingleRootNode }: RenderContextMenuOptions): ContextMenuAccess {
38
+ const contextMenu = this.menuFactory.createContextMenu(menuPath, args, context, contextKeyService, skipSingleRootNode);
39
39
  const { x, y } = coordinateFromAnchor(anchor);
40
40
  if (onHide) {
41
41
  contextMenu.aboutToClose.connect(() => onHide!());
@@ -108,8 +108,8 @@ export class BrowserMainMenuFactory implements MenuWidgetFactory {
108
108
  }
109
109
  }
110
110
 
111
- createContextMenu(path: MenuPath, args?: unknown[], context?: HTMLElement, contextKeyService?: ContextMatcher): MenuWidget {
112
- const menuModel = this.menuProvider.getMenu(path);
111
+ createContextMenu(path: MenuPath, args?: unknown[], context?: HTMLElement, contextKeyService?: ContextMatcher, skipSingleRootNode?: boolean): MenuWidget {
112
+ const menuModel = skipSingleRootNode ? this.menuProvider.removeSingleRootNode(this.menuProvider.getMenu(path), path) : this.menuProvider.getMenu(path);
113
113
  const menuCommandRegistry = this.createMenuCommandRegistry(menuModel, args).snapshot(path);
114
114
  const contextMenu = this.createMenuWidget(menuModel, { commands: menuCommandRegistry, context, rootMenuPath: path, contextKeyService });
115
115
  return contextMenu;
@@ -40,6 +40,7 @@ export class FrontendStopwatch extends Stopwatch {
40
40
  performance.mark(endMarker);
41
41
 
42
42
  let duration: number;
43
+ let startTime: number;
43
44
 
44
45
  try {
45
46
  performance.measure(name, startMarker, endMarker);
@@ -47,16 +48,18 @@ export class FrontendStopwatch extends Stopwatch {
47
48
  const entries = performance.getEntriesByName(name);
48
49
  // If no entries, then performance measurement was disabled or failed, so
49
50
  // signal that with a `NaN` result
50
- duration = entries.length > 0 ? entries[0].duration : Number.NaN;
51
+ duration = entries[0].duration ?? Number.NaN;
52
+ startTime = entries[0].startTime ?? Number.NaN;
51
53
  } catch (e) {
52
54
  console.warn(e);
53
55
  duration = Number.NaN;
56
+ startTime = Number.NaN;
54
57
  }
55
58
 
56
59
  performance.clearMeasures(name);
57
60
  performance.clearMarks(startMarker);
58
61
  performance.clearMarks(endMarker);
59
- return duration;
62
+ return { startTime, duration };
60
63
  }, options);
61
64
  }
62
65
  };
@@ -34,6 +34,8 @@ import { JSONValue } from '@phosphor/coreutils';
34
34
 
35
35
  export const PreferenceContribution = Symbol('PreferenceContribution');
36
36
 
37
+ export const DefaultOverridesPreferenceSchemaId = 'defaultOverrides';
38
+
37
39
  /**
38
40
  * A {@link PreferenceContribution} allows adding additional custom preferences.
39
41
  * For this, the {@link PreferenceContribution} has to provide a valid JSON Schema specifying which preferences
@@ -202,34 +204,44 @@ export class PreferenceSchemaProvider extends PreferenceProvider {
202
204
  const defaultScope = PreferenceSchema.getDefaultScope(schema);
203
205
  const overridable = schema.overridable || false;
204
206
  for (const [preferenceName, rawSchemaProps] of Object.entries(schema.properties)) {
205
- if (this.combinedSchema.properties[preferenceName]) {
207
+ if (this.combinedSchema.properties[preferenceName] && DefaultOverridesPreferenceSchemaId !== schema.id) {
206
208
  console.error('Preference name collision detected in the schema for property: ' + preferenceName);
207
- } else if (!rawSchemaProps.hasOwnProperty('included') || rawSchemaProps.included) {
208
- const schemaProps = PreferenceDataProperty.fromPreferenceSchemaProperty(rawSchemaProps, defaultScope);
209
- if (typeof schemaProps.overridable !== 'boolean' && overridable) {
210
- schemaProps.overridable = true;
211
- }
212
- if (schemaProps.overridable) {
213
- this.overridePatternProperties.properties[preferenceName] = schemaProps;
209
+ } else {
210
+ let schemaProps;
211
+ if (this.combinedSchema.properties[preferenceName] && DefaultOverridesPreferenceSchemaId === schema.id) {
212
+ // update existing default value in schema
213
+ schemaProps = PreferenceDataProperty.fromPreferenceSchemaProperty(rawSchemaProps, defaultScope);
214
+ this.updateSchemaPropsDefault(preferenceName, schemaProps);
215
+ } else if (!rawSchemaProps.hasOwnProperty('included') || rawSchemaProps.included) {
216
+ // add overrides for languages
217
+ schemaProps = PreferenceDataProperty.fromPreferenceSchemaProperty(rawSchemaProps, defaultScope);
218
+ if (typeof schemaProps.overridable !== 'boolean' && overridable) {
219
+ schemaProps.overridable = true;
220
+ }
221
+ if (schemaProps.overridable) {
222
+ this.overridePatternProperties.properties[preferenceName] = schemaProps;
223
+ }
224
+ this.updateSchemaProps(preferenceName, schemaProps);
214
225
  }
215
- this.updateSchemaProps(preferenceName, schemaProps);
216
-
217
- const schemaDefault = this.getDefaultValue(schemaProps);
218
- const configuredDefault = this.getConfiguredDefault(preferenceName);
219
- if (this.preferenceOverrideService.testOverrideValue(preferenceName, schemaDefault)) {
220
- schemaProps.defaultValue = PreferenceSchemaProperties.is(configuredDefault)
221
- ? PreferenceProvider.merge(schemaDefault, configuredDefault)
222
- : schemaDefault;
223
- if (schemaProps.defaultValue && PreferenceSchemaProperties.is(schemaProps.defaultValue)) {
224
- for (const overriddenPreferenceName in schemaProps.defaultValue) {
225
- const overrideValue = schemaDefault[overriddenPreferenceName];
226
- const overridePreferenceName = `${preferenceName}.${overriddenPreferenceName}`;
227
- changes.push(this.doSetPreferenceValue(overridePreferenceName, overrideValue, { scope, domain }));
226
+
227
+ if (schemaProps !== undefined) {
228
+ const schemaDefault = this.getDefaultValue(schemaProps);
229
+ const configuredDefault = this.getConfiguredDefault(preferenceName);
230
+ if (this.preferenceOverrideService.testOverrideValue(preferenceName, schemaDefault)) {
231
+ schemaProps.defaultValue = PreferenceSchemaProperties.is(configuredDefault)
232
+ ? PreferenceProvider.merge(schemaDefault, configuredDefault)
233
+ : schemaDefault;
234
+ if (schemaProps.defaultValue && PreferenceSchemaProperties.is(schemaProps.defaultValue)) {
235
+ for (const overriddenPreferenceName in schemaProps.defaultValue) {
236
+ const overrideValue = schemaDefault[overriddenPreferenceName];
237
+ const overridePreferenceName = `${preferenceName}.${overriddenPreferenceName}`;
238
+ changes.push(this.doSetPreferenceValue(overridePreferenceName, overrideValue, { scope, domain }));
239
+ }
228
240
  }
241
+ } else {
242
+ schemaProps.defaultValue = configuredDefault === undefined ? schemaDefault : configuredDefault;
243
+ changes.push(this.doSetPreferenceValue(preferenceName, schemaProps.defaultValue, { scope, domain }));
229
244
  }
230
- } else {
231
- schemaProps.defaultValue = configuredDefault === undefined ? schemaDefault : configuredDefault;
232
- changes.push(this.doSetPreferenceValue(preferenceName, schemaProps.defaultValue, { scope, domain }));
233
245
  }
234
246
  }
235
247
  }
@@ -383,6 +395,19 @@ export class PreferenceSchemaProvider extends PreferenceProvider {
383
395
  }
384
396
  }
385
397
 
398
+ protected updateSchemaPropsDefault(key: string, property: PreferenceDataProperty): void {
399
+ this.combinedSchema.properties[key].default = property.default;
400
+ this.combinedSchema.properties[key].defaultValue = property.defaultValue;
401
+ if (this.workspaceSchema.properties[key]) {
402
+ this.workspaceSchema.properties[key].default = property.default;
403
+ this.workspaceSchema.properties[key].defaultValue = property.defaultValue;
404
+ }
405
+ if (this.folderSchema.properties[key]) {
406
+ this.folderSchema.properties[key].default = property.default;
407
+ this.folderSchema.properties[key].defaultValue = property.defaultValue;
408
+ }
409
+ }
410
+
386
411
  protected removePropFromSchemas(key: string): void {
387
412
  // If we remove a key from combined, it should also be removed from all narrower scopes.
388
413
  delete this.combinedSchema.properties[key];
@@ -16,13 +16,13 @@
16
16
 
17
17
  import { Widget } from '@phosphor/widgets';
18
18
  import { Message } from '@phosphor/messaging';
19
- import { Event } from '../common/event';
19
+ import { Emitter, Event } from '../common/event';
20
20
  import { MaybePromise } from '../common/types';
21
21
  import { Key } from './keyboard/keys';
22
22
  import { AbstractDialog } from './dialogs';
23
23
  import { waitForClosed } from './widgets';
24
24
  import { nls } from '../common/nls';
25
- import { isObject } from '../common';
25
+ import { Disposable, isObject } from '../common';
26
26
 
27
27
  export interface Saveable {
28
28
  readonly dirty: boolean;
@@ -50,6 +50,45 @@ export interface SaveableSource {
50
50
  readonly saveable: Saveable;
51
51
  }
52
52
 
53
+ export class SaveableDelegate implements Saveable {
54
+ dirty = false;
55
+ protected readonly onDirtyChangedEmitter = new Emitter<void>();
56
+
57
+ get onDirtyChanged(): Event<void> {
58
+ return this.onDirtyChangedEmitter.event;
59
+ }
60
+ autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange' = 'off';
61
+
62
+ async save(options?: SaveOptions): Promise<void> {
63
+ await this.delegate?.save(options);
64
+ }
65
+
66
+ revert?(options?: Saveable.RevertOptions): Promise<void>;
67
+ createSnapshot?(): Saveable.Snapshot;
68
+ applySnapshot?(snapshot: object): void;
69
+
70
+ protected delegate?: Saveable;
71
+ protected toDispose?: Disposable;
72
+
73
+ set(delegate: Saveable): void {
74
+ this.toDispose?.dispose();
75
+ this.delegate = delegate;
76
+ this.toDispose = this.delegate.onDirtyChanged(() => {
77
+ this.dirty = delegate.dirty;
78
+ this.onDirtyChangedEmitter.fire();
79
+ });
80
+ this.autoSave = delegate.autoSave;
81
+ if (this.dirty !== delegate.dirty) {
82
+ this.dirty = delegate.dirty;
83
+ this.onDirtyChangedEmitter.fire();
84
+ }
85
+ this.revert = delegate.revert?.bind(delegate);
86
+ this.createSnapshot = delegate.createSnapshot?.bind(delegate);
87
+ this.applySnapshot = delegate.applySnapshot?.bind(delegate);
88
+ }
89
+
90
+ }
91
+
53
92
  export namespace Saveable {
54
93
  export interface RevertOptions {
55
94
  /**