From c3aa63cffad50746f531d571cc3e994c7754fe59 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Tue, 5 Sep 2023 13:54:22 +1000 Subject: [PATCH] translateToString() adds one more entry to outColumns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, for string 'ab好', outColumns=[0, 1, 2]. Now it becomes [0, 1, 2, 4]. This allows the user to know where does the last character ends. --- src/browser/AccessibilityManager.ts | 2 +- src/common/buffer/BufferLine.test.ts | 60 ++++++++++++++-------------- src/common/buffer/BufferLine.ts | 14 +++++-- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index c36bae12d9..1b964d1bbe 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -191,7 +191,7 @@ export class AccessibilityManager extends Disposable { if (element) { if (lineData.length === 0) { element.innerText = '\u00a0'; - this._rowColumns.set(element, [0]); + this._rowColumns.set(element, [0, 1]); } else { element.textContent = lineData; this._rowColumns.set(element, columns); diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index ed8df0f940..8461f57153 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -333,9 +333,9 @@ describe('BufferLine', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); const columns: number[] = []; assert.equal(line.translateToString(false, undefined, undefined, columns), ' '); - assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); assert.equal(line.translateToString(true, undefined, undefined, columns), ''); - assert.deepEqual(columns, []); + assert.deepEqual(columns, [0]); }); it('ASCII', function(): void { const columns: number[] = []; @@ -345,16 +345,16 @@ describe('BufferLine', function(): void { line.setCell(4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(5, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); assert.equal(line.translateToString(false, undefined, undefined, columns), 'a a aa '); - assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); assert.equal(line.translateToString(true, undefined, undefined, columns), 'a a aa'); - assert.deepEqual(columns, [0, 1, 2, 3, 4, 5]); + assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6]); for (const trimRight of [true, false]) { assert.equal(line.translateToString(trimRight, 0, 5, columns), 'a a a'); - assert.deepEqual(columns, [0, 1, 2, 3, 4]); + assert.deepEqual(columns, [0, 1, 2, 3, 4, 5]); assert.equal(line.translateToString(trimRight, 0, 4, columns), 'a a '); - assert.deepEqual(columns, [0, 1, 2, 3]); + assert.deepEqual(columns, [0, 1, 2, 3, 4]); assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a a'); - assert.deepEqual(columns, [0, 1, 2]); + assert.deepEqual(columns, [0, 1, 2, 3]); } }); @@ -366,16 +366,16 @@ describe('BufferLine', function(): void { line.setCell(4, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)])); line.setCell(5, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)])); assert.equal(line.translateToString(false, undefined, undefined, columns), 'a 𝄞 𝄞𝄞 '); - assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 8, 9]); + assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10]); assert.equal(line.translateToString(true, undefined, undefined, columns), 'a 𝄞 𝄞𝄞'); - assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5]); + assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6]); for (const trimRight of [true, false]) { assert.equal(line.translateToString(trimRight, 0, 5, columns), 'a 𝄞 𝄞'); - assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4]); + assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5]); assert.equal(line.translateToString(trimRight, 0, 4, columns), 'a 𝄞 '); - assert.deepEqual(columns, [0, 1, 2, 2, 3]); + assert.deepEqual(columns, [0, 1, 2, 2, 3, 4]); assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a 𝄞'); - assert.deepEqual(columns, [0, 1, 2, 2]); + assert.deepEqual(columns, [0, 1, 2, 2, 3]); } }); it('combining', function(): void { @@ -386,16 +386,16 @@ describe('BufferLine', function(): void { line.setCell(4, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)])); line.setCell(5, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)])); assert.equal(line.translateToString(false, undefined, undefined, columns), 'a e\u0301 e\u0301e\u0301 '); - assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 8, 9]); + assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10]); assert.equal(line.translateToString(true, undefined, undefined, columns), 'a e\u0301 e\u0301e\u0301'); - assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5]); + assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6]); for (const trimRight of [true, false]) { assert.equal(line.translateToString(trimRight, 0, 5, columns), 'a e\u0301 e\u0301'); - assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4]); + assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5]); assert.equal(line.translateToString(trimRight, 0, 4, columns), 'a e\u0301 '); - assert.deepEqual(columns, [0, 1, 2, 2, 3]); + assert.deepEqual(columns, [0, 1, 2, 2, 3, 4]); assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a e\u0301'); - assert.deepEqual(columns, [0, 1, 2, 2]); + assert.deepEqual(columns, [0, 1, 2, 2, 3]); } }); it('fullwidth', function(): void { @@ -409,22 +409,22 @@ describe('BufferLine', function(): void { line.setCell(7, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)])); line.setCell(8, CellData.fromCharData([0, '', 0, 0])); assert.equal(line.translateToString(false, undefined, undefined, columns), 'a 1 11 '); - assert.deepEqual(columns, [0, 1, 2, 4, 5, 7, 9]); + assert.deepEqual(columns, [0, 1, 2, 4, 5, 7, 9, 10]); assert.equal(line.translateToString(true, undefined, undefined, columns), 'a 1 11'); - assert.deepEqual(columns, [0, 1, 2, 4, 5, 7]); + assert.deepEqual(columns, [0, 1, 2, 4, 5, 7, 9]); for (const trimRight of [true, false]) { assert.equal(line.translateToString(trimRight, 0, 7, columns), 'a 1 1'); - assert.deepEqual(columns, [0, 1, 2, 4, 5]); + assert.deepEqual(columns, [0, 1, 2, 4, 5, 7]); assert.equal(line.translateToString(trimRight, 0, 6, columns), 'a 1 1'); - assert.deepEqual(columns, [0, 1, 2, 4, 5]); + assert.deepEqual(columns, [0, 1, 2, 4, 5, 7]); assert.equal(line.translateToString(trimRight, 0, 5, columns), 'a 1 '); - assert.deepEqual(columns, [0, 1, 2, 4]); + assert.deepEqual(columns, [0, 1, 2, 4, 5]); assert.equal(line.translateToString(trimRight, 0, 4, columns), 'a 1'); - assert.deepEqual(columns, [0, 1, 2]); + assert.deepEqual(columns, [0, 1, 2, 4]); assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a 1'); - assert.deepEqual(columns, [0, 1, 2]); + assert.deepEqual(columns, [0, 1, 2, 4]); assert.equal(line.translateToString(trimRight, 0, 2, columns), 'a '); - assert.deepEqual(columns, [0, 1]); + assert.deepEqual(columns, [0, 1, 2]); } }); it('space at end', function(): void { @@ -436,9 +436,9 @@ describe('BufferLine', function(): void { line.setCell(5, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(6, CellData.fromCharData([1, ' ', 1, ' '.charCodeAt(0)])); assert.equal(line.translateToString(false, undefined, undefined, columns), 'a a aa '); - assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); assert.equal(line.translateToString(true, undefined, undefined, columns), 'a a aa '); - assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6]); + assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7]); }); it('should always return some sane value', function(): void { const columns: number[] = []; @@ -447,16 +447,16 @@ describe('BufferLine', function(): void { // fullwidth pairs --> needs to be fixed after settling BufferLine impl const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false); assert.equal(line.translateToString(false, undefined, undefined, columns), ' '); - assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); assert.equal(line.translateToString(true, undefined, undefined, columns), ''); - assert.deepEqual(columns, []); + assert.deepEqual(columns, [0]); }); it('should work with endCol=0', () => { const columns: number[] = []; const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false); line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); assert.equal(line.translateToString(true, 0, 0, columns), ''); - assert.deepEqual(columns, []); + assert.deepEqual(columns, [0]); }); }); describe('addCharToCell', () => { diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 432319258b..e078d5ed90 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -510,10 +510,13 @@ export class BufferLine implements IBufferLine { /** * If outColumns is specified, it will be filled with column numbers such that - * returnedString[i] is display at outColumns[i] column. When a single cell is - * translated to multiple UTF-16 code units (e.g. surrogate pair) in the - * returned string, the corresponding entries in outColumns will have the same - * column number. + * returnedString[i] is display at outColumns[i] column. + * outColumns[returnedString.length] is where the character following + * returnedString will be displayed. + * + * When a single cell is translated to multiple UTF-16 code units (e.g. + * surrogate pair) in the returned string, the corresponding entries in + * outColumns will have the same column number. */ public translateToString(trimRight?: boolean, startCol?: number, endCol?: number, outColumns?: number[]): string { startCol = startCol ?? 0; @@ -537,6 +540,9 @@ export class BufferLine implements IBufferLine { } startCol += (content >> Content.WIDTH_SHIFT) || 1; // always advance by at least 1 } + if (outColumns) { + outColumns.push(startCol); + } return result; } }