mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into fix-3221
This commit is contained in:
@@ -170,6 +170,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
||||
- [**Intervue**](https://www.intervue.io): Pair programming for interviews. Multiple programming languages supported, with results displayed by xterm.js.
|
||||
- [**TRASA**](https://trasa.io): Zero trust access to Web, SSH, RDP and Database services.
|
||||
- [**Commas**](https://github.com/CyanSalt/commas): Commas is a hackable terminal and command runner.
|
||||
- [**Devtron**](https://github.com/devtron-labs/devtron): Software Delivery Workflow For Kubernetes.
|
||||
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
||||
|
||||
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. Note: Please add any new contributions to the end of the list only.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-fit",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-ligatures",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"description": "Add support for programming ligatures to xterm.js",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-search",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -21,7 +21,7 @@ abstract class BaseSerializeHandler {
|
||||
const cell2 = this._buffer.getNullCell();
|
||||
let oldCell = cell1;
|
||||
|
||||
this._beforeSerialize(endRow - startRow);
|
||||
this._beforeSerialize(endRow - startRow, startRow, endRow);
|
||||
|
||||
for (let row = startRow; row < endRow; row++) {
|
||||
const line = this._buffer.getLine(row);
|
||||
@@ -36,7 +36,7 @@ abstract class BaseSerializeHandler {
|
||||
oldCell = c;
|
||||
}
|
||||
}
|
||||
this._rowEnd(row);
|
||||
this._rowEnd(row, row === endRow - 1);
|
||||
}
|
||||
|
||||
this._afterSerialize();
|
||||
@@ -45,8 +45,8 @@ abstract class BaseSerializeHandler {
|
||||
}
|
||||
|
||||
protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { }
|
||||
protected _rowEnd(row: number): void { }
|
||||
protected _beforeSerialize(rows: number): void { }
|
||||
protected _rowEnd(row: number, isLastRow: boolean): void { }
|
||||
protected _beforeSerialize(rows: number, startRow: number, endRow: number): void { }
|
||||
protected _afterSerialize(): void { }
|
||||
protected _serializeString(): string { return ''; }
|
||||
}
|
||||
@@ -71,27 +71,152 @@ function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
|
||||
&& cell1.isDim() === cell2.isDim();
|
||||
}
|
||||
|
||||
|
||||
|
||||
class StringSerializeHandler extends BaseSerializeHandler {
|
||||
private _rowIndex: number = 0;
|
||||
private _allRows: string[] = new Array<string>();
|
||||
private _allRowSeparators: string[] = new Array<string>();
|
||||
private _currentRow: string = '';
|
||||
private _nullCellCount: number = 0;
|
||||
|
||||
constructor(buffer: IBuffer) {
|
||||
super(buffer);
|
||||
// we can see a full colored cell and a null cell that only have background the same style
|
||||
// but the information isn't preserved by null cell itself
|
||||
// so wee need to record it when required.
|
||||
private _cursorStyle: IBufferCell = this._buffer1.getNullCell();
|
||||
|
||||
// where exact the cursor styles comes from
|
||||
// because we can't copy the cell directly
|
||||
// so we remember where the content comes from instead
|
||||
private _cursorStyleRow: number = 0;
|
||||
private _cursorStyleCol: number = 0;
|
||||
|
||||
// this is a null cell for reference for checking whether background is empty or not
|
||||
private _backgroundCell: IBufferCell = this._buffer1.getNullCell();
|
||||
|
||||
private _firstRow: number = 0;
|
||||
private _lastCursorRow: number = 0;
|
||||
private _lastCursorCol: number = 0;
|
||||
private _lastContentCursorRow: number = 0;
|
||||
private _lastContentCursorCol: number = 0;
|
||||
|
||||
constructor(private _buffer1: IBuffer, private _terminal: Terminal) {
|
||||
super(_buffer1);
|
||||
}
|
||||
|
||||
protected _beforeSerialize(rows: number): void {
|
||||
protected _beforeSerialize(rows: number, start: number, end: number): void {
|
||||
this._allRows = new Array<string>(rows);
|
||||
this._lastContentCursorRow = start;
|
||||
this._lastCursorRow = start;
|
||||
this._firstRow = start;
|
||||
}
|
||||
|
||||
protected _rowEnd(row: number): void {
|
||||
this._allRows[this._rowIndex++] = this._currentRow;
|
||||
private _thisRowLastChar: IBufferCell = this._buffer1.getNullCell();
|
||||
private _thisRowLastSecondChar: IBufferCell = this._buffer1.getNullCell();
|
||||
private _nextRowFirstChar: IBufferCell = this._buffer1.getNullCell();
|
||||
protected _rowEnd(row: number, isLastRow: boolean): void {
|
||||
// if there is colorful empty cell at line end, whe must pad it back, or the the color block will missing
|
||||
if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) {
|
||||
// use clear right to set background.
|
||||
this._currentRow += `\x1b[${this._nullCellCount}X`;
|
||||
}
|
||||
|
||||
let rowSeparator = '';
|
||||
|
||||
// handle row separator
|
||||
if (!isLastRow) {
|
||||
// Enable BCE
|
||||
if (row - this._firstRow >= this._terminal.rows) {
|
||||
this._buffer1.getLine(this._cursorStyleRow)?.getCell(this._cursorStyleCol, this._backgroundCell);
|
||||
}
|
||||
|
||||
// Fetch current line
|
||||
const currentLine = this._buffer1.getLine(row)!;
|
||||
// Fetch next line
|
||||
const nextLine = this._buffer1.getLine(row + 1)!;
|
||||
|
||||
if (!nextLine.isWrapped) {
|
||||
// just insert the line break
|
||||
rowSeparator = '\r\n';
|
||||
// we sended the enter
|
||||
this._lastCursorRow = row + 1;
|
||||
this._lastCursorCol = 0;
|
||||
} else {
|
||||
rowSeparator = '';
|
||||
const thisRowLastChar = currentLine.getCell(currentLine.length - 1, this._thisRowLastChar)!;
|
||||
const thisRowLastSecondChar = currentLine.getCell(currentLine.length - 2, this._thisRowLastSecondChar)!;
|
||||
const nextRowFirstChar = nextLine.getCell(0, this._nextRowFirstChar)!;
|
||||
const isNextRowFirstCharDoubleWidth = nextRowFirstChar.getWidth() > 1;
|
||||
|
||||
// validate whether this line wrap is ever possible
|
||||
// which mean whether cursor can placed at a overflow position (x === row) naturally
|
||||
let isValid = false;
|
||||
|
||||
if (
|
||||
// you must output character to cause overflow, control sequence can't do this
|
||||
nextRowFirstChar.getChars() &&
|
||||
isNextRowFirstCharDoubleWidth ? this._nullCellCount <= 1 : this._nullCellCount <= 0
|
||||
) {
|
||||
if (
|
||||
// the last character can't be null,
|
||||
// you can't use control sequence to move cursor to (x === row)
|
||||
(thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) &&
|
||||
// change background of the first wrapped cell also affects BCE
|
||||
// so we mark it as invalid to simply the process to determine line separator
|
||||
equalBg(thisRowLastChar, nextRowFirstChar)
|
||||
) {
|
||||
isValid = true;
|
||||
}
|
||||
|
||||
if (
|
||||
// the second to last character can't be null if the next line starts with CJK,
|
||||
// you can't use control sequence to move cursor to (x === row)
|
||||
isNextRowFirstCharDoubleWidth &&
|
||||
(thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) &&
|
||||
// change background of the first wrapped cell also affects BCE
|
||||
// so we mark it as invalid to simply the process to determine line separator
|
||||
equalBg(thisRowLastChar, nextRowFirstChar) &&
|
||||
equalBg(thisRowLastSecondChar, nextRowFirstChar)
|
||||
) {
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
// force the wrap with magic
|
||||
// insert enough character to force the wrap
|
||||
rowSeparator = '-'.repeat(this._nullCellCount + 1);
|
||||
// move back and erase next line head
|
||||
rowSeparator += '\x1b[1D\x1b[1X';
|
||||
|
||||
if (this._nullCellCount > 0) {
|
||||
// do these because we filled the last several null slot, which we shouldn't
|
||||
rowSeparator += '\x1b[A';
|
||||
rowSeparator += `\x1b[${currentLine.length - this._nullCellCount}C`;
|
||||
rowSeparator += `\x1b[${this._nullCellCount}X`;
|
||||
rowSeparator += `\x1b[${currentLine.length - this._nullCellCount}D`;
|
||||
rowSeparator += '\x1b[B';
|
||||
}
|
||||
|
||||
// This is content and need the be serialized even it is invisible.
|
||||
// without this, wrap will be missing from outputs.
|
||||
this._lastContentCursorRow = row + 1;
|
||||
this._lastContentCursorCol = 0;
|
||||
|
||||
// force commit the cursor position
|
||||
this._lastCursorRow = row + 1;
|
||||
this._lastCursorCol = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._allRows[this._rowIndex] = this._currentRow;
|
||||
this._allRowSeparators[this._rowIndex++] = rowSeparator;
|
||||
this._currentRow = '';
|
||||
this._nullCellCount = 0;
|
||||
}
|
||||
|
||||
protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void {
|
||||
private _diffStyle(cell: IBufferCell, oldCell: IBufferCell): number[] {
|
||||
const sgrSeq: number[] = [];
|
||||
const fgChanged = !equalFg(cell, oldCell);
|
||||
const bgChanged = !equalBg(cell, oldCell);
|
||||
@@ -99,7 +224,9 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
|
||||
if (fgChanged || bgChanged || flagsChanged) {
|
||||
if (cell.isAttributeDefault()) {
|
||||
this._currentRow += '\x1b[0m';
|
||||
if (!oldCell.isAttributeDefault()) {
|
||||
sgrSeq.push(0);
|
||||
}
|
||||
} else {
|
||||
if (fgChanged) {
|
||||
const color = cell.getFgColor();
|
||||
@@ -131,30 +258,129 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (sgrSeq.length) {
|
||||
return sgrSeq;
|
||||
}
|
||||
|
||||
protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void {
|
||||
// a width 0 cell don't need to be count because it is just a placeholder after a CJK character;
|
||||
const isPlaceHolderCell = cell.getWidth() === 0;
|
||||
|
||||
if (isPlaceHolderCell) {
|
||||
return;
|
||||
}
|
||||
|
||||
// this cell don't have content
|
||||
const isEmptyCell = cell.getChars() === '';
|
||||
|
||||
const sgrSeq = this._diffStyle(cell, this._cursorStyle);
|
||||
|
||||
// the empty cell style is only assumed to be changed when background changed, because foreground is always 0.
|
||||
const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0;
|
||||
|
||||
/**
|
||||
* handles style change
|
||||
*/
|
||||
if (styleChanged) {
|
||||
// before update the style, we need to fill empty cell back
|
||||
if (this._nullCellCount > 0) {
|
||||
// use clear right to set background.
|
||||
if (!equalBg(this._cursorStyle, this._backgroundCell)) {
|
||||
this._currentRow += `\x1b[${this._nullCellCount}X`;
|
||||
}
|
||||
// use move right to move cursor.
|
||||
this._currentRow += `\x1b[${this._nullCellCount}C`;
|
||||
this._nullCellCount = 0;
|
||||
}
|
||||
|
||||
this._lastContentCursorRow = this._lastCursorRow = row;
|
||||
this._lastContentCursorCol = this._lastCursorCol = col;
|
||||
|
||||
this._currentRow += `\x1b[${sgrSeq.join(';')}m`;
|
||||
|
||||
// update the last cursor style
|
||||
const line = this._buffer1.getLine(row);
|
||||
if (line !== undefined) {
|
||||
line.getCell(col, this._cursorStyle);
|
||||
this._cursorStyleRow = row;
|
||||
this._cursorStyleCol = col;
|
||||
}
|
||||
}
|
||||
|
||||
// Count number of null cells encountered after the last non-null cell and move the cursor
|
||||
// if a non-null cell is found (eg. \t or cursor move)
|
||||
if (cell.getChars() === '') {
|
||||
/**
|
||||
* handles actual content
|
||||
*/
|
||||
if (isEmptyCell) {
|
||||
this._nullCellCount += cell.getWidth();
|
||||
} else if (this._nullCellCount > 0) {
|
||||
this._currentRow += `\x1b[${this._nullCellCount}C`;
|
||||
this._nullCellCount = 0;
|
||||
}
|
||||
} else {
|
||||
if (this._nullCellCount > 0) {
|
||||
// we can just assume we have same style with previous one here
|
||||
// because style change is handled by previous stage
|
||||
// use move right when background is empty, use clear right when there is background.
|
||||
if (equalBg(this._cursorStyle, this._backgroundCell)) {
|
||||
this._currentRow += `\x1b[${this._nullCellCount}C`;
|
||||
} else {
|
||||
this._currentRow += `\x1b[${this._nullCellCount}X`;
|
||||
this._currentRow += `\x1b[${this._nullCellCount}C`;
|
||||
}
|
||||
this._nullCellCount = 0;
|
||||
}
|
||||
|
||||
this._currentRow += cell.getChars();
|
||||
this._currentRow += cell.getChars();
|
||||
|
||||
// update cursor
|
||||
this._lastContentCursorRow = this._lastCursorRow = row;
|
||||
this._lastContentCursorCol = this._lastCursorCol = col + cell.getWidth();
|
||||
}
|
||||
}
|
||||
|
||||
protected _serializeString(): string {
|
||||
let rowEnd = this._allRows.length;
|
||||
for (; rowEnd > 0; rowEnd--) {
|
||||
if (this._allRows[rowEnd - 1]) {
|
||||
break;
|
||||
|
||||
// the fixup is only required for data without scrollback
|
||||
// because it will always be placed at last line otherwise
|
||||
if (this._buffer1.length - this._firstRow <= this._terminal.rows) {
|
||||
rowEnd = this._lastContentCursorRow + 1 - this._firstRow;
|
||||
this._lastCursorCol = this._lastContentCursorCol;
|
||||
this._lastCursorRow = this._lastContentCursorRow;
|
||||
}
|
||||
|
||||
let content = '';
|
||||
|
||||
for (let i = 0; i < rowEnd; i++) {
|
||||
content += this._allRows[i];
|
||||
if (i + 1 < rowEnd) {
|
||||
content += this._allRowSeparators[i];
|
||||
}
|
||||
}
|
||||
return this._allRows.slice(0, rowEnd).join('\r\n');
|
||||
|
||||
// restore the cursor
|
||||
const realCursorRow = this._buffer1.baseY + this._buffer1.cursorY;
|
||||
const realCursorCol = this._buffer1.cursorX;
|
||||
|
||||
const cursorMoved = (realCursorRow !== this._lastCursorRow || realCursorCol !== this._lastCursorCol);
|
||||
|
||||
const moveRight = (offset: number): void => {
|
||||
if (offset > 0) {
|
||||
content += `\u001b[${offset}C`;
|
||||
} else if (offset < 0) {
|
||||
content += `\u001b[${-offset}D`;
|
||||
}
|
||||
};
|
||||
const moveDown = (offset: number): void => {
|
||||
if (offset > 0) {
|
||||
content += `\u001b[${offset}B`;
|
||||
} else if (offset < 0) {
|
||||
content += `\u001b[${-offset}A`;
|
||||
}
|
||||
};
|
||||
|
||||
if (cursorMoved) {
|
||||
moveDown(realCursorRow - this._lastCursorRow);
|
||||
moveRight(realCursorCol - this._lastCursorCol);
|
||||
}
|
||||
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,20 +393,33 @@ export class SerializeAddon implements ITerminalAddon {
|
||||
this._terminal = terminal;
|
||||
}
|
||||
|
||||
public serialize(rows?: number): string {
|
||||
// TODO: Add re-position cursor support
|
||||
// TODO: Add word wrap mode support
|
||||
private _getString(buffer: IBuffer, scrollback?: number): string {
|
||||
const maxRows = buffer.length;
|
||||
const handler = new StringSerializeHandler(buffer, this._terminal!);
|
||||
|
||||
const correctRows = (scrollback === undefined) ? maxRows : constrain(scrollback + this!._terminal!.rows, 0, maxRows);
|
||||
const result = handler.serialize(maxRows - correctRows, maxRows);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public serialize(scrollback?: number): string {
|
||||
// TODO: Add combinedData support
|
||||
if (!this._terminal) {
|
||||
throw new Error('Cannot use addon until it has been loaded');
|
||||
}
|
||||
|
||||
const maxRows = this._terminal.buffer.active.length;
|
||||
const handler = new StringSerializeHandler(this._terminal.buffer.active);
|
||||
if (this._terminal.buffer.active.type === 'normal') {
|
||||
return this._getString(this._terminal.buffer.active, scrollback);
|
||||
}
|
||||
|
||||
rows = (rows === undefined) ? maxRows : constrain(rows, 0, maxRows);
|
||||
const normalScreenContent = this._getString(this._terminal.buffer.normal, scrollback);
|
||||
// alt screen don't have scrollback
|
||||
const alternativeScreenContent = this._getString(this._terminal.buffer.alternate, undefined);
|
||||
|
||||
return handler.serialize(maxRows - rows, maxRows);
|
||||
return normalScreenContent
|
||||
+ '\u001b[?1049h\u001b[H'
|
||||
+ alternativeScreenContent;
|
||||
}
|
||||
|
||||
public dispose(): void { }
|
||||
|
||||
@@ -14,6 +14,22 @@ let page: Page;
|
||||
const width = 800;
|
||||
const height = 600;
|
||||
|
||||
const writeRawSync = (page: any, str: string): Promise<void> => writeSync(page, '\' +' + JSON.stringify(str) + '+ \'');
|
||||
|
||||
const testNormalScreenEqual = async (page: any, str: string): Promise<void> => {
|
||||
await writeRawSync(page, str);
|
||||
const originalBuffer = await page.evaluate(`inspectBuffer(term.buffer.normal);`);
|
||||
|
||||
const result = await page.evaluate(`serializeAddon.serialize();`) as string;
|
||||
await page.evaluate(`term.reset();`);
|
||||
await writeRawSync(page, result);
|
||||
const newBuffer = await page.evaluate(`inspectBuffer(term.buffer.normal);`);
|
||||
|
||||
// chai decides -0 and 0 are different number...
|
||||
// and firefox have a bug that output -0 for unknown reason
|
||||
assert.equal(JSON.stringify(originalBuffer), JSON.stringify(newBuffer));
|
||||
};
|
||||
|
||||
describe('SerializeAddon', () => {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
@@ -27,19 +43,74 @@ describe('SerializeAddon', () => {
|
||||
await page.evaluate(`
|
||||
window.serializeAddon = new SerializeAddon();
|
||||
window.term.loadAddon(window.serializeAddon);
|
||||
window.inspectBuffer = (buffer) => {
|
||||
const lines = [];
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
// Do this intentionally to get content of underlining source
|
||||
const bufferLine = buffer.getLine(i)._line;
|
||||
lines.push(JSON.stringify(bufferLine));
|
||||
}
|
||||
return {
|
||||
x: buffer.cursorX,
|
||||
y: buffer.cursorY,
|
||||
data: lines
|
||||
};
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
after(async () => await browser.close());
|
||||
beforeEach(async () => await page.evaluate(`window.term.reset()`));
|
||||
|
||||
it('produce different output when we call test util with different text', async function(): Promise<any> {
|
||||
await writeRawSync(page, '12345');
|
||||
const buffer1 = await page.evaluate(`inspectBuffer(term.buffer.normal);`);
|
||||
|
||||
await page.evaluate(`term.reset();`);
|
||||
await writeRawSync(page, '67890');
|
||||
const buffer2 = await page.evaluate(`inspectBuffer(term.buffer.normal);`);
|
||||
|
||||
assert.throw(() => {
|
||||
assert.equal(JSON.stringify(buffer1), JSON.stringify(buffer2));
|
||||
});
|
||||
});
|
||||
|
||||
it('produce different output when we call test util with different line wrap', async function(): Promise<any> {
|
||||
await writeRawSync(page, '1234567890\r\n12345');
|
||||
const buffer3 = await page.evaluate(`inspectBuffer(term.buffer.normal);`);
|
||||
|
||||
await page.evaluate(`term.reset();`);
|
||||
await writeRawSync(page, '1234567890n12345');
|
||||
const buffer4 = await page.evaluate(`inspectBuffer(term.buffer.normal);`);
|
||||
|
||||
assert.throw(() => {
|
||||
assert.equal(JSON.stringify(buffer3), JSON.stringify(buffer4));
|
||||
});
|
||||
});
|
||||
|
||||
it('empty content', async function(): Promise<any> {
|
||||
const rows = 10;
|
||||
const cols = 10;
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), '');
|
||||
});
|
||||
|
||||
it('trim last empty lines', async function(): Promise<any> {
|
||||
it('unwrap wrapped line', async function(): Promise<any> {
|
||||
const lines = ['123456789123456789'];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
});
|
||||
|
||||
it('does not unwrap non-wrapped line', async function(): Promise<any> {
|
||||
const lines = [
|
||||
'123456789',
|
||||
'123456789'
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
});
|
||||
|
||||
|
||||
it('preserve last empty lines', async function(): Promise<any> {
|
||||
const cols = 10;
|
||||
const lines = [
|
||||
'',
|
||||
@@ -55,7 +126,7 @@ describe('SerializeAddon', () => {
|
||||
''
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.slice(0, 8).join('\r\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
});
|
||||
|
||||
it('digits content', async function(): Promise<any> {
|
||||
@@ -67,21 +138,22 @@ describe('SerializeAddon', () => {
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
});
|
||||
|
||||
it('serialize half rows of content', async function(): Promise<any> {
|
||||
const rows = 10;
|
||||
const halfRows = rows >> 1;
|
||||
it('serialize with half of scrollback', async function(): Promise<any> {
|
||||
const rows = 20;
|
||||
const scrollback = rows - 10;
|
||||
const halfScrollback = scrollback / 2;
|
||||
const cols = 10;
|
||||
const lines = newArray<string>((index: number) => digitsString(cols, index), rows);
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize(${halfRows});`), lines.slice(halfRows, 2 * halfRows).join('\r\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize(${halfScrollback});`), lines.slice(halfScrollback, rows).join('\r\n'));
|
||||
});
|
||||
|
||||
it('serialize 0 rows of content', async function(): Promise<any> {
|
||||
const rows = 10;
|
||||
it('serialize 0 rows of scrollback', async function(): Promise<any> {
|
||||
const rows = 20;
|
||||
const cols = 10;
|
||||
const lines = newArray<string>((index: number) => digitsString(cols, index), rows);
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize(0);`), '');
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize(0);`), lines.slice(rows - 10, rows).join('\r\n'));
|
||||
});
|
||||
|
||||
it('serialize all rows of content with color16', async function(): Promise<any> {
|
||||
@@ -276,17 +348,15 @@ describe('SerializeAddon', () => {
|
||||
'中文中文',
|
||||
'12中文',
|
||||
'中文12',
|
||||
'1中文中文中' // this line is going to be wrapped at last character because it has line length of 11 (1+2*5)
|
||||
];
|
||||
const expected = [
|
||||
'中文中文',
|
||||
'12中文',
|
||||
'中文12',
|
||||
'1中文中文',
|
||||
'中'
|
||||
// This line is going to be wrapped at last character
|
||||
// because it has line length of 11 (1+2*5).
|
||||
// We concat it back without the null cell currently.
|
||||
// But this may be incorrect.
|
||||
// see also #3097
|
||||
'1中文中文中'
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), expected.join('\r\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
});
|
||||
|
||||
it('serialize CJK Mixed with tab correctly', async () => {
|
||||
@@ -299,6 +369,118 @@ describe('SerializeAddon', () => {
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), expected.join('\r\n'));
|
||||
});
|
||||
|
||||
it('serialize with alt screen correctly', async () => {
|
||||
const SMCUP = '\u001b[?1049h';
|
||||
const CUP = '\u001b[H';
|
||||
|
||||
const lines = [
|
||||
`1${SMCUP}${CUP}2`
|
||||
];
|
||||
const expected = [
|
||||
`1${SMCUP}${CUP}2`
|
||||
];
|
||||
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`window.term.buffer.active.type`), 'alternate');
|
||||
assert.equal(JSON.stringify(await page.evaluate(`serializeAddon.serialize();`)), JSON.stringify(expected.join('\r\n')));
|
||||
});
|
||||
|
||||
it('serialize without alt screen correctly', async () => {
|
||||
const SMCUP = '\u001b[?1049h';
|
||||
const RMCUP = '\u001b[?1049l';
|
||||
|
||||
const lines = [
|
||||
`1${SMCUP}2${RMCUP}`
|
||||
];
|
||||
const expected = [
|
||||
`1`
|
||||
];
|
||||
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`window.term.buffer.active.type`), 'normal');
|
||||
assert.equal(JSON.stringify(await page.evaluate(`serializeAddon.serialize();`)), JSON.stringify(expected.join('\r\n')));
|
||||
});
|
||||
|
||||
it('serialize with background', async () => {
|
||||
const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`;
|
||||
|
||||
const lines = [
|
||||
`1\u001b[44m${CLEAR_RIGHT(5)}`,
|
||||
`2${CLEAR_RIGHT(9)}`
|
||||
];
|
||||
|
||||
await testNormalScreenEqual(page, lines.join('\r\n'));
|
||||
});
|
||||
|
||||
it('cause the BCE on scroll', async () => {
|
||||
const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`;
|
||||
|
||||
const padLines = newArray<string>(
|
||||
(index: number) => digitsString(10, index),
|
||||
10
|
||||
);
|
||||
|
||||
const lines = [
|
||||
...padLines,
|
||||
`\u001b[44m${CLEAR_RIGHT(5)}1111111111111111`
|
||||
];
|
||||
|
||||
await testNormalScreenEqual(page, lines.join('\r\n'));
|
||||
});
|
||||
|
||||
it('handle invalid wrap before scroll', async () => {
|
||||
const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`;
|
||||
const MOVE_UP = (l: number): string => `\u001b[${l}A`;
|
||||
const MOVE_DOWN = (l: number): string => `\u001b[${l}B`;
|
||||
const MOVE_LEFT = (l: number): string => `\u001b[${l}D`;
|
||||
|
||||
// A line wrap happened after current line.
|
||||
// But there is no content.
|
||||
// so wrap shouldn't even be able to happen.
|
||||
const segments = [
|
||||
`123456789012345`,
|
||||
MOVE_UP(1),
|
||||
CLEAR_RIGHT(5),
|
||||
MOVE_DOWN(1),
|
||||
MOVE_LEFT(5),
|
||||
CLEAR_RIGHT(5),
|
||||
MOVE_UP(1),
|
||||
'1'
|
||||
];
|
||||
|
||||
await testNormalScreenEqual(page, segments.join(''));
|
||||
});
|
||||
|
||||
it('handle invalid wrap after scroll', async () => {
|
||||
const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`;
|
||||
const MOVE_UP = (l: number): string => `\u001b[${l}A`;
|
||||
const MOVE_DOWN = (l: number): string => `\u001b[${l}B`;
|
||||
const MOVE_LEFT = (l: number): string => `\u001b[${l}D`;
|
||||
|
||||
const padLines = newArray<string>(
|
||||
(index: number) => digitsString(10, index),
|
||||
10
|
||||
);
|
||||
|
||||
// A line wrap happened after current line.
|
||||
// But there is no content.
|
||||
// so wrap shouldn't even be able to happen.
|
||||
const lines = [
|
||||
padLines.join('\r\n'),
|
||||
'\r\n',
|
||||
`123456789012345`,
|
||||
MOVE_UP(1),
|
||||
CLEAR_RIGHT(5),
|
||||
MOVE_DOWN(1),
|
||||
MOVE_LEFT(5),
|
||||
CLEAR_RIGHT(5),
|
||||
MOVE_UP(1),
|
||||
'1'
|
||||
];
|
||||
|
||||
await testNormalScreenEqual(page, lines.join(''));
|
||||
});
|
||||
});
|
||||
|
||||
function newArray<T>(initial: T | ((index: number) => T), count: number): T[] {
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "xterm",
|
||||
"description": "Full xterm terminal, in your browser",
|
||||
"version": "4.9.0",
|
||||
"version": "4.10.0",
|
||||
"main": "lib/xterm.js",
|
||||
"style": "css/xterm.css",
|
||||
"types": "typings/xterm.d.ts",
|
||||
@@ -11,6 +11,7 @@
|
||||
"prepackage": "npm run build",
|
||||
"package": "webpack",
|
||||
"start": "node demo/start",
|
||||
"start-debug": "node --inspect-brk demo/start",
|
||||
"lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/",
|
||||
"test": "npm run test-unit",
|
||||
"posttest": "npm run lint",
|
||||
|
||||
+138
-139
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from 'browser/TestUtils.test';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
@@ -75,7 +75,7 @@ describe('Terminal', () => {
|
||||
it('should fire a key event after a keypress DOM event', (done) => {
|
||||
term.onKey(e => {
|
||||
assert.equal(typeof e.key, 'string');
|
||||
expect(e.domEvent).to.be.an.instanceof(Object);
|
||||
assert.equal(e.domEvent instanceof Object, true);
|
||||
done();
|
||||
});
|
||||
const evKeyPress = <KeyboardEvent>{
|
||||
@@ -89,7 +89,7 @@ describe('Terminal', () => {
|
||||
it('should fire a key event after a keydown DOM event', (done) => {
|
||||
term.onKey(e => {
|
||||
assert.equal(typeof e.key, 'string');
|
||||
expect(e.domEvent).to.be.an.instanceof(Object);
|
||||
assert.equal(e.domEvent instanceof Object, true);
|
||||
done();
|
||||
});
|
||||
(<any>term).textarea = { value: '' };
|
||||
@@ -103,7 +103,6 @@ describe('Terminal', () => {
|
||||
});
|
||||
it('should fire the onResize event', (done) => {
|
||||
term.onResize(e => {
|
||||
expect(e).to.have.keys(['cols', 'rows']);
|
||||
assert.equal(typeof e.cols, 'number');
|
||||
assert.equal(typeof e.rows, 'number');
|
||||
done();
|
||||
@@ -724,10 +723,10 @@ describe('Terminal', () => {
|
||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||
term.writeSync(high + String.fromCharCode(i));
|
||||
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
|
||||
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(tchar.getChars().length).eql(2);
|
||||
expect(tchar.getWidth()).eql(1);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql('');
|
||||
assert.equal(tchar.getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(tchar.getChars().length, 2);
|
||||
assert.equal(tchar.getWidth(), 1);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -737,9 +736,9 @@ describe('Terminal', () => {
|
||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||
term.buffer.x = term.cols - 1;
|
||||
term.writeSync(high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length).eql(2);
|
||||
expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql('');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -750,10 +749,10 @@ describe('Terminal', () => {
|
||||
term.buffer.x = term.cols - 1;
|
||||
|
||||
term.writeSync('a' + high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql('a');
|
||||
expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length).eql(2);
|
||||
expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql('');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), 'a');
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -769,9 +768,9 @@ describe('Terminal', () => {
|
||||
}
|
||||
term.writeSync('a' + high + String.fromCharCode(i));
|
||||
// auto wraparound mode should cut off the rest of the line
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length).eql(2);
|
||||
expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql('');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -782,10 +781,10 @@ describe('Terminal', () => {
|
||||
term.writeSync(high);
|
||||
term.writeSync(String.fromCharCode(i));
|
||||
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
|
||||
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(tchar.getChars().length).eql(2);
|
||||
expect(tchar.getWidth()).eql(1);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql('');
|
||||
assert.equal(tchar.getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(tchar.getChars().length, 2);
|
||||
assert.equal(tchar.getWidth(), 1);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -796,81 +795,81 @@ describe('Terminal', () => {
|
||||
it('café', () => {
|
||||
term.writeSync('cafe\u0301');
|
||||
term.buffer.lines.get(0)!.loadCell(3, cell);
|
||||
expect(cell.getChars()).eql('e\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), 'e\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
});
|
||||
it('café - end of line', () => {
|
||||
term.buffer.x = term.cols - 1 - 3;
|
||||
term.writeSync('cafe\u0301');
|
||||
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
|
||||
expect(cell.getChars()).eql('e\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), 'e\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
term.buffer.lines.get(0)!.loadCell(1, cell);
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
});
|
||||
it('multiple combined é', () => {
|
||||
term.writeSync(Array(100).join('e\u0301'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
expect(cell.getChars()).eql('e\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), 'e\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('e\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), 'e\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
});
|
||||
it('multiple surrogate with combined', () => {
|
||||
term.writeSync(Array(100).join('\uD800\uDC00\u0301'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '\uD800\uDC00\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '\uD800\uDC00\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unicode - fullwidth characters', () => {
|
||||
const cell = new CellData();
|
||||
it('cursor movement even', () => {
|
||||
expect(term.buffer.x).eql(0);
|
||||
assert.equal(term.buffer.x, 0);
|
||||
term.writeSync('¥');
|
||||
expect(term.buffer.x).eql(2);
|
||||
assert.equal(term.buffer.x, 2);
|
||||
});
|
||||
it('cursor movement odd', () => {
|
||||
term.buffer.x = 1;
|
||||
expect(term.buffer.x).eql(1);
|
||||
assert.equal(term.buffer.x, 1);
|
||||
term.writeSync('¥');
|
||||
expect(term.buffer.x).eql(3);
|
||||
assert.equal(term.buffer.x, 3);
|
||||
});
|
||||
it('line of ¥ even', () => {
|
||||
term.writeSync(Array(50).join('¥'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (i % 2) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('¥');
|
||||
expect(cell.getChars().length).eql(1);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥');
|
||||
assert.equal(cell.getChars().length, 1);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('¥');
|
||||
expect(cell.getChars().length).eql(1);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥');
|
||||
assert.equal(cell.getChars().length, 1);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of ¥ odd', () => {
|
||||
term.buffer.x = 1;
|
||||
@@ -878,23 +877,23 @@ describe('Terminal', () => {
|
||||
for (let i = 1; i < term.cols - 1; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (!(i % 2)) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('¥');
|
||||
expect(cell.getChars().length).eql(1);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥');
|
||||
assert.equal(cell.getChars().length, 1);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('¥');
|
||||
expect(cell.getChars().length).eql(1);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥');
|
||||
assert.equal(cell.getChars().length, 1);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of ¥ with combining odd', () => {
|
||||
term.buffer.x = 1;
|
||||
@@ -902,42 +901,42 @@ describe('Terminal', () => {
|
||||
for (let i = 1; i < term.cols - 1; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (!(i % 2)) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('¥\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('¥\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of ¥ with combining even', () => {
|
||||
term.writeSync(Array(50).join('¥\u0301'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (i % 2) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('¥\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('¥\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of surrogate fullwidth with combining odd', () => {
|
||||
term.buffer.x = 1;
|
||||
@@ -945,42 +944,42 @@ describe('Terminal', () => {
|
||||
for (let i = 1; i < term.cols - 1; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (!(i % 2)) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '\ud843\ude6d\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '\ud843\ude6d\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of surrogate fullwidth with combining even', () => {
|
||||
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (i % 2) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '\ud843\ude6d\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '\ud843\ude6d\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -992,11 +991,11 @@ describe('Terminal', () => {
|
||||
term.buffer.y = 0;
|
||||
term.write('\x1b[4h');
|
||||
term.writeSync('abcde');
|
||||
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('e');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql('0');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('4');
|
||||
assert.equal(term.buffer.lines.get(0)!.length, term.cols);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(10, cell).getChars(), 'a');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(14, cell).getChars(), 'e');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(15, cell).getChars(), '0');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), '4');
|
||||
});
|
||||
it('fullwidth - insert', () => {
|
||||
term.writeSync(Array(9).join('0123456789').slice(-80));
|
||||
@@ -1004,12 +1003,12 @@ describe('Terminal', () => {
|
||||
term.buffer.y = 0;
|
||||
term.write('\x1b[4h');
|
||||
term.writeSync('¥¥¥');
|
||||
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('¥');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('¥');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql('');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('3');
|
||||
assert.equal(term.buffer.lines.get(0)!.length, term.cols);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(10, cell).getChars(), '¥');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(11, cell).getChars(), '');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(14, cell).getChars(), '¥');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(15, cell).getChars(), '');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), '3');
|
||||
});
|
||||
it('fullwidth - right border', () => {
|
||||
term.writeSync(Array(41).join('¥'));
|
||||
@@ -1017,15 +1016,15 @@ describe('Terminal', () => {
|
||||
term.buffer.y = 0;
|
||||
term.write('\x1b[4h');
|
||||
term.writeSync('a');
|
||||
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('¥');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced
|
||||
assert.equal(term.buffer.lines.get(0)!.length, term.cols);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(10, cell).getChars(), 'a');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(11, cell).getChars(), '¥');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), ''); // fullwidth char got replaced
|
||||
term.writeSync('b');
|
||||
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('b');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(12, cell).getChars()).eql('¥');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // empty cell after fullwidth
|
||||
assert.equal(term.buffer.lines.get(0)!.length, term.cols);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(11, cell).getChars(), 'b');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(12, cell).getChars(), '¥');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), ''); // empty cell after fullwidth
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1346,7 +1345,7 @@ describe('Terminal', () => {
|
||||
];
|
||||
terminal.writeSync(data.join(''));
|
||||
// brute force test with insane values
|
||||
expect(() => {
|
||||
assert.doesNotThrow(() => {
|
||||
for (let overscan = 0; overscan < 20; ++overscan) {
|
||||
for (let start = -10; start < 20; ++start) {
|
||||
for (let end = -10; end < 20; ++end) {
|
||||
@@ -1357,7 +1356,7 @@ describe('Terminal', () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}).to.not.throw();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1406,18 +1405,18 @@ describe('Terminal', () => {
|
||||
// not converting
|
||||
const termNotConverting = new TestTerminal({cols: 15, rows: 10});
|
||||
termNotConverting.writeSync('Hello\nWorld');
|
||||
expect(termNotConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello ');
|
||||
expect(termNotConverting.buffer.lines.get(1)!.translateToString(false)).equals(' World ');
|
||||
expect(termNotConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello');
|
||||
expect(termNotConverting.buffer.lines.get(1)!.translateToString(true)).equals(' World');
|
||||
assert.equal(termNotConverting.buffer.lines.get(0)!.translateToString(false), 'Hello ');
|
||||
assert.equal(termNotConverting.buffer.lines.get(1)!.translateToString(false), ' World ');
|
||||
assert.equal(termNotConverting.buffer.lines.get(0)!.translateToString(true), 'Hello');
|
||||
assert.equal(termNotConverting.buffer.lines.get(1)!.translateToString(true), ' World');
|
||||
|
||||
// converting
|
||||
const termConverting = new TestTerminal({cols: 15, rows: 10, convertEol: true});
|
||||
termConverting.writeSync('Hello\nWorld');
|
||||
expect(termConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello ');
|
||||
expect(termConverting.buffer.lines.get(1)!.translateToString(false)).equals('World ');
|
||||
expect(termConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello');
|
||||
expect(termConverting.buffer.lines.get(1)!.translateToString(true)).equals('World');
|
||||
assert.equal(termConverting.buffer.lines.get(0)!.translateToString(false), 'Hello ');
|
||||
assert.equal(termConverting.buffer.lines.get(1)!.translateToString(false), 'World ');
|
||||
assert.equal(termConverting.buffer.lines.get(0)!.translateToString(true), 'Hello');
|
||||
assert.equal(termConverting.buffer.lines.get(1)!.translateToString(true), 'World');
|
||||
});
|
||||
describe('Terminal InputHandler integration', () => {
|
||||
function getLines(term: TestTerminal, limit: number = term.rows): string[] {
|
||||
|
||||
@@ -386,7 +386,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
throw new Error('Terminal requires a parent element.');
|
||||
}
|
||||
|
||||
if (!document.body.contains(parent)) {
|
||||
if (!parent.isConnected) {
|
||||
this._logService.debug('Terminal.open was called on an element that was not attached to the DOM');
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
import { clone } from 'common/Clone';
|
||||
|
||||
describe('clone', () => {
|
||||
@@ -124,6 +124,6 @@ describe('clone', () => {
|
||||
|
||||
test.a.b.c = test;
|
||||
|
||||
expect(() => clone(test)).to.not.throw();
|
||||
assert.doesNotThrow(() => clone(test));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
import { InputHandler } from 'common/InputHandler';
|
||||
import { IBufferLine, IAttributeData, IAnsiColorChangeEvent } from 'common/Types';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
@@ -136,7 +136,16 @@ describe('InputHandler', () => {
|
||||
|
||||
it('insertChars', function(): void {
|
||||
const bufferService = new MockBufferService(80, 30);
|
||||
const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
bufferService,
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
|
||||
// insert some data in first and second line
|
||||
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
|
||||
@@ -144,36 +153,45 @@ describe('InputHandler', () => {
|
||||
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
|
||||
inputHandler.parse('1234567890');
|
||||
const line1: IBufferLine = bufferService.buffer.lines.get(0)!;
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '1234567890');
|
||||
|
||||
// insert one char from params = [0]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.insertChars(Params.fromArray([0]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456789');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' 123456789');
|
||||
|
||||
// insert one char from params = [1]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.insertChars(Params.fromArray([1]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 12345678');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' 12345678');
|
||||
|
||||
// insert two chars from params = [2]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.insertChars(Params.fromArray([2]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' 123456');
|
||||
|
||||
// insert 10 chars from params = [10]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.insertChars(Params.fromArray([10]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a'));
|
||||
});
|
||||
it('deleteChars', function(): void {
|
||||
const bufferService = new MockBufferService(80, 30);
|
||||
const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
bufferService,
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
|
||||
// insert some data in first and second line
|
||||
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
|
||||
@@ -181,39 +199,49 @@ describe('InputHandler', () => {
|
||||
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
|
||||
inputHandler.parse('1234567890');
|
||||
const line1: IBufferLine = bufferService.buffer.lines.get(0)!;
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '1234567890');
|
||||
|
||||
// delete one char from params = [0]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.deleteChars(Params.fromArray([0]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '234567890 ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '234567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '234567890 ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a') + '234567890');
|
||||
|
||||
// insert one char from params = [1]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.deleteChars(Params.fromArray([1]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '34567890 ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '34567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '34567890 ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a') + '34567890');
|
||||
|
||||
// insert two chars from params = [2]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.deleteChars(Params.fromArray([2]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '567890 ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '567890 ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a') + '567890');
|
||||
|
||||
|
||||
// insert 10 chars from params = [10]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.deleteChars(Params.fromArray([10]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a'));
|
||||
});
|
||||
it('eraseInLine', function(): void {
|
||||
const bufferService = new MockBufferService(80, 30);
|
||||
const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
bufferService,
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
|
||||
// fill 6 lines to test 3 different states
|
||||
inputHandler.parse(Array(bufferService.cols + 1).join('a'));
|
||||
@@ -224,24 +252,33 @@ describe('InputHandler', () => {
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.eraseInLine(Params.fromArray([0]));
|
||||
expect(bufferService.buffer.lines.get(0)!.translateToString(false)).equals(Array(71).join('a') + ' ');
|
||||
assert.equal(bufferService.buffer.lines.get(0)!.translateToString(false), Array(71).join('a') + ' ');
|
||||
|
||||
// params[1] - left erase
|
||||
bufferService.buffer.y = 1;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.eraseInLine(Params.fromArray([1]));
|
||||
expect(bufferService.buffer.lines.get(1)!.translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa');
|
||||
assert.equal(bufferService.buffer.lines.get(1)!.translateToString(false), Array(71).join(' ') + ' aaaaaaaaa');
|
||||
|
||||
// params[1] - left erase
|
||||
bufferService.buffer.y = 2;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.eraseInLine(Params.fromArray([2]));
|
||||
expect(bufferService.buffer.lines.get(2)!.translateToString(false)).equals(Array(bufferService.cols + 1).join(' '));
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.translateToString(false), Array(bufferService.cols + 1).join(' '));
|
||||
|
||||
});
|
||||
it('eraseInDisplay', function(): void {
|
||||
const bufferService = new MockBufferService(80, 7);
|
||||
const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
bufferService,
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
|
||||
// fill display with a's
|
||||
for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
|
||||
@@ -250,7 +287,7 @@ describe('InputHandler', () => {
|
||||
bufferService.buffer.y = 5;
|
||||
bufferService.buffer.x = 40;
|
||||
inputHandler.eraseInDisplay(Params.fromArray([0]));
|
||||
expect(termContent(bufferService, false)).eql([
|
||||
assert.deepEqual(termContent(bufferService, false), [
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
@@ -259,7 +296,7 @@ describe('InputHandler', () => {
|
||||
Array(40 + 1).join('a') + Array(bufferService.cols - 40 + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' ')
|
||||
]);
|
||||
expect(termContent(bufferService, true)).eql([
|
||||
assert.deepEqual(termContent(bufferService, true), [
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
@@ -278,7 +315,7 @@ describe('InputHandler', () => {
|
||||
bufferService.buffer.y = 5;
|
||||
bufferService.buffer.x = 40;
|
||||
inputHandler.eraseInDisplay(Params.fromArray([1]));
|
||||
expect(termContent(bufferService, false)).eql([
|
||||
assert.deepEqual(termContent(bufferService, false), [
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
@@ -287,7 +324,7 @@ describe('InputHandler', () => {
|
||||
Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a')
|
||||
]);
|
||||
expect(termContent(bufferService, true)).eql([
|
||||
assert.deepEqual(termContent(bufferService, true), [
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
@@ -306,7 +343,7 @@ describe('InputHandler', () => {
|
||||
bufferService.buffer.y = 5;
|
||||
bufferService.buffer.x = 40;
|
||||
inputHandler.eraseInDisplay(Params.fromArray([2]));
|
||||
expect(termContent(bufferService, false)).eql([
|
||||
assert.deepEqual(termContent(bufferService, false), [
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
@@ -315,7 +352,7 @@ describe('InputHandler', () => {
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' ')
|
||||
]);
|
||||
expect(termContent(bufferService, true)).eql([
|
||||
assert.deepEqual(termContent(bufferService, true), [
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
@@ -334,11 +371,11 @@ describe('InputHandler', () => {
|
||||
|
||||
// params[1] left and above with wrap
|
||||
// confirm precondition that line 2 is wrapped
|
||||
expect(bufferService.buffer.lines.get(2)!.isWrapped).true;
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true);
|
||||
bufferService.buffer.y = 2;
|
||||
bufferService.buffer.x = 40;
|
||||
inputHandler.eraseInDisplay(Params.fromArray([1]));
|
||||
expect(bufferService.buffer.lines.get(2)!.isWrapped).false;
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, false);
|
||||
|
||||
// reset and add a wrapped line
|
||||
bufferService.buffer.y = 0;
|
||||
@@ -349,16 +386,25 @@ describe('InputHandler', () => {
|
||||
|
||||
// params[1] left and above with wrap
|
||||
// confirm precondition that line 2 is wrapped
|
||||
expect(bufferService.buffer.lines.get(2)!.isWrapped).true;
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true);
|
||||
bufferService.buffer.y = 1;
|
||||
bufferService.buffer.x = 90; // Cursor is beyond last column
|
||||
inputHandler.eraseInDisplay(Params.fromArray([1]));
|
||||
expect(bufferService.buffer.lines.get(2)!.isWrapped).false;
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, false);
|
||||
});
|
||||
});
|
||||
describe('print', () => {
|
||||
it('should not cause an infinite loop (regression test)', () => {
|
||||
const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
new MockBufferService(80, 30),
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
const container = new Uint32Array(10);
|
||||
container[0] = 0x200B;
|
||||
inputHandler.print(container, 0, 1);
|
||||
@@ -383,48 +429,48 @@ describe('InputHandler', () => {
|
||||
});
|
||||
it('should handle DECSET/DECRST 47 (alt screen buffer)', () => {
|
||||
handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), '');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), ' TEST');
|
||||
// Text color of 'TEST' should be red
|
||||
expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1);
|
||||
assert.equal((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor()), 1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
|
||||
handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), '');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), ' TEST');
|
||||
// Text color of 'TEST' should be red
|
||||
expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1);
|
||||
assert.equal((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor()), 1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
|
||||
handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), 'TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), 'JUNK');
|
||||
// Text color of 'TEST' should be default
|
||||
expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||
assert.equal(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg, DEFAULT_ATTR_DATA.fg);
|
||||
// Text color of 'JUNK' should be red
|
||||
expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1);
|
||||
assert.equal((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor()), 1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
|
||||
handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), 'TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), '');
|
||||
// Text color of 'TEST' should be default
|
||||
expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||
assert.equal(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg, DEFAULT_ATTR_DATA.fg);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => {
|
||||
handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), 'TEST');
|
||||
// Text color of 'TEST' should be default
|
||||
expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||
assert.equal(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg, DEFAULT_ATTR_DATA.fg);
|
||||
handler.parse('\x1b[?1049h\x1b[uTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), 'TEST');
|
||||
// Text color of 'TEST' should be red
|
||||
expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1);
|
||||
assert.equal((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor()), 1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => {
|
||||
handler.parse('\x1b[42m\x1b[?1049h');
|
||||
// Buffer should be filled with green background
|
||||
expect(bufferService.buffer.lines.get(20)!.loadCell(10, new CellData()).getBgColor()).to.equal(2);
|
||||
assert.equal(bufferService.buffer.lines.get(20)!.loadCell(10, new CellData()).getBgColor(), 2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content,
|
||||
import { BufferLine } from 'common/buffer//BufferLine';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { CharData, IBufferLine } from '../Types';
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
|
||||
|
||||
@@ -151,20 +151,20 @@ describe('CellData', () => {
|
||||
describe('BufferLine', function(): void {
|
||||
it('ctor', function(): void {
|
||||
let line: IBufferLine = new TestBufferLine(0);
|
||||
expect(line.length).equals(0);
|
||||
expect(line.isWrapped).equals(false);
|
||||
assert.equal(line.length, 0);
|
||||
assert.equal(line.isWrapped, false);
|
||||
line = new TestBufferLine(10);
|
||||
expect(line.length).equals(10);
|
||||
expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
expect(line.isWrapped).equals(false);
|
||||
assert.equal(line.length, 10);
|
||||
assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
assert.equal(line.isWrapped, false);
|
||||
line = new TestBufferLine(10, undefined, true);
|
||||
expect(line.length).equals(10);
|
||||
expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
expect(line.isWrapped).equals(true);
|
||||
assert.equal(line.length, 10);
|
||||
assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
assert.equal(line.isWrapped, true);
|
||||
line = new TestBufferLine(10, CellData.fromCharData([123, 'a', 456, 'a'.charCodeAt(0)]), true);
|
||||
expect(line.length).equals(10);
|
||||
expect(line.loadCell(0, new CellData()).getAsCharData()).eql([123, 'a', 456, 'a'.charCodeAt(0)]);
|
||||
expect(line.isWrapped).equals(true);
|
||||
assert.equal(line.length, 10);
|
||||
assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [123, 'a', 456, 'a'.charCodeAt(0)]);
|
||||
assert.equal(line.isWrapped, true);
|
||||
});
|
||||
it('insertCells', function(): void {
|
||||
const line = new TestBufferLine(3);
|
||||
@@ -172,7 +172,7 @@ describe('BufferLine', function(): void {
|
||||
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
|
||||
line.insertCells(1, 3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql([
|
||||
assert.deepEqual(line.toArray(), [
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)]
|
||||
@@ -186,7 +186,7 @@ describe('BufferLine', function(): void {
|
||||
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
line.deleteCells(1, 2, CellData.fromCharData([6, 'f', 0, 'f'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql([
|
||||
assert.deepEqual(line.toArray(), [
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)],
|
||||
[5, 'e', 0, 'e'.charCodeAt(0)],
|
||||
@@ -202,7 +202,7 @@ describe('BufferLine', function(): void {
|
||||
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
line.replaceCells(2, 4, CellData.fromCharData([6, 'f', 0, 'f'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql([
|
||||
assert.deepEqual(line.toArray(), [
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[2, 'b', 0, 'b'.charCodeAt(0)],
|
||||
[6, 'f', 0, 'f'.charCodeAt(0)],
|
||||
@@ -218,7 +218,7 @@ describe('BufferLine', function(): void {
|
||||
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
line.fill(CellData.fromCharData([123, 'z', 0, 'z'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql([
|
||||
assert.deepEqual(line.toArray(), [
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
@@ -234,9 +234,9 @@ describe('BufferLine', function(): void {
|
||||
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
const line2 = line.clone();
|
||||
expect(TestBufferLine.prototype.toArray.apply(line2)).eql(line.toArray());
|
||||
expect(line2.length).equals(line.length);
|
||||
expect(line2.isWrapped).equals(line.isWrapped);
|
||||
assert.deepEqual(TestBufferLine.prototype.toArray.apply(line2), line.toArray());
|
||||
assert.equal(line2.length, line.length);
|
||||
assert.equal(line2.isWrapped, line.isWrapped);
|
||||
});
|
||||
it('copyFrom', function(): void {
|
||||
const line = new TestBufferLine(5);
|
||||
@@ -247,92 +247,92 @@ describe('BufferLine', function(): void {
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
const line2 = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), true);
|
||||
line2.copyFrom(line);
|
||||
expect(line2.toArray()).eql(line.toArray());
|
||||
expect(line2.length).equals(line.length);
|
||||
expect(line2.isWrapped).equals(line.isWrapped);
|
||||
assert.deepEqual(line2.toArray(), line.toArray());
|
||||
assert.equal(line2.length, line.length);
|
||||
assert.equal(line2.isWrapped, line.isWrapped);
|
||||
});
|
||||
it('should support combining chars', function(): void {
|
||||
// CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print
|
||||
// --> set code to the last charCodeAt value of the string
|
||||
// Note: needs to be fixed once the string pointer is in place
|
||||
const line = new TestBufferLine(2, CellData.fromCharData([1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql([[1, 'e\u0301', 0, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]]);
|
||||
assert.deepEqual(line.toArray(), [[1, 'e\u0301', 0, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]]);
|
||||
const line2 = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, '\u0301'.charCodeAt(0)]), true);
|
||||
line2.copyFrom(line);
|
||||
expect(line2.toArray()).eql(line.toArray());
|
||||
assert.deepEqual(line2.toArray(), line.toArray());
|
||||
const line3 = line.clone();
|
||||
expect(TestBufferLine.prototype.toArray.apply(line3)).eql(line.toArray());
|
||||
assert.deepEqual(TestBufferLine.prototype.toArray.apply(line3), line.toArray());
|
||||
});
|
||||
describe('resize', function(): void {
|
||||
it('enlarge(false)', function(): void {
|
||||
const line = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql((Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('enlarge(true)', function(): void {
|
||||
const line = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql((Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(true) - should apply new size', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.resize(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql((Array(5) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
assert.deepEqual(line.toArray(), (Array(5) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink to 0 length', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.resize(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql((Array(0) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
assert.deepEqual(line.toArray(), (Array(0) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('should remove combining data on replaced cells after shrinking then enlarging', () => {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.set(2, [ 0, '😁', 1, '😁'.charCodeAt(0) ]);
|
||||
line.set(9, [ 0, '😁', 1, '😁'.charCodeAt(0) ]);
|
||||
expect(line.translateToString()).eql('aa😁aaaaaa😁');
|
||||
expect(Object.keys(line.combined).length).eql(2);
|
||||
assert.equal(line.translateToString(), 'aa😁aaaaaa😁');
|
||||
assert.equal(Object.keys(line.combined).length, 2);
|
||||
line.resize(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.translateToString()).eql('aa😁aa');
|
||||
assert.equal(line.translateToString(), 'aa😁aa');
|
||||
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.translateToString()).eql('aa😁aaaaaaa');
|
||||
expect(Object.keys(line.combined).length).eql(1);
|
||||
assert.equal(line.translateToString(), 'aa😁aaaaaaa');
|
||||
assert.equal(Object.keys(line.combined).length, 1);
|
||||
});
|
||||
});
|
||||
describe('getTrimLength', 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);
|
||||
expect(line.getTrimmedLength()).equal(0);
|
||||
assert.equal(line.getTrimmedLength(), 0);
|
||||
});
|
||||
it('ASCII', function(): void {
|
||||
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)]));
|
||||
expect(line.getTrimmedLength()).equal(3);
|
||||
assert.equal(line.getTrimmedLength(), 3);
|
||||
});
|
||||
it('surrogate', function(): void {
|
||||
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)]));
|
||||
expect(line.getTrimmedLength()).equal(3);
|
||||
assert.equal(line.getTrimmedLength(), 3);
|
||||
});
|
||||
it('combining', function(): void {
|
||||
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)]));
|
||||
expect(line.getTrimmedLength()).equal(3);
|
||||
assert.equal(line.getTrimmedLength(), 3);
|
||||
});
|
||||
it('fullwidth', function(): void {
|
||||
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)]));
|
||||
line.setCell(3, CellData.fromCharData([0, '', 0, 0]));
|
||||
expect(line.getTrimmedLength()).equal(4); // also counts null cell after fullwidth
|
||||
assert.equal(line.getTrimmedLength(), 4); // also counts null cell after fullwidth
|
||||
});
|
||||
});
|
||||
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);
|
||||
expect(line.translateToString(false)).equal(' ');
|
||||
expect(line.translateToString(true)).equal('');
|
||||
assert.equal(line.translateToString(false), ' ');
|
||||
assert.equal(line.translateToString(true), '');
|
||||
});
|
||||
it('ASCII', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -340,14 +340,14 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.translateToString(false)).equal('a a aa ');
|
||||
expect(line.translateToString(true)).equal('a a aa');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a a a');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a a ');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a a');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a a a');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a a ');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a a');
|
||||
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');
|
||||
|
||||
});
|
||||
it('surrogate', function(): void {
|
||||
@@ -356,14 +356,14 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.translateToString(false)).equal('a 𝄞 𝄞𝄞 ');
|
||||
expect(line.translateToString(true)).equal('a 𝄞 𝄞𝄞');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a 𝄞 𝄞');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a 𝄞 ');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a 𝄞');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a 𝄞 𝄞');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a 𝄞 ');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a 𝄞');
|
||||
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 𝄞');
|
||||
});
|
||||
it('combining', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -371,14 +371,14 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.translateToString(false)).equal('a e\u0301 e\u0301e\u0301 ');
|
||||
expect(line.translateToString(true)).equal('a e\u0301 e\u0301e\u0301');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a e\u0301 e\u0301');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a e\u0301 ');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a e\u0301');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a e\u0301 e\u0301');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a e\u0301 ');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a e\u0301');
|
||||
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');
|
||||
});
|
||||
it('fullwidth', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -389,20 +389,20 @@ 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]));
|
||||
expect(line.translateToString(false)).equal('a 1 11 ');
|
||||
expect(line.translateToString(true)).equal('a 1 11');
|
||||
expect(line.translateToString(false, 0, 7)).equal('a 1 1');
|
||||
expect(line.translateToString(false, 0, 6)).equal('a 1 1');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a 1 ');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a 1');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a 1');
|
||||
expect(line.translateToString(false, 0, 2)).equal('a ');
|
||||
expect(line.translateToString(true, 0, 7)).equal('a 1 1');
|
||||
expect(line.translateToString(true, 0, 6)).equal('a 1 1');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a 1 ');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a 1');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a 1');
|
||||
expect(line.translateToString(true, 0, 2)).equal('a ');
|
||||
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 ');
|
||||
});
|
||||
it('space at end', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -411,21 +411,21 @@ 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)]));
|
||||
line.setCell(6, CellData.fromCharData([1, ' ', 1, ' '.charCodeAt(0)]));
|
||||
expect(line.translateToString(false)).equal('a a aa ');
|
||||
expect(line.translateToString(true)).equal('a a aa ');
|
||||
assert.equal(line.translateToString(false), 'a a aa ');
|
||||
assert.equal(line.translateToString(true), 'a a aa ');
|
||||
});
|
||||
it('should always return some sane value', function(): void {
|
||||
// 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);
|
||||
expect(line.translateToString(false)).equal(' ');
|
||||
expect(line.translateToString(true)).equal('');
|
||||
assert.equal(line.translateToString(false), ' ');
|
||||
assert.equal(line.translateToString(true), '');
|
||||
});
|
||||
it('should work with endCol=0', () => {
|
||||
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)]));
|
||||
expect(line.translateToString(true, 0, 0)).equal('');
|
||||
assert.equal(line.translateToString(true, 0, 0), '');
|
||||
});
|
||||
});
|
||||
describe('addCharToCell', () => {
|
||||
|
||||
@@ -58,8 +58,8 @@ describe('text encodings', () => {
|
||||
const decoder = new StringToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
for (let i = 0; i < 65536; ++i) {
|
||||
// skip surrogate pairs
|
||||
if (i >= 0xD800 && i <= 0xDFFF) {
|
||||
// skip surrogate pairs and a BOM
|
||||
if ((i >= 0xD800 && i <= 0xDFFF) || i === 0xFEFF) {
|
||||
continue;
|
||||
}
|
||||
const length = decoder.decode(String.fromCharCode(i), target);
|
||||
@@ -84,6 +84,14 @@ describe('text encodings', () => {
|
||||
decoder.clear();
|
||||
}
|
||||
});
|
||||
|
||||
it('0xFEFF(BOM)', () => {
|
||||
const decoder = new StringToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
const length = decoder.decode(String.fromCharCode(0xFEFF), target);
|
||||
assert.equal(length, 0);
|
||||
decoder.clear();
|
||||
});
|
||||
});
|
||||
|
||||
it('test strings', () => {
|
||||
@@ -118,8 +126,8 @@ describe('text encodings', () => {
|
||||
const decoder = new Utf8ToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
for (let i = 0; i < 65536; ++i) {
|
||||
// skip surrogate pairs
|
||||
if (i >= 0xD800 && i <= 0xDFFF) {
|
||||
// skip surrogate pairs and a BOM
|
||||
if ((i >= 0xD800 && i <= 0xDFFF) || i === 0xFEFF) {
|
||||
continue;
|
||||
}
|
||||
const utf8Data = fromByteString(encode(String.fromCharCode(i)));
|
||||
@@ -142,6 +150,15 @@ describe('text encodings', () => {
|
||||
decoder.clear();
|
||||
}
|
||||
});
|
||||
|
||||
it('0xFEFF(BOM)', () => {
|
||||
const decoder = new Utf8ToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
const utf8Data = fromByteString(encode(String.fromCharCode(0xFEFF)));
|
||||
const length = decoder.decode(utf8Data, target);
|
||||
assert.equal(length, 0);
|
||||
decoder.clear();
|
||||
});
|
||||
});
|
||||
|
||||
it('test strings', () => {
|
||||
@@ -215,6 +232,19 @@ describe('text encodings', () => {
|
||||
}
|
||||
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
||||
});
|
||||
|
||||
it('BOMs (3 byte sequences) - advance by 2', () => {
|
||||
const decoder = new Utf8ToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
const utf8Data = fromByteString('\xef\xbb\xbf\xef\xbb\xbf');
|
||||
let decoded = '';
|
||||
for (let i = 0; i < utf8Data.length; i += 2) {
|
||||
const written = decoder.decode(utf8Data.slice(i, i + 2), target);
|
||||
decoded += toString(target, written);
|
||||
}
|
||||
assert.equal(decoded, '');
|
||||
});
|
||||
|
||||
it('test break after 3 bytes - issue #2495', () => {
|
||||
const decoder = new Utf8ToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
|
||||
@@ -105,6 +105,10 @@ export class StringToUtf32 {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (code === 0xFEFF) {
|
||||
// BOM
|
||||
continue;
|
||||
}
|
||||
target[size++] = code;
|
||||
}
|
||||
return size;
|
||||
@@ -188,8 +192,8 @@ export class Utf8ToUtf32 {
|
||||
target[size++] = cp;
|
||||
}
|
||||
} else if (type === 3) {
|
||||
if (cp < 0x0800 || (cp >= 0xD800 && cp <= 0xDFFF)) {
|
||||
// illegal codepoint
|
||||
if (cp < 0x0800 || (cp >= 0xD800 && cp <= 0xDFFF) || cp === 0xFEFF) {
|
||||
// illegal codepoint or BOM
|
||||
} else {
|
||||
target[size++] = cp;
|
||||
}
|
||||
@@ -286,8 +290,8 @@ export class Utf8ToUtf32 {
|
||||
continue;
|
||||
}
|
||||
codepoint = (byte1 & 0x0F) << 12 | (byte2 & 0x3F) << 6 | (byte3 & 0x3F);
|
||||
if (codepoint < 0x0800 || (codepoint >= 0xD800 && codepoint <= 0xDFFF)) {
|
||||
// illegal codepoint, no i-- here
|
||||
if (codepoint < 0x0800 || (codepoint >= 0xD800 && codepoint <= 0xDFFF) || codepoint === 0xFEFF) {
|
||||
// illegal codepoint or BOM, no i-- here
|
||||
continue;
|
||||
}
|
||||
target[size++] = codepoint;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user