From 1b930e365cbc4151def3693cb0c6ffd6bccd49e4 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 21 Jan 2026 09:04:05 -0800 Subject: [PATCH 1/3] Add route search to command palette --- package.json | 13 +++++++++ src/extension.ts | 39 ++++++++++++++++++++++----- src/providers/endpointTreeProvider.ts | 19 ++++++++++++- 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index ecc4dbd..b8c840f 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,19 @@ "command": "fastapi-vscode.reportIssue", "title": "Report Issue...", "category": "FastAPI" + }, + { + "command": "fastapi-vscode.searchEndpoints", + "title": "Search Endpoints...", + "category": "FastAPI", + "icon": "$(search)" + } + ], + "keybindings": [ + { + "command": "fastapi-vscode.searchEndpoints", + "key": "ctrl+shift+e", + "mac": "cmd+shift+e" } ], "menus": { diff --git a/src/extension.ts b/src/extension.ts index 3c5c513..c8fd134 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -11,15 +11,14 @@ import type { SourceLocation } from "./core/types" import { type EndpointTreeItem, EndpointTreeProvider, + METHOD_ICONS, } from "./providers/endpointTreeProvider" import { TestCodeLensProvider } from "./providers/testCodeLensProvider" -import { vscodeFileSystem } from "./providers/vscodeFileSystem" import { disposeLogger, log } from "./utils/logger" let parserService: Parser | null = null function navigateToLocation(location: SourceLocation): void { - // filePath is now a URI string, parse it back to vscode.Uri const uri = vscode.Uri.parse(location.filePath) const position = new vscode.Position(location.line - 1, location.column) vscode.window.showTextDocument(uri, { @@ -130,10 +129,38 @@ function registerCommands( ), vscode.commands.registerCommand( - "fastapi-vscode.goToEndpoint", - (item: EndpointTreeItem) => { - if (item.type === "route") { - navigateToLocation(item.route.location) + "fastapi-vscode.searchEndpoints", + async () => { + const workspaceFolder = + vscode.workspace.workspaceFolders?.[0]?.uri.fsPath + const items = endpointProvider + .getAllRoutes() + .map((route) => { + const fullPath = vscode.Uri.parse(route.location.filePath).fsPath + const relativePath = workspaceFolder + ? fullPath.replace(workspaceFolder, "").replace(/^\//, "") + : fullPath + return { + label: `$(${METHOD_ICONS[route.method]}) ${route.method.toUpperCase()} ${stripLeadingDynamicSegments(route.path)}`, + description: route.functionName, + detail: relativePath, + route, + } + }) + .sort((a, b) => a.label.localeCompare(b.label)) + + if (items.length === 0) { + vscode.window.showInformationMessage( + "No FastAPI endpoints found in the workspace.", + ) + return + } + + const selected = await vscode.window.showQuickPick(items, { + placeHolder: "Search FastAPI endpoints...", + }) + if (selected) { + navigateToLocation(selected.route.location) } }, ), diff --git a/src/providers/endpointTreeProvider.ts b/src/providers/endpointTreeProvider.ts index 645940a..29d18a4 100644 --- a/src/providers/endpointTreeProvider.ts +++ b/src/providers/endpointTreeProvider.ts @@ -28,7 +28,7 @@ const defaultGrouping: GroupingFunction = (apps) => apps.map((app) => ({ type: "app" as const, app })) /** Method icons for route display */ -const METHOD_ICONS: Record = { +export const METHOD_ICONS: Record = { GET: "arrow-right", POST: "plus", PUT: "edit", @@ -56,6 +56,23 @@ export class EndpointTreeProvider this.groupApps = groupApps ?? defaultGrouping } + getApps(): AppDefinition[] { + return this.apps + } + + /** Returns all routes from all apps, including nested router routes. */ + getAllRoutes(): RouteDefinition[] { + const collectFromRouters = ( + routers: RouterDefinition[], + ): RouteDefinition[] => + routers.flatMap((r) => [...r.routes, ...collectFromRouters(r.children)]) + + return this.apps.flatMap((app) => [ + ...app.routes, + ...collectFromRouters(app.routers), + ]) + } + setApps(apps: AppDefinition[], groupApps?: GroupingFunction): void { this.apps = apps if (groupApps) { From 6ee56da8d63bd2d5992dfdd57bf1eb04b8025b96 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 21 Jan 2026 10:40:43 -0800 Subject: [PATCH 2/3] Simplify searchEndpoints --- src/extension.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index c8fd134..2ba97ed 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -131,23 +131,23 @@ function registerCommands( vscode.commands.registerCommand( "fastapi-vscode.searchEndpoints", async () => { - const workspaceFolder = - vscode.workspace.workspaceFolders?.[0]?.uri.fsPath + const workspacePrefix = + vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? "" const items = endpointProvider .getAllRoutes() .map((route) => { - const fullPath = vscode.Uri.parse(route.location.filePath).fsPath - const relativePath = workspaceFolder - ? fullPath.replace(workspaceFolder, "").replace(/^\//, "") - : fullPath + const path = stripLeadingDynamicSegments(route.path) return { - label: `$(${METHOD_ICONS[route.method]}) ${route.method.toUpperCase()} ${stripLeadingDynamicSegments(route.path)}`, + label: `$(${METHOD_ICONS[route.method]}) ${route.method.toUpperCase()} ${path}`, description: route.functionName, - detail: relativePath, + detail: vscode.Uri.parse(route.location.filePath) + .fsPath.replace(workspacePrefix, "") + .replace(/^\//, ""), route, + sortKey: `${path} ${route.method}`, } }) - .sort((a, b) => a.label.localeCompare(b.label)) + .sort((a, b) => a.sortKey.localeCompare(b.sortKey)) if (items.length === 0) { vscode.window.showInformationMessage( From d8210f6267e8a39ae76beb50e026da51aa668c83 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 21 Jan 2026 10:43:34 -0800 Subject: [PATCH 3/3] Add accidentally removed command --- src/extension.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/extension.ts b/src/extension.ts index 2ba97ed..dd4ca8d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -128,6 +128,15 @@ function registerCommands( }, ), + vscode.commands.registerCommand( + "fastapi-vscode.goToEndpoint", + (item: EndpointTreeItem) => { + if (item.type === "route") { + navigateToLocation(item.route.location) + } + }, + ), + vscode.commands.registerCommand( "fastapi-vscode.searchEndpoints", async () => {