Allow selection in the a11y tree and sync the selection to terminal

This commit is contained in:
Jason Lin
2023-10-11 16:23:03 +11:00
parent 34017f32fb
commit 1b940de0bf
5 changed files with 239 additions and 53 deletions
+76 -45
View File
@@ -331,56 +331,75 @@ describe('BufferLine', function(): void {
describe('translateToString with and w\'o trimming', function(): void {
it('empty line', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
assert.equal(line.translateToString(false), ' ');
assert.equal(line.translateToString(true), '');
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.equal(line.translateToString(true, undefined, undefined, columns), '');
assert.deepEqual(columns, []);
});
it('ASCII', function(): void {
const columns: number[] = [];
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
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), 'a a aa ');
assert.equal(line.translateToString(true), 'a a aa');
assert.equal(line.translateToString(false, 0, 5), 'a a a');
assert.equal(line.translateToString(false, 0, 4), 'a a ');
assert.equal(line.translateToString(false, 0, 3), 'a a');
assert.equal(line.translateToString(true, 0, 5), 'a a a');
assert.equal(line.translateToString(true, 0, 4), 'a a ');
assert.equal(line.translateToString(true, 0, 3), 'a a');
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.equal(line.translateToString(true, undefined, undefined, columns), 'a a aa');
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5]);
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.equal(line.translateToString(trimRight, 0, 4, columns), 'a a ');
assert.deepEqual(columns, [0, 1, 2, 3]);
assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a a');
assert.deepEqual(columns, [0, 1, 2]);
}
});
it('surrogate', function(): void {
const columns: number[] = [];
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
line.setCell(5, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
assert.equal(line.translateToString(false), 'a 𝄞 𝄞𝄞 ');
assert.equal(line.translateToString(true), 'a 𝄞 𝄞𝄞');
assert.equal(line.translateToString(false, 0, 5), 'a 𝄞 𝄞');
assert.equal(line.translateToString(false, 0, 4), 'a 𝄞 ');
assert.equal(line.translateToString(false, 0, 3), 'a 𝄞');
assert.equal(line.translateToString(true, 0, 5), 'a 𝄞 𝄞');
assert.equal(line.translateToString(true, 0, 4), 'a 𝄞 ');
assert.equal(line.translateToString(true, 0, 3), 'a 𝄞');
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.equal(line.translateToString(true, undefined, undefined, columns), 'a 𝄞 𝄞𝄞');
assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5]);
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.equal(line.translateToString(trimRight, 0, 4, columns), 'a 𝄞 ');
assert.deepEqual(columns, [0, 1, 2, 2, 3]);
assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a 𝄞');
assert.deepEqual(columns, [0, 1, 2, 2]);
}
});
it('combining', function(): void {
const columns: number[] = [];
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
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), 'a e\u0301 e\u0301e\u0301 ');
assert.equal(line.translateToString(true), 'a e\u0301 e\u0301e\u0301');
assert.equal(line.translateToString(false, 0, 5), 'a e\u0301 e\u0301');
assert.equal(line.translateToString(false, 0, 4), 'a e\u0301 ');
assert.equal(line.translateToString(false, 0, 3), 'a e\u0301');
assert.equal(line.translateToString(true, 0, 5), 'a e\u0301 e\u0301');
assert.equal(line.translateToString(true, 0, 4), 'a e\u0301 ');
assert.equal(line.translateToString(true, 0, 3), 'a e\u0301');
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.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]);
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.equal(line.translateToString(trimRight, 0, 4, columns), 'a e\u0301 ');
assert.deepEqual(columns, [0, 1, 2, 2, 3]);
assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a e\u0301');
assert.deepEqual(columns, [0, 1, 2, 2]);
}
});
it('fullwidth', function(): void {
const columns: number[] = [];
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
@@ -389,43 +408,55 @@ describe('BufferLine', function(): void {
line.setCell(6, CellData.fromCharData([0, '', 0, 0]));
line.setCell(7, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
line.setCell(8, CellData.fromCharData([0, '', 0, 0]));
assert.equal(line.translateToString(false), 'a 1 11 ');
assert.equal(line.translateToString(true), 'a 1 11');
assert.equal(line.translateToString(false, 0, 7), 'a 1 1');
assert.equal(line.translateToString(false, 0, 6), 'a 1 1');
assert.equal(line.translateToString(false, 0, 5), 'a 1 ');
assert.equal(line.translateToString(false, 0, 4), 'a 1');
assert.equal(line.translateToString(false, 0, 3), 'a 1');
assert.equal(line.translateToString(false, 0, 2), 'a ');
assert.equal(line.translateToString(true, 0, 7), 'a 1 1');
assert.equal(line.translateToString(true, 0, 6), 'a 1 1');
assert.equal(line.translateToString(true, 0, 5), 'a 1 ');
assert.equal(line.translateToString(true, 0, 4), 'a 1');
assert.equal(line.translateToString(true, 0, 3), 'a 1');
assert.equal(line.translateToString(true, 0, 2), 'a ');
assert.equal(line.translateToString(false, undefined, undefined, columns), 'a 1 11 ');
assert.deepEqual(columns, [0, 1, 2, 4, 5, 7, 9]);
assert.equal(line.translateToString(true, undefined, undefined, columns), 'a 1 11');
assert.deepEqual(columns, [0, 1, 2, 4, 5, 7]);
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.equal(line.translateToString(trimRight, 0, 6, columns), 'a 1 1');
assert.deepEqual(columns, [0, 1, 2, 4, 5]);
assert.equal(line.translateToString(trimRight, 0, 5, columns), 'a 1 ');
assert.deepEqual(columns, [0, 1, 2, 4]);
assert.equal(line.translateToString(trimRight, 0, 4, columns), 'a 1');
assert.deepEqual(columns, [0, 1, 2]);
assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a 1');
assert.deepEqual(columns, [0, 1, 2]);
assert.equal(line.translateToString(trimRight, 0, 2, columns), 'a ');
assert.deepEqual(columns, [0, 1]);
}
});
it('space at end', function(): void {
const columns: number[] = [];
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
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), 'a a aa ');
assert.equal(line.translateToString(true), 'a a aa ');
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.equal(line.translateToString(true, undefined, undefined, columns), 'a a aa ');
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6]);
});
it('should always return some sane value', function(): void {
const columns: number[] = [];
// sanity check - broken line with invalid out of bound null width cells
// this can atm happen with deleting/inserting chars in inputhandler by "breaking"
// 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), ' ');
assert.equal(line.translateToString(true), '');
assert.equal(line.translateToString(false, undefined, undefined, columns), ' ');
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert.equal(line.translateToString(true, undefined, undefined, columns), '');
assert.deepEqual(columns, []);
});
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), '');
assert.equal(line.translateToString(true, 0, 0, columns), '');
assert.deepEqual(columns, []);
});
});
describe('addCharToCell', () => {
+21 -3
View File
@@ -508,16 +508,34 @@ export class BufferLine implements IBufferLine {
}
}
public translateToString(trimRight: boolean = false, startCol: number = 0, endCol: number = this.length): string {
/**
* 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.
*/
public translateToString(trimRight?: boolean, startCol?: number, endCol?: number, outColumns?: number[]): string {
startCol = startCol ?? 0;
endCol = endCol ?? this.length;
if (trimRight) {
endCol = Math.min(endCol, this.getTrimmedLength());
}
if (outColumns) {
outColumns.length = 0;
}
let result = '';
while (startCol < endCol) {
const content = this._data[startCol * CELL_SIZE + Cell.CONTENT];
const cp = content & Content.CODEPOINT_MASK;
result += (content & Content.IS_COMBINED_MASK) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR;
startCol += (content >> Content.WIDTH_SHIFT) || 1; // always advance by 1
const chars = (content & Content.IS_COMBINED_MASK) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR;
result += chars;
if (outColumns) {
for (let i = 0; i < chars.length; ++i) {
outColumns.push(startCol);
}
}
startCol += (content >> Content.WIDTH_SHIFT) || 1; // always advance by at least 1
}
return result;
}