Fix exception on resize during write

Fixes #5588
This commit is contained in:
Daniel Imms
2026-01-08 18:03:42 -08:00
parent 729a670043
commit 16a13f8504
3 changed files with 28 additions and 2 deletions
+11 -2
View File
@@ -521,7 +521,13 @@ export class InputHandler extends Disposable implements IInputHandler {
const wraparoundMode = this._coreService.decPrivateModes.wraparound;
const insertMode = this._coreService.modes.insertMode;
const curAttr = this._curAttrData;
let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
// Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async
// scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597
if (!bufferRow) {
return;
}
this._dirtyRowTracker.markDirty(this._activeBuffer.y);
@@ -586,7 +592,10 @@ export class InputHandler extends Disposable implements IInputHandler {
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true;
}
// row changed, get it again
bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
if (!bufferRow) {
return;
}
if (oldWidth > 0 && bufferRow instanceof BufferLine) {
// Combining character widens 1 column to 2.
// Move old character to next line.
+7
View File
@@ -264,6 +264,13 @@ export class Buffer implements IBuffer {
this._cols = newCols;
this._rows = newRows;
// Ensure the cursor position invariant: ybase + y must be within buffer bounds
// This can be violated during reflow or when shrinking rows
if (this.lines.length > 0) {
const maxY = Math.max(0, this.lines.length - this.ybase - 1);
this.y = Math.min(this.y, maxY);
}
this._memoryCleanupQueue.clear();
// schedule memory cleanup only, if more than 10% of the lines are affected
if (dirtyMemoryLines > 0.1 * this.lines.length) {
+10
View File
@@ -402,6 +402,16 @@ test.describe('API Integration Tests', () => {
await pollFor(ctx.page, `window.calls`, [[10, 5], [20, 15]]);
});
test('resize during write should not throw', async () => {
await openTerminal(ctx, { rows: 50, cols: 80 });
const largeData = 'x'.repeat(10000);
await ctx.proxy.write(largeData);
await ctx.proxy.resize(80, 10);
await ctx.proxy.write(largeData);
await ctx.proxy.resize(80, 5);
await ctx.proxy.write(largeData);
});
test('onTitleChange', async () => {
await openTerminal(ctx);
await ctx.page.evaluate(`