Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/appDiscovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down
30 changes: 27 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<string, AppDefinition[]>())

// 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
Expand All @@ -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)
}
Expand Down