From 50241b92c8de236f35df4508434346a13ef37aeb Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Mon, 7 Jul 2025 20:11:54 +1000 Subject: [PATCH 01/10] Create unstable_ansi.ts --- cli/unstable_ansi.ts | 164 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 cli/unstable_ansi.ts diff --git a/cli/unstable_ansi.ts b/cli/unstable_ansi.ts new file mode 100644 index 000000000000..141a7a4f8214 --- /dev/null +++ b/cli/unstable_ansi.ts @@ -0,0 +1,164 @@ +import { delay } from "@std/async/delay"; +export class Ansi { + /** + * Inserts `x` spaces at the cursor position. Shifting existing line content + * to the right. Overflow is discarded. + */ + static insertBlanks(x = 1): string { + return `\x1b[${x}@`; + } + /** + * Inserts `x` lines at cursor position. Shifting current line and below + * down. Overflow is discarded. + */ + static insertLines(x = 1): string { + return `\x1b[${x}L`; + } + /** + * Deletes `x` lines at cursor position. Shifting below lines up. + */ + static deleteLines(x = 1): string { + return `\x1b[${x}M`; + } + /** + * Deletes `x` characters at cursor position and to the right. Shifting line + * content left. + */ + static deleteCharacters(x = 1): string { + return `\x1b[${x}P`; + } + /** + * Moves cursor position up `x` lines or up to the top margin. + */ + static moveCursorUp(x = 1): string { + return `\x1b[${x}A`; + } + /** + * Moves cursor position down `x` lines or up to the bottom margin. + */ + static moveCursorDown(x = 1): string { + return `\x1b[${x}B`; + } + /** + * Moves cursor position `x` columns right or up to the right margin. + */ + static moveCursorRight(x = 1): string { + return `\x1b[${x}C`; + } + /** + * Moves cursor position `x` columns left or up to the left margin. + */ + static moveCursorLeft(x = 1): string { + return `\x1b[${x}D`; + } + /** + * Moves cursor position `x` lines down or up to the bottom margin, and to + * the beginning of the line. + */ + static moveCursorDownStart(x = 1): string { + return `\x1b[${x}E`; + } + /** + * Moves cursor position `x` lines up or up to the top of the margin, and to the beginning of the line. + */ + static moveCursorUpStart(x = 1): string { + return `\x1b[${x}F`; + } + /** + * Sets cursor column position to `x` or up to the sides of the margins. + */ + static setCursorColumn(x = 1): string { + return `\x1b[${x}G`; + } + /** + * Sets cursor position to `x` line and `y` column or up to the margin. + */ + static setCursorPosition(x = 1, y = 1): string { + return `\x1b[${x};${y}H`; + } + /** + * Moves cursor position `x` tab stops right or up to the right margin. + */ + static moveCursorRightTab(x = 1): string { + return `\x1b[${x}I`; + } + /** + * Moves cursor position `x` tab stops left or up to the left margin. + */ + static moveCursorLeftTab(x = 1): string { + return `\x1b[${x}Z`; + } + /** + * Erases content of lines below cursor position and content to the right on + * the same line as cursor. + */ + static get eraseDisplayAfterCursor(): string { + return "\x1b[0J"; + } + /** + * Erases content of lines above cursor position and content to the left on + * the same line eas cursor. + */ + static get eraseDisplayBeforeCursor(): string { + return "\x1b[1J"; + } + /** + * Erases all content. + */ + static get eraseDisplay(): string { + return "\x1b[2J"; + } + /** + * Erases line content to the right of cursor position. + */ + static get eraseLineAfterCursor(): string { + return "\x1b[0K"; + } + /** + * Erases line content to the left of cursor position. + */ + static get eraseLineBeforeCursor(): string { + return "\x1b[1K"; + } + /** + * Erases entire line content. + */ + static get eraseLine(): string { + return "\x1b[2K"; + } + /** + * Scrolls the scrollable region `x` lines up. Inserts blank lines at the + * bottom of the scrollable region. + */ + static scrollDownAndInsert(x = 1): string { + return `\x1b[${x}S`; + } + /** + * Scroll the scrollable region `x` lines down. Insert blank lines at the top of the scrollable region + */ + static scrollUpAndInsert(x = 1): string { + return `\x1b[${x}T`; + } + /** + * Erases `x` characters at cursor position and to the right. + */ + static eraseCharacters(x = 1): string { + return `\x1b[${x}X`; + } +} + +const encoder = new TextEncoder(); +async function write(text: string): Promise { + await Deno.stderr.write(encoder.encode(text)); +} + +const steps: string[] = [ + "Hello World\nHello World\nHello World", + "\x1b[1 t\x07", +]; + +for (const step of steps) { + await write(step); + await delay(1000); +} +await write("\n"); From ff46743537b326e1d3d0e932b2a193f2f7b3f498 Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Mon, 7 Jul 2025 23:14:13 +1000 Subject: [PATCH 02/10] Update unstable_ansi.ts --- cli/unstable_ansi.ts | 222 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 191 insertions(+), 31 deletions(-) diff --git a/cli/unstable_ansi.ts b/cli/unstable_ansi.ts index 141a7a4f8214..4f1e26eed0a8 100644 --- a/cli/unstable_ansi.ts +++ b/cli/unstable_ansi.ts @@ -1,15 +1,90 @@ -import { delay } from "@std/async/delay"; +// Copyright 2018-2025 the Deno authors. MIT license. + +// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +// TODO: CSI Ps n + export class Ansi { + /** + * Causes content on the current line to enlarge, showing only the top half + * of characters with each character taking up two columns. Can be used in + * combination with {@linkcode Ansi.doubleHeightBottom} on the next line to + * make text appear twice as big. + */ + static get doubleHeightTop(): string { + return "\x1b#3"; + } + /** + * Causes content on the current line to enlarge, showing only the bottom + * half of the characters with each character taking up two columns. Can be used in combination with {@linkcode Ansi.doubleHeightTop} on the previous line to make text appear twice as big. + */ + static get doubleHeightBottom(): string { + return "\x1b#4"; + } + /** + * Causes content on the current line to shrink down to a single column, + * essentally reverting the effects of {@linkcode Ansi.doubleHeightTop}, {@linkcode Ansi.doubleHeightBottom}, or {@linkcode Ansi.doubleWidth}. + */ + static get singleWidth(): string { + return "\x1b#5"; + } + /** + * Causes content on the current line to stretch out, with each character + * taking up two columns. Can be reverted with {@linkcode Ansi.singleWidth} + */ + static get doubleWidth(): string { + return "\x1b#6"; + } + /** + * Saves current cursor position that can later be restored with + * {@linkcode Ansi.restoreCursorPosition}. + */ + static get saveCursorPosition(): string { + return "\x1b7"; + } + /** + * Restores cursor position that was earlier saved with + * {@linkcode Ansi.saveCursorPosition}. + */ + static get restoreCursorPosition(): string { + return "\x1b8"; + } + static get keypadApplicationMode(): string { + return "\x1b="; + } + static get keypadNormalMode(): string { + return "\x1b>"; + } + /** + * This is a full reset of the terminal, reverting it back to its original + * default settings, clearing the screen, resetting modes, colors, character + * sets and more. Essentally making the terminal behave as if it were just + * started by the user. This command is very disruptive to the user. Also see + * {@linkcode Ansi.softReset}. + */ + static get hardReset(): string { + return "\x1bc"; + } + /** + * This command resets many settings to their inital state without fully + * reinitalising the terminal like {@linkcode Ansi.hardReset}. It preserves + * things like cursor position and display content, but clears modes, + * character sets, etc. Should probably be called when exiting the program. + */ + static get softReset(): string { + return "\x1b[!p"; + } /** * Inserts `x` spaces at the cursor position. Shifting existing line content - * to the right. Overflow is discarded. + * to the right. Cursor position does not change. Characters that exit the + * display are discarded. */ - static insertBlanks(x = 1): string { + static insertSpace(x = 1): string { return `\x1b[${x}@`; } /** * Inserts `x` lines at cursor position. Shifting current line and below - * down. Overflow is discarded. + * down. Cursor position does not change. Characters that exit the display + * are discarded. */ static insertLines(x = 1): string { return `\x1b[${x}L`; @@ -53,25 +128,35 @@ export class Ansi { } /** * Moves cursor position `x` lines down or up to the bottom margin, and to - * the beginning of the line. + * the beginning of that line. */ static moveCursorDownStart(x = 1): string { return `\x1b[${x}E`; } /** - * Moves cursor position `x` lines up or up to the top of the margin, and to the beginning of the line. + * Moves cursor position `x` lines up or up to the top of the margin, and to + * the beginning of that line. */ static moveCursorUpStart(x = 1): string { return `\x1b[${x}F`; } /** - * Sets cursor column position to `x` or up to the sides of the margins. + * Sets cursor position to column `x` or up to the sides of the margins. + * Columns begin at `1` not `0`. */ static setCursorColumn(x = 1): string { return `\x1b[${x}G`; } /** - * Sets cursor position to `x` line and `y` column or up to the margin. + * Sets cursor position to line `x` or down to the bottom of the margin. + * Lines begin at `1` not `0`. + */ + static setCursorLine(x = 1): string { + return `\x1b[${x}d`; + } + /** + * Sets cursor position to `x` line and `y` column or up to the margin. Lines + * and columns begin at `1` not `0`. */ static setCursorPosition(x = 1, y = 1): string { return `\x1b[${x};${y}H`; @@ -97,7 +182,7 @@ export class Ansi { } /** * Erases content of lines above cursor position and content to the left on - * the same line eas cursor. + * the same line as cursor. */ static get eraseDisplayBeforeCursor(): string { return "\x1b[1J"; @@ -127,38 +212,113 @@ export class Ansi { return "\x1b[2K"; } /** - * Scrolls the scrollable region `x` lines up. Inserts blank lines at the - * bottom of the scrollable region. + * Erases `x` characters at cursor position and to the right. + */ + static eraseCharacters(x = 1): string { + return `\x1b[${x}X`; + } + /** + * Shifts content within the scrollable region up `x` lines, inserting blank + * lines at the bottom of the scrollable region. */ - static scrollDownAndInsert(x = 1): string { + static shiftUpAndInsert(x = 1): string { return `\x1b[${x}S`; } /** - * Scroll the scrollable region `x` lines down. Insert blank lines at the top of the scrollable region + * Shifts content within the scrollable region down `x` lines, inserting + * blank lines at the top of the scrollable region. */ - static scrollUpAndInsert(x = 1): string { + static shiftDownAndInsert(x = 1): string { return `\x1b[${x}T`; } /** - * Erases `x` characters at cursor position and to the right. + * Repeats last graphic character printed `x` times at cursor position. */ - static eraseCharacters(x = 1): string { - return `\x1b[${x}X`; + static repeatLastCharacter(x = 1): string { + return `\x1b[${x}b`; + } + /** + * Causes existing characters to the right of the cursor position to shift + * right as new characters are written. Opposite of + * {@linkcode Ansi.replaceMode}. + */ + static get insertMode(): string { + return "\x1b[4h"; + } + /** + * Causes existing characters to be overwritten at the cursor position by new + * characters. See also {@linkcode Ansi.insertMode}. + */ + static get replaceMode(): string { + return "\x1b[4l"; + } + /** + * Causes top and bottom margins to shrink to scrollable region (See + * {@linkcode Ansi.setScrollableRegion}) preventing the cursor from moving + * to the lines outside it. + */ + static get enableOriginMode(): string { + return "\x1b[?6h"; + } + /** + * Causes the top and bottom margins to enlarge to the user's display. See + * {@linkcode Ansi.enableOriginMode}. + */ + static get disableOriginMode(): string { + return "\x1b[?6l"; + } + /** + * Causes cursor to automatically move to the next line when it hits the + * end of the current line to continue writing. See also + * {@linkcode Ansi.disableAutoWrap}. + */ + static get enableAutoWrap(): string { + return "\x1b[?7h"; + } + /** + * Causes cursor to remain on the same line when it hits the end of the + * current line. See also {@linkcode Ansi.enableAutoWrap}. + */ + static get disableAutoWrap(): string { + return "\x1b[?7l"; + } + /** + * Sets the cursor animation style. + */ + static setCursorStyle(x: CursorStyle): string { + return `\x1b[${x} q`; + } + /** + * Causes cursor position to be visible to the user. See also + * {@linkcode Ansi.hideCursor}. + */ + static get showCursor(): string { + return "\x1b[?25h"; + } + /** + * Causes cursor position to be hidden from the user. See also + * {@linkcode Ansi.showCursor}. + */ + static get hideCursor(): string { + return "\x1b[?25l"; + } + /** + * Sets the scrollable region of the display. Allowing either or both the top + * and bottom lines to not have their content moved when the scrolling region + * is updated. `x` is the top line of the scrollable region. `y` is the + * bottom line of the scrollable region. + */ + static setScrollableRegion(x = 1, y?: number): string { + y ??= Deno.consoleSize().rows; + return `\x1b[${x};${y}r`; } } -const encoder = new TextEncoder(); -async function write(text: string): Promise { - await Deno.stderr.write(encoder.encode(text)); -} - -const steps: string[] = [ - "Hello World\nHello World\nHello World", - "\x1b[1 t\x07", -]; - -for (const step of steps) { - await write(step); - await delay(1000); +export const enum CursorStyle { + BlinkingBlock = 1, + SteadyBlock = 2, + BlinkingUnderline = 3, + SteadyUnderline = 4, + BlinkingBar = 5, + SteadyBar = 6, } -await write("\n"); From 7417e1a91d4a0bd7ec92ddd1ac630821e9ed1fcc Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Tue, 8 Jul 2025 13:15:08 +1000 Subject: [PATCH 03/10] fmt --- cli/unstable_ansi.ts | 148 +++++++++++++++++++++++++++---------------- 1 file changed, 93 insertions(+), 55 deletions(-) diff --git a/cli/unstable_ansi.ts b/cli/unstable_ansi.ts index 4f1e26eed0a8..e962bfdccad7 100644 --- a/cli/unstable_ansi.ts +++ b/cli/unstable_ansi.ts @@ -13,13 +13,17 @@ export class Ansi { static get doubleHeightTop(): string { return "\x1b#3"; } + /** * Causes content on the current line to enlarge, showing only the bottom - * half of the characters with each character taking up two columns. Can be used in combination with {@linkcode Ansi.doubleHeightTop} on the previous line to make text appear twice as big. + * half of the characters with each character taking up two columns. Can be + * used in combination with {@linkcode Ansi.doubleHeightTop} on the previous + * line to make text appear twice as big. */ static get doubleHeightBottom(): string { return "\x1b#4"; } + /** * Causes content on the current line to shrink down to a single column, * essentally reverting the effects of {@linkcode Ansi.doubleHeightTop}, {@linkcode Ansi.doubleHeightBottom}, or {@linkcode Ansi.doubleWidth}. @@ -27,6 +31,7 @@ export class Ansi { static get singleWidth(): string { return "\x1b#5"; } + /** * Causes content on the current line to stretch out, with each character * taking up two columns. Can be reverted with {@linkcode Ansi.singleWidth} @@ -34,6 +39,7 @@ export class Ansi { static get doubleWidth(): string { return "\x1b#6"; } + /** * Saves current cursor position that can later be restored with * {@linkcode Ansi.restoreCursorPosition}. @@ -41,6 +47,7 @@ export class Ansi { static get saveCursorPosition(): string { return "\x1b7"; } + /** * Restores cursor position that was earlier saved with * {@linkcode Ansi.saveCursorPosition}. @@ -48,12 +55,7 @@ export class Ansi { static get restoreCursorPosition(): string { return "\x1b8"; } - static get keypadApplicationMode(): string { - return "\x1b="; - } - static get keypadNormalMode(): string { - return "\x1b>"; - } + /** * This is a full reset of the terminal, reverting it back to its original * default settings, clearing the screen, resetting modes, colors, character @@ -64,6 +66,7 @@ export class Ansi { static get hardReset(): string { return "\x1bc"; } + /** * This command resets many settings to their inital state without fully * reinitalising the terminal like {@linkcode Ansi.hardReset}. It preserves @@ -73,6 +76,7 @@ export class Ansi { static get softReset(): string { return "\x1b[!p"; } + /** * Inserts `x` spaces at the cursor position. Shifting existing line content * to the right. Cursor position does not change. Characters that exit the @@ -81,6 +85,22 @@ export class Ansi { static insertSpace(x = 1): string { return `\x1b[${x}@`; } + + /** + * Deletes `x` characters at cursor position and to the right. Shifting line + * content left. + */ + static deleteCharacters(x = 1): string { + return `\x1b[${x}P`; + } + + /** + * Erases `x` characters at cursor position and to the right. + */ + static eraseCharacters(x = 1): string { + return `\x1b[${x}X`; + } + /** * Inserts `x` lines at cursor position. Shifting current line and below * down. Cursor position does not change. Characters that exit the display @@ -89,57 +109,72 @@ export class Ansi { static insertLines(x = 1): string { return `\x1b[${x}L`; } + /** * Deletes `x` lines at cursor position. Shifting below lines up. */ static deleteLines(x = 1): string { return `\x1b[${x}M`; } - /** - * Deletes `x` characters at cursor position and to the right. Shifting line - * content left. - */ - static deleteCharacters(x = 1): string { - return `\x1b[${x}P`; - } + /** * Moves cursor position up `x` lines or up to the top margin. */ static moveCursorUp(x = 1): string { return `\x1b[${x}A`; } + + /** + * Moves cursor position `x` lines up or up to the top of the margin, and to + * the beginning of that line. + */ + static moveCursorUpStart(x = 1): string { + return `\x1b[${x}F`; + } + /** * Moves cursor position down `x` lines or up to the bottom margin. */ static moveCursorDown(x = 1): string { return `\x1b[${x}B`; } + + /** + * Moves cursor position `x` lines down or up to the bottom margin, and to + * the beginning of that line. + */ + static moveCursorDownStart(x = 1): string { + return `\x1b[${x}E`; + } + /** * Moves cursor position `x` columns right or up to the right margin. */ static moveCursorRight(x = 1): string { return `\x1b[${x}C`; } + /** - * Moves cursor position `x` columns left or up to the left margin. + * Moves cursor position `x` tab stops right or up to the right margin. */ - static moveCursorLeft(x = 1): string { - return `\x1b[${x}D`; + static moveCursorRightTab(x = 1): string { + return `\x1b[${x}I`; } + /** - * Moves cursor position `x` lines down or up to the bottom margin, and to - * the beginning of that line. + * Moves cursor position `x` columns left or up to the left margin. */ - static moveCursorDownStart(x = 1): string { - return `\x1b[${x}E`; + static moveCursorLeft(x = 1): string { + return `\x1b[${x}D`; } + /** - * Moves cursor position `x` lines up or up to the top of the margin, and to - * the beginning of that line. + * Moves cursor position `x` tab stops left or up to the left margin. */ - static moveCursorUpStart(x = 1): string { - return `\x1b[${x}F`; + static moveCursorLeftTab(x = 1): string { + return `\x1b[${x}Z`; } + /** * Sets cursor position to column `x` or up to the sides of the margins. * Columns begin at `1` not `0`. @@ -147,6 +182,7 @@ export class Ansi { static setCursorColumn(x = 1): string { return `\x1b[${x}G`; } + /** * Sets cursor position to line `x` or down to the bottom of the margin. * Lines begin at `1` not `0`. @@ -154,6 +190,7 @@ export class Ansi { static setCursorLine(x = 1): string { return `\x1b[${x}d`; } + /** * Sets cursor position to `x` line and `y` column or up to the margin. Lines * and columns begin at `1` not `0`. @@ -161,18 +198,28 @@ export class Ansi { static setCursorPosition(x = 1, y = 1): string { return `\x1b[${x};${y}H`; } + /** - * Moves cursor position `x` tab stops right or up to the right margin. + * Erases line content to the right of cursor position. */ - static moveCursorRightTab(x = 1): string { - return `\x1b[${x}I`; + static get eraseLineAfterCursor(): string { + return "\x1b[0K"; } + /** - * Moves cursor position `x` tab stops left or up to the left margin. + * Erases line content to the left of cursor position. */ - static moveCursorLeftTab(x = 1): string { - return `\x1b[${x}Z`; + static get eraseLineBeforeCursor(): string { + return "\x1b[1K"; } + + /** + * Erases entire line content. + */ + static get eraseLine(): string { + return "\x1b[2K"; + } + /** * Erases content of lines below cursor position and content to the right on * the same line as cursor. @@ -180,6 +227,7 @@ export class Ansi { static get eraseDisplayAfterCursor(): string { return "\x1b[0J"; } + /** * Erases content of lines above cursor position and content to the left on * the same line as cursor. @@ -187,36 +235,14 @@ export class Ansi { static get eraseDisplayBeforeCursor(): string { return "\x1b[1J"; } + /** * Erases all content. */ static get eraseDisplay(): string { return "\x1b[2J"; } - /** - * Erases line content to the right of cursor position. - */ - static get eraseLineAfterCursor(): string { - return "\x1b[0K"; - } - /** - * Erases line content to the left of cursor position. - */ - static get eraseLineBeforeCursor(): string { - return "\x1b[1K"; - } - /** - * Erases entire line content. - */ - static get eraseLine(): string { - return "\x1b[2K"; - } - /** - * Erases `x` characters at cursor position and to the right. - */ - static eraseCharacters(x = 1): string { - return `\x1b[${x}X`; - } + /** * Shifts content within the scrollable region up `x` lines, inserting blank * lines at the bottom of the scrollable region. @@ -224,6 +250,7 @@ export class Ansi { static shiftUpAndInsert(x = 1): string { return `\x1b[${x}S`; } + /** * Shifts content within the scrollable region down `x` lines, inserting * blank lines at the top of the scrollable region. @@ -231,12 +258,14 @@ export class Ansi { static shiftDownAndInsert(x = 1): string { return `\x1b[${x}T`; } + /** * Repeats last graphic character printed `x` times at cursor position. */ static repeatLastCharacter(x = 1): string { return `\x1b[${x}b`; } + /** * Causes existing characters to the right of the cursor position to shift * right as new characters are written. Opposite of @@ -245,6 +274,7 @@ export class Ansi { static get insertMode(): string { return "\x1b[4h"; } + /** * Causes existing characters to be overwritten at the cursor position by new * characters. See also {@linkcode Ansi.insertMode}. @@ -252,6 +282,7 @@ export class Ansi { static get replaceMode(): string { return "\x1b[4l"; } + /** * Causes top and bottom margins to shrink to scrollable region (See * {@linkcode Ansi.setScrollableRegion}) preventing the cursor from moving @@ -260,6 +291,7 @@ export class Ansi { static get enableOriginMode(): string { return "\x1b[?6h"; } + /** * Causes the top and bottom margins to enlarge to the user's display. See * {@linkcode Ansi.enableOriginMode}. @@ -267,6 +299,7 @@ export class Ansi { static get disableOriginMode(): string { return "\x1b[?6l"; } + /** * Causes cursor to automatically move to the next line when it hits the * end of the current line to continue writing. See also @@ -275,6 +308,7 @@ export class Ansi { static get enableAutoWrap(): string { return "\x1b[?7h"; } + /** * Causes cursor to remain on the same line when it hits the end of the * current line. See also {@linkcode Ansi.enableAutoWrap}. @@ -282,12 +316,14 @@ export class Ansi { static get disableAutoWrap(): string { return "\x1b[?7l"; } + /** * Sets the cursor animation style. */ static setCursorStyle(x: CursorStyle): string { return `\x1b[${x} q`; } + /** * Causes cursor position to be visible to the user. See also * {@linkcode Ansi.hideCursor}. @@ -295,6 +331,7 @@ export class Ansi { static get showCursor(): string { return "\x1b[?25h"; } + /** * Causes cursor position to be hidden from the user. See also * {@linkcode Ansi.showCursor}. @@ -302,6 +339,7 @@ export class Ansi { static get hideCursor(): string { return "\x1b[?25l"; } + /** * Sets the scrollable region of the display. Allowing either or both the top * and bottom lines to not have their content moved when the scrolling region From 3df59884a501e245e2a9c29c45ad4ce7824d6794 Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Tue, 8 Jul 2025 13:15:26 +1000 Subject: [PATCH 04/10] include default setting for cursor style --- cli/unstable_ansi.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/unstable_ansi.ts b/cli/unstable_ansi.ts index e962bfdccad7..a31c437a161c 100644 --- a/cli/unstable_ansi.ts +++ b/cli/unstable_ansi.ts @@ -353,6 +353,7 @@ export class Ansi { } export const enum CursorStyle { + Default = 0, BlinkingBlock = 1, SteadyBlock = 2, BlinkingUnderline = 3, From d8fe8907115964a8f132223cc6e73e1135224aec Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Tue, 8 Jul 2025 13:27:43 +1000 Subject: [PATCH 05/10] fix method to omit instead of calc number --- cli/unstable_ansi.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/unstable_ansi.ts b/cli/unstable_ansi.ts index a31c437a161c..1da1dd7fbff4 100644 --- a/cli/unstable_ansi.ts +++ b/cli/unstable_ansi.ts @@ -347,8 +347,7 @@ export class Ansi { * bottom line of the scrollable region. */ static setScrollableRegion(x = 1, y?: number): string { - y ??= Deno.consoleSize().rows; - return `\x1b[${x};${y}r`; + return `\x1b[${x}${y == undefined ? "" : `;${y}`}r`; } } From 9a33735659de69047cf22887116b70a71c4515db Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:28:12 +1000 Subject: [PATCH 06/10] add a million examples... --- cli/unstable_ansi.ts | 530 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 525 insertions(+), 5 deletions(-) diff --git a/cli/unstable_ansi.ts b/cli/unstable_ansi.ts index 1da1dd7fbff4..d19174c8c815 100644 --- a/cli/unstable_ansi.ts +++ b/cli/unstable_ansi.ts @@ -1,14 +1,39 @@ // Copyright 2018-2025 the Deno authors. MIT license. -// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html -// TODO: CSI Ps n - +/** + * Ansi is a class with static methods and properties that returns various Ansi + * Escape Sequences. This class is not an exhaustive list of what is possible + * with Ansi Escape Sequences, nor does it guarentee that every code will work + * in every terminal. The only way to guarentee that only one code will work in + * a particular terminal, is to check for yourself. Calling these methods and + * properties does not automatically change the terminal settings. Only once + * they are passed to stdout or stderr will they take effect. + * + * These codes were based off the + * [xterm reference](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html). + * + * @example Basic Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.doubleHeightTop + "Hello World!"); + * console.log(Ansi.doubleHeightBottom + "Hello World!"); + * ``` + */ export class Ansi { /** * Causes content on the current line to enlarge, showing only the top half * of characters with each character taking up two columns. Can be used in * combination with {@linkcode Ansi.doubleHeightBottom} on the next line to * make text appear twice as big. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.doubleHeightTop + "Hello World!"); + * console.log(Ansi.doubleHeightBottom + "Hello World!"); + * ``` */ static get doubleHeightTop(): string { return "\x1b#3"; @@ -19,6 +44,14 @@ export class Ansi { * half of the characters with each character taking up two columns. Can be * used in combination with {@linkcode Ansi.doubleHeightTop} on the previous * line to make text appear twice as big. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.doubleHeightTop + "Hello World!"); + * console.log(Ansi.doubleHeightBottom + "Hello World!"); + * ``` */ static get doubleHeightBottom(): string { return "\x1b#4"; @@ -26,7 +59,23 @@ export class Ansi { /** * Causes content on the current line to shrink down to a single column, - * essentally reverting the effects of {@linkcode Ansi.doubleHeightTop}, {@linkcode Ansi.doubleHeightBottom}, or {@linkcode Ansi.doubleWidth}. + * essentally reverting the effects of {@linkcode Ansi.doubleHeightTop}, + * {@linkcode Ansi.doubleHeightBottom}, or {@linkcode Ansi.doubleWidth}. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.doubleHeightTop + "Hello World!"); + * console.log(Ansi.doubleHeightBottom + "Hello World!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUpStart(2) + + * Ansi.deleteLines(1) + + * Ansi.singleWidth, + * ); + * ``` */ static get singleWidth(): string { return "\x1b#5"; @@ -34,7 +83,14 @@ export class Ansi { /** * Causes content on the current line to stretch out, with each character - * taking up two columns. Can be reverted with {@linkcode Ansi.singleWidth} + * taking up two columns. Can be reverted with {@linkcode Ansi.singleWidth}. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.doubleWidth + "Hello World!"); + * ``` */ static get doubleWidth(): string { return "\x1b#6"; @@ -43,6 +99,19 @@ export class Ansi { /** * Saves current cursor position that can later be restored with * {@linkcode Ansi.restoreCursorPosition}. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log( + * Ansi.saveCursorPosition + + * Ansi.setCursorPosition(Deno.consoleSize().rows, 1) + + * Ansi.eraseLine + + * "Hello World!" + + * Ansi.restoreCursorPosition, + * ); + * ``` */ static get saveCursorPosition(): string { return "\x1b7"; @@ -51,6 +120,19 @@ export class Ansi { /** * Restores cursor position that was earlier saved with * {@linkcode Ansi.saveCursorPosition}. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log( + * Ansi.saveCursorPosition + + * Ansi.setCursorPosition(Deno.consoleSize().rows) + + * Ansi.eraseLine + + * "Hello World!" + + * Ansi.restoreCursorPosition, + * ); + * ``` */ static get restoreCursorPosition(): string { return "\x1b8"; @@ -62,6 +144,16 @@ export class Ansi { * sets and more. Essentally making the terminal behave as if it were just * started by the user. This command is very disruptive to the user. Also see * {@linkcode Ansi.softReset}. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.hideCursor); + * await delay(5000); + * console.log(Ansi.hardReset); + * ``` */ static get hardReset(): string { return "\x1bc"; @@ -72,6 +164,16 @@ export class Ansi { * reinitalising the terminal like {@linkcode Ansi.hardReset}. It preserves * things like cursor position and display content, but clears modes, * character sets, etc. Should probably be called when exiting the program. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.hideCursor); + * await delay(5000); + * console.log(Ansi.softReset); + * ``` */ static get softReset(): string { return "\x1b[!p"; @@ -81,6 +183,20 @@ export class Ansi { * Inserts `x` spaces at the cursor position. Shifting existing line content * to the right. Cursor position does not change. Characters that exit the * display are discarded. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUp() + + * Ansi.setCursorColumn(6) + + * Ansi.insertSpace(10), + * ); + * ``` */ static insertSpace(x = 1): string { return `\x1b[${x}@`; @@ -89,6 +205,20 @@ export class Ansi { /** * Deletes `x` characters at cursor position and to the right. Shifting line * content left. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUpStart() + + * Ansi.deleteCharacters(5) + + * "Bye", + * ); + * ``` */ static deleteCharacters(x = 1): string { return `\x1b[${x}P`; @@ -96,6 +226,21 @@ export class Ansi { /** * Erases `x` characters at cursor position and to the right. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUp() + + * Ansi.setCursorColumn(7) + + * Ansi.eraseCharacters(6) + + * "Bob!", + * ); + * ``` */ static eraseCharacters(x = 1): string { return `\x1b[${x}X`; @@ -105,6 +250,20 @@ export class Ansi { * Inserts `x` lines at cursor position. Shifting current line and below * down. Cursor position does not change. Characters that exit the display * are discarded. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello\nWorld!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUpStart() + + * Ansi.insertLines() + + * "and Goodbye", + * ); + * ``` */ static insertLines(x = 1): string { return `\x1b[${x}L`; @@ -112,6 +271,18 @@ export class Ansi { /** * Deletes `x` lines at cursor position. Shifting below lines up. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello\nWorld!"); + * await delay(1000); + * console.log(Ansi.moveCursorUpStart() + Ansi.deleteLines()); + * await delay(1000); + * console.log("and Goodbye!"); + * ``` */ static deleteLines(x = 1): string { return `\x1b[${x}M`; @@ -119,6 +290,16 @@ export class Ansi { /** * Moves cursor position up `x` lines or up to the top margin. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello\nWorld!"); + * await delay(1000); + * console.log(Ansi.moveCursorUp(2) + "Goodbye"); + * ``` */ static moveCursorUp(x = 1): string { return `\x1b[${x}A`; @@ -127,6 +308,16 @@ export class Ansi { /** * Moves cursor position `x` lines up or up to the top of the margin, and to * the beginning of that line. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello\nWorld!"); + * await delay(1000); + * console.log(Ansi.moveCursorUpStart(2) + "Goodbye"); + * ``` */ static moveCursorUpStart(x = 1): string { return `\x1b[${x}F`; @@ -134,6 +325,23 @@ export class Ansi { /** * Moves cursor position down `x` lines or up to the bottom margin. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello\nWorld!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUpStart(2) + + * "Goodbye" + + * Ansi.moveCursorDown() + + * Ansi.setCursorColumn() + + * Ansi.eraseLine + + * "Bob!", + * ); + * ``` */ static moveCursorDown(x = 1): string { return `\x1b[${x}B`; @@ -142,6 +350,22 @@ export class Ansi { /** * Moves cursor position `x` lines down or up to the bottom margin, and to * the beginning of that line. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello\nWorld!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUpStart(2) + + * "Goodbye" + + * Ansi.moveCursorDownStart() + + * Ansi.eraseLine + + * "Bob!", + * ); + * ``` */ static moveCursorDownStart(x = 1): string { return `\x1b[${x}E`; @@ -149,6 +373,13 @@ export class Ansi { /** * Moves cursor position `x` columns right or up to the right margin. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.moveCursorRight(2) + "Hello World!"); + * ``` */ static moveCursorRight(x = 1): string { return `\x1b[${x}C`; @@ -156,6 +387,13 @@ export class Ansi { /** * Moves cursor position `x` tab stops right or up to the right margin. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.moveCursorRightTab() + "Hello World!"); + * ``` */ static moveCursorRightTab(x = 1): string { return `\x1b[${x}I`; @@ -163,6 +401,17 @@ export class Ansi { /** * Moves cursor position `x` columns left or up to the left margin. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log( + * Ansi.moveCursorRight(4) + + * Ansi.moveCursorLeft(2) + + * "Hello World!", + * ); + * ``` */ static moveCursorLeft(x = 1): string { return `\x1b[${x}D`; @@ -170,6 +419,16 @@ export class Ansi { /** * Moves cursor position `x` tab stops left or up to the left margin. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log( + * Ansi.moveCursorRightTab(2) + + * Ansi.moveCursorLeftTab() + + * "Hello World!", + * ); */ static moveCursorLeftTab(x = 1): string { return `\x1b[${x}Z`; @@ -178,6 +437,20 @@ export class Ansi { /** * Sets cursor position to column `x` or up to the sides of the margins. * Columns begin at `1` not `0`. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUp() + + * Ansi.setCursorColumn(7) + + * "and Goodbye!", + * ); + * ``` */ static setCursorColumn(x = 1): string { return `\x1b[${x}G`; @@ -186,6 +459,13 @@ export class Ansi { /** * Sets cursor position to line `x` or down to the bottom of the margin. * Lines begin at `1` not `0`. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.setCursorLine() + Ansi.eraseLine + "Hello World!"); + * ``` */ static setCursorLine(x = 1): string { return `\x1b[${x}d`; @@ -194,6 +474,17 @@ export class Ansi { /** * Sets cursor position to `x` line and `y` column or up to the margin. Lines * and columns begin at `1` not `0`. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log( + * Ansi.setCursorPosition(5, 2) + + * Ansi.eraseLine + + * "Hello World!", + * ); + * ``` */ static setCursorPosition(x = 1, y = 1): string { return `\x1b[${x};${y}H`; @@ -201,6 +492,20 @@ export class Ansi { /** * Erases line content to the right of cursor position. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUp() + + * Ansi.setCursorColumn(7) + + * Ansi.eraseLineAfterCursor, + * ); + * ``` */ static get eraseLineAfterCursor(): string { return "\x1b[0K"; @@ -208,6 +513,20 @@ export class Ansi { /** * Erases line content to the left of cursor position. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUp() + + * Ansi.setCursorColumn(7) + + * Ansi.eraseLineBeforeCursor, + * ); + * ``` */ static get eraseLineBeforeCursor(): string { return "\x1b[1K"; @@ -215,6 +534,16 @@ export class Ansi { /** * Erases entire line content. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log(Ansi.moveCursorUp() + Ansi.eraseLine); + * ``` */ static get eraseLine(): string { return "\x1b[2K"; @@ -223,6 +552,20 @@ export class Ansi { /** * Erases content of lines below cursor position and content to the right on * the same line as cursor. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUp() + + * Ansi.setCursorColumn(7) + + * Ansi.eraseDisplayAfterCursor, + * ); + * ``` */ static get eraseDisplayAfterCursor(): string { return "\x1b[0J"; @@ -231,6 +574,20 @@ export class Ansi { /** * Erases content of lines above cursor position and content to the left on * the same line as cursor. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.moveCursorUp() + + * Ansi.setCursorColumn(7) + + * Ansi.eraseDisplayBeforeCursor, + * ); + * ``` */ static get eraseDisplayBeforeCursor(): string { return "\x1b[1J"; @@ -238,6 +595,16 @@ export class Ansi { /** * Erases all content. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log(Ansi.eraseDisplay); + * ``` */ static get eraseDisplay(): string { return "\x1b[2J"; @@ -246,6 +613,13 @@ export class Ansi { /** * Shifts content within the scrollable region up `x` lines, inserting blank * lines at the bottom of the scrollable region. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.shiftUpAndInsert()); + * ``` */ static shiftUpAndInsert(x = 1): string { return `\x1b[${x}S`; @@ -254,6 +628,13 @@ export class Ansi { /** * Shifts content within the scrollable region down `x` lines, inserting * blank lines at the top of the scrollable region. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.shiftDownAndInsert(3)); + * ``` */ static shiftDownAndInsert(x = 1): string { return `\x1b[${x}T`; @@ -261,6 +642,13 @@ export class Ansi { /** * Repeats last graphic character printed `x` times at cursor position. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!" + Ansi.repeatLastCharacter(4)); + * ``` */ static repeatLastCharacter(x = 1): string { return `\x1b[${x}b`; @@ -270,6 +658,22 @@ export class Ansi { * Causes existing characters to the right of the cursor position to shift * right as new characters are written. Opposite of * {@linkcode Ansi.replaceMode}. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.insertMode + + * Ansi.moveCursorUp() + + * Ansi.setCursorColumn(7) + + * "and Goodbye " + + * Ansi.replaceMode, + * ); + * ``` */ static get insertMode(): string { return "\x1b[4h"; @@ -278,6 +682,22 @@ export class Ansi { /** * Causes existing characters to be overwritten at the cursor position by new * characters. See also {@linkcode Ansi.insertMode}. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log("Hello World!"); + * await delay(1000); + * console.log( + * Ansi.insertMode + + * Ansi.moveCursorUp() + + * Ansi.setCursorColumn(7) + + * "and Goodbye " + + * Ansi.replaceMode, + * ); + * ``` */ static get replaceMode(): string { return "\x1b[4l"; @@ -287,6 +707,22 @@ export class Ansi { * Causes top and bottom margins to shrink to scrollable region (See * {@linkcode Ansi.setScrollableRegion}) preventing the cursor from moving * to the lines outside it. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log( + * Ansi.eraseDisplay + + * Ansi.setCursorPosition() + + * "Hello World" + + * Ansi.setScrollableRegion(2) + + * Ansi.enableOriginMode, + * ); + * await delay(1000); + * console.log(Ansi.setCursorPosition() + "Bye World!"); + * ``` */ static get enableOriginMode(): string { return "\x1b[?6h"; @@ -295,6 +731,29 @@ export class Ansi { /** * Causes the top and bottom margins to enlarge to the user's display. See * {@linkcode Ansi.enableOriginMode}. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log( + * Ansi.eraseDisplay + + * Ansi.setCursorPosition() + + * "Hello World" + + * Ansi.setScrollableRegion(2) + + * Ansi.enableOriginMode, + * ); + * await delay(1000); + * console.log(Ansi.setCursorPosition() + "Bye World!"); + * await delay(1000); + * console.log( + * Ansi.disableOriginMode + + * Ansi.setCursorPosition() + + * Ansi.eraseLine + + * "Hi World!", + * ); + * ``` */ static get disableOriginMode(): string { return "\x1b[?6l"; @@ -304,6 +763,13 @@ export class Ansi { * Causes cursor to automatically move to the next line when it hits the * end of the current line to continue writing. See also * {@linkcode Ansi.disableAutoWrap}. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.enableAutoWrap + "A" + "h".repeat(500)); + * ``` */ static get enableAutoWrap(): string { return "\x1b[?7h"; @@ -312,6 +778,13 @@ export class Ansi { /** * Causes cursor to remain on the same line when it hits the end of the * current line. See also {@linkcode Ansi.enableAutoWrap}. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.disableAutoWrap + "A" + "h".repeat(500)); + * ``` */ static get disableAutoWrap(): string { return "\x1b[?7l"; @@ -319,6 +792,13 @@ export class Ansi { /** * Sets the cursor animation style. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.setCursorStyle(CursorStyle.BlinkingUnderline)); + * ``` */ static setCursorStyle(x: CursorStyle): string { return `\x1b[${x} q`; @@ -327,6 +807,15 @@ export class Ansi { /** * Causes cursor position to be visible to the user. See also * {@linkcode Ansi.hideCursor}. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.hideCursor); + * await delay(5000); + * console.log(Ansi.showCursor); */ static get showCursor(): string { return "\x1b[?25h"; @@ -335,6 +824,15 @@ export class Ansi { /** * Causes cursor position to be hidden from the user. See also * {@linkcode Ansi.showCursor}. + * + * @example Usage + * ```ts ignore + * import { delay } from "@std/async/delay"; + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.hideCursor); + * await delay(5000); + * console.log(Ansi.showCursor); */ static get hideCursor(): string { return "\x1b[?25l"; @@ -345,12 +843,34 @@ export class Ansi { * and bottom lines to not have their content moved when the scrolling region * is updated. `x` is the top line of the scrollable region. `y` is the * bottom line of the scrollable region. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log( + * Ansi.eraseDisplay + + * Ansi.setScrollableRegion(3, 10), + * ); + * setInterval(() => console.log(Math.random()), 1000); + * ``` */ static setScrollableRegion(x = 1, y?: number): string { return `\x1b[${x}${y == undefined ? "" : `;${y}`}r`; } } +/** + * CurorStyle is a const enum used to set the value in + * {@linkcode Ansi.setCursorStyle}. + * + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable_ansi"; + * + * console.log(Ansi.setCursorStyle(CursorStyle.BlinkingUnderline)); + * ``` + */ export const enum CursorStyle { Default = 0, BlinkingBlock = 1, From 6882b322ce725c6a57493e81f48de7ac4e438068 Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:30:19 +1000 Subject: [PATCH 07/10] add export --- cli/deno.json | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/deno.json b/cli/deno.json index 722276e6ec35..bd336083b2d5 100644 --- a/cli/deno.json +++ b/cli/deno.json @@ -5,6 +5,7 @@ ".": "./mod.ts", "./parse-args": "./parse_args.ts", "./prompt-secret": "./prompt_secret.ts", + "./unstable-ansi": "./unstable_ansi.ts", "./unstable-progress-bar": "./unstable_progress_bar.ts", "./unstable-progress-bar-stream": "./unstable_progress_bar_stream.ts", "./unstable-prompt-select": "./unstable_prompt_select.ts", From 7c94569dbfabe7bb170c865a8cb40a6d2a3dc3a9 Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:30:27 +1000 Subject: [PATCH 08/10] fix spelling --- cli/unstable_ansi.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cli/unstable_ansi.ts b/cli/unstable_ansi.ts index d19174c8c815..a550b1436cb8 100644 --- a/cli/unstable_ansi.ts +++ b/cli/unstable_ansi.ts @@ -3,8 +3,8 @@ /** * Ansi is a class with static methods and properties that returns various Ansi * Escape Sequences. This class is not an exhaustive list of what is possible - * with Ansi Escape Sequences, nor does it guarentee that every code will work - * in every terminal. The only way to guarentee that only one code will work in + * with Ansi Escape Sequences, nor does it guarantee that every code will work + * in every terminal. The only way to guarantee that only one code will work in * a particular terminal, is to check for yourself. Calling these methods and * properties does not automatically change the terminal settings. Only once * they are passed to stdout or stderr will they take effect. @@ -59,7 +59,7 @@ export class Ansi { /** * Causes content on the current line to shrink down to a single column, - * essentally reverting the effects of {@linkcode Ansi.doubleHeightTop}, + * essentially reverting the effects of {@linkcode Ansi.doubleHeightTop}, * {@linkcode Ansi.doubleHeightBottom}, or {@linkcode Ansi.doubleWidth}. * * @example Usage @@ -141,7 +141,7 @@ export class Ansi { /** * This is a full reset of the terminal, reverting it back to its original * default settings, clearing the screen, resetting modes, colors, character - * sets and more. Essentally making the terminal behave as if it were just + * sets and more. Essentially making the terminal behave as if it were just * started by the user. This command is very disruptive to the user. Also see * {@linkcode Ansi.softReset}. * @@ -160,8 +160,8 @@ export class Ansi { } /** - * This command resets many settings to their inital state without fully - * reinitalising the terminal like {@linkcode Ansi.hardReset}. It preserves + * This command resets many settings to their initial state without fully + * reinitialising the terminal like {@linkcode Ansi.hardReset}. It preserves * things like cursor position and display content, but clears modes, * character sets, etc. Should probably be called when exiting the program. * @@ -861,7 +861,7 @@ export class Ansi { } /** - * CurorStyle is a const enum used to set the value in + * CursorStyle is a const enum used to set the value in * {@linkcode Ansi.setCursorStyle}. * * @example Usage From 7aa82151ddb81571a89fa055cb4e57f0b134e210 Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:49:36 +1000 Subject: [PATCH 09/10] fix lint errors by adding missing jsdocs --- cli/unstable_ansi.ts | 123 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/cli/unstable_ansi.ts b/cli/unstable_ansi.ts index a550b1436cb8..7719979c8b36 100644 --- a/cli/unstable_ansi.ts +++ b/cli/unstable_ansi.ts @@ -27,6 +27,8 @@ export class Ansi { * combination with {@linkcode Ansi.doubleHeightBottom} on the next line to * make text appear twice as big. * + * @returns string The ANSI escape code for double-height top. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -45,6 +47,8 @@ export class Ansi { * used in combination with {@linkcode Ansi.doubleHeightTop} on the previous * line to make text appear twice as big. * + * @returns string The ANSI escape code for double-height bottom. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -62,6 +66,8 @@ export class Ansi { * essentially reverting the effects of {@linkcode Ansi.doubleHeightTop}, * {@linkcode Ansi.doubleHeightBottom}, or {@linkcode Ansi.doubleWidth}. * + * @returns string The ANSI escape code for single-width. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -85,6 +91,8 @@ export class Ansi { * Causes content on the current line to stretch out, with each character * taking up two columns. Can be reverted with {@linkcode Ansi.singleWidth}. * + * @returns string The ANSI escape code for double-width. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -100,6 +108,8 @@ export class Ansi { * Saves current cursor position that can later be restored with * {@linkcode Ansi.restoreCursorPosition}. * + * @returns string The ANSI escape code for saving cursor position. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -121,6 +131,8 @@ export class Ansi { * Restores cursor position that was earlier saved with * {@linkcode Ansi.saveCursorPosition}. * + * @returns string The ANSI escape code for restoring cursor position. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -145,6 +157,8 @@ export class Ansi { * started by the user. This command is very disruptive to the user. Also see * {@linkcode Ansi.softReset}. * + * @returns string The ANSI escape code for hard resetting the terminal. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -161,10 +175,12 @@ export class Ansi { /** * This command resets many settings to their initial state without fully - * reinitialising the terminal like {@linkcode Ansi.hardReset}. It preserves + * reinitializing the terminal like {@linkcode Ansi.hardReset}. It preserves * things like cursor position and display content, but clears modes, * character sets, etc. Should probably be called when exiting the program. * + * @returns string The ANSI escape code for soft resetting the terminal. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -184,6 +200,9 @@ export class Ansi { * to the right. Cursor position does not change. Characters that exit the * display are discarded. * + * @param x The number of spaces to insert. + * @returns string The ANSI escape code for inserting spaces. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -206,6 +225,9 @@ export class Ansi { * Deletes `x` characters at cursor position and to the right. Shifting line * content left. * + * @param x The number of characters to delete. + * @returns string The ANSI escape code for deleting characters. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -227,6 +249,9 @@ export class Ansi { /** * Erases `x` characters at cursor position and to the right. * + * @param x The number of characters to erase. + * @returns string The ANSI escape code for erasing characters. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -251,6 +276,9 @@ export class Ansi { * down. Cursor position does not change. Characters that exit the display * are discarded. * + * @param x The number of lines to insert. + * @returns string The ANSI escape code for inserting lines. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -272,6 +300,9 @@ export class Ansi { /** * Deletes `x` lines at cursor position. Shifting below lines up. * + * @param x The number of lines to delete. + * @returns string The ANSI escape code for deleting lines. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -291,6 +322,9 @@ export class Ansi { /** * Moves cursor position up `x` lines or up to the top margin. * + * @param x The number of lines to move up. + * @returns string The ANSI escape code for moving the cursor up. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -309,6 +343,10 @@ export class Ansi { * Moves cursor position `x` lines up or up to the top of the margin, and to * the beginning of that line. * + * @param x The number of lines to move up. + * @returns string The ANSI escape code for moving the cursor up and to the + * beginning of the line. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -326,6 +364,9 @@ export class Ansi { /** * Moves cursor position down `x` lines or up to the bottom margin. * + * @param x The number of lines to move down. + * @returns string The ANSI escape code for moving the cursor down. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -351,6 +392,10 @@ export class Ansi { * Moves cursor position `x` lines down or up to the bottom margin, and to * the beginning of that line. * + * @param x The number of lines to move down. + * @returns string The ANSI escape code for moving the cursor down and to the + * beginning of the line. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -374,6 +419,9 @@ export class Ansi { /** * Moves cursor position `x` columns right or up to the right margin. * + * @param x The number of columns to move right. + * @returns string The ANSI escape code for moving the cursor right. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -388,6 +436,10 @@ export class Ansi { /** * Moves cursor position `x` tab stops right or up to the right margin. * + * @param x The number of tab stops to move right. + * @returns string The ANSI escape code for moving the cursor right every tab + * stop. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -402,6 +454,9 @@ export class Ansi { /** * Moves cursor position `x` columns left or up to the left margin. * + * @param x The number of columns to move left. + * @returns string The ANSI escape code for moving the cursor left. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -420,6 +475,10 @@ export class Ansi { /** * Moves cursor position `x` tab stops left or up to the left margin. * + * @param x The number of tab stops to move left. + * @returns string The ANSI escape code for moving the cursor left every tab + * stop. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -429,6 +488,7 @@ export class Ansi { * Ansi.moveCursorLeftTab() + * "Hello World!", * ); + * ``` */ static moveCursorLeftTab(x = 1): string { return `\x1b[${x}Z`; @@ -438,6 +498,9 @@ export class Ansi { * Sets cursor position to column `x` or up to the sides of the margins. * Columns begin at `1` not `0`. * + * @param x The column to move to. + * @returns string The ANSI escape code for setting the cursor column. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -460,6 +523,9 @@ export class Ansi { * Sets cursor position to line `x` or down to the bottom of the margin. * Lines begin at `1` not `0`. * + * @param x The line to move to. + * @returns string The ANSI escape code for setting the cursor line. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -475,6 +541,10 @@ export class Ansi { * Sets cursor position to `x` line and `y` column or up to the margin. Lines * and columns begin at `1` not `0`. * + * @param x The line to move to. + * @param y The column to move to. + * @returns string The ANSI escape code for setting the cursor position. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -493,6 +563,9 @@ export class Ansi { /** * Erases line content to the right of cursor position. * + * @returns string The ANSI escape code for erasing the line content to the + * right of the cursor. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -514,6 +587,9 @@ export class Ansi { /** * Erases line content to the left of cursor position. * + * @returns string The ANSI escape code for erasing the line content to the + * left of the cursor. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -535,6 +611,8 @@ export class Ansi { /** * Erases entire line content. * + * @returns string The ANSI escape code for erasing the entire line content. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -553,6 +631,9 @@ export class Ansi { * Erases content of lines below cursor position and content to the right on * the same line as cursor. * + * @returns string The ANSI escape code for erasing the content after the + * cursor. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -575,6 +656,9 @@ export class Ansi { * Erases content of lines above cursor position and content to the left on * the same line as cursor. * + * @returns string The ANSI escape code for erasing the content before the + * cursor. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -596,6 +680,8 @@ export class Ansi { /** * Erases all content. * + * @returns string The ANSI escape code for erasing the entire display. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -614,6 +700,9 @@ export class Ansi { * Shifts content within the scrollable region up `x` lines, inserting blank * lines at the bottom of the scrollable region. * + * @param x The number of lines content should be shifted up. + * @returns string The ANSI escape code for shifting content up. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -629,6 +718,9 @@ export class Ansi { * Shifts content within the scrollable region down `x` lines, inserting * blank lines at the top of the scrollable region. * + * @param x The number of lines content should be shifted down. + * @returns string The ANSI escape code for shifting content down. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -643,6 +735,10 @@ export class Ansi { /** * Repeats last graphic character printed `x` times at cursor position. * + * @param x The number of times the last character printed should be repeated. + * @returns string The ANSI escape code for repeating the last printed + * character. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -659,6 +755,8 @@ export class Ansi { * right as new characters are written. Opposite of * {@linkcode Ansi.replaceMode}. * + * @returns string The ANSI escape code for entering insert mode. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -683,6 +781,8 @@ export class Ansi { * Causes existing characters to be overwritten at the cursor position by new * characters. See also {@linkcode Ansi.insertMode}. * + * @returns string The ANSI escape code for entering replace mode. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -708,6 +808,8 @@ export class Ansi { * {@linkcode Ansi.setScrollableRegion}) preventing the cursor from moving * to the lines outside it. * + * @returns string The ANSI escape code for enabling origin mode. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -732,6 +834,8 @@ export class Ansi { * Causes the top and bottom margins to enlarge to the user's display. See * {@linkcode Ansi.enableOriginMode}. * + * @returns string The ANSI escape code for disabling origin mode. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -764,6 +868,8 @@ export class Ansi { * end of the current line to continue writing. See also * {@linkcode Ansi.disableAutoWrap}. * + * @returns string The ANSI escape code to enable auto-wrap. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -779,6 +885,8 @@ export class Ansi { * Causes cursor to remain on the same line when it hits the end of the * current line. See also {@linkcode Ansi.enableAutoWrap}. * + * @returns string The ANSI escape code to disable auto-wrap. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -793,6 +901,9 @@ export class Ansi { /** * Sets the cursor animation style. * + * @param x The cursor style to set. + * @returns string The ANSI escape code to set the cursor style. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; @@ -808,6 +919,8 @@ export class Ansi { * Causes cursor position to be visible to the user. See also * {@linkcode Ansi.hideCursor}. * + * @returns string The ANSI escape code to show the cursor. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -816,6 +929,7 @@ export class Ansi { * console.log(Ansi.hideCursor); * await delay(5000); * console.log(Ansi.showCursor); + * ``` */ static get showCursor(): string { return "\x1b[?25h"; @@ -825,6 +939,8 @@ export class Ansi { * Causes cursor position to be hidden from the user. See also * {@linkcode Ansi.showCursor}. * + * @returns string The ANSI escape code to hide the cursor. + * * @example Usage * ```ts ignore * import { delay } from "@std/async/delay"; @@ -833,6 +949,7 @@ export class Ansi { * console.log(Ansi.hideCursor); * await delay(5000); * console.log(Ansi.showCursor); + * ``` */ static get hideCursor(): string { return "\x1b[?25l"; @@ -844,6 +961,10 @@ export class Ansi { * is updated. `x` is the top line of the scrollable region. `y` is the * bottom line of the scrollable region. * + * @param x The top line of the scrollable region. + * @param y The bottom line of the scrollable region. + * @returns string The ANSI escape code to set the scrollable region. + * * @example Usage * ```ts ignore * import { Ansi } from "@std/cli/unstable_ansi"; From b13ee750cd2426eb98733439dae314937ad5823a Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:00:27 +1000 Subject: [PATCH 10/10] feat(cli): askForCursorPositionSync --- cli/deno.json | 1 + cli/unstable_get_cursor.ts | 78 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 cli/unstable_get_cursor.ts diff --git a/cli/deno.json b/cli/deno.json index bd336083b2d5..e7618c4a3b12 100644 --- a/cli/deno.json +++ b/cli/deno.json @@ -6,6 +6,7 @@ "./parse-args": "./parse_args.ts", "./prompt-secret": "./prompt_secret.ts", "./unstable-ansi": "./unstable_ansi.ts", + "./unstable-get-cursor": "./unstable_get_cursor.ts", "./unstable-progress-bar": "./unstable_progress_bar.ts", "./unstable-progress-bar-stream": "./unstable_progress_bar_stream.ts", "./unstable-prompt-select": "./unstable_prompt_select.ts", diff --git a/cli/unstable_get_cursor.ts b/cli/unstable_get_cursor.ts new file mode 100644 index 000000000000..16341d4e1b5b --- /dev/null +++ b/cli/unstable_get_cursor.ts @@ -0,0 +1,78 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +/** + * This function asks the terminal for the cursor's current position. + * Information entered by the user will be discarded while requesting the cursor + * position. The cursor position may have also moved in this time due to the + * user entering information. Due to the sync nature of this function, it is + * not recommended to use this function frequently. Instead + * {@linkcode Ansi.saveCursorPosition} and + * {@linkcode Ansi.restoreCursorPosition} should be used when possible. + * + * @returns The cursor position or null if the terminal is not a TTY. + * @example Usage + * ```ts ignore + * import { Ansi } from "@std/cli/unstable-ansi"; + * import { askForCursorPositionSync } from "@std/cli/unstable-get-cursor"; + * + * const { column, row } = askForCursorPositionSync()!; + * await Deno.stderr.write(new TextEncoder().encode( + * Ansi.setCursorPosition() + + * "Hello World!" + + * Ansi.setCursorPosition(row, column), + * )); + * ``` + */ +export function askForCursorPositionSync(): + | { row: number; column: number } + | null { + if (!Deno.stdin.isTerminal() || !Deno.stderr.isTerminal()) { + return null; + } + Deno.stdin.setRaw(true, { cbreak: true }); + Deno.stderr.writeSync(Uint8Array.from([0x1b, 0x5b, 0x36, 0x6e])); // \x1b[6n + const buffer = new Uint8Array(1024); + while (true) { + const x = Deno.stdin.readSync(buffer.subarray(32)); + const csi = findCSI(buffer.subarray(0, 32 + x!)); + if (csi?.final === 82) { // R + Deno.stdin.setRaw(false); + const [row, column] = new TextDecoder() + .decode(csi.parameters) + .split(";") + .map((x) => parseInt(x)); + return { row: row!, column: column! }; + } + buffer.set(buffer.subarray(buffer.length - 32)); + } +} + +function findCSI( + buffer: Uint8Array, +): { parameters: Uint8Array; intermediates: Uint8Array; final: number } | null { + for (let i = 0; i < buffer.length - 1; ++i) { + if (buffer[i] === 0x1b && buffer[i + 1] === 0x5b) { + i += 2; + const parameters = getRange(buffer.subarray(i), 0x30, 0x3f); + i += parameters.length; + const intermediates = getRange(buffer.subarray(i), 0x20, 0x2f); + i += intermediates.length; + const final = buffer[i]; + if (final != undefined && 0x40 <= final && final <= 0x7e) { + return { parameters, intermediates, final }; + } + } + } + return null; +} + +function getRange(buffer: Uint8Array, min: number, max: number) { + let i = 0; + for (; i < buffer.length; ++i) { + const byte = buffer[i]!; + if (byte < min || max < byte) { + return buffer.subarray(0, i); + } + } + return buffer.subarray(0, i); +}