readline: promote _getCursorPos to public api

Alias _getCursorPos() = getCursorPos() for backwards
compatibility.

Refs: https://github.com/nodejs/node/issues/30347

PR-URL: https://github.com/nodejs/node/pull/30687
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
Jeremy Albright 2019-11-27 11:02:02 -09:00 committed by Michaël Zasso
parent 0007524f2d
commit a68729cf3d
No known key found for this signature in database
GPG Key ID: 770F7A9A5AE15600
4 changed files with 54 additions and 40 deletions

View File

@ -398,6 +398,19 @@ reading input from a TTY stream. The position of cursor determines the
portion of the input string that will be modified as input is processed, portion of the input string that will be modified as input is processed,
as well as the column where the terminal caret will be rendered. as well as the column where the terminal caret will be rendered.
### rl.getCursorPos()
<!-- YAML
added: REPLACEME
-->
* Returns: {Object}
* `rows` {number} the row of the prompt the cursor currently lands on
* `cols` {number} the screen column the cursor currently lands on
Returns the real position of the cursor in relation to the input
prompt + string. Long input (wrapping) strings, as well as multiple
line prompts are included in the calculations.
## readline.clearLine(stream, dir\[, callback\]) ## readline.clearLine(stream, dir\[, callback\])
<!-- YAML <!-- YAML
added: v0.7.7 added: v0.7.7

View File

