From fc17c92ee2a87d4f2f899bcd9df92b6022c32398 Mon Sep 17 00:00:00 2001 From: fernandoescolar Date: Sat, 23 Nov 2024 19:39:30 +0100 Subject: [PATCH] big refactor in solutions (to allow slnx #309) --- src/SolutionTreeItemCollection.ts | 4 +- ...Project.ts => DotNetAddExistingProject.ts} | 4 +- ...erence.ts => DotNetAddPackageReference.ts} | 6 +- ...erence.ts => DotNetAddProjectReference.ts} | 4 +- src/actions/{Build.ts => DotNetBuild.ts} | 4 +- src/actions/{Clean.ts => DotNetClean.ts} | 4 +- .../{CreateProject.ts => DotNetNewProject.ts} | 2 +- .../{CreateSolution.ts => DotNetNewSln.ts} | 2 +- src/actions/{Pack.ts => DotNetPack.ts} | 4 +- src/actions/{Publish.ts => DotNetPublish.ts} | 4 +- ...ject.ts => DotNetRemoveExistingProject.ts} | 4 +- ...nce.ts => DotNetRemovePackageReference.ts} | 4 +- ...nce.ts => DotNetRemoveProjectReference.ts} | 4 +- src/actions/{Restore.ts => DotNetRestore.ts} | 4 +- src/actions/{Run.ts => DotNetRun.ts} | 4 +- src/actions/{Test.ts => DotNetTest.ts} | 4 +- src/actions/{Watch.ts => DotNetWatch.ts} | 4 +- src/actions/MoveProject.ts | 12 --- ...dSolutionFile.ts => SlnAddSolutionFile.ts} | 10 +- ...onFolder.ts => SlnCreateSolutionFolder.ts} | 20 ++-- ...lutionFile.ts => SlnDeleteSolutionFile.ts} | 10 +- ...onFolder.ts => SlnDeleteSolutionFolder.ts} | 28 +++--- src/actions/SlnMoveProject.ts | 12 +++ ...tionFolder.ts => SlnMoveSolutionFolder.ts} | 29 +++--- .../{RenameProject.ts => SlnRenameProject.ts} | 8 +- ...onFolder.ts => SlnRenameSolutionFolder.ts} | 6 +- src/actions/index.ts | 48 ++++----- .../AddExistingFileToSolutionFolderCommand.ts | 13 ++- src/commands/AddExistingProjectCommand.ts | 4 +- src/commands/AddNewProjectCommand.ts | 6 +- src/commands/AddPackageCommand.ts | 4 +- src/commands/AddProjectReferenceCommand.ts | 22 ++--- src/commands/BuildCommand.ts | 4 +- src/commands/CleanCommand.ts | 4 +- src/commands/CreateNewSolutionCommand.ts | 4 +- src/commands/CreateSolutionFolderCommand.ts | 8 +- src/commands/DeleteUnifiedCommand.ts | 25 ++--- src/commands/MoveToSolutionFolderCommand.ts | 33 +++---- src/commands/PackCommand.ts | 4 +- src/commands/PublishCommand.ts | 4 +- src/commands/RenameSolutionItemCommand.ts | 12 +-- src/commands/RestoreCommand.ts | 4 +- src/commands/RunCommand.ts | 4 +- src/commands/TestCommand.ts | 4 +- src/commands/UpdatePackageVersionCommand.ts | 4 +- src/commands/UpdatePackagesVersionCommand.ts | 4 +- src/commands/WatchRunCommand.ts | 4 +- src/core/Projects/ProjectFactory.ts | 63 ++++-------- src/core/Solutions/SlnLoader.ts | 61 ++++++++++++ src/core/Solutions/SolutionFactory.ts | 18 ++++ src/core/Solutions/index.ts | 8 +- src/core/Solutions/model.ts | 97 +++++++++++++++++++ .../ProjectConfigurationInSolution.ts | 0 .../Solutions/{ => sln}/ProjectInSolution.ts | 0 .../Solutions/{ => sln}/ProjectTypeIds.ts | 0 .../SolutionConfigurationInSolution.ts | 0 src/core/Solutions/{ => sln}/SolutionFile.ts | 0 .../{ => sln}/SolutionProjectType.ts | 0 src/core/Solutions/sln/index.ts | 6 ++ src/tree/TreeItem.ts | 6 +- src/tree/TreeItemContext.ts | 4 +- src/tree/TreeItemFactory.ts | 43 ++++---- .../MoveSolutionFolderInTheSameSolution.ts | 24 +++-- src/tree/items/ProjectTreeItem.ts | 10 +- src/tree/items/SolutionFileTreeItem.ts | 6 +- src/tree/items/SolutionFolderTreeItem.ts | 18 ++-- src/tree/items/SolutionTreeItem.ts | 4 +- src/tree/items/UnknownProjectTreeItem.ts | 6 +- src/tree/items/cps/CpsProjectTreeItem.ts | 6 +- .../items/standard/StandardProjectTreeItem.ts | 6 +- 70 files changed, 476 insertions(+), 330 deletions(-) rename src/actions/{AddExistingProject.ts => DotNetAddExistingProject.ts} (74%) rename src/actions/{AddPackageReference.ts => DotNetAddPackageReference.ts} (76%) rename src/actions/{AddProjectReference.ts => DotNetAddProjectReference.ts} (75%) rename src/actions/{Build.ts => DotNetBuild.ts} (72%) rename src/actions/{Clean.ts => DotNetClean.ts} (72%) rename src/actions/{CreateProject.ts => DotNetNewProject.ts} (88%) rename src/actions/{CreateSolution.ts => DotNetNewSln.ts} (86%) rename src/actions/{Pack.ts => DotNetPack.ts} (72%) rename src/actions/{Publish.ts => DotNetPublish.ts} (71%) rename src/actions/{RemoveExistingProject.ts => DotNetRemoveExistingProject.ts} (73%) rename src/actions/{RemovePackageReference.ts => DotNetRemovePackageReference.ts} (73%) rename src/actions/{RemoveProjectReference.ts => DotNetRemoveProjectReference.ts} (75%) rename src/actions/{Restore.ts => DotNetRestore.ts} (71%) rename src/actions/{Run.ts => DotNetRun.ts} (72%) rename src/actions/{Test.ts => DotNetTest.ts} (72%) rename src/actions/{Watch.ts => DotNetWatch.ts} (72%) delete mode 100644 src/actions/MoveProject.ts rename src/actions/{AddSolutionFile.ts => SlnAddSolutionFile.ts} (89%) rename src/actions/{CreateSolutionFolder.ts => SlnCreateSolutionFolder.ts} (67%) rename src/actions/{DeleteSolutionFile.ts => SlnDeleteSolutionFile.ts} (81%) rename src/actions/{DeleteSolutionFolder.ts => SlnDeleteSolutionFolder.ts} (62%) create mode 100644 src/actions/SlnMoveProject.ts rename src/actions/{MoveSolutionFolder.ts => SlnMoveSolutionFolder.ts} (53%) rename src/actions/{RenameProject.ts => SlnRenameProject.ts} (83%) rename src/actions/{RenameSolutionFolder.ts => SlnRenameSolutionFolder.ts} (86%) create mode 100644 src/core/Solutions/SlnLoader.ts create mode 100644 src/core/Solutions/SolutionFactory.ts create mode 100644 src/core/Solutions/model.ts rename src/core/Solutions/{ => sln}/ProjectConfigurationInSolution.ts (100%) rename src/core/Solutions/{ => sln}/ProjectInSolution.ts (100%) rename src/core/Solutions/{ => sln}/ProjectTypeIds.ts (100%) rename src/core/Solutions/{ => sln}/SolutionConfigurationInSolution.ts (100%) rename src/core/Solutions/{ => sln}/SolutionFile.ts (100%) rename src/core/Solutions/{ => sln}/SolutionProjectType.ts (100%) create mode 100644 src/core/Solutions/sln/index.ts diff --git a/src/SolutionTreeItemCollection.ts b/src/SolutionTreeItemCollection.ts index f8d02ba..0a07d2d 100644 --- a/src/SolutionTreeItemCollection.ts +++ b/src/SolutionTreeItemCollection.ts @@ -1,5 +1,5 @@ import { SolutionExplorerProvider } from "@SolutionExplorerProvider"; -import { SolutionFile } from "@core/Solutions"; +import { SolutionFactory } from "@core/Solutions"; import { TreeItem, TreeItemFactory } from "@tree"; @@ -28,7 +28,7 @@ export class SolutionTreeItemCollection { } public async addSolution(solutionPath: string, rootPath: string, solutionProvider: SolutionExplorerProvider): Promise { - const solution = await SolutionFile.parse(solutionPath); + const solution = await SolutionFactory.load(solutionPath); const item = await TreeItemFactory.createFromSolution(solutionProvider, solution, rootPath); if (!this.children) { this.children = []; diff --git a/src/actions/AddExistingProject.ts b/src/actions/DotNetAddExistingProject.ts similarity index 74% rename from src/actions/AddExistingProject.ts rename to src/actions/DotNetAddExistingProject.ts index c53fa53..dfa7eb1 100644 --- a/src/actions/AddExistingProject.ts +++ b/src/actions/DotNetAddExistingProject.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class AddExistingProject extends CustomTerminalAction { +export class DotNetAddExistingProject extends CustomTerminalAction { constructor(private readonly solutionPath: string, private readonly projectPath: string) { super({ name: "addExistingProjectToSolution", parameters: { solutionPath, projectPath }, - workingFolder: AddExistingProject.getWorkingPath(solutionPath) + workingFolder: DotNetAddExistingProject.getWorkingPath(solutionPath) }); } diff --git a/src/actions/AddPackageReference.ts b/src/actions/DotNetAddPackageReference.ts similarity index 76% rename from src/actions/AddPackageReference.ts rename to src/actions/DotNetAddPackageReference.ts index b9bb4a7..1472185 100644 --- a/src/actions/AddPackageReference.ts +++ b/src/actions/DotNetAddPackageReference.ts @@ -1,12 +1,12 @@ import { TerminalCommand } from "@extensions/defaultTerminalCommands"; import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class AddPackageReference extends CustomTerminalAction { +export class DotNetAddPackageReference extends CustomTerminalAction { constructor(private readonly projectPath: string, private readonly packageId: string, packageVersion?: string) { super({ - name: AddPackageReference.getTerminalCommand(projectPath, packageId, packageVersion), + name: DotNetAddPackageReference.getTerminalCommand(projectPath, packageId, packageVersion), parameters: { projectPath, packageId, packageVersion: packageVersion || "" }, - workingFolder: AddPackageReference.getWorkingPath(projectPath) + workingFolder: DotNetAddPackageReference.getWorkingPath(projectPath) }); } diff --git a/src/actions/AddProjectReference.ts b/src/actions/DotNetAddProjectReference.ts similarity index 75% rename from src/actions/AddProjectReference.ts rename to src/actions/DotNetAddProjectReference.ts index f41108b..100b32d 100644 --- a/src/actions/AddProjectReference.ts +++ b/src/actions/DotNetAddProjectReference.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class AddProjectReference extends CustomTerminalAction { +export class DotNetAddProjectReference extends CustomTerminalAction { constructor(private readonly projectPath: string, private readonly referencedProjectPath: string) { super({ name: "addProjectReferenceToProject", parameters: { projectPath, referencedProjectPath }, - workingFolder: AddProjectReference.getWorkingPath(projectPath) + workingFolder: DotNetAddProjectReference.getWorkingPath(projectPath) }); } diff --git a/src/actions/Build.ts b/src/actions/DotNetBuild.ts similarity index 72% rename from src/actions/Build.ts rename to src/actions/DotNetBuild.ts index a0a89d9..787d4a3 100644 --- a/src/actions/Build.ts +++ b/src/actions/DotNetBuild.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class Build extends CustomTerminalAction { +export class DotNetBuild extends CustomTerminalAction { constructor(private readonly projectPath: string) { super({ name: "build", parameters: { projectPath }, - workingFolder: Build.getWorkingPath(projectPath) + workingFolder: DotNetBuild.getWorkingPath(projectPath) }); } diff --git a/src/actions/Clean.ts b/src/actions/DotNetClean.ts similarity index 72% rename from src/actions/Clean.ts rename to src/actions/DotNetClean.ts index 5e8e61f..7cdbfb4 100644 --- a/src/actions/Clean.ts +++ b/src/actions/DotNetClean.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class Clean extends CustomTerminalAction { +export class DotNetClean extends CustomTerminalAction { constructor(private readonly projectPath: string) { super({ name: "clean", parameters: { projectPath }, - workingFolder: Clean.getWorkingPath(projectPath) + workingFolder: DotNetClean.getWorkingPath(projectPath) }); } diff --git a/src/actions/CreateProject.ts b/src/actions/DotNetNewProject.ts similarity index 88% rename from src/actions/CreateProject.ts rename to src/actions/DotNetNewProject.ts index 4e280f1..6f65abd 100644 --- a/src/actions/CreateProject.ts +++ b/src/actions/DotNetNewProject.ts @@ -1,6 +1,6 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class CreateProject extends CustomTerminalAction { +export class DotNetCreateProject extends CustomTerminalAction { constructor(projectType: string, language: string, private readonly projectName: string, folderName: string, workingFolder: string) { super({ name: "createProject", diff --git a/src/actions/CreateSolution.ts b/src/actions/DotNetNewSln.ts similarity index 86% rename from src/actions/CreateSolution.ts rename to src/actions/DotNetNewSln.ts index 57d543a..04b2f8c 100644 --- a/src/actions/CreateSolution.ts +++ b/src/actions/DotNetNewSln.ts @@ -1,6 +1,6 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class CreateSolution extends CustomTerminalAction { +export class DotNetNewSln extends CustomTerminalAction { constructor(private readonly solutionName: string, workingFolder: string) { super({ name: "createSolution", diff --git a/src/actions/Pack.ts b/src/actions/DotNetPack.ts similarity index 72% rename from src/actions/Pack.ts rename to src/actions/DotNetPack.ts index 1375fe4..9350807 100644 --- a/src/actions/Pack.ts +++ b/src/actions/DotNetPack.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class Pack extends CustomTerminalAction { +export class DotNetPack extends CustomTerminalAction { constructor(private readonly projectPath: string) { super({ name: "pack", parameters: { projectPath }, - workingFolder: Pack.getWorkingPath(projectPath) + workingFolder: DotNetPack.getWorkingPath(projectPath) }); } diff --git a/src/actions/Publish.ts b/src/actions/DotNetPublish.ts similarity index 71% rename from src/actions/Publish.ts rename to src/actions/DotNetPublish.ts index b905d20..661703a 100644 --- a/src/actions/Publish.ts +++ b/src/actions/DotNetPublish.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class Publish extends CustomTerminalAction { +export class DotNetPublish extends CustomTerminalAction { constructor(private readonly projectPath: string) { super({ name: "publish", parameters: { projectPath }, - workingFolder: Publish.getWorkingPath(projectPath) + workingFolder: DotNetPublish.getWorkingPath(projectPath) }); } diff --git a/src/actions/RemoveExistingProject.ts b/src/actions/DotNetRemoveExistingProject.ts similarity index 73% rename from src/actions/RemoveExistingProject.ts rename to src/actions/DotNetRemoveExistingProject.ts index d8793a4..e7462c1 100644 --- a/src/actions/RemoveExistingProject.ts +++ b/src/actions/DotNetRemoveExistingProject.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class RemoveExistingProject extends CustomTerminalAction { +export class DotNetRemoveExistingProject extends CustomTerminalAction { constructor(private readonly solutionPath: string, private readonly projectPath: string) { super({ name: "removeProjectFromSolution", parameters: { solutionPath, projectPath }, - workingFolder: RemoveExistingProject.getWorkingPath(solutionPath) + workingFolder: DotNetRemoveExistingProject.getWorkingPath(solutionPath) }); } diff --git a/src/actions/RemovePackageReference.ts b/src/actions/DotNetRemovePackageReference.ts similarity index 73% rename from src/actions/RemovePackageReference.ts rename to src/actions/DotNetRemovePackageReference.ts index 693dbd9..1c11a4f 100644 --- a/src/actions/RemovePackageReference.ts +++ b/src/actions/DotNetRemovePackageReference.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class RemovePackageReference extends CustomTerminalAction { +export class DotNetRemovePackageReference extends CustomTerminalAction { constructor(private readonly projectPath: string, private readonly packageId: string) { super({ name: "removePackageReferenceFromProject", parameters: { projectPath, packageId }, - workingFolder: RemovePackageReference.getWorkingPath(projectPath) + workingFolder: DotNetRemovePackageReference.getWorkingPath(projectPath) }); } diff --git a/src/actions/RemoveProjectReference.ts b/src/actions/DotNetRemoveProjectReference.ts similarity index 75% rename from src/actions/RemoveProjectReference.ts rename to src/actions/DotNetRemoveProjectReference.ts index 28f7f93..3b6b3b3 100644 --- a/src/actions/RemoveProjectReference.ts +++ b/src/actions/DotNetRemoveProjectReference.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class RemoveProjectReference extends CustomTerminalAction { +export class DotNetRemoveProjectReference extends CustomTerminalAction { constructor(private readonly projectPath: string, private readonly referencedProjectPath: string) { super({ name: "removeProjectReferenceFromProject", parameters: { projectPath, referencedProjectPath }, - workingFolder: RemoveProjectReference.getWorkingPath(projectPath) + workingFolder: DotNetRemoveProjectReference.getWorkingPath(projectPath) }); } diff --git a/src/actions/Restore.ts b/src/actions/DotNetRestore.ts similarity index 71% rename from src/actions/Restore.ts rename to src/actions/DotNetRestore.ts index eb84b5f..8d0d6e0 100644 --- a/src/actions/Restore.ts +++ b/src/actions/DotNetRestore.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class Restore extends CustomTerminalAction { +export class DotNetRestore extends CustomTerminalAction { constructor(private readonly projectPath: string) { super({ name: "restore", parameters: { projectPath }, - workingFolder: Restore.getWorkingPath(projectPath) + workingFolder: DotNetRestore.getWorkingPath(projectPath) }); } diff --git a/src/actions/Run.ts b/src/actions/DotNetRun.ts similarity index 72% rename from src/actions/Run.ts rename to src/actions/DotNetRun.ts index ce86692..4cbfee6 100644 --- a/src/actions/Run.ts +++ b/src/actions/DotNetRun.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class Run extends CustomTerminalAction { +export class DotNetRun extends CustomTerminalAction { constructor(private readonly projectPath: string) { super({ name: "run", parameters: { projectPath }, - workingFolder: Run.getWorkingPath(projectPath) + workingFolder: DotNetRun.getWorkingPath(projectPath) }); } diff --git a/src/actions/Test.ts b/src/actions/DotNetTest.ts similarity index 72% rename from src/actions/Test.ts rename to src/actions/DotNetTest.ts index cb80fa7..7ddaee0 100644 --- a/src/actions/Test.ts +++ b/src/actions/DotNetTest.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class Test extends CustomTerminalAction { +export class DotNetTest extends CustomTerminalAction { constructor(private readonly projectPath: string) { super({ name: "test", parameters: { projectPath }, - workingFolder: Test.getWorkingPath(projectPath) + workingFolder: DotNetTest.getWorkingPath(projectPath) }); } diff --git a/src/actions/Watch.ts b/src/actions/DotNetWatch.ts similarity index 72% rename from src/actions/Watch.ts rename to src/actions/DotNetWatch.ts index d671369..8e61ffd 100644 --- a/src/actions/Watch.ts +++ b/src/actions/DotNetWatch.ts @@ -1,11 +1,11 @@ import { CustomTerminalAction } from "./base/CustomTerminalAction"; -export class Watch extends CustomTerminalAction { +export class DotNetWatch extends CustomTerminalAction { constructor(private readonly projectPath: string) { super({ name: "watch", parameters: { projectPath }, - workingFolder: Watch.getWorkingPath(projectPath) + workingFolder: DotNetWatch.getWorkingPath(projectPath) }); } diff --git a/src/actions/MoveProject.ts b/src/actions/MoveProject.ts deleted file mode 100644 index 94af6a9..0000000 --- a/src/actions/MoveProject.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ProjectInSolution, SolutionFile } from "@core/Solutions"; -import { MoveSolutionFolder } from "./MoveSolutionFolder"; - -export class MoveProject extends MoveSolutionFolder { - constructor(solution: SolutionFile, projectInSolution: ProjectInSolution, targetPath: string) { - super(solution, projectInSolution, targetPath); - } - - public toString(): string { - return `Move project ${this.projectInSolution.projectName} to ${this.targetPath}in ${this.solution.name}`; - } -} diff --git a/src/actions/AddSolutionFile.ts b/src/actions/SlnAddSolutionFile.ts similarity index 89% rename from src/actions/AddSolutionFile.ts rename to src/actions/SlnAddSolutionFile.ts index 258aad2..509d71c 100644 --- a/src/actions/AddSolutionFile.ts +++ b/src/actions/SlnAddSolutionFile.ts @@ -2,16 +2,16 @@ import * as path from "@extensions/path"; import * as fs from "@extensions/fs"; import * as dialogs from "@extensions/dialogs"; import { Action, ActionContext } from "./base/Action"; -import { ProjectInSolution, SolutionFile } from "@core/Solutions"; +import { SolutionItem, Solution } from "@core/Solutions"; type CopyFileOptions = "Yes" | "Skip" | "Cancel"; -export class AddSolutionFile implements Action { - constructor(private readonly solution: SolutionFile, private readonly projectInSolution: ProjectInSolution, private filePath: string) { +export class SlnAddSolutionFile implements Action { + constructor(private readonly solution: Solution, private readonly solutionItem: SolutionItem, private filePath: string) { } public toString(): string { - return `Add file ${this.filePath} to folder ${this.projectInSolution.projectName} in solution ${this.solution.name}`; + return `Add file ${this.filePath} to folder ${this.solutionItem.name} in solution ${this.solution.name}`; } public async execute(context: ActionContext): Promise { @@ -82,7 +82,7 @@ export class AddSolutionFile implements Action { const lines: string[] = data.split('\n'); let projectLineIndexStart = -1, lineIndex = -1, hasSection = false; lines.some((line, index, arr) => { - if (projectLineIndexStart < 0 && line.trim().startsWith('Project(') && line.indexOf('"' + this.projectInSolution.projectGuid + '"') > 0) { + if (projectLineIndexStart < 0 && line.trim().startsWith('Project(') && line.indexOf('"' + this.solutionItem.id + '"') > 0) { projectLineIndexStart = index; } diff --git a/src/actions/CreateSolutionFolder.ts b/src/actions/SlnCreateSolutionFolder.ts similarity index 67% rename from src/actions/CreateSolutionFolder.ts rename to src/actions/SlnCreateSolutionFolder.ts index 45144e7..1df776b 100644 --- a/src/actions/CreateSolutionFolder.ts +++ b/src/actions/SlnCreateSolutionFolder.ts @@ -1,10 +1,10 @@ import { v4 as uuidv4 } from "uuid"; import * as fs from "@extensions/fs"; -import { ProjectInSolution, SolutionFile, SolutionProjectType } from "@core/Solutions"; +import { SolutionItem, Solution, SolutionFolder } from "@core/Solutions"; import { Action, ActionContext } from "./base/Action"; -export class CreateSolutionFolder implements Action { - constructor(private readonly solution: SolutionFile, private readonly folderName: string, private readonly projectInSolution?: ProjectInSolution) { +export class SlnCreateSolutionFolder implements Action { + constructor(private readonly solution: Solution, private readonly folderName: string, private readonly parentItem?: SolutionItem) { } public toString(): string { @@ -14,12 +14,12 @@ export class CreateSolutionFolder implements Action { public async execute(context: ActionContext): Promise { if (context.cancelled) { return; } - if (!this.projectInSolution) { - if (this.solution.projects.findIndex(p => p.projectName === this.folderName && p.projectType === SolutionProjectType.solutionFolder && !p.parentProjectGuid) >= 0) { + if (!this.parentItem) { + if (this.solution.getFolders().some(p => p.name === this.folderName)) { throw new Error('Can not create solution folder, the folder already exists'); } } else { - if (this.solution.projects.findIndex(p => p.projectName === this.folderName && p.projectType === SolutionProjectType.solutionFolder && p.parentProjectGuid === this.projectInSolution?.projectGuid) >= 0) { + if (this.parentItem instanceof SolutionFolder && this.parentItem.getFolders().some(p => p.name === this.folderName)) { throw new Error('Can not create solution folder, the folder already exists'); } } @@ -38,12 +38,12 @@ export class CreateSolutionFolder implements Action { return false; }); - if (this.projectInSolution && done) { + if (this.parentItem && done) { let endGlobalIndex: number = -1; done = lines.some((line, index, arr) => { - if (this.projectInSolution && line.trim() === 'GlobalSection(NestedProjects) = preSolution') { + if (this.parentItem && line.trim() === 'GlobalSection(NestedProjects) = preSolution') { lines.splice(index + 1, 0, - ' {' + guid + '} = ' + this.projectInSolution.projectGuid + '\r' + ' {' + guid + '} = ' + this.parentItem.id + '\r' ); return true; } @@ -58,7 +58,7 @@ export class CreateSolutionFolder implements Action { if (!done && endGlobalIndex > 0) { lines.splice(endGlobalIndex, 0, ' GlobalSection(NestedProjects) = preSolution\r', - ' {' + guid + '} = ' + this.projectInSolution.projectGuid + '\r', + ' {' + guid + '} = ' + this.parentItem.id + '\r', ' EndGlobalSection\r'); done = true; } diff --git a/src/actions/DeleteSolutionFile.ts b/src/actions/SlnDeleteSolutionFile.ts similarity index 81% rename from src/actions/DeleteSolutionFile.ts rename to src/actions/SlnDeleteSolutionFile.ts index b081697..d242299 100644 --- a/src/actions/DeleteSolutionFile.ts +++ b/src/actions/SlnDeleteSolutionFile.ts @@ -1,14 +1,14 @@ import * as path from "@extensions/path"; import * as fs from "@extensions/fs"; import { Action, ActionContext } from "./base/Action"; -import { ProjectInSolution, SolutionFile } from "@core/Solutions"; +import { SolutionItem, Solution } from "@core/Solutions"; -export class DeleteSolutionFile implements Action { - constructor(private readonly solution: SolutionFile, private readonly projectInSolution: ProjectInSolution, private filePath: string) { +export class SlnDeleteSolutionFile implements Action { + constructor(private readonly solution: Solution, private readonly solutionItem: SolutionItem, private filePath: string) { } public toString(): string { - return `Delete file ${this.filePath} from folder ${this.projectInSolution.projectName} in solution ${this.solution.name}`; + return `Delete file ${this.filePath} from folder ${this.solutionItem.name} in solution ${this.solution.name}`; } public async execute(context: ActionContext): Promise { @@ -26,7 +26,7 @@ export class DeleteSolutionFile implements Action { let lines: string[] = data.split('\n'); let lineIndex = -1, projectLineIndexStart = -1, projectLineIndexEnd = -1, hasSection = false; lines.some((line, index, arr) => { - if (projectLineIndexStart < 0 && line.trim().startsWith('Project(') && line.indexOf('"' + this.projectInSolution.projectGuid + '"') > 0) { + if (projectLineIndexStart < 0 && line.trim().startsWith('Project(') && line.indexOf('"' + this.solutionItem.id + '"') > 0) { projectLineIndexStart = index; } diff --git a/src/actions/DeleteSolutionFolder.ts b/src/actions/SlnDeleteSolutionFolder.ts similarity index 62% rename from src/actions/DeleteSolutionFolder.ts rename to src/actions/SlnDeleteSolutionFolder.ts index af1692f..34be664 100644 --- a/src/actions/DeleteSolutionFolder.ts +++ b/src/actions/SlnDeleteSolutionFolder.ts @@ -1,14 +1,13 @@ import * as fs from "@extensions/fs"; -import { ProjectInSolution, SolutionFile } from "@core/Solutions"; +import { SolutionItem, Solution, SolutionFolder } from "@core/Solutions"; import { Action, ActionContext } from "./base/Action"; - -export class DeleteSolutionFolder implements Action { - constructor(private readonly solution: SolutionFile, private readonly projectInSolution: ProjectInSolution) { +export class SlnDeleteSolutionFolder implements Action { + constructor(private readonly solution: Solution, private readonly solutionItem: SolutionItem) { } public toString(): string { - return `Delete folder ${this.projectInSolution.projectName} from solution ${this.solution.name}`; + return `Delete folder ${this.solutionItem.name} from solution ${this.solution.name}`; } public async execute(context: ActionContext): Promise { @@ -16,12 +15,15 @@ export class DeleteSolutionFolder implements Action { let data: string = await fs.readFile(this.solution.fullPath); let lines: string[] = data.split('\n'); - let toDelete: ProjectInSolution[] = [this.projectInSolution]; - this.solution.projects.forEach(p => { - if (p.parentProjectGuid === this.projectInSolution.projectGuid) { + let toDelete: SolutionItem[] = [this.solutionItem]; + if (this.solutionItem instanceof SolutionFolder) { + this.solutionItem.getAllFolders().forEach(p => { toDelete.push(p); - } - }); + }); + this.solutionItem.getAllProjects().forEach(p => { + toDelete.push(p); + }); + } toDelete.forEach(p => { this.deleteProject(p, lines); @@ -30,10 +32,10 @@ export class DeleteSolutionFolder implements Action { await fs.writeFile(this.solution.fullPath, lines.join('\n')); } - private deleteProject(p: ProjectInSolution, lines: string[]): void { + private deleteProject(p: SolutionItem, lines: string[]): void { let projectLineIndexStart = -1, projectLineIndexEnd = -1; lines.some((line, index, arr) => { - if (projectLineIndexStart < 0 && line.trim().startsWith('Project(') && line.indexOf('"' + p.projectGuid + '"') > 0) { + if (projectLineIndexStart < 0 && line.trim().startsWith('Project(') && line.indexOf('"' + p.id + '"') > 0) { projectLineIndexStart = index; } @@ -51,7 +53,7 @@ export class DeleteSolutionFolder implements Action { let index: number; do { - index = lines.findIndex(l => l.indexOf(p.projectGuid) >= 0); + index = lines.findIndex(l => l.indexOf(p.id) >= 0); if (index >= 0) { lines.splice(index, 1); } diff --git a/src/actions/SlnMoveProject.ts b/src/actions/SlnMoveProject.ts new file mode 100644 index 0000000..cd291e2 --- /dev/null +++ b/src/actions/SlnMoveProject.ts @@ -0,0 +1,12 @@ +import { SolutionItem, Solution } from "@core/Solutions"; +import { SlnMoveSolutionFolder } from "./SlnMoveSolutionFolder"; + +export class SlnMoveProject extends SlnMoveSolutionFolder { + constructor(solution: Solution, solutionItem: SolutionItem, targetPath: string) { + super(solution, solutionItem, targetPath); + } + + public toString(): string { + return `Move project ${this.solutionItem.name} to ${this.folderId} in ${this.solution.name}`; + } +} diff --git a/src/actions/MoveSolutionFolder.ts b/src/actions/SlnMoveSolutionFolder.ts similarity index 53% rename from src/actions/MoveSolutionFolder.ts rename to src/actions/SlnMoveSolutionFolder.ts index 4973098..cc87266 100644 --- a/src/actions/MoveSolutionFolder.ts +++ b/src/actions/SlnMoveSolutionFolder.ts @@ -1,9 +1,9 @@ import * as fs from "@extensions/fs"; import { Action, ActionContext } from "./base/Action"; -import { ProjectInSolution, SolutionFile } from "@core/Solutions"; +import { Solution, SolutionFolder, SolutionItem } from "@core/Solutions"; -export class MoveSolutionFolder implements Action { - constructor(protected readonly solution: SolutionFile, protected readonly projectInSolution: ProjectInSolution, protected readonly targetPath: string) { +export class SlnMoveSolutionFolder implements Action { + constructor(protected readonly solution: Solution, protected readonly solutionItem: SolutionItem, protected readonly folderId: string) { } public async execute(context: ActionContext): Promise { @@ -12,16 +12,16 @@ export class MoveSolutionFolder implements Action { let data: string = await fs.readFile(this.solution.fullPath); let lines: string[] = data.split('\n'); let done: boolean = false; - if (!this.projectInSolution.parentProjectGuid) { - if (this.targetPath === 'root') { + if (this.solutionItem.parent instanceof Solution) { + if (this.folderId === 'root') { return; } let endGlobalIndex: number = -1; done = lines.some((line, index, arr) => { - if (this.projectInSolution && line.trim() === 'GlobalSection(NestedProjects) = preSolution') { + if (this.solutionItem && line.trim() === 'GlobalSection(NestedProjects) = preSolution') { lines.splice(index + 1, 0, - ' ' + this.projectInSolution.projectGuid + ' = ' + this.targetPath + '\r' + ' ' + this.solutionItem.id + ' = ' + this.folderId + '\r' ); return true; } @@ -36,19 +36,22 @@ export class MoveSolutionFolder implements Action { if (!done && endGlobalIndex > 0) { lines.splice(endGlobalIndex, 0, ' GlobalSection(NestedProjects) = preSolution\r', - ' ' + this.projectInSolution.projectGuid + ' = ' + this.targetPath + '\r', + ' ' + this.solutionItem.id + ' = ' + this.folderId + '\r', ' EndGlobalSection\r'); done = true; } - } else if (this.targetPath !== 'root') { - let index = lines.findIndex(l => l.trim().startsWith(this.projectInSolution.projectGuid + ' = ' + this.projectInSolution.parentProjectGuid)); + } else if (this.folderId !== 'root') { + const parentId = this.solutionItem.parent instanceof SolutionFolder ? this.solutionItem.parent.id : ''; + const index = lines.findIndex(l => l.trim().startsWith(this.solutionItem.id + ' = ' + parentId)); if (index >= 0) { lines.splice(index, 1, - ' ' + this.projectInSolution.projectGuid + ' = ' + this.targetPath + '\r'); + ' ' + this.solutionItem.id + ' = ' + this.folderId + '\r'); done = true; } } else { - let index = lines.findIndex(l => l.trim().startsWith(this.projectInSolution.projectGuid + ' = ' + this.projectInSolution.parentProjectGuid)); + const parentId = this.solutionItem.parent instanceof SolutionFolder ? this.solutionItem.parent.id : ''; + + const index = lines.findIndex(l => l.trim().startsWith(this.solutionItem.id + ' = ' + parentId)); if (index >= 0) { lines.splice(index, 1); done = true; @@ -63,6 +66,6 @@ export class MoveSolutionFolder implements Action { } public toString(): string { - return `Move solution folder ${this.projectInSolution.projectName} to ${this.targetPath}in ${this.solution.name}`; + return `Move solution folder ${this.solutionItem.name} to ${this.folderId} in ${this.solution.name}`; } } diff --git a/src/actions/RenameProject.ts b/src/actions/SlnRenameProject.ts similarity index 83% rename from src/actions/RenameProject.ts rename to src/actions/SlnRenameProject.ts index 29ba79c..6e6de96 100644 --- a/src/actions/RenameProject.ts +++ b/src/actions/SlnRenameProject.ts @@ -3,12 +3,12 @@ import * as fs from "@extensions/fs"; import * as dialogs from "@extensions/dialogs"; import { Project } from "@core/Projects"; import { ActionContext } from "./base/Action"; -import { SolutionFile } from "@core/Solutions"; -import { RenameSolutionFolder } from "./RenameSolutionFolder"; +import { Solution } from "@core/Solutions"; +import { SlnRenameSolutionFolder } from "./SlnRenameSolutionFolder"; -export class RenameProject extends RenameSolutionFolder { - constructor(solution: SolutionFile, private readonly project: Project, private readonly projectname: string, private readonly newprojectname: string) { +export class SlnRenameProject extends SlnRenameSolutionFolder { + constructor(solution: Solution, private readonly project: Project, private readonly projectname: string, private readonly newprojectname: string) { super(solution, projectname, newprojectname); } diff --git a/src/actions/RenameSolutionFolder.ts b/src/actions/SlnRenameSolutionFolder.ts similarity index 86% rename from src/actions/RenameSolutionFolder.ts rename to src/actions/SlnRenameSolutionFolder.ts index 5cef9d1..046939c 100644 --- a/src/actions/RenameSolutionFolder.ts +++ b/src/actions/SlnRenameSolutionFolder.ts @@ -1,10 +1,10 @@ import * as fs from "@extensions/fs"; import { Action, ActionContext } from "./base/Action"; -import { SolutionFile } from "@core/Solutions"; +import { Solution } from "@core/Solutions"; -export class RenameSolutionFolder implements Action { - constructor(protected readonly solution: SolutionFile, private readonly folderName: string, private readonly newFolderName: string) { +export class SlnRenameSolutionFolder implements Action { + constructor(protected readonly solution: Solution, private readonly folderName: string, private readonly newFolderName: string) { } public toString(): string { diff --git a/src/actions/index.ts b/src/actions/index.ts index 4f2dc0c..6796d00 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -1,50 +1,50 @@ export * from "./base/Action"; -export * from "./AddExistingProject"; -export * from "./AddPackageReference"; -export * from "./AddProjectReference"; -export * from "./AddSolutionFile"; -export * from "./Build"; -export * from "./Clean"; +export * from "./DotNetAddExistingProject"; +export * from "./DotNetAddPackageReference"; +export * from "./DotNetAddProjectReference"; +export * from "./SlnAddSolutionFile"; +export * from "./DotNetBuild"; +export * from "./DotNetClean"; export * from "./CollapseAll"; export * from "./Copy"; export * from "./CopyProjectFile"; export * from "./CreateProjectFile"; export * from "./CreateProjectFolder"; -export * from "./CreateProject"; -export * from "./CreateSolution"; -export * from "./CreateSolutionFolder"; +export * from "./DotNetNewProject"; +export * from "./DotNetNewSln"; +export * from "./SlnCreateSolutionFolder"; export * from "./DeleteMultipleItems" export * from "./DeleteProjectFile"; export * from "./DeleteProjectFolder"; -export * from "./DeleteSolutionFolder"; -export * from "./DeleteSolutionFile"; +export * from "./SlnDeleteSolutionFolder"; +export * from "./SlnDeleteSolutionFile"; export * from "./Focus"; export * from "./InstallWorkspaceTemplateFiles"; export * from "./InvalidateNugetCache"; -export * from "./MoveProject"; +export * from "./SlnMoveProject"; export * from "./MoveProjectFile"; export * from "./MoveProjectFileUp"; export * from "./MoveProjectFileDown"; export * from "./MoveProjectFolder"; -export * from "./MoveSolutionFolder"; +export * from "./SlnMoveSolutionFolder"; export * from "./OpenFile"; export * from "./OpenSolution"; -export * from "./Pack"; +export * from "./DotNetPack"; export * from "./Paste"; -export * from "./Publish"; +export * from "./DotNetPublish"; export * from "./RefreshTree"; export * from "./RefreshTreeItem"; -export * from "./RemoveExistingProject"; -export * from "./RemovePackageReference"; -export * from "./RemoveProjectReference"; -export * from "./RenameProject"; +export * from "./DotNetRemoveExistingProject"; +export * from "./DotNetRemovePackageReference"; +export * from "./DotNetRemoveProjectReference"; +export * from "./SlnRenameProject"; export * from "./RenameProjectFile"; export * from "./RenameProjectFolder"; export * from "./RenameSolution"; -export * from "./RenameSolutionFolder"; -export * from "./Restore"; +export * from "./SlnRenameSolutionFolder"; +export * from "./DotNetRestore"; export * from "./RevealInOS"; -export * from "./Run"; +export * from "./DotNetRun"; export * from "./SelectActiveDocumentInTree"; -export * from "./Test"; -export * from "./Watch"; +export * from "./DotNetTest"; +export * from "./DotNetWatch"; diff --git a/src/commands/AddExistingFileToSolutionFolderCommand.ts b/src/commands/AddExistingFileToSolutionFolderCommand.ts index 7de90bd..e341de5 100644 --- a/src/commands/AddExistingFileToSolutionFolderCommand.ts +++ b/src/commands/AddExistingFileToSolutionFolderCommand.ts @@ -1,7 +1,8 @@ import * as dialogs from "@extensions/dialogs"; import { ContextValues, TreeItem } from "@tree"; -import { Action, AddSolutionFile } from "@actions"; +import { Action, SlnAddSolutionFile } from "@actions"; import { SingleItemActionsCommand } from "@commands"; +import { SolutionType } from "@core/Solutions"; export class AddExistingFileToSolutionFolderCommand extends SingleItemActionsCommand { constructor() { @@ -9,16 +10,20 @@ export class AddExistingFileToSolutionFolderCommand extends SingleItemActionsCom } public shouldRun(item: TreeItem | undefined): boolean { - return !!item && !!item.projectInSolution && (item.contextValue === ContextValues.solutionFolder); + return !!item && !!item.solutionItem && (item.contextValue === ContextValues.solutionFolder); } public async getActions(item: TreeItem | undefined): Promise { const filePath = await dialogs.openFile('Select a file to add'); - if (!item || !item.solution || !item.projectInSolution || !filePath) { + if (!item || !item.solution || !item.solutionItem || !filePath) { return []; } - return [ new AddSolutionFile(item.solution, item.projectInSolution, filePath) ]; + if (item.solution.type === SolutionType.Sln) { + return [ new SlnAddSolutionFile(item.solution, item.solutionItem, filePath) ]; + } + + return []; } } diff --git a/src/commands/AddExistingProjectCommand.ts b/src/commands/AddExistingProjectCommand.ts index c3810f5..c948332 100644 --- a/src/commands/AddExistingProjectCommand.ts +++ b/src/commands/AddExistingProjectCommand.ts @@ -1,6 +1,6 @@ import * as dialogs from "@extensions/dialogs"; import { ContextValues, TreeItem } from "@tree"; -import { Action, AddExistingProject } from "@actions"; +import { Action, DotNetAddExistingProject } from "@actions"; import { SingleItemActionsCommand } from "@commands"; import { SolutionExplorerProvider } from "@SolutionExplorerProvider"; @@ -22,7 +22,7 @@ export class AddExistingProjectCommand extends SingleItemActionsCommand { return []; } - return [ new AddExistingProject(solution, projectPath) ]; + return [ new DotNetAddExistingProject(solution, projectPath) ]; } private getSolutions(item: TreeItem): dialogs.ItemsOrItemsResolver { diff --git a/src/commands/AddNewProjectCommand.ts b/src/commands/AddNewProjectCommand.ts index 87ad958..2ad4edf 100644 --- a/src/commands/AddNewProjectCommand.ts +++ b/src/commands/AddNewProjectCommand.ts @@ -2,7 +2,7 @@ import { execSync } from 'child_process'; import * as path from "@extensions/path"; import * as dialogs from '@extensions/dialogs'; import { ContextValues, TreeItem } from "@tree"; -import { Action, AddExistingProject, CreateProject } from '@actions'; +import { Action, DotNetAddExistingProject, DotNetCreateProject } from '@actions'; import { SingleItemActionsCommand } from "@commands"; import { SolutionExplorerProvider } from '@SolutionExplorerProvider'; @@ -70,8 +70,8 @@ export class AddNewProjectCommand extends SingleItemActionsCommand { if (language === 'VB') { projectPath += '.vbproj'; } return [ - new CreateProject(projectType, language, projectName, folderName, workingpath), - new AddExistingProject(solution, projectPath) + new DotNetCreateProject(projectType, language, projectName, folderName, workingpath), + new DotNetAddExistingProject(solution, projectPath) ]; } diff --git a/src/commands/AddPackageCommand.ts b/src/commands/AddPackageCommand.ts index 67abfe9..ca07b05 100644 --- a/src/commands/AddPackageCommand.ts +++ b/src/commands/AddPackageCommand.ts @@ -1,7 +1,7 @@ import * as dialogs from '@extensions/dialogs'; import * as nuget from '@extensions/nuget'; import { TreeItem } from "@tree"; -import { Action, AddPackageReference } from "@actions"; +import { Action, DotNetAddPackageReference } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class AddPackageCommand extends SingleItemActionsCommand { @@ -30,7 +30,7 @@ export class AddPackageCommand extends SingleItemActionsCommand { return []; } - return [ new AddPackageReference(item.project.fullPath, parameters[1], parameters[2]) ]; + return [ new DotNetAddPackageReference(item.project.fullPath, parameters[1], parameters[2]) ]; } private async getNugetFeeds(projectFullPath: string): Promise { diff --git a/src/commands/AddProjectReferenceCommand.ts b/src/commands/AddProjectReferenceCommand.ts index 662a7b4..e6b324c 100644 --- a/src/commands/AddProjectReferenceCommand.ts +++ b/src/commands/AddProjectReferenceCommand.ts @@ -1,8 +1,6 @@ -import * as path from "@extensions/path"; import { TreeItem } from "@tree"; -import { SolutionProjectType, ProjectInSolution } from "@core/Solutions"; import { SingleItemActionsCommand } from "@commands"; -import { Action, AddProjectReference } from "@actions"; +import { Action, DotNetAddProjectReference } from "@actions"; import * as dialogs from '@extensions/dialogs'; export class AddProjectReferenceCommand extends SingleItemActionsCommand { @@ -21,26 +19,18 @@ export class AddProjectReferenceCommand extends SingleItemActionsCommand { const projectPath = await dialogs.selectOption('Select project...', () => this.getCPSProjects(item)); if (!projectPath) { return []; } - return [ new AddProjectReference(item.project.fullPath, projectPath) ]; + return [ new DotNetAddProjectReference(item.project.fullPath, projectPath) ]; } private getCPSProjects(item: TreeItem): Promise<{[id: string]: string}> { let result: {[id: string]: string} = {}; - item.solution.projects.forEach(p => { + item.solution.getAllProjects().forEach(p => { if (item.project && item.project.fullPath === p.fullPath) { return false; } - if (p.projectType !== SolutionProjectType.solutionFolder) { - result[this.getProjectName(p, item.solution.projects)] = p.fullPath; - } + const projectName = p.getFullDisplayName(); + result[p.getFullDisplayName()] = p.fullPath || ''; + }); return Promise.resolve(result); } - - private getProjectName(project: ProjectInSolution, projects: ProjectInSolution[]): string { - if (!project.parentProjectGuid) { return project.projectName; } - - let index = projects.findIndex(p => p.projectGuid === project.parentProjectGuid); - if (index <= 0) { return project.projectName; } - return this.getProjectName(projects[index], projects) + path.sep + project.projectName; - } } diff --git a/src/commands/BuildCommand.ts b/src/commands/BuildCommand.ts index 16b9f27..42fdd4e 100644 --- a/src/commands/BuildCommand.ts +++ b/src/commands/BuildCommand.ts @@ -1,5 +1,5 @@ import { ContextValues, TreeItem } from "@tree"; -import { Action, Build } from "@actions"; +import { Action, DotNetBuild } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class BuildCommand extends SingleItemActionsCommand { @@ -14,6 +14,6 @@ export class BuildCommand extends SingleItemActionsCommand { public async getActions(item: TreeItem | undefined): Promise { if (!item || !item.path) { return []; } - return [ new Build(item.path) ]; + return [ new DotNetBuild(item.path) ]; } } diff --git a/src/commands/CleanCommand.ts b/src/commands/CleanCommand.ts index 398f38b..f84ee71 100644 --- a/src/commands/CleanCommand.ts +++ b/src/commands/CleanCommand.ts @@ -1,5 +1,5 @@ import { ContextValues, TreeItem } from "@tree"; -import { Action, Clean } from "@actions"; +import { Action, DotNetClean } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class CleanCommand extends SingleItemActionsCommand { @@ -14,6 +14,6 @@ export class CleanCommand extends SingleItemActionsCommand { public async getActions(item: TreeItem | undefined): Promise { if (!item || !item.path) { return []; } - return [ new Clean(item.path) ]; + return [ new DotNetClean(item.path) ]; } } diff --git a/src/commands/CreateNewSolutionCommand.ts b/src/commands/CreateNewSolutionCommand.ts index d5f4f9c..fa82fc9 100644 --- a/src/commands/CreateNewSolutionCommand.ts +++ b/src/commands/CreateNewSolutionCommand.ts @@ -1,7 +1,7 @@ import * as vscode from "vscode"; import * as dialogs from "@extensions/dialogs"; import { TreeItem } from "@tree"; -import { Action, CreateSolution } from "@actions"; +import { Action, DotNetNewSln } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class CreateNewSolutionCommand extends SingleItemActionsCommand { @@ -20,6 +20,6 @@ export class CreateNewSolutionCommand extends SingleItemActionsCommand { return []; } - return [ new CreateSolution(solutionName, workingFolder) ]; + return [ new DotNetNewSln(solutionName, workingFolder) ]; } } diff --git a/src/commands/CreateSolutionFolderCommand.ts b/src/commands/CreateSolutionFolderCommand.ts index 30d071c..1f07542 100644 --- a/src/commands/CreateSolutionFolderCommand.ts +++ b/src/commands/CreateSolutionFolderCommand.ts @@ -1,7 +1,8 @@ import * as dialogs from "@extensions/dialogs"; import { TreeItem } from "@tree"; -import { Action, CreateSolutionFolder } from "@actions"; +import { Action, SlnCreateSolutionFolder } from "@actions"; import { SingleItemActionsCommand } from "@commands"; +import { SolutionType } from "@core/Solutions"; export class CreateSolutionFolderCommand extends SingleItemActionsCommand { constructor() { @@ -21,7 +22,10 @@ export class CreateSolutionFolderCommand extends SingleItemActionsCommand { if (!folderName) { return []; } + if (item.solution.type === SolutionType.Sln) { + return [ new SlnCreateSolutionFolder(item.solution, folderName, item.solutionItem) ]; + } - return [ new CreateSolutionFolder(item.solution, folderName, item.projectInSolution) ]; + return []; } } diff --git a/src/commands/DeleteUnifiedCommand.ts b/src/commands/DeleteUnifiedCommand.ts index ee51808..8fc4ddc 100644 --- a/src/commands/DeleteUnifiedCommand.ts +++ b/src/commands/DeleteUnifiedCommand.ts @@ -4,13 +4,14 @@ import { Action, DeleteProjectFile, DeleteProjectFolder, - DeleteSolutionFile, - DeleteSolutionFolder, - RemoveExistingProject, - RemovePackageReference, - RemoveProjectReference, + SlnDeleteSolutionFile, + SlnDeleteSolutionFolder, + DotNetRemoveExistingProject, + DotNetRemovePackageReference, + DotNetRemoveProjectReference, DeleteMultipleItems } from "@actions"; +import { SolutionType } from "@core/Solutions"; export class DeleteUnifiedCommand extends ActionsCommand { constructor() { @@ -107,19 +108,19 @@ export class DeleteUnifiedCommand extends ActionsCommand { ? [new DeleteProjectFolder(item.project, item.path, showDialog)] : []], [ContextValues.projectReferencedPackage, 'cps', item => item.project && item.path - ? [new RemovePackageReference(item.project.fullPath, item.path)] : []], + ? [new DotNetRemovePackageReference(item.project.fullPath, item.path)] : []], [ContextValues.projectReferencedProject, 'cps', item => item.project && item.path - ? [new RemoveProjectReference(item.project.fullPath, item.path)] : []], + ? [new DotNetRemoveProjectReference(item.project.fullPath, item.path)] : []], [ContextValues.project, item => item.project - ? [new RemoveExistingProject(item.solution.fullPath, item.project.fullPath)] : []], + ? [new DotNetRemoveExistingProject(item.solution.fullPath, item.project.fullPath)] : []], - [ContextValues.solutionFile, item => item.projectInSolution && item.path - ? [new DeleteSolutionFile(item.solution, item.projectInSolution, item.path)] : []], + [ContextValues.solutionFile, item => item.solution.type === SolutionType.Sln && item.solutionItem && item.path + ? [new SlnDeleteSolutionFile(item.solution, item.solutionItem, item.path)] : []], - [ContextValues.solutionFolder, item => item.projectInSolution - ? [new DeleteSolutionFolder(item.solution, item.projectInSolution)] : []], + [ContextValues.solutionFolder, item => item.solution.type === SolutionType.Sln && item.solutionItem + ? [new SlnDeleteSolutionFolder(item.solution, item.solutionItem)] : []], ]); } } diff --git a/src/commands/MoveToSolutionFolderCommand.ts b/src/commands/MoveToSolutionFolderCommand.ts index 96c03f6..1a1c408 100644 --- a/src/commands/MoveToSolutionFolderCommand.ts +++ b/src/commands/MoveToSolutionFolderCommand.ts @@ -1,8 +1,8 @@ import * as path from "@extensions/path"; import * as dialogs from "@extensions/dialogs"; import { ContextValues, TreeItem } from "@tree"; -import { SolutionProjectType, ProjectInSolution, SolutionFile } from "@core/Solutions"; -import { Action, MoveProject, MoveSolutionFolder } from "@actions"; +import { Solution, SolutionType } from "@core/Solutions"; +import { Action, SlnMoveProject, SlnMoveSolutionFolder } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class MoveToSolutionFolderCommand extends SingleItemActionsCommand { @@ -11,7 +11,7 @@ export class MoveToSolutionFolderCommand extends SingleItemActionsCommand { } public shouldRun(item: TreeItem | undefined): boolean { - return !!item && !!item.solution && !!item.projectInSolution; + return !!item && !!item.solution && !!item.solutionItem; } public async getActions(item: TreeItem | undefined): Promise { @@ -20,26 +20,24 @@ export class MoveToSolutionFolderCommand extends SingleItemActionsCommand { const folder = await dialogs.selectOption('Select folder...', () => this.getFolders(item.solution)); if (!folder) { return []; } - const projectInSolution = item.projectInSolution; - if (!projectInSolution) { return []; } + const solutionItem = item.solutionItem; + if (!solutionItem) { return []; } - if (ContextValues.matchAnyLanguage(ContextValues.project, item.contextValue)) { - return [ new MoveProject(item.solution, projectInSolution, folder) ]; + if (item.solution.type === SolutionType.Sln && ContextValues.matchAnyLanguage(ContextValues.project, item.contextValue)) { + return [ new SlnMoveProject(item.solution, solutionItem, folder) ]; } - if (ContextValues.matchAnyLanguage(ContextValues.solutionFolder, item.contextValue)) { - return [ new MoveSolutionFolder(item.solution, projectInSolution, folder) ]; + if (item.solution.type === SolutionType.Sln && ContextValues.matchAnyLanguage(ContextValues.solutionFolder, item.contextValue)) { + return [ new SlnMoveSolutionFolder(item.solution, solutionItem, folder) ]; } return []; } - private getFolders(solution: SolutionFile): Promise<{[id:string]: string}> { + private getFolders(solution: Solution): Promise<{[id:string]: string}> { let folders: { id: string, name: string }[] = []; - solution.projects.forEach(p => { - if (p.projectType === SolutionProjectType.solutionFolder) { - folders.push( { id: p.projectGuid, name: this.getFolderName(p, solution) }); - } + solution.getAllFolders().forEach(p => { + folders.push( { id: p.id, name: p.getFullDisplayName() }); }); folders.sort((a, b) => { @@ -56,11 +54,4 @@ export class MoveToSolutionFolderCommand extends SingleItemActionsCommand { return Promise.resolve(result); } - - private getFolderName(p: ProjectInSolution, solution: SolutionFile): string { - if (!p.parentProjectGuid) { return path.sep + p.projectName; } - - let parent = solution.projectsById[p.parentProjectGuid]; - return this.getFolderName(parent, solution) + path.sep + p.projectName; - } } \ No newline at end of file diff --git a/src/commands/PackCommand.ts b/src/commands/PackCommand.ts index ecf6cd7..f411a89 100644 --- a/src/commands/PackCommand.ts +++ b/src/commands/PackCommand.ts @@ -1,5 +1,5 @@ import { ContextValues, TreeItem } from "@tree"; -import { Action, Pack } from "@actions"; +import { Action, DotNetPack } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class PackCommand extends SingleItemActionsCommand { @@ -14,6 +14,6 @@ export class PackCommand extends SingleItemActionsCommand { public async getActions(item: TreeItem | undefined): Promise { if (!item || !item.path) { return []; } - return [ new Pack(item.path) ]; + return [ new DotNetPack(item.path) ]; } } diff --git a/src/commands/PublishCommand.ts b/src/commands/PublishCommand.ts index dfba86b..db2b7d2 100644 --- a/src/commands/PublishCommand.ts +++ b/src/commands/PublishCommand.ts @@ -1,5 +1,5 @@ import { ContextValues, TreeItem } from "@tree"; -import { Action, Publish } from "@actions"; +import { Action, DotNetPublish } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class PublishCommand extends SingleItemActionsCommand { @@ -14,6 +14,6 @@ export class PublishCommand extends SingleItemActionsCommand { public async getActions(item: TreeItem | undefined): Promise { if (!item || !item.path) { return []; } - return [ new Publish(item.path) ]; + return [ new DotNetPublish(item.path) ]; } } diff --git a/src/commands/RenameSolutionItemCommand.ts b/src/commands/RenameSolutionItemCommand.ts index b0a3843..10071d8 100644 --- a/src/commands/RenameSolutionItemCommand.ts +++ b/src/commands/RenameSolutionItemCommand.ts @@ -1,9 +1,9 @@ import * as dialogs from "@extensions/dialogs"; import { SolutionExplorerProvider } from "@SolutionExplorerProvider"; import { ContextValues, TreeItem } from "@tree"; -import { ProjectInSolution } from "@core/Solutions"; -import { Action, RenameProject, RenameSolution, RenameSolutionFolder } from "@actions"; +import { Action, SlnRenameProject, RenameSolution, SlnRenameSolutionFolder } from "@actions"; import { SingleItemActionsCommand } from "@commands"; +import { SolutionType } from "@core/Solutions"; export class RenameSolutionItemCommand extends SingleItemActionsCommand { constructor(private readonly provider: SolutionExplorerProvider) { @@ -24,12 +24,12 @@ export class RenameSolutionItemCommand extends SingleItemActionsCommand { return [ new RenameSolution(item.solution.fullPath, newname) ]; } - if (item.projectInSolution && item.project) { - return [ new RenameProject(item.solution, item.project, item.label, newname) ]; + if (item.solution.type === SolutionType.Sln && item.solutionItem && item.project) { + return [ new SlnRenameProject(item.solution, item.project, item.label, newname) ]; } - if (item.projectInSolution) { - return [ new RenameSolutionFolder(item.solution, item.label, newname) ]; + if (item.solution.type === SolutionType.Sln && item.solutionItem) { + return [ new SlnRenameSolutionFolder(item.solution, item.label, newname) ]; } return []; diff --git a/src/commands/RestoreCommand.ts b/src/commands/RestoreCommand.ts index 9ae975c..55360a6 100644 --- a/src/commands/RestoreCommand.ts +++ b/src/commands/RestoreCommand.ts @@ -1,5 +1,5 @@ import { ContextValues, TreeItem } from "@tree"; -import { Action, Restore } from "@actions"; +import { Action, DotNetRestore } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class RestoreCommand extends SingleItemActionsCommand { @@ -14,6 +14,6 @@ export class RestoreCommand extends SingleItemActionsCommand { public async getActions(item: TreeItem | undefined): Promise { if (!item || !item.path) { return []; } - return [ new Restore(item.path) ]; + return [ new DotNetRestore(item.path) ]; } } diff --git a/src/commands/RunCommand.ts b/src/commands/RunCommand.ts index 4c6eb34..6266bb9 100644 --- a/src/commands/RunCommand.ts +++ b/src/commands/RunCommand.ts @@ -1,5 +1,5 @@ import { ContextValues, TreeItem } from "@tree"; -import { Action, Run } from "@actions"; +import { Action, DotNetRun } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class RunCommand extends SingleItemActionsCommand { @@ -14,6 +14,6 @@ export class RunCommand extends SingleItemActionsCommand { public async getActions(item: TreeItem | undefined): Promise { if (!item || !item.path) { return []; } - return [ new Run(item.path) ]; + return [ new DotNetRun(item.path) ]; } } diff --git a/src/commands/TestCommand.ts b/src/commands/TestCommand.ts index c4d6137..6517d82 100644 --- a/src/commands/TestCommand.ts +++ b/src/commands/TestCommand.ts @@ -1,5 +1,5 @@ import { ContextValues, TreeItem } from "@tree"; -import { Action, Test } from "@actions"; +import { Action, DotNetTest } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class TestCommand extends SingleItemActionsCommand { @@ -14,6 +14,6 @@ export class TestCommand extends SingleItemActionsCommand { public async getActions(item: TreeItem | undefined): Promise { if (!item || !item.path) { return []; } - return [ new Test(item.path) ]; + return [ new DotNetTest(item.path) ]; } } diff --git a/src/commands/UpdatePackageVersionCommand.ts b/src/commands/UpdatePackageVersionCommand.ts index b4fa6ef..4ff6cf2 100644 --- a/src/commands/UpdatePackageVersionCommand.ts +++ b/src/commands/UpdatePackageVersionCommand.ts @@ -1,7 +1,7 @@ import * as dialogs from '@extensions/dialogs'; import * as nuget from '@extensions/nuget'; import { ContextValues, TreeItem } from "@tree"; -import { Action, AddPackageReference } from "@actions"; +import { Action, DotNetAddPackageReference } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class UpdatePackageVersionCommand extends SingleItemActionsCommand { @@ -31,6 +31,6 @@ export class UpdatePackageVersionCommand extends SingleItemActionsCommand { return []; } - return [ new AddPackageReference(item.project.fullPath, packageId, parameters[1]) ]; + return [ new DotNetAddPackageReference(item.project.fullPath, packageId, parameters[1]) ]; } } diff --git a/src/commands/UpdatePackagesVersionCommand.ts b/src/commands/UpdatePackagesVersionCommand.ts index 1e96f65..79087b2 100644 --- a/src/commands/UpdatePackagesVersionCommand.ts +++ b/src/commands/UpdatePackagesVersionCommand.ts @@ -1,5 +1,5 @@ import { TreeItem } from "@tree"; -import { Action, AddPackageReference } from "@actions"; +import { Action, DotNetAddPackageReference } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class UpdatePackagesVersionCommand extends SingleItemActionsCommand { @@ -16,6 +16,6 @@ export class UpdatePackagesVersionCommand extends SingleItemActionsCommand { const references = await item.project.getPackageReferences(); const project = item.project; - return references.map(reference => new AddPackageReference(project.fullPath, reference.name)); + return references.map(reference => new DotNetAddPackageReference(project.fullPath, reference.name)); } } diff --git a/src/commands/WatchRunCommand.ts b/src/commands/WatchRunCommand.ts index 193f321..1b2eb20 100644 --- a/src/commands/WatchRunCommand.ts +++ b/src/commands/WatchRunCommand.ts @@ -1,5 +1,5 @@ import { ContextValues, TreeItem } from "@tree"; -import { Action, Watch } from "@actions"; +import { Action, DotNetWatch } from "@actions"; import { SingleItemActionsCommand } from "@commands"; export class WatchRunCommand extends SingleItemActionsCommand { @@ -14,6 +14,6 @@ export class WatchRunCommand extends SingleItemActionsCommand { public async getActions(item: TreeItem | undefined): Promise { if (!item || !item.path) { return []; } - return [ new Watch(item.path) ]; + return [ new DotNetWatch(item.path) ]; } } diff --git a/src/core/Projects/ProjectFactory.ts b/src/core/Projects/ProjectFactory.ts index 5d5d515..a9da947 100644 --- a/src/core/Projects/ProjectFactory.ts +++ b/src/core/Projects/ProjectFactory.ts @@ -1,8 +1,8 @@ import * as path from "@extensions/path"; import * as fs from "@extensions/fs"; -import { ProjectInSolution, SolutionProjectType, ProjectTypeIds } from "../Solutions"; import { MsBuildProject } from "./MsBuildProject"; import { Project } from "./Project"; +import { SolutionItem, SolutionProject, SolutionProjectType } from "@core/Solutions"; const projectFileExtensions: { [id: string]: string } = { [".csproj"]: ".cs", @@ -13,23 +13,29 @@ const projectFileExtensions: { [id: string]: string } = { }; export class ProjectFactory { - public static async parse(project: string | ProjectInSolution): Promise { - const fullPath: string = typeof project === "string" ? project : project.fullPath; - if (!(await fs.exists(fullPath))) { + public static async parse(project: SolutionItem): Promise { + const p = project as SolutionProject; + if (!p) { return undefined; } - let result = ProjectFactory.loadNodejsProject(fullPath); - if (!result && typeof project !== "string") { - result = ProjectFactory.loadProjectByType(project); + const fullPath = project.fullPath || ""; + if (!(await fs.exists(fullPath))) { + return undefined; } - if (!result) { - result = ProjectFactory.loadProjectByExtension(fullPath); + let result: Project | undefined; + if (p.type === SolutionProjectType.default) { + result = ProjectFactory.loadDefaultProject(fullPath); + } + if (p.type === SolutionProjectType.noReferences) { + result = ProjectFactory.loadNoReferencesProject(fullPath); + } + if (p.type === SolutionProjectType.shared) { + result = ProjectFactory.loadSharedProject(fullPath); } - - ProjectFactory.getFileDefaultExtension(result); if (result) { + ProjectFactory.getFileDefaultExtension(result); try { await result.preload(); } catch (e) { @@ -50,41 +56,6 @@ export class ProjectFactory { } } - private static loadNodejsProject(fullPath: string): Project | undefined { - if (fullPath.toLocaleLowerCase().endsWith(".njsproj")) { - return ProjectFactory.loadNoReferencesProject(fullPath); - } - } - - private static loadProjectByType(project: ProjectInSolution): Project | undefined { - if (project.projectType === SolutionProjectType.knownToBeMSBuildFormat) { - return ProjectFactory.loadDefaultProject(project.fullPath); - } - - if (project.projectType === SolutionProjectType.webProject) { - return ProjectFactory.loadDefaultProject(project.fullPath); - } - - if (project.projectTypeId === ProjectTypeIds.shProjectGuid) { - return ProjectFactory.loadSharedProject(project.fullPath); - } - - if (project.projectTypeId === ProjectTypeIds.deployProjectGuid) { - return ProjectFactory.loadNoReferencesProject(project.fullPath); - } - } - - private static loadProjectByExtension(fullPath: string): Project | undefined { - const extension = path.extname(fullPath); - if (extension === ".shproj") { - return ProjectFactory.loadSharedProject(fullPath); - } else if (extension === ".a") { - - } else { - return ProjectFactory.loadDefaultProject(fullPath); - } - } - private static loadDefaultProject(fullPath: string): Project { return new MsBuildProject(fullPath, undefined, undefined); } diff --git a/src/core/Solutions/SlnLoader.ts b/src/core/Solutions/SlnLoader.ts new file mode 100644 index 0000000..200115f --- /dev/null +++ b/src/core/Solutions/SlnLoader.ts @@ -0,0 +1,61 @@ +import * as path from "path"; +import { Solution, SolutionItem, SolutionFolder, SolutionProject, SolutionProjectType, SolutionType } from "."; +import { + SolutionFile as Sln, + SolutionProjectType as SlnProjectType, + ProjectInSolution as SlnProjectInSolution, + ProjectTypeIds as SlnProjectTypeIds, +} from "./sln"; + +export class SlnLoader { + static async load(filepath: string): Promise { + const sln = await Sln.parse(filepath); + const solution = new Solution(); + solution.type = SolutionType.Sln; + solution.fullPath = filepath; + solution.folderPath = path.dirname(filepath); + solution.name = path.basename(filepath); + sln.projects.filter(x => !x.parentProjectGuid).forEach(project => { + const i = this.createSolutionItem(sln, project); + solution.addItem(i); + }); + + return solution; + } + + private static createSolutionItem(sln: Sln, project: SlnProjectInSolution): SolutionItem { + if (project.projectType === SlnProjectType.solutionFolder) { + const folder = new SolutionFolder(project.projectGuid); + folder.name = project.projectName; + folder.fullPath = project.fullPath; + folder.solutionFiles = project.solutionItems; + sln.projects.filter(x => x.parentProjectGuid === project.projectGuid).forEach(x => { + const i = this.createSolutionItem(sln, x); + folder.addItem(i); + }); + + return folder; + } + + const p = new SolutionProject(project.projectGuid); + p.name = project.projectName; + p.fullPath = project.fullPath; + if (p.fullPath.toLocaleLowerCase().endsWith(".njsproj")) { + p.type = SolutionProjectType.noReferences; + } else if (p.fullPath.toLocaleLowerCase().endsWith(".shproj")) { + p.type = SolutionProjectType.shared; + } else if (project.projectType === SlnProjectType.knownToBeMSBuildFormat) { + p.type = SolutionProjectType.default; + } else if (project.projectType === SlnProjectType.webProject) { + p.type = SolutionProjectType.default; + } else if (project.projectTypeId === SlnProjectTypeIds.shProjectGuid) { + p.type = SolutionProjectType.shared; + } else if (project.projectTypeId === SlnProjectTypeIds.deployProjectGuid) { + p.type = SolutionProjectType.noReferences; + } else { + p.type = SolutionProjectType.default; + } + + return p; + } +} diff --git a/src/core/Solutions/SolutionFactory.ts b/src/core/Solutions/SolutionFactory.ts new file mode 100644 index 0000000..35d3c3b --- /dev/null +++ b/src/core/Solutions/SolutionFactory.ts @@ -0,0 +1,18 @@ +import * as fs from "@extensions/fs"; +import { Solution } from './model'; +import { SlnLoader } from "./SlnLoader"; + +export class SolutionFactory { + public static async load(path: string): Promise { + if (!fs.exists(path)) { + throw new Error(`Solution file not found: ${path}`); + } + + if (path.endsWith(".sln")) { + return SlnLoader.load(path); + } + + return new Solution(); + } +} + diff --git a/src/core/Solutions/index.ts b/src/core/Solutions/index.ts index e2190df..d6d4827 100644 --- a/src/core/Solutions/index.ts +++ b/src/core/Solutions/index.ts @@ -1,7 +1,3 @@ -export * from "./ProjectConfigurationInSolution"; -export * from "./ProjectInSolution"; -export * from "./ProjectTypeIds"; -export * from "./SolutionConfigurationInSolution"; -export * from "./SolutionFile"; -export * from "./SolutionProjectType"; +export * from "./model"; +export * from "./SolutionFactory"; export * from "../../SolutionFinder"; \ No newline at end of file diff --git a/src/core/Solutions/model.ts b/src/core/Solutions/model.ts new file mode 100644 index 0000000..c1a31b9 --- /dev/null +++ b/src/core/Solutions/model.ts @@ -0,0 +1,97 @@ +import * as path from '@extensions/path'; + +export interface SolutionItem { + parent: SolutionParentObject | undefined; + name: string; + fullPath: string | undefined; + id: string; + getFullDisplayName(): string; +} + +class SolutionObject { + public parent: SolutionParentObject | undefined; + public name: string = ''; + public fullPath: string | undefined; + public getFullDisplayName(): string { + return this.name; + } +} + +class SolutionParentObject extends SolutionObject { + protected readonly items: SolutionItem[] = []; + + public addItem(item: SolutionItem): void { + this.items.push(item); + } + + public getFolders(): SolutionFolder[] { + return this.items.filter((item) => item instanceof SolutionFolder) as SolutionFolder[]; + } + + public getProjects(): SolutionProject[] { + return this.items.filter((item) => item instanceof SolutionProject) as SolutionProject[]; + } + + public getAllFolders(): SolutionFolder[] { + let result: SolutionFolder[] = []; + this.getFolders().forEach(folder => { + result.push(folder); + result = result.concat(folder.getAllFolders()); + }); + + return result; + } + + public getAllProjects(): SolutionProject[] { + let result: SolutionProject[] = []; + this.getProjects().forEach(project => result.push(project)); + this.getFolders().forEach(folder => result = result.concat(folder.getAllProjects())); + + return result; + } +} + +export enum SolutionType { + Sln, + Slnx +} + +export class Solution extends SolutionParentObject { + public folderPath: string = ""; + public fullPath: string = ""; + public type: SolutionType = SolutionType.Sln; +} + +export class SolutionFolder extends SolutionParentObject implements SolutionItem { + constructor(public id: string) { + super(); + } + + public solutionFiles: { [id: string] : string } = {}; + + public getFullDisplayName(): string { + if (!this.parent) { return this.name; } + return this.parent.getFullDisplayName() + path.sep + this.name; + } +} + +export enum SolutionProjectType { + unknown, + default, + shared, + noReferences +} + +export class SolutionProject extends SolutionObject implements SolutionItem { + constructor(public id: string) { + super(); + } + + public type: SolutionProjectType = SolutionProjectType.unknown; + + public getFullDisplayName(): string { + if (!this.parent) { return this.name; } + return this.parent.getFullDisplayName() + path.sep + this.name; + } +} + diff --git a/src/core/Solutions/ProjectConfigurationInSolution.ts b/src/core/Solutions/sln/ProjectConfigurationInSolution.ts similarity index 100% rename from src/core/Solutions/ProjectConfigurationInSolution.ts rename to src/core/Solutions/sln/ProjectConfigurationInSolution.ts diff --git a/src/core/Solutions/ProjectInSolution.ts b/src/core/Solutions/sln/ProjectInSolution.ts similarity index 100% rename from src/core/Solutions/ProjectInSolution.ts rename to src/core/Solutions/sln/ProjectInSolution.ts diff --git a/src/core/Solutions/ProjectTypeIds.ts b/src/core/Solutions/sln/ProjectTypeIds.ts similarity index 100% rename from src/core/Solutions/ProjectTypeIds.ts rename to src/core/Solutions/sln/ProjectTypeIds.ts diff --git a/src/core/Solutions/SolutionConfigurationInSolution.ts b/src/core/Solutions/sln/SolutionConfigurationInSolution.ts similarity index 100% rename from src/core/Solutions/SolutionConfigurationInSolution.ts rename to src/core/Solutions/sln/SolutionConfigurationInSolution.ts diff --git a/src/core/Solutions/SolutionFile.ts b/src/core/Solutions/sln/SolutionFile.ts similarity index 100% rename from src/core/Solutions/SolutionFile.ts rename to src/core/Solutions/sln/SolutionFile.ts diff --git a/src/core/Solutions/SolutionProjectType.ts b/src/core/Solutions/sln/SolutionProjectType.ts similarity index 100% rename from src/core/Solutions/SolutionProjectType.ts rename to src/core/Solutions/sln/SolutionProjectType.ts diff --git a/src/core/Solutions/sln/index.ts b/src/core/Solutions/sln/index.ts new file mode 100644 index 0000000..3259369 --- /dev/null +++ b/src/core/Solutions/sln/index.ts @@ -0,0 +1,6 @@ +export * from "./ProjectConfigurationInSolution"; +export * from "./ProjectInSolution"; +export * from "./ProjectTypeIds"; +export * from "./SolutionConfigurationInSolution"; +export * from "./SolutionFile"; +export * from "./SolutionProjectType"; diff --git a/src/tree/TreeItem.ts b/src/tree/TreeItem.ts index c5afd54..04da465 100644 --- a/src/tree/TreeItem.ts +++ b/src/tree/TreeItem.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import * as path from "@extensions/path"; import * as config from "@extensions/config"; import { fasthash } from "@extensions/hash"; -import { ProjectInSolution, SolutionFile } from "@core/Solutions"; +import { Solution, SolutionItem } from "@core/Solutions"; import { Project } from "@core/Projects"; import * as TreeItemIconProvider from "./TreeItemIconProvider"; import { TreeItemContext } from "./TreeItemContext"; @@ -20,7 +20,7 @@ export abstract class TreeItem extends vscode.TreeItem { public collapsibleState: vscode.TreeItemCollapsibleState, public contextValue: string, public path?: string, - public readonly projectInSolution?: ProjectInSolution + public readonly solutionItem?: SolutionItem ) { super(label, collapsibleState); this.createId(); @@ -38,7 +38,7 @@ export abstract class TreeItem extends vscode.TreeItem { return this.context.parent; } - public get solution(): SolutionFile { + public get solution(): Solution { return this.context.solution; } diff --git a/src/tree/TreeItemContext.ts b/src/tree/TreeItemContext.ts index d8b1cf7..e3d8c0a 100644 --- a/src/tree/TreeItemContext.ts +++ b/src/tree/TreeItemContext.ts @@ -1,11 +1,11 @@ import { IEventAggregator } from "@events"; -import { SolutionFile } from "@core/Solutions"; +import { Solution } from "@core/Solutions"; import { Project } from "@core/Projects"; import { SolutionExplorerProvider } from "@SolutionExplorerProvider"; import { TreeItem } from "./"; export class TreeItemContext { - constructor(public readonly provider: SolutionExplorerProvider, public readonly solution: SolutionFile, public readonly workspaceRoot: string, public readonly project?: Project, public readonly parent?: TreeItem) { + constructor(public readonly provider: SolutionExplorerProvider, public readonly solution: Solution, public readonly workspaceRoot: string, public readonly project?: Project, public readonly parent?: TreeItem) { } public get eventAggregator(): IEventAggregator { diff --git a/src/tree/TreeItemFactory.ts b/src/tree/TreeItemFactory.ts index af79845..7b20317 100644 --- a/src/tree/TreeItemFactory.ts +++ b/src/tree/TreeItemFactory.ts @@ -1,6 +1,6 @@ import * as path from "@extensions/path"; import { getItemNesting } from "@extensions/config"; -import { ProjectInSolution, SolutionProjectType, SolutionFile } from "@core/Solutions"; +import { Solution, SolutionProject, SolutionFolder, SolutionItem } from "@core/Solutions"; import { PackageReference, Project, ProjectFactory, ProjectItemEntry } from "@core/Projects"; import { SolutionExplorerProvider } from "@SolutionExplorerProvider"; import { TreeItem } from "@tree/TreeItem"; @@ -16,7 +16,7 @@ import { StandardProjectTreeItem } from "@tree/items/standard/StandardProjectTre import { SolutionFileTreeItem } from "@tree/items/SolutionFileTreeItem"; import { ProjectReferencedPackageTreeItem } from "./items/ProjectReferencedPackageTreeItem"; -export async function createFromSolution(provider: SolutionExplorerProvider, solution: SolutionFile, workspaceRoot: string): Promise { +export async function createFromSolution(provider: SolutionExplorerProvider, solution: Solution, workspaceRoot: string): Promise { let context = new TreeItemContext(provider, solution, workspaceRoot); let treeItem = new SolutionTreeItem(context); await treeItem.getChildren(); @@ -24,29 +24,24 @@ export async function createFromSolution(provider: SolutionExplorerProvider, sol return treeItem; } -export async function createItemsFromSolution(context: TreeItemContext, solution: SolutionFile, projectInSolution?: ProjectInSolution): Promise { +export async function createItemsFromSolution(context: TreeItemContext, solution: Solution, solutionItem?: SolutionItem): Promise { let result: TreeItem[] = []; - let folders: ProjectInSolution[] = []; - let projects: ProjectInSolution[] = []; - solution.projects.forEach(project => { - if (!projectInSolution && project.parentProjectGuid) { return false; } - if (projectInSolution && project.parentProjectGuid !== projectInSolution.projectGuid) { return false; } - if (project.projectType === SolutionProjectType.solutionFolder) { - folders.push(project); - } else { - projects.push(project); - } - }); + let folders: SolutionFolder[] = solution.getFolders(); + let projects: SolutionProject[] = solution.getProjects(); + if (solutionItem instanceof SolutionFolder) { + folders = solutionItem.getFolders(); + projects = solutionItem.getProjects(); + } folders.sort((a, b) => { - let x = a.projectName.toLowerCase(); - let y = b.projectName.toLowerCase(); + let x = a.name.toLowerCase(); + let y = b.name.toLowerCase(); return x < y ? -1 : x > y ? 1 : 0; }); projects.sort((a, b) => { - let x = a.projectName.toLowerCase(); - let y = b.projectName.toLowerCase(); + let x = a.name.toLowerCase(); + let y = b.name.toLowerCase(); return x < y ? -1 : x > y ? 1 : 0; }); @@ -58,18 +53,18 @@ export async function createItemsFromSolution(context: TreeItemContext, solution result.push(await createFromProject(context, projects[i])); } - if (projectInSolution) { - Object.keys(projectInSolution.solutionItems).forEach(k => { - const fullpath = path.join(solution.folderPath, projectInSolution.solutionItems[k]); - result.push(new SolutionFileTreeItem(context, k, fullpath, projectInSolution)); + if (solutionItem instanceof SolutionFolder) { + Object.keys(solutionItem.solutionFiles).forEach(k => { + const fullpath = path.join(solution.folderPath, solutionItem.solutionFiles[k]); + result.push(new SolutionFileTreeItem(context, k, fullpath, solutionItem)); }); } return result; } -async function createFromProject(context: TreeItemContext, project: ProjectInSolution): Promise { - if (project.projectType === SolutionProjectType.solutionFolder) { +async function createFromProject(context: TreeItemContext, project: SolutionItem): Promise { + if (project instanceof SolutionFolder) { const treeItem = await SolutionFolderTreeItem.create(context, project); await treeItem.getChildren(); return treeItem; diff --git a/src/tree/drop/MoveSolutionFolderInTheSameSolution.ts b/src/tree/drop/MoveSolutionFolderInTheSameSolution.ts index 323e1eb..c741bda 100644 --- a/src/tree/drop/MoveSolutionFolderInTheSameSolution.ts +++ b/src/tree/drop/MoveSolutionFolderInTheSameSolution.ts @@ -1,6 +1,7 @@ import { ContextValues, TreeItem } from "@tree"; -import { Action, MoveProject } from "@actions"; +import { Action, SlnMoveProject } from "@actions"; import { DropHandler } from "./DropHandler"; +import { SolutionType } from "@core/Solutions"; export class MoveSolutionFolderInTheSameSolution extends DropHandler { public async canHandle(source: TreeItem, target: TreeItem): Promise { @@ -8,27 +9,32 @@ export class MoveSolutionFolderInTheSameSolution extends DropHandler { } public async handle(source: TreeItem, target: TreeItem): Promise { - if (!target.projectInSolution) { return []; } + if (!target.solutionItem) { return []; } const targetpath = this.isSolution(target) ? 'root' : - this.isProject(target) ? target.projectInSolution.parentProjectGuid || 'root' : - this.isSolutionFolder(target) ? target.projectInSolution.projectGuid : + this.isProject(target) ? target.solutionItem.id || 'root' : + this.isSolutionFolder(target) ? target.solutionItem.id : undefined; - if (!target.solution || !source.projectInSolution || targetpath === undefined) { return []; } - return [new MoveProject(target.solution, source.projectInSolution, targetpath)]; + if (!target.solution || !source.solutionItem || targetpath === undefined) { return []; } + + if (target.solution.type === SolutionType.Sln) { + return [new SlnMoveProject(target.solution, source.solutionItem, targetpath)]; + } + + return []; } protected isProject(item: TreeItem): boolean { - return !!item.project && !!item.projectInSolution; + return !!item.project && !!item.solutionItem; } protected isSolutionFolder(item: TreeItem): boolean { - return !item.project && !!item.projectInSolution; + return !item.project && !!item.solutionItem; } protected isSolution(item: TreeItem): boolean { - return ContextValues.matchAnyLanguage(ContextValues.solution, item.contextValue) && !item.projectInSolution; + return ContextValues.matchAnyLanguage(ContextValues.solution, item.contextValue) && !item.solutionItem; } protected isValidTarget(item: TreeItem): boolean { diff --git a/src/tree/items/ProjectTreeItem.ts b/src/tree/items/ProjectTreeItem.ts index f39136e..cf8564c 100644 --- a/src/tree/items/ProjectTreeItem.ts +++ b/src/tree/items/ProjectTreeItem.ts @@ -1,11 +1,11 @@ -import { ProjectInSolution } from "@core/Solutions"; +import { SolutionItem } from "@core/Solutions"; import { TreeItem, TreeItemCollapsibleState, TreeItemFactory, TreeItemContext, ContextValues } from "@tree"; import { ProjectReferencesTreeItem } from "./ProjectReferencesTreeItem"; import { getOpenProjectOnClick } from "@extensions/config" export class ProjectTreeItem extends TreeItem { - constructor(context: TreeItemContext, projectInSolution: ProjectInSolution) { - super(context, projectInSolution.projectName, TreeItemCollapsibleState.Collapsed, ContextValues.project, projectInSolution.fullPath, projectInSolution); + constructor(context: TreeItemContext, solutionItem: SolutionItem) { + super(context, solutionItem.name, TreeItemCollapsibleState.Collapsed, ContextValues.project, solutionItem.fullPath, solutionItem); this.allowIconTheme = false; this.addContextValueSuffix(); @@ -14,7 +14,7 @@ export class ProjectTreeItem extends TreeItem { command: 'solutionExplorer.openFile', arguments: [this], title: 'Open File' - }; + }; } } @@ -23,7 +23,7 @@ export class ProjectTreeItem extends TreeItem { super.refresh() }); } - + protected async createChildren(childContext: TreeItemContext): Promise { let result: TreeItem[] = []; if (!this.project) { diff --git a/src/tree/items/SolutionFileTreeItem.ts b/src/tree/items/SolutionFileTreeItem.ts index 0e507c6..306c7d6 100644 --- a/src/tree/items/SolutionFileTreeItem.ts +++ b/src/tree/items/SolutionFileTreeItem.ts @@ -1,9 +1,9 @@ -import { ProjectInSolution } from "@core/Solutions"; +import { SolutionItem } from "@core/Solutions"; import { TreeItem, TreeItemCollapsibleState, TreeItemContext, ContextValues } from "@tree"; export class SolutionFileTreeItem extends TreeItem { - constructor(context: TreeItemContext, name: string, filepath: string, projectInSolution: ProjectInSolution) { - super(context, name, TreeItemCollapsibleState.None, ContextValues.solutionFile, filepath, projectInSolution); + constructor(context: TreeItemContext, name: string, filepath: string, solutionItem: SolutionItem) { + super(context, name, TreeItemCollapsibleState.None, ContextValues.solutionFile, filepath, solutionItem); } command = { diff --git a/src/tree/items/SolutionFolderTreeItem.ts b/src/tree/items/SolutionFolderTreeItem.ts index 6e1a5fc..b4b8647 100644 --- a/src/tree/items/SolutionFolderTreeItem.ts +++ b/src/tree/items/SolutionFolderTreeItem.ts @@ -1,10 +1,10 @@ import * as path from "@extensions/path"; import * as fs from "@extensions/fs"; -import { ProjectInSolution } from "@core/Solutions"; +import { SolutionItem, SolutionFolder } from "@core/Solutions"; import { TreeItem, TreeItemCollapsibleState, TreeItemFactory, TreeItemContext, ContextValues } from "@tree"; -async function getPath(context: TreeItemContext, projectInSolution: ProjectInSolution): Promise { - const folder = path.join(path.dirname(context.solution.fullPath), projectInSolution.projectName); +async function getPath(context: TreeItemContext, solutionItem: SolutionItem): Promise { + const folder = path.join(path.dirname(context.solution.fullPath), solutionItem.name); const exists = await fs.exists(folder); if (exists) { const isDirectory = await fs.isDirectory(folder); @@ -17,12 +17,12 @@ async function getPath(context: TreeItemContext, projectInSolution: ProjectInSol } export class SolutionFolderTreeItem extends TreeItem { - private constructor(context: TreeItemContext, projectInSolution: ProjectInSolution, path: string | undefined) { - super(context, projectInSolution.projectName, TreeItemCollapsibleState.Expanded, ContextValues.solutionFolder, path, projectInSolution); + private constructor(context: TreeItemContext, solutionItem: SolutionItem, path: string | undefined) { + super(context, solutionItem.name, TreeItemCollapsibleState.Expanded, ContextValues.solutionFolder, path, solutionItem); } protected createChildren(childContext: TreeItemContext): Promise { - return TreeItemFactory.createItemsFromSolution(childContext, this.solution, this.projectInSolution); + return TreeItemFactory.createItemsFromSolution(childContext, this.solution, this.solutionItem); } public async search(filepath: string): Promise { @@ -39,9 +39,9 @@ export class SolutionFolderTreeItem extends TreeItem { return null; } - public static async create(context: TreeItemContext, projectInSolution: ProjectInSolution): Promise { - const path = await getPath(context, projectInSolution); - const solutionFolder = new SolutionFolderTreeItem(context, projectInSolution, path); + public static async create(context: TreeItemContext, solutionItem: SolutionItem): Promise { + const path = await getPath(context, solutionItem); + const solutionFolder = new SolutionFolderTreeItem(context, solutionItem, path); return solutionFolder; } } diff --git a/src/tree/items/SolutionTreeItem.ts b/src/tree/items/SolutionTreeItem.ts index fd118c9..d575c34 100644 --- a/src/tree/items/SolutionTreeItem.ts +++ b/src/tree/items/SolutionTreeItem.ts @@ -1,5 +1,5 @@ import { ISubscription, EventTypes, IEvent, IFileEvent, FileEventType } from "@events"; -import { SolutionFile } from "@core/Solutions"; +import { Solution, SolutionFactory } from "@core/Solutions"; import { TreeItem, TreeItemCollapsibleState, TreeItemFactory, TreeItemContext, ContextValues } from "@tree"; export class SolutionTreeItem extends TreeItem { @@ -34,7 +34,7 @@ export class SolutionTreeItem extends TreeItem { private onFileEvent(event: IEvent): void { let fileEvent = event; if (fileEvent.path === this.solution.fullPath && fileEvent.fileEventType !== FileEventType.delete) { - SolutionFile.parse(this.solution.fullPath).then(res => { + SolutionFactory.load(this.solution.fullPath).then(res => { this.context = new TreeItemContext(this.context.provider, res, this.workspaceRoot); this.refresh(); }); diff --git a/src/tree/items/UnknownProjectTreeItem.ts b/src/tree/items/UnknownProjectTreeItem.ts index 46f5aa5..bfb3f09 100644 --- a/src/tree/items/UnknownProjectTreeItem.ts +++ b/src/tree/items/UnknownProjectTreeItem.ts @@ -1,11 +1,11 @@ -import { ProjectInSolution } from "@core/Solutions"; +import { SolutionItem } from "@core/Solutions"; import { TreeItem, TreeItemCollapsibleState, TreeItemContext, ContextValues } from "@tree"; import { ErrorTreeItem } from "./ErrorTreeItem"; export class UnknownProjectTreeItem extends TreeItem { - constructor(context: TreeItemContext, projectInSolution: ProjectInSolution) { - super(context, projectInSolution.projectName, TreeItemCollapsibleState.Collapsed, ContextValues.project, projectInSolution.fullPath, projectInSolution); + constructor(context: TreeItemContext, solutionItem: SolutionItem) { + super(context, solutionItem.name, TreeItemCollapsibleState.Collapsed, ContextValues.project, solutionItem.fullPath, solutionItem); this.allowIconTheme = false; } diff --git a/src/tree/items/cps/CpsProjectTreeItem.ts b/src/tree/items/cps/CpsProjectTreeItem.ts index 8b5676f..b4b1a10 100644 --- a/src/tree/items/cps/CpsProjectTreeItem.ts +++ b/src/tree/items/cps/CpsProjectTreeItem.ts @@ -1,14 +1,14 @@ import * as path from "@extensions/path"; import { TreeItemContext } from "@tree"; -import { ProjectInSolution } from "@core/Solutions"; +import { SolutionItem } from "@core/Solutions"; import { EventTypes, IEvent, ISubscription, IFileEvent } from "@events"; import { ProjectTreeItem } from "@tree/items/ProjectTreeItem"; export class CpsProjectTreeItem extends ProjectTreeItem { private subscription: ISubscription | undefined; - constructor(context: TreeItemContext, projectInSolution: ProjectInSolution) { - super(context, projectInSolution); + constructor(context: TreeItemContext, solutionItem: SolutionItem) { + super(context, solutionItem); this.subscription = context.eventAggregator.subscribe(EventTypes.file, evt => this.onFileEvent(evt)); } diff --git a/src/tree/items/standard/StandardProjectTreeItem.ts b/src/tree/items/standard/StandardProjectTreeItem.ts index fbf3176..5d94f3f 100644 --- a/src/tree/items/standard/StandardProjectTreeItem.ts +++ b/src/tree/items/standard/StandardProjectTreeItem.ts @@ -1,13 +1,13 @@ import { EventTypes, IEvent, ISubscription, IFileEvent, FileEvent } from "@events"; -import { ProjectInSolution } from "@core/Solutions"; +import { SolutionItem } from "@core/Solutions"; import { TreeItemContext } from "@tree"; import { ProjectTreeItem } from "@tree/items/ProjectTreeItem"; export class StandardProjectTreeItem extends ProjectTreeItem { private subscription: ISubscription | undefined; - constructor(context: TreeItemContext, projectInSolution: ProjectInSolution) { - super(context, projectInSolution); + constructor(context: TreeItemContext, solutionItem: SolutionItem) { + super(context, solutionItem); this.subscription = context.eventAggregator.subscribe(EventTypes.file, evt => this.onFileEvent(evt)); }