export interface LineMatch { line: number; start: number; end: number; } export const MATCH_LIMIT = 10_000; const escapeRegExp = (text: string): string => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); export const findMatches = ( lines: readonly string[], query: string, limit = MATCH_LIMIT ): LineMatch[] => { const matches: LineMatch[] = []; if (query === "") return matches; const pattern = new RegExp(escapeRegExp(query), "giu"); for (let line = 0; line < lines.length; line++) { for (const found of lines[line].matchAll(pattern)) { matches.push({ line: line + 1, start: found.index, end: found.index + found[0].length }); if (matches.length >= limit) return matches; } } return matches; }; export const wrapIndex = (index: number, length: number): number => length === 0 ? -1 : ((index % length) + length) % length;