Skip to content

Commit

Permalink
fix: enable multi-root workspace support (#3848)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhanba authored Jul 18, 2024
1 parent fe71646 commit d26c0ef
Show file tree
Hide file tree
Showing 15 changed files with 178 additions and 52 deletions.
10 changes: 10 additions & 0 deletions configs/vscode-extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,16 @@
"version": "1.55.2"
}
],
"ms-python": [
{
"name": "python",
"version": "2024.4.1"
},
{
"name": "debugpy",
"version": "2024.6.0"
}
],
"ms-vscode": [
{
"name": "js-debug",
Expand Down
1 change: 1 addition & 0 deletions packages/core-browser/src/common/common.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,7 @@ export namespace WORKSPACE_COMMANDS {

export const REMOVE_WORKSPACE_FOLDER: Command = {
id: 'workspace.removeFolderFromWorkspace',
label: '%workspace.removeFolderFromWorkspace%',
category: CATEGORY,
};

Expand Down
18 changes: 17 additions & 1 deletion packages/core-browser/src/common/common.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ import { MenuId } from '../menu/next/menu-id';
import { PreferenceContribution } from '../preferences';
import { AppConfig } from '../react-providers/config-provider';

import { COMMON_COMMANDS, EDITOR_COMMANDS, FILE_COMMANDS, TERMINAL_COMMANDS } from './common.command';
import {
COMMON_COMMANDS,
EDITOR_COMMANDS,
FILE_COMMANDS,
TERMINAL_COMMANDS,
WORKSPACE_COMMANDS,
} from './common.command';
import { ClientAppContribution } from './common.define';

export const inputFocusedContextKey = 'inputFocus';
Expand Down Expand Up @@ -155,6 +161,16 @@ export class ClientCommonContribution
group: '1_open',
when: 'config.application.supportsOpenWorkspace',
},
{
command: WORKSPACE_COMMANDS.ADD_WORKSPACE_FOLDER.id,
group: '1_open',
when: 'config.workspace.supportMultiRootWorkspace',
},
{
command: WORKSPACE_COMMANDS.SAVE_WORKSPACE_AS_FILE.id,
group: '1_open',
when: 'config.workspace.supportMultiRootWorkspace',
},
{
command: EDITOR_COMMANDS.NEW_UNTITLED_FILE.id,
group: '2_new',
Expand Down
16 changes: 14 additions & 2 deletions packages/file-tree-next/src/browser/dialog/file-dialog.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Select,
TreeNodeType,
} from '@opensumi/ide-components';
import { isMacintosh, localize, path, useInjectable } from '@opensumi/ide-core-browser';
import { URI, isMacintosh, localize, path, useInjectable } from '@opensumi/ide-core-browser';
import { Progress } from '@opensumi/ide-core-browser/lib/progress/progress-bar';
import { IDialogService, IOpenDialogOptions, ISaveDialogOptions } from '@opensumi/ide-overlay';

Expand Down Expand Up @@ -153,7 +153,19 @@ export const FileDialog = ({ options, model, isOpenDialog }: React.PropsWithChil
}
} else {
if ((options as IOpenDialogOptions).canSelectFiles && type === TreeNodeType.TreeNode) {
handleItemClick(item, type);
const filterExts = new Set(
Object.values((options as IOpenDialogOptions).filters ?? {})
.flat()
.map((item) => `.${item}`),
);
if (filterExts.size > 0) {
const ext = URI.parse(item.filestat.uri).path.ext;
if (filterExts.has(ext)) {
handleItemClick(item, type);
}
} else {
handleItemClick(item, type);
}
} else if ((options as IOpenDialogOptions).canSelectFolders && type === TreeNodeType.CompositeTreeNode) {
handleItemClick(item, type);
}
Expand Down
145 changes: 107 additions & 38 deletions packages/file-tree-next/src/browser/file-tree-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import { EXPLORER_CONTAINER_ID } from '@opensumi/ide-explorer/lib/browser/explor
import { IMainLayoutService, IViewsRegistry, MainLayoutContribution } from '@opensumi/ide-main-layout';
import { ViewContentGroups } from '@opensumi/ide-main-layout/lib/browser/views-registry';
import { IOpenDialogOptions, ISaveDialogOptions, IWindowDialogService } from '@opensumi/ide-overlay';
import { DEFAULT_WORKSPACE_SUFFIX_NAME, IWorkspaceService, UNTITLED_WORKSPACE } from '@opensumi/ide-workspace';
import { IWorkspaceService, UNTITLED_WORKSPACE } from '@opensumi/ide-workspace';

import { IFileTreeService, PasteTypes, RESOURCE_VIEW_ID } from '../common';
import { Directory } from '../common/file-tree-node.define';
Expand Down Expand Up @@ -129,10 +129,6 @@ export class FileTreeContribution
private deleteThrottler: Throttler = new Throttler();
private willDeleteUris: URI[] = [];

get workspaceSuffixName() {
return this.appConfig.workspaceSuffixName || DEFAULT_WORKSPACE_SUFFIX_NAME;
}

initialize() {
// 等待排除配置初始化结束后再初始化文件树
this.workspaceService.initFileServiceExclude().then(async () => {
Expand Down Expand Up @@ -198,7 +194,7 @@ export class FileTreeContribution
if (workspace) {
const uri = new URI(workspace.uri);
resourceTitle = uri.displayName;
if (!workspace.isDirectory && resourceTitle.endsWith(`.${this.workspaceSuffixName}`)) {
if (!workspace.isDirectory && resourceTitle.endsWith(`.${this.workspaceService.workspaceSuffixName}`)) {
resourceTitle = resourceTitle.slice(0, resourceTitle.lastIndexOf('.'));
if (resourceTitle === UNTITLED_WORKSPACE) {
return localize('file.workspace.defaultTip');
Expand Down Expand Up @@ -246,6 +242,25 @@ export class FileTreeContribution
group: '0_new',
});

menuRegistry.registerMenuItem(MenuId.ExplorerContext, {
command: {
id: WORKSPACE_COMMANDS.ADD_WORKSPACE_FOLDER.id,
label: localize('workspace.addFolderToWorkspace'),
},
order: 1,
group: '0_workspace',
when: 'config.workspace.supportMultiRootWorkspace',
});
menuRegistry.registerMenuItem(MenuId.ExplorerContext, {
command: {
id: WORKSPACE_COMMANDS.REMOVE_WORKSPACE_FOLDER.id,
label: localize('workspace.removeFolderFromWorkspace'),
},
order: 1,
group: '0_workspace',
when: 'config.workspace.supportMultiRootWorkspace',
});

menuRegistry.registerMenuItem(MenuId.ExplorerContext, {
command: {
id: FILE_COMMANDS.OPEN_RESOURCES.id,
Expand Down Expand Up @@ -872,29 +887,33 @@ export class FileTreeContribution
this.appConfig.isElectronRenderer,
});

if (this.appConfig.isElectronRenderer) {
commands.registerCommand(FILE_COMMANDS.VSCODE_OPEN_FOLDER, {
execute: (uri?: URI, arg?: boolean | { forceNewWindow?: boolean }) => {
const windowService: IWindowService = this.injector.get(IWindowService);
const options = { newWindow: true };
if (typeof arg === 'boolean') {
options.newWindow = arg;
} else {
options.newWindow = typeof arg?.forceNewWindow === 'boolean' ? arg.forceNewWindow : true;
}
commands.registerCommand(FILE_COMMANDS.VSCODE_OPEN_FOLDER, {
execute: (uri?: URI, arg?: boolean | { forceNewWindow?: boolean }) => {
const windowService: IWindowService = this.injector.get(IWindowService);
const options = { newWindow: true };
if (typeof arg === 'boolean') {
options.newWindow = arg;
} else {
options.newWindow = typeof arg?.forceNewWindow === 'boolean' ? arg.forceNewWindow : true;
}

if (uri) {
return windowService.openWorkspace(uri, options);
}
if (uri) {
return windowService.openWorkspace(uri, options);
}

return this.commandService.executeCommand(FILE_COMMANDS.OPEN_FOLDER.id, options);
},
});
return this.commandService.executeCommand(FILE_COMMANDS.OPEN_FOLDER.id, options);
},
isVisible: () => {
const supportsOpenWorkspace = this.preferenceService.get<boolean>('application.supportsOpenFolder');
return supportsOpenWorkspace ?? false;
},
});

commands.registerCommand(FILE_COMMANDS.OPEN_FOLDER, {
execute: (options: { newWindow: boolean }) => {
commands.registerCommand(FILE_COMMANDS.OPEN_FOLDER, {
execute: (options: { newWindow: boolean }) => {
const windowService: IWindowService = this.injector.get(IWindowService);
if (this.appConfig.isElectronRenderer) {
const dialogService: IElectronNativeDialogService = this.injector.get(IElectronNativeDialogService);
const windowService: IWindowService = this.injector.get(IWindowService);
dialogService
.showOpenDialog({
title: localize('workspace.openDirectory'),
Expand All @@ -905,25 +924,44 @@ export class FileTreeContribution
windowService.openWorkspace(URI.file(paths[0]), options || { newWindow: true });
}
});
},
});
} else {
const dialogService: IWindowDialogService = this.injector.get(IWindowDialogService);
dialogService
.showOpenDialog({
title: localize('workspace.openDirectory'),
canSelectFiles: false,
canSelectFolders: true,
})
.then((uris) => {
if (uris && uris.length > 0) {
windowService.openWorkspace(uris[0], options || { newWindow: true });
}
});
}
},
isVisible: () => {
const supportsOpenWorkspace = this.preferenceService.get<boolean>('application.supportsOpenFolder');
return supportsOpenWorkspace ?? false;
},
});

commands.registerCommand(FILE_COMMANDS.OPEN_WORKSPACE, {
execute: (options: { newWindow: boolean }) => {
const supportsOpenWorkspace = this.preferenceService.get('application.supportsOpenWorkspace');
if (!supportsOpenWorkspace) {
return;
}
commands.registerCommand(FILE_COMMANDS.OPEN_WORKSPACE, {
execute: (options?: { newWindow: boolean }) => {
const supportsOpenWorkspace = this.preferenceService.get('application.supportsOpenWorkspace');
if (!supportsOpenWorkspace) {
return;
}
const windowService: IWindowService = this.injector.get(IWindowService);
if (this.appConfig.isElectronRenderer) {
const dialogService: IElectronNativeDialogService = this.injector.get(IElectronNativeDialogService);
const windowService: IWindowService = this.injector.get(IWindowService);
dialogService
.showOpenDialog({
title: localize('workspace.openWorkspace'),
properties: ['openFile'],
filters: [
{
name: localize('workspace.openWorkspaceTitle'),
extensions: [this.workspaceSuffixName],
extensions: [this.workspaceService.workspaceSuffixName],
},
],
})
Expand All @@ -932,9 +970,31 @@ export class FileTreeContribution
windowService.openWorkspace(URI.file(paths[0]), options || { newWindow: true });
}
});
},
});
}
} else {
const dialogService: IWindowDialogService = this.injector.get(IWindowDialogService);
dialogService
.showOpenDialog({
title: localize('workspace.openWorkspace'),
canSelectFiles: true,
canSelectFolders: false,
canSelectMany: false,
filters: {
workspace: [this.workspaceService.workspaceSuffixName],
},
})
.then((uris) => {
if (uris && uris.length > 0) {
const workspaceService: IWorkspaceService = this.injector.get(IWorkspaceService);
workspaceService.open(uris[0], { preserveWindow: options?.newWindow ?? false });
}
});
}
},
isVisible: () => {
const supportsOpenWorkspace = this.preferenceService.get<boolean>('application.supportsOpenWorkspace');
return supportsOpenWorkspace ?? false;
},
});

commands.registerCommand(FILE_COMMANDS.REVEAL_IN_EXPLORER, {
execute: (uriOrResource?: URI | { uri?: URI }) => {
Expand Down Expand Up @@ -1178,6 +1238,15 @@ export class FileTreeContribution
viewId: RESOURCE_VIEW_ID,
order: 5,
});
registry.registerItem({
id: WORKSPACE_COMMANDS.ADD_WORKSPACE_FOLDER.id,
command: WORKSPACE_COMMANDS.ADD_WORKSPACE_FOLDER.id,
label: localize('workspace.addFolderToWorkspace'),
viewId: RESOURCE_VIEW_ID,
order: 0,
group: 'file_explore_workspace',
when: 'config.workspace.supportMultiRootWorkspace',
});
}

private doDelete() {
Expand Down
5 changes: 3 additions & 2 deletions packages/i18n/src/common/en-US.lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const localizationBundle = {
'file.action.new.folder': 'New Folder',
'file.action.refresh': 'Refresh',
'file.open.folder': 'Open Folder',
'file.open.workspace': 'Open Workspace',
'file.open.workspace': 'Open Workspace from File ...',
'file.action.collapse': 'Collapse',
'file.confirm.delete': 'Are you sure you want to delete the following files?\n{0}',
'file.confirm.delete.ok': 'Move to trash',
Expand Down Expand Up @@ -1062,6 +1062,7 @@ export const localizationBundle = {
'terminal.killProcess': 'Kill Process',
'terminal.process.unHealthy':
'*This terminal session has been timed out and killed by the system. Please open a new terminal session to proceed with operations.',
'terminal.selectCWDForNewTerminal': 'Select current working directory for new terminal',

'terminal.focusNext.inTerminalGroup': 'Terminal: Focus Next Terminal in Terminal Group',
'terminal.focusPrevious.inTerminalGroup': 'Terminal: Focus Previous Terminal in Terminal Group',
Expand Down Expand Up @@ -1215,7 +1216,7 @@ export const localizationBundle = {
'editor.compareAndSave.title': '{0} (on Disk) <=> {1} (Editing) ',

'workspace.openDirectory': 'Open Directory',
'workspace.addFolderToWorkspace': 'Add Folder Into Workspace ...',
'workspace.addFolderToWorkspace': 'Add Folder to Workspace ...',
'workspace.removeFolderFromWorkspace': 'Remove Folder From Workspace',
'workspace.saveWorkspaceAsFile': 'Save Workspace As ...',
'workspace.openWorkspace': 'Open Workspace',
Expand Down
3 changes: 2 additions & 1 deletion packages/i18n/src/common/zh-CN.lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const localizationBundle = {
'file.action.collapse': '全部折叠',
'file.location': '在文件树中定位',
'file.open.folder': '打开文件夹',
'file.open.workspace': '打开工作区',
'file.open.workspace': '从文件打开工作区',
'file.confirm.delete': '确定删除下面列的文件?\n{0}',
'file.confirm.delete.ok': '移入回收站',
'file.confirm.delete.cancel': '取消',
Expand Down Expand Up @@ -719,6 +719,7 @@ export const localizationBundle = {
'terminal.toggleTerminal': '切换终端面板',
'terminal.killProcess': '结束进程',
'terminal.process.unHealthy': '*此终端会话已被系统超时回收,请打开新的终端会话来进行操作',
'terminal.selectCWDForNewTerminal': '为新 terminal 选择当前工作路径',

'view.command.show': '打开 {0}',

Expand Down
2 changes: 1 addition & 1 deletion packages/overlay/src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export interface IDialogOptions {
title?: string;
defaultUri?: URI;
filters?: {
[name: string]: string;
[name: string]: string[];
};
}

Expand Down
7 changes: 5 additions & 2 deletions packages/terminal-next/src/browser/terminal.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,10 @@ export class TerminalClient extends Disposable implements ITerminalClient {
if (this.workspace.isMultiRootWorkspaceOpened) {
// 工作区模式下每次新建终端都需要用户手动进行一次路径选择
const roots = this.workspace.tryGetRoots();
const choose = await this.quickPick.show(roots.map((file) => new URI(file.uri).codeUri.fsPath));
const choose = await this.quickPick.show(
roots.map((file) => new URI(file.uri).codeUri.fsPath),
{ placeholder: localize('terminal.selectCWDForNewTerminal') },
);
return choose;
} else if (this.workspace.workspace) {
return new URI(this.workspace.workspace?.uri).codeUri.fsPath;
Expand All @@ -530,7 +533,7 @@ export class TerminalClient extends Disposable implements ITerminalClient {
const widget = this._widget;
if (TerminalClient.WORKSPACE_PATH_CACHED.has(widget.group.id)) {
this._workspacePath = TerminalClient.WORKSPACE_PATH_CACHED.get(widget.group.id)!;
} else {
} else if (!widget.recovery) {
const choose = await this._pickWorkspace();
if (choose) {
this._workspacePath = choose;
Expand Down
2 changes: 2 additions & 0 deletions packages/terminal-next/src/browser/terminal.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ export class TerminalController extends WithEventBus implements ITerminalControl
group,
typeof session === 'string' ? session : session.client,
!!session.task,
false,
true,
);
const client = await this.clientFactory(widget);

Expand Down
Loading

0 comments on commit d26c0ef

Please sign in to comment.