remove project feature (#12297)

* remove project feature

* update test
This commit is contained in:
Alan Ren
2020-09-15 11:12:30 -07:00
committed by GitHub
parent 725e1b2ee3
commit 908a15d6a8
12 changed files with 104 additions and 4 deletions

View File

@@ -46,6 +46,10 @@
"title": "%refresh-workspace-command%",
"category": "",
"icon": "$(refresh)"
},
{
"command": "projects.removeProject",
"title": "%remove-project-command%"
}
],
"menus": {
@@ -69,6 +73,17 @@
{
"command": "dataworkspace.refresh",
"when": "false"
},
{
"command": "projects.removeProject",
"when": "false"
}
],
"view/item/context": [
{
"command": "projects.removeProject",
"when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.project",
"group": "9_dbProjectsLast@9"
}
]
},

View File

@@ -4,5 +4,6 @@
"data-workspace-view-container-name": "Projects",
"main-view-name": "Projects",
"add-project-command": "Add Project",
"refresh-workspace-command": "Refresh"
"refresh-workspace-command": "Refresh",
"remove-project-command":"Remove Project"
}

View File

@@ -58,6 +58,17 @@ export interface IWorkspaceService {
* @param projectFiles the list of project files to be added, the project file should be absolute path.
*/
addProjectsToWorkspace(projectFiles: vscode.Uri[]): Promise<void>;
/**
* Remove the project from workspace
* @param projectFile The project file to be removed
*/
removeProject(projectFile: vscode.Uri): Promise<void>;
/**
* Event fires when projects in workspace changes
*/
readonly onDidWorkspaceProjectsChange: vscode.Event<void>;
}
/**

View File

@@ -11,7 +11,11 @@ import { UnknownProjectsErrorMessage } from './constants';
* Tree data provider for the workspace main view
*/
export class WorkspaceTreeDataProvider implements vscode.TreeDataProvider<WorkspaceTreeItem>{
constructor(private _workspaceService: IWorkspaceService) { }
constructor(private _workspaceService: IWorkspaceService) {
this._workspaceService.onDidWorkspaceProjectsChange(() => {
this.refresh();
});
}
private _onDidChangeTreeData: vscode.EventEmitter<void | WorkspaceTreeItem | null | undefined> | undefined = new vscode.EventEmitter<WorkspaceTreeItem | undefined | void>();
readonly onDidChangeTreeData?: vscode.Event<void | WorkspaceTreeItem | null | undefined> | undefined = this._onDidChangeTreeData?.event;

View File

@@ -31,6 +31,12 @@ declare module 'dataworkspace' {
*/
getProjectTreeDataProvider(projectFile: vscode.Uri): Promise<vscode.TreeDataProvider<any>>;
/**
* Notify the project provider extension that the specified project file has been removed from the data workspace
* @param projectFile The Uri of the project file
*/
RemoveProject(projectFile: vscode.Uri): Promise<void>;
/**
* Gets the supported project types
*/

View File

@@ -10,6 +10,7 @@ import { WorkspaceTreeDataProvider } from './common/workspaceTreeDataProvider';
import { WorkspaceService } from './services/workspaceService';
import { DataWorkspaceExtension } from './dataWorkspaceExtension';
import { SelectProjectFileActionName } from './common/constants';
import { WorkspaceTreeItem } from './common/interfaces';
export async function activate(context: vscode.ExtensionContext): Promise<dataworkspace.IExtension> {
const workspaceService = new WorkspaceService();
@@ -36,7 +37,6 @@ export async function activate(context: vscode.ExtensionContext): Promise<datawo
return;
}
await workspaceService.addProjectsToWorkspace(fileUris);
workspaceTreeDataProvider.refresh();
}
}));
@@ -44,6 +44,10 @@ export async function activate(context: vscode.ExtensionContext): Promise<datawo
workspaceTreeDataProvider.refresh();
}));
context.subscriptions.push(vscode.commands.registerCommand('projects.removeProject', async (treeItem: WorkspaceTreeItem) => {
await workspaceService.removeProject(vscode.Uri.file(treeItem.element.project.projectFilePath));
}));
return new DataWorkspaceExtension();
}

View File

