Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
readline: row-agnostic multiline readline implementation
Fixes #2959.
  • Loading branch information
rlidwka authored and TooTallNate committed Mar 20, 2012
1 parent 8517089 commit 06a058d
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 42 deletions.
129 changes: 92 additions & 37 deletions lib/readline.js
Expand Up @@ -102,6 +102,10 @@ function Interface(input, output, completer) {
process.on('SIGWINCH', function() {
var winSize = output.getWindowSize();
exports.columns = winSize[0];

// FIXME: when #2922 will be approved, change this to
// output.on('resize', ...
self._refreshLine();
});
}
}
Expand Down Expand Up @@ -166,11 +170,8 @@ Interface.prototype._addHistory = function() {
if (this.line.length === 0) return '';

this.history.unshift(this.line);
this.line = '';
this.historyIndex = -1;

this.cursor = 0;

// Only store so many
if (this.history.length > kHistorySize) this.history.pop();

Expand All @@ -179,18 +180,45 @@ Interface.prototype._addHistory = 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.
this.output.cursorTo(0);
// erase data
this.output.clearScreenDown();

// Write the prompt and the current buffer content.
this.output.write(this._prompt);
this.output.write(this.line);
this.output.write(line);

// Erase to right.
this.output.clearLine(1);
// Force terminal to allocate a new line
if (lineCols === 0) {
this.output.write(" ");
}

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


Expand Down Expand Up @@ -242,6 +270,9 @@ Interface.prototype._insertString = function(c) {
this.line += c;
this.cursor += c.length;
this.output.write(c);

// a hack to get the line refreshed if it's needed
this._moveCursor(0);
}
};

Expand Down Expand Up @@ -342,8 +373,7 @@ Interface.prototype._wordLeft = function() {
if (this.cursor > 0) {
var leading = this.line.slice(0, this.cursor);
var match = leading.match(/([^\w\s]+|\w+|)\s*$/);
this.cursor -= match[0].length;
this._refreshLine();
this._moveCursor(-match[0].length);
}
};

Expand All @@ -352,8 +382,7 @@ Interface.prototype._wordRight = function() {
if (this.cursor < this.line.length) {
var trailing = this.line.slice(this.cursor);
var match = trailing.match(/^(\s+|\W+|\w+)\s*/);
this.cursor += match[0].length;
this._refreshLine();
this._moveCursor(match[0].length);
}
};

Expand Down Expand Up @@ -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() {
var line = this._addHistory();
this.output.write('\r\n');
this.clearLine();
this._onLine(line);
};

Expand Down Expand Up @@ -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
Interface.prototype._ttyWrite = function(s, key) {
var next_word, next_non_word, previous_word, previous_non_word;
Expand Down Expand Up @@ -502,27 +573,19 @@ Interface.prototype._ttyWrite = function(s, key) {
break;

case 'a': // go to the start of the line
this.cursor = 0;
this._refreshLine();
this._moveCursor(-Infinity);
break;

case 'e': // go to the end of the line
this.cursor = this.line.length;
this._refreshLine();
this._moveCursor(+Infinity);
break;

case 'b': // back one character
if (this.cursor > 0) {
this.cursor--;
this._refreshLine();
}
this._moveCursor(-1);
break;

case 'f': // forward one character
if (this.cursor != this.line.length) {
this.cursor++;
this._refreshLine();
}
this._moveCursor(+1);
break;

case 'n': // next history item
Expand Down Expand Up @@ -618,27 +681,19 @@ Interface.prototype._ttyWrite = function(s, key) {
break;

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;

case 'home':
this.cursor = 0;
this._refreshLine();
this._moveCursor(-Infinity);
break;

case 'end':
this.cursor = this.line.length;
this._refreshLine();
this._moveCursor(+Infinity);
break;

case 'up':
Expand Down
8 changes: 3 additions & 5 deletions lib/repl.js
Expand Up @@ -138,10 +138,10 @@ function REPLServer(prompt, stream, eval, useGlobal, ignoreUndefined) {

var sawSIGINT = false;
rli.on('SIGINT', function() {
rli.output.write('\n');
var empty = rli.line.length === 0;
rli.clearLine();

if (!(self.bufferedCommand && self.bufferedCommand.length > 0) &&
rli.line.length === 0) {
if (!(self.bufferedCommand && self.bufferedCommand.length > 0) && empty) {
if (sawSIGINT) {
rli.pause();
self.emit('exit');
Expand All @@ -154,8 +154,6 @@ function REPLServer(prompt, stream, eval, useGlobal, ignoreUndefined) {
sawSIGINT = false;
}

rli.line = '';

self.bufferedCommand = '';
self.displayPrompt();
});
Expand Down
5 changes: 5 additions & 0 deletions lib/tty.js
Expand Up @@ -393,6 +393,11 @@ WriteStream.prototype.clearLine = function(dir) {
};


WriteStream.prototype.clearScreenDown = function() {
this.write('\x1b[0J');
};


WriteStream.prototype.getWindowSize = function() {
return this._handle.getWindowSize();
};

0 comments on commit 06a058d

Please sign in to comment.