mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 04:15:30 +00:00
Improved type safety / cleanup for query lang
This commit is contained in:
parent
69e0a1a101
commit
e6859cbf5c
10 changed files with 156 additions and 110 deletions
File diff suppressed because one or more lines are too long
|
|
@ -53,10 +53,13 @@ SymExpr { symKw value }
|
|||
RepoSetExpr { reposetKw value }
|
||||
|
||||
// Modifiers
|
||||
ArchivedExpr { archivedKw value }
|
||||
ForkExpr { forkKw value }
|
||||
VisibilityExpr { visibilityKw value }
|
||||
ArchivedExpr { archivedKw archivedValue }
|
||||
ForkExpr { forkKw forkValue }
|
||||
VisibilityExpr { visibilityKw visibilityValue }
|
||||
|
||||
archivedValue { "yes" | "no" | "only" }
|
||||
forkValue { "yes" | "no" | "only" }
|
||||
visibilityValue { "public" | "private" | "any" }
|
||||
|
||||
Term { quotedString | word }
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ lang:typescript
|
|||
|
||||
Program(PrefixExpr(LangExpr))
|
||||
|
||||
# Archived prefix
|
||||
# Archived prefix - no
|
||||
|
||||
archived:no
|
||||
|
||||
|
|
@ -70,7 +70,15 @@ archived:no
|
|||
|
||||
Program(PrefixExpr(ArchivedExpr))
|
||||
|
||||
# Fork prefix
|
||||
# Archived prefix - only
|
||||
|
||||
archived:only
|
||||
|
||||
==>
|
||||
|
||||
Program(PrefixExpr(ArchivedExpr))
|
||||
|
||||
# Fork prefix - yes
|
||||
|
||||
fork:yes
|
||||
|
||||
|
|
@ -78,6 +86,14 @@ fork:yes
|
|||
|
||||
Program(PrefixExpr(ForkExpr))
|
||||
|
||||
# Fork prefix - only
|
||||
|
||||
fork:only
|
||||
|
||||
==>
|
||||
|
||||
Program(PrefixExpr(ForkExpr))
|
||||
|
||||
# Visibility prefix - public
|
||||
|
||||
visibility:public
|
||||
|
|
@ -206,7 +222,7 @@ lang:python
|
|||
|
||||
Program(PrefixExpr(LangExpr))
|
||||
|
||||
# Archived values
|
||||
# Archived prefix - yes
|
||||
|
||||
archived:yes
|
||||
|
||||
|
|
@ -214,7 +230,15 @@ archived:yes
|
|||
|
||||
Program(PrefixExpr(ArchivedExpr))
|
||||
|
||||
# Fork values
|
||||
# Archived prefix - invalid value (error case)
|
||||
|
||||
archived:invalid
|
||||
|
||||
==>
|
||||
|
||||
Program(AndExpr(PrefixExpr(ArchivedExpr(⚠)),Term))
|
||||
|
||||
# Fork prefix - no
|
||||
|
||||
fork:no
|
||||
|
||||
|
|
@ -222,6 +246,14 @@ fork:no
|
|||
|
||||
Program(PrefixExpr(ForkExpr))
|
||||
|
||||
# Fork prefix - invalid value (error case)
|
||||
|
||||
fork:invalid
|
||||
|
||||
==>
|
||||
|
||||
Program(AndExpr(PrefixExpr(ForkExpr(⚠)),Term))
|
||||
|
||||
# Visibility prefix - private
|
||||
|
||||
visibility:private
|
||||
|
|
@ -230,6 +262,22 @@ visibility:private
|
|||
|
||||
Program(PrefixExpr(VisibilityExpr))
|
||||
|
||||
# Visibility prefix - any
|
||||
|
||||
visibility:any
|
||||
|
||||
==>
|
||||
|
||||
Program(PrefixExpr(VisibilityExpr))
|
||||
|
||||
# Visibility prefix - invalid value (error case)
|
||||
|
||||
visibility:invalid
|
||||
|
||||
==>
|
||||
|
||||
Program(AndExpr(PrefixExpr(VisibilityExpr(⚠)),Term))
|
||||
|
||||
# File with dashes
|
||||
|
||||
file:my-component.tsx
|
||||
|
|
|
|||
|
|
@ -16,57 +16,53 @@ export enum SearchPrefix {
|
|||
sym = "sym:",
|
||||
content = "content:",
|
||||
archived = "archived:",
|
||||
case = "case:",
|
||||
fork = "fork:",
|
||||
public = "public:",
|
||||
visibility = "visibility:",
|
||||
context = "context:",
|
||||
}
|
||||
|
||||
export const publicModeSuggestions: Suggestion[] = [
|
||||
export const visibilityModeSuggestions: Suggestion[] = [
|
||||
{
|
||||
value: "yes",
|
||||
value: "public",
|
||||
description: "Only include results from public repositories."
|
||||
},
|
||||
{
|
||||
value: "no",
|
||||
value: "private",
|
||||
description: "Only include results from private repositories."
|
||||
},
|
||||
{
|
||||
value: "any",
|
||||
description: "Include results from both public and private repositories (default)."
|
||||
},
|
||||
];
|
||||
|
||||
export const forkModeSuggestions: Suggestion[] = [
|
||||
{
|
||||
value: "yes",
|
||||
description: "Include results from forked repositories (default)."
|
||||
},
|
||||
{
|
||||
value: "no",
|
||||
description: "Exclude results from forked repositories."
|
||||
},
|
||||
{
|
||||
value: "only",
|
||||
description: "Only include results from forked repositories."
|
||||
},
|
||||
{
|
||||
value: "no",
|
||||
description: "Only include results from non-forked repositories."
|
||||
},
|
||||
];
|
||||
|
||||
export const caseModeSuggestions: Suggestion[] = [
|
||||
{
|
||||
value: "auto",
|
||||
description: "Search patterns are case-insensitive if all characters are lowercase, and case sensitive otherwise (default)."
|
||||
},
|
||||
{
|
||||
value: "yes",
|
||||
description: "Case sensitive search."
|
||||
},
|
||||
{
|
||||
value: "no",
|
||||
description: "Case insensitive search."
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
export const archivedModeSuggestions: Suggestion[] = [
|
||||
{
|
||||
value: "yes",
|
||||
description: "Only include results in archived repositories."
|
||||
description: "Include results from archived repositories (default)."
|
||||
},
|
||||
{
|
||||
value: "no",
|
||||
description: "Only include results in non-archived repositories."
|
||||
description: "Exclude results from archived repositories."
|
||||
},
|
||||
{
|
||||
value: "only",
|
||||
description: "Only include results from archived repositories."
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ import Fuse from "fuse.js";
|
|||
import { forwardRef, Ref, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
archivedModeSuggestions,
|
||||
caseModeSuggestions,
|
||||
forkModeSuggestions,
|
||||
publicModeSuggestions,
|
||||
visibilityModeSuggestions,
|
||||
} from "./constants";
|
||||
import { IconType } from "react-icons/lib";
|
||||
import { VscFile, VscFilter, VscRepo, VscSymbolMisc } from "react-icons/vsc";
|
||||
|
|
@ -32,9 +31,8 @@ export type SuggestionMode =
|
|||
"archived" |
|
||||
"file" |
|
||||
"language" |
|
||||
"case" |
|
||||
"fork" |
|
||||
"public" |
|
||||
"visibility" |
|
||||
"revision" |
|
||||
"symbol" |
|
||||
"content" |
|
||||
|
|
@ -137,9 +135,9 @@ const SearchSuggestionsBox = forwardRef(({
|
|||
DefaultIcon?: IconType
|
||||
} => {
|
||||
switch (suggestionMode) {
|
||||
case "public":
|
||||
case "visibility":
|
||||
return {
|
||||
list: publicModeSuggestions,
|
||||
list: visibilityModeSuggestions,
|
||||
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||
}
|
||||
case "fork":
|
||||
|
|
@ -147,11 +145,6 @@ const SearchSuggestionsBox = forwardRef(({
|
|||
list: forkModeSuggestions,
|
||||
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||
}
|
||||
case "case":
|
||||
return {
|
||||
list: caseModeSuggestions,
|
||||
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||
}
|
||||
case "archived":
|
||||
return {
|
||||
list: archivedModeSuggestions,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export const useRefineModeSuggestions = () => {
|
|||
},
|
||||
] : []),
|
||||
{
|
||||
value: SearchPrefix.public,
|
||||
value: SearchPrefix.visibility,
|
||||
description: "Filter on repository visibility."
|
||||
},
|
||||
{
|
||||
|
|
@ -86,10 +86,6 @@ export const useRefineModeSuggestions = () => {
|
|||
value: SearchPrefix.archived,
|
||||
description: "Include results from archived repositories.",
|
||||
},
|
||||
{
|
||||
value: SearchPrefix.case,
|
||||
description: "Control case-sensitivity of search patterns."
|
||||
},
|
||||
{
|
||||
value: SearchPrefix.fork,
|
||||
description: "Include only results from forked repositories."
|
||||
|
|
|
|||
|
|
@ -70,12 +70,6 @@ export const useSuggestionModeMappings = () => {
|
|||
SearchPrefix.archived
|
||||
]
|
||||
},
|
||||
{
|
||||
suggestionMode: "case",
|
||||
prefixes: [
|
||||
SearchPrefix.case
|
||||
]
|
||||
},
|
||||
{
|
||||
suggestionMode: "fork",
|
||||
prefixes: [
|
||||
|
|
@ -83,9 +77,9 @@ export const useSuggestionModeMappings = () => {
|
|||
]
|
||||
},
|
||||
{
|
||||
suggestionMode: "public",
|
||||
suggestionMode: "visibility",
|
||||
prefixes: [
|
||||
SearchPrefix.public
|
||||
SearchPrefix.visibility
|
||||
]
|
||||
},
|
||||
...(isSearchContextsEnabled ? [
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export const zoekt = () => {
|
|||
|
||||
// Check for prefixes first
|
||||
// If these match, we return 'keyword'
|
||||
if (stream.match(/(archived:|branch:|b:|rev:|c:|case:|content:|f:|file:|fork:|public:|r:|repo:|regex:|lang:|sym:|t:|type:|context:)/)) {
|
||||
if (stream.match(/(archived:|rev:|content:|f:|file:|fork:|visibility:|r:|repo:|regex:|lang:|sym:|t:|type:|context:)/)) {
|
||||
return t.keyword.toString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,34 +91,7 @@ export const POST = async (request: NextRequest) => {
|
|||
console.log(JSON.stringify(zoektQuery, null, 2));
|
||||
|
||||
const searchRequest: SearchRequest = {
|
||||
query: {
|
||||
and: {
|
||||
children: [
|
||||
zoektQuery,
|
||||
// {
|
||||
// raw_config: {
|
||||
// flags: [
|
||||
// 'FLAG_NO_FORKS',
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
]
|
||||
}
|
||||
},
|
||||
// query: {
|
||||
// and: {
|
||||
// // @todo: we should use repo_ids to filter out repositories that the user
|
||||
// // has access to (if permission syncing is enabled!).
|
||||
// children: [
|
||||
// {
|
||||
// regexp: {
|
||||
// regexp: query,
|
||||
// case_sensitive: true,
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
query: zoektQuery,
|
||||
opts: {
|
||||
chunk_matches: true,
|
||||
max_match_display_count: matches,
|
||||
|
|
|
|||
|
|
@ -17,9 +17,25 @@ import {
|
|||
ArchivedExpr,
|
||||
ForkExpr,
|
||||
VisibilityExpr,
|
||||
RepoSetExpr
|
||||
RepoSetExpr,
|
||||
} from '@sourcebot/query-language';
|
||||
|
||||
type ArchivedValue = 'yes' | 'no' | 'only';
|
||||
type VisibilityValue = 'public' | 'private' | 'any';
|
||||
type ForkValue = 'yes' | 'no' | 'only';
|
||||
|
||||
const isArchivedValue = (value: string): value is ArchivedValue => {
|
||||
return value === 'yes' || value === 'no' || value === 'only';
|
||||
}
|
||||
|
||||
const isVisibilityValue = (value: string): value is VisibilityValue => {
|
||||
return value === 'public' || value === 'private' || value === 'any';
|
||||
}
|
||||
|
||||
const isForkValue = (value: string): value is ForkValue => {
|
||||
return value === 'yes' || value === 'no' || value === 'only';
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a Lezer parse tree into a Zoekt gRPC query
|
||||
*/
|
||||
|
|
@ -199,12 +215,17 @@ export const transformToZoektQuery = ({
|
|||
};
|
||||
|
||||
case VisibilityExpr: {
|
||||
const visibilityValue = value.toLowerCase();
|
||||
const rawValue = value.toLowerCase();
|
||||
|
||||
if (!isVisibilityValue(rawValue)) {
|
||||
throw new Error(`Invalid visibility value: ${rawValue}. Expected 'public', 'private', or 'any'`);
|
||||
}
|
||||
|
||||
const flags: ('FLAG_ONLY_PUBLIC' | 'FLAG_ONLY_PRIVATE')[] = [];
|
||||
|
||||
if (visibilityValue === 'public') {
|
||||
if (rawValue === 'public') {
|
||||
flags.push('FLAG_ONLY_PUBLIC');
|
||||
} else if (visibilityValue === 'private') {
|
||||
} else if (rawValue === 'private') {
|
||||
flags.push('FLAG_ONLY_PRIVATE');
|
||||
}
|
||||
// 'any' means no filter
|
||||
|
|
@ -217,16 +238,20 @@ export const transformToZoektQuery = ({
|
|||
};
|
||||
}
|
||||
|
||||
// @todo: handle this
|
||||
case ArchivedExpr: {
|
||||
const archivedValue = value.toLowerCase();
|
||||
const rawValue = value.toLowerCase();
|
||||
|
||||
if (!isArchivedValue(rawValue)) {
|
||||
throw new Error(`Invalid archived value: ${rawValue}. Expected 'yes', 'no', or 'only'`);
|
||||
}
|
||||
|
||||
const flags: ('FLAG_ONLY_ARCHIVED' | 'FLAG_NO_ARCHIVED')[] = [];
|
||||
|
||||
if (archivedValue === 'yes') {
|
||||
if (rawValue === 'yes') {
|
||||
// 'yes' means include archived repositories (default)
|
||||
} else if (archivedValue === 'no') {
|
||||
} else if (rawValue === 'no') {
|
||||
flags.push('FLAG_NO_ARCHIVED');
|
||||
} else if (archivedValue === 'only') {
|
||||
} else if (rawValue === 'only') {
|
||||
flags.push('FLAG_ONLY_ARCHIVED');
|
||||
}
|
||||
|
||||
|
|
@ -237,12 +262,30 @@ export const transformToZoektQuery = ({
|
|||
query: "raw_config"
|
||||
};
|
||||
}
|
||||
case ForkExpr:
|
||||
// These are repo metadata filters
|
||||
// They need to be handled via repo filters in Zoekt
|
||||
// For now, return a const query (you might need custom handling)
|
||||
console.warn(`${prefixNode.type.name} not yet implemented`);
|
||||
return { const: true, query: "const" };
|
||||
case ForkExpr: {
|
||||
const rawValue = value.toLowerCase();
|
||||
|
||||
if (!isForkValue(rawValue)) {
|
||||
throw new Error(`Invalid fork value: ${rawValue}. Expected 'yes', 'no', or 'only'`);
|
||||
}
|
||||
|
||||
const flags: ('FLAG_ONLY_FORKS' | 'FLAG_NO_FORKS')[] = [];
|
||||
|
||||
if (rawValue === 'yes') {
|
||||
// 'yes' means include forks (default)
|
||||
} else if (rawValue === 'no') {
|
||||
flags.push('FLAG_NO_FORKS');
|
||||
} else if (rawValue === 'only') {
|
||||
flags.push('FLAG_ONLY_FORKS');
|
||||
}
|
||||
|
||||
return {
|
||||
raw_config: {
|
||||
flags
|
||||
},
|
||||
query: "raw_config"
|
||||
};
|
||||
}
|
||||
|
||||
case RepoSetExpr: {
|
||||
return {
|
||||
|
|
|
|||
Loading…
Reference in a new issue