diff --git a/src/BufferLine.ts b/src/BufferLine.ts index c16c3808..7c70c93b 100644 --- a/src/BufferLine.ts +++ b/src/BufferLine.ts @@ -248,6 +248,59 @@ export class BufferLine implements IBufferLine { } } + /** + * Set cell data from input handler. + * Since the input handler see the incoming chars as UTF32 codepoints, + * it gets an optimized access method. + */ + public setDataFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number): void { + this._data[index * CELL_SIZE + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT); + this._data[index * CELL_SIZE + Cell.FG] = fg; + this._data[index * CELL_SIZE + Cell.BG] = bg; + } + + /** + * Add a char to a cell from input handler. + * During input stage combining chars with a width of 0 follow and stack + * onto a leading char. Since we already set the attrs + * by the previous `setDataFromCodePoint` call, we can omit it here. + */ + public addCharToCell(index: number, codePoint: number): void { + let content = this._data[index * CELL_SIZE + Cell.CONTENT]; + if (content & Content.IS_COMBINED) { + // we already have a combined string, simply add + this._combined[index] += stringFromCodePoint(codePoint); + } else { + if (content & Content.CODEPOINT_MASK) { + // normal case for combining chars: + // - move current leading char + new one into combined string + // - set codepoint in cell buffer to index + // - set combined flag + this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint); + content &= ~Content.CODEPOINT_MASK; + content |= index | Content.IS_COMBINED; + } else { + // should not happen - we actually have no data in the cell yet + // simply set the data in the cell buffer with a width of 1 + content = codePoint | (1 << Content.WIDTH_SHIFT); + } + this._data[index * CELL_SIZE + Cell.CONTENT] = content; + } + } + + /** + * Set data from another buffer cell. + * Useful for basic in buffer copy action. + */ + public setDataFromCellData(index: number, content: number, fg: number, bg: number, combined?: string): void { + this._data[index * CELL_SIZE + Cell.CONTENT] = content; + this._data[index * CELL_SIZE + Cell.FG] = fg; + this._data[index * CELL_SIZE + Cell.BG] = bg; + if (content & Content.IS_COMBINED && combined) { + this._combined[index] = combined; + } + } + public insertCells(pos: number, n: number, fillCharData: CharData): void { pos %= this.length; if (n < this.length - pos) { diff --git a/src/InputHandler.ts b/src/InputHandler.ts index e270a5f3..bf8149b2 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -331,7 +331,6 @@ export class InputHandler extends Disposable implements IInputHandler { public print(data: Uint32Array, start: number, end: number): void { let code: number; - let char: string; let chWidth: number; const buffer: IBuffer = this._terminal.buffer; const charset: ICharset = this._terminal.charset; @@ -345,7 +344,6 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.updateRange(buffer.y); for (let pos = start; pos < end; ++pos) { code = data[pos]; - char = stringFromCodePoint(code); // calculate print space // expensive call, therefore we save width in line buffer @@ -355,15 +353,14 @@ export class InputHandler extends Disposable implements IInputHandler { // charset are only defined for ASCII, therefore we only // search for an replacement char if code < 127 if (code < 127 && charset) { - const ch = charset[char]; + const ch = charset[String.fromCharCode(code)]; if (ch) { code = ch.charCodeAt(0); - char = ch; } } if (screenReaderMode) { - this._terminal.emit('a11y.char', char); + this._terminal.emit('a11y.char', stringFromCodePoint(code)); } // insert combining char at last cursor position @@ -380,12 +377,12 @@ export class InputHandler extends Disposable implements IInputHandler { // since an empty cell is only set by fullwidth chars const chMinusTwo = bufferRow.get(buffer.x - 2); if (chMinusTwo) { - chMinusTwo[CHAR_DATA_CHAR_INDEX] += char; + chMinusTwo[CHAR_DATA_CHAR_INDEX] += stringFromCodePoint(code); chMinusTwo[CHAR_DATA_CODE_INDEX] = code; bufferRow.set(buffer.x - 2, chMinusTwo); // must be set explicitly now } } else { - chMinusOne[CHAR_DATA_CHAR_INDEX] += char; + chMinusOne[CHAR_DATA_CHAR_INDEX] += stringFromCodePoint(code); chMinusOne[CHAR_DATA_CODE_INDEX] = code; bufferRow.set(buffer.x - 1, chMinusOne); // must be set explicitly now } @@ -438,7 +435,7 @@ export class InputHandler extends Disposable implements IInputHandler { } // write current char to buffer and advance cursor - bufferRow.set(buffer.x++, [curAttr, char, chWidth, code]); + bufferRow.set(buffer.x++, [curAttr, stringFromCodePoint(code), chWidth, code]); // fullwidth char - also set next cell to placeholder stub and advance cursor // for graphemes bigger than fullwidth we can simply loop to zero diff --git a/src/Types.ts b/src/Types.ts index 60b86de1..5c4b4880 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -519,6 +519,9 @@ export interface IBufferLine { isWrapped: boolean; get(index: number): CharData; set(index: number, value: CharData): void; + setDataFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number): void; + addCharToCell(index: number, codePoint: number): void; + setDataFromCellData(index: number, content: number, fg: number, bg: number, combined?: string): void; insertCells(pos: number, n: number, ch: CharData): void; deleteCells(pos: number, n: number, fill: CharData): void; replaceCells(start: number, end: number, fill: CharData): void;