@ -367,7 +367,7 @@ Interface.prototype._refreshLine = function() {
const lineRows = dispPos.rows; const lineRows = dispPos.rows;
// cursor position // cursor position
const cursorPos = this._getCursorPos(); const cursorPos = this.getCursorPos();
// First move to the bottom of the current line, based on cursor pos // First move to the bottom of the current line, based on cursor pos
const prevRows = this.prevRows || 0; const prevRows = this.prevRows || 0;
@ -483,7 +483,7 @@ Interface.prototype._insertString = function(c) {
this.line += c; this.line += c;
this.cursor += c.length; this.cursor += c.length;
if (this._getCursorPos().cols === 0) { if (this.getCursorPos().cols === 0) {
this._refreshLine(); this._refreshLine();
} else { } else {
this._writeToOutput(c); this._writeToOutput(c);
@ -750,7 +750,7 @@ Interface.prototype._getDisplayPos = function(str) {
// Returns current cursor's position and line // Returns current cursor's position and line
Interface.prototype._getCursorPos = function() { Interface.prototype.getCursorPos = function() {
const columns = this.columns; const columns = this.columns;
const strBeforeCursor = this._prompt + this.line.substring(0, this.cursor); const strBeforeCursor = this._prompt + this.line.substring(0, this.cursor);
const dispPos = this._getDisplayPos( const dispPos = this._getDisplayPos(
@ -767,20 +767,21 @@ Interface.prototype._getCursorPos = function() {
} }
return { cols: cols, rows: rows }; return { cols: cols, rows: rows };
}; };
Interface.prototype._getCursorPos = Interface.prototype.getCursorPos;
// This function moves cursor dx places to the right // This function moves cursor dx places to the right
// (-dx for left) and refreshes the line if it is needed // (-dx for left) and refreshes the line if it is needed
Interface.prototype._moveCursor = function(dx) { Interface.prototype._moveCursor = function(dx) {
const oldcursor = this.cursor; const oldcursor = this.cursor;
const oldPos = this._getCursorPos(); const oldPos = this.getCursorPos();
this.cursor += dx; this.cursor += dx;
// bounds check // bounds check
if (this.cursor < 0) this.cursor = 0; if (this.cursor < 0) this.cursor = 0;
else if (this.cursor > this.line.length) this.cursor = this.line.length; else if (this.cursor > this.line.length) this.cursor = this.line.length;
const newPos = this._getCursorPos(); const newPos = this.getCursorPos();
// Check if cursors are in the same line // Check if cursors are in the same line
if (oldPos.rows === newPos.rows) { if (oldPos.rows === newPos.rows) {

View File

@ -590,7 +590,7 @@ function isWarned(emitter) {
rli.question(expectedLines[0], function() { rli.question(expectedLines[0], function() {
rli.close(); rli.close();
}); });
const cursorPos = rli._getCursorPos(); const cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, expectedLines[0].length); assert.strictEqual(cursorPos.cols, expectedLines[0].length);
rli.close(); rli.close();
@ -606,7 +606,7 @@ function isWarned(emitter) {
rli.question(expectedLines.join('\n'), function() { rli.question(expectedLines.join('\n'), function() {
rli.close(); rli.close();
}); });
const cursorPos = rli._getCursorPos(); const cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, expectedLines.length - 1); assert.strictEqual(cursorPos.rows, expectedLines.length - 1);
assert.strictEqual(cursorPos.cols, expectedLines.slice(-1)[0].length); assert.strictEqual(cursorPos.cols, expectedLines.slice(-1)[0].length);
rli.close(); rli.close();
@ -623,11 +623,11 @@ function isWarned(emitter) {
}); });
fi.emit('data', 'the quick brown fox'); fi.emit('data', 'the quick brown fox');
fi.emit('keypress', '.', { ctrl: true, name: 'a' }); fi.emit('keypress', '.', { ctrl: true, name: 'a' });
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
fi.emit('keypress', '.', { ctrl: true, name: 'e' }); fi.emit('keypress', '.', { ctrl: true, name: 'e' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 19); assert.strictEqual(cursorPos.cols, 19);
rli.close(); rli.close();
@ -643,28 +643,28 @@ function isWarned(emitter) {
terminal: terminal terminal: terminal
}); });
fi.emit('data', 'the quick brown fox'); fi.emit('data', 'the quick brown fox');
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 19); assert.strictEqual(cursorPos.cols, 19);
// Back one character // Back one character
fi.emit('keypress', '.', { ctrl: true, name: 'b' }); fi.emit('keypress', '.', { ctrl: true, name: 'b' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 18); assert.strictEqual(cursorPos.cols, 18);
// Back one character // Back one character
fi.emit('keypress', '.', { ctrl: true, name: 'b' }); fi.emit('keypress', '.', { ctrl: true, name: 'b' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 17); assert.strictEqual(cursorPos.cols, 17);
// Forward one character // Forward one character
fi.emit('keypress', '.', { ctrl: true, name: 'f' }); fi.emit('keypress', '.', { ctrl: true, name: 'f' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 18); assert.strictEqual(cursorPos.cols, 18);
// Forward one character // Forward one character
fi.emit('keypress', '.', { ctrl: true, name: 'f' }); fi.emit('keypress', '.', { ctrl: true, name: 'f' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 19); assert.strictEqual(cursorPos.cols, 19);
rli.close(); rli.close();
@ -683,13 +683,13 @@ function isWarned(emitter) {
// Move left one character/code point // Move left one character/code point
fi.emit('keypress', '.', { name: 'left' }); fi.emit('keypress', '.', { name: 'left' });
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
// Move right one character/code point // Move right one character/code point
fi.emit('keypress', '.', { name: 'right' }); fi.emit('keypress', '.', { name: 'right' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
if (common.hasIntl) { if (common.hasIntl) {
assert.strictEqual(cursorPos.cols, 2); assert.strictEqual(cursorPos.cols, 2);
@ -717,12 +717,12 @@ function isWarned(emitter) {
// Move left one character/code point // Move left one character/code point
fi.emit('keypress', '.', { name: 'left' }); fi.emit('keypress', '.', { name: 'left' });
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
fi.emit('data', '🐕'); fi.emit('data', '🐕');
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
if (common.hasIntl) { if (common.hasIntl) {
@ -753,7 +753,7 @@ function isWarned(emitter) {
// Move left one character/code point // Move left one character/code point
fi.emit('keypress', '.', { name: 'right' }); fi.emit('keypress', '.', { name: 'right' });
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
if (common.hasIntl) { if (common.hasIntl) {
assert.strictEqual(cursorPos.cols, 2); assert.strictEqual(cursorPos.cols, 2);
@ -764,7 +764,7 @@ function isWarned(emitter) {
} }
fi.emit('data', '🐕'); fi.emit('data', '🐕');
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
if (common.hasIntl) { if (common.hasIntl) {
assert.strictEqual(cursorPos.cols, 4); assert.strictEqual(cursorPos.cols, 4);
@ -790,19 +790,19 @@ function isWarned(emitter) {
}); });
fi.emit('data', 'the quick brown fox'); fi.emit('data', 'the quick brown fox');
fi.emit('keypress', '.', { ctrl: true, name: 'left' }); fi.emit('keypress', '.', { ctrl: true, name: 'left' });
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 16); assert.strictEqual(cursorPos.cols, 16);
fi.emit('keypress', '.', { meta: true, name: 'b' }); fi.emit('keypress', '.', { meta: true, name: 'b' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 10); assert.strictEqual(cursorPos.cols, 10);
fi.emit('keypress', '.', { ctrl: true, name: 'right' }); fi.emit('keypress', '.', { ctrl: true, name: 'right' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 16); assert.strictEqual(cursorPos.cols, 16);
fi.emit('keypress', '.', { meta: true, name: 'f' }); fi.emit('keypress', '.', { meta: true, name: 'f' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 19); assert.strictEqual(cursorPos.cols, 19);
rli.close(); rli.close();
@ -904,13 +904,13 @@ function isWarned(emitter) {
terminal: terminal terminal: terminal
}); });
fi.emit('data', 'the quick brown fox'); fi.emit('data', 'the quick brown fox');
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 19); assert.strictEqual(cursorPos.cols, 19);
// Delete left character // Delete left character
fi.emit('keypress', '.', { ctrl: true, name: 'h' }); fi.emit('keypress', '.', { ctrl: true, name: 'h' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 18); assert.strictEqual(cursorPos.cols, 18);
rli.on('line', common.mustCall((line) => { rli.on('line', common.mustCall((line) => {
@ -930,7 +930,7 @@ function isWarned(emitter) {
terminal: terminal terminal: terminal
}); });
fi.emit('data', '💻'); fi.emit('data', '💻');
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
if (common.hasIntl) { if (common.hasIntl) {
assert.strictEqual(cursorPos.cols, 2); assert.strictEqual(cursorPos.cols, 2);
@ -939,7 +939,7 @@ function isWarned(emitter) {
} }
// Delete left character // Delete left character
fi.emit('keypress', '.', { ctrl: true, name: 'h' }); fi.emit('keypress', '.', { ctrl: true, name: 'h' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
rli.on('line', common.mustCall((line) => { rli.on('line', common.mustCall((line) => {
@ -962,13 +962,13 @@ function isWarned(emitter) {
// Go to the start of the line // Go to the start of the line
fi.emit('keypress', '.', { ctrl: true, name: 'a' }); fi.emit('keypress', '.', { ctrl: true, name: 'a' });
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
// Delete right character // Delete right character
fi.emit('keypress', '.', { ctrl: true, name: 'd' }); fi.emit('keypress', '.', { ctrl: true, name: 'd' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
rli.on('line', common.mustCall((line) => { rli.on('line', common.mustCall((line) => {
@ -991,13 +991,13 @@ function isWarned(emitter) {
// Go to the start of the line // Go to the start of the line
fi.emit('keypress', '.', { ctrl: true, name: 'a' }); fi.emit('keypress', '.', { ctrl: true, name: 'a' });
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
// Delete right character // Delete right character
fi.emit('keypress', '.', { ctrl: true, name: 'd' }); fi.emit('keypress', '.', { ctrl: true, name: 'd' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
rli.on('line', common.mustCall((line) => { rli.on('line', common.mustCall((line) => {
@ -1017,13 +1017,13 @@ function isWarned(emitter) {
terminal: terminal terminal: terminal
}); });
fi.emit('data', 'the quick brown fox'); fi.emit('data', 'the quick brown fox');
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 19); assert.strictEqual(cursorPos.cols, 19);
// Delete from current to start of line // Delete from current to start of line
fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'backspace' }); fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'backspace' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
rli.on('line', common.mustCall((line) => { rli.on('line', common.mustCall((line) => {
@ -1046,13 +1046,13 @@ function isWarned(emitter) {
// Go to the start of the line // Go to the start of the line
fi.emit('keypress', '.', { ctrl: true, name: 'a' }); fi.emit('keypress', '.', { ctrl: true, name: 'a' });
let cursorPos = rli._getCursorPos(); let cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
// Delete from current to end of line // Delete from current to end of line
fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'delete' }); fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'delete' });
cursorPos = rli._getCursorPos(); cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 0); assert.strictEqual(cursorPos.cols, 0);
rli.on('line', common.mustCall((line) => { rli.on('line', common.mustCall((line) => {
@ -1073,7 +1073,7 @@ function isWarned(emitter) {
}); });
fi.columns = 10; fi.columns = 10;
fi.emit('data', 'multi-line text'); fi.emit('data', 'multi-line text');
const cursorPos = rli._getCursorPos(); const cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 1); assert.strictEqual(cursorPos.rows, 1);
assert.strictEqual(cursorPos.cols, 5); assert.strictEqual(cursorPos.cols, 5);
rli.close(); rli.close();
@ -1090,7 +1090,7 @@ function isWarned(emitter) {
}); });
fi.columns = 10; fi.columns = 10;
fi.emit('data', 't'); fi.emit('data', 't');
const cursorPos = rli._getCursorPos(); const cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 4); assert.strictEqual(cursorPos.rows, 4);
assert.strictEqual(cursorPos.cols, 3); assert.strictEqual(cursorPos.cols, 3);
rli.close(); rli.close();
@ -1108,7 +1108,7 @@ function isWarned(emitter) {
const lines = ['line 1', 'line 2', 'line 3']; const lines = ['line 1', 'line 2', 'line 3'];
fi.emit('data', lines.join('\n')); fi.emit('data', lines.join('\n'));
fi.emit('keypress', '.', { ctrl: true, name: 'l' }); fi.emit('keypress', '.', { ctrl: true, name: 'l' });
const cursorPos = rli._getCursorPos(); const cursorPos = rli.getCursorPos();
assert.strictEqual(cursorPos.rows, 0); assert.strictEqual(cursorPos.rows, 0);
assert.strictEqual(cursorPos.cols, 6); assert.strictEqual(cursorPos.cols, 6);
rli.on('line', common.mustCall((line) => { rli.on('line', common.mustCall((line) => {

View File

@ -37,7 +37,7 @@ const ctrlU = { ctrl: true, name: 'u' };
for (const [cursor, string] of tests) { for (const [cursor, string] of tests) {
rl.write(string); rl.write(string);
assert.strictEqual(rl._getCursorPos().cols, cursor); assert.strictEqual(rl.getCursorPos().cols, cursor);
rl.write(null, ctrlU); rl.write(null, ctrlU);
} }
} }