From b4187d914c3476e572a87fb7653361705af34915 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Fri, 23 Dec 2022 20:18:05 +0000 Subject: [PATCH 1/4] Add migration state colors --- .vscode/settings.json | 1 + README.md | 7 ++++--- src/constants/constants.ts | 2 -- src/treeView/MigrationTreeItemDecorationProvider.ts | 10 +++++++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2e373ff..dd0eca9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -46,6 +46,7 @@ }, "cSpell.language": "en-GB", "cSpell.words": [ + "ASPNETCORE", "color", "dbcontext", "efcore", diff --git a/README.md b/README.md index dc65f9d..5996397 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ A VS Code extension to manage Entity Framework migrations. ## Features -![treeview](images/treeview-screenshot.png) +![Entity Framework Migrations](images/treeview-screenshot.png) -- List dbContexts for all projects within a solution +- List migrations by [DbContext](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext) - Add/remove/run/undo migrations +- Export [DbContext](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext) as SQL script ## Requirements @@ -22,7 +23,7 @@ A VS Code extension to manage Entity Framework migrations. This extension contributes the following settings: -- `entityframework.env`: Custom environment vars, for example: +- `entityframework.env`: Custom environment variables, for example: ```json { "entityframework.env": { diff --git a/src/constants/constants.ts b/src/constants/constants.ts index 2ba5b2d..c0866a5 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -1,3 +1 @@ -export const DB_CONTEXT_MODEL_SNAPSHOT_SUFFIX = 'ModelSnapshot.cs'; - export const EXTENSION_NAMESPACE = 'entityframework'; diff --git a/src/treeView/MigrationTreeItemDecorationProvider.ts b/src/treeView/MigrationTreeItemDecorationProvider.ts index 4a0232c..e7ceb5b 100644 --- a/src/treeView/MigrationTreeItemDecorationProvider.ts +++ b/src/treeView/MigrationTreeItemDecorationProvider.ts @@ -2,23 +2,31 @@ import * as vscode from 'vscode'; import { Disposable } from '../util/Disposable'; import { MigrationTreeItemScheme } from './MigrationTreeItemScheme'; +// https://code.visualstudio.com/api/references/theme-color +const APPLIED_COLOR_ID = 'gitDecoration.addedResourceForeground'; +const NOT_APPLIED_COLOR_ID = 'gitDecoration.modifiedResourceForeground'; + export class MigrationTreeItemDecorationProvider extends Disposable { constructor() { super(); this.subscriptions.push(vscode.window.registerFileDecorationProvider(this)); } - public provideFileDecoration(uri: vscode.Uri) { + public provideFileDecoration( + uri: vscode.Uri, + ): vscode.ProviderResult { switch (uri.scheme) { case MigrationTreeItemScheme.Applied: return { badge: '✓', tooltip: '(Migration applied)', + color: new vscode.ThemeColor(APPLIED_COLOR_ID), }; case MigrationTreeItemScheme.NotApplied: return { badge: '✕', tooltip: '(Migration not applied)', + color: new vscode.ThemeColor(NOT_APPLIED_COLOR_ID), }; } } From 8364b478ed7a62f866db69ca3420117d72dabaed Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Fri, 23 Dec 2022 20:32:23 +0000 Subject: [PATCH 2/4] Better error handling --- src/cli/ef.ts | 8 ++++++++ src/terminal/Terminal.ts | 17 +++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/cli/ef.ts b/src/cli/ef.ts index 74046ec..70d48a8 100644 --- a/src/cli/ef.ts +++ b/src/cli/ef.ts @@ -19,6 +19,14 @@ export function removePrefixFromStdOut(output: string): string { .join('\n'); } +export function getErrorsFromStdOut(output: string): string { + return output + .split(/\r\n|\r|\n/) + .filter(line => line.startsWith('error:')) + .map(line => line.replace(/^[a-z]+: /, '')) + .join('\n'); +} + export async function execEF( cmd: string, workspaceRoot: string, diff --git a/src/terminal/Terminal.ts b/src/terminal/Terminal.ts index d274440..baba8ed 100644 --- a/src/terminal/Terminal.ts +++ b/src/terminal/Terminal.ts @@ -3,7 +3,7 @@ import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'; import { EventWaiter } from '../util/EventWaiter'; import { getEnvConfig } from '../config/config'; -import { removePrefixFromStdOut } from '../cli/ef'; +import { getErrorsFromStdOut, removePrefixFromStdOut } from '../cli/ef'; const NL = '\n'; const CR = '\r'; @@ -50,7 +50,9 @@ export class Terminal implements vscode.Pseudoterminal { }); return new Promise(res => { - this.write(this.cmdArgs.join(' ') + '\n'); + // --prefix-output is an internal flag that is added to all commands + const argsWithoutPrefixOutput = this.cmdArgs.slice(0, -1); + this.write(argsWithoutPrefixOutput.join(' ') + '\n'); this.cmd?.stdout.on('data', data => { const dataString = data.toString(); @@ -64,10 +66,13 @@ export class Terminal implements vscode.Pseudoterminal { this.write(removePrefixFromStdOut(dataString)); }); - this.cmd?.on('exit', code => { + this.cmd?.on('exit', async _code => { this.cmd = undefined; - this.write(`Exited with code ${code}\n\n`); - res(stderr || stdout); + const error = stderr || getErrorsFromStdOut(stdout); + if (error) { + await vscode.window.showErrorMessage(error); + } + res(stdout); }); }); } @@ -80,7 +85,7 @@ export class Terminal implements vscode.Pseudoterminal { } public write(message: string): void { - // We need NLCR to move down and left + // NLCR is required to move down and left const sanitisedMessage = message.replace(nlRegExp, `${NL + CR}$1`); this.writeEmitter.fire(sanitisedMessage); } From 543b3a016ad510a4d6ef1d96a33c3bb3e4d89b78 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Sat, 24 Dec 2022 17:07:02 +0000 Subject: [PATCH 3/4] Remove reliance on .sln file --- README.md | 27 +++++++----- package.json | 2 +- src/actions/GenerateScriptAction.ts | 4 +- src/cli/ef.ts | 32 ++++++++------ src/commands/AddMigrationCommand.ts | 2 +- src/commands/GenerateScriptCommand.ts | 2 +- src/commands/RemoveMigrationCommand.ts | 2 +- src/commands/RunMigrationCommand.ts | 2 +- src/commands/UndoMigrationCommand.ts | 4 +- src/constants/constants.ts | 2 + src/extension.ts | 6 +-- src/solution/ProjectFilesProvider.ts | 44 +++++++++++++++++++ ...onProvider.ts => SolutionFilesProvider.ts} | 2 +- src/terminal/TerminalProvider.ts | 5 +-- src/treeView/DbContextTreeItem.ts | 18 ++++---- src/treeView/MigrationTreeItem.ts | 5 +-- src/treeView/ProjectTreeItem.ts | 29 ++++++------ src/treeView/SolutionTreeItem.ts | 24 +++++----- src/treeView/TreeDataProvider.ts | 13 +++--- src/treeView/TreeItem.ts | 4 +- src/types/ProjectFile.ts | 8 ++++ src/vs-parse.d.ts | 6 +++ 22 files changed, 156 insertions(+), 87 deletions(-) create mode 100644 src/solution/ProjectFilesProvider.ts rename src/solution/{SolutionProvider.ts => SolutionFilesProvider.ts} (95%) create mode 100644 src/types/ProjectFile.ts diff --git a/README.md b/README.md index 5996397..16d0277 100644 --- a/README.md +++ b/README.md @@ -16,24 +16,13 @@ A VS Code extension to manage Entity Framework migrations. - [dotnet sdk](https://dotnet.microsoft.com/download) - [efcore tools](https://learn.microsoft.com/en-us/ef/core/cli/dotnet) -- A solution (`.sln`) file with projects - [Microsoft.EntityFrameworkCore.Design](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Design) must be installed in one of the projects ## Extension Settings This extension contributes the following settings: -- `entityframework.env`: Custom environment variables, for example: - ```json - { - "entityframework.env": { - "ASPNETCORE_ENVIRONMENT": "LocalDev", - "TenantId": "12345" - } - } - ``` - `entityframework.commands`: Custom commands, for example: - ```json { "entityframework.commands": { @@ -88,10 +77,24 @@ This extension contributes the following settings: } } ``` +- `entityframework.env`: Custom environment variables, for example: + ```json + { + "entityframework.env": { + "ASPNETCORE_ENVIRONMENT": "LocalDev", + "TenantId": "12345" + } + } + ``` ## Performance -The EF tools execute application code at design time to get information about the project, thus performance can be slow on large projects. +The EF tools execute application code at design time to get information about the project, thus performance on large projects can be slow. + +## Support + +- 👉 [Submit a bug report](https://github.com/badsyntax/vscode-entity-framework/issues/new?assignees=badsyntax&labels=bug&template=bug_report.md&title=) +- 👉 [Submit a feature request](https://github.com/badsyntax/vscode-entity-framework/issues/new?assignees=badsyntax&labels=enhancement&template=feature_request.md&title=) ## License diff --git a/package.json b/package.json index 933bf46..59721ae 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "Other" ], "activationEvents": [ - "workspaceContains:**/*.sln" + "workspaceContains:**/*.csproj" ], "license": "SEE LICENSE IN LICENSE.md", "bugs": { diff --git a/src/actions/GenerateScriptAction.ts b/src/actions/GenerateScriptAction.ts index 74b48c3..e7dd079 100644 --- a/src/actions/GenerateScriptAction.ts +++ b/src/actions/GenerateScriptAction.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; -import { extractDataFromStdOut } from '../cli/ef'; +import { getDataFromStdOut } from '../cli/ef'; import { getCommandsConfig } from '../config/config'; import type { TerminalProvider } from '../terminal/TerminalProvider'; import { TerminalAction } from './TerminalAction'; @@ -24,7 +24,7 @@ export class GenerateScriptAction extends TerminalAction { } public async run() { - const output = extractDataFromStdOut(await super.run()); + const output = getDataFromStdOut(await super.run()); const uri = vscode.Uri.parse('ef-script:' + output); const doc = await vscode.workspace.openTextDocument(uri); await vscode.languages.setTextDocumentLanguage(doc, 'sql'); diff --git a/src/cli/ef.ts b/src/cli/ef.ts index 70d48a8..2b04ba0 100644 --- a/src/cli/ef.ts +++ b/src/cli/ef.ts @@ -3,28 +3,32 @@ import { promisify } from 'node:util'; import { getEnvConfig } from '../config/config'; const execAsync = promisify(exec); +const NEWLINE_SEPARATOR = /\r\n|\r|\n/; +const STDOUT_PREFIX = /^[a-z]+: /; -export function extractDataFromStdOut(output: string): string { +export function removePrefixFromStdOut(output: string): string { return output - .split(/\r\n|\r|\n/) - .filter(line => line.startsWith('data: ')) - .map(line => line.replace('data: ', '')) + .split(NEWLINE_SEPARATOR) + .map(line => line.replace(STDOUT_PREFIX, '')) .join('\n'); } -export function removePrefixFromStdOut(output: string): string { - return output - .split(/\r\n|\r|\n/) - .map(line => line.replace(/^[a-z]+: /, '')) - .join('\n'); +export function getDataFromStdOut(output: string): string { + return removePrefixFromStdOut( + output + .split(NEWLINE_SEPARATOR) + .filter(line => line.startsWith('data:')) + .join('\n'), + ); } export function getErrorsFromStdOut(output: string): string { - return output - .split(/\r\n|\r|\n/) - .filter(line => line.startsWith('error:')) - .map(line => line.replace(/^[a-z]+: /, '')) - .join('\n'); + return removePrefixFromStdOut( + output + .split(NEWLINE_SEPARATOR) + .filter(line => line.startsWith('error:')) + .join('\n'), + ); } export async function execEF( diff --git a/src/commands/AddMigrationCommand.ts b/src/commands/AddMigrationCommand.ts index 69c1922..169a7a4 100644 --- a/src/commands/AddMigrationCommand.ts +++ b/src/commands/AddMigrationCommand.ts @@ -19,7 +19,7 @@ export class AddMigrationCommand extends Command { } return new AddMigrationAction( this.terminalProvider, - this.item.solutionFile.workspaceRoot, + this.item.workspaceRoot, this.item.label, this.item.project, ).run(); diff --git a/src/commands/GenerateScriptCommand.ts b/src/commands/GenerateScriptCommand.ts index 39b0287..1047cb8 100644 --- a/src/commands/GenerateScriptCommand.ts +++ b/src/commands/GenerateScriptCommand.ts @@ -19,7 +19,7 @@ export class GenerateScriptCommand extends Command { } return new GenerateScriptAction( this.terminalProvider, - this.item.solutionFile.workspaceRoot, + this.item.workspaceRoot, this.item.label, this.item.project, ).run(); diff --git a/src/commands/RemoveMigrationCommand.ts b/src/commands/RemoveMigrationCommand.ts index 9ad1844..f8b824d 100644 --- a/src/commands/RemoveMigrationCommand.ts +++ b/src/commands/RemoveMigrationCommand.ts @@ -19,7 +19,7 @@ export class RemoveMigrationCommand extends Command { } return new RemoveMigrationAction( this.terminalProvider, - this.item.solutionFile.workspaceRoot, + this.item.workspaceRoot, this.item.dbContext, this.item.project, ).run(); diff --git a/src/commands/RunMigrationCommand.ts b/src/commands/RunMigrationCommand.ts index 9aa1375..474a2a1 100644 --- a/src/commands/RunMigrationCommand.ts +++ b/src/commands/RunMigrationCommand.ts @@ -19,7 +19,7 @@ export class RunMigrationCommand extends Command { } return new RunMigrationAction( this.terminalProvider, - this.item.solutionFile.workspaceRoot, + this.item.workspaceRoot, this.item.dbContext, this.item.project, this.item.migration.id, diff --git a/src/commands/UndoMigrationCommand.ts b/src/commands/UndoMigrationCommand.ts index cff2db6..47d258d 100644 --- a/src/commands/UndoMigrationCommand.ts +++ b/src/commands/UndoMigrationCommand.ts @@ -22,7 +22,7 @@ export class UndoMigrationCommand extends Command { return; } const cacheId = DbContextTreeItem.getCacheId( - this.item.solutionFile.workspaceRoot, + this.item.workspaceRoot, this.item.project, this.item.dbContext, ); @@ -33,7 +33,7 @@ export class UndoMigrationCommand extends Command { index === 0 ? '0' : migrations[index - 1].migration.id; return new RunMigrationAction( this.terminalProvider, - this.item.solutionFile.workspaceRoot, + this.item.workspaceRoot, this.item.dbContext, this.item.project, migrationId, diff --git a/src/constants/constants.ts b/src/constants/constants.ts index c0866a5..04bf26e 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -1 +1,3 @@ export const EXTENSION_NAMESPACE = 'entityframework'; + +export const TERMINAL_NAME = 'ef-migrations'; diff --git a/src/extension.ts b/src/extension.ts index 8305adb..087ef37 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,19 +3,19 @@ import type * as vscode from 'vscode'; import { TreeDataProvider } from './treeView/TreeDataProvider'; import { CommandProvider } from './commands/CommandProvider'; import { MigrationTreeItemDecorationProvider } from './treeView/MigrationTreeItemDecorationProvider'; -import { SolutionFinder } from './solution/SolutionProvider'; import { Terminal } from './terminal/Terminal'; import { TerminalProvider } from './terminal/TerminalProvider'; import { ScriptFileProvider } from './util/ScriptFileProvider'; +import { ProjectFilesProvider } from './solution/ProjectFilesProvider'; const subscriptions: vscode.Disposable[] = []; export async function activate(_context: vscode.ExtensionContext) { - const solutionFiles = await SolutionFinder.getSolutionFiles(); + const projectFiles = await ProjectFilesProvider.getProjectFiles(); const scriptFileProvider = new ScriptFileProvider(); const migrationTreeItemDecorationProvider = new MigrationTreeItemDecorationProvider(); - const treeDataProvider = new TreeDataProvider(solutionFiles); + const treeDataProvider = new TreeDataProvider(projectFiles); const terminalProvider = new TerminalProvider(new Terminal()); const commandProvider = new CommandProvider( treeDataProvider, diff --git a/src/solution/ProjectFilesProvider.ts b/src/solution/ProjectFilesProvider.ts new file mode 100644 index 0000000..b87b1b9 --- /dev/null +++ b/src/solution/ProjectFilesProvider.ts @@ -0,0 +1,44 @@ +import * as vscode from 'vscode'; +import path from 'node:path'; +import { parseProject } from 'vs-parse'; +import type { ProjectFile } from '../types/ProjectFile'; + +export class ProjectFilesProvider { + public static async getProjectFiles() { + const workspaceRoots = vscode.workspace.workspaceFolders || []; + + const projects: ProjectFile[] = []; + + for (const workspaceRoot of workspaceRoots) { + const projectFiles = await vscode.workspace.findFiles( + new vscode.RelativePattern(workspaceRoot, '**/*.csproj'), + ); + for (const projectFile of projectFiles) { + const project = await parseProject(projectFile.fsPath); + const hasEFDesignPackage = + (project.packages || []).find( + pkg => pkg.name === 'Microsoft.EntityFrameworkCore.Design', + ) !== undefined; + if (hasEFDesignPackage) { + const projectName = path.basename(path.dirname(projectFile.fsPath)); + projects.push({ + name: projectName, + project, + workspaceRoot: workspaceRoot.uri.fsPath, + path: projectFile.fsPath, + }); + } + } + // const solution = await parseSolution(solutionFile.path, { + // deepParse: true, + // }); + // solutionFiles.push({ + // name: path.parse(solutionFile.path).name, + // solution, + // workspaceRoot, + // }); + } + + return projects; + } +} diff --git a/src/solution/SolutionProvider.ts b/src/solution/SolutionFilesProvider.ts similarity index 95% rename from src/solution/SolutionProvider.ts rename to src/solution/SolutionFilesProvider.ts index 05ef7c0..a298889 100644 --- a/src/solution/SolutionProvider.ts +++ b/src/solution/SolutionFilesProvider.ts @@ -3,7 +3,7 @@ import { parseSolution } from 'vs-parse'; import path from 'node:path'; import type { SolutionFile } from '../types/SolutionFile'; -export class SolutionFinder { +export class SolutionFilesProvider { public static async getSolutionFiles() { const workspaceRoots = vscode.workspace.workspaceFolders?.map(w => w.uri.fsPath) || []; diff --git a/src/terminal/TerminalProvider.ts b/src/terminal/TerminalProvider.ts index c227609..773d249 100644 --- a/src/terminal/TerminalProvider.ts +++ b/src/terminal/TerminalProvider.ts @@ -1,9 +1,8 @@ import * as vscode from 'vscode'; +import { TERMINAL_NAME } from '../constants/constants'; import { Disposable } from '../util/Disposable'; import type { Terminal } from './Terminal'; -const TERMINAL_NAME = 'ef-migrations'; - export class TerminalProvider extends Disposable { constructor(private readonly terminal: Terminal) { super(); @@ -11,7 +10,7 @@ export class TerminalProvider extends Disposable { public provideTerminal(): Terminal { let existingTerminal = vscode.window.terminals.find( - t => t.name === TERMINAL_NAME, + ({ name }) => name === TERMINAL_NAME, ); if (!existingTerminal) { existingTerminal = vscode.window.createTerminal({ diff --git a/src/treeView/DbContextTreeItem.ts b/src/treeView/DbContextTreeItem.ts index b355663..2b9e11c 100644 --- a/src/treeView/DbContextTreeItem.ts +++ b/src/treeView/DbContextTreeItem.ts @@ -3,10 +3,10 @@ import type { Migration } from '../types/Migration'; import { getIconPath } from './iconProvider'; import { MigrationTreeItem } from './MigrationTreeItem'; import { TreeItem } from './TreeItem'; -import { execEF, extractDataFromStdOut } from '../cli/ef'; +import { execEF, getDataFromStdOut } from '../cli/ef'; import { TreeItemCache } from './TreeItemCache'; -import type { SolutionFile } from '../types/SolutionFile'; import { ContextValues } from './ContextValues'; +import type { ProjectFile } from '../types/ProjectFile'; export const dbContextsCache = new TreeItemCache(); @@ -15,16 +15,16 @@ export class DbContextTreeItem extends TreeItem { constructor( public readonly label: string, - solutionFile: SolutionFile, + private readonly projectFile: ProjectFile, public readonly project: string, collapsibleState: vscode.TreeItemCollapsibleState = vscode .TreeItemCollapsibleState.Collapsed, ) { - super(label, solutionFile, collapsibleState); + super(label, projectFile.workspaceRoot, collapsibleState); this.iconPath = getIconPath('database_light.svg', 'database_dark.svg'); this.contextValue = ContextValues.dbContext; this.cacheId = DbContextTreeItem.getCacheId( - solutionFile.workspaceRoot, + projectFile.workspaceRoot, this.project, this.label, ); @@ -48,16 +48,14 @@ export class DbContextTreeItem extends TreeItem { try { const output = await execEF( `migrations list --context ${this.label} --project ${this.project} --no-color --json --prefix-output`, - this.solutionFile.workspaceRoot, + this.projectFile.workspaceRoot, ); - const migrations = JSON.parse( - extractDataFromStdOut(output), - ) as Migration[]; + const migrations = JSON.parse(getDataFromStdOut(output)) as Migration[]; const children = migrations.map( (migration, index) => new MigrationTreeItem( migration.name, - this.solutionFile, + this.projectFile.workspaceRoot, this.label, this.project, migration, diff --git a/src/treeView/MigrationTreeItem.ts b/src/treeView/MigrationTreeItem.ts index 51ccfda..61dec25 100644 --- a/src/treeView/MigrationTreeItem.ts +++ b/src/treeView/MigrationTreeItem.ts @@ -1,6 +1,5 @@ import * as vscode from 'vscode'; import type { Migration } from '../types/Migration'; -import type { SolutionFile } from '../types/SolutionFile'; import { getIconPath } from './iconProvider'; import { MigrationTreeItemScheme } from './MigrationTreeItemScheme'; @@ -9,13 +8,13 @@ import { TreeItem } from './TreeItem'; export class MigrationTreeItem extends TreeItem { constructor( public readonly label: string, - solutionFile: SolutionFile, + workspaceRoot: string, public readonly dbContext: string, public readonly project: string, public readonly migration: Migration, isLast: boolean, ) { - super(label, solutionFile, vscode.TreeItemCollapsibleState.None); + super(label, workspaceRoot, vscode.TreeItemCollapsibleState.None); this.iconPath = getIconPath('file-code_light.svg', 'file-code_dark.svg'); this.contextValue = 'migration-' + getMigrationContextValue(migration, isLast); diff --git a/src/treeView/ProjectTreeItem.ts b/src/treeView/ProjectTreeItem.ts index 1d70441..5d0f258 100644 --- a/src/treeView/ProjectTreeItem.ts +++ b/src/treeView/ProjectTreeItem.ts @@ -1,13 +1,13 @@ import * as vscode from 'vscode'; - +import path from 'node:path'; import type { DbContext } from '../types/DbContext'; import { DbContextTreeItem } from './DbContextTreeItem'; import { getIconPath } from './iconProvider'; import { TreeItem } from './TreeItem'; -import { execEF, extractDataFromStdOut } from '../cli/ef'; +import { execEF, getDataFromStdOut } from '../cli/ef'; import { TreeItemCache } from './TreeItemCache'; -import type { SolutionFile } from '../types/SolutionFile'; +import type { ProjectFile } from '../types/ProjectFile'; export const projectsCache = new TreeItemCache(); @@ -15,14 +15,14 @@ export class ProjectTreeItem extends TreeItem { private readonly cacheId: string; constructor( public readonly label: string, - solutionFile: SolutionFile, + private readonly projectFile: ProjectFile, collapsibleState: vscode.TreeItemCollapsibleState = vscode .TreeItemCollapsibleState.Collapsed, ) { - super(label, solutionFile, collapsibleState); + super(label, projectFile.workspaceRoot, collapsibleState); this.iconPath = getIconPath('csproj.svg'); this.cacheId = ProjectTreeItem.getCacheId( - solutionFile.workspaceRoot, + projectFile.workspaceRoot, this.label, ); } @@ -38,20 +38,23 @@ export class ProjectTreeItem extends TreeItem { return cachedChildren; } + const projectFileDir = path.resolve(path.dirname(this.projectFile.path)); + const workspaceRootDir = path.resolve(this.workspaceRoot); + const isRootProject = projectFileDir === workspaceRootDir; + const project = isRootProject ? '.' : this.label; + try { const output = await execEF( - `dbcontext list --project ${this.label} --no-color --json --prefix-output`, - this.solutionFile.workspaceRoot, + `dbcontext list --project ${project} --no-color --json --prefix-output`, + this.projectFile.workspaceRoot, ); - const dbContexts = JSON.parse( - extractDataFromStdOut(output), - ) as DbContext[]; + const dbContexts = JSON.parse(getDataFromStdOut(output)) as DbContext[]; const children = dbContexts.map( dbContext => new DbContextTreeItem( dbContext.name, - this.solutionFile, - this.label, + this.projectFile, + project, vscode.TreeItemCollapsibleState.Collapsed, ), ); diff --git a/src/treeView/SolutionTreeItem.ts b/src/treeView/SolutionTreeItem.ts index b293062..d5ebadc 100644 --- a/src/treeView/SolutionTreeItem.ts +++ b/src/treeView/SolutionTreeItem.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { getIconPath } from './iconProvider'; -import { ProjectTreeItem } from './ProjectTreeItem'; +// import { ProjectTreeItem } from './ProjectTreeItem'; import type { SolutionFile } from '../types/SolutionFile'; export class SolutionTreeItem extends vscode.TreeItem { @@ -13,15 +13,15 @@ export class SolutionTreeItem extends vscode.TreeItem { this.iconPath = getIconPath('sln.svg'); } - async getChildren(): Promise { - return Promise.resolve( - this.solutionFile.solution.projects - .filter(project => - (project.packages || []).find( - pkg => pkg.name === 'Microsoft.EntityFrameworkCore.Design', - ), - ) - .map(project => new ProjectTreeItem(project.name, this.solutionFile)), - ); - } + // async getChildren(): Promise { + // return Promise.resolve( + // this.solutionFile.solution.projects + // .filter(project => + // (project.packages || []).find( + // pkg => pkg.name === 'Microsoft.EntityFrameworkCore.Design', + // ), + // ) + // .map(project => new ProjectTreeItem(project.name, this.solutionFile)), + // ); + // } } diff --git a/src/treeView/TreeDataProvider.ts b/src/treeView/TreeDataProvider.ts index 9b17236..280d4af 100644 --- a/src/treeView/TreeDataProvider.ts +++ b/src/treeView/TreeDataProvider.ts @@ -1,9 +1,9 @@ import * as vscode from 'vscode'; import { type TreeItem } from './TreeItem'; -import { SolutionTreeItem } from './SolutionTreeItem'; import { Disposable } from '../util/Disposable'; -import type { SolutionFile } from '../types/SolutionFile'; import { EXTENSION_NAMESPACE } from '../constants/constants'; +import type { ProjectFile } from '../types/ProjectFile'; +import { ProjectTreeItem } from './ProjectTreeItem'; export class TreeDataProvider extends Disposable @@ -17,7 +17,7 @@ export class TreeDataProvider TreeItem | undefined | null | void > = this._onDidChangeTreeData.event; - constructor(private readonly solutionFiles: SolutionFile[]) { + constructor(private readonly projectFiles: ProjectFile[]) { super(); this.subscriptions.push( vscode.window.registerTreeDataProvider( @@ -39,9 +39,12 @@ export class TreeDataProvider if (element) { return element.getChildren(); } else { - return this.solutionFiles.map( - solutionFile => new SolutionTreeItem(solutionFile), + return this.projectFiles.map( + projectFile => new ProjectTreeItem(projectFile.name, projectFile), ); + // return this.solutionFiles.map( + // solutionFile => new SolutionTreeItem(solutionFile), + // ); } } } diff --git a/src/treeView/TreeItem.ts b/src/treeView/TreeItem.ts index 38ddd35..78f0c4d 100644 --- a/src/treeView/TreeItem.ts +++ b/src/treeView/TreeItem.ts @@ -1,10 +1,10 @@ import * as vscode from 'vscode'; -import type { SolutionFile } from '../types/SolutionFile'; +// import type { SolutionFile } from '../types/SolutionFile'; export abstract class TreeItem extends vscode.TreeItem { constructor( public readonly label: string, - public readonly solutionFile: SolutionFile, + public readonly workspaceRoot: string, collapsibleState?: vscode.TreeItemCollapsibleState, ) { super(label, collapsibleState); diff --git a/src/types/ProjectFile.ts b/src/types/ProjectFile.ts new file mode 100644 index 0000000..2d118c9 --- /dev/null +++ b/src/types/ProjectFile.ts @@ -0,0 +1,8 @@ +import type { Project } from 'vs-parse'; + +export type ProjectFile = { + name: string; + project: Project; + workspaceRoot: string; + path: string; +}; diff --git a/src/vs-parse.d.ts b/src/vs-parse.d.ts index f9be134..0e204f3 100644 --- a/src/vs-parse.d.ts +++ b/src/vs-parse.d.ts @@ -24,4 +24,10 @@ declare module 'vs-parse' { deepParse: boolean; }, ): Promise; + export function parseProject( + path: string, + opts?: { + deepParse: boolean; + }, + ): Promise; } From 91b9b6f6b650263a2e29d0fb27c0b9de2967bb0a Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Sat, 24 Dec 2022 17:10:12 +0000 Subject: [PATCH 4/4] Cleanup --- src/solution/ProjectFilesProvider.ts | 8 -------- src/treeView/SolutionTreeItem.ts | 16 ++++------------ src/treeView/TreeDataProvider.ts | 3 --- src/treeView/TreeItem.ts | 1 - 4 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/solution/ProjectFilesProvider.ts b/src/solution/ProjectFilesProvider.ts index b87b1b9..4a94893 100644 --- a/src/solution/ProjectFilesProvider.ts +++ b/src/solution/ProjectFilesProvider.ts @@ -29,14 +29,6 @@ export class ProjectFilesProvider { }); } } - // const solution = await parseSolution(solutionFile.path, { - // deepParse: true, - // }); - // solutionFiles.push({ - // name: path.parse(solutionFile.path).name, - // solution, - // workspaceRoot, - // }); } return projects; diff --git a/src/treeView/SolutionTreeItem.ts b/src/treeView/SolutionTreeItem.ts index d5ebadc..5046a7c 100644 --- a/src/treeView/SolutionTreeItem.ts +++ b/src/treeView/SolutionTreeItem.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; import { getIconPath } from './iconProvider'; -// import { ProjectTreeItem } from './ProjectTreeItem'; import type { SolutionFile } from '../types/SolutionFile'; +import type { ProjectTreeItem } from './ProjectTreeItem'; export class SolutionTreeItem extends vscode.TreeItem { constructor( @@ -13,15 +13,7 @@ export class SolutionTreeItem extends vscode.TreeItem { this.iconPath = getIconPath('sln.svg'); } - // async getChildren(): Promise { - // return Promise.resolve( - // this.solutionFile.solution.projects - // .filter(project => - // (project.packages || []).find( - // pkg => pkg.name === 'Microsoft.EntityFrameworkCore.Design', - // ), - // ) - // .map(project => new ProjectTreeItem(project.name, this.solutionFile)), - // ); - // } + async getChildren(): Promise { + return Promise.resolve([]); + } } diff --git a/src/treeView/TreeDataProvider.ts b/src/treeView/TreeDataProvider.ts index 280d4af..9495c23 100644 --- a/src/treeView/TreeDataProvider.ts +++ b/src/treeView/TreeDataProvider.ts @@ -42,9 +42,6 @@ export class TreeDataProvider return this.projectFiles.map( projectFile => new ProjectTreeItem(projectFile.name, projectFile), ); - // return this.solutionFiles.map( - // solutionFile => new SolutionTreeItem(solutionFile), - // ); } } } diff --git a/src/treeView/TreeItem.ts b/src/treeView/TreeItem.ts index 78f0c4d..7f38d9b 100644 --- a/src/treeView/TreeItem.ts +++ b/src/treeView/TreeItem.ts @@ -1,5 +1,4 @@ import * as vscode from 'vscode'; -// import type { SolutionFile } from '../types/SolutionFile'; export abstract class TreeItem extends vscode.TreeItem { constructor(