readline: show completions only after 2nd TAB
Show `TAB` completion suggestions only after the user has pressed `TAB` twice in a row, so that the full list of suggestions doesn’t present a distraction. The first time a `TAB` key is pressed, only partial longest-common-prefix completion is performed. This moves the `readline` autocompletion a lot closer to what e.g. `bash` does. Fixes: https://github.com/nodejs/node/issues/7665 PR-URL: https://github.com/nodejs/node/pull/7754 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
c809b88345
commit
1a9e247c79
@ -41,6 +41,7 @@ function Interface(input, output, completer, terminal) {
|
||||
|
||||
this._sawReturn = false;
|
||||
this.isCompletionEnabled = true;
|
||||
this._previousKey = null;
|
||||
|
||||
EventEmitter.call(this);
|
||||
var historySize;
|
||||
@ -391,7 +392,7 @@ Interface.prototype._insertString = function(c) {
|
||||
}
|
||||
};
|
||||
|
||||
Interface.prototype._tabComplete = function() {
|
||||
Interface.prototype._tabComplete = function(lastKeypressWasTab) {
|
||||
var self = this;
|
||||
|
||||
self.pause();
|
||||
@ -407,9 +408,7 @@ Interface.prototype._tabComplete = function() {
|
||||
const completeOn = rv[1]; // the text that was completed
|
||||
if (completions && completions.length) {
|
||||
// Apply/show completions.
|
||||
if (completions.length === 1) {
|
||||
self._insertString(completions[0].slice(completeOn.length));
|
||||
} else {
|
||||
if (lastKeypressWasTab) {
|
||||
self._writeToOutput('\r\n');
|
||||
var width = completions.reduce(function(a, b) {
|
||||
return a.length > b.length ? a : b;
|
||||
@ -429,16 +428,15 @@ Interface.prototype._tabComplete = function() {
|
||||
}
|
||||
}
|
||||
handleGroup(self, group, width, maxColumns);
|
||||
|
||||
// If there is a common prefix to all matches, then apply that
|
||||
// portion.
|
||||
var f = completions.filter(function(e) { if (e) return e; });
|
||||
var prefix = commonPrefix(f);
|
||||
if (prefix.length > completeOn.length) {
|
||||
self._insertString(prefix.slice(completeOn.length));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If there is a common prefix to all matches, then apply that portion.
|
||||
const f = completions.filter(function(e) { if (e) return e; });
|
||||
const prefix = commonPrefix(f);
|
||||
if (prefix.length > completeOn.length) {
|
||||
self._insertString(prefix.slice(completeOn.length));
|
||||
}
|
||||
|
||||
self._refreshLine();
|
||||
}
|
||||
});
|
||||
@ -474,6 +472,7 @@ function commonPrefix(strings) {
|
||||
if (!strings || strings.length == 0) {
|
||||
return '';
|
||||
}
|
||||
if (strings.length === 1) return strings[0];
|
||||
var sorted = strings.slice().sort();
|
||||
var min = sorted[0];
|
||||
var max = sorted[sorted.length - 1];
|
||||
@ -688,7 +687,9 @@ Interface.prototype._moveCursor = function(dx) {
|
||||
|
||||
// handle a write from the tty
|
||||
Interface.prototype._ttyWrite = function(s, key) {
|
||||
const previousKey = this._previousKey;
|
||||
key = key || {};
|
||||
this._previousKey = key;
|
||||
|
||||
// Ignore escape key - Fixes #2876
|
||||
if (key.name == 'escape') return;
|
||||
@ -892,7 +893,8 @@ Interface.prototype._ttyWrite = function(s, key) {
|
||||
case 'tab':
|
||||
// If tab completion enabled, do that...
|
||||
if (typeof this.completer === 'function' && this.isCompletionEnabled) {
|
||||
this._tabComplete();
|
||||
const lastKeypressWasTab = previousKey && previousKey.name === 'tab';
|
||||
this._tabComplete(lastKeypressWasTab);
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const PassThrough = require('stream').PassThrough;
|
||||
const readline = require('readline');
|
||||
@ -26,12 +26,17 @@ oStream.on('data', function(data) {
|
||||
output += data;
|
||||
});
|
||||
|
||||
oStream.on('end', function() {
|
||||
oStream.on('end', common.mustCall(() => {
|
||||
const expect = 'process.stdout\r\n' +
|
||||
'process.stdin\r\n' +
|
||||
'process.stderr';
|
||||
assert(new RegExp(expect).test(output));
|
||||
});
|
||||
}));
|
||||
|
||||
iStream.write('process.std\t');
|
||||
iStream.write('process.s\t');
|
||||
|
||||
assert(/process.std\b/.test(output)); // Completion works.
|
||||
assert(!/stdout/.test(output)); // Completion doesn’t show all results yet.
|
||||
|
||||
iStream.write('\t');
|
||||
oStream.end();
|
||||
|
Loading…
x
Reference in New Issue
Block a user