buroventures-harald-code
Version:
Harald Code - AI-powered coding assistant CLI
455 lines (454 loc) • 13.2 kB
TypeScript
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
export type Direction = 'left' | 'right' | 'up' | 'down' | 'wordLeft' | 'wordRight' | 'home' | 'end';
export declare const findNextWordStart: (text: string, currentOffset: number) => number;
export declare const findPrevWordStart: (text: string, currentOffset: number) => number;
export declare const findWordEnd: (text: string, currentOffset: number) => number;
export declare const getOffsetFromPosition: (row: number, col: number, lines: string[]) => number;
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;
isValidPath: (path: string) => boolean;
shellModeActive?: boolean;
}
interface UndoHistoryEntry {
lines: string[];
cursorRow: number;
cursorCol: number;
}
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;
export interface TextBufferState {
lines: string[];
cursorRow: number;
cursorCol: number;
preferredCol: number | null;
undoStack: UndoHistoryEntry[];
redoStack: UndoHistoryEntry[];
clipboard: string | null;
selectionAnchor: [number, number] | null;
viewportWidth: number;
}
export declare const pushUndo: (currentState: TextBufferState) => TextBufferState;
export type TextBufferAction = {
type: 'set_text';
payload: string;
pushToUndo?: boolean;
} | {
type: 'insert';
payload: string;
} | {
type: 'backspace';
} | {
type: 'move';
payload: {
dir: Direction;
};
} | {
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_width';
payload: 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_change_word_forward';
payload: {
count: number;
};
} | {
type: 'vim_change_word_backward';
payload: {
count: number;
};
} | {
type: 'vim_change_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';
} | {
type: 'vim_change_to_end_of_line';
} | {
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_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';
};
export declare function textBufferReducer(state: TextBufferState, action: TextBufferAction): TextBufferState;
export declare function useTextBuffer({ initialText, initialCursorOffset, viewport, stdin, setRawMode, onChange, isValidPath, shellModeActive, }: 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;
allVisualLines: string[];
viewportVisualLines: string[];
visualCursor: [number, number];
visualScrollRow: number;
/**
* Replaces the entire buffer content with the provided text.
* The operation is undoable.
*/
setText: (text: string) => 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: {
name: string;
ctrl: boolean;
meta: boolean;
shift: boolean;
paste: boolean;
sequence: string;
}) => void;
/**
* 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: (opts?: {
editor?: string;
}) => Promise<void>;
replaceRangeByOffset: (startOffset: number, endOffset: number, replacementText: string) => void;
moveToOffset(offset: number): void;
/**
* 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;
/**
* 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;
/**
* 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)
*/
vimDeleteToEndOfLine: () => void;
/**
* Change from cursor to end of line (vim 'C' command)
*/
vimChangeToEndOfLine: () => 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;
/**
* 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 {};