diff --git a/doc/api/readline.md b/doc/api/readline.md index 43314e136fc..4ff572f2e5f 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -487,14 +487,22 @@ function completer(linePartial, callback) { } ``` -## readline.cursorTo(stream, x, y) +## readline.cursorTo(stream, x, y[, callback]) * `stream` {stream.Writable} * `x` {number} * `y` {number} +* `callback` {Function} Invoked once the operation completes. +* Returns: {boolean} `false` if `stream` wishes for the calling code to wait for + the `'drain'` event to be emitted before continuing to write additional data; + otherwise `true`. The `readline.cursorTo()` method moves cursor to the specified position in a given [TTY][] `stream`. diff --git a/lib/readline.js b/lib/readline.js index cccae73eec5..f21d5bd77bc 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -1189,21 +1189,21 @@ function emitKeypressEvents(stream, iface) { * moves the cursor to the x and y coordinate on the given stream */ -function cursorTo(stream, x, y) { - if (stream === null || stream === undefined) - return; +function cursorTo(stream, x, y, callback) { + if (callback !== undefined && typeof callback !== 'function') + throw new ERR_INVALID_CALLBACK(callback); - if (typeof x !== 'number' && typeof y !== 'number') - return; + if (stream == null || (typeof x !== 'number' && typeof y !== 'number')) { + if (typeof callback === 'function') + process.nextTick(callback); + return true; + } if (typeof x !== 'number') throw new ERR_INVALID_CURSOR_POS(); - if (typeof y !== 'number') { - stream.write(CSI`${x + 1}G`); - } else { - stream.write(CSI`${y + 1};${x + 1}H`); - } + const data = typeof y !== 'number' ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; + return stream.write(data, callback); } /** diff --git a/test/parallel/test-readline-csi.js b/test/parallel/test-readline-csi.js index b89990d594d..1556892cff1 100644 --- a/test/parallel/test-readline-csi.js +++ b/test/parallel/test-readline-csi.js @@ -106,15 +106,17 @@ assert.strictEqual(readline.moveCursor(undefined, 1, 1, common.mustCall()), true); // Undefined or null as stream should not throw. -readline.cursorTo(null); -readline.cursorTo(); +assert.strictEqual(readline.cursorTo(null), true); +assert.strictEqual(readline.cursorTo(), true); +assert.strictEqual(readline.cursorTo(null, 1, 1, common.mustCall()), true); +assert.strictEqual(readline.cursorTo(undefined, 1, 1, common.mustCall()), true); writable.data = ''; -readline.cursorTo(writable, 'a'); +assert.strictEqual(readline.cursorTo(writable, 'a'), true); assert.strictEqual(writable.data, ''); writable.data = ''; -readline.cursorTo(writable, 'a', 'b'); +assert.strictEqual(readline.cursorTo(writable, 'a', 'b'), true); assert.strictEqual(writable.data, ''); writable.data = ''; @@ -128,9 +130,18 @@ common.expectsError( assert.strictEqual(writable.data, ''); writable.data = ''; -readline.cursorTo(writable, 1, 'a'); +assert.strictEqual(readline.cursorTo(writable, 1, 'a'), true); assert.strictEqual(writable.data, '\x1b[2G'); writable.data = ''; -readline.cursorTo(writable, 1, 2); +assert.strictEqual(readline.cursorTo(writable, 1, 2), true); assert.strictEqual(writable.data, '\x1b[3;2H'); + +writable.data = ''; +assert.strictEqual(readline.cursorTo(writable, 1, 2, common.mustCall()), true); +assert.strictEqual(writable.data, '\x1b[3;2H'); + +// Verify that cursorTo() throws on invalid callback. +assert.throws(() => { + readline.cursorTo(writable, 1, 1, null); +}, /ERR_INVALID_CALLBACK/);