Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions doc/api/repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -645,14 +645,35 @@ buffered but not yet executed. This method is primarily intended to be
called from within the action function for commands registered using the
`replServer.defineCommand()` method.

### `replServer.setupHistory(historyPath, callback)`
### `replServer.setupHistory(historyConfig, callback)`

<!-- YAML
added: v11.10.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/58225
description: Updated the `historyConfig` parameter to accept an object
with `filePath`, `size`, `removeHistoryDuplicates` and
`onHistoryFileLoaded` properties.
-->

* `historyPath` {string} the path to the history file
* `historyConfig` {Object|string} the path to the history file
If it is a string, it is the path to the history file.
If it is an object, it can have the following properties:
* `filePath` {string} the path to the history file
* `size` {number} Maximum number of history lines retained. To disable
the history set this value to `0`. This option makes sense only if
`terminal` is set to `true` by the user or by an internal `output` check,
otherwise the history caching mechanism is not initialized at all.
**Default:** `30`.
* `removeHistoryDuplicates` {boolean} If `true`, when a new input line added
to the history list duplicates an older one, this removes the older line
from the list. **Default:** `false`.
* `onHistoryFileLoaded` {Function} called when history writes are ready or upon error
* `err` {Error}
* `repl` {repl.REPLServer}
* `callback` {Function} called when history writes are ready or upon error
(Optional if provided as `onHistoryFileLoaded` in `historyConfig`)
* `err` {Error}
* `repl` {repl.REPLServer}

Expand Down
2 changes: 1 addition & 1 deletion lib/internal/main/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ if (process.env.NODE_REPL_EXTERNAL_MODULE) {
throw err;
}
repl.on('exit', () => {
if (repl._flushing) {
if (repl.historyManager.isFlushing) {
return repl.once('flushHistory', () => {
process.exit();
});
Expand Down
192 changes: 57 additions & 135 deletions lib/internal/readline/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
const {
ArrayFrom,
ArrayPrototypeFilter,
ArrayPrototypeIndexOf,
ArrayPrototypeJoin,
ArrayPrototypeMap,
ArrayPrototypePop,
ArrayPrototypePush,
ArrayPrototypeReverse,
ArrayPrototypeShift,
ArrayPrototypeSplice,
ArrayPrototypeUnshift,
DateNow,
FunctionPrototypeCall,
Expand All @@ -19,6 +17,7 @@ const {
MathMax,
MathMaxApply,
NumberIsFinite,
ObjectDefineProperty,
ObjectSetPrototypeOf,
RegExpPrototypeExec,
SafeStringIterator,
Expand All @@ -30,7 +29,6 @@ const {
StringPrototypeSlice,
StringPrototypeSplit,
StringPrototypeStartsWith,
StringPrototypeTrim,
Symbol,
SymbolAsyncIterator,
SymbolDispose,
Expand All @@ -46,8 +44,6 @@ const {

const {
validateAbortSignal,
validateArray,
validateNumber,
validateString,
validateUint32,
} = require('internal/validators');
Expand All @@ -67,7 +63,6 @@ const {
charLengthLeft,
commonPrefix,
kSubstringSearch,
reverseString,
} = require('internal/readline/utils');
let emitKeypressEvents;
let kFirstEventParam;
Expand All @@ -78,8 +73,8 @@ const {
} = require('internal/readline/callbacks');

const { StringDecoder } = require('string_decoder');
const { ReplHistory } = require('internal/repl/history');

const kHistorySize = 30;
const kMaxUndoRedoStackSize = 2048;
const kMincrlfDelay = 100;
/**
Expand Down Expand Up @@ -153,7 +148,6 @@ const kWriteToOutput = Symbol('_writeToOutput');
const kYank = Symbol('_yank');
const kYanking = Symbol('_yanking');
const kYankPop = Symbol('_yankPop');
const kNormalizeHistoryLineEndings = Symbol('_normalizeHistoryLineEndings');
const kSavePreviousState = Symbol('_savePreviousState');
const kRestorePreviousState = Symbol('_restorePreviousState');
const kPreviousLine = Symbol('_previousLine');
Expand All @@ -175,9 +169,6 @@ function InterfaceConstructor(input, output, completer, terminal) {

FunctionPrototypeCall(EventEmitter, this);

let history;
let historySize;
let removeHistoryDuplicates = false;
let crlfDelay;
let prompt = '> ';
let signal;
Expand All @@ -187,14 +178,17 @@ function InterfaceConstructor(input, output, completer, terminal) {
output = input.output;
completer = input.completer;
terminal = input.terminal;
history = input.history;
historySize = input.historySize;
signal = input.signal;

// It is possible to configure the history through the input object
const historySize = input.historySize;
const history = input.history;
const removeHistoryDuplicates = input.removeHistoryDuplicates;

if (input.tabSize !== undefined) {
validateUint32(input.tabSize, 'tabSize', true);
this.tabSize = input.tabSize;
}
removeHistoryDuplicates = input.removeHistoryDuplicates;
if (input.prompt !== undefined) {
prompt = input.prompt;
}
Expand All @@ -215,24 +209,18 @@ function InterfaceConstructor(input, output, completer, terminal) {

crlfDelay = input.crlfDelay;
input = input.input;
}

if (completer !== undefined && typeof completer !== 'function') {
throw new ERR_INVALID_ARG_VALUE('completer', completer);
input.size = historySize;
input.history = history;
input.removeHistoryDuplicates = removeHistoryDuplicates;
}

if (history === undefined) {
history = [];
} else {
validateArray(history, 'history');
}
this.setupHistoryManager(input);

if (historySize === undefined) {
historySize = kHistorySize;
if (completer !== undefined && typeof completer !== 'function') {
throw new ERR_INVALID_ARG_VALUE('completer', completer);
}

validateNumber(historySize, 'historySize', 0);

// Backwards compat; check the isTTY prop of the output stream
// when `terminal` was not specified
if (terminal === undefined && !(output === null || output === undefined)) {
Expand All @@ -248,8 +236,6 @@ function InterfaceConstructor(input, output, completer, terminal) {
this.input = input;
this[kUndoStack] = [];
this[kRedoStack] = [];
this.history = history;
this.historySize = historySize;
this[kPreviousCursorCols] = -1;

// The kill ring is a global list of blocks of text that were previously
Expand All @@ -260,7 +246,6 @@ function InterfaceConstructor(input, output, completer, terminal) {
this[kKillRing] = [];
this[kKillRingCursor] = 0;

this.removeHistoryDuplicates = !!removeHistoryDuplicates;
this.crlfDelay = crlfDelay ?
MathMax(kMincrlfDelay, crlfDelay) :
kMincrlfDelay;
Expand All @@ -270,7 +255,6 @@ function InterfaceConstructor(input, output, completer, terminal) {

this.terminal = !!terminal;


function onerror(err) {
self.emit('error', err);
}
Expand Down Expand Up @@ -349,8 +333,6 @@ function InterfaceConstructor(input, output, completer, terminal) {
// Cursor position on the line.
this.cursor = 0;

this.historyIndex = -1;

if (output !== null && output !== undefined)
output.on('resize', onresize);

Expand Down Expand Up @@ -403,6 +385,36 @@ class Interface extends InterfaceConstructor {
return this[kPrompt];
}

setupHistoryManager(options) {
this.historyManager = new ReplHistory(this, options);

if (options.onHistoryFileLoaded) {
this.historyManager.initialize(options.onHistoryFileLoaded);
}
Comment on lines +391 to +393