Readline: use symbolic key names instead of ascii control codes

This commit is contained in:
Bert Belder 2011-01-14 03:44:05 +01:00 коммит произвёл Ryan Dahl
Родитель 8c9b2d1066
Коммит 4475b76535
3 изменённых файлов: 217 добавлений и 191 удалений

Просмотреть файл

@ -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> ');

Просмотреть файл

@ -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;
}
}
};

Просмотреть файл

@ -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) {