diff --git a/packages/web/src/features/search/searchApi.ts b/packages/web/src/features/search/searchApi.ts index b6630f7d..696d3a10 100644 --- a/packages/web/src/features/search/searchApi.ts +++ b/packages/web/src/features/search/searchApi.ts @@ -19,6 +19,7 @@ import { parseQueryIntoLezerTree, transformLezerTreeToZoektGrpcQuery } from './q import { RepositoryInfo, SearchRequest, SearchResponse, SearchResultFile, SearchStats, SourceRange, StreamedSearchResponse } from "./types"; import { FlushReason as ZoektFlushReason } from "@/proto/zoekt/webserver/v1/FlushReason"; import { RevisionExpr } from "@sourcebot/query-language"; +import { getCodeHostBrowseFileAtBranchUrl } from "@/lib/utils"; const logger = createLogger("searchApi"); @@ -29,7 +30,7 @@ export const search = (searchRequest: SearchRequest) => sew(() => prisma, }); - console.debug('zoektSearchRequest:', JSON.stringify(zoektSearchRequest, null, 2)); + logger.debug('zoektSearchRequest:', JSON.stringify(zoektSearchRequest, null, 2)); return zoektSearch(zoektSearchRequest, prisma); })); @@ -41,6 +42,8 @@ export const streamSearch = (searchRequest: SearchRequest) => sew(() => prisma, }); + logger.debug('zoektStreamSearchRequest:', JSON.stringify(zoektSearchRequest, null, 2)); + return zoektStreamSearch(zoektSearchRequest, prisma); })); @@ -304,13 +307,13 @@ const transformZoektSearchResponse = async (response: ZoektGrpcSearchResponse, r const convertRange = (range: ZoektGrpcRange): SourceRange => ({ start: { byteOffset: range.start?.byte_offset ?? 0, - column: range.start?.column ?? 0, - lineNumber: range.start?.line_number ?? 0, + column: range.start?.column ?? 1, + lineNumber: range.start?.line_number ?? 1, }, end: { byteOffset: range.end?.byte_offset ?? 0, - column: range.end?.column ?? 0, - lineNumber: range.end?.line_number ?? 0, + column: range.end?.column ?? 1, + lineNumber: range.end?.line_number ?? 1, } }) @@ -322,8 +325,13 @@ const transformZoektSearchResponse = async (response: ZoektGrpcSearchResponse, r repository: repo.name, repositoryId: repo.id, language: file.language, - // @todo: we will need to have a mechanism of forming the file's web url. - webUrl: '', + webUrl: getCodeHostBrowseFileAtBranchUrl({ + webUrl: repo.webUrl, + codeHostType: repo.external_codeHostType, + // If a file has multiple branches, default to the first one. + branchName: file.branches?.[0] ?? 'HEAD', + filePath: fileName, + }), chunks: file.chunk_matches .filter((chunk) => !chunk.file_name) // filter out filename chunks. .map((chunk) => { @@ -336,8 +344,8 @@ const transformZoektSearchResponse = async (response: ZoektGrpcSearchResponse, r lineNumber: chunk.content_start.line_number, } : { byteOffset: 0, - column: 0, - lineNumber: 0, + column: 1, + lineNumber: 1, }, symbols: chunk.symbol_info.map((symbol) => { return { diff --git a/packages/web/src/lib/utils.ts b/packages/web/src/lib/utils.ts index 0d76eb3b..ea229977 100644 --- a/packages/web/src/lib/utils.ts +++ b/packages/web/src/lib/utils.ts @@ -376,6 +376,42 @@ export const getCodeHostBrowseAtBranchUrl = ({ } } +export const getCodeHostBrowseFileAtBranchUrl = ({ + webUrl, + codeHostType, + branchName, + filePath, +}: { + webUrl?: string | null, + codeHostType: CodeHostType, + branchName: string, + filePath: string, +}) => { + if (!webUrl) { + return undefined; + } + + switch (codeHostType) { + case 'github': + return `${webUrl}/blob/${branchName}/${filePath}`; + case 'gitlab': + return `${webUrl}/-/blob/${branchName}/${filePath}`; + case 'gitea': + return `${webUrl}/src/branch/${branchName}/${filePath}`; + case 'azuredevops': + return `${webUrl}?path=${filePath}&version=${branchName}`; + case 'bitbucketCloud': + return `${webUrl}/src/${branchName}/${filePath}`; + case 'bitbucketServer': + return `${webUrl}/browse/${filePath}?at=${branchName}`; + case 'gerrit': + return `${webUrl}/+/${branchName}/${filePath}`; + case 'genericGitHost': + return undefined; + + } +} + export const isAuthSupportedForCodeHost = (codeHostType: CodeHostType): boolean => { switch (codeHostType) { case "github":