fix(BufferLine): limit copyCellsFrom metadata to copied range

copyCellsFrom copied Uint32 cell data only for the requested column
range but migrated every combined entry with key >= srcCol, which could
attach grapheme metadata to unrelated destination columns during
partial line copies in reflow and wraparound.

Copy combined and extended attributes only per copied cell and clear
stale entries on the destination when the source cell does not use
them.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Daniel Imms
2026-06-03 03:36:39 +00:00
committed by Cursor Agent
co-authored by Cursor
parent ea75b99e2b
commit 60bae9528d
2 changed files with 19 additions and 0 deletions
+15
View File
@@ -495,6 +495,21 @@ describe('BufferLine', function(): void {
assert.deepEqual(columns, [0]);
});
});
describe('copyCellsFrom', () => {
it('should only move combined data within the copied range', () => {
const src = new TestBufferLine(10, NULL_CELL_DATA, false);
src.setCell(1, createCellData(1, 'a', 1));
src.addCodepointToCell(1, '\u0301'.charCodeAt(0), 0);
src.setCell(5, createCellData(1, 'z', 1));
src.addCodepointToCell(5, '\u0301'.charCodeAt(0), 0);
const dest = new TestBufferLine(10, NULL_CELL_DATA, false);
dest.copyCellsFrom(src, 0, 0, 2, false);
assert.property(dest.combined, '1');
assert.notProperty(dest.combined, '5');
});
});
describe('addCharToCell', () => {
it('should set width to 1 for empty cell', () => {
const line = new TestBufferLine(3, NULL_CELL_DATA, false);
+4
View File
@@ -609,9 +609,13 @@ export class BufferLine implements IBufferLine {
const srcStart = srcCol * Constants.CELL_INDICIES;
if (src._data[srcStart + Cell.CONTENT] & Content.IS_COMBINED_MASK) {
this._combined[destCol] = src._combined[srcCol];
} else {
delete this._combined[destCol];
}
if (src._data[srcStart + Cell.BG] & BgFlags.HAS_EXTENDED) {
this._extendedAttrs[destCol] = src._extendedAttrs[srcCol];
} else {
delete this._extendedAttrs[destCol];
}
}