diff --git a/src/cli/repl/core.ts b/src/cli/repl/core.ts index 3c4bbfa582..1e8d94e570 100644 --- a/src/cli/repl/core.ts +++ b/src/cli/repl/core.ts @@ -11,8 +11,12 @@ import { commandNames, getCommand, standardReplOutput } from './commands' import * as readline from 'node:readline' import { splitAtEscapeSensitive } from '../../util/args' import { executeRShellCommand } from './commands/execute' +import os from 'os' +import path from 'path' +import fs from 'fs' const replCompleterKeywords = Array.from(commandNames, s => `:${s}`) +const defaultHistoryFile = path.join(os.tmpdir(), '.flowrhistory') /** * Used by the repl to provide automatic completions for a given (partial) input line @@ -26,6 +30,7 @@ export const DEFAULT_REPL_READLINE_CONFIGURATION: readline.ReadLineOptions = { output: process.stdout, tabSize: 4, terminal: true, + history: loadReplHistory(defaultHistoryFile), removeHistoryDuplicates: true, completer: replCompleter } @@ -72,11 +77,14 @@ export async function replProcessAnswer(output: ReplOutput, expr: string, shell: * If you want to provide a custom one but use the same `completer`, refer to {@link replCompleter}. * For the default arguments, see {@link DEFAULT_REPL_READLINE_CONFIGURATION}. * @param output - Defines two methods that every function in the repl uses to output its data. + * @param historyFile - The file to use for persisting the repl's history. Passing undefined causes history not to be saved. * * For the execution, this function makes use of {@link replProcessAnswer} * */ -export async function repl(shell = new RShell({ revive: 'always' }), rl = readline.createInterface(DEFAULT_REPL_READLINE_CONFIGURATION), output = standardReplOutput) { +export async function repl(shell = new RShell({ revive: 'always' }), rl = readline.createInterface(DEFAULT_REPL_READLINE_CONFIGURATION), output = standardReplOutput, historyFile: string | undefined = defaultHistoryFile) { + if(historyFile) + rl.on('history', h => fs.writeFileSync(historyFile, h.join('\n'), {encoding: 'utf-8'})) // the incredible repl :D, we kill it with ':quit' // eslint-disable-next-line no-constant-condition,@typescript-eslint/no-unnecessary-condition @@ -92,3 +100,9 @@ export async function repl(shell = new RShell({ revive: 'always' }), rl = readli }) } } + +export function loadReplHistory(historyFile: string): string[] | undefined { + if(!fs.existsSync(historyFile)) + return undefined + return fs.readFileSync(historyFile, {encoding: 'utf-8'}).split('\n') +}