From e890221f3688823bee81ed0393302f891c801d77 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 5 Feb 2024 14:06:00 +0100 Subject: [PATCH 1/2] feat(repl): save and load history to temp file --- src/cli/repl/core.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/cli/repl/core.ts b/src/cli/repl/core.ts index 3c4bbfa582..70ade27d85 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: loadHistory(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 }) } } + +function loadHistory(historyFile: string): string[] | undefined { + if(!fs.existsSync(historyFile)) + return undefined + return fs.readFileSync(historyFile, {encoding: 'utf-8'}).split('\n') +} From af750a19a24ce5fc3bcfef8a502a58fc51c7f2ec Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 5 Feb 2024 14:15:01 +0100 Subject: [PATCH 2/2] refactor: expose the loadReplHistory function in case someone uses a different file --- src/cli/repl/core.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/repl/core.ts b/src/cli/repl/core.ts index 70ade27d85..1e8d94e570 100644 --- a/src/cli/repl/core.ts +++ b/src/cli/repl/core.ts @@ -30,7 +30,7 @@ export const DEFAULT_REPL_READLINE_CONFIGURATION: readline.ReadLineOptions = { output: process.stdout, tabSize: 4, terminal: true, - history: loadHistory(defaultHistoryFile), + history: loadReplHistory(defaultHistoryFile), removeHistoryDuplicates: true, completer: replCompleter } @@ -101,7 +101,7 @@ export async function repl(shell = new RShell({ revive: 'always' }), rl = readli } } -function loadHistory(historyFile: string): string[] | undefined { +export function loadReplHistory(historyFile: string): string[] | undefined { if(!fs.existsSync(historyFile)) return undefined return fs.readFileSync(historyFile, {encoding: 'utf-8'}).split('\n')