@@ -15,6 +15,9 @@ const WorkspaceConfigurationName = 'dataworkspace';
const ProjectsConfigurationName = 'projects';
export class WorkspaceService implements IWorkspaceService {
private _onDidWorkspaceProjectsChange: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
readonly onDidWorkspaceProjectsChange: vscode.Event<void> = this._onDidWorkspaceProjectsChange?.event;
async addProjectsToWorkspace(projectFiles: vscode.Uri[]): Promise<void> {
if (vscode.workspace.workspaceFile) {
const currentProjects: vscode.Uri[] = await this.getProjectsInWorkspace();
@@ -37,6 +40,7 @@ export class WorkspaceService implements IWorkspaceService {
if (newProjectFileAdded) {
// Save the new set of projects to the workspace configuration.
await this.setWorkspaceConfigurationValue(ProjectsConfigurationName, currentProjects.map(project => this.toRelativePath(project)));
this._onDidWorkspaceProjectsChange.fire();
}
if (newWorkspaceFolders.length > 0) {
@@ -68,6 +72,18 @@ export class WorkspaceService implements IWorkspaceService {
return ProjectProviderRegistry.getProviderByProjectType(projectType);
}
async removeProject(projectFile: vscode.Uri): Promise<void> {
if (vscode.workspace.workspaceFile) {
const currentProjects: vscode.Uri[] = await this.getProjectsInWorkspace();
const projectIdx = currentProjects.findIndex((p: vscode.Uri) => p.fsPath === projectFile.fsPath);
if (projectIdx !== -1) {
currentProjects.splice(projectIdx, 1);
await this.setWorkspaceConfigurationValue(ProjectsConfigurationName, currentProjects.map(project => this.toRelativePath(project)));
this._onDidWorkspaceProjectsChange.fire();
}
}
}
/**
* Ensure the project provider extension for the specified project is loaded
* @param projectType The file extension of the project, if not specified, all project provider extensions will be loaded.

View File

@@ -23,6 +23,9 @@ export function createProjectProvider(projectTypes: IProjectType[]): IProjectPro
const treeDataProvider = new MockTreeDataProvider();
const projectProvider: IProjectProvider = {
supportedProjectTypes: projectTypes,
RemoveProject: (projectFile: vscode.Uri): Promise<void> => {
return Promise.resolve();
},
getProjectTreeDataProvider: (projectFile: vscode.Uri): Promise<vscode.TreeDataProvider<any>> => {
return Promise.resolve(treeDataProvider);
}

View File

@@ -186,6 +186,10 @@ suite('WorkspaceService Tests', function (): void {
stubWorkspaceFile(DefaultWorkspaceFilePath);
const updateConfigurationStub = sinon.stub();
const getConfigurationStub = sinon.stub().returns([processPath('folder1/proj2.sqlproj')]);
const onWorkspaceProjectsChangedStub = sinon.stub();
const onWorkspaceProjectsChangedDisposable = service.onDidWorkspaceProjectsChange(() => {
onWorkspaceProjectsChangedStub();
});
stubGetConfigurationValue(getConfigurationStub, updateConfigurationStub);
const asRelativeStub = sinon.stub(vscode.workspace, 'asRelativePath');
sinon.stub(vscode.workspace, 'workspaceFolders').value(['.']);
@@ -207,5 +211,28 @@ suite('WorkspaceService Tests', function (): void {
should.strictEqual(updateWorkspaceFoldersStub.calledWith(1, null, sinon.match((arg) => {
return arg.uri.path === '/test/other';
})), true, 'updateWorkspaceFolder parameters does not match expectation');
should.strictEqual(onWorkspaceProjectsChangedStub.calledOnce, true, 'the onDidWorkspaceProjectsChange event should have been fired');
onWorkspaceProjectsChangedDisposable.dispose();
});
test('test removeProject', async () => {
const processPath = (original: string): string => {
return original.replace(/\//g, path.sep);
};
stubWorkspaceFile(DefaultWorkspaceFilePath);
const updateConfigurationStub = sinon.stub();
const getConfigurationStub = sinon.stub().returns([processPath('folder1/proj2.sqlproj'), processPath('folder2/proj3.sqlproj')]);
const onWorkspaceProjectsChangedStub = sinon.stub();
const onWorkspaceProjectsChangedDisposable = service.onDidWorkspaceProjectsChange(() => {
onWorkspaceProjectsChangedStub();
});
stubGetConfigurationValue(getConfigurationStub, updateConfigurationStub);
await service.removeProject(vscode.Uri.file('/test/folder/folder1/proj2.sqlproj'));
should.strictEqual(updateConfigurationStub.calledWith('projects', sinon.match.array.deepEquals([
processPath('folder2/proj3.sqlproj')
]), vscode.ConfigurationTarget.Workspace), true, 'updateConfiguration parameters does not match expectation for remove project');
should.strictEqual(onWorkspaceProjectsChangedStub.calledOnce, true, 'the onDidWorkspaceProjectsChange event should have been fired');
onWorkspaceProjectsChangedDisposable.dispose();
});
});

View File

@@ -71,6 +71,9 @@ suite('workspaceTreeDataProvider Tests', function (): void {
icon: '',
displayName: 'sql project'
}],
RemoveProject: (projectFile: vscode.Uri): Promise<void> => {
return Promise.resolve();
},
getProjectTreeDataProvider: (projectFile: vscode.Uri): Promise<vscode.TreeDataProvider<any>> => {
return Promise.resolve(treeDataProvider);
}

View File

@@ -342,7 +342,7 @@
},
{
"command": "sqlDatabaseProjects.close",
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project",
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project",
"group": "9_dbProjectsLast@9"
}
],

View File

@@ -24,6 +24,16 @@ export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvide
return provider;
}
/**
* Callback method when a project has been removed from the workspace view
* @param projectFile The Uri of the project file
*/
RemoveProject(projectFile: vscode.Uri): Promise<void> {
// No resource release needed
console.log(`project file unloaded: ${projectFile.fsPath}`);
return Promise.resolve();
}
/**
* Gets the supported project types
*/