Add VS Code native New Project create flow (#15906)

* Add VS Code native New Project create flow

* Update project name title

* Ignore focus out

* comments

* ellipsis
This commit is contained in:
Charles Gagnon
2021-06-25 10:46:40 -07:00
committed by GitHub
parent 1caef2dc6e
commit 33ff661c6f
4 changed files with 116 additions and 13 deletions

View File

@@ -15,7 +15,7 @@ export const AllProjectTypes = localize('AllProjectTypes', "All Project Types");
export const ProviderNotFoundForProjectTypeError = (projectType: string): string => { return localize('UnknownProjectTypeError', "No provider was found for project type with id: '{0}'", projectType); };
export const WorkspaceRequiredMessage = localize('dataworkspace.workspaceRequiredMessage', "A workspace is required in order to use the project feature.");
export const OpenWorkspace = localize('dataworkspace.openWorkspace', "Open Workspace…");
export const CreateWorkspaceConfirmation = localize('dataworkspace.createWorkspaceConfirmation', "A workspace will be created and opened in order to open project. Azure Data Studio will restart and if there is a folder currently open, it will be closed.");
export const CreateWorkspaceConfirmation = localize('dataworkspace.createWorkspaceConfirmation', "A workspace will be created and opened in order to open the project. Azure Data Studio will restart and if there is a folder currently open, it will be closed.");
export const EnterWorkspaceConfirmation = localize('dataworkspace.enterWorkspaceConfirmation', "To open this workspace, Azure Data Studio will restart. If there is a workspace or folder currently open, it will be closed.");
export const WorkspaceContainsNotAddedProjects = localize('dataworkspace.workspaceContainsNotAddedProjects', "The current workspace contains one or more projects that have not been added to the workspace. Use the 'Open existing' dialog to add projects to the projects pane.");
export const LaunchOpenExisitingDialog = localize('dataworkspace.launchOpenExistingDialog', "Launch 'Open Existing' Dialog");
@@ -35,8 +35,10 @@ export const showNotAddedProjectsMessageKey = 'showNotAddedProjectsInWorkspacePr
export const OkButtonText = localize('dataworkspace.ok', "OK");
export const CancelButtonText = localize('dataworkspace.cancel', "Cancel");
export const BrowseButtonText = localize('dataworkspace.browse', "Browse");
export const BrowseEllipsis = localize('dataworkspace.browseEllipsis', "Browse...");
export const OpenButtonText = localize('dataworkspace.open', "Open");
export const CreateButtonText = localize('dataworkspace.create', "Create");
export const Select = localize('dataworkspace.select', "Select");
export const WorkspaceFileExtension = '.code-workspace';
export const DefaultInputWidth = '400px';
export const DefaultButtonWidth = '80px';
@@ -46,6 +48,7 @@ export const NewProjectDialogTitle = localize('dataworkspace.NewProjectDialogTit
export const TypeTitle = localize('dataworkspace.Type', "Type");
export const ProjectNameTitle = localize('dataworkspace.projectNameTitle', "Name");
export const ProjectNamePlaceholder = localize('dataworkspace.projectNamePlaceholder', "Enter project name");
export const EnterProjectName = localize('dataworkspace.enterProjectName', "Enter Project Name");
export const ProjectLocationTitle = localize('dataworkspace.projectLocationTitle', "Location");
export const ProjectLocationPlaceholder = localize('dataworkspace.projectLocationPlaceholder', "Select location to create project");
export const AddProjectToCurrentWorkspace = localize('dataworkspace.AddProjectToCurrentWorkspace', "This project will be added to the current workspace.");
@@ -53,10 +56,13 @@ export const NewWorkspaceWillBeCreated = localize('dataworkspace.NewWorkspaceWil
export const WorkspaceLocationTitle = localize('dataworkspace.workspaceLocationTitle', "Workspace location");
export const ProjectParentDirectoryNotExistError = (location: string): string => { return localize('dataworkspace.projectParentDirectoryNotExistError', "The selected project location '{0}' does not exist or is not a directory.", location); };
export const ProjectDirectoryAlreadyExistError = (projectName: string, location: string): string => { return localize('dataworkspace.projectDirectoryAlreadyExistError', "There is already a directory named '{0}' in the selected location: '{1}'.", projectName, location); };
export const ProjectDirectoryAlreadyExistErrorShort = (projectName: string) => { return localize('dataworkspace.projectDirectoryAlreadyExistErrorShort', "Directory '{0}' already exists in the selected location, please choose another", projectName); };
export const WorkspaceFileInvalidError = (workspace: string): string => { return localize('dataworkspace.workspaceFileInvalidError', "The selected workspace file path '{0}' does not have the required file extension {1}.", workspace, WorkspaceFileExtension); };
export const WorkspaceParentDirectoryNotExistError = (location: string): string => { return localize('dataworkspace.workspaceParentDirectoryNotExistError', "The selected workspace location '{0}' does not exist or is not a directory.", location); };
export const WorkspaceFileAlreadyExistsError = (file: string): string => { return localize('dataworkspace.workspaceFileAlreadyExistsError', "The selected workspace file '{0}' already exists. To add the project to an existing workspace, use the Open Existing dialog to first open the workspace.", file); };
export const SelectProjectType = localize('dataworkspace.selectProjectType', "Select Project Type");
export const SelectProjectLocation = localize('dataworkspace.selectProjectLocation', "Select Project Location");
export const NameCannotBeEmpty = localize('dataworkspace.nameCannotBeEmpty', "Name cannot be empty");
//Open Existing Dialog
export const OpenExistingDialogTitle = localize('dataworkspace.openExistingDialogTitle', "Open existing");
export const FileNotExistError = (fileType: string, filePath: string): string => { return localize('dataworkspace.fileNotExistError', "The selected {0} file '{1}' does not exist or is not a file.", fileType, filePath); };

View File

@@ -0,0 +1,84 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as path from 'path';
import * as constants from '../common/constants';
import { directoryExist } from '../common/utils';
import { defaultProjectSaveLocation } from '../common/projectLocationHelper';
import { WorkspaceService } from '../services/workspaceService';
/**
* Create flow for a New Project using only VS Code-native APIs such as QuickPick
*/
export async function createNewProjectWithQuickpick(workspaceService: WorkspaceService): Promise<void> {
// Refresh list of project types
const projectTypes = (await workspaceService.getAllProjectTypes()).map(projType => {
return {
label: projType.displayName,
description: projType.description,
id: projType.id
} as vscode.QuickPickItem & { id: string };
});
// 1. Prompt for project type
const projectType = await vscode.window.showQuickPick(projectTypes, { title: constants.SelectProjectType, ignoreFocusOut: true });
if (!projectType) {
return;
}
// 2. Prompt for project name
const projectName = await vscode.window.showInputBox(
{
title: constants.EnterProjectName,
validateInput: (value) => {
return value ? undefined : constants.NameCannotBeEmpty;
},
ignoreFocusOut: true
});
if (!projectName) {
return;
}
// 3. Prompt for Project location
// Show quick pick with just browse option to give user context about what the file dialog is for (since that doesn't always have a title)
const browseProjectLocation = await vscode.window.showQuickPick(
[constants.BrowseEllipsis],
{ title: constants.SelectProjectLocation, ignoreFocusOut: true });
if (!browseProjectLocation) {
return;
}
// We validate that the folder doesn't already exist, and if it does keep prompting them to pick a new one
let valid = false;
let projectLocation = '';
while (!valid) {
const locations = await vscode.window.showOpenDialog({
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
openLabel: constants.Select,
title: constants.SelectProjectLocation,
defaultUri: defaultProjectSaveLocation()
});
if (!locations) {
return;
}
projectLocation = locations[0].fsPath;
const exists = await directoryExist(path.join(projectLocation, projectName));
if (exists) {
// Show the browse quick pick again with the title updated with the error
const browseProjectLocation = await vscode.window.showQuickPick(
[constants.BrowseEllipsis],
{ title: constants.ProjectDirectoryAlreadyExistErrorShort(projectName), ignoreFocusOut: true });
if (!browseProjectLocation) {
return;
}
} else {
valid = true;
}
}
await workspaceService.createProject(projectName, vscode.Uri.file(projectLocation), projectType.id, undefined);
}

View File

@@ -13,11 +13,13 @@ import { OpenExistingDialog } from './dialogs/openExistingDialog';
import { IWorkspaceService } from './common/interfaces';
import { IconPathHelper } from './common/iconHelper';
import { ProjectDashboard } from './dialogs/projectDashboard';
import { getAzdataApi } from './common/utils';
import { createNewProjectWithQuickpick } from './dialogs/newProjectQuickpick';
export async function activate(context: vscode.ExtensionContext): Promise<IExtension> {
const workspaceService = new WorkspaceService(context);
await workspaceService.loadTempProjects();
await workspaceService.checkForProjectsNotAddedToWorkspace();
workspaceService.checkForProjectsNotAddedToWorkspace();
context.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders(async () => {
await workspaceService.checkForProjectsNotAddedToWorkspace();
}));
@@ -31,8 +33,13 @@ export async function activate(context: vscode.ExtensionContext): Promise<IExten
setProjectProviderContextValue(workspaceService);
context.subscriptions.push(vscode.commands.registerCommand('projects.new', async () => {
if (getAzdataApi()) {
const dialog = new NewProjectDialog(workspaceService);
await dialog.open();
} else {
await createNewProjectWithQuickpick(workspaceService);
}
}));
context.subscriptions.push(vscode.commands.registerCommand('projects.openExisting', async () => {

View File

@@ -49,17 +49,23 @@ export class WorkspaceService implements IWorkspaceService {
* @param projectFileFsPath project to add to the workspace
*/
async CreateNewWorkspaceForProject(projectFileFsPath: string, workspaceFile: vscode.Uri | undefined): Promise<void> {
// save temp project
await this._context.globalState.update(TempProject, [projectFileFsPath]);
// create workspace
const projectFolder = vscode.Uri.file(path.dirname(projectFileFsPath));
const azdataApi = getAzdataApi();
if (azdataApi) {
// save temp project
await this._context.globalState.update(TempProject, [projectFileFsPath]);
if (isCurrentWorkspaceUntitled()) {
vscode.workspace.updateWorkspaceFolders(vscode.workspace.workspaceFolders!.length, null, { uri: projectFolder });
await getAzdataApi()?.workspace.saveAndEnterWorkspace(workspaceFile!);
await azdataApi.workspace.saveAndEnterWorkspace(workspaceFile!);
} else {
await getAzdataApi()?.workspace.createAndEnterWorkspace(projectFolder, workspaceFile);
await azdataApi.workspace.createAndEnterWorkspace(projectFolder, workspaceFile);
}
} else {
// In VS Code we don't have access to the workspace APIs exposed by ADS and so can't actually create a new saved workspace.
// Instead we'll just always call this, which will either add it to the existing untitled workspace or create a new
// untitled workspace which the user can then save later on as they wish.
vscode.workspace.updateWorkspaceFolders(vscode.workspace.workspaceFolders?.length || 0, null, { uri: projectFolder });
}
}