Prechádzať zdrojové kódy

refactor to hostbridge: route @mentions search via WorkspaceService.searchWorkspaceItems (#5655)

kvyb 5 mesiacov pred
rodič
commit
1997ee3e80

+ 12 - 2
knip.json

@@ -7,7 +7,17 @@
 		"src/generated/hosts/vscode/protobus-services.ts",
 		"src/generated/hosts/vscode/hostbridge-grpc-service-config.ts"
 	],
-	"project": ["src/**/*.ts"],
-	"ignore": ["out/**", "node_modules/**", "*.d.ts", "**/*.test.ts", "**/__tests__", "src/test/**", "src/shared/**"],
+	"project": [
+		"src/**/*.ts"
+	],
+	"ignore": [
+		"out/**",
+		"node_modules/**",
+		"*.d.ts",
+		"**/*.test.ts",
+		"**/__tests__",
+		"src/test/**",
+		"src/shared/**"
+	],
 	"vite": true
 }

+ 24 - 0
proto/host/workspace.proto

@@ -18,6 +18,8 @@ service WorkspaceService {
 
   // Get diagnostics from the workspace.
   rpc getDiagnostics(GetDiagnosticsRequest) returns (GetDiagnosticsResponse);
+  // Returns workspace items (files/folders) matching a query for mention autocomplete
+  rpc searchWorkspaceItems(SearchWorkspaceItemsRequest) returns (SearchWorkspaceItemsResponse);
 
   // Makes the problems panel/pane visible in the IDE and focuses it.
   rpc openProblemsPanel(OpenProblemsPanelRequest) returns (OpenProblemsPanelResponse);
@@ -55,6 +57,28 @@ message GetDiagnosticsResponse {
   repeated cline.FileDiagnostics file_diagnostics = 1;
 }
 
+// Request for host-side workspace search (files/folders) used by mentions autocomplete
+message SearchWorkspaceItemsRequest {
+  string query = 1;                    // Search query string
+  optional int32 limit = 2;            // Optional limit for results (default decided by host)
+  // Optional selected type filter
+  enum SearchItemType {
+    FILE = 0;
+    FOLDER = 1;
+  }
+  optional SearchItemType selected_type = 3;
+}
+
+// Response for host-side workspace search
+message SearchWorkspaceItemsResponse {
+  message SearchItem {
+    string path = 1;                    // Workspace-relative path using platform separators
+    SearchWorkspaceItemsRequest.SearchItemType type = 2;
+    optional string label = 3;          // Optional display label (e.g., basename)
+  }
+  repeated SearchItem items = 1;
+}
+
 message OpenProblemsPanelRequest {}
 message OpenProblemsPanelResponse {}
 

+ 26 - 23
src/core/controller/file/searchFiles.ts

@@ -1,8 +1,9 @@
-import { searchWorkspaceFiles } from "@services/search/file-search"
+import { Controller } from ".."
 import { FileSearchRequest, FileSearchResults, FileSearchType } from "@shared/proto/cline/file"
-import { convertSearchResultsToProtoFileInfos } from "@shared/proto-conversions/file/search-result-conversion"
 import { getWorkspacePath } from "@utils/path"
-import { Controller } from ".."
+import { convertSearchResultsToProtoFileInfos } from "@shared/proto-conversions/file/search-result-conversion"
+import { HostProvider } from "@/hosts/host-provider"
+import { SearchWorkspaceItemsRequest_SearchItemType } from "@shared/proto/host/workspace"
 
 /**
  * Searches for files in the workspace with fuzzy matching
@@ -23,35 +24,37 @@ export async function searchFiles(_controller: Controller, request: FileSearchRe
 	}
 
 	try {
-		// Map enum to string for the search service
-		let selectedTypeString: "file" | "folder" | undefined
-		if (request.selectedType === FileSearchType.FILE) {
-			selectedTypeString = "file"
-		} else if (request.selectedType === FileSearchType.FOLDER) {
-			selectedTypeString = "folder"
-		}
+		// Map enum to host SearchItemType (0 = FILE, 1 = FOLDER)
+		const selectedTypeValue: SearchWorkspaceItemsRequest_SearchItemType | undefined =
+			request.selectedType === FileSearchType.FILE
+				? SearchWorkspaceItemsRequest_SearchItemType.FILE
+				: request.selectedType === FileSearchType.FOLDER
+					? SearchWorkspaceItemsRequest_SearchItemType.FOLDER
+					: undefined
 
-		// Call file search service with query from request
-		const searchResults = await searchWorkspaceFiles(
-			request.query || "",
-			workspacePath,
-			request.limit || 20, // Use default limit of 20 if not specified
-			selectedTypeString,
+		// Use host-provided search via hostbridge (no fallback)
+		const hostResponse = await HostProvider.workspace.searchWorkspaceItems({
+			query: request.query || "",
+			limit: request.limit || 20,
+			selectedType: selectedTypeValue,
+		})
+
+		const mapped: { path: string; type: "file" | "folder"; label?: string }[] = (hostResponse.items || []).map(
+			(item: { path?: string; type: SearchWorkspaceItemsRequest_SearchItemType; label?: string }) => ({
+				path: String(item.path || ""),
+				type: item.type === SearchWorkspaceItemsRequest_SearchItemType.FOLDER ? "folder" : "file",
+				label: item.label || undefined,
+			}),
 		)
 
-		// Convert search results to proto FileInfo objects using the conversion function
-		const protoResults = convertSearchResultsToProtoFileInfos(searchResults)
+		const protoResults = convertSearchResultsToProtoFileInfos(mapped)
 
-		// Return successful results
 		return FileSearchResults.create({
 			results: protoResults,
 			mentionsRequestId: request.mentionsRequestId,
 		})
 	} catch (error) {
-		// Log the error but don't include it in the response, following the pattern in searchCommits
-		console.error("Error in searchFiles:", error instanceof Error ? error.message : String(error))
-
-		// Return empty results without error message
+		console.error("Error in host searchWorkspaceItems:", error instanceof Error ? error.message : String(error))
 		return FileSearchResults.create({
 			results: [],
 			mentionsRequestId: request.mentionsRequestId,

+ 35 - 0
src/hosts/vscode/hostbridge/workspace/searchWorkspaceItems.ts

@@ -0,0 +1,35 @@
+import * as vscode from "vscode"
+import { basename } from "path"
+import {
+	SearchWorkspaceItemsRequest,
+	SearchWorkspaceItemsResponse,
+	SearchWorkspaceItemsRequest_SearchItemType,
+} from "@/shared/proto/index.host"
+import { searchWorkspaceFiles } from "@/services/search/file-search"
+
+export async function searchWorkspaceItems(request: SearchWorkspaceItemsRequest): Promise<SearchWorkspaceItemsResponse> {
+	const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath
+	if (!workspacePath) {
+		return SearchWorkspaceItemsResponse.create({ items: [] })
+	}
+
+	let selectedTypeString: "file" | "folder" | undefined = undefined
+	if (request.selectedType === SearchWorkspaceItemsRequest_SearchItemType.FILE) {
+		selectedTypeString = "file"
+	} else if (request.selectedType === SearchWorkspaceItemsRequest_SearchItemType.FOLDER) {
+		selectedTypeString = "folder"
+	}
+
+	const results = await searchWorkspaceFiles(request.query || "", workspacePath, request.limit || 20, selectedTypeString)
+
+	return SearchWorkspaceItemsResponse.create({
+		items: results.map((r) => ({
+			path: r.path,
+			type:
+				r.type === "folder"
+					? SearchWorkspaceItemsRequest_SearchItemType.FOLDER
+					: SearchWorkspaceItemsRequest_SearchItemType.FILE,
+			label: r.label ?? basename(r.path),
+		})),
+	})
+}