@theia/core 1.24.0 → 1.25.0-next.10

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 (59) hide show
  1. package/README.md +3 -3
  2. package/lib/browser/common-frontend-contribution.d.ts +4 -0
  3. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  4. package/lib/browser/common-frontend-contribution.js +44 -4
  5. package/lib/browser/common-frontend-contribution.js.map +1 -1
  6. package/lib/browser/decorations-service.d.ts.map +1 -1
  7. package/lib/browser/frontend-application-module.d.ts.map +1 -1
  8. package/lib/browser/frontend-application-module.js +2 -0
  9. package/lib/browser/frontend-application-module.js.map +1 -1
  10. package/lib/browser/save-resource-service.d.ts +10 -5
  11. package/lib/browser/save-resource-service.d.ts.map +1 -1
  12. package/lib/browser/save-resource-service.js +28 -8
  13. package/lib/browser/save-resource-service.js.map +1 -1
  14. package/lib/browser/saveable.d.ts +1 -2
  15. package/lib/browser/saveable.d.ts.map +1 -1
  16. package/lib/browser/saveable.js +5 -10
  17. package/lib/browser/saveable.js.map +1 -1
  18. package/lib/browser/shell/application-shell.d.ts +3 -1
  19. package/lib/browser/shell/application-shell.d.ts.map +1 -1
  20. package/lib/browser/shell/application-shell.js +22 -10
  21. package/lib/browser/shell/application-shell.js.map +1 -1
  22. package/lib/browser/shell/theia-dock-panel.d.ts +2 -0
  23. package/lib/browser/shell/theia-dock-panel.d.ts.map +1 -1
  24. package/lib/browser/shell/theia-dock-panel.js +17 -1
  25. package/lib/browser/shell/theia-dock-panel.js.map +1 -1
  26. package/lib/browser/status-bar/status-bar.d.ts +4 -7
  27. package/lib/browser/status-bar/status-bar.d.ts.map +1 -1
  28. package/lib/browser/status-bar/status-bar.js +11 -4
  29. package/lib/browser/status-bar/status-bar.js.map +1 -1
  30. package/lib/browser/user-working-directory-provider.d.ts +17 -0
  31. package/lib/browser/user-working-directory-provider.d.ts.map +1 -0
  32. package/lib/browser/user-working-directory-provider.js +64 -0
  33. package/lib/browser/user-working-directory-provider.js.map +1 -0
  34. package/lib/common/accessibility.d.ts +17 -0
  35. package/lib/common/accessibility.d.ts.map +1 -0
  36. package/lib/common/accessibility.js +18 -0
  37. package/lib/common/accessibility.js.map +1 -0
  38. package/lib/common/quick-pick-service.d.ts +2 -0
  39. package/lib/common/quick-pick-service.d.ts.map +1 -1
  40. package/lib/common/quick-pick-service.js.map +1 -1
  41. package/lib/common/resource.d.ts +26 -0
  42. package/lib/common/resource.d.ts.map +1 -1
  43. package/lib/common/resource.js +76 -1
  44. package/lib/common/resource.js.map +1 -1
  45. package/package.json +3 -3
  46. package/src/browser/common-frontend-contribution.ts +44 -6
  47. package/src/browser/decorations-service.ts +2 -2
  48. package/src/browser/frontend-application-module.ts +2 -0
  49. package/src/browser/save-resource-service.ts +24 -10
  50. package/src/browser/saveable.ts +11 -9
  51. package/src/browser/shell/application-shell.ts +23 -9
  52. package/src/browser/shell/theia-dock-panel.ts +16 -0
  53. package/src/browser/status-bar/status-bar.tsx +14 -14
  54. package/src/browser/style/sidepanel.css +7 -12
  55. package/src/browser/style/tabs.css +14 -45
  56. package/src/browser/user-working-directory-provider.ts +48 -0
  57. package/src/common/accessibility.ts +33 -0
  58. package/src/common/quick-pick-service.ts +2 -0
  59. package/src/common/resource.ts +79 -0
@@ -38,6 +38,7 @@ import { waitForRevealed, waitForClosed } from '../widgets';
38
38
  import { CorePreferences } from '../core-preferences';
39
39
  import { BreadcrumbsRendererFactory } from '../breadcrumbs/breadcrumbs-renderer';
