Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8bd6ab7

Browse filesBrowse files
DannyNemerMylesBorins
authored andcommitted
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: #2982 Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
1 parent 62a8f47 commit 8bd6ab7
Copy full SHA for 8bd6ab7

File tree

Expand file treeCollapse file tree

3 files changed

+73
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+73
-0
lines changed
Open diff view settings
Collapse file

‎doc/api/readline.md‎

Copy file name to clipboardExpand all lines: doc/api/readline.md
+3Lines changed: 3 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,9 @@ added: v0.1.98
363363
`crlfDelay` milliseconds, both `\r` and `\n` will be treated as separate
364364
end-of-line input. Default to `100` milliseconds.
365365
`crlfDelay` will be coerced to `[100, 2000]` range.
366+
* `deDupeHistory` {boolean} If `true`, when a new input line added to the
367+
history list duplicates an older one, this removes the older line from the
368+
list. Defaults to `false`.
366369

367370
The `readline.createInterface()` method creates a new `readline.Interface`
368371
instance.
Collapse file

‎lib/readline.js‎

Copy file name to clipboardExpand all lines: lib/readline.js
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ function Interface(input, output, completer, terminal) {
3939

4040
EventEmitter.call(this);
4141
var historySize;
42+
var deDupeHistory = false;
4243
let crlfDelay;
4344
let prompt = '> ';
4445

@@ -48,6 +49,7 @@ function Interface(input, output, completer, terminal) {
4849
completer = input.completer;
4950
terminal = input.terminal;
5051
historySize = input.historySize;
52+
deDupeHistory = input.deDupeHistory;
5153
if (input.prompt !== undefined) {
5254
prompt = input.prompt;
5355
}
@@ -80,6 +82,7 @@ function Interface(input, output, completer, terminal) {
8082
this.output = output;
8183
this.input = input;
8284
this.historySize = historySize;
85+
this.deDupeHistory = !!deDupeHistory;
8386
this.crlfDelay = Math.max(kMincrlfDelay,
8487
Math.min(kMaxcrlfDelay, crlfDelay >>> 0));
8588

@@ -249,6 +252,12 @@ Interface.prototype._addHistory = function() {
249252
if (this.line.trim().length === 0) return this.line;
250253

251254
if (this.history.length === 0 || this.history[0] !== this.line) {
255+
if (this.deDupeHistory) {
256+
// Remove older history line if identical to new one
257+
const dupIndex = this.history.indexOf(this.line);
258+
if (dupIndex !== -1) this.history.splice(dupIndex, 1);
259+
}
260+
252261
this.history.unshift(this.line);
253262

254263
// Only store so many
Collapse file

‎test/parallel/test-readline-interface.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-readline-interface.js
+61Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,67 @@ function isWarned(emitter) {
303303
return false;
304304
});
305305

306+
// duplicate lines are removed from history when `options.deDupeHistory`
307+
// is `true`
308+
fi = new FakeInput();
309+
rli = new readline.Interface({
310+
input: fi,
311+
output: fi,
312+
terminal: true,
313+
deDupeHistory: true
314+
});
315+
expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
316+
callCount = 0;
317+
rli.on('line', function(line) {
318+
assert.strictEqual(line, expectedLines[callCount]);
319+
callCount++;
320+
});
321+
fi.emit('data', expectedLines.join('\n') + '\n');
322+
assert.strictEqual(callCount, expectedLines.length);
323+
fi.emit('keypress', '.', { name: 'up' }); // 'bat'
324+
assert.strictEqual(rli.line, expectedLines[--callCount]);
325+
fi.emit('keypress', '.', { name: 'up' }); // 'bar'
326+
assert.notStrictEqual(rli.line, expectedLines[--callCount]);
327+
assert.strictEqual(rli.line, expectedLines[--callCount]);
328+
fi.emit('keypress', '.', { name: 'up' }); // 'baz'
329+
assert.strictEqual(rli.line, expectedLines[--callCount]);
330+
fi.emit('keypress', '.', { name: 'up' }); // 'foo'
331+
assert.notStrictEqual(rli.line, expectedLines[--callCount]);
332+
assert.strictEqual(rli.line, expectedLines[--callCount]);
333+
assert.strictEqual(callCount, 0);
334+
rli.close();
335+
336+
// duplicate lines are not removed from history when `options.deDupeHistory`
337+
// is `false`
338+
fi = new FakeInput();
339+
rli = new readline.Interface({
340+
input: fi,
341+
output: fi,
342+
terminal: true,
343+
deDupeHistory: false
344+
});
345+
expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
346+
callCount = 0;
347+
rli.on('line', function(line) {
348+
assert.strictEqual(line, expectedLines[callCount]);
349+
callCount++;
350+
});
351+
fi.emit('data', expectedLines.join('\n') + '\n');
352+
assert.strictEqual(callCount, expectedLines.length);
353+
fi.emit('keypress', '.', { name: 'up' }); // 'bat'
354+
assert.strictEqual(rli.line, expectedLines[--callCount]);
355+
fi.emit('keypress', '.', { name: 'up' }); // 'bar'
356+
assert.notStrictEqual(rli.line, expectedLines[--callCount]);
357+
assert.strictEqual(rli.line, expectedLines[--callCount]);
358+
fi.emit('keypress', '.', { name: 'up' }); // 'baz'
359+
assert.strictEqual(rli.line, expectedLines[--callCount]);
360+
fi.emit('keypress', '.', { name: 'up' }); // 'bar'
361+
assert.strictEqual(rli.line, expectedLines[--callCount]);
362+
fi.emit('keypress', '.', { name: 'up' }); // 'foo'
363+
assert.strictEqual(rli.line, expectedLines[--callCount]);
364+
assert.strictEqual(callCount, 0);
365+
rli.close();
366+
306367
// sending a multi-byte utf8 char over multiple writes
307368
const buf = Buffer.from('☮', 'utf8');
308369
fi = new FakeInput();

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.