import type { CodeSearchLine } from "./code-intel";
export const CODE_PAGE_SIZE = 20;
/** Deep pages re-rank every match, so paging stops here; narrow the query to see further. */
export const CODE_MAX_PAGES = 50;
const MAX_LINE_CHARS = 240;
const LEAD_CHARS = 60;
/** Case-insensitive [start, end) spans of every term in `text`, merged where they overlap. */
export function matchRanges(text: string, terms: string[]): Array<[number, number]> {
const lower = text.toLowerCase();
const spans: Array<[number, number]> = [];
for (const term of terms) {
const needle = term.toLowerCase();
if (!needle) continue;
for (let at = lower.indexOf(needle); at !== -1; at = lower.indexOf(needle, at + needle.length)) {
spans.push([at, at + needle.length]);
}
}
spans.sort((left, right) => left[0] - right[0]);
const merged: Array<[number, number]> = [];
for (const span of spans) {
const last = merged.at(-1);
if (last && span[0] <= last[1]) last[1] = Math.max(last[1], span[1]);
else merged.push([span[0], span[1]]);
}
return merged;
}
/** Long lines are cut to a window that starts a little before the first match. */
function clip(text: string, ranges: Array<[number, number]>): { text: string; offset: number } {
if (text.length <= MAX_LINE_CHARS) return { text, offset: 0 };
const offset = Math.max(0, Math.min((ranges[0]?.[0] ?? 0) - LEAD_CHARS, text.length - MAX_LINE_CHARS));
return { text: text.slice(offset, offset + MAX_LINE_CHARS), offset };
}
/**
* The first `limit` lines of `content` containing any term, with 1-based line numbers and highlight
* spans, plus how many lines matched in total.