Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/browser/actions/workspaceActions.ts
3296 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 { ITelemetryData } from '../../../platform/telemetry/common/telemetry.js';
8
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, hasWorkspaceFileExtension } from '../../../platform/workspace/common/workspace.js';
9
import { IWorkspaceEditingService } from '../../services/workspaces/common/workspaceEditing.js';
10
import { IEditorService } from '../../services/editor/common/editorService.js';
11
import { ICommandService } from '../../../platform/commands/common/commands.js';
12
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDER_COMMAND_ID, SET_ROOT_FOLDER_COMMAND_ID } from './workspaceCommands.js';
13
import { IFileDialogService } from '../../../platform/dialogs/common/dialogs.js';
14
import { MenuRegistry, MenuId, Action2, registerAction2 } from '../../../platform/actions/common/actions.js';
15
import { EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, OpenFolderWorkspaceSupportContext, WorkbenchStateContext, WorkspaceFolderCountContext } from '../../common/contextkeys.js';
16
import { ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js';
17
import { IHostService } from '../../services/host/browser/host.js';
18
import { KeyChord, KeyCode, KeyMod } from '../../../base/common/keyCodes.js';
19
import { ContextKeyExpr } from '../../../platform/contextkey/common/contextkey.js';
20
import { IWorkbenchEnvironmentService } from '../../services/environment/common/environmentService.js';
21
import { IWorkspacesService } from '../../../platform/workspaces/common/workspaces.js';
22
import { KeybindingWeight } from '../../../platform/keybinding/common/keybindingsRegistry.js';
23
import { IsMacNativeContext } from '../../../platform/contextkey/common/contextkeys.js';
24
import { ILocalizedString } from '../../../platform/action/common/action.js';
25
import { Categories } from '../../../platform/action/common/actionCommonCategories.js';
26
27
const workspacesCategory: ILocalizedString = localize2('workspaces', 'Workspaces');
28
29
export class OpenFileAction extends Action2 {
30
31
static readonly ID = 'workbench.action.files.openFile';
32
33
constructor() {
34
super({
35
id: OpenFileAction.ID,
36
title: localize2('openFile', 'Open File...'),
37
category: Categories.File,
38
f1: true,
39
keybinding: {
40
when: IsMacNativeContext.toNegated(),
41
weight: KeybindingWeight.WorkbenchContrib,
42
primary: KeyMod.CtrlCmd | KeyCode.KeyO
43
}
44
});
45
}
46
47
override async run(accessor: ServicesAccessor, data?: ITelemetryData): Promise<void> {
48
const fileDialogService = accessor.get(IFileDialogService);
49
50
return fileDialogService.pickFileAndOpen({ forceNewWindow: false, telemetryExtraData: data });
51
}
52
}
53
54
export class OpenFolderAction extends Action2 {
55
56
static readonly ID = 'workbench.action.files.openFolder';
57
58
constructor() {
59
super({
60
id: OpenFolderAction.ID,
61
title: localize2('openFolder', 'Open Folder...'),
62
category: Categories.File,
63
f1: true,
64
precondition: OpenFolderWorkspaceSupportContext,
65
keybinding: {
66
weight: KeybindingWeight.WorkbenchContrib,
67
primary: undefined,
68
linux: {
69
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyO)
70
},
71
win: {
72
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyO)
73
}
74
}
75
});
76
}
77
78
override async run(accessor: ServicesAccessor, data?: ITelemetryData): Promise<void> {
79
const fileDialogService = accessor.get(IFileDialogService);
80
81
return fileDialogService.pickFolderAndOpen({ forceNewWindow: false, telemetryExtraData: data });
82
}
83
}
84
85
export class OpenFolderViaWorkspaceAction extends Action2 {
86
87
// This action swaps the folders of a workspace with
88
// the selected folder and is a workaround for providing
89
// "Open Folder..." in environments that do not support
90
// this without having a workspace open (e.g. web serverless)
91
92
static readonly ID = 'workbench.action.files.openFolderViaWorkspace';
93
94
constructor() {
95
super({
96
id: OpenFolderViaWorkspaceAction.ID,
97
title: localize2('openFolder', 'Open Folder...'),
98
category: Categories.File,
99
f1: true,
100
precondition: ContextKeyExpr.and(OpenFolderWorkspaceSupportContext.toNegated(), WorkbenchStateContext.isEqualTo('workspace')),
101
keybinding: {
102
weight: KeybindingWeight.WorkbenchContrib,
103
primary: KeyMod.CtrlCmd | KeyCode.KeyO
104
}
105
});
106
}
107
108
override run(accessor: ServicesAccessor): Promise<void> {
109
const commandService = accessor.get(ICommandService);
110
111
return commandService.executeCommand(SET_ROOT_FOLDER_COMMAND_ID);
112
}
113
}
114
115
export class OpenFileFolderAction extends Action2 {
116
117
static readonly ID = 'workbench.action.files.openFileFolder';
118
static readonly LABEL: ILocalizedString = localize2('openFileFolder', 'Open...');
119
120
constructor() {
121
super({
122
id: OpenFileFolderAction.ID,
123
title: OpenFileFolderAction.LABEL,
124
category: Categories.File,
125
f1: true,
126
precondition: ContextKeyExpr.and(IsMacNativeContext, OpenFolderWorkspaceSupportContext),
127
keybinding: {
128
weight: KeybindingWeight.WorkbenchContrib,
129
primary: KeyMod.CtrlCmd | KeyCode.KeyO
130
}
131
});
132
}
133
134
override async run(accessor: ServicesAccessor, data?: ITelemetryData): Promise<void> {
135
const fileDialogService = accessor.get(IFileDialogService);
136
137
return fileDialogService.pickFileFolderAndOpen({ forceNewWindow: false, telemetryExtraData: data });
138
}
139
}
140
141
class OpenWorkspaceAction extends Action2 {
142
143
static readonly ID = 'workbench.action.openWorkspace';
144
145
constructor() {
146
super({
147
id: OpenWorkspaceAction.ID,
148
title: localize2('openWorkspaceAction', 'Open Workspace from File...'),
149
category: Categories.File,
150
f1: true,
151
precondition: EnterMultiRootWorkspaceSupportContext
152
});
153
}
154
155
override async run(accessor: ServicesAccessor, data?: ITelemetryData): Promise<void> {
156
const fileDialogService = accessor.get(IFileDialogService);
157
158
return fileDialogService.pickWorkspaceAndOpen({ telemetryExtraData: data });
159
}
160
}
161
162
class CloseWorkspaceAction extends Action2 {
163
164
static readonly ID = 'workbench.action.closeFolder';
165
166
constructor() {
167
super({
168
id: CloseWorkspaceAction.ID,
169
title: localize2('closeWorkspace', 'Close Workspace'),
170
category: workspacesCategory,
171
f1: true,
172
precondition: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), EmptyWorkspaceSupportContext),
173
keybinding: {
174
weight: KeybindingWeight.WorkbenchContrib,
175
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyF)
176
}
177
});
178
}
179
180
override async run(accessor: ServicesAccessor): Promise<void> {
181
const hostService = accessor.get(IHostService);
182
const environmentService = accessor.get(IWorkbenchEnvironmentService);
183
184
return hostService.openWindow({ forceReuseWindow: true, remoteAuthority: environmentService.remoteAuthority });
185
}
186
}
187
188
class OpenWorkspaceConfigFileAction extends Action2 {
189
190
static readonly ID = 'workbench.action.openWorkspaceConfigFile';
191
192
constructor() {
193
super({
194
id: OpenWorkspaceConfigFileAction.ID,
195
title: localize2('openWorkspaceConfigFile', 'Open Workspace Configuration File'),
196
category: workspacesCategory,
197
f1: true,
198
precondition: WorkbenchStateContext.isEqualTo('workspace')
199
});
200
}
201
202
override async run(accessor: ServicesAccessor): Promise<void> {
203
const contextService = accessor.get(IWorkspaceContextService);
204
const editorService = accessor.get(IEditorService);
205
206
const configuration = contextService.getWorkspace().configuration;
207
if (configuration) {
208
await editorService.openEditor({ resource: configuration, options: { pinned: true } });
209
}
210
}
211
}
212
213
export class AddRootFolderAction extends Action2 {
214
215
static readonly ID = 'workbench.action.addRootFolder';
216
217
constructor() {
218
super({
219
id: AddRootFolderAction.ID,
220
title: ADD_ROOT_FOLDER_LABEL,
221
category: workspacesCategory,
222
f1: true,
223
precondition: ContextKeyExpr.or(EnterMultiRootWorkspaceSupportContext, WorkbenchStateContext.isEqualTo('workspace'))
224
});
225
}
226
227
override run(accessor: ServicesAccessor): Promise<void> {
228
const commandService = accessor.get(ICommandService);
229
230
return commandService.executeCommand(ADD_ROOT_FOLDER_COMMAND_ID);
231
}
232
}
233
234
export class RemoveRootFolderAction extends Action2 {
235
236
static readonly ID = 'workbench.action.removeRootFolder';
237
238
constructor() {
239
super({
240
id: RemoveRootFolderAction.ID,
241
title: localize2('globalRemoveFolderFromWorkspace', 'Remove Folder from Workspace...'),
242
category: workspacesCategory,
243
f1: true,
244
precondition: ContextKeyExpr.and(WorkspaceFolderCountContext.notEqualsTo('0'), ContextKeyExpr.or(EnterMultiRootWorkspaceSupportContext, WorkbenchStateContext.isEqualTo('workspace')))
245
});
246
}
247
248
override async run(accessor: ServicesAccessor): Promise<void> {
249
const commandService = accessor.get(ICommandService);
250
const workspaceEditingService = accessor.get(IWorkspaceEditingService);
251
252
const folder = await commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND_ID);
253
if (folder) {
254
await workspaceEditingService.removeFolders([folder.uri]);
255
}
256
}
257
}
258
259
class SaveWorkspaceAsAction extends Action2 {
260
261
static readonly ID = 'workbench.action.saveWorkspaceAs';
262
263
constructor() {
264
super({
265
id: SaveWorkspaceAsAction.ID,
266
title: localize2('saveWorkspaceAsAction', 'Save Workspace As...'),
267
category: workspacesCategory,
268
f1: true,
269
precondition: EnterMultiRootWorkspaceSupportContext
270
});
271
}
272
273
override async run(accessor: ServicesAccessor): Promise<void> {
274
const workspaceEditingService = accessor.get(IWorkspaceEditingService);
275
const contextService = accessor.get(IWorkspaceContextService);
276
277
const configPathUri = await workspaceEditingService.pickNewWorkspacePath();
278
if (configPathUri && hasWorkspaceFileExtension(configPathUri)) {
279
switch (contextService.getWorkbenchState()) {
280
case WorkbenchState.EMPTY:
281
case WorkbenchState.FOLDER: {
282
const folders = contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri }));
283
return workspaceEditingService.createAndEnterWorkspace(folders, configPathUri);
284
}
285
case WorkbenchState.WORKSPACE:
286
return workspaceEditingService.saveAndEnterWorkspace(configPathUri);
287
}
288
}
289
}
290
}
291
292
class DuplicateWorkspaceInNewWindowAction extends Action2 {
293
294
static readonly ID = 'workbench.action.duplicateWorkspaceInNewWindow';
295
296
constructor() {
297
super({
298
id: DuplicateWorkspaceInNewWindowAction.ID,
299
title: localize2('duplicateWorkspaceInNewWindow', 'Duplicate As Workspace in New Window'),
300
category: workspacesCategory,
301
f1: true,
302
precondition: EnterMultiRootWorkspaceSupportContext
303
});
304
}
305
306
override async run(accessor: ServicesAccessor): Promise<void> {
307
const workspaceContextService = accessor.get(IWorkspaceContextService);
308
const workspaceEditingService = accessor.get(IWorkspaceEditingService);
309
const hostService = accessor.get(IHostService);
310
const workspacesService = accessor.get(IWorkspacesService);
311
const environmentService = accessor.get(IWorkbenchEnvironmentService);
312
313
const folders = workspaceContextService.getWorkspace().folders;
314
const remoteAuthority = environmentService.remoteAuthority;
315
316
const newWorkspace = await workspacesService.createUntitledWorkspace(folders, remoteAuthority);
317
await workspaceEditingService.copyWorkspaceSettings(newWorkspace);
318
319
return hostService.openWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true, remoteAuthority });
320
}
321
}
322
323
// --- Actions Registration
324
325
registerAction2(AddRootFolderAction);
326
registerAction2(RemoveRootFolderAction);
327
registerAction2(OpenFileAction);
328
registerAction2(OpenFolderAction);
329
registerAction2(OpenFolderViaWorkspaceAction);
330
registerAction2(OpenFileFolderAction);
331
registerAction2(OpenWorkspaceAction);
332
registerAction2(OpenWorkspaceConfigFileAction);
333
registerAction2(CloseWorkspaceAction);
334
registerAction2(SaveWorkspaceAsAction);
335
registerAction2(DuplicateWorkspaceInNewWindowAction);
336
337
// --- Menu Registration
338
339
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
340
group: '2_open',
341
command: {
342
id: OpenFileAction.ID,
343
title: localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")
344
},
345
order: 1,
346
when: IsMacNativeContext.toNegated()
347
});
348
349
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
350
group: '2_open',
351
command: {
352
id: OpenFolderAction.ID,
353
title: localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")
354
},
355
order: 2,
356
when: OpenFolderWorkspaceSupportContext
357
});
358
359
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
360
group: '2_open',
361
command: {
362
id: OpenFolderViaWorkspaceAction.ID,
363
title: localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")
364
},
365
order: 2,
366
when: ContextKeyExpr.and(OpenFolderWorkspaceSupportContext.toNegated(), WorkbenchStateContext.isEqualTo('workspace'))
367
});
368
369
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
370
group: '2_open',
371
command: {
372
id: OpenFileFolderAction.ID,
373
title: localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")
374
},
375
order: 1,
376
when: ContextKeyExpr.and(IsMacNativeContext, OpenFolderWorkspaceSupportContext)
377
});
378
379
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
380
group: '2_open',
381
command: {
382
id: OpenWorkspaceAction.ID,
383
title: localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace from File...")
384
},
385
order: 3,
386
when: EnterMultiRootWorkspaceSupportContext
387
});
388
389
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
390
group: '3_workspace',
391
command: {
392
id: ADD_ROOT_FOLDER_COMMAND_ID,
393
title: localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...")
394
},
395
when: ContextKeyExpr.or(EnterMultiRootWorkspaceSupportContext, WorkbenchStateContext.isEqualTo('workspace')),
396
order: 1
397
});
398
399
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
400
group: '3_workspace',
401
command: {
402
id: SaveWorkspaceAsAction.ID,
403
title: localize('miSaveWorkspaceAs', "Save Workspace As...")
404
},
405
order: 2,
406
when: EnterMultiRootWorkspaceSupportContext
407
});
408
409
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
410
group: '3_workspace',
411
command: {
412
id: DuplicateWorkspaceInNewWindowAction.ID,
413
title: localize('duplicateWorkspace', "Duplicate Workspace")
414
},
415
order: 3,
416
when: EnterMultiRootWorkspaceSupportContext
417
});
418
419
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
420
group: '6_close',
421
command: {
422
id: CloseWorkspaceAction.ID,
423
title: localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder")
424
},
425
order: 3,
426
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), EmptyWorkspaceSupportContext)
427
});
428
429
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
430
group: '6_close',
431
command: {
432
id: CloseWorkspaceAction.ID,
433
title: localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace")
434
},
435
order: 3,
436
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), EmptyWorkspaceSupportContext)
437
});
438
439