Skip to content

Commit 8112fbf

Browse files
authored
Merge pull request #5598 from Tyriar/5588
Fix exception on resize during write
2 parents 9bd128b + 16a13f8 commit 8112fbf

3 files changed

Lines changed: 28 additions & 2 deletions

File tree

src/common/InputHandler.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,13 @@ export class InputHandler extends Disposable implements IInputHandler {
521521
const wraparoundMode = this._coreService.decPrivateModes.wraparound;
522522
const insertMode = this._coreService.modes.insertMode;
523523
const curAttr = this._curAttrData;
524-
let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
524+
let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
525+
526+
// Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async
527+
// scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597
528+
if (!bufferRow) {
529+
return;
530+
}
525531

526532
this._dirtyRowTracker.markDirty(this._activeBuffer.y);
527533

@@ -586,7 +592,10 @@ export class InputHandler extends Disposable implements IInputHandler {
586592
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true;
587593
}
588594
// row changed, get it again
589-
bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
595+
bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
596+
if (!bufferRow) {
597+
return;
598+
}
590599
if (oldWidth > 0 && bufferRow instanceof BufferLine) {
591600
// Combining character widens 1 column to 2.
592601
// Move old character to next line.

src/common/buffer/Buffer.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,13 @@ export class Buffer implements IBuffer {
264264
this._cols = newCols;
265265
this._rows = newRows;
266266

267+
// Ensure the cursor position invariant: ybase + y must be within buffer bounds
268+
// This can be violated during reflow or when shrinking rows
269+
if (this.lines.length > 0) {
270+
const maxY = Math.max(0, this.lines.length - this.ybase - 1);
271+
this.y = Math.min(this.y, maxY);
272+
}
273+
267274
this._memoryCleanupQueue.clear();
268275
// schedule memory cleanup only, if more than 10% of the lines are affected
269276
if (dirtyMemoryLines > 0.1 * this.lines.length) {

test/playwright/Terminal.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,16 @@ test.describe('API Integration Tests', () => {
457457
await pollFor(ctx.page, `window.calls`, [[10, 5], [20, 15]]);
458458
});
459459

460+
test('resize during write should not throw', async () => {
461+
await openTerminal(ctx, { rows: 50, cols: 80 });
462+
const largeData = 'x'.repeat(10000);
463+
await ctx.proxy.write(largeData);
464+
await ctx.proxy.resize(80, 10);
465+
await ctx.proxy.write(largeData);
466+
await ctx.proxy.resize(80, 5);
467+
await ctx.proxy.write(largeData);
468+
});
469+
460470
test('onTitleChange', async () => {
461471
await openTerminal(ctx);
462472
await ctx.page.evaluate(`

0 commit comments

Comments
 (0)