Browse Source

Readline: use symbolic key names instead of ascii control codes

v0.7.4-release
Bert Belder 14 years ago
committed by Ryan Dahl
parent
commit
4475b76535
  1. 4
      lib/_debugger.js
  2. 386
      lib/readline.js
  3. 4
      lib/repl.js

4
lib/_debugger.js

@ -578,8 +578,8 @@ function Interface() {
}); });
this.stdin = process.openStdin(); this.stdin = process.openStdin();
this.stdin.addListener('data', function(chunk) { this.stdin.addListener('keypress', function(s, key) {
term.write(chunk); term.write(s, key);
}); });
term.setPrompt('debug> '); term.setPrompt('debug> ');

386
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; 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) { Interface.prototype._normalWrite = function(b) {
// Very simple implementation right now. Should try to break on // Very simple implementation right now. Should try to break on
// new lines. // new lines.
this._onLine(b.toString()); if (b !== undefined)
this._onLine(b.toString());
}; };
Interface.prototype._insertString = function(c) { Interface.prototype._insertString = function(c) {
@ -288,6 +289,28 @@ function commonPrefix(strings) {
return min; 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() { Interface.prototype._historyNext = function() {
if (this.historyIndex > 0) { if (this.historyIndex > 0) {
this.historyIndex--; this.historyIndex--;
@ -325,222 +348,225 @@ Interface.prototype._attemptClose = function() {
// handle a write from the tty // handle a write from the tty
Interface.prototype._ttyWrite = function(b) { Interface.prototype._ttyWrite = function(s, key) {
switch (b[0]) { var next_word, next_non_word, previous_word, previous_non_word;
/* ctrl+c */ key = key || {};
case 3:
//process.kill(process.pid, "SIGINT");
if (this.listeners('SIGINT').length) {
this.emit('SIGINT');
} else {
// default behavior, end the readline
this._attemptClose();
}
break;
case 4: // control-d, delete right or EOF if (key.ctrl) {
if (this.cursor === 0 && this.line.length === 0) { /* Control key pressed */
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);
this._refreshLine(); switch (key.name) {
} case 'c':
break; if (this.listeners('SIGINT').length) {
this.emit('SIGINT');
} else {
// default behavior, end the readline
this._attemptClose();
}
break;
case 13: /* enter */ case 'h': // delete left
var line = this._addHistory(); this._deleteLeft();
this.output.write('\r\n'); break;
this._onLine(line);
break;
case 127: /* backspace */ case 'd': // delete right or EOF
case 8: /* ctrl+h */ if (this.cursor === 0 && this.line.length === 0) {
if (this.cursor > 0 && this.line.length > 0) { this._attemptClose();
this.line = this.line.slice(0, this.cursor - 1) + } else if (this.cursor < this.line.length) {
this.line.slice(this.cursor, this.line.length); this._deleteRight();
}
break;
this.cursor--; case 'u': // delete the whole line
this._refreshLine(); this.cursor = 0;
} this.line = '';
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(); this._refreshLine();
} break;
break;
case 6: // control-f, forward one character case 'k': // delete from current to end of line
if (this.cursor != this.line.length) { this.line = this.line.slice(0, this.cursor);
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(); this._refreshLine();
} break;
break;
case 9: // tab, completion case 'a': // go to the start of the line
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;
}
}
this.cursor = 0; this.cursor = 0;
this._refreshLine(); this._refreshLine();
break;
} else if (b[1] === 102 && this.cursor < this.line.length) { case 'e': // go to the end of the line
// 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;
}
}
this.cursor = this.line.length; this.cursor = this.line.length;
this._refreshLine(); this._refreshLine();
break;
} else if (b[1] === 100 && this.cursor < this.line.length) { case 'b': // back one character
// meta-d delete forward word if (this.cursor > 0) {
next_word = this.line.slice(this.cursor, this.line.length).search(/\w/); this.cursor--;
if (next_word !== -1) { this._refreshLine();
next_non_word = this.line.slice(this.cursor + next_word, }
this.line.length).search(/\W/); break;
if (next_non_word !== -1) {
this.line = this.line.slice(this.cursor + case 'f': // forward one character
next_word + if (this.cursor != this.line.length) {
next_non_word); this.cursor++;
this.cursor = 0; this._refreshLine();
this._refreshLine(); }
break; 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 = ''; break;
this.cursor = 0;
this._refreshLine(); 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) { case 'tab': // tab completion
// left arrow if (this.completer) {
this._tabComplete();
}
break;
case 'left':
if (this.cursor > 0) { if (this.cursor > 0) {
this.cursor--; this.cursor--;
this.output.moveCursor(-1, 0); this.output.moveCursor(-1, 0);
} }
break;
} else if (b[1] === 91 && b[2] === 67) { case 'right':
// right arrow
if (this.cursor != this.line.length) { if (this.cursor != this.line.length) {
this.cursor++; this.cursor++;
this.output.moveCursor(1, 0); this.output.moveCursor(1, 0);
} }
break;
} else if ((b[1] === 91 && b[2] === 72) || case 'home':
(b[1] === 79 && b[2] === 72) ||
(b[1] === 91 && b[2] === 55) ||
(b[1] === 91 && b[2] === 49 && (b[3] && b[3] === 126))) {
// home
this.cursor = 0; this.cursor = 0;
this._refreshLine(); this._refreshLine();
} else if ((b[1] === 91 && b[2] === 70) || break;
(b[1] === 79 && b[2] === 70) ||
(b[1] === 91 && b[2] === 56) || case 'end':
(b[1] === 91 && b[2] === 52 && (b[3] && b[3] === 126))) {
// end
this.cursor = this.line.length; this.cursor = this.line.length;
this._refreshLine(); this._refreshLine();
break;
} else if (b[1] === 91 && b[2] === 65) { case 'up':
// up arrow
this._historyPrev(); this._historyPrev();
break;
} else if (b[1] === 91 && b[2] === 66) { case 'down':
// down arrow
this._historyNext(); this._historyNext();
break;
} else if (b[1] === 91 && b[2] === 51 && this.cursor < this.line.length) { default:
// delete right if (Buffer.isBuffer(s))
this.line = this.line.slice(0, this.cursor) + s = s.toString('utf-8');
this.line.slice(this.cursor + 1, this.line.length);
this._refreshLine();
} if (s) {
break; var lines = s.split(/\r\n|\n|\r/);
for (var i = 0, len = lines.length; i < len; i++) {
default: if (i > 0) {
var c = b.toString('utf8'); this._line();
var lines = c.split(/\r\n|\n|\r/); }
for (var i = 0, len = lines.length; i < len; i++) { this._insertString(lines[i]);
if (i > 0) { }
this._ttyWrite(new Buffer([13]));
} }
this._insertString(lines[i]); }
}
break;
} }
}; };

4
lib/repl.js

@ -90,8 +90,8 @@ function REPLServer(prompt, stream) {
} }
}); });
self.inputStream.addListener('data', function(chunk) { self.inputStream.addListener('keypress', function(s, key) {
rli.write(chunk); rli.write(s, key);
}); });
rli.addListener('line', function(cmd) { rli.addListener('line', function(cmd) {

Loading…
Cancel
Save