136 lines · 5.1 KB
import { all } from "../db";
import { repoSearch, type RepoSearchStatus } from "./repo-search-do";
import { visibleReposClause } from "../repos";
import { CODE_MAX_PAGES, CODE_PAGE_SIZE } from "~/lib/code-search";

export { CODE_MAX_PAGES };
import type { CodeSearchPage } from "~/lib/code-intel";
import type { Viewer } from "~/lib/types";

const MIN_TERM_LENGTH = 3;
const MAX_TERMS = 8;
const MAX_TERM_LENGTH = 100;
const PATH_PREFIX = "path:";

function quote(term: string): string {
  return `"${term.replaceAll('"', '""')}"`;
}

type ParsedQuery = { fts: string; contentTerms: string[] };

function parse(input: string): ParsedQuery | null {
  const terms: string[] = [];
  const contentTerms: string[] = [];
  for (const raw of input.trim().split(/\s+/)) {
    const isPath = raw.startsWith(PATH_PREFIX);
    const term = (isPath ? raw.slice(PATH_PREFIX.length) : raw).slice(0, MAX_TERM_LENGTH);
    if (term.length < MIN_TERM_LENGTH) continue;
    terms.push(isPath ? `path : ${quote(term)}` : quote(term));
    if (!isPath) contentTerms.push(term);
    if (terms.length >= MAX_TERMS) break;
  }
  return terms.length > 0 ? { fts: terms.join(" "), contentTerms } : null;
}

/** Turns free text into a safe FTS5 expression: every term is quoted (trigram needs 3+ chars); `path:x` filters by path. */
export function ftsQuery(input: string): string | null {
  return parse(input)?.fts ?? null;
}

/** The plain (non-`path:`) terms of a query: what gets highlighted inside file contents. */