Files
oh-my-pi/packages/tui/test/line-width-sidecar.test.ts
metaphorics 92380971da perf(tui): carry measured widths through rendering
Carry exact visible widths from Text and Box render results into their owners, and through Editor wrap/layout/render state. Stamp derived widths and render caches with a monotonic Hangul width-config epoch. Retain sidecar entries only by WeakMap owner identity, with immutable publication-time line/width proof, and conservatively remeasure escape-leading padded rows.

Before:
  static_redraw 1633.11 us/op
  editor_edits  1249.71 us/op
After hardened repair:
  static_redraw 1321.51 us/op (1.236x)
  editor_edits  1150.32 us/op (1.086x)
Win:
  Exact byte/grid/scrollback hashes match across all five phases; every phase CV is below 20%, with no B/C regression.
Memory:
  retained heap 251,393,541 -> 251,500,114 bytes (+0.042%).

Op: carry exact widths through render ownership and editor layout
Restores: repeated visible-width measurement in core TUI hot paths
2026-07-18 10:50:39 +09:00

87 lines
3.0 KiB
TypeScript

/**
* Carried-width contract: components may publish the exact `visibleWidth` of
* each line of a render result, keyed by the result array itself. Consumers
* (Box) must only ever observe widths that (a) match a direct measurement and
* (b) were computed under the current width configuration — a Hangul
* Compatibility Jamo width change must invalidate every published width.
*/
import { afterEach, describe, expect, it } from "bun:test";
import {
getPublishedLineWidths,
getWidthConfigEpoch,
publishLineWidths,
resetHangulCompatibilityJamoWidthForTests,
setHangulCompatibilityJamoWidth,
visibleWidth,
} from "@oh-my-pi/pi-tui/utils";
afterEach(() => {
resetHangulCompatibilityJamoWidthForTests();
});
describe("line-width sidecar", () => {
it("returns published widths for the same array reference only", () => {
const lines = ["abc", "漢字"];
publishLineWidths(lines, [3, 4]);
expect(getPublishedLineWidths(lines)).toEqual([3, 4]);
// A value-equal but distinct array has no published widths.
expect(getPublishedLineWidths(["abc", "漢字"])).toBeUndefined();
});
it("rejects a publication whose widths do not match the line count", () => {
expect(() => publishLineWidths(["one", "two"], [3])).toThrow(RangeError);
});
it("keeps publication proof immutable from publishers and consumers", () => {
const lines = ["hi"];
const widths = [2];
publishLineWidths(lines, widths);
widths[0] = 99;
const published = getPublishedLineWidths(lines);
expect(published).toEqual([2]);
expect(Object.isFrozen(published)).toBe(true);
expect(Reflect.set(published ?? [], "0", 7)).toBe(false);
expect(getPublishedLineWidths(lines)).toEqual([2]);
});
it("drops published widths after same-array content or length mutation", () => {
const lines = ["hi"];
publishLineWidths(lines, [2]);
lines[0] = "hello";
expect(getPublishedLineWidths(lines)).toBeUndefined();
publishLineWidths(lines, [5]);
lines.push("!");
expect(getPublishedLineWidths(lines)).toBeUndefined();
});
it("drops published widths when the Hangul jamo width setting changes", () => {
const jamo = "\u3131\u314F";
const lines = [jamo];
setHangulCompatibilityJamoWidth(1);
publishLineWidths(lines, [visibleWidth(jamo)]);
expect(getPublishedLineWidths(lines)).toEqual([visibleWidth(jamo)]);
// Widths measured under the old setting must not survive the switch:
// the same string now measures differently.
setHangulCompatibilityJamoWidth(2);
expect(getPublishedLineWidths(lines)).toBeUndefined();
// Republishing under the new setting is visible again and exact.
publishLineWidths(lines, [visibleWidth(jamo)]);
expect(getPublishedLineWidths(lines)).toEqual([4]);
});
it("bumps the width-config epoch only on an effective setting change", () => {
const before = getWidthConfigEpoch();
setHangulCompatibilityJamoWidth(1);
const afterFirst = getWidthConfigEpoch();
expect(afterFirst).toBeGreaterThan(before);
// No-op set: same value, no invalidation.
setHangulCompatibilityJamoWidth(1);
expect(getWidthConfigEpoch()).toBe(afterFirst);
});
});