/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { type EditorType } from '@google/gemini-cli-core';
import type { Key } from '../../contexts/KeypressContext.js';
export declare const LARGE_PASTE_LINE_THRESHOLD = 5;
export declare const LARGE_PASTE_CHAR_THRESHOLD = 500;
export declare const PASTED_TEXT_PLACEHOLDER_REGEX: RegExp;
export declare function expandPastePlaceholders(text: string, pastedContent: Record<string, string>): string;
export type Direction = 'left' | 'right' | 'up' | 'down' | 'wordLeft' | 'wordRight' | 'home' | 'end';
export declare const isWordCharStrict: (char: string) => boolean;
export declare const isWhitespace: (char: string) => boolean;
export declare const isCombiningMark: (char: string) => boolean;
export declare const isWordCharWithCombining: (char: string) => boolean;
export declare const getCharScript: (char: string) => string;
export declare const isDifferentScript: (char1: string, char2: string) => boolean;
export declare const findNextWordStartInLine: (line: string, col: number) => number | null;
export declare const findPrevWordStartInLine: (line: string, col: number) => number | null;
export declare const findWordEndInLine: (line: string, col: number) => number | null;
export declare const findNextBigWordStartInLine: (line: string, col: number) => number | null;
export declare const findPrevBigWordStartInLine: (line: string, col: number) => number | null;
export declare const findBigWordEndInLine: (line: string, col: number) => number | null;
export declare const findNextWordAcrossLines: (lines: string[], cursorRow: number, cursorCol: number, searchForWordStart: boolean) => {
    row: number;
    col: number;
} | null;
export declare const findPrevWordAcrossLines: (lines: string[], cursorRow: number, cursorCol: number) => {
    row: number;
    col: number;
} | null;
export declare const findNextBigWordAcrossLines: (lines: string[], cursorRow: number, cursorCol: number, searchForWordStart: boolean) => {
    row: number;
    col: number;
} | null;
export declare const findPrevBigWordAcrossLines: (lines: string[], cursorRow: number, cursorCol: number) => {
    row: number;
    col: number;
} | null;
export declare const getPositionFromOffsets: (startOffset: number, endOffset: number, lines: string[]) => {
    startRow: number;
    startCol: number;
    endRow: number;
    endCol: number;
};
export declare const getLineRangeOffsets: (startRow: number, lineCount: number, lines: string[]) => {
    startOffset: number;
    endOffset: number;
};
export declare const replaceRangeInternal: (state: TextBufferState, startRow: number, startCol: number, endRow: number, endCol: number, text: string) => TextBufferState;
export interface Viewport {
    height: number;
    width: number;
}
interface UseTextBufferProps {
    initialText?: string;
    initialCursorOffset?: number;
    viewport: Viewport;
    stdin?: NodeJS.ReadStream | null;
    setRawMode?: (mode: boolean) => void;
    onChange?: (text: string) => void;
    escapePastedPaths?: boolean;
    shellModeActive?: boolean;
    inputFilter?: (text: string) => string;
    singleLine?: boolean;
    getPreferredEditor?: () => EditorType | undefined;
}
interface UndoHistoryEntry {
    lines: string[];
    cursorRow: number;
    cursorCol: number;
    pastedContent: Record<string, string>;
    expandedPaste: ExpandedPasteInfo | null;
}
export declare function offsetToLogicalPos(text: string, offset: number): [number, number];
/**
 * Converts logical row/col position to absolute text offset
 * Inverse operation of offsetToLogicalPos
 */
export declare function logicalPosToOffset(lines: string[], row: number, col: number): number;
/**
 * Transformations allow for the CLI to render terse representations of things like file paths
 * (e.g., "@some/path/to/an/image.png" to "[Image image.png]")
 * When the cursor enters a transformed representation, it expands to reveal the logical representation.
 * (e.g., "[Image image.png]" to "@some/path/to/an/image.png")
 */
export interface Transformation {
    logStart: number;
    logEnd: number;
    logicalText: string;
    collapsedText: string;
    type: 'image' | 'paste';
    id?: string;
}
export declare const imagePathRegex: RegExp;
export declare function getTransformedImagePath(filePath: string): string;
export declare function calculateTransformationsForLine(line: string): Transformation[];
export declare function calculateTransformations(lines: string[]): Transformation[][];
export declare function getTransformUnderCursor(row: number, col: number, spansByLine: Transformation[][], options?: {
    includeEdge?: boolean;
}): Transformation | null;
export interface ExpandedPasteInfo {
    id: string;
    startLine: number;
    lineCount: number;
    prefix: string;
    suffix: string;
}
/**
 * Check if a line index falls within an expanded paste region.
 * Returns the paste placeholder ID if found, null otherwise.
 */
