From 443071db5749603f816199b9ec8bc512fb441d98 Mon Sep 17 00:00:00 2001 From: Rlidwka Date: Mon, 5 Mar 2012 19:55:08 +0400 Subject: [PATCH] readline: add multiline support --- lib/readline.js | 86 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/lib/readline.js b/lib/readline.js index 2e16229b1efa91..3b6713dae09efc 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -95,13 +95,18 @@ function Interface(input, output, completer) { this.history = []; this.historyIndex = -1; + // a number of lines used by current command + this.usedLines = 1; + var winSize = output.getWindowSize(); exports.columns = winSize[0]; + exports.rows = winSize[1]; if (process.listeners('SIGWINCH').length === 0) { process.on('SIGWINCH', function() { var winSize = output.getWindowSize(); exports.columns = winSize[0]; + exports.rows = winSize[1]; }); } } @@ -113,6 +118,10 @@ Interface.prototype.__defineGetter__('columns', function() { return exports.columns; }); +Interface.prototype.__defineGetter__('rows', function() { + return exports.rows; +}); + Interface.prototype.setPrompt = function(prompt, length) { this._prompt = prompt; if (length) { @@ -178,19 +187,53 @@ Interface.prototype._addHistory = function() { }; +Interface.prototype._recalcUsedLines = function() { + var line = this._prompt + this.line; + var newcount = Math.ceil(line.length / this.columns); + if (newcount > this.usedLines) this.usedLines = newcount; +}; + + Interface.prototype._refreshLine = function() { + var columns = this.columns; + if (!columns) columns = Infinity; + + // See if a number of used lines has changed + var oldLines = this.usedLines; + this._recalcUsedLines(); + if (oldLines != this.usedLines) { + this.output.cursorTo(0, this.rows - 1); + for (var i = oldLines; i < this.usedLines; i++) { + this.output.write('\r\n'); + } + } + // Cursor to left edge. - this.output.cursorTo(0); + if (this.usedLines === 1) { + this.output.cursorTo(0); + } else { + this.output.cursorTo(0, this.rows - this.usedLines); + } // Write the prompt and the current buffer content. - this.output.write(this._prompt); - this.output.write(this.line); + var buffer = this._prompt + this.line; + this.output.write(buffer); // Erase to right. this.output.clearLine(1); + var clearLinesCnt = this.usedLines - Math.floor(buffer.length / columns) - 1; + for (var i = this.rows - clearLinesCnt; i < this.rows; i++) { + this.output.cursorTo(0, i); + this.output.clearLine(0); + } // Move cursor to original position. - this.output.cursorTo(this._promptLength + this.cursor); + var curPos = this._getCursorPos(); + if (this.usedLines === 1) { + this.output.cursorTo(curPos[0]); + } else { + this.output.cursorTo(curPos[0], this.rows - this.usedLines + curPos[1]); + } }; @@ -242,6 +285,7 @@ Interface.prototype._insertString = function(c) { this.line += c; this.cursor += c.length; this.output.write(c); + this._recalcUsedLines(); } }; @@ -415,6 +459,7 @@ Interface.prototype._deleteLineRight = function() { Interface.prototype._line = function() { var line = this._addHistory(); this.output.write('\r\n'); + this.usedLines = 1; this._onLine(line); }; @@ -446,6 +491,33 @@ Interface.prototype._historyPrev = function() { }; +// Returns current cursor's position and line +Interface.prototype._getCursorPos = function() { + var columns = this.columns; + var pos = this.cursor + this._promptLength; + if (!columns) return [pos, 0]; + + var lineNum = Math.floor(pos / columns); + var colNum = pos - lineNum * columns; + return [colNum, lineNum]; +}; + + +Interface.prototype._moveCursor = function(dx) { + var oldcursor = this.cursor; + var oldLine = this._getCursorPos()[1]; + this.cursor += dx; + var newLine = this._getCursorPos()[1]; + + // check if cursors are in the same line + if (oldLine == newLine) { + this.output.moveCursor(dx, 0); + } else { + this._refreshLine(); + } +}; + + // handle a write from the tty Interface.prototype._ttyWrite = function(s, key) { var next_word, next_non_word, previous_word, previous_non_word; @@ -616,15 +688,13 @@ Interface.prototype._ttyWrite = function(s, key) { case 'left': if (this.cursor > 0) { - this.cursor--; - this.output.moveCursor(-1, 0); + this._moveCursor(-1); } break; case 'right': if (this.cursor != this.line.length) { - this.cursor++; - this.output.moveCursor(1, 0); + this._moveCursor(1); } break;