diff --git a/docs/recipes/watch-mode.md b/docs/recipes/watch-mode.md
index e88815b0d..ab1807d36 100644
--- a/docs/recipes/watch-mode.md
+++ b/docs/recipes/watch-mode.md
@@ -71,7 +71,7 @@ Dependency tracking works for required modules. Custom extensions and transpiler
## Manually rerunning all tests
-You can quickly rerun all tests by typing r on the console, followed by Enter.
+You can quickly rerun all tests by typing r on the console.
## Debugging
diff --git a/lib/watcher.js b/lib/watcher.js
index 9398cadb4..b25bb8aec 100644
--- a/lib/watcher.js
+++ b/lib/watcher.js
@@ -8,6 +8,7 @@ var union = require('array-union');
var uniq = require('array-uniq');
var defaultIgnore = require('ignore-by-default').directories();
var multimatch = require('multimatch');
+var keypress = require('keypress');
var nodePath = require('path');
var slash = require('slash');
@@ -149,11 +150,19 @@ Watcher.prototype.cleanUnlinkedTests = function (unlinkedTests) {
Watcher.prototype.observeStdin = function (stdin) {
var self = this;
+ if (stdin.isTTY) {
+ keypress(stdin);
+ stdin.setRawMode(true);
+ }
+
stdin.resume();
stdin.setEncoding('utf8');
- stdin.on('data', function (data) {
- data = data.trim().toLowerCase();
- if (data !== 'r' && data !== 'rs') {
+ stdin.on('keypress', function (char, key) {
+ if (key.ctrl && key.name === 'c') {
+ process.exit(2);
+ }
+
+ if (char !== 'r') {
return;
}
diff --git a/package.json b/package.json
index c25626824..f3a2d9503 100644
--- a/package.json
+++ b/package.json
@@ -108,6 +108,7 @@
"is-obj": "^1.0.0",
"is-observable": "^0.1.0",
"is-promise": "^2.1.0",
+ "keypress": "^0.2.1",
"last-line-stream": "^1.0.0",
"loud-rejection": "^1.2.0",
"matcher": "^0.1.1",
diff --git a/test/watcher.js b/test/watcher.js
index fdcc20607..ebf10fa09 100644
--- a/test/watcher.js
+++ b/test/watcher.js
@@ -113,6 +113,9 @@ group('chokidar is installed', function (beforeEach, test, group) {
stdin = new PassThrough();
stdin.pause();
+ stdin.on('data', function (data) {
+ stdin.emit('keypress', data, {});
+ });
});
var start = function (sources) {
@@ -511,102 +514,100 @@ group('chokidar is installed', function (beforeEach, test, group) {
});
});
- ["r", "rs"].forEach(function (input) {
- test('reruns initial tests when "' + input + '" is entered on stdin', function (t) {
- t.plan(2);
- api.run.returns(Promise.resolve());
- start().observeStdin(stdin);
+ test('reruns initial tests when "r" is entered on stdin', function (t) {
+ t.plan(2);
+ api.run.returns(Promise.resolve());
+ start().observeStdin(stdin);
- stdin.write(input + '\n');
- return delay().then(function () {
- t.ok(api.run.calledTwice);
+ stdin.write('r');
+ return delay().then(function () {
+ t.ok(api.run.calledTwice);
- stdin.write('\t' + input + ' \n');
- return delay();
- }).then(function () {
- t.ok(api.run.calledThrice);
- });
+ stdin.write('r');
+ return delay();
+ }).then(function () {
+ t.ok(api.run.calledThrice);
});
+ });
- test('entering "' + input + '" on stdin cancels any debouncing', function (t) {
- t.plan(7);
- api.run.returns(Promise.resolve());
- start().observeStdin(stdin);
+ test('entering "r" on stdin cancels any debouncing', function (t) {
+ t.plan(7);
+ api.run.returns(Promise.resolve());
+ start().observeStdin(stdin);
- var before = clock.now;
- var done;
- api.run.returns(new Promise(function (resolve) {
- done = resolve;
- }));
+ var before = clock.now;
+ var done;
+ api.run.returns(new Promise(function (resolve) {
+ done = resolve;
+ }));
- add();
- stdin.write(input + '\n');
- return delay().then(function () {
- // Processing "rs" caused a new run.
- t.ok(api.run.calledTwice);
+ add();
+ stdin.write('r');
+ return delay().then(function () {
+ // Processing "r" caused a new run.
+ t.ok(api.run.calledTwice);
- // Try to advance the clock. This is *after* input was processed. The
- // debounce timeout should have been canceled, so the clock can't have
- // advanced.
- clock.next();
- t.is(before, clock.now);
+ // Try to advance the clock. This is *after* input was processed. The
+ // debounce timeout should have been canceled, so the clock can't have
+ // advanced.
+ clock.next();
+ t.is(before, clock.now);
- add();
- // Advance clock *before* input is received. Note that the previous run
- // hasn't finished yet.
- clock.next();
- stdin.write(input + '\n');
+ add();
+ // Advance clock *before* input is received. Note that the previous run
+ // hasn't finished yet.
+ clock.next();
+ stdin.write('r');
- return delay();
- }).then(function () {
- // No new runs yet.
- t.ok(api.run.calledTwice);
- // Though the clock has advanced.
- t.is(clock.now - before, 10);
- before = clock.now;
+ return delay();
+ }).then(function () {
+ // No new runs yet.
+ t.ok(api.run.calledTwice);
+ // Though the clock has advanced.
+ t.is(clock.now - before, 10);
+ before = clock.now;
- var previous = done;
- api.run.returns(new Promise(function (resolve) {
- done = resolve;
- }));
+ var previous = done;
+ api.run.returns(new Promise(function (resolve) {
+ done = resolve;
+ }));
- // Finish the previous run.
- previous();
+ // Finish the previous run.
+ previous();
- return delay();
- }).then(function () {
- // There's only one new run.
- t.ok(api.run.calledThrice);
+ return delay();
+ }).then(function () {
+ // There's only one new run.
+ t.ok(api.run.calledThrice);
- stdin.write(input + '\n');
- return delay();
- }).then(function () {
- add();
+ stdin.write('r');
+ return delay();
+ }).then(function () {
+ add();
- // Finish the previous run. This should cause a new run due to the
- // input.
- done();
+ // Finish the previous run. This should cause a new run due to the
+ // input.
+ done();
- return delay();
- }).then(function () {
- // Again there's only one new run.
- t.is(api.run.callCount, 4);
+ return delay();
+ }).then(function () {
+ // Again there's only one new run.
+ t.is(api.run.callCount, 4);
- // Try to advance the clock. This is *after* input was processed. The
- // debounce timeout should have been canceled, so the clock can't have
- // advanced.
- clock.next();
- t.is(before, clock.now);
- });
+ // Try to advance the clock. This is *after* input was processed. The
+ // debounce timeout should have been canceled, so the clock can't have
+ // advanced.
+ clock.next();
+ t.is(before, clock.now);
});
});
- test('does nothing if anything other than "rs" is entered on stdin', function (t) {
+ test('does nothing if anything other than "r" is entered on stdin', function (t) {
t.plan(2);
api.run.returns(Promise.resolve());
start().observeStdin(stdin);
- stdin.write('foo\n');
+ stdin.write('foo');
return debounce().then(function () {
t.ok(logger.reset.calledOnce);
t.ok(api.run.calledOnce);