export declare function getExpandedPasteAtLine(lineIndex: number, expandedPaste: ExpandedPasteInfo | null): string | null;
/**
 * Surgery for expanded paste regions when lines are added or removed.
 * Adjusts startLine indices and detaches any region that is partially or fully deleted.
 */
export declare function shiftExpandedRegions(expandedPaste: ExpandedPasteInfo | null, changeStartLine: number, lineDelta: number, changeEndLine?: number): {
    newInfo: ExpandedPasteInfo | null;
    isDetached: boolean;
};
/**
 * Detach any expanded paste region if the cursor is within it.
 * This converts the expanded content to regular text that can no longer be collapsed.
 * Returns the state unchanged if cursor is not in an expanded region.
 */
export declare function detachExpandedPaste(state: TextBufferState): TextBufferState;
export declare function calculateTransformedLine(logLine: string, logIndex: number, logicalCursor: [number, number], transformations: Transformation[]): {
    transformedLine: string;
    transformedToLogMap: number[];
};
export interface VisualLayout {
    visualLines: string[];
    logicalToVisualMap: Array<Array<[number, number]>>;
    visualToLogicalMap: Array<[number, number]>;
    transformedToLogicalMaps: number[][];
    visualToTransformedMap: number[];
}
export interface TextBufferState {
    lines: string[];
    cursorRow: number;
    cursorCol: number;
    transformationsByLine: Transformation[][];
    preferredCol: number | null;
    undoStack: UndoHistoryEntry[];
    redoStack: UndoHistoryEntry[];
    clipboard: string | null;
    selectionAnchor: [number, number] | null;
    viewportWidth: number;
    viewportHeight: number;
    visualLayout: VisualLayout;
    pastedContent: Record<string, string>;
    expandedPaste: ExpandedPasteInfo | null;
}
export declare const pushUndo: (currentState: TextBufferState) => TextBufferState;
export type TextBufferAction = {
    type: 'insert';
    payload: string;
    isPaste?: boolean;
} | {
    type: 'set_text';
    payload: string;
    pushToUndo?: boolean;
    cursorPosition?: 'start' | 'end' | number;
} | {
    type: 'add_pasted_content';
    payload: {
        id: string;
        text: string;
    };
} | {
    type: 'backspace';
} | {
    type: 'move';
    payload: {
        dir: Direction;
    };
} | {
    type: 'set_cursor';
    payload: {
        cursorRow: number;
        cursorCol: number;
        preferredCol: number | null;
    };
} | {
    type: 'delete';
} | {
    type: 'delete_word_left';
} | {
    type: 'delete_word_right';
} | {
    type: 'kill_line_right';
} | {
    type: 'kill_line_left';
} | {
    type: 'undo';
} | {
    type: 'redo';
} | {
    type: 'replace_range';
    payload: {
        startRow: number;
        startCol: number;
        endRow: number;
        endCol: number;
        text: string;
    };
} | {
    type: 'move_to_offset';
    payload: {
        offset: number;
    };
} | {
    type: 'create_undo_snapshot';
} | {
    type: 'set_viewport';
    payload: {
        width: number;
        height: number;
    };
} | {
    type: 'vim_delete_word_forward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_word_backward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_word_end';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_big_word_forward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_big_word_backward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_big_word_end';
    payload: {
        count: number;
    };
} | {
    type: 'vim_change_word_forward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_change_word_backward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_change_word_end';
    payload: {
        count: number;
    };
} | {
    type: 'vim_change_big_word_forward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_change_big_word_backward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_change_big_word_end';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_line';
    payload: {
        count: number;
    };
} | {
    type: 'vim_change_line';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_to_end_of_line';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_to_start_of_line';
} | {
    type: 'vim_change_to_end_of_line';
    payload: {
        count: number;
    };
} | {
    type: 'vim_change_movement';
    payload: {
        movement: 'h' | 'j' | 'k' | 'l';
        count: number;
    };
} | {
    type: 'vim_move_left';
    payload: {
        count: number;
    };
} | {
    type: 'vim_move_right';
    payload: {
        count: number;
    };
} | {
    type: 'vim_move_up';
    payload: {
        count: number;
    };
} | {
    type: 'vim_move_down';
    payload: {
        count: number;
    };
} | {
    type: 'vim_move_word_forward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_move_word_backward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_move_word_end';
    payload: {
        count: number;
    };
} | {
    type: 'vim_move_big_word_forward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_move_big_word_backward';
    payload: {
        count: number;
    };
} | {
    type: 'vim_move_big_word_end';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_char';
    payload: {
        count: number;
    };
} | {
    type: 'vim_insert_at_cursor';
} | {
    type: 'vim_append_at_cursor';
} | {
    type: 'vim_open_line_below';
} | {
    type: 'vim_open_line_above';
} | {
    type: 'vim_append_at_line_end';
} | {
    type: 'vim_insert_at_line_start';
} | {
    type: 'vim_move_to_line_start';
} | {
    type: 'vim_move_to_line_end';
} | {
    type: 'vim_move_to_first_nonwhitespace';
} | {
    type: 'vim_move_to_first_line';
} | {
    type: 'vim_move_to_last_line';
} | {
    type: 'vim_move_to_line';
    payload: {
        lineNumber: number;
    };
} | {
    type: 'vim_escape_insert_mode';
} | {
    type: 'vim_delete_to_first_nonwhitespace';
} | {
    type: 'vim_change_to_start_of_line';
} | {
    type: 'vim_change_to_first_nonwhitespace';
} | {
    type: 'vim_delete_to_first_line';
    payload: {
        count: number;
    };
} | {
    type: 'vim_delete_to_last_line';
    payload: {
        count: number;
    };
} | {
    type: 'toggle_paste_expansion';
    payload: {
        id: string;
        row: number;
        col: number;
    };
};
export interface TextBufferOptions {
    inputFilter?: (text: string) => string;
    singleLine?: boolean;
}
export declare function textBufferReducer(state: TextBufferState, action: TextBufferAction, options?: TextBufferOptions): TextBufferState;
export declare function useTextBuffer({ initialText, initialCursorOffset, viewport, stdin, setRawMode, onChange, escapePastedPaths, shellModeActive, inputFilter, singleLine, getPreferredEditor, }: UseTextBufferProps): TextBuffer;
export interface TextBuffer {
    lines: string[];
    text: string;
    cursor: [number, number];
    /**
     * When the user moves the caret vertically we try to keep their original
     * horizontal column even when passing through shorter lines.  We remember
     * that *preferred* column in this field while the user is still travelling
     * vertically.  Any explicit horizontal movement resets the preference.
     */
    preferredCol: number | null;
    selectionAnchor: [number, number] | null;
    pastedContent: Record<string, string>;
    allVisualLines: string[];
    viewportVisualLines: string[];
    visualCursor: [number, number];
    visualScrollRow: number;
    /**
     * For each visual line (by absolute index in allVisualLines) provides a tuple
     * [logicalLineIndex, startColInLogical] that maps where that visual line
     * begins within the logical buffer. Indices are code-point based.
     */
    visualToLogicalMap: Array<[number, number]>;
    /**
     * For each logical line, an array mapping transformed positions (in the transformed
     * line) back to logical column indices.
     */
    transformedToLogicalMaps: number[][];
    /**
     * For each visual line (absolute index across all visual lines), the start index
     * within that logical line's transformed content.
     */
    visualToTransformedMap: number[];
    /** Cached transformations per logical line */
    transformationsByLine: Transformation[][];
    visualLayout: VisualLayout;
    /**
     * Replaces the entire buffer content with the provided text.
     * The operation is undoable.
     */
    setText: (text: string, cursorPosition?: 'start' | 'end' | number) => void;
    /**
     * Insert a single character or string without newlines.
     */
    insert: (ch: string, opts?: {
        paste?: boolean;
    }) => void;
    newline: () => void;
    backspace: () => void;
    del: () => void;
    move: (dir: Direction) => void;
    undo: () => void;
    redo: () => void;
    /**
     * Replaces the text within the specified range with new text.
     * Handles both single-line and multi-line ranges.
     *
     * @param startRow The starting row index (inclusive).
     * @param startCol The starting column index (inclusive, code-point based).
     * @param endRow The ending row index (inclusive).
     * @param endCol The ending column index (exclusive, code-point based).
     * @param text The new text to insert.
     * @returns True if the buffer was modified, false otherwise.
     */
    replaceRange: (startRow: number, startCol: number, endRow: number, endCol: number, text: string) => void;
    /**
     * Delete the word to the *left* of the caret, mirroring common
     * Ctrl/Alt+Backspace behaviour in editors & terminals. Both the adjacent
     * whitespace *and* the word characters immediately preceding the caret are
     * removed.  If the caret is already at column‑0 this becomes a no-op.
     */
    deleteWordLeft: () => void;
    /**
     * Delete the word to the *right* of the caret, akin to many editors'
     * Ctrl/Alt+Delete shortcut.  Removes any whitespace/punctuation that
     * follows the caret and the next contiguous run of word characters.
     */
    deleteWordRight: () => void;
    /**
     * Deletes text from the cursor to the end of the current line.
     */
    killLineRight: () => void;
    /**
     * Deletes text from the start of the current line to the cursor.
     */
    killLineLeft: () => void;
    /**
     * High level "handleInput" – receives what Ink gives us.
     */
    handleInput: (key: Key) => boolean;
    /**
     * Opens the current buffer contents in the user's preferred terminal text
     * editor ($VISUAL or $EDITOR, falling back to "vi").  The method blocks
     * until the editor exits, then reloads the file and replaces the in‑memory
     * buffer with whatever the user saved.
     *
     * The operation is treated as a single undoable edit – we snapshot the
     * previous state *once* before launching the editor so one `undo()` will
     * revert the entire change set.
     *
     * Note: We purposefully rely on the *synchronous* spawn API so that the
     * calling process genuinely waits for the editor to close before
     * continuing.  This mirrors Git's behaviour and simplifies downstream
     * control‑flow (callers can simply `await` the Promise).
     */
    openInExternalEditor: () => Promise<void>;
    replaceRangeByOffset: (startOffset: number, endOffset: number, replacementText: string) => void;
    getOffset: () => number;
    moveToOffset(offset: number): void;
    moveToVisualPosition(visualRow: number, visualCol: number): void;
    /**
     * Convert visual coordinates to logical position without moving cursor.
     * Returns null if the position is out of bounds.
     */
    getLogicalPositionFromVisual(visualRow: number, visualCol: number): {
        row: number;
        col: number;
    } | null;
    /**
     * Check if a line index falls within an expanded paste region.
     * Returns the paste placeholder ID if found, null otherwise.
     */
    getExpandedPasteAtLine(lineIndex: number): string | null;
    /**
     * Toggle expansion state for a paste placeholder.
     * If collapsed, expands to show full content inline.
     * If expanded, collapses back to placeholder.
     */
    togglePasteExpansion(id: string, row: number, col: number): void;
    /**
     * The current expanded paste info (read-only).
     */
    expandedPaste: ExpandedPasteInfo | null;
    /**
     * Delete N words forward from cursor position (vim 'dw' command)
     */
    vimDeleteWordForward: (count: number) => void;
    /**
     * Delete N words backward from cursor position (vim 'db' command)
     */
    vimDeleteWordBackward: (count: number) => void;
    /**
     * Delete to end of N words from cursor position (vim 'de' command)
     */
    vimDeleteWordEnd: (count: number) => void;
    /**
     * Delete N big words forward from cursor position (vim 'dW' command)
     */
    vimDeleteBigWordForward: (count: number) => void;
    /**
     * Delete N big words backward from cursor position (vim 'dB' command)
     */
    vimDeleteBigWordBackward: (count: number) => void;
    /**
     * Delete to end of N big words from cursor position (vim 'dE' command)
     */
    vimDeleteBigWordEnd: (count: number) => void;
    /**
     * Change N words forward from cursor position (vim 'cw' command)
     */
    vimChangeWordForward: (count: number) => void;
    /**
     * Change N words backward from cursor position (vim 'cb' command)
     */
    vimChangeWordBackward: (count: number) => void;
    /**
     * Change to end of N words from cursor position (vim 'ce' command)
     */
    vimChangeWordEnd: (count: number) => void;
    /**
     * Change N big words forward from cursor position (vim 'cW' command)
     */
    vimChangeBigWordForward: (count: number) => void;
    /**
     * Change N big words backward from cursor position (vim 'cB' command)
     */
    vimChangeBigWordBackward: (count: number) => void;
    /**
     * Change to end of N big words from cursor position (vim 'cE' command)
     */
    vimChangeBigWordEnd: (count: number) => void;
    /**
     * Delete N lines from cursor position (vim 'dd' command)
     */
    vimDeleteLine: (count: number) => void;
    /**
     * Change N lines from cursor position (vim 'cc' command)
     */
    vimChangeLine: (count: number) => void;
    /**
     * Delete from cursor to end of line (vim 'D' command)
     * With count > 1, deletes to end of current line plus (count-1) additional lines
     */
    vimDeleteToEndOfLine: (count?: number) => void;
    /**
     * Delete from start of line to cursor (vim 'd0' command)
     */
    vimDeleteToStartOfLine: () => void;
    /**
     * Change from cursor to end of line (vim 'C' command)
     * With count > 1, changes to end of current line plus (count-1) additional lines
     */
    vimChangeToEndOfLine: (count?: number) => void;
    /**
     * Delete from cursor to first non-whitespace character (vim 'd^' command)
     */
    vimDeleteToFirstNonWhitespace: () => void;
    /**
     * Change from cursor to start of line (vim 'c0' command)
     */
    vimChangeToStartOfLine: () => void;
    /**
     * Change from cursor to first non-whitespace character (vim 'c^' command)
     */
    vimChangeToFirstNonWhitespace: () => void;
    /**
     * Delete from current line to first line (vim 'dgg' command)
     */
    vimDeleteToFirstLine: (count: number) => void;
    /**
     * Delete from current line to last line (vim 'dG' command)
     */
    vimDeleteToLastLine: (count: number) => void;
    /**
     * Change movement operations (vim 'ch', 'cj', 'ck', 'cl' commands)
     */
    vimChangeMovement: (movement: 'h' | 'j' | 'k' | 'l', count: number) => void;
    /**
     * Move cursor left N times (vim 'h' command)
     */
    vimMoveLeft: (count: number) => void;
    /**
     * Move cursor right N times (vim 'l' command)
     */
    vimMoveRight: (count: number) => void;
    /**
     * Move cursor up N times (vim 'k' command)
     */
    vimMoveUp: (count: number) => void;
    /**
     * Move cursor down N times (vim 'j' command)
     */
    vimMoveDown: (count: number) => void;
    /**
     * Move cursor forward N words (vim 'w' command)
     */
    vimMoveWordForward: (count: number) => void;
    /**
     * Move cursor backward N words (vim 'b' command)
     */
    vimMoveWordBackward: (count: number) => void;
    /**
     * Move cursor to end of Nth word (vim 'e' command)
     */
    vimMoveWordEnd: (count: number) => void;
    /**
     * Move cursor forward N big words (vim 'W' command)
     */
    vimMoveBigWordForward: (count: number) => void;
    /**
     * Move cursor backward N big words (vim 'B' command)
     */
    vimMoveBigWordBackward: (count: number) => void;
    /**
     * Move cursor to end of Nth big word (vim 'E' command)
     */
    vimMoveBigWordEnd: (count: number) => void;
    /**
     * Delete N characters at cursor (vim 'x' command)
     */
    vimDeleteChar: (count: number) => void;
    /**
     * Enter insert mode at cursor (vim 'i' command)
     */
    vimInsertAtCursor: () => void;
    /**
     * Enter insert mode after cursor (vim 'a' command)
     */
    vimAppendAtCursor: () => void;
    /**
     * Open new line below and enter insert mode (vim 'o' command)
     */
    vimOpenLineBelow: () => void;
    /**
     * Open new line above and enter insert mode (vim 'O' command)
     */
    vimOpenLineAbove: () => void;
    /**
     * Move to end of line and enter insert mode (vim 'A' command)
     */
    vimAppendAtLineEnd: () => void;
    /**
     * Move to first non-whitespace and enter insert mode (vim 'I' command)
     */
    vimInsertAtLineStart: () => void;
    /**
     * Move cursor to beginning of line (vim '0' command)
     */
    vimMoveToLineStart: () => void;
    /**
     * Move cursor to end of line (vim '$' command)
     */
    vimMoveToLineEnd: () => void;
    /**
     * Move cursor to first non-whitespace character (vim '^' command)
     */
    vimMoveToFirstNonWhitespace: () => void;
    /**
     * Move cursor to first line (vim 'gg' command)
     */
    vimMoveToFirstLine: () => void;
    /**
     * Move cursor to last line (vim 'G' command)
     */
    vimMoveToLastLine: () => void;
    /**
     * Move cursor to specific line number (vim '[N]G' command)
     */
    vimMoveToLine: (lineNumber: number) => void;
    /**
     * Handle escape from insert mode (moves cursor left if not at line start)
     */
    vimEscapeInsertMode: () => void;
}
export {};
