Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/browser/actions/windowActions.ts
5237 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { localize, localize2 } from '../../../nls.js';
7
import { IWindowOpenable } from '../../../platform/window/common/window.js';
8
import { IDialogService } from '../../../platform/dialogs/common/dialogs.js';
9
import { MenuRegistry, MenuId, Action2, registerAction2 } from '../../../platform/actions/common/actions.js';
10
import { KeyChord, KeyCode, KeyMod } from '../../../base/common/keyCodes.js';
11
import { IsMainWindowFullscreenContext } from '../../common/contextkeys.js';
12
import { IsMacNativeContext, IsDevelopmentContext, IsWebContext, IsIOSContext } from '../../../platform/contextkey/common/contextkeys.js';
13
import { Categories } from '../../../platform/action/common/actionCommonCategories.js';
14
import { KeybindingsRegistry, KeybindingWeight } from '../../../platform/keybinding/common/keybindingsRegistry.js';
15
import { IQuickInputButton, IQuickInputService, IQuickPickSeparator, IKeyMods, IQuickPickItem } from '../../../platform/quickinput/common/quickInput.js';
16
import { IWorkspaceContextService, IWorkspaceIdentifier, isWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from '../../../platform/workspace/common/workspace.js';
17
import { ILabelService, Verbosity } from '../../../platform/label/common/label.js';
18
import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js';
19
import { IModelService } from '../../../editor/common/services/model.js';
20
import { ILanguageService } from '../../../editor/common/languages/language.js';
21
import { IRecent, isRecentFolder, isRecentWorkspace, IWorkspacesService } from '../../../platform/workspaces/common/workspaces.js';
22
import { URI } from '../../../base/common/uri.js';
23
import { getIconClasses } from '../../../editor/common/services/getIconClasses.js';
24
import { FileKind } from '../../../platform/files/common/files.js';
25
import { splitRecentLabel } from '../../../base/common/labels.js';
26
import { isMacintosh, isWeb, isWindows } from '../../../base/common/platform.js';
27
import { ContextKeyExpr } from '../../../platform/contextkey/common/contextkey.js';
28
import { inQuickPickContext, getQuickNavigateHandler } from '../quickaccess.js';
29
import { IHostService } from '../../services/host/browser/host.js';
30
import { ResourceMap } from '../../../base/common/map.js';
31
import { Codicon } from '../../../base/common/codicons.js';
32
import { ThemeIcon } from '../../../base/common/themables.js';
33
import { CommandsRegistry } from '../../../platform/commands/common/commands.js';
34
import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';
35
import { ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js';
36
import { isFolderBackupInfo, isWorkspaceBackupInfo } from '../../../platform/backup/common/backup.js';
37
import { getActiveElement, getActiveWindow, isHTMLElement } from '../../../base/browser/dom.js';
38
39
export const inRecentFilesPickerContextKey = 'inRecentFilesPicker';
40
41
interface IRecentlyOpenedPick extends IQuickPickItem {
42
resource: URI;
43
openable: IWindowOpenable;
44
remoteAuthority: string | undefined;
45
}
46
47
abstract class BaseOpenRecentAction extends Action2 {
48
49
private readonly removeFromRecentlyOpened: IQuickInputButton = {
50
iconClass: ThemeIcon.asClassName(Codicon.removeClose),
51
tooltip: localize('remove', "Remove from Recently Opened")
52
};
53
54
private readonly dirtyRecentlyOpenedFolder: IQuickInputButton = {
55
iconClass: 'dirty-workspace ' + ThemeIcon.asClassName(Codicon.closeDirty),
56
tooltip: localize('dirtyRecentlyOpenedFolder', "Folder With Unsaved Files"),
57
alwaysVisible: true
58
};
59
60
private readonly dirtyRecentlyOpenedWorkspace: IQuickInputButton = {
61
...this.dirtyRecentlyOpenedFolder,
62
tooltip: localize('dirtyRecentlyOpenedWorkspace', "Workspace With Unsaved Files"),
63
};
64
65
private readonly windowOpenedRecentlyOpenedFolder: IQuickInputButton = {
66
iconClass: 'opened-workspace ' + ThemeIcon.asClassName(Codicon.window),
67
tooltip: localize('openedRecentlyOpenedFolder', "Folder Opened in a Window"),
68
alwaysVisible: true
69
};
70
71
private readonly windowOpenedRecentlyOpenedWorkspace: IQuickInputButton = {
72
...this.windowOpenedRecentlyOpenedFolder,
73
tooltip: localize('openedRecentlyOpenedWorkspace', "Workspace Opened in a Window"),
74
};
75
76
private readonly activeWindowOpenedRecentlyOpenedFolder: IQuickInputButton = {
77
iconClass: 'opened-workspace ' + ThemeIcon.asClassName(Codicon.windowActive),
78
tooltip: localize('activeOpenedRecentlyOpenedFolder', "Folder Opened in Active Window"),
79
alwaysVisible: true
80
};
81
82
private readonly activeWindowOpenedRecentlyOpenedWorkspace: IQuickInputButton = {
83
...this.activeWindowOpenedRecentlyOpenedFolder,
84
tooltip: localize('activeOpenedRecentlyOpenedWorkspace', "Workspace Opened in Active Window"),
85
};
86
87
protected abstract isQuickNavigate(): boolean;
88
89
override async run(accessor: ServicesAccessor): Promise<void> {
90
const workspacesService = accessor.get(IWorkspacesService);
91
const quickInputService = accessor.get(IQuickInputService);
92
const contextService = accessor.get(IWorkspaceContextService);
93
const labelService = accessor.get(ILabelService);
94
const keybindingService = accessor.get(IKeybindingService);
95
const modelService = accessor.get(IModelService);
96
const languageService = accessor.get(ILanguageService);
97
const hostService = accessor.get(IHostService);
98
const dialogService = accessor.get(IDialogService);
99
100
const [mainWindows, recentlyOpened, dirtyWorkspacesAndFolders] = await Promise.all([
101
hostService.getWindows({ includeAuxiliaryWindows: false }),
102
workspacesService.getRecentlyOpened(),
103
workspacesService.getDirtyWorkspaces()
104
]);
105
106
let hasWorkspaces = false;
107
108
// Identify all folders and workspaces with unsaved files
109
const dirtyFolders = new ResourceMap<boolean>();
110
const dirtyWorkspaces = new ResourceMap<IWorkspaceIdentifier>();
111
for (const dirtyWorkspace of dirtyWorkspacesAndFolders) {
112
if (isFolderBackupInfo(dirtyWorkspace)) {
113
dirtyFolders.set(dirtyWorkspace.folderUri, true);
114
} else {
115
dirtyWorkspaces.set(dirtyWorkspace.workspace.configPath, dirtyWorkspace.workspace);
116
hasWorkspaces = true;
117
}
118
}
119
120
// Identify all folders and workspaces opened in main windows
121
const activeWindowId = getActiveWindow().vscodeWindowId;
122
const openedInWindows = new ResourceMap<{ isActive: boolean }>();
123
for (const window of mainWindows) {
124
const isActive = window.id === activeWindowId;
125
if (isSingleFolderWorkspaceIdentifier(window.workspace)) {
126
openedInWindows.set(window.workspace.uri, { isActive });
127
} else if (isWorkspaceIdentifier(window.workspace)) {
128
openedInWindows.set(window.workspace.configPath, { isActive });
129
}
130
}
131
132
// Identify all recently opened folders and workspaces
133
const recentFolders = new ResourceMap<boolean>();
134
const recentWorkspaces = new ResourceMap<IWorkspaceIdentifier>();
135
for (const recent of recentlyOpened.workspaces) {
136
if (isRecentFolder(recent)) {
137
recentFolders.set(recent.folderUri, true);
138
} else {
139
recentWorkspaces.set(recent.workspace.configPath, recent.workspace);
140
hasWorkspaces = true;
141
}
142
}
143
144
// Fill in all known recently opened workspaces
145
const workspacePicks: IRecentlyOpenedPick[] = [];
146
for (const recent of recentlyOpened.workspaces) {
147
const isDirty = isRecentFolder(recent) ? dirtyFolders.has(recent.folderUri) : dirtyWorkspaces.has(recent.workspace.configPath);
148
const windowState = isRecentFolder(recent) ? openedInWindows.get(recent.folderUri) : openedInWindows.get(recent.workspace.configPath);
149
150
workspacePicks.push(this.toQuickPick(modelService, languageService, labelService, recent, { isDirty, windowState }));
151
}
152
153
// Fill any backup workspace that is not yet shown at the end
154
for (const dirtyWorkspaceOrFolder of dirtyWorkspacesAndFolders) {
155
if (isFolderBackupInfo(dirtyWorkspaceOrFolder) && !recentFolders.has(dirtyWorkspaceOrFolder.folderUri)) {
156
workspacePicks.push(this.toQuickPick(modelService, languageService, labelService, dirtyWorkspaceOrFolder, { isDirty: true, windowState: undefined }));
157
} else if (isWorkspaceBackupInfo(dirtyWorkspaceOrFolder) && !recentWorkspaces.has(dirtyWorkspaceOrFolder.workspace.configPath)) {
158
workspacePicks.push(this.toQuickPick(modelService, languageService, labelService, dirtyWorkspaceOrFolder, { isDirty: true, windowState: undefined }));
159
}
160
}
161
162
const filePicks = recentlyOpened.files.map(p => this.toQuickPick(modelService, languageService, labelService, p, { isDirty: false, windowState: undefined }));
163
164
// focus second entry if the first recent workspace is the current workspace
165
const firstEntry = recentlyOpened.workspaces[0];
166
const autoFocusSecondEntry: boolean = firstEntry && contextService.isCurrentWorkspace(isRecentWorkspace(firstEntry) ? firstEntry.workspace : firstEntry.folderUri);
167
168
let keyMods: IKeyMods | undefined;
169
170
const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: hasWorkspaces ? localize('workspacesAndFolders', "folders & workspaces") : localize('folders', "folders") };
171
const fileSeparator: IQuickPickSeparator = { type: 'separator', label: localize('files', "files") };
172
const picks = [workspaceSeparator, ...workspacePicks, fileSeparator, ...filePicks];
173
174
const pick = await quickInputService.pick(picks, {
175
contextKey: inRecentFilesPickerContextKey,
176
activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0],
177
placeHolder: isMacintosh ? localize('openRecentPlaceholderMac', "Select to open (hold Cmd-key to force new window or Option-key for same window)") : localize('openRecentPlaceholder', "Select to open (hold Ctrl-key to force new window or Alt-key for same window)"),
178
matchOnDescription: true,
179
sortByLabel: false,
180
onKeyMods: mods => keyMods = mods,
181
quickNavigate: this.isQuickNavigate() ? { keybindings: keybindingService.lookupKeybindings(this.desc.id) } : undefined,
182
hideInput: this.isQuickNavigate(),
183
onDidTriggerItemButton: async context => {
184
185
// Remove
186
if (context.button === this.removeFromRecentlyOpened || context.button === this.windowOpenedRecentlyOpenedFolder || context.button === this.windowOpenedRecentlyOpenedWorkspace) {
187
await workspacesService.removeRecentlyOpened([context.item.resource]);
188
context.removeItem();
189
}
190
191
// Dirty Folder/Workspace
192
else if (context.button === this.dirtyRecentlyOpenedFolder || context.button === this.dirtyRecentlyOpenedWorkspace) {
193
const isDirtyWorkspace = context.button === this.dirtyRecentlyOpenedWorkspace;
194
const { confirmed } = await dialogService.confirm({
195
title: isDirtyWorkspace ? localize('dirtyWorkspace', "Workspace with Unsaved Files") : localize('dirtyFolder', "Folder with Unsaved Files"),
196
message: isDirtyWorkspace ? localize('dirtyWorkspaceConfirm', "Do you want to open the workspace to review the unsaved files?") : localize('dirtyFolderConfirm', "Do you want to open the folder to review the unsaved files?"),
197
detail: isDirtyWorkspace ? localize('dirtyWorkspaceConfirmDetail', "Workspaces with unsaved files cannot be removed until all unsaved files have been saved or reverted.") : localize('dirtyFolderConfirmDetail', "Folders with unsaved files cannot be removed until all unsaved files have been saved or reverted.")
198
});
199
200
if (confirmed) {
201
hostService.openWindow(
202
[context.item.openable], {
203
remoteAuthority: context.item.remoteAuthority || null // local window if remoteAuthority is not set or can not be deducted from the openable
204
});
205
quickInputService.cancel();
206
}
207
}
208
}
209
});
210
211
if (pick) {
212
return hostService.openWindow([pick.openable], {
213
forceNewWindow: keyMods?.ctrlCmd,
214
forceReuseWindow: keyMods?.alt,
215
remoteAuthority: pick.remoteAuthority || null // local window if remoteAuthority is not set or can not be deducted from the openable
216
});
217
}
218
}
219
220
private toQuickPick(modelService: IModelService, languageService: ILanguageService, labelService: ILabelService, recent: IRecent, kind: { isDirty: boolean; windowState?: { isActive: boolean } }): IRecentlyOpenedPick {
221
let openable: IWindowOpenable | undefined;
222
let iconClasses: string[];
223
let fullLabel: string | undefined;
224
let resource: URI | undefined;
225
let isWorkspace = false;
226
227
// Folder
228
if (isRecentFolder(recent)) {
229
resource = recent.folderUri;
230
iconClasses = getIconClasses(modelService, languageService, resource, FileKind.FOLDER);
231
openable = { folderUri: resource };
232
fullLabel = recent.label || labelService.getWorkspaceLabel(resource, { verbose: Verbosity.LONG });
233
}
234
235
// Workspace
236
else if (isRecentWorkspace(recent)) {
237
resource = recent.workspace.configPath;
238
iconClasses = getIconClasses(modelService, languageService, resource, FileKind.ROOT_FOLDER);
239
openable = { workspaceUri: resource };
240
fullLabel = recent.label || labelService.getWorkspaceLabel(recent.workspace, { verbose: Verbosity.LONG });
241
isWorkspace = true;
242
}
243
244
// File
245
else {
246
resource = recent.fileUri;
247
iconClasses = getIconClasses(modelService, languageService, resource, FileKind.FILE);
248
openable = { fileUri: resource };
249
fullLabel = recent.label || labelService.getUriLabel(resource, { appendWorkspaceSuffix: true });
250
}
251
252
const { name, parentPath } = splitRecentLabel(fullLabel);
253
254
const buttons: IQuickInputButton[] = [];
255
if (kind.isDirty) {
256
buttons.push(isWorkspace ? this.dirtyRecentlyOpenedWorkspace : this.dirtyRecentlyOpenedFolder);
257
} else if (kind.windowState) {
258
if (kind.windowState.isActive) {
259
buttons.push(isWorkspace ? this.activeWindowOpenedRecentlyOpenedWorkspace : this.activeWindowOpenedRecentlyOpenedFolder);
260
} else {
261
buttons.push(isWorkspace ? this.windowOpenedRecentlyOpenedWorkspace : this.windowOpenedRecentlyOpenedFolder);
262
}
263
} else {
264
buttons.push(this.removeFromRecentlyOpened);
265
}
266
267
return {
268
iconClasses,
269
label: name,
270
ariaLabel: kind.isDirty ? isWorkspace ? localize('recentDirtyWorkspaceAriaLabel', "{0}, workspace with unsaved changes", name) : localize('recentDirtyFolderAriaLabel', "{0}, folder with unsaved changes", name) : name,
271
description: parentPath,
272
buttons,
273
openable,
274
resource,
275
remoteAuthority: recent.remoteAuthority
276
};
277
}
278
}
279
280
export class OpenRecentAction extends BaseOpenRecentAction {
281
282
static ID = 'workbench.action.openRecent';
283
284
constructor() {
285
super({
286
id: OpenRecentAction.ID,
287
title: {
288
...localize2('openRecent', "Open Recent..."),
289
mnemonicTitle: localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More..."),
290
},
291
category: Categories.File,
292
f1: true,
293
keybinding: {
294
weight: KeybindingWeight.WorkbenchContrib,
295
primary: KeyMod.CtrlCmd | KeyCode.KeyR,
296
mac: { primary: KeyMod.WinCtrl | KeyCode.KeyR }
297
},
298
menu: {
299
id: MenuId.MenubarRecentMenu,
300
group: 'y_more',
301
order: 1
302
}
303
});
304
}
305
306
protected isQuickNavigate(): boolean {
307
return false;
308
}
309
}
310
311
class QuickPickRecentAction extends BaseOpenRecentAction {
312
313
constructor() {
314
super({
315
id: 'workbench.action.quickOpenRecent',
316
title: localize2('quickOpenRecent', 'Quick Open Recent...'),
317
category: Categories.File,
318
f1: false // hide quick pickers from command palette to not confuse with the other entry that shows a input field
319
});
320
}
321
322
protected isQuickNavigate(): boolean {
323
return true;
324
}
325
}
326
327
class ToggleFullScreenAction extends Action2 {
328
329
constructor() {
330
super({
331
id: 'workbench.action.toggleFullScreen',
332
title: {
333
...localize2('toggleFullScreen', "Toggle Full Screen"),
334
mnemonicTitle: localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "&&Full Screen"),
335
},
336
category: Categories.View,
337
f1: true,
338
keybinding: {
339
weight: KeybindingWeight.WorkbenchContrib,
340
primary: KeyCode.F11,
341
mac: {
342
primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyF
343
}
344
},
345
precondition: IsIOSContext.toNegated(),
346
toggled: IsMainWindowFullscreenContext,
347
menu: [{
348
id: MenuId.MenubarAppearanceMenu,
349
group: '1_toggle_view',
350
order: 1
351
}]
352
});
353
}
354
355
override run(accessor: ServicesAccessor): Promise<void> {
356
const hostService = accessor.get(IHostService);
357
358
return hostService.toggleFullScreen(getActiveWindow());
359
}
360
}
361
362
export class ReloadWindowAction extends Action2 {
363
364
static readonly ID = 'workbench.action.reloadWindow';
365
366
constructor() {
367
super({
368
id: ReloadWindowAction.ID,
369
title: localize2('reloadWindow', 'Reload Window'),
370
category: Categories.Developer,
371
f1: true,
372
keybinding: {
373
weight: KeybindingWeight.WorkbenchContrib + 50,
374
when: IsDevelopmentContext,
375
primary: KeyMod.CtrlCmd | KeyCode.KeyR
376
}
377
});
378
}
379
380
override async run(accessor: ServicesAccessor): Promise<void> {
381
const hostService = accessor.get(IHostService);
382
383
return hostService.reload();
384
}
385
}
386
387
class ShowAboutDialogAction extends Action2 {
388
389
constructor() {
390
super({
391
id: 'workbench.action.showAboutDialog',
392
title: {
393
...localize2('about', "About"),
394
mnemonicTitle: localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About"),
395
},
396
category: Categories.Help,
397
f1: true,
398
menu: {
399
id: MenuId.MenubarHelpMenu,
400
group: 'z_about',
401
order: 1,
402
when: IsMacNativeContext.toNegated()
403
}
404
});
405
}
406
407
override run(accessor: ServicesAccessor): Promise<void> {
408
const dialogService = accessor.get(IDialogService);
409
410
return dialogService.about();
411
}
412
}
413
414
class NewWindowAction extends Action2 {
415
416
constructor() {
417
super({
418
id: 'workbench.action.newWindow',
419
title: {
420
...localize2('newWindow', "New Window"),
421
mnemonicTitle: localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window"),
422
},
423
f1: true,
424
keybinding: {
425
weight: KeybindingWeight.WorkbenchContrib,
426
primary: isWeb ? (isWindows ? KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.Shift | KeyCode.KeyN) : KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.KeyN) : KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyN,
427
secondary: isWeb ? [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyN] : undefined
428
},
429
menu: {
430
id: MenuId.MenubarFileMenu,
431
group: '1_new',
432
order: 3
433
}
434
});
435
}
436
437
override run(accessor: ServicesAccessor): Promise<void> {
438
const hostService = accessor.get(IHostService);
439
440
return hostService.openWindow({ remoteAuthority: null });
441
}
442
}
443
444
class BlurAction extends Action2 {
445
446
constructor() {
447
super({
448
id: 'workbench.action.blur',
449
title: localize2('blur', 'Remove keyboard focus from focused element')
450
});
451
}
452
453
run(): void {
454
const activeElement = getActiveElement();
455
if (isHTMLElement(activeElement)) {
456
activeElement.blur();
457
}
458
}
459
}
460
461
// --- Actions Registration
462
463
registerAction2(NewWindowAction);
464
registerAction2(ToggleFullScreenAction);
465
registerAction2(QuickPickRecentAction);
466
registerAction2(OpenRecentAction);
467
registerAction2(ReloadWindowAction);
468
registerAction2(ShowAboutDialogAction);
469
registerAction2(BlurAction);
470
471
// --- Commands/Keybindings Registration
472
473
const recentFilesPickerContext = ContextKeyExpr.and(inQuickPickContext, ContextKeyExpr.has(inRecentFilesPickerContextKey));
474
475
const quickPickNavigateNextInRecentFilesPickerId = 'workbench.action.quickOpenNavigateNextInRecentFilesPicker';
476
KeybindingsRegistry.registerCommandAndKeybindingRule({
477
id: quickPickNavigateNextInRecentFilesPickerId,
478
weight: KeybindingWeight.WorkbenchContrib + 50,
479
handler: getQuickNavigateHandler(quickPickNavigateNextInRecentFilesPickerId, true),
480
when: recentFilesPickerContext,
481
primary: KeyMod.CtrlCmd | KeyCode.KeyR,
482
mac: { primary: KeyMod.WinCtrl | KeyCode.KeyR }
483
});
484
485
const quickPickNavigatePreviousInRecentFilesPicker = 'workbench.action.quickOpenNavigatePreviousInRecentFilesPicker';
486
KeybindingsRegistry.registerCommandAndKeybindingRule({
487
id: quickPickNavigatePreviousInRecentFilesPicker,
488
weight: KeybindingWeight.WorkbenchContrib + 50,
489
handler: getQuickNavigateHandler(quickPickNavigatePreviousInRecentFilesPicker, false),
490
when: recentFilesPickerContext,
491
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyR,
492
mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KeyR }
493
});
494
495
CommandsRegistry.registerCommand('workbench.action.toggleConfirmBeforeClose', accessor => {
496
const configurationService = accessor.get(IConfigurationService);
497
const setting = configurationService.inspect<'always' | 'keyboardOnly' | 'never'>('window.confirmBeforeClose').userValue;
498
499
return configurationService.updateValue('window.confirmBeforeClose', setting === 'never' ? 'keyboardOnly' : 'never');
500
});
501
502
// --- Menu Registration
503
504
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
505
group: 'z_ConfirmClose',
506
command: {
507
id: 'workbench.action.toggleConfirmBeforeClose',
508
title: localize('miConfirmClose', "Confirm Before Close"),
509
toggled: ContextKeyExpr.notEquals('config.window.confirmBeforeClose', 'never')
510
},
511
order: 1,
512
when: IsWebContext
513
});
514
515
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
516
title: localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"),
517
submenu: MenuId.MenubarRecentMenu,
518
group: '2_open',
519
order: 4
520
});
521
522