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