mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 12:25:22 +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 }
|
RepoSetExpr { reposetKw value }
|
||||||
|
|
||||||
// Modifiers
|
// Modifiers
|
||||||
ArchivedExpr { archivedKw value }
|
ArchivedExpr { archivedKw archivedValue }
|
||||||
ForkExpr { forkKw value }
|
ForkExpr { forkKw forkValue }
|
||||||
VisibilityExpr { visibilityKw value }
|
VisibilityExpr { visibilityKw visibilityValue }
|
||||||
|
|
||||||
|
archivedValue { "yes" | "no" | "only" }
|
||||||
|
forkValue { "yes" | "no" | "only" }
|
||||||
|
visibilityValue { "public" | "private" | "any" }
|
||||||
|
|
||||||
Term { quotedString | word }
|
Term { quotedString | word }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ lang:typescript
|
||||||
|
|
||||||
Program(PrefixExpr(LangExpr))
|
Program(PrefixExpr(LangExpr))
|
||||||
|
|
||||||
# Archived prefix
|
# Archived prefix - no
|
||||||
|
|
||||||
archived:no
|
archived:no
|
||||||
|
|
||||||
|
|
@ -70,7 +70,15 @@ archived:no
|
||||||
|
|
||||||
Program(PrefixExpr(ArchivedExpr))
|
Program(PrefixExpr(ArchivedExpr))
|
||||||
|
|
||||||
# Fork prefix
|
# Archived prefix - only
|
||||||
|
|
||||||
|
archived:only
|
||||||
|
|
||||||
|
==>
|
||||||
|
|
||||||
|
Program(PrefixExpr(ArchivedExpr))
|
||||||
|
|
||||||
|
# Fork prefix - yes
|
||||||
|
|
||||||
fork:yes
|
fork:yes
|
||||||
|
|
||||||
|
|
@ -78,6 +86,14 @@ fork:yes
|
||||||
|
|
||||||
Program(PrefixExpr(ForkExpr))
|
Program(PrefixExpr(ForkExpr))
|
||||||
|
|
||||||
|
# Fork prefix - only
|
||||||
|
|
||||||
|
fork:only
|
||||||
|
|
||||||
|
==>
|
||||||
|
|
||||||
|
Program(PrefixExpr(ForkExpr))
|
||||||
|
|
||||||
# Visibility prefix - public
|
# Visibility prefix - public
|
||||||
|
|
||||||
visibility:public
|
visibility:public
|
||||||
|
|
@ -206,7 +222,7 @@ lang:python
|
||||||
|
|
||||||
Program(PrefixExpr(LangExpr))
|
Program(PrefixExpr(LangExpr))
|
||||||
|
|
||||||
# Archived values
|
# Archived prefix - yes
|
||||||
|
|
||||||
archived:yes
|
archived:yes
|
||||||
|
|
||||||
|
|
@ -214,7 +230,15 @@ archived:yes
|
||||||
|
|
||||||
Program(PrefixExpr(ArchivedExpr))
|
Program(PrefixExpr(ArchivedExpr))
|
||||||
|
|
||||||
# Fork values
|
# Archived prefix - invalid value (error case)
|
||||||
|
|
||||||
|
archived:invalid
|
||||||
|
|
||||||
|
==>
|
||||||
|
|
||||||
|
Program(AndExpr(PrefixExpr(ArchivedExpr(⚠)),Term))
|
||||||
|
|
||||||
|
# Fork prefix - no
|
||||||
|
|
||||||
fork:no
|
fork:no
|
||||||
|
|
||||||
|
|
@ -222,6 +246,14 @@ fork:no
|
||||||
|
|
||||||
Program(PrefixExpr(ForkExpr))
|
Program(PrefixExpr(ForkExpr))
|
||||||
|
|
||||||
|
# Fork prefix - invalid value (error case)
|
||||||
|
|
||||||
|
fork:invalid
|
||||||
|
|
||||||
|
==>
|
||||||
|
|
||||||
|
Program(AndExpr(PrefixExpr(ForkExpr(⚠)),Term))
|
||||||
|
|
||||||
# Visibility prefix - private
|
# Visibility prefix - private
|
||||||
|
|
||||||
visibility:private
|
visibility:private
|
||||||
|
|
@ -230,6 +262,22 @@ visibility:private
|
||||||
|
|
||||||
Program(PrefixExpr(VisibilityExpr))
|
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 with dashes
|
||||||
|
|
||||||
file:my-component.tsx
|
file:my-component.tsx
|
||||||
|
|
|
||||||
|
|
@ -16,57 +16,53 @@ export enum SearchPrefix {
|
||||||
sym = "sym:",
|
sym = "sym:",
|
||||||
content = "content:",
|
content = "content:",
|
||||||
archived = "archived:",
|
archived = "archived:",
|
||||||
case = "case:",
|
|
||||||
fork = "fork:",
|
fork = "fork:",
|
||||||
public = "public:",
|
visibility = "visibility:",
|
||||||
context = "context:",
|
context = "context:",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const publicModeSuggestions: Suggestion[] = [
|
export const visibilityModeSuggestions: Suggestion[] = [
|
||||||
{
|
{
|
||||||
value: "yes",
|
value: "public",
|
||||||
description: "Only include results from public repositories."
|
description: "Only include results from public repositories."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "no",
|
value: "private",
|
||||||
description: "Only include results from private repositories."
|
description: "Only include results from private repositories."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: "any",
|
||||||
|
description: "Include results from both public and private repositories (default)."
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const forkModeSuggestions: Suggestion[] = [
|
export const forkModeSuggestions: Suggestion[] = [
|
||||||
{
|
{
|
||||||
value: "yes",
|
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."
|
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[] = [
|
export const archivedModeSuggestions: Suggestion[] = [
|
||||||
{
|
{
|
||||||
value: "yes",
|
value: "yes",
|
||||||
description: "Only include results in archived repositories."
|
description: "Include results from archived repositories (default)."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "no",
|
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 { forwardRef, Ref, useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
archivedModeSuggestions,
|
archivedModeSuggestions,
|
||||||
caseModeSuggestions,
|
|
||||||
forkModeSuggestions,
|
forkModeSuggestions,
|
||||||
publicModeSuggestions,
|
visibilityModeSuggestions,
|
||||||
} from "./constants";
|
} from "./constants";
|
||||||
import { IconType } from "react-icons/lib";
|
import { IconType } from "react-icons/lib";
|
||||||
import { VscFile, VscFilter, VscRepo, VscSymbolMisc } from "react-icons/vsc";
|
import { VscFile, VscFilter, VscRepo, VscSymbolMisc } from "react-icons/vsc";
|
||||||
|
|
@ -32,9 +31,8 @@ export type SuggestionMode =
|
||||||
"archived" |
|
"archived" |
|
||||||
"file" |
|
"file" |
|
||||||
"language" |
|
"language" |
|
||||||
"case" |
|
|
||||||
"fork" |
|
"fork" |
|
||||||
"public" |
|
"visibility" |
|
||||||
"revision" |
|
"revision" |
|
||||||
"symbol" |
|
"symbol" |
|
||||||
"content" |
|
"content" |
|
||||||
|
|
@ -137,9 +135,9 @@ const SearchSuggestionsBox = forwardRef(({
|
||||||
DefaultIcon?: IconType
|
DefaultIcon?: IconType
|
||||||
} => {
|
} => {
|
||||||
switch (suggestionMode) {
|
switch (suggestionMode) {
|
||||||
case "public":
|
case "visibility":
|
||||||
return {
|
return {
|
||||||
list: publicModeSuggestions,
|
list: visibilityModeSuggestions,
|
||||||
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||||
}
|
}
|
||||||
case "fork":
|
case "fork":
|
||||||
|
|
@ -147,11 +145,6 @@ const SearchSuggestionsBox = forwardRef(({
|
||||||
list: forkModeSuggestions,
|
list: forkModeSuggestions,
|
||||||
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||||
}
|
}
|
||||||
case "case":
|
|
||||||
return {
|
|
||||||
list: caseModeSuggestions,
|
|
||||||
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
|
||||||
}
|
|
||||||
case "archived":
|
case "archived":
|
||||||
return {
|
return {
|
||||||
list: archivedModeSuggestions,
|
list: archivedModeSuggestions,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export const useRefineModeSuggestions = () => {
|
||||||
},
|
},
|
||||||
] : []),
|
] : []),
|
||||||
{
|
{
|
||||||
value: SearchPrefix.public,
|
value: SearchPrefix.visibility,
|
||||||
description: "Filter on repository visibility."
|
description: "Filter on repository visibility."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -86,10 +86,6 @@ export const useRefineModeSuggestions = () => {
|
||||||
value: SearchPrefix.archived,
|
value: SearchPrefix.archived,
|
||||||
description: "Include results from archived repositories.",
|
description: "Include results from archived repositories.",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
value: SearchPrefix.case,
|
|
||||||
description: "Control case-sensitivity of search patterns."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
value: SearchPrefix.fork,
|
value: SearchPrefix.fork,
|
||||||
description: "Include only results from forked repositories."
|
description: "Include only results from forked repositories."
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,6 @@ export const useSuggestionModeMappings = () => {
|
||||||
SearchPrefix.archived
|
SearchPrefix.archived
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
suggestionMode: "case",
|
|
||||||
prefixes: [
|
|
||||||
SearchPrefix.case
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
suggestionMode: "fork",
|
suggestionMode: "fork",
|
||||||
prefixes: [
|
prefixes: [
|
||||||
|
|
@ -83,9 +77,9 @@ export const useSuggestionModeMappings = () => {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
suggestionMode: "public",
|
suggestionMode: "visibility",
|
||||||
prefixes: [
|
prefixes: [
|
||||||
SearchPrefix.public
|
SearchPrefix.visibility
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
...(isSearchContextsEnabled ? [
|
...(isSearchContextsEnabled ? [
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export const zoekt = () => {
|
||||||
|
|
||||||
// Check for prefixes first
|
// Check for prefixes first
|
||||||
// If these match, we return 'keyword'
|
// 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();
|
return t.keyword.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,34 +91,7 @@ export const POST = async (request: NextRequest) => {
|
||||||
console.log(JSON.stringify(zoektQuery, null, 2));
|
console.log(JSON.stringify(zoektQuery, null, 2));
|
||||||
|
|
||||||
const searchRequest: SearchRequest = {
|
const searchRequest: SearchRequest = {
|
||||||
query: {
|
query: zoektQuery,
|
||||||
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,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
opts: {
|
opts: {
|
||||||
chunk_matches: true,
|
chunk_matches: true,
|
||||||
max_match_display_count: matches,
|
max_match_display_count: matches,
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,25 @@ import {
|
||||||
ArchivedExpr,
|
ArchivedExpr,
|
||||||
ForkExpr,
|
ForkExpr,
|
||||||
VisibilityExpr,
|
VisibilityExpr,
|
||||||
RepoSetExpr
|
RepoSetExpr,
|
||||||
} from '@sourcebot/query-language';
|
} 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
|
* Transform a Lezer parse tree into a Zoekt gRPC query
|
||||||
*/
|
*/
|
||||||
|
|
@ -199,12 +215,17 @@ export const transformToZoektQuery = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
case VisibilityExpr: {
|
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')[] = [];
|
const flags: ('FLAG_ONLY_PUBLIC' | 'FLAG_ONLY_PRIVATE')[] = [];
|
||||||
|
|
||||||
if (visibilityValue === 'public') {
|
if (rawValue === 'public') {
|
||||||
flags.push('FLAG_ONLY_PUBLIC');
|
flags.push('FLAG_ONLY_PUBLIC');
|
||||||
} else if (visibilityValue === 'private') {
|
} else if (rawValue === 'private') {
|
||||||
flags.push('FLAG_ONLY_PRIVATE');
|
flags.push('FLAG_ONLY_PRIVATE');
|
||||||
}
|
}
|
||||||
// 'any' means no filter
|
// 'any' means no filter
|
||||||
|
|
@ -217,16 +238,20 @@ export const transformToZoektQuery = ({
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo: handle this
|
|
||||||
case ArchivedExpr: {
|
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')[] = [];
|
const flags: ('FLAG_ONLY_ARCHIVED' | 'FLAG_NO_ARCHIVED')[] = [];
|
||||||
|
|
||||||
if (archivedValue === 'yes') {
|
if (rawValue === 'yes') {
|
||||||
// 'yes' means include archived repositories (default)
|
// 'yes' means include archived repositories (default)
|
||||||
} else if (archivedValue === 'no') {
|
} else if (rawValue === 'no') {
|
||||||
flags.push('FLAG_NO_ARCHIVED');
|
flags.push('FLAG_NO_ARCHIVED');
|
||||||
} else if (archivedValue === 'only') {
|
} else if (rawValue === 'only') {
|
||||||
flags.push('FLAG_ONLY_ARCHIVED');
|
flags.push('FLAG_ONLY_ARCHIVED');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,12 +262,30 @@ export const transformToZoektQuery = ({
|
||||||
query: "raw_config"
|
query: "raw_config"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case ForkExpr:
|
case ForkExpr: {
|
||||||
// These are repo metadata filters
|
const rawValue = value.toLowerCase();
|
||||||
// They need to be handled via repo filters in Zoekt
|
|
||||||
// For now, return a const query (you might need custom handling)
|
if (!isForkValue(rawValue)) {
|
||||||
console.warn(`${prefixNode.type.name} not yet implemented`);
|
throw new Error(`Invalid fork value: ${rawValue}. Expected 'yes', 'no', or 'only'`);
|
||||||
return { const: true, query: "const" };
|
}
|
||||||
|
|
||||||
|
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: {
|
case RepoSetExpr: {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue