mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 09:35:40 -05:00
Load all data workspace projects directly from workspace (#15921)
* Load all projects directly from workspace * fixes * Remove relativity and fix tests * fix compile * PR comments * remove unused * distro
This commit is contained in:
@@ -12,62 +12,13 @@ import * as glob from 'fast-glob';
|
||||
import { IWorkspaceService } from '../common/interfaces';
|
||||
import { ProjectProviderRegistry } from '../common/projectProviderRegistry';
|
||||
import Logger from '../common/logger';
|
||||
import { TelemetryReporter, TelemetryViews, calculateRelativity, TelemetryActions } from '../common/telemetry';
|
||||
import { getAzdataApi, isCurrentWorkspaceUntitled } from '../common/utils';
|
||||
|
||||
const WorkspaceConfigurationName = 'dataworkspace';
|
||||
const ProjectsConfigurationName = 'projects';
|
||||
const TempProject = 'tempProject';
|
||||
import { TelemetryReporter, TelemetryViews, TelemetryActions } from '../common/telemetry';
|
||||
|
||||
export class WorkspaceService implements IWorkspaceService {
|
||||
private _onDidWorkspaceProjectsChange: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
|
||||
readonly onDidWorkspaceProjectsChange: vscode.Event<void> = this._onDidWorkspaceProjectsChange?.event;
|
||||
|
||||
constructor(private _context: vscode.ExtensionContext) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load any temp project that needed to be loaded before ADS was restarted
|
||||
* which would happen if a workspace was created in order open or create a project
|
||||
*/
|
||||
async loadTempProjects(): Promise<void> {
|
||||
const tempProjects: string[] | undefined = this._context.globalState.get(TempProject) ?? undefined;
|
||||
|
||||
if (tempProjects && vscode.workspace.workspaceFile) {
|
||||
// add project to workspace now that the workspace has been created and saved
|
||||
for (let project of tempProjects) {
|
||||
await this.addProjectsToWorkspace([vscode.Uri.file(<string>project)]);
|
||||
}
|
||||
await this._context.globalState.update(TempProject, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new workspace in the same folder as the project. Because ADS gets restarted when
|
||||
* a new workspace is created and opened, the project needs to be saved as the temp project that will be loaded
|
||||
* when the extension gets restarted
|
||||
* @param projectFileFsPath project to add to the workspace
|
||||
*/
|
||||
async CreateNewWorkspaceForProject(projectFileFsPath: string, workspaceFile: vscode.Uri | undefined): Promise<void> {
|
||||
// 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 azdataApi.workspace.saveAndEnterWorkspace(workspaceFile!);
|
||||
} else {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
constructor() { }
|
||||
|
||||
get isProjectProviderAvailable(): boolean {
|
||||
for (const extension of vscode.extensions.all) {
|
||||
@@ -83,8 +34,8 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
* Verify that a workspace is open or that if one isn't, it's ok to create a workspace and restart ADS
|
||||
*/
|
||||
async validateWorkspace(): Promise<boolean> {
|
||||
if (!vscode.workspace.workspaceFile || isCurrentWorkspaceUntitled()) {
|
||||
const result = await vscode.window.showWarningMessage(constants.CreateWorkspaceConfirmation, { modal: true }, constants.OkButtonText);
|
||||
if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0) {
|
||||
const result = await vscode.window.showWarningMessage(constants.RestartConfirmation, { modal: true }, constants.OkButtonText);
|
||||
if (result === constants.OkButtonText) {
|
||||
return true;
|
||||
} else {
|
||||
@@ -96,33 +47,12 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows confirmation message that the ADS will be restarted and current workspace/file will be closed. If confirmed, the specified workspace will be entered.
|
||||
* @param workspaceFile
|
||||
*/
|
||||
async enterWorkspace(workspaceFile: vscode.Uri): Promise<void> {
|
||||
const result = await vscode.window.showWarningMessage(constants.EnterWorkspaceConfirmation, { modal: true }, constants.OkButtonText);
|
||||
if (result === constants.OkButtonText) {
|
||||
await getAzdataApi()?.workspace.enterWorkspace(workspaceFile);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async addProjectsToWorkspace(projectFiles: vscode.Uri[], workspaceFilePath?: vscode.Uri): Promise<void> {
|
||||
async addProjectsToWorkspace(projectFiles: vscode.Uri[]): Promise<void> {
|
||||
if (!projectFiles || projectFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// a workspace needs to be open to add projects
|
||||
if (!vscode.workspace.workspaceFile || isCurrentWorkspaceUntitled()) {
|
||||
await this.CreateNewWorkspaceForProject(projectFiles[0].fsPath, workspaceFilePath);
|
||||
|
||||
// this won't get hit since ADS will get restarted, but helps with testing
|
||||
return;
|
||||
}
|
||||
|
||||
const currentProjects: vscode.Uri[] = this.getProjectsInWorkspace();
|
||||
const currentProjects: vscode.Uri[] = await this.getProjectsInWorkspace();
|
||||
const newWorkspaceFolders: string[] = [];
|
||||
let newProjectFileAdded = false;
|
||||
for (const projectFile of projectFiles) {
|
||||
@@ -132,7 +62,6 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.WorkspaceTreePane, TelemetryActions.ProjectAddedToWorkspace)
|
||||
.withAdditionalProperties({
|
||||
workspaceProjectRelativity: calculateRelativity(projectFile.fsPath),
|
||||
projectType: path.extname(projectFile.fsPath)
|
||||
}).send();
|
||||
|
||||
@@ -148,14 +77,12 @@ 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) {
|
||||
// second parameter is null means don't remove any workspace folders
|
||||
vscode.workspace.updateWorkspaceFolders(vscode.workspace.workspaceFolders!.length, null, ...(newWorkspaceFolders.map(folder => ({ uri: vscode.Uri.file(folder) }))));
|
||||
// Add to the end of the workspace folders to avoid a restart of the extension host if we can
|
||||
vscode.workspace.updateWorkspaceFolders(vscode.workspace.workspaceFolders?.length || 0, undefined, ...(newWorkspaceFolders.map(folder => ({ uri: vscode.Uri.file(folder) }))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,8 +95,12 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
return projectTypes;
|
||||
}
|
||||
|
||||
getProjectsInWorkspace(ext?: string): vscode.Uri[] {
|
||||
let projects = vscode.workspace.workspaceFile ? this.getWorkspaceConfigurationValue<string[]>(ProjectsConfigurationName).map(project => this.toUri(project)) : [];
|
||||
async getProjectsInWorkspace(ext?: string): Promise<vscode.Uri[]> {
|
||||
const projectPromises = vscode.workspace.workspaceFolders?.map(f => this.getAllProjectsInFolder(f.uri));
|
||||
if (!projectPromises) {
|
||||
return [];
|
||||
}
|
||||
let projects = (await Promise.all(projectPromises)).reduce((prev, curr) => prev.concat(curr), []);
|
||||
|
||||
// filter by specified extension
|
||||
if (ext) {
|
||||
@@ -179,57 +110,12 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
return projects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for projects that are in the workspace folders but have not been added to the workspace through the dialog or by editing the .code-workspace file
|
||||
*/
|
||||
async checkForProjectsNotAddedToWorkspace(): Promise<void> {
|
||||
const config = vscode.workspace.getConfiguration(constants.projectsConfigurationKey);
|
||||
|
||||
// only check if the user hasn't selected not to show this prompt again
|
||||
if (!config[constants.showNotAddedProjectsMessageKey]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// look for any projects that haven't been added to the workspace
|
||||
const projectsInWorkspace = this.getProjectsInWorkspace();
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||
|
||||
if (!workspaceFolders) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const folder of workspaceFolders) {
|
||||
const results = await this.getAllProjectsInFolder(folder.uri);
|
||||
|
||||
let containsNotAddedProject = false;
|
||||
for (const projFile of results) {
|
||||
// if any of the found projects aren't already in the workspace's projects, we can stop checking and show the info message
|
||||
if (!projectsInWorkspace.find(p => p.fsPath === projFile)) {
|
||||
containsNotAddedProject = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (containsNotAddedProject) {
|
||||
const result = await vscode.window.showInformationMessage(constants.WorkspaceContainsNotAddedProjects, constants.LaunchOpenExisitingDialog, constants.DoNotAskAgain);
|
||||
if (result === constants.LaunchOpenExisitingDialog) {
|
||||
// open settings
|
||||
await vscode.commands.executeCommand('projects.openExisting');
|
||||
} else if (result === constants.DoNotAskAgain) {
|
||||
await config.update(constants.showNotAddedProjectsMessageKey, false, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all the supported projects in the folder
|
||||
* @param folder folder to look look for projects
|
||||
* @returns array of file paths of supported projects
|
||||
* @returns array of file URIs for supported projects
|
||||
*/
|
||||
async getAllProjectsInFolder(folder: vscode.Uri): Promise<string[]> {
|
||||
async getAllProjectsInFolder(folder: vscode.Uri): Promise<vscode.Uri[]> {
|
||||
// get the unique supported project extensions
|
||||
const supportedProjectExtensions = [...new Set((await this.getAllProjectTypes()).map(p => { return p.projectFileExtension; }))];
|
||||
|
||||
@@ -241,7 +127,7 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
const projFilter = supportedProjectExtensions.length > 1 ? path.posix.join(escapedPath, '**', `*.{${supportedProjectExtensions.toString()}}`) : path.posix.join(escapedPath, '**', `*.${supportedProjectExtensions[0]}`);
|
||||
|
||||
// glob will return an array of file paths with forward slashes, so they need to be converted back if on windows
|
||||
return (await glob(projFilter)).map(p => path.resolve(p));
|
||||
return (await glob(projFilter)).map(p => vscode.Uri.file(path.resolve(p)));
|
||||
}
|
||||
|
||||
async getProjectProvider(projectFile: vscode.Uri): Promise<dataworkspace.IProjectProvider | undefined> {
|
||||
@@ -253,29 +139,11 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
return ProjectProviderRegistry.getProviderByProjectExtension(projectType);
|
||||
}
|
||||
|
||||
async removeProject(projectFile: vscode.Uri): Promise<void> {
|
||||
if (vscode.workspace.workspaceFile) {
|
||||
const currentProjects: vscode.Uri[] = this.getProjectsInWorkspace();
|
||||
const projectIdx = currentProjects.findIndex((p: vscode.Uri) => p.fsPath === projectFile.fsPath);
|
||||
if (projectIdx !== -1) {
|
||||
currentProjects.splice(projectIdx, 1);
|
||||
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.WorkspaceTreePane, TelemetryActions.ProjectRemovedFromWorkspace)
|
||||
.withAdditionalProperties({
|
||||
projectType: path.extname(projectFile.fsPath)
|
||||
}).send();
|
||||
|
||||
await this.setWorkspaceConfigurationValue(ProjectsConfigurationName, currentProjects.map(project => this.toRelativePath(project)));
|
||||
this._onDidWorkspaceProjectsChange.fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async createProject(name: string, location: vscode.Uri, projectTypeId: string, workspaceFile?: vscode.Uri): Promise<vscode.Uri> {
|
||||
const provider = ProjectProviderRegistry.getProviderByProjectType(projectTypeId);
|
||||
if (provider) {
|
||||
const projectFile = await provider.createProject(name, location, projectTypeId);
|
||||
this.addProjectsToWorkspace([projectFile], workspaceFile);
|
||||
this.addProjectsToWorkspace([projectFile]);
|
||||
this._onDidWorkspaceProjectsChange.fire();
|
||||
return projectFile;
|
||||
} else {
|
||||
@@ -283,7 +151,7 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
}
|
||||
}
|
||||
|
||||
async gitCloneProject(url: string, localClonePath: string, workspaceFile: vscode.Uri): Promise<void> {
|
||||
async gitCloneProject(url: string, localClonePath: string): Promise<void> {
|
||||
const gitApi: git.API = (<git.GitExtension>vscode.extensions.getExtension('vscode.git')!.exports).getAPI(1);
|
||||
const opts = {
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
@@ -300,8 +168,8 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
);
|
||||
|
||||
// get all the project files in the cloned repo and add them to workspace
|
||||
const repoProjects = (await this.getAllProjectsInFolder(vscode.Uri.file(repositoryPath))).map(p => { return vscode.Uri.file(p); });
|
||||
this.addProjectsToWorkspace(repoProjects, workspaceFile);
|
||||
const repoProjects = (await this.getAllProjectsInFolder(vscode.Uri.file(repositoryPath)));
|
||||
this.addProjectsToWorkspace(repoProjects);
|
||||
} catch (e) {
|
||||
vscode.window.showErrorMessage(constants.gitCloneError);
|
||||
console.error(e);
|
||||
@@ -344,29 +212,4 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
ProjectProviderRegistry.registerProvider(extension.exports, extension.id);
|
||||
}
|
||||
}
|
||||
|
||||
getWorkspaceConfigurationValue<T>(configurationName: string): T {
|
||||
return vscode.workspace.getConfiguration(WorkspaceConfigurationName).get(configurationName) as T;
|
||||
}
|
||||
|
||||
async setWorkspaceConfigurationValue(configurationName: string, value: any): Promise<void> {
|
||||
await vscode.workspace.getConfiguration(WorkspaceConfigurationName).update(configurationName, value, vscode.ConfigurationTarget.Workspace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative path to the workspace file
|
||||
* @param filePath the absolute path
|
||||
*/
|
||||
private toRelativePath(filePath: vscode.Uri): string {
|
||||
return path.relative(path.dirname(vscode.workspace.workspaceFile!.path!), filePath.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Uri of the given relative path
|
||||
* @param relativePath the relative path
|
||||
*/
|
||||
private toUri(relativePath: string): vscode.Uri {
|
||||
const fullPath = path.join(path.dirname(vscode.workspace.workspaceFile!.path!), relativePath);
|
||||
return vscode.Uri.file(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user