takt/src/interactive/history-manager.ts
2026-01-25 15:16:27 +09:00

108 lines
2.8 KiB
TypeScript

/**
* Input history management with persistence
*
* Manages input history for the interactive REPL, providing:
* - In-memory history for session use
* - Persistent storage for cross-session recall
* - Navigation through history entries
*/
import {
loadInputHistory,
saveInputHistory,
} from '../config/paths.js';
/**
* Manages input history with persistence.
* Provides a unified interface for in-memory and file-based history.
*/
export class InputHistoryManager {
private history: string[];
private readonly projectDir: string;
private index: number;
private currentInput: string;
constructor(projectDir: string) {
this.projectDir = projectDir;
this.history = loadInputHistory(projectDir);
this.index = this.history.length;
this.currentInput = '';
}
/** Add an entry to history (both in-memory and persistent) */
add(input: string): void {
// Don't add consecutive duplicates
if (this.history[this.history.length - 1] !== input) {
this.history.push(input);
saveInputHistory(this.projectDir, this.history);
}
}
/** Get the current history array (read-only) */
getHistory(): readonly string[] {
return this.history;
}
/** Get the current history index */
getIndex(): number {
return this.index;
}
/** Reset index to the end of history */
resetIndex(): void {
this.index = this.history.length;
this.currentInput = '';
}
/** Save the current input before navigating history */
saveCurrentInput(input: string): void {
if (this.index === this.history.length) {
this.currentInput = input;
}
}
/** Get the saved current input */
getCurrentInput(): string {
return this.currentInput;
}
/** Navigate to the previous entry. Returns the entry or undefined if at start. */
navigatePrevious(): string | undefined {
if (this.index > 0) {
this.index--;
return this.history[this.index];
}
return undefined;
}
/** Navigate to the next entry. Returns the entry, current input at end, or undefined. */
navigateNext(): { entry: string; isCurrentInput: boolean } | undefined {
if (this.index < this.history.length) {
this.index++;
if (this.index === this.history.length) {
return { entry: this.currentInput, isCurrentInput: true };
}
const entry = this.history[this.index];
if (entry !== undefined) {
return { entry, isCurrentInput: false };
}
}
return undefined;
}
/** Check if currently at a history entry (not at the end) */
isAtHistoryEntry(): boolean {
return this.index < this.history.length;
}
/** Get the entry at the current index */
getCurrentEntry(): string | undefined {
return this.history[this.index];
}
/** Get the total number of history entries */
get length(): number {
return this.history.length;
}
}