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

This commit is contained in:
kvyb
2025-08-20 22:30:30 +08:00
committed by GitHub
parent 89ec9c4277
commit 1997ee3e80
4 changed files with 98 additions and 26 deletions
+12 -2
View File
@@ -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
View File
@@ -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 {}
+27 -24
View File
@@ -1,8 +1,9 @@
import { searchWorkspaceFiles } from "@services/search/file-search"
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 { FileSearchRequest, FileSearchResults, FileSearchType } from "@shared/proto/cline/file"
import { getWorkspacePath } from "@utils/path"
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,
@@ -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),
})),
})
}