diff --git a/lib/_debugger.js b/lib/_debugger.js index 08e70573aad..7619b254119 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -578,8 +578,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 efd636e6652..2e03a9fa3c8 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -177,16 +177,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) { @@ -288,6 +289,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--; @@ -325,222 +348,225 @@ Interface.prototype._attemptClose = 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._attemptClose(); - } - 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._attemptClose(); - } 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; - - case 13: /* enter */ - var line = this._addHistory(); - this.output.write('\r\n'); - this._onLine(line); - 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); - - 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--; - this._refreshLine(); - } - 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; - this._refreshLine(); - } - 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; - } + switch (key.name) { + case 'c': + if (this.listeners('SIGINT').length) { + this.emit('SIGINT'); + } else { + // default behavior, end the readline + this._attemptClose(); } + break; + + case 'h': // delete left + this._deleteLeft(); + break; + + case 'd': // delete right or EOF + if (this.cursor === 0 && this.line.length === 0) { + this._attemptClose(); + } else if (this.cursor < this.line.length) { + this._deleteRight(); + } + break; + + case 'u': // delete the whole line + this.cursor = 0; + this.line = ''; + this._refreshLine(); + break; + + case 'k': // delete from current to end of line + this.line = this.line.slice(0, this.cursor); + 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(); } - this.line = ''; - this.cursor = 0; - this._refreshLine(); + break; - } else if (b[1] === 91 && b[2] === 68) { - // left arrow + 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(); + } + 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; + + 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 954dd31610e..add102ee5f9 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) {