diff options
Diffstat (limited to 'ext/node/polyfills/internal/readline/promises.mjs')
-rw-r--r-- | ext/node/polyfills/internal/readline/promises.mjs | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/ext/node/polyfills/internal/readline/promises.mjs b/ext/node/polyfills/internal/readline/promises.mjs new file mode 100644 index 000000000..36aa3de12 --- /dev/null +++ b/ext/node/polyfills/internal/readline/promises.mjs @@ -0,0 +1,139 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and other Node contributors. + +import { ArrayPrototypeJoin, ArrayPrototypePush } from "internal:deno_node/polyfills/internal/primordials.mjs"; + +import { CSI } from "internal:deno_node/polyfills/internal/readline/utils.mjs"; +import { validateBoolean, validateInteger } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { isWritable } from "internal:deno_node/polyfills/internal/streams/utils.mjs"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; + +const { + kClearToLineBeginning, + kClearToLineEnd, + kClearLine, + kClearScreenDown, +} = CSI; + +export 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 ArrayPrototypePush(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 ArrayPrototypePush(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 ArrayPrototypePush(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 { + ArrayPrototypePush(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(ArrayPrototypeJoin(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 default Readline; |