108 lines
2.8 KiB
TypeScript
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;
|
|
}
|
|
}
|