mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
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);
|
||||
});
|
||||
});
|
||||
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
|
||||
*/
|
||||
|
||||
import { CharData, IBufferLine, ICellData } from 'common/Types';
|
||||
import { CharData, IBufferLine, ICellData, IAttributeData } from 'common/Types';
|
||||
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 { 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;
|
||||
|
||||
// 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) {
|
||||
const cell = new CellData();
|
||||
for (let i = this.length - pos - n - 1; i >= 0; --i) {
|
||||
@@ -243,9 +249,14 @@ export class BufferLine implements IBufferLine {
|
||||
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;
|
||||
if (n < this.length - pos) {
|
||||
const cell = new CellData();
|
||||
@@ -260,9 +271,28 @@ export class BufferLine implements IBufferLine {
|
||||
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) {
|
||||
this.setCell(start++, fillCellData);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user