support search contexts

This commit is contained in:
bkellam 2025-11-17 18:47:13 -08:00
parent e6859cbf5c
commit 307e17e8d6
7 changed files with 92 additions and 27 deletions

View file

@ -1,6 +1,6 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
export const
negate = 21,
negate = 22,
Program = 1,
OrExpr = 2,
AndExpr = 3,
@ -9,12 +9,13 @@ export const
ArchivedExpr = 6,
RevisionExpr = 7,
ContentExpr = 8,
FileExpr = 9,
ForkExpr = 10,
VisibilityExpr = 11,
RepoExpr = 12,
LangExpr = 13,
SymExpr = 14,
RepoSetExpr = 15,
ParenExpr = 16,
Term = 17
ContextExpr = 9,
FileExpr = 10,
ForkExpr = 11,
VisibilityExpr = 12,
RepoExpr = 13,
LangExpr = 14,
SymExpr = 15,
RepoSetExpr = 16,
ParenExpr = 17,
Term = 18

File diff suppressed because one or more lines are too long

View file

@ -35,6 +35,7 @@ PrefixExpr {
ArchivedExpr |
RevisionExpr |
ContentExpr |
ContextExpr |
FileExpr |
ForkExpr |
VisibilityExpr |
@ -46,6 +47,7 @@ PrefixExpr {
RevisionExpr { revisionKw value }
ContentExpr { contentKw value }
ContextExpr { contextKw value }
FileExpr { fileKw value }
RepoExpr { repoKw value }
LangExpr { langKw value }
@ -71,6 +73,7 @@ value { quotedString | word }
archivedKw { "archived:" }
revisionKw { "rev:" }
contentKw { "content:" | "c:" }
contextKw { "context:" }
fileKw { "file:" | "f:" }
forkKw { "fork:" }
visibilityKw { "visibility:" }
@ -91,7 +94,7 @@ value { quotedString | word }
@precedence {
quotedString,
archivedKw, revisionKw, contentKw, fileKw,
archivedKw, revisionKw, contentKw, contextKw, fileKw,
forkKw, visibilityKw, repoKw, langKw,
symKw, reposetKw, or,
word

View file

@ -94,6 +94,14 @@ Program(NegateExpr(PrefixExpr(ForkExpr)))
Program(NegateExpr(PrefixExpr(VisibilityExpr)))
# Negate context prefix
-context:backend
==>
Program(NegateExpr(PrefixExpr(ContextExpr)))
# Negate symbol prefix
-sym:OldClass

View file

@ -102,6 +102,14 @@ visibility:public
Program(PrefixExpr(VisibilityExpr))
# Context prefix
context:web
==>
Program(PrefixExpr(ContextExpr))
# Symbol prefix
sym:MyClass
@ -302,6 +310,14 @@ content:@Component
Program(PrefixExpr(ContentExpr))
# Context with underscores
context:data_engineering
==>
Program(PrefixExpr(ContextExpr))
# Prefix in parentheses
(file:test.js)

View file

@ -18,6 +18,7 @@ import { NextRequest } from 'next/server';
import * as path from 'path';
import { parser as _parser } from '@sourcebot/query-language';
import { transformToZoektQuery } from './transformer';
import { SINGLE_TENANT_ORG_ID } from '@/lib/constants';
const logger = createLogger('streamSearchApi');
@ -78,14 +79,33 @@ export const POST = async (request: NextRequest) => {
const parser = _parser.configure({
strict: true,
})
});
const tree = parser.parse(query);
const zoektQuery = transformToZoektQuery({
const zoektQuery = await transformToZoektQuery({
tree,
input: query,
isCaseSensitivityEnabled,
isRegexEnabled,
onExpandSearchContext: async (contextName: string) => {
const context = await prisma.searchContext.findUnique({
where: {
name_orgId: {
name: contextName,
orgId: SINGLE_TENANT_ORG_ID,
}
},
include: {
repos: true,
}
});
if (!context) {
throw new Error(`Search context "${contextName}" not found`);
}
return context.repos.map((repo) => repo.name);
},
});
console.log(JSON.stringify(zoektQuery, null, 2));

View file

@ -12,6 +12,7 @@ import {
RepoExpr,
RevisionExpr,
ContentExpr,
ContextExpr,
LangExpr,
SymExpr,
ArchivedExpr,
@ -44,14 +45,16 @@ export const transformToZoektQuery = ({
input,
isCaseSensitivityEnabled,
isRegexEnabled,
onExpandSearchContext,
}: {
tree: Tree;
input: string;
isCaseSensitivityEnabled: boolean;
isRegexEnabled: boolean;
}): Q => {
onExpandSearchContext: (contextName: string) => Promise<string[]>;
}): Promise<Q> => {
const transformNode = (node: SyntaxNode): Q => {
const transformNode = async (node: SyntaxNode): Promise<Q> => {
switch (node.type.id) {
case Program: {
// Program wraps the actual query - transform its child
@ -65,7 +68,7 @@ export const transformToZoektQuery = ({
case AndExpr:
return {
and: {
children: getChildren(node).map(c => transformNode(c))
children: await Promise.all(getChildren(node).map(c => transformNode(c)))
},
query: "and"
}
@ -73,7 +76,7 @@ export const transformToZoektQuery = ({
case OrExpr:
return {
or: {
children: getChildren(node).map(c => transformNode(c))
children: await Promise.all(getChildren(node).map(c => transformNode(c)))
},
query: "or"
};
@ -86,7 +89,7 @@ export const transformToZoektQuery = ({
}
return {
not: {
child: transformNode(negateChild)
child: await transformNode(negateChild)
},
query: "not"
};
@ -130,7 +133,7 @@ export const transformToZoektQuery = ({
}
}
const transformPrefixExpr = (node: SyntaxNode): Q => {
const transformPrefixExpr = async (node: SyntaxNode): Promise<Q> => {
// Find which specific prefix type this is
const prefixNode = node.firstChild;
if (!prefixNode) {
@ -189,6 +192,7 @@ export const transformToZoektQuery = ({
query: "substring"
};
case LangExpr:
return {
language: {
@ -287,6 +291,19 @@ export const transformToZoektQuery = ({
};
}
case ContextExpr: {
const repoNames = await onExpandSearchContext(value);
return {
repo_set: {
set: repoNames.reduce((acc, s) => {
acc[s.trim()] = true;
return acc;
}, {} as Record<string, boolean>)
},
query: "repo_set"
};
}
case RepoSetExpr: {
return {
repo_set: {