221 lines
6.2 KiB
TypeScript
221 lines
6.2 KiB
TypeScript
import { nanoseconds } from 'bun'
|
|
|
|
const encoder = new TextEncoder()
|
|
const emptyBytes = encoder.encode('')
|
|
type Writer = (x: Uint8Array | Array<Uint8Array>) => Promise<number>
|
|
const writer: Writer = x => Bun.write(Bun.stdout, x)
|
|
|
|
export const ANSI = {
|
|
// Text Styles
|
|
RESET: '\x1B[0m',
|
|
BOLD: '\x1B[1m',
|
|
DIM: '\x1B[2m',
|
|
ITALIC: '\x1B[3m',
|
|
UNDERLINE: '\x1B[4m',
|
|
BLINK: '\x1B[5m',
|
|
REVERSE: '\x1B[7m',
|
|
HIDDEN: '\x1B[8m',
|
|
|
|
// Foreground Colors
|
|
BLACK: '\x1B[30m',
|
|
RED: '\x1B[31m',
|
|
GREEN: '\x1B[32m',
|
|
YELLOW: '\x1B[33m',
|
|
BLUE: '\x1B[34m',
|
|
MAGENTA: '\x1B[35m',
|
|
CYAN: '\x1B[36m',
|
|
WHITE: '\x1B[37m',
|
|
|
|
// Background Colors
|
|
BG_BLACK: '\x1B[40m',
|
|
BG_RED: '\x1B[41m',
|
|
BG_GREEN: '\x1B[42m',
|
|
BG_YELLOW: '\x1B[43m',
|
|
BG_BLUE: '\x1B[44m',
|
|
BG_MAGENTA: '\x1B[45m',
|
|
BG_CYAN: '\x1B[46m',
|
|
BG_WHITE: '\x1B[47m',
|
|
|
|
// Cursor Control
|
|
GET_CURSOR_POSITION: '\x1B[6n',
|
|
SAVE_CURSOR: '\x1B[s',
|
|
RESTORE_CURSOR: '\x1B[u',
|
|
CURSOR_TO: (x: number, y: number) => `\x1B[${y};${x}H`,
|
|
CURSOR_UP: (n = 1) => `\x1B[${n}A`,
|
|
CURSOR_DOWN: (n = 1) => `\x1B[${n}B`,
|
|
CURSOR_FORWARD: (n = 1) => `\x1B[${n}C`,
|
|
CURSOR_BACK: (n = 1) => `\x1B[${n}D`,
|
|
|
|
// Screen Control
|
|
CLEAR_SCREEN: '\x1B[2J',
|
|
CLEAR_LINE: '\x1B[2K',
|
|
CLEAR_END_LINE: '\x1B[0K',
|
|
CLEAR_START_LINE: '\x1B[1K',
|
|
CLEAR_TO_END: '\x1B[0J', // From cursor to end of screen
|
|
CLEAR_TO_START: '\x1B[1J', // From cursor to beginning of screen
|
|
CLEAR_BELOW: '\x1B[J',
|
|
}
|
|
|
|
export const ANSI_BUFFERS = {
|
|
// Static code buffers
|
|
RESET: encoder.encode(ANSI.RESET),
|
|
BOLD: encoder.encode(ANSI.BOLD),
|
|
DIM: encoder.encode(ANSI.DIM),
|
|
ITALIC: encoder.encode(ANSI.ITALIC),
|
|
UNDERLINE: encoder.encode(ANSI.UNDERLINE),
|
|
BLINK: encoder.encode(ANSI.BLINK),
|
|
REVERSE: encoder.encode(ANSI.REVERSE),
|
|
HIDDEN: encoder.encode(ANSI.HIDDEN),
|
|
|
|
// Colors
|
|
BLACK: encoder.encode(ANSI.BLACK),
|
|
RED: encoder.encode(ANSI.RED),
|
|
GREEN: encoder.encode(ANSI.GREEN),
|
|
YELLOW: encoder.encode(ANSI.YELLOW),
|
|
BLUE: encoder.encode(ANSI.BLUE),
|
|
MAGENTA: encoder.encode(ANSI.MAGENTA),
|
|
CYAN: encoder.encode(ANSI.CYAN),
|
|
WHITE: encoder.encode(ANSI.WHITE),
|
|
|
|
// Backgrounds
|
|
BG_BLACK: encoder.encode(ANSI.BG_BLACK),
|
|
BG_RED: encoder.encode(ANSI.BG_RED),
|
|
BG_GREEN: encoder.encode(ANSI.BG_GREEN),
|
|
BG_YELLOW: encoder.encode(ANSI.BG_YELLOW),
|
|
BG_BLUE: encoder.encode(ANSI.BG_BLUE),
|
|
BG_MAGENTA: encoder.encode(ANSI.BG_MAGENTA),
|
|
BG_CYAN: encoder.encode(ANSI.BG_CYAN),
|
|
BG_WHITE: encoder.encode(ANSI.BG_WHITE),
|
|
|
|
// Cursor Control
|
|
GET_CURSOR_POSITION: encoder.encode(ANSI.GET_CURSOR_POSITION),
|
|
SAVE_CURSOR: encoder.encode(ANSI.SAVE_CURSOR),
|
|
RESTORE_CURSOR: encoder.encode(ANSI.RESTORE_CURSOR),
|
|
|
|
// Screen Control
|
|
CLEAR_SCREEN: encoder.encode(ANSI.CLEAR_SCREEN),
|
|
CLEAR_LINE: encoder.encode(ANSI.CLEAR_LINE),
|
|
CLEAR_TO_END: encoder.encode(ANSI.CLEAR_TO_END),
|
|
CLEAR_TO_START: encoder.encode(ANSI.CLEAR_TO_START),
|
|
CLEAR_BELOW: encoder.encode(ANSI.CLEAR_BELOW),
|
|
}
|
|
|
|
export const ANSI_DYNAMIC = {
|
|
// Cursor Control
|
|
CURSOR_TO: (x: number, y: number) => encoder.encode(ANSI.CURSOR_TO(x, y)),
|
|
CURSOR_UP: (x = 1) => encoder.encode(ANSI.CURSOR_UP(x)),
|
|
CURSOR_DOWN: (x = 1) => encoder.encode(ANSI.CURSOR_DOWN(x)),
|
|
CURSOR_FORWARD: (x = 1) => encoder.encode(ANSI.CURSOR_FORWARD(x)),
|
|
CURSOR_BACK: (x = 1) => encoder.encode(ANSI.CURSOR_BACK(x)),
|
|
}
|
|
|
|
type AnsiBufferKey = keyof typeof ANSI_BUFFERS
|
|
|
|
type WriteAnsi = (
|
|
c: Array<(AnsiBufferKey | Uint8Array)>,
|
|
t?: string,
|
|
a?: boolean,
|
|
) => Promise<number>
|
|
const writeAnsi: WriteAnsi = async (codes, text?, autoReset = true) => {
|
|
const arr = [
|
|
...codes,
|
|
!text ? emptyBytes : encoder.encode(text),
|
|
autoReset ? ANSI_BUFFERS['RESET'] : emptyBytes,
|
|
].map(x => x instanceof Uint8Array ? x : ANSI_BUFFERS[x])
|
|
|
|
return writer(Buffer.concat(arr))
|
|
}
|
|
|
|
const writeAnsi1: WriteAnsi = async (codes, text?, autoReset = true) => {
|
|
const buffers = [
|
|
...codes,
|
|
!text ? emptyBytes : encoder.encode(text),
|
|
autoReset ? ANSI_BUFFERS['RESET'] : emptyBytes,
|
|
].map(x => x instanceof Uint8Array ? x : ANSI_BUFFERS[x])
|
|
|
|
// Calculate total length and create combined buffer
|
|
const totalLength = buffers.reduce((sum, buf) => sum + buf.length, 0)
|
|
const combined = new Uint8Array(totalLength)
|
|
|
|
// Copy all buffers into the combined array
|
|
let offset = 0
|
|
for (const buffer of buffers) {
|
|
combined.set(buffer, offset)
|
|
offset += buffer.length
|
|
}
|
|
|
|
return writer(combined)
|
|
}
|
|
|
|
// For CommonJS compatibility
|
|
export default {ANSI, ANSI_BUFFERS, writeAnsi}
|
|
|
|
const bench = async (x, n) => {
|
|
const a = Bun.nanoseconds()
|
|
let i = 0
|
|
while (i < n) {
|
|
await x()
|
|
i++
|
|
}
|
|
const b = Bun.nanoseconds()
|
|
return (b - a) / 100000
|
|
}
|
|
|
|
const one = async () => {
|
|
await writeAnsi(
|
|
['BG_CYAN', 'BLACK', 'ITALIC'],
|
|
'This is the first option I want',
|
|
)
|
|
await writeAnsi(
|
|
['BG_BLUE', 'BLACK', 'BOLD'],
|
|
'This is the second option I want',
|
|
)
|
|
await writeAnsi(
|
|
['BG_YELLOW', 'BLACK', 'BLINK'],
|
|
'Now, this is the third and final option',
|
|
)
|
|
}
|
|
|
|
const two = async () => {
|
|
await writeAnsi1(
|
|
['BG_CYAN', 'BLACK', 'ITALIC'],
|
|
'This is the first option I want',
|
|
)
|
|
await writeAnsi1(
|
|
['BG_BLUE', 'BLACK', 'BOLD'],
|
|
'This is the second option I want',
|
|
)
|
|
await writeAnsi1(
|
|
['BG_YELLOW', 'BLACK', 'BLINK'],
|
|
'Now, this is the third and final option',
|
|
)
|
|
}
|
|
|
|
const test = async () => {
|
|
const testN = 100000
|
|
|
|
await bench(one, 100000)
|
|
await bench(two, 100000)
|
|
|
|
const testTwo = await bench(two, testN)
|
|
const testOne = await bench(one, testN)
|
|
|
|
await writeAnsi(
|
|
['RESET', ANSI_DYNAMIC.CURSOR_TO(0, 0)],
|
|
`${ANSI.CLEAR_SCREEN}\n\n`,
|
|
)
|
|
await writeAnsi(['BG_YELLOW', 'BLACK'], '---------------\n')
|
|
await writeAnsi(['BG_YELLOW', 'BOLD', 'BLACK'], '***************\n')
|
|
await writeAnsi(['BG_YELLOW', 'BLACK'], '---------------\n')
|
|
await writeAnsi(['BG_YELLOW', 'BLACK'], 'ONE:')
|
|
await writeAnsi(['BG_YELLOW', 'GREEN'], testOne.toString() + '\n')
|
|
await writeAnsi(['BG_YELLOW', 'BLACK'], 'TWO:')
|
|
await writeAnsi(['BG_YELLOW', 'GREEN'], testTwo.toString() + '\n')
|
|
await writeAnsi(['BG_YELLOW', 'BLACK'], '---------------\n')
|
|
await writeAnsi(['BG_YELLOW', 'BOLD', 'BLACK'], '***************\n')
|
|
await writeAnsi(['BG_YELLOW', 'BLACK'], '---------------')
|
|
await writeAnsi(['RESET'], '\n\n')
|
|
}
|
|
|
|
test()
|