The raw query string from the search box.
The markup, interpretation state, and an error message when invalid.
highlightSearchQuery("acetone").state; // "plain"
highlightSearchQuery("a AND b").state; // "advanced"
highlightSearchQuery("NOT a AND NOT b").state; // "error" (no inclusive term)
highlightSearchQuery("a AND (b").state; // "error" (unbalanced parens)
export function highlightSearchQuery(input: string): HighlightResult {
const tokens = tokenizeWithSpans(input);
const advanced = hasAdvancedSyntax(input);
const html = tokens.map((token) => renderToken(token, advanced)).join('');
if (!advanced) {
return { html, state: 'plain' };
}
const parsed = parseSearchQuery(input);
// Advanced syntax present but the parser fell back to a plain term ⇒ malformed.
if (!parsed.isAdvanced) {
const hasUnbalancedParens = tokens.some((token) => token.kind === 'paren' && token.error);
return {
html,
state: 'error',
message: hasUnbalancedParens ? 'Unbalanced parentheses.' : 'Invalid advanced query syntax.',
};
}
if (extractAllPositiveTerms(parsed.ast).length === 0) {
return {
html,
state: 'error',
message: 'Add at least one term to include — a query of only exclusions matches everything.',
};
}
return { html, state: 'advanced' };
}
Highlights a search query for the search box, returning token-colored markup plus how the query is interpreted. The result mirrors parseSearchQuery's own grammar:
"plain"— no advanced syntax (rendered without coloring by the input component)."advanced"— a valid boolean query with at least one inclusive term."error"— advanced syntax that is malformed (e.g. unbalanced parens) or has no inclusive constraint (onlyNOT/exclusions), which would match almost everything.