branch handling

This commit is contained in:
bkellam 2025-11-19 18:35:03 -08:00
parent 898c9097db
commit 696d06beeb
7 changed files with 33 additions and 20 deletions

View file

@ -233,7 +233,7 @@ export const PathHeader = ({
}} }}
> >
<span className="mr-0.5">@</span> <span className="mr-0.5">@</span>
{`${branchDisplayName}`} {`${branchDisplayName.replace(/^refs\/(heads|tags)\//, '')}`}
</p> </p>
)} )}
<span>·</span> <span>·</span>

View file

@ -12,6 +12,7 @@ import {
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { RepositoryInfo, SearchResultFile, SearchStats } from "@/features/search/types"; import { RepositoryInfo, SearchResultFile, SearchStats } from "@/features/search/types";
import useCaptureEvent from "@/hooks/useCaptureEvent";
import { useDomain } from "@/hooks/useDomain"; import { useDomain } from "@/hooks/useDomain";
import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam"; import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam";
import { useSearchHistory } from "@/hooks/useSearchHistory"; import { useSearchHistory } from "@/hooks/useSearchHistory";
@ -32,7 +33,6 @@ import { CodePreviewPanel } from "./codePreviewPanel";
import { FilterPanel } from "./filterPanel"; import { FilterPanel } from "./filterPanel";
import { useFilteredMatches } from "./filterPanel/useFilterMatches"; import { useFilteredMatches } from "./filterPanel/useFilterMatches";
import { SearchResultsPanel, SearchResultsPanelHandle } from "./searchResultsPanel"; import { SearchResultsPanel, SearchResultsPanelHandle } from "./searchResultsPanel";
import useCaptureEvent from "@/hooks/useCaptureEvent";
interface SearchResultsPageProps { interface SearchResultsPageProps {
searchQuery: string; searchQuery: string;
@ -156,6 +156,13 @@ export const SearchResultsPage = ({
router.push(url); router.push(url);
}, [maxMatchCount, router, searchQuery, domain]); }, [maxMatchCount, router, searchQuery, domain]);
// Look for any files that are not on the default branch.
const isBranchFilteringEnabled = useMemo(() => {
return files.some((file) => {
return file.branches?.some((branch) => branch !== 'HEAD') ?? false;
});
}, [files]);
return ( return (
<div className="flex flex-col h-screen overflow-clip"> <div className="flex flex-col h-screen overflow-clip">
{/* TopBar */} {/* TopBar */}
@ -189,8 +196,7 @@ export const SearchResultsPage = ({
isStreaming={isStreaming} isStreaming={isStreaming}
searchStats={stats} searchStats={stats}
isMoreResultsButtonVisible={!isExhaustive} isMoreResultsButtonVisible={!isExhaustive}
// @todo: handle branch filtering isBranchFilteringEnabled={isBranchFilteringEnabled}
isBranchFilteringEnabled={false}
/> />
)} )}
</div> </div>

View file

@ -75,7 +75,7 @@ export const FileMatchContainer = ({
} }
return `${branches[0]}${branches.length > 1 ? ` +${branches.length - 1}` : ''}`; return `${branches[0]}${branches.length > 1 ? ` +${branches.length - 1}` : ''}`;
}, [isBranchFilteringEnabled, branches]); }, [branches, isBranchFilteringEnabled]);
const repo = useMemo(() => { const repo = useMemo(() => {
return repoInfo[file.repositoryId]; return repoInfo[file.repositoryId];

View file

@ -88,7 +88,7 @@ export const SearchResultsPanel = forwardRef<SearchResultsPanelHandle, SearchRes
const resetScroll = useCallback(() => { const resetScroll = useCallback(() => {
virtualizer.scrollToIndex(0); virtualizer.scrollToIndex(0);
}, [fileMatches.length, virtualizer]); }, [virtualizer]);
// Expose the resetScroll function to parent components // Expose the resetScroll function to parent components
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
@ -121,7 +121,7 @@ export const SearchResultsPanel = forwardRef<SearchResultsPanelHandle, SearchRes
align: 'start' align: 'start'
}); });
} }
}, [showAllMatchesMap, virtualizer]); }, [showAllMatchesActions, showAllMatchesMap, virtualizer]);
return ( return (

View file

@ -33,7 +33,6 @@ const isCacheValid = (entry: CacheEntry): boolean => {
}; };
export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegexEnabled, isCaseSensitivityEnabled }: SearchRequest) => { export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegexEnabled, isCaseSensitivityEnabled }: SearchRequest) => {
const [state, setState] = useState<{ const [state, setState] = useState<{
isStreaming: boolean, isStreaming: boolean,
isExhaustive: boolean, isExhaustive: boolean,

View file

@ -61,7 +61,6 @@ export const transformLezerTreeToZoektGrpcQuery = async ({
isRegexEnabled: boolean; isRegexEnabled: boolean;
onExpandSearchContext: (contextName: string) => Promise<string[]>; onExpandSearchContext: (contextName: string) => Promise<string[]>;
}): Promise<ZoektGrpcQuery> => { }): Promise<ZoektGrpcQuery> => {
const transformNode = async (node: SyntaxNode): Promise<ZoektGrpcQuery> => { const transformNode = async (node: SyntaxNode): Promise<ZoektGrpcQuery> => {
switch (node.type.id) { switch (node.type.id) {
case Program: { case Program: {
@ -336,12 +335,8 @@ const getChildren = (node: SyntaxNode): SyntaxNode[] => {
const children: SyntaxNode[] = []; const children: SyntaxNode[] = [];
let child = node.firstChild; let child = node.firstChild;
while (child) { while (child) {
// Skip certain node types that are just structural
if (!["(", ")", "or"].includes(child.type.name)) {
children.push(child); children.push(child);
}
child = child.nextSibling; child = child.nextSibling;
} }
return children; return children;
} }

View file

@ -18,6 +18,7 @@ import path from 'path';
import { parseQueryIntoLezerTree, transformLezerTreeToZoektGrpcQuery } from './query'; import { parseQueryIntoLezerTree, transformLezerTreeToZoektGrpcQuery } from './query';
import { RepositoryInfo, SearchRequest, SearchResponse, SearchResultFile, SearchStats, SourceRange, StreamedSearchResponse } from "./types"; import { RepositoryInfo, SearchRequest, SearchResponse, SearchResultFile, SearchStats, SourceRange, StreamedSearchResponse } from "./types";
import { FlushReason as ZoektFlushReason } from "@/proto/zoekt/webserver/v1/FlushReason"; import { FlushReason as ZoektFlushReason } from "@/proto/zoekt/webserver/v1/FlushReason";
import { RevisionExpr } from "@sourcebot/query-language";
const logger = createLogger("searchApi"); const logger = createLogger("searchApi");
@ -454,18 +455,30 @@ const createZoektSearchRequest = async ({
}, },
}); });
// Find if there are any `rev:` filters in the query.
let containsRevExpression = false;
tree.iterate({
enter: (node) => {
if (node.type.id === RevisionExpr) {
containsRevExpression = true;
// false to stop the iteration.
return false;
}
}
});
const zoektSearchRequest: ZoektGrpcSearchRequest = { const zoektSearchRequest: ZoektGrpcSearchRequest = {
query: { query: {
and: { and: {
children: [ children: [
zoektQuery, zoektQuery,
// @todo: handle branch filtering. // If the query does not contain a `rev:` filter, we default to searching `HEAD`.
{ ...(!containsRevExpression ? [{
branch: { branch: {
pattern: 'HEAD', pattern: 'HEAD',
exact: true, exact: true,
} }
} }] : []),
] ]
} }
}, },