40
40
  import { Deferred } from '../../common/promise-util';
41
+ import { SaveResourceService } from '../save-resource-service';
41
42
 
42
43
  /** The class name added to ApplicationShell instances. */
43
44
  const APPLICATION_SHELL_CLASS = 'theia-ApplicationShell';
@@ -216,7 +217,8 @@ export class ApplicationShell extends Widget {
216
217
  @inject(SplitPositionHandler) protected splitPositionHandler: SplitPositionHandler,
217
218
  @inject(FrontendApplicationStateService) protected readonly applicationStateService: FrontendApplicationStateService,
218
219
  @inject(ApplicationShellOptions) @optional() options: RecursivePartial<ApplicationShell.Options> = {},
219
- @inject(CorePreferences) protected readonly corePreferences: CorePreferences
220
+ @inject(CorePreferences) protected readonly corePreferences: CorePreferences,
221
+ @inject(SaveResourceService) protected readonly saveResourceService: SaveResourceService,
220
222
  ) {
221
223
  super(options as Widget.IOptions);
222
224
  }
@@ -688,6 +690,7 @@ export class ApplicationShell extends Widget {
688
690
  this.collapseBottomPanel();
689
691
  }
690
692
  const widgets = toArray(this.bottomPanel.widgets());
693
+ this.bottomPanel.markActiveTabBar(widgets[0]?.title);
691
694
  if (bottomPanel.pinned && bottomPanel.pinned.length === widgets.length) {
692
695
  widgets.forEach((a, i) => {
693
696
  if (bottomPanel.pinned![i]) {
@@ -704,6 +707,9 @@ export class ApplicationShell extends Widget {
704
707
  this.mainPanel.restoreLayout(mainPanel);
705
708
  this.registerWithFocusTracker(mainPanel.main);
706
709
  const widgets = toArray(this.mainPanel.widgets());
710
+ // We don't store information about the last active tabbar
711
+ // So we simply mark the first as being active
712
+ this.mainPanel.markActiveTabBar(widgets[0]?.title);
707
713
  if (mainPanelPinned && mainPanelPinned.length === widgets.length) {
708
714
  widgets.forEach((a, i) => {
709
715
  if (mainPanelPinned[i]) {
@@ -1070,7 +1076,11 @@ export class ApplicationShell extends Widget {
1070
1076
  }
1071
1077
  this.tracker.add(widget);
1072
1078
  this.checkActivation(widget);
1073
- Saveable.apply(widget, () => this.widgets.filter((maybeSaveable): maybeSaveable is Widget & SaveableSource => !!Saveable.get(maybeSaveable)));
1079
+ Saveable.apply(
1080
+ widget,
1081
+ () => this.widgets.filter((maybeSaveable): maybeSaveable is Widget & SaveableSource => !!Saveable.get(maybeSaveable)),
1082
+ (toSave, options) => this.saveResourceService.save(toSave, options),
1083
+ );
1074
1084
  if (ApplicationShell.TrackableWidgetProvider.is(widget)) {
1075
1085
  for (const toTrack of widget.getTrackableWidgets()) {
1076
1086
  this.track(toTrack);
@@ -1412,6 +1422,10 @@ export class ApplicationShell extends Widget {
1412
1422
  alignment: StatusBarAlignment.RIGHT,
1413
1423
  tooltip: 'Toggle Bottom Panel',
1414
1424
  command: 'core.toggle.bottom.panel',
1425
+ accessibilityInformation: {
1426
+ label: 'Toggle Bottom Panel',
1427
+ role: 'button'
1428
+ },
1415
1429
  priority: -1000
1416
1430
  };
1417
1431
  this.statusBar.setElement(BOTTOM_PANEL_TOGGLE_ID, element);
@@ -1831,32 +1845,32 @@ export class ApplicationShell extends Widget {
1831
1845
  * Test whether the current widget is dirty.
1832
1846
  */
1833
1847
  canSave(): boolean {
1834
- return Saveable.isDirty(this.currentWidget);
1848
+ return this.saveResourceService.canSave(this.currentWidget);
1835
1849
  }
1836
1850
 
1837
1851
  /**
1838
1852
  * Save the current widget if it is dirty.
1839
1853
  */
1840
1854
  async save(options?: SaveOptions): Promise<void> {
1841
- await Saveable.save(this.currentWidget, options);
1855
+ await this.saveResourceService.save(this.currentWidget, options);
1842
1856
  }
1843
1857
 
1844
1858
  /**
1845
1859
  * Test whether there is a dirty widget.
1846
1860
  */
1847
1861
  canSaveAll(): boolean {
1848
- return this.tracker.widgets.some(Saveable.isDirty);
1862
+ return this.tracker.widgets.some(widget => this.saveResourceService.canSave(widget));
1849
1863
  }
1850
1864
 
1851
1865
  /**
1852
1866
  * Save all dirty widgets.
1853
1867
  */
1854
1868
  async saveAll(options?: SaveOptions): Promise<void> {
1855
- await Promise.all(this.tracker.widgets.map(widget => {
1856
- if (Saveable.isDirty(widget)) {
1857
- Saveable.save(widget, options);
1869
+ for (const widget of this.widgets) {
1870
+ if (this.saveResourceService.canSaveNotSaveAs(widget)) {
1871
+ await this.saveResourceService.save(widget, options);
1858
1872
  }
1859
- }));
1873
+ }
1860
1874
  }
1861
1875
 
1862
1876
  /**
@@ -24,6 +24,7 @@ import { inject } from 'inversify';
24
24
  import { Emitter, environment } from '../../common';
25
25
 
26
26
  export const MAXIMIZED_CLASS = 'theia-maximized';
27
+ export const ACTIVE_TABBAR_CLASS = 'theia-tabBar-active';
27
28
  const VISIBLE_MENU_MAXIMIZED_CLASS = 'theia-visible-menu-maximized';
28
29
 
29
30
  export const MAIN_AREA_ID = 'theia-main-content-panel';
@@ -106,6 +107,7 @@ export class TheiaDockPanel extends DockPanel {
106
107
  markAsCurrent(title: Title<Widget> | undefined): void {
107
108
  this.toDisposeOnMarkAsCurrent.dispose();
108
109
  this._currentTitle = title;
110
+ this.markActiveTabBar(title);
109
111
  if (title) {
110
112
  const resetCurrent = () => this.markAsCurrent(undefined);
111
113
  title.owner.disposed.connect(resetCurrent);
@@ -115,17 +117,31 @@ export class TheiaDockPanel extends DockPanel {
115
117
  }
116
118
  }
117
119
 
120
+ markActiveTabBar(title?: Title<Widget>): void {
121
+ const tabBars = toArray(this.tabBars());
122
+ tabBars.forEach(tabBar => tabBar.removeClass(ACTIVE_TABBAR_CLASS));
123
+ const activeTabBar = title && this.findTabBar(title);
124
+ if (activeTabBar) {
125
+ activeTabBar.addClass(ACTIVE_TABBAR_CLASS);
126
+ } else if (tabBars.length > 0) {
127
+ // At least one tabbar needs to be active
128
+ tabBars[0].addClass(ACTIVE_TABBAR_CLASS);
129
+ }
130
+ }
131
+
118
132
  override addWidget(widget: Widget, options?: DockPanel.IAddOptions): void {
119
133
  if (this.mode === 'single-document' && widget.parent === this) {
120
134
  return;
121
135
  }
122
136
  super.addWidget(widget, options);
123
137
  this.widgetAdded.emit(widget);
138
+ this.markActiveTabBar(widget.title);
124
139
  }
125
140
 
126
141
  override activateWidget(widget: Widget): void {
127
142
  super.activateWidget(widget);
128
143
  this.widgetActivated.emit(widget);
144
+ this.markActiveTabBar(widget.title);
129
145
  }
130
146
 
131
147
  protected override onChildRemoved(msg: Widget.ChildMessage): void {
@@ -22,6 +22,7 @@ import { ReactWidget } from '../widgets/react-widget';
22
22
  import { FrontendApplicationStateService } from '../frontend-application-state';
23
23
  import { LabelParser, LabelIcon } from '../label-parser';
24
24
  import { PreferenceService } from '../preferences';
25
+ import { AccessibilityInformation } from '../../common/accessibility';
25
26
 
26
27
  export interface StatusBarEntry {
27
28
  /**
@@ -44,6 +45,7 @@ export interface StatusBarEntry {
44
45
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
46
  arguments?: any[];
46
47
  priority?: number;
48
+ accessibilityInformation?: AccessibilityInformation;
47
49
  onclick?: (e: MouseEvent) => void;
48
50
  }
49
51
 
@@ -51,13 +53,6 @@ export enum StatusBarAlignment {
51
53
  LEFT, RIGHT
52
54
  }
53
55
 
54
- export interface StatusBarEntryAttributes {
55
- className?: string;
56
- title?: string;
57
- style?: object;
58
- onClick?: (e: MouseEvent) => void;
59
- }
60
-
61
56
  export const STATUSBAR_WIDGET_FACTORY_ID = 'statusBar';
62
57
 
63
58
  export const StatusBar = Symbol('StatusBar');
@@ -169,15 +164,15 @@ export class StatusBarImpl extends ReactWidget implements StatusBar {
169
164
  };
170
165
  }
171
166
 
172
- protected createAttributes(entry: StatusBarEntry): StatusBarEntryAttributes {
173
- const attrs: StatusBarEntryAttributes = {};
167
+ protected createAttributes(entry: StatusBarEntry): React.Attributes & React.HTMLAttributes<HTMLElement> {
168
+ const attrs: React.Attributes & React.HTMLAttributes<HTMLElement> = {};
174
169
 
175
170
  if (entry.command) {
176
171
  attrs.onClick = this.onclick(entry);
177
172
  attrs.className = 'element hasCommand';
178
173
  } else if (entry.onclick) {
179
174
  attrs.onClick = e => {
180
- if (entry.onclick) {
175
+ if (entry.onclick && e instanceof MouseEvent) {
181
176
  entry.onclick(e);
182
177
  }
183
178
  };
@@ -189,15 +184,20 @@ export class StatusBarImpl extends ReactWidget implements StatusBar {
189
184
  if (entry.tooltip) {
190
185
  attrs.title = entry.tooltip;
191
186
  }
187
+ if (entry.className) {
188
+ attrs.className += ' ' + entry.className;
189
+ }
190
+ if (entry.accessibilityInformation) {
191
+ attrs['aria-label'] = entry.accessibilityInformation.label;
192
+ attrs.role = entry.accessibilityInformation.role;
193
+ } else {
194
+ attrs['aria-label'] = [entry.text, entry.tooltip].join(', ');
195
+ }
192
196
 
193
197
  attrs.style = {
194
198
  color: entry.color || this.color
195
199
  };
196
200
 
197
- if (entry.className) {
198
- attrs.className += ' ' + entry.className;
199
- }
200
-
201
201
  return attrs;
202
202
  }
203
203
 
@@ -90,19 +90,11 @@
90
90
  border-top-color: transparent;
91
91
  }
92
92
 
93
- .p-TabBar.theia-app-left .p-TabBar-tab.p-mod-current.theia-mod-active {
94
- border-left: var(--theia-panel-border-width) solid var(--theia-focusBorder);
95
- }
96
-
97
93
  .p-TabBar.theia-app-right .p-TabBar-tab.p-mod-current {
98
94
  border-right: var(--theia-panel-border-width) solid var(--theia-activityBar-activeBorder);
99
95
  border-top-color: transparent;
100
96
  }
101
97
 
102
- .p-TabBar.theia-app-right .p-TabBar-tab.p-mod-current.theia-mod-active {
103
- border-right: var(--theia-panel-border-width) solid var(--theia-focusBorder);
104
- }
105
-
106
98
  .p-TabBar.theia-app-sides .p-TabBar-tabLabel,
107
99
  .p-TabBar.theia-app-sides .p-TabBar-tabCloseIcon {
108
100
  display: none;
@@ -285,16 +277,19 @@
285
277
 
286
278
  #theia-bottom-content-panel .p-TabBar-tab:not(.p-mod-current) {
287
279
  color: var(--theia-panelTitle-inactiveForeground);
288
- border-top: var(--theia-border-width) solid var(--theia-panel-background);
289
280
  }
290
281
 
291
282
  #theia-bottom-content-panel .p-TabBar-tab.p-mod-current {
292
283
  color: var(--theia-panelTitle-activeForeground);
293
- border-top: var(--theia-border-width) solid var(--theia-panelTitle-activeBorder);
284
+ box-shadow: 0 var(--theia-border-width) 0 var(--theia-panelTitle-activeBorder) inset;
285
+ }
286
+
287
+ #theia-bottom-content-panel .p-TabBar:not(.theia-tabBar-active) .p-TabBar-tab .theia-tab-icon-label {
288
+ color: var(--theia-tab-unfocusedInactiveForeground);
294
289
  }
295
290
 
296
- #theia-bottom-content-panel .p-TabBar-tab.p-mod-current.theia-mod-active {
297
- border-top-color: var(--theia-focusBorder);
291
+ #theia-bottom-content-panel .p-TabBar:not(.theia-tabBar-active) .p-TabBar-tab.p-mod-current .theia-tab-icon-label {
292
+ color: var(--theia-tab-unfocusedActiveForeground);
298
293
  }
299
294
 
300
295
  /*-----------------------------------------------------------------------------
@@ -80,65 +80,34 @@
80
80
 
81
81
  #theia-main-content-panel .p-TabBar .p-TabBar-tab {
82
82
  border-right: 1px solid var(--theia-tab-border);
83
+ background: var(--theia-tab-inactiveBackground);
84
+ color: var(--theia-tab-inactiveForeground);
83
85
  }
84
86
 
85
- #theia-main-content-panel .p-TabBar .p-TabBar-tab:hover.theia-mod-active {
86
- background: var(--theia-tab-hoverBackground) !important;
87
- box-shadow: var(--theia-tab-hoverBorder) 0 -1px inset !important;
88
- }
89
-
90
- #theia-main-content-panel .p-TabBar .p-TabBar-tab:hover:not(.theia-mod-active) {
91
- background: var(--theia-tab-unfocusedHoverBackground) !important;
92
- box-shadow: var(--theia-tab-unfocusedHoverBorder) 0 -1px inset !important;
87
+ #theia-main-content-panel .p-TabBar .p-TabBar-tab:hover {
88
+ background: var(--theia-tab-hoverBackground);
89
+ box-shadow: 0 1px 0 var(--theia-tab-hoverBorder) inset;
93
90
  }
94
91
 
95
- /* active tab in an active group */
96
- body.theia-editor-highlightModifiedTabs
97
- #theia-main-content-panel .p-TabBar .p-TabBar-tab.p-mod-current.theia-mod-active.theia-mod-dirty {
98
- border-top: 1px solid var(--theia-tab-activeModifiedBorder);
92
+ #theia-main-content-panel .p-TabBar:not(.theia-tabBar-active) .p-TabBar-tab:hover {
93
+ background: var(--theia-tab-unfocusedHoverBackground);
94
+ box-shadow: 0 1px 0 var(--theia-tab-unfocusedHoverBorder) inset;
99
95
  }
100
96
 
101
- #theia-main-content-panel .p-TabBar .p-TabBar-tab.p-mod-current.theia-mod-active {
97
+ #theia-main-content-panel .p-TabBar .p-TabBar-tab.p-mod-current {
102
98
  background: var(--theia-tab-activeBackground);
103
99
  color: var(--theia-tab-activeForeground);
104
- border-top: 1px solid var(--theia-tab-activeBorderTop);
105
- border-bottom: 1px solid var(--theia-tab-activeBorder);
106
- }
107
-
108
- /* inactive tab in an active group */
109
- body.theia-editor-highlightModifiedTabs
110
- #theia-main-content-panel .p-TabBar .p-TabBar-tab:not(.p-mod-current).theia-mod-active.theia-mod-dirty {
111
- border-top: 1px solid var(--theia-tab-inactiveModifiedBorder);
112
- }
113
-
114
- #theia-main-content-panel .p-TabBar .p-TabBar-tab:not(.p-mod-current).theia-mod-active {
115
- background: var(--theia-tab-inactiveBackground);
116
- color: var(--theia-tab-inactiveForeground);
100
+ box-shadow: 0 1px 0 var(--theia-tab-activeBorderTop) inset, 0 -1px 0 var(--theia-tab-activeBorder) inset;
117
101
  }
118
102
 
119
- /* active tab in an unfocused group */
120
- body.theia-editor-highlightModifiedTabs
121
- #theia-main-content-panel .p-TabBar .p-TabBar-tab.p-mod-current:not(.theia-mod-active).theia-mod-dirty {
122
- border-top: 1px solid var(--theia-tab-unfocusedActiveModifiedBorder);
103
+ #theia-main-content-panel .p-TabBar:not(.theia-tabBar-active) .p-TabBar-tab {
104
+ color: var(--theia-tab-unfocusedInactiveForeground);
123
105
  }
124
106
 
125
- #theia-main-content-panel .p-TabBar .p-TabBar-tab.p-mod-current:not(.theia-mod-active) {
107
+ #theia-main-content-panel .p-TabBar:not(.theia-tabBar-active) .p-TabBar-tab.p-mod-current {
126
108
  background: var(--theia-tab-unfocusedActiveBackground);
127
109
  color: var(--theia-tab-unfocusedActiveForeground);
128
- border-top: 1px solid var(--theia-tab-unfocusedActiveBorderTop);
129
- border-bottom: 1px solid var(--theia-tab-unfocusedActiveBorder);
130
- }
131
-
132
- /* inactive tab in an unfocused group */
133
- body.theia-editor-highlightModifiedTabs
134
- #theia-main-content-panel .p-TabBar .p-TabBar-tab:not(.p-mod-current):not(.theia-mod-active).theia-mod-dirty {
135
- border-top: 1px solid var(--theia-tab-unfocusedInactiveModifiedBorder);
136
- }
137
-
138
- #theia-main-content-panel .p-TabBar .p-TabBar-tab:not(.p-mod-current):not(.theia-mod-active) {
139
- background: var(--theia-tab-inactiveBackground);
140
- color: var(--theia-tab-inactiveForeground);
141
- border-top: 1px solid var(--theia-tab-inactiveBackground);
110
+ box-shadow: 0 1px 0 var(--theia-tab-unfocusedActiveBorderTop) inset, 0 -1px 0 var(--theia-tab-unfocusedActiveBorder) inset;
142
111
  }
143
112
 
144
113
  .p-TabBar.theia-app-centers {
@@ -0,0 +1,48 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 Ericsson 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 } from 'inversify';
18
+ import URI from '../common/uri';
19
+ import { MaybePromise, SelectionService, UriSelection } from '../common';
20
+ import { EnvVariablesServer } from '../common/env-variables';
21
+
22
+ @injectable()
23
+ export class UserWorkingDirectoryProvider {
24
+ @inject(SelectionService) protected readonly selectionService: SelectionService;
25
+ @inject(EnvVariablesServer) protected readonly envVariables: EnvVariablesServer;
26
+
27
+ /**
28
+ * @returns A {@link URI} that represents a good guess about the directory in which the user is currently operating.
29
+ *
30
+ * Factors considered may include the current widget, current selection, user home directory, or other application state.
31
+ */
32
+ async getUserWorkingDir(): Promise<URI> {
33
+ return await this.getFromSelection()
34
+ ?? this.getFromUserHome();
35
+ }
36
+
37
+ protected getFromSelection(): MaybePromise<URI | undefined> {
38
+ return this.ensureIsDirectory(UriSelection.getUri(this.selectionService.selection));
39
+ }
40
+
41
+ protected getFromUserHome(): MaybePromise<URI> {
42
+ return this.envVariables.getHomeDirUri().then(home => new URI(home));
43
+ }
44
+
45
+ protected ensureIsDirectory(uri?: URI): MaybePromise<URI | undefined> {
46
+ return uri?.parent;
47
+ }
48
+ }
@@ -0,0 +1,33 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 STMicroelectronics and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ /**
18
+ * Accessibility information which controls screen reader behavior.
19
+ */
20
+ export interface AccessibilityInformation {
21
+ /**
22
+ * Label to be read out by a screen reader once the item has focus.
23
+ */
24
+ readonly label: string;
25
+
26
+ /**
27
+ * Role of the widget which defines how a screen reader interacts with it.
28
+ * The role should be set in special cases when for example a tree-like element behaves like a checkbox.
29
+ * If role is not specified the editor will pick the appropriate role automatically.
30
+ * More about aria roles can be found here https://w3c.github.io/aria/#widget_roles
31
+ */
32
+ readonly role?: string;
33
+ }
@@ -201,6 +201,7 @@ export interface QuickPick<T extends QuickPickItemOrSeparator> extends QuickInpu
201
201
  canSelectMany: boolean;
202
202
  matchOnDescription: boolean;
203
203
  matchOnDetail: boolean;
204
+ keepScrollPosition: boolean;
204
205
  readonly onDidAccept: Event<{ inBackground: boolean } | undefined>;
205
206
  readonly onDidChangeValue: Event<string>;
206
207
  readonly onDidTriggerButton: Event<QuickInputButton>;
@@ -261,6 +262,7 @@ export interface QuickPickOptions<T extends QuickPickItemOrSeparator> {
261
262
  matchOnDetail?: boolean;
262
263
  matchOnLabel?: boolean;
263
264
  sortByLabel?: boolean;
265
+ keepScrollPosition?: boolean;
264
266
  autoFocusOnList?: boolean;
265
267
  ignoreFocusOut?: boolean;
266
268
  valueSelection?: Readonly<[number, number]>;
@@ -317,3 +317,82 @@ export class InMemoryTextResourceResolver implements ResourceResolver {
317
317
  return new InMemoryTextResource(uri);
318
318
  }
319
319
  }
320
+
321
+ export const UNTITLED_SCHEME = 'untitled';
322
+
323
+ let untitledResourceSequenceIndex = 0;
324
+
325
+ @injectable()
326
+ export class UntitledResourceResolver implements ResourceResolver {
327
+
328
+ protected readonly resources = new Map<string, UntitledResource>();
329
+
330
+ async resolve(uri: URI): Promise<UntitledResource> {
331
+ if (uri.scheme !== UNTITLED_SCHEME) {
332
+ throw new Error('The given uri is not untitled file uri: ' + uri);
333
+ } else {
334
+ const untitledResource = this.resources.get(uri.toString());
335
+ if (!untitledResource) {
336
+ return this.createUntitledResource('', '', uri);
337
+ } else {
338
+ return untitledResource;
339
+ }
340
+ }
341
+ }
342
+
343
+ async createUntitledResource(content?: string, extension?: string, uri?: URI): Promise<UntitledResource> {
344
+ return new UntitledResource(this.resources, uri ? uri : new URI().withScheme(UNTITLED_SCHEME).withPath(`/Untitled-${untitledResourceSequenceIndex++}${extension ?? ''}`),
345
+ content);
346
+ }
347
+ }
348
+
349
+ export class UntitledResource implements Resource {
350
+
351
+ protected readonly onDidChangeContentsEmitter = new Emitter<void>();
352
+ get onDidChangeContents(): Event<void> {
353
+ return this.onDidChangeContentsEmitter.event;
354
+ }
355
+
356
+ constructor(private resources: Map<string, UntitledResource>, public uri: URI, private content?: string) {
357
+ this.resources.set(this.uri.toString(), this);
358
+ }
359
+
360
+ dispose(): void {
361
+ this.resources.delete(this.uri.toString());
362
+ this.onDidChangeContentsEmitter.dispose();
363
+ }
364
+
365
+ async readContents(options?: { encoding?: string | undefined; } | undefined): Promise<string> {
366
+ if (this.content) {
367
+ return this.content;
368
+ } else {
369
+ return '';
370
+ }
371
+ }
372
+
373
+ async saveContents(content: string, options?: { encoding?: string, overwriteEncoding?: boolean }): Promise<void> {
374
+ // This function must exist to ensure readOnly is false for the Monaco editor.
375
+ // However it should not be called because saving 'untitled' is always processed as 'Save As'.
376
+ throw Error('Untitled resources cannot be saved.');
377
+ }
378
+
379
+ protected fireDidChangeContents(): void {
380
+ this.onDidChangeContentsEmitter.fire(undefined);
381
+ }
382
+
383
+ get version(): ResourceVersion | undefined {
384
+ return undefined;
385
+ }
386
+
387
+ get encoding(): string | undefined {
388
+ return undefined;
389
+ }
390
+ }
391
+
392
+ export function createUntitledURI(extension?: string, parent?: URI): URI {
393
+ const name = `Untitled-${untitledResourceSequenceIndex++}${extension ?? ''}`;
394
+ if (parent) {
395
+ return parent.resolve(name).withScheme(UNTITLED_SCHEME);
396
+ }
397
+ return new URI().resolve(name).withScheme(UNTITLED_SCHEME);
398
+ }