mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(process): preserve terminal cell widths during normalization (#4142)
This commit is contained in:
1 parent
02a0e72f70
commit
1ba178befb
3 files changed
+58
-5
No files matched your search
@@ -100,6 +100,27 @@ it("trims only after normalization sees the full-width row", async () => {
|
||||
expect(frames[0]?.lines[0]).toBe("pid <pid>");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ data: "界", cells: 2 },
|
||||
// The pinned xterm Unicode table treats this emoji as one cell.
|
||||
{ data: "😀", cells: 1 },
|
||||
{ data: "e\u0301", cells: 1 },
|
||||
{ data: "ab", cells: 2 },
|
||||
])(
|
||||
"normalizes the terminal cells of $data without extra padding",
|
||||
async ({ data, cells }) => {
|
||||
const { frames } = await renderFrames(data, (value) =>
|
||||
value.replaceAll(" ", "_"),
|
||||
);
|
||||
expect(frames[0]?.lines).toEqual([
|
||||
data + "_".repeat(20 - cells),
|
||||
"_".repeat(20),
|
||||
"_".repeat(20),
|
||||
"_".repeat(20),
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
it("omits rendered frames with unknown expanded size and keeps later bounded states", async () => {
|
||||
const renderer = new TerminalRenderer({
|
||||
columns: 10,
|
||||
|
||||
@@ -131,14 +131,14 @@ export class TerminalRenderer {
|
||||
let bytes = 0;
|
||||
for (let row = 0; row < this.#terminal.rows; row += 1) {
|
||||
const line = buffer.getLine(buffer.viewportY + row);
|
||||
const paddedLine = (
|
||||
line?.translateToString(false, 0, this.#terminal.cols) ?? ""
|
||||
).padEnd(this.#terminal.cols, " ");
|
||||
const renderedLine =
|
||||
line?.translateToString(false, 0, this.#terminal.cols) ??
|
||||
" ".repeat(this.#terminal.cols);
|
||||
const trailingSpaceBytes = Buffer.byteLength(
|
||||
paddedLine.match(TRAILING_SPACES)?.[0] ?? "",
|
||||
renderedLine.match(TRAILING_SPACES)?.[0] ?? "",
|
||||
);
|
||||
const normalized = this.options.normalize(
|
||||
paddedLine,
|
||||
renderedLine,
|
||||
Math.min(Number.MAX_SAFE_INTEGER, remainingBytes + trailingSpaceBytes),
|
||||
);
|
||||
if (normalized.state === "over_budget") {
|
||||
|
||||
@@ -7,6 +7,38 @@ import { itWithCaptureCapability } from "./processCaptureCapability.js";
|
||||
import { captureProcessScenario } from "../../../src/process/capture/ProcessHarness.js";
|
||||
import { parseProcessScenario } from "../../../src/domain/process/processScenario.js";
|
||||
|
||||
itWithCaptureCapability(
|
||||
"normalizes wide terminal cells without inventing blank cells",
|
||||
async () => {
|
||||
const result = await captureProcessScenario(
|
||||
parseProcessScenario({
|
||||
executable: process.execPath,
|
||||
arguments: ["-e", 'process.stdout.write("界")'],
|
||||
terminal: { columns: 4, rows: 1, scrollback: 0 },
|
||||
normalization: {
|
||||
patterns: [{ pattern: " ", replacement: "_" }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
if (!result.ok) throw result.error;
|
||||
expect(
|
||||
result.value.frames
|
||||
.map(({ raw_data, data }) => raw_data ?? data)
|
||||
.join(""),
|
||||
).toBe("界");
|
||||
expect(result.value.rendered_frames.at(-1)).toMatchObject({
|
||||
columns: 4,
|
||||
cursor_x: 2,
|
||||
lines: ["界__"],
|
||||
});
|
||||
expect(result.value.cleanup).toMatchObject({
|
||||
owned_process_group: "verified",
|
||||
temporary_root: "removed",
|
||||
});
|
||||
},
|
||||
20_000,
|
||||
);
|
||||
|
||||
itWithCaptureCapability(
|
||||
"captures one command's terminal output, selected files, and owned cleanup",
|
||||
async () => {
|
||||
|
||||
Reference in new issue
Block a user