Skip to content

Commit

Permalink
Merge pull request #1641 from jerch/typedarray_BufferLine
Browse files Browse the repository at this point in the history
buffer redesign - 2. Typedarray based BufferLine
  • Loading branch information
jerch committed Oct 7, 2018
2 parents e7ae4be + 2febd9b commit 8d16bb6
Show file tree
Hide file tree
Showing 16 changed files with 550 additions and 260 deletions.
3 changes: 2 additions & 1 deletion demo/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ function initOptions(term: TerminalType): void {
fontFamily: null,
fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
rendererType: ['dom', 'canvas']
rendererType: ['dom', 'canvas'],
experimentalBufferLineImpl: ['JsArray', 'TypedArray']
};
const options = Object.keys((<any>term)._core.options);
const booleanOptions = [];
Expand Down
60 changes: 32 additions & 28 deletions src/Buffer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('Buffer', () => {

describe('fillViewportRows', () => {
it('should fill the buffer with blank lines based on the size of the viewport', () => {
const blankLineChar = BufferLine.blankLine(terminal.cols, DEFAULT_ATTR).get(0);
const blankLineChar = buffer.getBlankLine(DEFAULT_ATTR).get(0);
buffer.fillViewportRows();
assert.equal(buffer.lines.length, INIT_ROWS);
for (let y = 0; y < INIT_ROWS; y++) {
Expand Down Expand Up @@ -155,8 +155,12 @@ describe('Buffer', () => {
assert.equal(buffer.lines.maxLength, INIT_ROWS);
buffer.y = INIT_ROWS - 1;
buffer.fillViewportRows();
buffer.lines.get(5).get(0)[1] = 'a';
buffer.lines.get(INIT_ROWS - 1).get(0)[1] = 'b';
let chData = buffer.lines.get(5).get(0);
chData[1] = 'a';
buffer.lines.get(5).set(0, chData);
chData = buffer.lines.get(INIT_ROWS - 1).get(0);
chData[1] = 'b';
buffer.lines.get(INIT_ROWS - 1).set(0, chData);
buffer.resize(INIT_COLS, INIT_ROWS - 5);
assert.equal(buffer.lines.get(0).get(0)[1], 'a');
assert.equal(buffer.lines.get(INIT_ROWS - 1 - 5).get(0)[1], 'b');
Expand All @@ -180,7 +184,7 @@ describe('Buffer', () => {
buffer.fillViewportRows();
// Create 10 extra blank lines
for (let i = 0; i < 10; i++) {
buffer.lines.push(BufferLine.blankLine(terminal.cols, DEFAULT_ATTR));
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR));
}
// Set cursor to the bottom of the buffer
buffer.y = INIT_ROWS - 1;
Expand All @@ -200,7 +204,7 @@ describe('Buffer', () => {
buffer.fillViewportRows();
// Create 10 extra blank lines
for (let i = 0; i < 10; i++) {
buffer.lines.push(BufferLine.blankLine(terminal.cols, DEFAULT_ATTR));
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR));
}
// Set cursor to the bottom of the buffer
buffer.y = INIT_ROWS - 1;
Expand Down Expand Up @@ -273,33 +277,33 @@ describe('Buffer', () => {

describe ('translateBufferLineToString', () => {
it('should handle selecting a section of ascii text', () => {
const line = new BufferLine();
line.push([ null, 'a', 1, 'a'.charCodeAt(0)]);
line.push([ null, 'b', 1, 'b'.charCodeAt(0)]);
line.push([ null, 'c', 1, 'c'.charCodeAt(0)]);
line.push([ null, 'd', 1, 'd'.charCodeAt(0)]);
const line = new BufferLine(4);
line.set(0, [ null, 'a', 1, 'a'.charCodeAt(0)]);
line.set(1, [ null, 'b', 1, 'b'.charCodeAt(0)]);
line.set(2, [ null, 'c', 1, 'c'.charCodeAt(0)]);
line.set(3, [ null, 'd', 1, 'd'.charCodeAt(0)]);
buffer.lines.set(0, line);

const str = buffer.translateBufferLineToString(0, true, 0, 2);
assert.equal(str, 'ab');
});

it('should handle a cut-off double width character by including it', () => {
const line = new BufferLine();
line.push([ null, '語', 2, 35486 ]);
line.push([ null, '', 0, null]);
line.push([ null, 'a', 1, 'a'.charCodeAt(0)]);
const line = new BufferLine(3);
line.set(0, [ null, '語', 2, 35486 ]);
line.set(1, [ null, '', 0, null]);
line.set(2, [ null, 'a', 1, 'a'.charCodeAt(0)]);
buffer.lines.set(0, line);

const str1 = buffer.translateBufferLineToString(0, true, 0, 1);
assert.equal(str1, '語');
});

it('should handle a zero width character in the middle of the string by not including it', () => {
const line = new BufferLine();
line.push([ null, '語', 2, '語'.charCodeAt(0) ]);
line.push([ null, '', 0, null]);
line.push([ null, 'a', 1, 'a'.charCodeAt(0)]);
const line = new BufferLine(3);
line.set(0, [ null, '語', 2, '語'.charCodeAt(0) ]);
line.set(1, [ null, '', 0, null]);
line.set(2, [ null, 'a', 1, 'a'.charCodeAt(0)]);
buffer.lines.set(0, line);

const str0 = buffer.translateBufferLineToString(0, true, 0, 1);
Expand All @@ -313,9 +317,9 @@ describe('Buffer', () => {
});

it('should handle single width emojis', () => {
const line = new BufferLine();
line.push([ null, '😁', 1, '😁'.charCodeAt(0) ]);
line.push([ null, 'a', 1, 'a'.charCodeAt(0)]);
const line = new BufferLine(2);
line.set(0, [ null, '😁', 1, '😁'.charCodeAt(0) ]);
line.set(1, [ null, 'a', 1, 'a'.charCodeAt(0)]);
buffer.lines.set(0, line);

const str1 = buffer.translateBufferLineToString(0, true, 0, 1);
Expand All @@ -326,9 +330,9 @@ describe('Buffer', () => {
});

it('should handle double width emojis', () => {
const line = new BufferLine();
line.push([ null, '😁', 2, '😁'.charCodeAt(0) ]);
line.push([ null, '', 0, null]);
const line = new BufferLine(2);
line.set(0, [ null, '😁', 2, '😁'.charCodeAt(0) ]);
line.set(1, [ null, '', 0, null]);
buffer.lines.set(0, line);

const str1 = buffer.translateBufferLineToString(0, true, 0, 1);
Expand All @@ -337,10 +341,10 @@ describe('Buffer', () => {
const str2 = buffer.translateBufferLineToString(0, true, 0, 2);
assert.equal(str2, '😁');

const line2 = new BufferLine();
line2.push([ null, '😁', 2, '😁'.charCodeAt(0) ]);
line2.push([ null, '', 0, null]);
line2.push([ null, 'a', 1, 'a'.charCodeAt(0)]);
const line2 = new BufferLine(3);
line2.set(0, [ null, '😁', 2, '😁'.charCodeAt(0) ]);
line2.set(1, [ null, '', 0, null]);
line2.set(2, [ null, 'a', 1, 'a'.charCodeAt(0)]);
buffer.lines.set(0, line2);

const str3 = buffer.translateBufferLineToString(0, true, 0, 3);
Expand Down
46 changes: 39 additions & 7 deletions src/Buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
*/

import { CircularList } from './common/CircularList';
import { CharData, ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult } from './Types';
import { CharData, ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult, IBufferLineConstructor } from './Types';
import { EventEmitter } from './common/EventEmitter';
import { IMarker } from 'xterm';
import { BufferLine } from './BufferLine';
import { BufferLine, BufferLineTypedArray } from './BufferLine';

export const DEFAULT_ATTR = (0 << 18) | (257 << 9) | (256 << 0);
export const CHAR_DATA_ATTR_INDEX = 0;
Expand Down Expand Up @@ -39,6 +39,7 @@ export class Buffer implements IBuffer {
public savedY: number;
public savedX: number;
public markers: Marker[] = [];
private _bufferLineConstructor: IBufferLineConstructor;

/**
* Create a new Buffer.
Expand All @@ -53,6 +54,37 @@ export class Buffer implements IBuffer {
this.clear();
}

public setBufferLineFactory(type: string): void {
if (type === 'TypedArray') {
if (this._bufferLineConstructor !== BufferLineTypedArray) {
this._bufferLineConstructor = BufferLineTypedArray;
this._recreateLines();
}
} else {
if (this._bufferLineConstructor !== BufferLine) {
this._bufferLineConstructor = BufferLine;
this._recreateLines();
}
}
}

private _recreateLines(): void {
if (!this.lines) return;
for (let i = 0; i < this.lines.length; ++i) {
const oldLine = this.lines.get(i);
const newLine = new this._bufferLineConstructor(oldLine.length);
for (let j = 0; j < oldLine.length; ++j) {
newLine.set(j, oldLine.get(j));
}
this.lines.set(i, newLine);
}
}

public getBlankLine(attr: number, isWrapped?: boolean): IBufferLine {
const fillCharData: CharData = [attr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
return new this._bufferLineConstructor(this._terminal.cols, fillCharData, isWrapped);
}

public get hasScrollback(): boolean {
return this._hasScrollback && this.lines.maxLength > this._terminal.rows;
}
Expand Down Expand Up @@ -85,7 +117,7 @@ export class Buffer implements IBuffer {
if (this.lines.length === 0) {
let i = this._terminal.rows;
while (i--) {
this.lines.push(BufferLine.blankLine(this._terminal.cols, DEFAULT_ATTR));
this.lines.push(this.getBlankLine(DEFAULT_ATTR));
}
}
}
Expand All @@ -94,6 +126,7 @@ export class Buffer implements IBuffer {
* Clears the buffer to it's initial state, discarding all previous data.
*/
public clear(): void {
this.setBufferLineFactory(this._terminal.options.experimentalBufferLineImpl);
this.ydisp = 0;
this.ybase = 0;
this.y = 0;
Expand Down Expand Up @@ -124,9 +157,7 @@ export class Buffer implements IBuffer {
if (this._terminal.cols < newCols) {
const ch: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // does xterm use the default attr?
for (let i = 0; i < this.lines.length; i++) {
while (this.lines.get(i).length < newCols) {
this.lines.get(i).push(ch);
}
this.lines.get(i).resize(newCols, ch);
}
}

Expand All @@ -147,7 +178,8 @@ export class Buffer implements IBuffer {
} else {
// Add a blank line if there is no buffer left at the top to scroll to, or if there
// are blank lines after the cursor
this.lines.push(BufferLine.blankLine(newCols, DEFAULT_ATTR));
const fillCharData: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
this.lines.push(new this._bufferLineConstructor(newCols, fillCharData));
}
}
}
Expand Down
Loading

0 comments on commit 8d16bb6

Please sign in to comment.