readline: add option to stop duplicates in history
Adds `options.deDupeHistory` for `readline.createInterface(options)`. If `options.deDupeHistory` is `true`, when a new input line being added to the history list duplicates an older one, removes the older line from the list. Defaults to `false`. Many users would appreciate this option, as it is a common setting in shells. This option certainly should not be default behavior, as it would be problematic in applications such as the `repl`, which inherits from the readline `Interface`. Extends documentation to reflect this API addition. Adds tests for when `options.deDupeHistory` is truthy, and when `options.deDupeHistory` is falsey. PR-URL: https://github.com/nodejs/node/pull/2982 Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
This commit is contained in:
Родитель
df69d95b76
Коммит
5bda5faffd
|
@ -370,6 +370,9 @@ changes:
|
|||
`crlfDelay` milliseconds, both `\r` and `\n` will be treated as separate
|
||||
end-of-line input. Default to `100` milliseconds.
|
||||
`crlfDelay` will be coerced to `[100, 2000]` range.
|
||||
* `deDupeHistory` {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. Defaults to `false`.
|
||||
|
||||
The `readline.createInterface()` method creates a new `readline.Interface`
|
||||
instance.
|
||||
|
|
|
@ -60,6 +60,7 @@ function Interface(input, output, completer, terminal) {
|
|||
|
||||
EventEmitter.call(this);
|
||||
var historySize;
|
||||
var deDupeHistory = false;
|
||||
let crlfDelay;
|
||||
let prompt = '> ';
|
||||
|
||||
|
@ -69,6 +70,7 @@ function Interface(input, output, completer, terminal) {
|
|||
completer = input.completer;
|
||||
terminal = input.terminal;
|
||||
historySize = input.historySize;
|
||||
deDupeHistory = input.deDupeHistory;
|
||||
if (input.prompt !== undefined) {
|
||||
prompt = input.prompt;
|
||||
}
|
||||
|
@ -101,6 +103,7 @@ function Interface(input, output, completer, terminal) {
|
|||
this.output = output;
|
||||
this.input = input;
|
||||
this.historySize = historySize;
|
||||
this.deDupeHistory = !!deDupeHistory;
|
||||
this.crlfDelay = Math.max(kMincrlfDelay,
|
||||
Math.min(kMaxcrlfDelay, crlfDelay >>> 0));
|
||||
|
||||
|
@ -278,6 +281,12 @@ Interface.prototype._addHistory = function() {
|
|||
if (this.line.trim().length === 0) return this.line;
|
||||
|
||||
if (this.history.length === 0 || this.history[0] !== this.line) {
|
||||
if (this.deDupeHistory) {
|
||||
// Remove older history line if identical to new one
|
||||
const dupIndex = this.history.indexOf(this.line);
|
||||
if (dupIndex !== -1) this.history.splice(dupIndex, 1);
|
||||
}
|
||||
|
||||
this.history.unshift(this.line);
|
||||
|
||||
// Only store so many
|
||||
|
|
|
@ -326,6 +326,67 @@ function isWarned(emitter) {
|
|||
return false;
|
||||
});
|
||||
|
||||
// duplicate lines are removed from history when `options.deDupeHistory`
|
||||
// is `true`
|
||||
fi = new FakeInput();
|
||||
rli = new readline.Interface({
|
||||
input: fi,
|
||||
output: fi,
|
||||
terminal: true,
|
||||
deDupeHistory: true
|
||||
});
|
||||
expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
|
||||
callCount = 0;
|
||||
rli.on('line', function(line) {
|
||||
assert.strictEqual(line, expectedLines[callCount]);
|
||||
callCount++;
|
||||
});
|
||||
fi.emit('data', expectedLines.join('\n') + '\n');
|
||||
assert.strictEqual(callCount, expectedLines.length);
|
||||
fi.emit('keypress', '.', { name: 'up' }); // 'bat'
|
||||
assert.strictEqual(rli.line, expectedLines[--callCount]);
|
||||
fi.emit('keypress', '.', { name: 'up' }); // 'bar'
|
||||
assert.notStrictEqual(rli.line, expectedLines[--callCount]);
|
||||
assert.strictEqual(rli.line, expectedLines[--callCount]);
|
||||
fi.emit('keypress', '.', { name: 'up' }); // 'baz'
|
||||
assert.strictEqual(rli.line, expectedLines[--callCount]);
|
||||
fi.emit('keypress', '.', { name: 'up' }); // 'foo'
|
||||
assert.notStrictEqual(rli.line, expectedLines[--callCount]);
|
||||
assert.strictEqual(rli.line, expectedLines[--callCount]);
|
||||
assert.strictEqual(callCount, 0);
|
||||
rli.close();
|
||||
|
||||
// duplicate lines are not removed from history when `options.deDupeHistory`
|
||||
// is `false`
|
||||
fi = new FakeInput();
|
||||
rli = new readline.Interface({
|
||||
input: fi,
|
||||
output: fi,
|
||||
terminal: true,
|
||||
deDupeHistory: false
|
||||
});
|
||||
expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
|
||||
callCount = 0;
|
||||
rli.on('line', function(line) {
|
||||
assert.strictEqual(line, expectedLines[callCount]);
|
||||
callCount++;
|
||||
});
|
||||
fi.emit('data', expectedLines.join('\n') + '\n');
|
||||
assert.strictEqual(callCount, expectedLines.length);
|
||||
fi.emit('keypress', '.', { name: 'up' }); // 'bat'
|
||||
assert.strictEqual(rli.line, expectedLines[--callCount]);
|
||||
fi.emit('keypress', '.', { name: 'up' }); // 'bar'
|
||||
assert.notStrictEqual(rli.line, expectedLines[--callCount]);
|
||||
assert.strictEqual(rli.line, expectedLines[--callCount]);
|
||||
fi.emit('keypress', '.', { name: 'up' }); // 'baz'
|
||||
assert.strictEqual(rli.line, expectedLines[--callCount]);
|
||||
fi.emit('keypress', '.', { name: 'up' }); // 'bar'
|
||||
assert.strictEqual(rli.line, expectedLines[--callCount]);
|
||||
fi.emit('keypress', '.', { name: 'up' }); // 'foo'
|
||||
assert.strictEqual(rli.line, expectedLines[--callCount]);
|
||||
assert.strictEqual(callCount, 0);
|
||||
rli.close();
|
||||
|
||||
// sending a multi-byte utf8 char over multiple writes
|
||||
const buf = Buffer.from('☮', 'utf8');
|
||||
fi = new FakeInput();
|
||||
|
|
Загрузка…
Ссылка в новой задаче