fix(tui): normalize tabs for terminal output (#6697)
This commit is contained in:
@@ -1,6 +1,37 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, it } from "node:test";
|
||||
import { extractSegments, sliceWithWidth, visibleWidth } from "../src/utils.ts";
|
||||
import { type Component, TUI } from "../src/tui.ts";
|
||||
import { extractSegments, normalizeTerminalOutput, sliceWithWidth, visibleWidth } from "../src/utils.ts";
|
||||
import { VirtualTerminal } from "./virtual-terminal.ts";
|
||||
|
||||
class FullViewportContent implements Component {
|
||||
render(width: number): string[] {
|
||||
return ["base 0", "base 1", "base 2"].map((line) => line.padEnd(width));
|
||||
}
|
||||
|
||||
invalidate(): void {}
|
||||
}
|
||||
|
||||
class CapturingVirtualTerminal extends VirtualTerminal {
|
||||
private output = "";
|
||||
|
||||
override write(data: string): void {
|
||||
this.output += data;
|
||||
super.write(data);
|
||||
}
|
||||
|
||||
getOutput(): string {
|
||||
return this.output;
|
||||
}
|
||||
}
|
||||
|
||||
class TabStatusOverlay implements Component {
|
||||
render(): string[] {
|
||||
return ["\tX"];
|
||||
}
|
||||
|
||||
invalidate(): void {}
|
||||
}
|
||||
|
||||
describe("tab width accounting", () => {
|
||||
it("keeps slice helper widths consistent with visible width", () => {
|
||||
@@ -25,4 +56,32 @@ describe("tab width accounting", () => {
|
||||
assert.strictEqual(tabFits.beforeWidth, 11);
|
||||
assert.strictEqual(visibleWidth(tabFits.before), tabFits.beforeWidth);
|
||||
});
|
||||
|
||||
it("keeps tabs inside terminal control sequences byte-identical", () => {
|
||||
const controlSequences = [
|
||||
"\x1b]8;;https://example.test/a\tb\x07",
|
||||
"\x1b]0;window\ttitle\x1b\\",
|
||||
"\x1b_payload\tdata\x1b\\",
|
||||
];
|
||||
|
||||
for (const controlSequence of controlSequences) {
|
||||
assert.strictEqual(normalizeTerminalOutput(`${controlSequence}label\ttext`), `${controlSequence}label text`);
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps tab-containing overlays on one physical terminal row", async () => {
|
||||
const terminal = new CapturingVirtualTerminal(16, 3);
|
||||
const tui = new TUI(terminal);
|
||||
tui.addChild(new FullViewportContent());
|
||||
tui.showOverlay(new TabStatusOverlay(), { width: 4, row: 1, col: 4 });
|
||||
tui.start();
|
||||
|
||||
try {
|
||||
await terminal.waitForRender();
|
||||
assert.deepStrictEqual(terminal.getViewport(), ["base 0 ", "base X ", "base 2 "]);
|
||||
assert.ok(!terminal.getOutput().includes("\t"));
|
||||
} finally {
|
||||
tui.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user