Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save piscisaureus/779088 to your computer and use it in GitHub Desktop.

Select an option

Save piscisaureus/779088 to your computer and use it in GitHub Desktop.
From ae3007dfe319517dd4880182dea7d508230dfffc Mon Sep 17 00:00:00 2001
From: Bert Belder <bertbelder@gmail.com>
Date: Fri, 14 Jan 2011 03:44:05 +0100
Subject: [PATCH 1/1] readline: use symbolic key names instead of ascii control codes
---
lib/_debugger.js | 4 +-
lib/readline.js | 386 +++++++++++++++++++++++++++++-------------------------
lib/repl.js | 4 +-
3 files changed, 210 insertions(+), 184 deletions(-)
diff --git a/lib/_debugger.js b/lib/_debugger.js
index 6392b88..1c7c779 100644
--- a/lib/_debugger.js
+++ b/lib/_debugger.js
@@ -367,8 +367,8 @@ function Interface() {
});
this.stdin = process.openStdin();
- this.stdin.addListener('data', function(chunk) {
- term.write(chunk);
+ this.stdin.addListener('keypress', function(s, key) {
+ term.write(s, key);
});
term.setPrompt('debug> ');
diff --git a/lib/readline.js b/lib/readline.js
index d611d95..2fb696b 100644
--- a/lib/readline.js
+++ b/lib/readline.js
@@ -178,16 +178,17 @@ Interface.prototype.resume = function() {
};
-Interface.prototype.write = function(d) {
+Interface.prototype.write = function(d, key) {
if (this._closed) return;
- return this.enabled ? this._ttyWrite(d) : this._normalWrite(d);
+ this.enabled ? this._ttyWrite(d, key) : this._normalWrite(d, key);
};
Interface.prototype._normalWrite = function(b) {
// Very simple implementation right now. Should try to break on
// new lines.
- this._onLine(b.toString());
+ if (b !== undefined)
+ this._onLine(b.toString());
};
Interface.prototype._insertString = function(c) {
@@ -289,6 +290,28 @@ function commonPrefix(strings) {
return min;
}
+Interface.prototype._deleteLeft = function() {
+ if (this.cursor > 0 && this.line.length > 0) {
+ this.line = this.line.slice(0, this.cursor - 1) +
+ this.line.slice(this.cursor, this.line.length);
+
+ this.cursor--;
+ this._refreshLine();
+ }
+};
+
+Interface.prototype._deleteRight = function() {
+ this.line = this.line.slice(0, this.cursor) +
+ this.line.slice(this.cursor + 1, this.line.length);
+ this._refreshLine();
+}
+
+Interface.prototype._line = function() {
+ var line = this._addHistory();
+ this.output.write('\r\n');
+ this._onLine(line);
+};
+
Interface.prototype._historyNext = function() {
if (this.historyIndex > 0) {
this.historyIndex--;
@@ -315,222 +338,225 @@ Interface.prototype._historyPrev = function() {
};
// handle a write from the tty
-Interface.prototype._ttyWrite = function(b) {
- switch (b[0]) {
- /* ctrl+c */
- case 3:
- //process.kill(process.pid, "SIGINT");
- if (this.listeners('SIGINT').length) {
- this.emit('SIGINT');
- } else {
- // default behavior, end the readline
- this.close();
- }
- break;
+Interface.prototype._ttyWrite = function(s, key) {
+ var next_word, next_non_word, previous_word, previous_non_word;
+ key = key || {};
- case 4: // control-d, delete right or EOF
- if (this.cursor === 0 && this.line.length === 0) {
- this.close();
- } else if (this.cursor < this.line.length) {
- this.line = this.line.slice(0, this.cursor) +
- this.line.slice(this.cursor + 1, this.line.length);
+ if (key.ctrl) {
+ /* Control key pressed */
- this._refreshLine();
- }
- break;
+ switch (key.name) {
+ case 'c':
+ if (this.listeners('SIGINT').length) {
+ this.emit('SIGINT');
+ } else {
+ // default behavior, end the readline
+ this.close();
+ }
+ break;
- case 13: /* enter */
- var line = this._addHistory();
- this.output.write('\r\n');
- this._onLine(line);
- break;
+ case 'h': // delete left
+ this._deleteLeft();
+ break;
- case 127: /* backspace */
- case 8: /* ctrl+h */
- if (this.cursor > 0 && this.line.length > 0) {
- this.line = this.line.slice(0, this.cursor - 1) +
- this.line.slice(this.cursor, this.line.length);
+ case 'd': // delete right or EOF
+ if (this.cursor === 0 && this.line.length === 0) {
+ this.close();
+ } else if (this.cursor < this.line.length) {
+ this._deleteRight();
+ }
+ break;
- this.cursor--;
- this._refreshLine();
- }
- break;
-
- case 21: /* Ctrl+u, delete the whole line. */
- this.cursor = 0;
- this.line = '';
- this._refreshLine();
- break;
-
- case 11: /* Ctrl+k, delete from current to end of line. */
- this.line = this.line.slice(0, this.cursor);
- this._refreshLine();
- break;
-
- case 1: /* Ctrl+a, go to the start of the line */
- this.cursor = 0;
- this._refreshLine();
- break;
-
- case 5: /* ctrl+e, go to the end of the line */
- this.cursor = this.line.length;
- this._refreshLine();
- break;
-
- case 2: // control-b, back one character
- if (this.cursor > 0) {
- this.cursor--;
+ case 'u': // delete the whole line
+ this.cursor = 0;
+ this.line = '';
this._refreshLine();
- }
- break;
+ break;
- case 6: // control-f, forward one character
- if (this.cursor != this.line.length) {
- this.cursor++;
- this._refreshLine();
- }
- break;
-
- case 14: // control-n, next history item
- this._historyNext();
- break;
-
- case 23: // control-w, delete backwards to a word boundary
- if (this.cursor !== 0) {
- var leading = this.line.slice(0, this.cursor);
- var match = leading.match(/\s?((\W+|\w+)\s*)$/);
- leading = leading.slice(0, leading.length - match[1].length);
- this.line = leading + this.line.slice(this.cursor, this.line.length);
- this.cursor = leading.length;
+ case 'k': // delete from current to end of line
+ this.line = this.line.slice(0, this.cursor);
this._refreshLine();
- }
- break;
+ break;
- case 9: // tab, completion
- if (this.completer) {
- this._tabComplete();
- }
- break;
-
- case 16: // control-p, previous history item
- this._historyPrev();
- break;
-
- case 26: /* ctrl+z */
- process.kill(process.pid, 'SIGTSTP');
- return;
-
- case 27: /* escape sequence */
- var next_word, next_non_word, previous_word, previous_non_word;
-
- if (b[1] === 98 && this.cursor > 0) {
- // meta-b - backward word
- previous_word = this.line.slice(0, this.cursor)
- .split('').reverse().join('')
- .search(/\w/);
- if (previous_word !== -1) {
- previous_non_word = this.line.slice(0, this.cursor - previous_word)
- .split('').reverse().join('')
- .search(/\W/);
- if (previous_non_word !== -1) {
- this.cursor -= previous_word + previous_non_word;
- this._refreshLine();
- break;
- }
- }
+ case 'a': // go to the start of the line
this.cursor = 0;
this._refreshLine();
+ break;
- } else if (b[1] === 102 && this.cursor < this.line.length) {
- // meta-f - forward word
- next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
- if (next_word !== -1) {
- next_non_word = this.line.slice(this.cursor + next_word,
- this.line.length).search(/\W/);
- if (next_non_word !== -1) {
- this.cursor += next_word + next_non_word;
- this._refreshLine();
- break;
- }
- }
+ case 'e': // go to the end of the line
this.cursor = this.line.length;
this._refreshLine();
+ break;
- } else if (b[1] === 100 && this.cursor < this.line.length) {
- // meta-d delete forward word
- next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
- if (next_word !== -1) {
- next_non_word = this.line.slice(this.cursor + next_word,
- this.line.length).search(/\W/);
- if (next_non_word !== -1) {
- this.line = this.line.slice(this.cursor +
- next_word +
- next_non_word);
- this.cursor = 0;
- this._refreshLine();
- break;
+ case 'b': // back one character
+ if (this.cursor > 0) {
+ this.cursor--;
+ this._refreshLine();
+ }
+ break;
+
+ case 'f': // forward one character
+ if (this.cursor != this.line.length) {
+ this.cursor++;
+ this._refreshLine();
+ }
+ break;
+
+ case 'n': // next history item
+ this._historyNext();
+ break;
+
+ case 'w': // delete backwards to a word boundary
+ if (this.cursor !== 0) {
+ var leading = this.line.slice(0, this.cursor);
+ var match = leading.match(/\s?((\W+|\w+)\s*)$/);
+ leading = leading.slice(0, leading.length - match[1].length);
+ this.line = leading + this.line.slice(this.cursor, this.line.length);
+ this.cursor = leading.length;
+ this._refreshLine();
+ }
+ break;
+
+ case 'p': // previous history item
+ this._historyPrev();
+ break;
+
+ case 'z':
+ process.kill(process.pid, 'SIGTSTP');
+ return;
+ }
+
+ } else if (key.meta) {
+ /* Meta key pressed */
+
+ switch (key.name) {
+ case 'b': // backward word
+ if (this.cursor > 0) {
+ previous_word = this.line.slice(0, this.cursor)
+ .split('').reverse().join('')
+ .search(/\w/);
+ if (previous_word !== -1) {
+ previous_non_word = this.line.slice(0, this.cursor - previous_word)
+ .split('').reverse().join('')
+ .search(/\W/);
+ if (previous_non_word !== -1) {
+ this.cursor -= previous_word + previous_non_word;
+ this._refreshLine();
+ break;
+ }
}
+ this.cursor = 0;
+ this._refreshLine();
}
- this.line = '';
- this.cursor = 0;
- this._refreshLine();
+ break;
+
+ case 'f': // forward word
+ if (this.cursor < this.line.length) {
+ next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
+ if (next_word !== -1) {
+ next_non_word = this.line.slice(this.cursor + next_word,
+ this.line.length).search(/\W/);
+ if (next_non_word !== -1) {
+ this.cursor += next_word + next_non_word;
+ this._refreshLine();
+ break;
+ }
+ }
+ this.cursor = this.line.length;
+ this._refreshLine();
+ }
+ break;
+
+ case 'd': // delete forward word
+ if (this.cursor < this.line.length) {
+ next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
+ if (next_word !== -1) {
+ next_non_word = this.line.slice(this.cursor + next_word,
+ this.line.length).search(/\W/);
+ if (next_non_word !== -1) {
+ this.line = this.line.slice(this.cursor +
+ next_word +
+ next_non_word);
+ this.cursor = 0;
+ this._refreshLine();
+ break;
+ }
+ }
+ this.line = '';
+ this.cursor = 0;
+ this._refreshLine();
+ }
+ break;
+ }
+
+ } else {
+ /* No modifier keys used */
+
+ switch (key.name) {
+ case 'enter':
+ this._line();
+ break;
+
+ case 'backspace':
+ this._deleteLeft();
+ break;
+
+ case 'delete':
+ this._deleteRight();
+ break;
- } else if (b[1] === 91 && b[2] === 68) {
- // left arrow
+ case 'tab': // tab completion
+ if (this.completer) {
+ this._tabComplete();
+ }
+ break;
+
+ case 'left':
if (this.cursor > 0) {
this.cursor--;
this.output.moveCursor(-1, 0);
}
+ break;
- } else if (b[1] === 91 && b[2] === 67) {
- // right arrow
+ case 'right':
if (this.cursor != this.line.length) {
this.cursor++;
this.output.moveCursor(1, 0);
}
+ break;
- } else if ((b[1] === 91 && b[2] === 72) ||
- (b[1] === 79 && b[2] === 72) ||
- (b[1] === 91 && b[2] === 55) ||
- (b[1] === 91 && b[2] === 49 && (b[3] && b[3] === 126))) {
- // home
+ case 'home':
this.cursor = 0;
this._refreshLine();
- } else if ((b[1] === 91 && b[2] === 70) ||
- (b[1] === 79 && b[2] === 70) ||
- (b[1] === 91 && b[2] === 56) ||
- (b[1] === 91 && b[2] === 52 && (b[3] && b[3] === 126))) {
- // end
+ break;
+
+ case 'end':
this.cursor = this.line.length;
this._refreshLine();
+ break;
- } else if (b[1] === 91 && b[2] === 65) {
- // up arrow
+ case 'up':
this._historyPrev();
+ break;
- } else if (b[1] === 91 && b[2] === 66) {
- // down arrow
+ case 'down':
this._historyNext();
+ break;
- } else if (b[1] === 91 && b[2] === 51 && this.cursor < this.line.length) {
- // delete right
- this.line = this.line.slice(0, this.cursor) +
- this.line.slice(this.cursor + 1, this.line.length);
- this._refreshLine();
+ default:
+ if (Buffer.isBuffer(s))
+ s = s.toString('utf-8');
- }
- break;
-
- default:
- var c = b.toString('utf8');
- var lines = c.split(/\r\n|\n|\r/);
- for (var i = 0, len = lines.length; i < len; i++) {
- if (i > 0) {
- this._ttyWrite(new Buffer([13]));
+ if (s) {
+ var lines = s.split(/\r\n|\n|\r/);
+ for (var i = 0, len = lines.length; i < len; i++) {
+ if (i > 0) {
+ this._line();
+ }
+ this._insertString(lines[i]);
+ }
}
- this._insertString(lines[i]);
- }
- break;
+ }
}
};
diff --git a/lib/repl.js b/lib/repl.js
index 954dd31..add102e 100644
--- a/lib/repl.js
+++ b/lib/repl.js
@@ -90,8 +90,8 @@ function REPLServer(prompt, stream) {
}
});
- self.inputStream.addListener('data', function(chunk) {
- rli.write(chunk);
+ self.inputStream.addListener('keypress', function(s, key) {
+ rli.write(s, key);
});
rli.addListener('line', function(cmd) {
--
1.7.3.1.msysgit.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment