Created
January 14, 2011 02:45
-
-
Save piscisaureus/779088 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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