diff --git a/lib/readline.js b/lib/readline.js index 741f58b934b688..02fa9d08f666f9 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -49,9 +49,7 @@ function Interface(input, output, completer, terminal) { } historySize = historySize || kHistorySize; - completer = completer || function() { return []; }; - - if (typeof completer !== 'function') { + if (completer && typeof completer !== 'function') { throw new TypeError('Argument \'completer\' must be a function'); } @@ -74,9 +72,11 @@ function Interface(input, output, completer, terminal) { this.historySize = historySize; // Check arity, 2 - for async, 1 for sync - this.completer = completer.length === 2 ? completer : function(v, callback) { - callback(null, completer(v)); - }; + if (typeof completer === 'function') { + this.completer = completer.length === 2 ? completer : function(v, cb) { + cb(null, completer(v)); + }; + } this.setPrompt('> '); @@ -346,9 +346,6 @@ Interface.prototype._normalWrite = function(b) { }; Interface.prototype._insertString = function(c) { - //BUG: Problem when adding tabs with following content. - // Perhaps the bug is in _refreshLine(). Not sure. - // A hack would be to insert spaces instead of literal '\t'. if (this.cursor < this.line.length) { var beg = this.line.slice(0, this.cursor); var end = this.line.slice(this.cursor, this.line.length); @@ -841,10 +838,6 @@ Interface.prototype._ttyWrite = function(s, key) { this._deleteRight(); break; - case 'tab': // tab completion - this._tabComplete(); - break; - case 'left': this._moveCursor(-1); break; @@ -869,6 +862,14 @@ Interface.prototype._ttyWrite = function(s, key) { this._historyNext(); break; + case 'tab': + // If tab completion enabled, do that... + if (typeof this.completer === 'function') { + this._tabComplete(); + break; + } + // falls through + default: if (s instanceof Buffer) s = s.toString('utf-8'); diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index e11cfd333b9dfb..5880d022502711 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -175,6 +175,59 @@ function isWarned(emitter) { assert.equal(callCount, expectedLines.length); rli.close(); + // \t when there is no completer function should behave like an ordinary + // character + fi = new FakeInput(); + rli = new readline.Interface({ input: fi, output: fi, terminal: true }); + called = false; + rli.on('line', function(line) { + assert.equal(line, '\t'); + assert.strictEqual(called, false); + called = true; + }); + fi.emit('data', '\t'); + fi.emit('data', '\n'); + assert.ok(called); + rli.close(); + + // \t does not become part of the input when there is a completer function + fi = new FakeInput(); + var completer = function(line) { + return [[], line]; + }; + rli = new readline.Interface({ + input: fi, + output: fi, + terminal: true, + completer: completer + }); + called = false; + rli.on('line', function(line) { + assert.equal(line, 'foo'); + assert.strictEqual(called, false); + called = true; + }); + fi.emit('data', '\tfo\to\t'); + fi.emit('data', '\n'); + assert.ok(called); + rli.close(); + + // constructor throws if completer is not a function or undefined + fi = new FakeInput(); + assert.throws(function() { + readline.createInterface({ + input: fi, + completer: 'string is not valid' + }); + }, function(err) { + if (err instanceof TypeError) { + if (/Argument \'completer\' must be a function/.test(err)) { + return true; + } + } + return false; + }); + // sending a multi-byte utf8 char over multiple writes var buf = Buffer('☮', 'utf8'); fi = new FakeInput();