UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

134 lines (115 loc) 3.57 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/readline/promises.js import { CSI } from "nstdlib/lib/internal/readline/utils"; import { validateBoolean, validateInteger, } from "nstdlib/lib/internal/validators"; import { isWritable } from "nstdlib/lib/internal/streams/utils"; import { codes as __codes__ } from "nstdlib/lib/internal/errors"; const { ERR_INVALID_ARG_TYPE } = __codes__; const { kClearToLineBeginning, kClearToLineEnd, kClearLine, kClearScreenDown } = CSI; class Readline { #autoCommit = false; #stream; #todo = []; constructor(stream, options = undefined) { if (!isWritable(stream)) throw new ERR_INVALID_ARG_TYPE("stream", "Writable", stream); this.#stream = stream; if (options?.autoCommit != null) { validateBoolean(options.autoCommit, "options.autoCommit"); this.#autoCommit = options.autoCommit; } } /** * Moves the cursor to the x and y coordinate on the given stream. * @param {integer} x * @param {integer} [y] * @returns {Readline} this */ cursorTo(x, y = undefined) { validateInteger(x, "x"); if (y != null) validateInteger(y, "y"); const data = y == null ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); else Array.prototype.push.call(this.#todo, data); return this; } /** * Moves the cursor relative to its current location. * @param {integer} dx * @param {integer} dy * @returns {Readline} this */ moveCursor(dx, dy) { if (dx || dy) { validateInteger(dx, "dx"); validateInteger(dy, "dy"); let data = ""; if (dx < 0) { data += CSI`${-dx}D`; } else if (dx > 0) { data += CSI`${dx}C`; } if (dy < 0) { data += CSI`${-dy}A`; } else if (dy > 0) { data += CSI`${dy}B`; } if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); else Array.prototype.push.call(this.#todo, data); } return this; } /** * Clears the current line the cursor is on. * @param {-1|0|1} dir Direction to clear: * -1 for left of the cursor * +1 for right of the cursor * 0 for the entire line * @returns {Readline} this */ clearLine(dir) { validateInteger(dir, "dir", -1, 1); const data = dir < 0 ? kClearToLineBeginning : dir > 0 ? kClearToLineEnd : kClearLine; if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); else Array.prototype.push.call(this.#todo, data); return this; } /** * Clears the screen from the current position of the cursor down. * @returns {Readline} this */ clearScreenDown() { if (this.#autoCommit) { process.nextTick(() => this.#stream.write(kClearScreenDown)); } else { Array.prototype.push.call(this.#todo, kClearScreenDown); } return this; } /** * Sends all the pending actions to the associated `stream` and clears the * internal list of pending actions. * @returns {Promise<void>} Resolves when all pending actions have been * flushed to the associated `stream`. */ commit() { return new Promise((resolve) => { this.#stream.write(Array.prototype.join.call(this.#todo, ""), resolve); this.#todo = []; }); } /** * Clears the internal list of pending actions without sending it to the * associated `stream`. * @returns {Readline} this */ rollback() { this.#todo = []; return this; } } export { Readline };