repl: make ^D emit an 'end' event on the readline instance
authorNathan Rajlich <nathan@tootallnate.net>
Tue, 27 Mar 2012 19:41:42 +0000 (12:41 -0700)
committerNathan Rajlich <nathan@tootallnate.net>
Tue, 27 Mar 2012 20:54:49 +0000 (13:54 -0700)
Also emit 'exit' on the repl when 'end' is emitted on the readline.

Fixes `node debug test/fixtures/breakpoints.js` when ^D is pressed.

doc/api/readline.markdown
doc/api/repl.markdown
lib/readline.js
lib/repl.js
test/simple/test-repl-end-emits-exit.js [new file with mode: 0644]

index e707a27..7c9c00c 100644 (file)
@@ -160,6 +160,14 @@ Example of listening for `resume`:
       console.log('Readline resumed.');
     });
 
+### Event: 'end'
+
+`function () {}`
+
+Emitted when the `input` stream receives its "end" event, or when `^D` is
+pressed by the user. It's generally a good idea to consider this `Interface`
+instance as completed after this is emitted.
+
 ### Event: 'SIGINT'
 
 `function () {}`
index c61c575..622abdf 100644 (file)
@@ -116,7 +116,8 @@ see: https://gist.github.com/2053342
 `function () {}`
 
 Emitted when the user exits the REPL in any of the defined ways. Namely, typing
-`.exit` at the repl, or pressing Ctrl+C twice to signal SIGINT.
+`.exit` at the repl, pressing Ctrl+C twice to signal SIGINT, or pressing Ctrl+D
+to signal "end" on the `input` stream.
 
 Example of listening for `exit`:
 
index 94d88ed..f7dbe05 100644 (file)
@@ -90,6 +90,9 @@ function Interface(input, output, completer, terminal) {
     input.on('data', function(data) {
       self._normalWrite(data);
     });
+    input.on('end', function() {
+      self.emit('end');
+    });
 
   } else {
 
@@ -575,6 +578,7 @@ Interface.prototype._ttyWrite = function(s, key) {
       case 'd': // delete right or EOF
         if (this.cursor === 0 && this.line.length === 0) {
           this.pause();
+          this.emit('end');
         } else if (this.cursor < this.line.length) {
           this._deleteRight();
         }
index c96cbe1..84293e2 100644 (file)
@@ -168,6 +168,11 @@ function REPLServer(prompt, stream, eval, useGlobal, ignoreUndefined) {
 
   rli.setPrompt(self.prompt);
 
+  rli.on('end', function() {
+    self.rli.output.write('\n');
+    self.emit('exit');
+  });
+
   var sawSIGINT = false;
   rli.on('SIGINT', function() {
     var empty = rli.line.length === 0;
diff --git a/test/simple/test-repl-end-emits-exit.js b/test/simple/test-repl-end-emits-exit.js
new file mode 100644 (file)
index 0000000..375c0f8
--- /dev/null
@@ -0,0 +1,77 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var common = require('../common'),
+    assert = require('assert'),
+    Stream = require('stream'),
+    repl = require('repl'),
+    gotTerminalExit = false,
+    gotRegularExit = false;
+
+// create a dummy stream that does nothing
+var stream = new Stream();
+stream.write = stream.pause = stream.resume = function(){};
+stream.readable = stream.writable = true;
+
+function testTerminalMode() {
+  var r1 = repl.start({
+    input: stream,
+    output: stream,
+    terminal: true
+  });
+
+  process.nextTick(function() {
+    // manually fire a ^D keypress
+    stream.emit('data', '\u0004');
+  });
+
+  r1.on('exit', function() {
+    // should be fired from the simulated ^D keypress
+    gotTerminalExit = true;
+    testRegularMode();
+  });
+}
+
+function testRegularMode() {
+  var r2 = repl.start({
+    input: stream,
+    output: stream,
+    terminal: false
+  });
+
+  process.nextTick(function() {
+    stream.emit('end');
+  });
+
+  r2.on('exit', function() {
+    // should be fired from the simulated 'end' event
+    gotRegularExit = true;
+  });
+}
+
+process.on('exit', function() {
+  assert(gotTerminalExit);
+  assert(gotRegularExit);
+});
+
+
+// start
+testTerminalMode();