import { type Found } from 'posix-regex'; interface SearchInput { /** `/` forward search, `?` backward search, `&` display filter. */ type: '/' | '?' | '&'; chars: string[]; count: number; invert: boolean; fromStart: boolean; pastEof: boolean; keep: boolean; noRegex: boolean; wrap: boolean; subs: Set; litNext: boolean; subPrompt: boolean; /** Position when the prompt opened, restored by --incsearch. */ originRow: number; originSubRow: number; originEof: boolean; } /** * What the search asks of a compiled pattern — the part PsxRegExp and * a host RegExp can both answer, so --use-js-regexp can swap one for * the other without the callers knowing which they hold. */ export interface SearchRegex { readonly source: string; readonly flags: string; readonly global: boolean; lastIndex: number; test(text: string): boolean; exec(text: string): Found | null; } interface Filter { regex: SearchRegex; invert: boolean; subs: Set; } interface SearchState { /** Pattern currently being typed at the prompt, or null. */ input: SearchInput | null; /** Last compiled pattern, reused by n/N and highlighting. */ regex: SearchRegex | null; /** Whether the pattern matches NON-matching lines (^N / !). */ invert: boolean; /** Direction of the last search: 1 forward, -1 backward. */ lastDir: 1 | -1; /** Whether matches are highlighted (toggled by ESC-u). */ highlight: boolean; /** Sub-pattern groups the last search was restricted to (^S). */ subs: Set; /** Active `&` display filters; lines must match all of them. */ filters: Filter[]; /** Case sensitivity: 0 sensitive, 1 smart (-i), 2 always ignore (-I). */ caseless: 0 | 1 | 2; /** Previously entered patterns, shared by `/`, `?` and `&` like less. */ history: string[]; /** Transient status message shown at the prompt. */ message: string; /** execSearch already wrote less's cmd_exec clear_bot for this * command, so the next frame opens with nothing. */ cmdExecOpened: boolean; /** A mid-scan note was written straight to the bottom line, so the * next paint must rewrite it rather than dedupe it. */ bottomClobbered: boolean; /** Follow-up messages shown as each one is dismissed, like less's * consecutive blocking error() calls. */ messageQueue: string[]; } export declare const search: SearchState; /** * Drops the search a session leaves behind, like a fresh less: the * compiled pattern, the & filters, the sub-pattern set and the * caseless state. The HISTORY stays — less persists that across * invocations through its history file. */ export declare function resetSearch(): void; /** * Points the recall spot past the newest entry, for when the history * is replaced wholesale (loading the history file). */ export declare function resetHistoryRecall(): void; /** * Changes case sensitivity (-i / -I) and recompiles the current pattern so * highlighting and repeats follow the new setting immediately. * * @param caseless - 0 sensitive, 1 smart (-i), 2 always ignore (-I). */ export declare function chgCaseless(caseless: 0 | 1 | 2): void; /** * Opens the search or filter prompt. * * @param type - `/`, `?` or `&`. * @param count - N-th occurrence to find. */ export declare function startSearch(type: '/' | '?' | '&', count: number): void; /** * Restores the position captured when the search prompt opened, like * less's incremental search undoing on cancel or pattern change. */ export declare function restoreSearchOrigin(input: { originRow: number; originSubRow: number; originEof: boolean; }): void; /** * Searches while the pattern is being typed (--incsearch): each change * restarts from the original position; failures stay silent. * * @param content - Full content lines. */ export declare function incrementalSearch(content: string[], finder?: SearchFinder | null): void; /** * Feeds one keypress into the pattern being typed at the prompt. * * - CR submits, ^C cancels, backspace edits (and cancels on empty). * - While the pattern is empty, modifier keys toggle search flags like * less (^N/!, ^E/*, ^F/@, ^K, ^R, ^S, ^W, ^L). * - Up/Down recall previous patterns starting with the typed text, * like cmdbuf.c's cmd_updown; other escape sequences are ignored. * * @param key - Raw key input. * @returns `run` to execute, `cancel` when aborted, otherwise `pending`. */ export declare function searchInputKey(key: string): 'pending' | 'run' | 'cancel'; /** * Builds the bottom-line prompt for the pattern being typed, mirroring * less's modifier prefixes (e.g. `Non-match &/`). * * @returns The prompt string, or null when no pattern is being typed. */ export declare function searchPrompt(): string | null; /** Registers the --autosave history file writer. */ export declare function onAutosave(fn: () => void): void; /** Registers the new-entry recorder. */ export declare function onHistRecord(fn: (entry: string) => void): void; /** Registers the history modified-flag raiser (less's cmd_accept). */ export declare function onHistTouch(fn: () => void): void; /** * Records an accepted pattern, like cmd_accept: empty and repeated * patterns stay out, the list caps at less's history size, and * --autosave writes the file right away. */ export declare function addHistory(pattern: string): void; /** * Executes the typed `/` or `?` search: compiles the pattern and jumps to * the N-th matching line. * * - An empty pattern repeats the previous search in the typed direction. * - `^K` compiles and highlights without moving. */ export interface SearchRequest { dir: 1 | -1; count: number; fromStart: boolean; wrap: boolean; afterTarget: boolean; pattern: string; incremental?: boolean; } export type SearchFinder = (request: SearchRequest) => boolean; export declare function execSearch(content: string[], finder?: SearchFinder | null): void; type LineFilter = (line: string) => boolean; /** * Executes the typed `&` filter pattern. * * - Filters stack like less's filter list: lines must match all of them. * - Filters are independent of the search pattern and its highlighting. * * @returns A line matcher to filter content with, `null` when the pattern is * empty (remove all filters), or `undefined` when invalid. */ export declare function execFilter(): LineFilter | null | undefined; /** * Repeats the previous search. * * @param content - Full content lines. * @param count - N-th occurrence to find. * @param reverse - Whether to search opposite to the previous direction. */ export declare function repeatSearch(content: string[], count: number, reverse: boolean, finder?: SearchFinder | null): void; export declare function setHiliteHidden(hidden: boolean): void; export declare function toggleHighlight(): void; /** * Clears search highlighting by forgetting the pattern entirely (ESC-U). * * - Mirrors less's `clear_pattern`: afterwards `n` has nothing to repeat. */ export declare function clearHighlight(): void; /** less's clr_hilite: the list stops applying, so drop it. */ export declare function clearHiliteCache(): void; export declare function highlightLine(line: string, row?: number): string; export declare const searchCaseFlags: (pattern: string) => string; /** * Compiles a pattern the way less's compile_pattern2 does. * * less hands the pattern to regcomp with REG_EXTENDED (pattern.h's * REGCOMP_FLAG) and REG_ICASE for -i. The dialect carries the extended * spelling itself — see REGEX_DIALECT, where leaving it to the "e" * flag would silently fall back to BASIC. * * Matching is by CHARACTER, as less's is — cvt_text hands regcomp a * UTF-8 string where "." spans one character however many bytes it * takes. That used to need the "u" flag, applied only when the pattern * still compiled with it, because "u" also rejected patterns less * accepts (a stray "\\d" in a class, an unescaped brace). POSIX reads * by code point always and has no such quarrel, so the guess is gone. */ /** * The `Pattern too complex. Try again with POSIX RegExp?` prompt, in * the shape less asks about a binary file: a question on the bottom row * that y answers and anything else declines. * * A --use-js-regexp search that had to be killed leaves the user with * nothing - not because the pattern is wrong, but because the engine * they asked for cannot finish it. The engine that can is already * here, so offer it rather than reporting a failure. */ export declare const posixRetry: { pending: boolean; }; /** True while highlighting is being skipped for a pattern it cannot * get through. */ export declare const hiliteGivenUp: () => boolean; /** Lets highlighting be attempted again. */ export declare function retryHilite(): void; /** * Marks the frame as the thing running, so matching done for it is * treated as a repaint rather than as a search. * * Decided from the RENDER, not from the search entry points. Wrapping * those looked right and measured backwards: a search's matching does * not happen inside the callback that starts it, and the frame that * follows does - so a search ran as "any key aborts" and had its own * RETURN kill it, while the repaint waited for a ^C nobody would * press. The frame is the one boundary that is always exactly where * it says it is. */ export declare function duringRepaint(run: () => T): T; /** Kept for the search entry points: they still mark the run. */ export declare function duringUserSearch(run: () => T): T; /** Takes the next search away from --use-js-regexp, for the retry. */ export declare function retryWithPosix(): void; /** * Puts the option back, for the next pattern. * * NOT after one compile: compiling builds two regexes - the one a * search walks with, and the one highlighting paints with - and * clearing between them sent the search to POSIX and left the * highlighting on the engine that could not finish, which is what "y" * looked like it was ignoring. * * It lasts as long as the pattern it was answered for. A new search * goes back to whatever the option says, and so does a toggle. */ export declare function clearForcePosix(): void; export declare function stripStyles(line: string): string; export declare function matchesSearchLine(line: string): boolean; /** * Runs the normal guarded matcher over one bounded source batch. The * remaining count is shared across batches, allowing a file input to scan * by byte position without retaining the traversed lines. */ export declare function scanSearchBatch(lines: string[], state: { remaining: number; }): number | 'miss' | 'stop'; /** Records the local row a source-backed search landed on for -g. */ export declare function recordSearchMatch(row: number): void; /** * Applies a `&` display filter in the same guarded slices as a search. * * @param lines - Full content lines. * @param filter - The combined filter matcher. * @returns The kept lines, or null when the filter must be dropped * (catastrophic pattern) or the user interrupted. */ export declare function filterLines(lines: string[], filter: (line: string) => boolean): string[] | null; /** * The guarded filter result for each input row. File-backed inputs use the * mask to retain byte positions while sharing the regular filter engine. */ export declare function filterLineMask(lines: string[], filter: (line: string) => boolean): boolean[] | null; /** True when what ended the last match was ^C or the --intr char. */ export declare const abortedByInterrupt: () => boolean; /** * Runs matching that nobody asked for - a frame's highlighting - so * that any key ends it, not only an interrupt. */ export declare function duringRepaintMatch(run: () => T): T; export declare function searchInterrupted(force?: boolean): boolean; /** Registers less's pre-search repaint pair. */ export declare function onHilitePaint(fn: ((content: string[]) => void) | null): void; /** * shift_visible on a line's DISPLAY text. * * Both engines run this, and differ only in where the text comes from: * a row of session.content, or a line read from the seekable source. * Everything after that -- less's four-way choice of new hshift -- was * written out twice, identically. * * less reaches here only with a real match in hand (sp[0] and ep[0] are * non-NULL, search.c:1745), which under an inverted search they never * are: the line "matches" precisely because the pattern did not. The * invert test below says that up front; falling through to a null exec * says the same thing one step later. * * @param text - The line as displayed, styles already stripped. */ export declare function shiftVisibleText(text: string): void; /** * Whether a content line matches the current search pattern, for the * -J status column marker. * * @param line - The raw content line. */ export declare function lineMatches(line: string): boolean; /** * The -J status column search char, like less's init_status_col: `*` * for a match in the displayed part of the line, `<`/`>` for matches * chopped off before/after the visible columns, `=` for both sides. * Hidden highlights (ESC-u) and -G0 still mark the column, like less's * is_hilited_attr status-column path ignoring hide_hilite; -g marks * only the last search's match line. * * @param line - The raw content line. * @param row - The content row, for the -g current-match gate. */ export declare function statusColChar(line: string, row: number, from?: number, to?: number): string; export {};