add wide char handling to bufferline primitives
This commit is contained in:
@@ -366,4 +366,94 @@ describe('BufferLine', function(): void {
|
|||||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('correct fullwidth handling', () => {
|
||||||
|
function populate(line: BufferLine): void {
|
||||||
|
const cell = CellData.fromCharData([1, '¥', 2, '¥'.charCodeAt(0)]);
|
||||||
|
for (let i = 0; i < line.length; i += 2) {
|
||||||
|
line.setCell(i, cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it('insert - wide char at pos', () => {
|
||||||
|
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.insertCells(9, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), '¥¥¥¥ a');
|
||||||
|
line.insertCells(8, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), '¥¥¥¥a ');
|
||||||
|
line.insertCells(1, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' a ¥¥¥a');
|
||||||
|
});
|
||||||
|
it('insert - wide char at end', () => {
|
||||||
|
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.insertCells(0, 3, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), 'aaa¥¥¥ ');
|
||||||
|
line.insertCells(4, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), 'aaa a ¥¥');
|
||||||
|
line.insertCells(4, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), 'aaa aa ¥ ');
|
||||||
|
});
|
||||||
|
it('delete', () => {
|
||||||
|
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.deleteCells(0, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' ¥¥¥¥a');
|
||||||
|
line.deleteCells(5, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' ¥¥¥aaa');
|
||||||
|
line.deleteCells(0, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' ¥¥aaaaa');
|
||||||
|
});
|
||||||
|
it('replace - start at 0', () => {
|
||||||
|
let line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(0, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), 'a ¥¥¥¥');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(0, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), 'aa¥¥¥¥');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(0, 3, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), 'aaa ¥¥¥');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(0, 8, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), 'aaaaaaaa¥');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(0, 9, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), 'aaaaaaaaa ');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(0, 10, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), 'aaaaaaaaaa');
|
||||||
|
});
|
||||||
|
it('replace - start at 1', () => {
|
||||||
|
let line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(1, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' a¥¥¥¥');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(1, 3, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' aa ¥¥¥');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(1, 4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' aaa¥¥¥');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(1, 8, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' aaaaaaa¥');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(1, 9, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' aaaaaaaa ');
|
||||||
|
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||||
|
populate(line);
|
||||||
|
line.replaceCells(1, 10, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
|
chai.assert.equal(line.translateToString(), ' aaaaaaaaa');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CharData, IBufferLine, ICellData } from 'common/Types';
|
import { CharData, IBufferLine, ICellData, IAttributeData } from 'common/Types';
|
||||||
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
||||||
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';
|
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from 'common/buffer/CellData';
|
||||||
@@ -228,8 +228,14 @@ export class BufferLine implements IBufferLine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertCells(pos: number, n: number, fillCellData: ICellData): void {
|
public insertCells(pos: number, n: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
|
||||||
pos %= this.length;
|
pos %= this.length;
|
||||||
|
|
||||||
|
// handle fullwidth at pos: reset cell one to the left if pos is second cell of a wide char
|
||||||
|
if (pos && this.getWidth(pos - 1) === 2) {
|
||||||
|
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (n < this.length - pos) {
|
if (n < this.length - pos) {
|
||||||
const cell = new CellData();
|
const cell = new CellData();
|
||||||
for (let i = this.length - pos - n - 1; i >= 0; --i) {
|
for (let i = this.length - pos - n - 1; i >= 0; --i) {
|
||||||
@@ -243,9 +249,14 @@ export class BufferLine implements IBufferLine {
|
|||||||
this.setCell(i, fillCellData);
|
this.setCell(i, fillCellData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle fullwidth at line end: reset last cell if it is first cell of a wide char
|
||||||
|
if (this.getWidth(this.length - 1) === 2) {
|
||||||
|
this.setCellFromCodePoint(this.length - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteCells(pos: number, n: number, fillCellData: ICellData): void {
|
public deleteCells(pos: number, n: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
|
||||||
pos %= this.length;
|
pos %= this.length;
|
||||||
if (n < this.length - pos) {
|
if (n < this.length - pos) {
|
||||||
const cell = new CellData();
|
const cell = new CellData();
|
||||||
@@ -260,9 +271,28 @@ export class BufferLine implements IBufferLine {
|
|||||||
this.setCell(i, fillCellData);
|
this.setCell(i, fillCellData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle fullwidth at pos:
|
||||||
|
// - reset pos-1 if wide char
|
||||||
|
// - reset pos if width==0 (previous second cell of a wide char)
|
||||||
|
if (pos && this.getWidth(pos - 1) === 2) {
|
||||||
|
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||||
|
}
|
||||||
|
if (this.getWidth(pos) === 0 && !this.hasContent(pos)) {
|
||||||
|
this.setCellFromCodePoint(pos, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public replaceCells(start: number, end: number, fillCellData: ICellData): void {
|
public replaceCells(start: number, end: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
|
||||||
|
// handle fullwidth at start: reset cell one to the left if start is second cell of a wide char
|
||||||
|
if (start && this.getWidth(start - 1) === 2) {
|
||||||
|
this.setCellFromCodePoint(start - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||||
|
}
|
||||||
|
// handle fullwidth at last cell + 1: reset to empty cell if it is second part of a wide char
|
||||||
|
if (end < this.length && this.getWidth(end - 1) === 2) {
|
||||||
|
this.setCellFromCodePoint(end, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||||
|
}
|
||||||
|
|
||||||
while (start < end && start < this.length) {
|
while (start < end && start < this.length) {
|
||||||
this.setCell(start++, fillCellData);
|
this.setCell(start++, fillCellData);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user