readline: allow tabs in input
authorRich Trott <rtrott@gmail.com>
Thu, 21 May 2015 04:17:10 +0000 (21:17 -0700)
committerBrendan Ashworth <brendan.ashworth@me.com>
Sat, 6 Jun 2015 20:11:19 +0000 (13:11 -0700)
If tab completion is not being used, allow user to enter tab
characters.

PR-URL: https://github.com/nodejs/io.js/pull/1761
Reviewed-By: Brendan Ashworth <brendan.ashworth@me.com>
lib/readline.js
test/parallel/test-readline-interface.js

index 5782eb8..4107c99 100644 (file)
@@ -47,9 +47,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');
   }
 
@@ -72,9 +70,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('> ');
 
@@ -344,9 +344,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);
@@ -839,10 +836,6 @@ Interface.prototype._ttyWrite = function(s, key) {
         this._deleteRight();
         break;
 
-      case 'tab': // tab completion
-        this._tabComplete();
-        break;
-
       case 'left':
         this._moveCursor(-1);
         break;
@@ -867,6 +860,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');
index 6ee9ad2..2e08264 100644 (file)
@@ -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();