[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay bomb.sh/docs/tty
Something went wrong. Try again.
TypeScript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798import { describe, expect, it } from "./suite.ts";import { createTerm } from "../term.ts";import { close, grow, open, pack, text } from "../ops.ts";import { createTermNative } from "../term-native.ts";import { print } from "./print.ts";
const decode = (bytes: Uint8Array) => new TextDecoder().decode(bytes);
function indexOfSeq(haystack: Uint8Array, needle: number[]): number { outer: for (let i = 0; i + needle.length <= haystack.length; i++) { for (let j = 0; j < needle.length; j++) { if (haystack[i + j] !== needle[j]) continue outer; } return i; } return -1;}
describe("width", () => { it("measures non-printable codepoints as one replacement cell", async () => { let term = await createTerm({ width: 20, height: 4 });
// U+FDD0 is a noncharacter: wcwidth() reports it non-printable and the // renderer emits it as U+FFFD in a single cell, so measurement must // count it as one cell too or fit-sized boxes end up too narrow. let result = term.render([ open("root", { layout: { width: grow(), height: grow(), direction: "ttb" }, }), open("box"), text("ab"), close(), close(), ]);
expect(result.info.get("box")?.bounds.width).toBe(3); });
it("renders a noncharacter as a single replacement cell", async () => { let term = await createTerm({ width: 20, height: 4 });
let out = print( decode( term.render([ open("root", { layout: { width: grow(), height: grow(), direction: "ttb" }, }), text("ab"), close(), ]).output, ), 20, 4, );
expect(out).toContain("a�b"); });
it("replaces surrogate codepoints from malformed UTF-8 with U+FFFD", async () => { let native = await createTermNative(10, 3);
// TextEncoder never produces surrogate bytes, so pack a placeholder // (U+1234 = E1 88 B4) and patch it to CESU-8 U+D800 (ED A0 80) to // reach the decoder the way a malformed C-side buffer would. let ops = [ open("root", { layout: { width: grow(), height: grow(), direction: "ttb" }, }), text("aሴb"), close(), ]; let len = pack( ops, native.memory.buffer, native.opsBuf, native.memory.buffer.byteLength, ); // pack() returns a length in 32-bit words let buf = new Uint8Array(native.memory.buffer, native.opsBuf, len * 4); let at = indexOfSeq(buf, [0xe1, 0x88, 0xb4]); expect(at).toBeGreaterThan(-1); buf[at] = 0xed; buf[at + 1] = 0xa0; buf[at + 2] = 0x80;
native.reduce(native.statePtr, native.opsBuf, len, 0, 1, 0); let out = new Uint8Array( native.memory.buffer, native.output(native.statePtr), native.length(native.statePtr), );
// the invalid sequence must never reach the terminal expect(indexOfSeq(out, [0xed, 0xa0, 0x80])).toBe(-1); expect(print(decode(out), 10, 3)).toContain("a�b"); });});