|
@ -102,6 +102,10 @@ function Interface(input, output, completer) { |
|
|
process.on('SIGWINCH', function() { |
|
|
process.on('SIGWINCH', function() { |
|
|
var winSize = output.getWindowSize(); |
|
|
var winSize = output.getWindowSize(); |
|
|
exports.columns = winSize[0]; |
|
|
exports.columns = winSize[0]; |
|
|
|
|
|
|
|
|
|
|
|
// FIXME: when #2922 will be approved, change this to
|
|
|
|
|
|
// output.on('resize', ...
|
|
|
|
|
|
self._refreshLine(); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -166,11 +170,8 @@ Interface.prototype._addHistory = function() { |
|
|
if (this.line.length === 0) return ''; |
|
|
if (this.line.length === 0) return ''; |
|
|
|
|
|
|
|
|
this.history.unshift(this.line); |
|
|
this.history.unshift(this.line); |
|
|
this.line = ''; |
|
|
|
|
|
this.historyIndex = -1; |
|
|
this.historyIndex = -1; |
|
|
|
|
|
|
|
|
this.cursor = 0; |
|
|
|
|
|
|
|
|
|
|
|
// Only store so many
|
|
|
// Only store so many
|
|
|
if (this.history.length > kHistorySize) this.history.pop(); |
|
|
if (this.history.length > kHistorySize) this.history.pop(); |
|
|
|
|
|
|
|
@ -179,18 +180,45 @@ Interface.prototype._addHistory = function() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype._refreshLine = function() { |
|
|
Interface.prototype._refreshLine = function() { |
|
|
|
|
|
var columns = this.columns; |
|
|
|
|
|
|
|
|
|
|
|
// line length
|
|
|
|
|
|
var line = this._prompt + this.line; |
|
|
|
|
|
var lineLength = line.length; |
|
|
|
|
|
var lineCols = lineLength % columns; |
|
|
|
|
|
var lineRows = (lineLength - lineCols) / columns; |
|
|
|
|
|
|
|
|
|
|
|
// cursor position
|
|
|
|
|
|
var cursorPos = this._getCursorPos(); |
|
|
|
|
|
|
|
|
|
|
|
// first move to the bottom of the current line, based on cursor pos
|
|
|
|
|
|
var prevRows = this.prevRows || 0; |
|
|
|
|
|
if (prevRows > 0) { |
|
|
|
|
|
this.output.moveCursor(0, -prevRows); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Cursor to left edge.
|
|
|
// Cursor to left edge.
|
|
|
this.output.cursorTo(0); |
|
|
this.output.cursorTo(0); |
|
|
|
|
|
// erase data
|
|
|
|
|
|
this.output.clearScreenDown(); |
|
|
|
|
|
|
|
|
// Write the prompt and the current buffer content.
|
|
|
// Write the prompt and the current buffer content.
|
|
|
this.output.write(this._prompt); |
|
|
this.output.write(line); |
|
|
this.output.write(this.line); |
|
|
|
|
|
|
|
|
|
|
|
// Erase to right.
|
|
|
// Force terminal to allocate a new line
|
|
|
this.output.clearLine(1); |
|
|
if (lineCols === 0) { |
|
|
|
|
|
this.output.write(" "); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Move cursor to original position.
|
|
|
// Move cursor to original position.
|
|
|
this.output.cursorTo(this._promptLength + this.cursor); |
|
|
this.output.cursorTo(cursorPos.cols); |
|
|
|
|
|
|
|
|
|
|
|
var diff = lineRows - cursorPos.rows; |
|
|
|
|
|
if (diff > 0) { |
|
|
|
|
|
this.output.moveCursor(0, -diff); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.prevRows = cursorPos.rows; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -242,6 +270,9 @@ Interface.prototype._insertString = function(c) { |
|
|
this.line += c; |
|
|
this.line += c; |
|
|
this.cursor += c.length; |
|
|
this.cursor += c.length; |
|
|
this.output.write(c); |
|
|
this.output.write(c); |
|
|
|
|
|
|
|
|
|
|
|
// a hack to get the line refreshed if it's needed
|
|
|
|
|
|
this._moveCursor(0); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
@ -342,8 +373,7 @@ Interface.prototype._wordLeft = function() { |
|
|
if (this.cursor > 0) { |
|
|
if (this.cursor > 0) { |
|
|
var leading = this.line.slice(0, this.cursor); |
|
|
var leading = this.line.slice(0, this.cursor); |
|
|
var match = leading.match(/([^\w\s]+|\w+|)\s*$/); |
|
|
var match = leading.match(/([^\w\s]+|\w+|)\s*$/); |
|
|
this.cursor -= match[0].length; |
|
|
this._moveCursor(-match[0].length); |
|
|
this._refreshLine(); |
|
|
|
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
@ -352,8 +382,7 @@ Interface.prototype._wordRight = function() { |
|
|
if (this.cursor < this.line.length) { |
|
|
if (this.cursor < this.line.length) { |
|
|
var trailing = this.line.slice(this.cursor); |
|
|
var trailing = this.line.slice(this.cursor); |
|
|
var match = trailing.match(/^(\s+|\W+|\w+)\s*/); |
|
|
var match = trailing.match(/^(\s+|\W+|\w+)\s*/); |
|
|
this.cursor += match[0].length; |
|
|
this._moveCursor(match[0].length); |
|
|
this._refreshLine(); |
|
|
|
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
@ -412,9 +441,18 @@ Interface.prototype._deleteLineRight = function() { |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype.clearLine = function() { |
|
|
|
|
|
this._moveCursor(+Infinity); |
|
|
|
|
|
this.output.write('\r\n'); |
|
|
|
|
|
this.line = ''; |
|
|
|
|
|
this.cursor = 0; |
|
|
|
|
|
this.prevRows = 0; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Interface.prototype._line = function() { |
|
|
Interface.prototype._line = function() { |
|
|
var line = this._addHistory(); |
|
|
var line = this._addHistory(); |
|
|
this.output.write('\r\n'); |
|
|
this.clearLine(); |
|
|
this._onLine(line); |
|
|
this._onLine(line); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
@ -446,6 +484,39 @@ Interface.prototype._historyPrev = function() { |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Returns current cursor's position and line
|
|
|
|
|
|
Interface.prototype._getCursorPos = function() { |
|
|
|
|
|
var columns = this.columns; |
|
|
|
|
|
var cursorPos = this.cursor + this._promptLength; |
|
|
|
|
|
var cols = cursorPos % columns; |
|
|
|
|
|
var rows = (cursorPos - cols) / columns; |
|
|
|
|
|
return {cols: cols, rows: rows}; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This function moves cursor dx places to the right
|
|
|
|
|
|
// (-dx for left) and refreshes the line if it is needed
|
|
|
|
|
|
Interface.prototype._moveCursor = function(dx) { |
|
|
|
|
|
var oldcursor = this.cursor; |
|
|
|
|
|
var oldPos = this._getCursorPos(); |
|
|
|
|
|
this.cursor += dx; |
|
|
|
|
|
|
|
|
|
|
|
// bounds check
|
|
|
|
|
|
if (this.cursor < 0) this.cursor = 0; |
|
|
|
|
|
if (this.cursor > this.line.length) this.cursor = this.line.length; |
|
|
|
|
|
|
|
|
|
|
|
var newPos = this._getCursorPos(); |
|
|
|
|
|
|
|
|
|
|
|
// check if cursors are in the same line
|
|
|
|
|
|
if (oldPos.rows == newPos.rows && newPos.cols != 0) { |
|
|
|
|
|
this.output.moveCursor(this.cursor - oldcursor, 0); |
|
|
|
|
|
this.prevRows = newPos.rows; |
|
|
|
|
|
} else { |
|
|
|
|
|
this._refreshLine(); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// handle a write from the tty
|
|
|
// handle a write from the tty
|
|
|
Interface.prototype._ttyWrite = function(s, key) { |
|
|
Interface.prototype._ttyWrite = function(s, key) { |
|
|
var next_word, next_non_word, previous_word, previous_non_word; |
|
|
var next_word, next_non_word, previous_word, previous_non_word; |
|
@ -502,27 +573,19 @@ Interface.prototype._ttyWrite = function(s, key) { |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'a': // go to the start of the line
|
|
|
case 'a': // go to the start of the line
|
|
|
this.cursor = 0; |
|
|
this._moveCursor(-Infinity); |
|
|
this._refreshLine(); |
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'e': // go to the end of the line
|
|
|
case 'e': // go to the end of the line
|
|
|
this.cursor = this.line.length; |
|
|
this._moveCursor(+Infinity); |
|
|
this._refreshLine(); |
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'b': // back one character
|
|
|
case 'b': // back one character
|
|
|
if (this.cursor > 0) { |
|
|
this._moveCursor(-1); |
|
|
this.cursor--; |
|
|
|
|
|
this._refreshLine(); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'f': // forward one character
|
|
|
case 'f': // forward one character
|
|
|
if (this.cursor != this.line.length) { |
|
|
this._moveCursor(+1); |
|
|
this.cursor++; |
|
|
|
|
|
this._refreshLine(); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'n': // next history item
|
|
|
case 'n': // next history item
|
|
@ -618,27 +681,19 @@ Interface.prototype._ttyWrite = function(s, key) { |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'left': |
|
|
case 'left': |
|
|
if (this.cursor > 0) { |
|
|
this._moveCursor(-1); |
|
|
this.cursor--; |
|
|
|
|
|
this.output.moveCursor(-1, 0); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'right': |
|
|
case 'right': |
|
|
if (this.cursor != this.line.length) { |
|
|
this._moveCursor(+1); |
|
|
this.cursor++; |
|
|
|
|
|
this.output.moveCursor(1, 0); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'home': |
|
|
case 'home': |
|
|
this.cursor = 0; |
|
|
this._moveCursor(-Infinity); |
|
|
this._refreshLine(); |
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'end': |
|
|
case 'end': |
|
|
this.cursor = this.line.length; |
|
|
this._moveCursor(+Infinity); |
|
|
this._refreshLine(); |
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case 'up': |
|
|
case 'up': |
|
|