mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
fix(predictive-echo): anchor hold after unpredicted wire edits (review findings)
Independent post-build review found three gaps, all one family: input that changes the composer without a prediction leaves the DISPLAYED cursor stale for one RTT, and anchoring a new run on it painted ghosts one cell off (blank-neutral, so they lived out the full TTL: "tehh" on backspace-then-retype, exactly on the links the feature targets). Fix: the addon now HOLDS new predictions after any such edit (backspace with nothing outstanding = deleting echoed text, clearPredictions, and now also IME/plain-paste 'text' commits, which the hook clears like 'clear') until the next PARSED write releases the hold. The inline predictChar reconcile deliberately does not count: only the emitter pass or the public reconcile() is the display-caught-up contract. Worst case is exactly one unpredicted keystroke, whose own echo releases the hold. Also patched the one bypass path the PR had missed: _handleCjkInput now clears predictions like insertTerminalText and the other bypass sends. Package suite 230, vm gating 85, E2E 10/10 all green after the change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -335,6 +335,7 @@ describe('PredictiveEchoAddon', () => {
|
||||
|
||||
it('predictBackspace pops newest, returns false when empty, never touches confirmed', async () => {
|
||||
expect(addon.predictBackspace()).toBe(false);
|
||||
addon.reconcile(); // the empty pop armed the anchor hold; release it
|
||||
addon.predictChar('a');
|
||||
addon.predictChar('b');
|
||||
expect(addon.predictBackspace()).toBe(true);
|
||||
@@ -478,6 +479,7 @@ describe('PredictiveEchoAddon', () => {
|
||||
rows.style.color = 'rgb(255, 0, 0)'; // skin change
|
||||
a.refreshFont();
|
||||
a.clearPredictions();
|
||||
a.reconcile(); // release the anchor hold armed by the clear
|
||||
a.predictChar('v');
|
||||
const span2 = themed.terminal.element.querySelector('.xterm-screen span') as HTMLSpanElement;
|
||||
expect(span2.style.color).toBe('rgb(255, 0, 0)');
|
||||
@@ -485,6 +487,37 @@ describe('PredictiveEchoAddon', () => {
|
||||
themed.cleanup();
|
||||
});
|
||||
|
||||
it('anchor hold: backspace into echoed text suppresses prediction until a write parses', async () => {
|
||||
// \x7f went to the wire with nothing outstanding: the cursor will move
|
||||
// in a way the display has not shown, so anchoring now paints one cell
|
||||
// off (review finding: "tehh" ghosts on backspace-then-retype at RTT)
|
||||
expect(addon.predictBackspace()).toBe(false);
|
||||
expect(addon.predictChar('x')).toBe(false);
|
||||
expect(spansOf(mock)).toHaveLength(0);
|
||||
mock.fireWriteParsed(); // the display caught up
|
||||
await flushMicrotasks();
|
||||
expect(addon.predictChar('x')).toBe(true);
|
||||
});
|
||||
|
||||
it('anchor hold: clearPredictions suppresses until a write parses (or manual reconcile)', async () => {
|
||||
addon.predictChar('a');
|
||||
addon.clearPredictions(); // consumer saw Enter/Esc/arrow/paste
|
||||
expect(addon.predictChar('b')).toBe(false);
|
||||
mock.fireWriteParsed();
|
||||
await flushMicrotasks();
|
||||
expect(addon.predictChar('b')).toBe(true);
|
||||
});
|
||||
|
||||
it('anchor hold: the inline predictChar reconcile does NOT release it', () => {
|
||||
addon.clearPredictions();
|
||||
// Several keystrokes in a row before any echo: all suppressed, because
|
||||
// predictChar's inline pass must not count as the display catching up
|
||||
expect(addon.predictChar('a')).toBe(false);
|
||||
expect(addon.predictChar('b')).toBe(false);
|
||||
addon.reconcile(); // public/manual pass IS the caught-up contract
|
||||
expect(addon.predictChar('c')).toBe(true);
|
||||
});
|
||||
|
||||
it('state getter reports outstanding/confirmedTotal/droppedTotal/anchor', async () => {
|
||||
expect(addon.state).toEqual({ outstanding: 0, confirmedTotal: 0, droppedTotal: 0, anchor: null });
|
||||
addon.predictChar('a');
|
||||
|
||||
Reference in New Issue
Block a user