diff --git a/src/appDiscovery.ts b/src/appDiscovery.ts index f225adc..d7b42cf 100644 --- a/src/appDiscovery.ts +++ b/src/appDiscovery.ts @@ -62,15 +62,18 @@ async function parsePyprojectForEntryPoint( return null } - // Parse "my_app.main:app" format (variable name after : is optional) + // Parse "my_app.main:app" or "api.py:app" format (variable name after : is optional) const colonIndex = entrypointValue.indexOf(":") const modulePath = colonIndex === -1 ? entrypointValue : entrypointValue.slice(0, colonIndex) const variableName = colonIndex === -1 ? undefined : entrypointValue.slice(colonIndex + 1) - // Convert module path to file path: my_app.main -> my_app/main.py - const relativePath = `${modulePath.replace(/\./g, "/")}.py` + // Handle both module format (api.module) and file format (api.py) + const relativePath = + modulePath.endsWith(".py") && !modulePath.includes("/") + ? modulePath // Simple file path: api.py -> api.py + : `${modulePath.replace(/\./g, "/")}.py` // Module path: my_app.main -> my_app/main.py const fullUri = vscode.Uri.joinPath(folderUri, relativePath) return (await vscodeFileSystem.exists(fullUri.toString())) diff --git a/src/extension.ts b/src/extension.ts index dd4ca8d..cd8cb30 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,7 +7,7 @@ import { discoverFastAPIApps } from "./appDiscovery" import { clearImportCache } from "./core/importResolver" import { Parser } from "./core/parser" import { stripLeadingDynamicSegments } from "./core/pathUtils" -import type { SourceLocation } from "./core/types" +import type { AppDefinition, SourceLocation } from "./core/types" import { type EndpointTreeItem, EndpointTreeProvider, @@ -63,7 +63,31 @@ export async function activate(context: vscode.ExtensionContext) { // Discover apps and create providers const apps = await discoverFastAPIApps(parserService) - const endpointProvider = new EndpointTreeProvider(apps) + + // Create grouping function that groups by workspace folder if there are multiple folders + const groupApps = (apps: AppDefinition[]) => { + const workspaceFolders = vscode.workspace.workspaceFolders + if (!workspaceFolders || workspaceFolders.length <= 1) { + // Single workspace folder: show apps directly at root + return apps.map((app) => ({ type: "app" as const, app })) + } + + // Multi-root workspace: group by workspace folder + const grouped = apps.reduce((acc, app) => { + const existing = acc.get(app.workspaceFolder) ?? [] + acc.set(app.workspaceFolder, [...existing, app]) + return acc + }, new Map()) + + // Create workspace items with folder names + return Array.from(grouped.entries()).map(([folderPath, apps]) => { + const folder = workspaceFolders.find((f) => f.uri.fsPath === folderPath) + const label = folder?.name ?? folderPath.split("/").pop() ?? folderPath + return { type: "workspace" as const, label, apps } + }) + } + + const endpointProvider = new EndpointTreeProvider(apps, groupApps) const codeLensProvider = new TestCodeLensProvider(parserService, apps) // File watcher for auto-refresh @@ -73,7 +97,7 @@ export async function activate(context: vscode.ExtensionContext) { refreshTimeout = setTimeout(async () => { if (!parserService) return const newApps = await discoverFastAPIApps(parserService) - endpointProvider.setApps(newApps) + endpointProvider.setApps(newApps, groupApps) codeLensProvider.setApps(newApps) }, 300) }