Add writeAnsiU function and remove tests
`writeAnsi` is a convenient function that automatically encodes text and automatically adds a reset. `writeAnsiU` is a unary function that takes in an array of Uint8Arrays and keys for ANSI_BUFFERS. There is no automatic text encoding, so an `ansiText` function has been added. There is also no automatic reset. This allows the function's logic to be simpler, resulting in 3-4 times performance increase per Windows benchmarking. It is also more flexible for complex formatting scenarios. Both functions convienently allow strings that are keys of ANSI_BUFFERS to be passed, and these are auto-matched. Both functions return a Promise<number>, allowing for maximum performance but necessitating error handling on the part of the consumer. It is also important to `await` these functions when calling in a specific order. This is an advantage of `writeAnsiU`, which allows for longer, more complex prints in a single function call.
This commit is contained in:
parent
4151d0335c
commit
e83fcd0b4d
@ -100,7 +100,7 @@ export const ANSI_BUFFERS = {
|
|||||||
CLEAR_BELOW: encoder.encode(ANSI.CLEAR_BELOW),
|
CLEAR_BELOW: encoder.encode(ANSI.CLEAR_BELOW),
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ANSI_DYNAMIC = {
|
const ANSI_DYNAMIC = {
|
||||||
// Cursor Control
|
// Cursor Control
|
||||||
CURSOR_TO: (x: number, y: number) => encoder.encode(ANSI.CURSOR_TO(x, y)),
|
CURSOR_TO: (x: number, y: number) => encoder.encode(ANSI.CURSOR_TO(x, y)),
|
||||||
CURSOR_UP: (x = 1) => encoder.encode(ANSI.CURSOR_UP(x)),
|
CURSOR_UP: (x = 1) => encoder.encode(ANSI.CURSOR_UP(x)),
|
||||||
@ -116,7 +116,7 @@ type WriteAnsi = (
|
|||||||
t?: string,
|
t?: string,
|
||||||
a?: boolean,
|
a?: boolean,
|
||||||
) => Promise<number>
|
) => Promise<number>
|
||||||
const writeAnsi: WriteAnsi = async (codes, text?, autoReset = true) => {
|
const writeAnsi: WriteAnsi = (codes, text?, autoReset = true) => {
|
||||||
const arr = [
|
const arr = [
|
||||||
...codes,
|
...codes,
|
||||||
!text ? emptyBytes : encoder.encode(text),
|
!text ? emptyBytes : encoder.encode(text),
|
||||||
@ -126,95 +126,21 @@ const writeAnsi: WriteAnsi = async (codes, text?, autoReset = true) => {
|
|||||||
return writer(Buffer.concat(arr))
|
return writer(Buffer.concat(arr))
|
||||||
}
|
}
|
||||||
|
|
||||||
const writeAnsi1: WriteAnsi = async (codes, text?, autoReset = true) => {
|
type AnsiText = (x: string) => Uint8Array
|
||||||
const buffers = [
|
const ansiText: AnsiText = x => encoder.encode(x)
|
||||||
...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
|
type WriteAnsiU = (x: Array<AnsiBufferKey | Uint8Array>) => Promise<number>
|
||||||
const totalLength = buffers.reduce((sum, buf) => sum + buf.length, 0)
|
const writeAnsiU: WriteAnsiU = x =>
|
||||||
const combined = new Uint8Array(totalLength)
|
writer(Buffer.concat(x.map(
|
||||||
|
x => x instanceof Uint8Array ? x : ANSI_BUFFERS[x],
|
||||||
// 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
|
// For CommonJS compatibility
|
||||||
export default {ANSI, ANSI_BUFFERS, writeAnsi}
|
export default {
|
||||||
|
ANSI,
|
||||||
const bench = async (x, n) => {
|
ANSI_BUFFERS,
|
||||||
const a = Bun.nanoseconds()
|
ANSI_DYNAMIC,
|
||||||
let i = 0
|
ansiText,
|
||||||
while (i < n) {
|
writeAnsi,
|
||||||
await x()
|
writeAnsiU,
|
||||||
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()
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user