Readline: use symbolic key names instead of ascii control codes
This commit is contained in:
Родитель
8c9b2d1066
Коммит
4475b76535
|
@ -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> ');
|
||||
|
|
400
lib/readline.js
400
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче