Skip to content

Commit

Permalink
Merge 93d7a30 into f3cd652
Browse files Browse the repository at this point in the history
  • Loading branch information
jerch committed Oct 29, 2018
2 parents f3cd652 + 93d7a30 commit c0c067a
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 19 deletions.
11 changes: 5 additions & 6 deletions src/BufferLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,11 @@ export class BufferLine implements IBufferLine {
}
}

public copyFrom(line: IBufferLine): void {
this._data = [];
for (let i = 0; i < line.length; ++i) {
this._push(line.get(i));
}
public copyFrom(line: BufferLine): IBufferLine {
this._data = line._data.slice(0);
this.length = line.length;
this.isWrapped = line.isWrapped;
return this;
}

public clone(): IBufferLine {
Expand Down Expand Up @@ -251,7 +249,7 @@ export class BufferLineTypedArray implements IBufferLine {
}

/** alter to a full copy of line */
public copyFrom(line: BufferLineTypedArray): void {
public copyFrom(line: BufferLineTypedArray): IBufferLine {
if (this.length !== line.length) {
this._data = new Uint32Array(line._data);
} else {
Expand All @@ -264,6 +262,7 @@ export class BufferLineTypedArray implements IBufferLine {
this._combined[el] = line._combined[el];
}
this.isWrapped = line.isWrapped;
return this;
}

/** create a new clone */
Expand Down
41 changes: 34 additions & 7 deletions src/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
* http://linux.die.net/man/7/urxvt
*/

import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, CharacterJoinerHandler } from './Types';
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, CharacterJoinerHandler, IBufferLine } from './Types';
import { IMouseZoneManager } from './ui/Types';
import { IRenderer } from './renderer/Types';
import { BufferSet } from './BufferSet';
import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from './Buffer';
import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, CHAR_DATA_ATTR_INDEX } from './Buffer';
import { CompositionHelper } from './CompositionHelper';
import { EventEmitter } from './common/EventEmitter';
import { Viewport } from './Viewport';
Expand Down Expand Up @@ -106,7 +106,8 @@ const DEFAULT_OPTIONS: ITerminalOptions = {
theme: null,
rightClickSelectsWord: Browser.isMac,
rendererType: 'canvas',
experimentalBufferLineImpl: 'JsArray'
experimentalBufferLineImpl: 'JsArray',
experimentalPushRecycling: false
};

export class Terminal extends EventEmitter implements ITerminal, IDisposable, IInputHandlingTerminal {
Expand Down Expand Up @@ -208,6 +209,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
private _screenDprMonitor: ScreenDprMonitor;
private _theme: ITheme;

// bufferline to clone/copy from for new blank lines
private _blankLine: IBufferLine = null;

public cols: number;
public rows: number;

Expand Down Expand Up @@ -497,6 +501,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
case 'experimentalBufferLineImpl':
this.buffers.normal.setBufferLineFactory(value);
this.buffers.alt.setBufferLineFactory(value);
this._blankLine = null;
break;
}
// Inform renderer of changes
Expand Down Expand Up @@ -1175,7 +1180,19 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
* @param isWrapped Whether the new line is wrapped from the previous line.
*/
public scroll(isWrapped?: boolean): void {
const newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped);
let newLine: IBufferLine;
const useRecycling = this.options.experimentalPushRecycling;
if (useRecycling) {
newLine = this._blankLine;
if (!newLine || newLine.length !== this.cols || newLine.get(0)[CHAR_DATA_ATTR_INDEX] !== this.eraseAttr()) {
newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped);
this._blankLine = newLine;
}
newLine.isWrapped = !!(isWrapped);
} else {
newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped);
}

const topRow = this.buffer.ybase + this.buffer.scrollTop;
const bottomRow = this.buffer.ybase + this.buffer.scrollBottom;

Expand All @@ -1185,9 +1202,19 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II

// Insert the line using the fastest method
if (bottomRow === this.buffer.lines.length - 1) {
this.buffer.lines.push(newLine);
if (useRecycling) {
if (willBufferBeTrimmed) {
// Warning: Never call .trimAndRecycle() without the
// willBufferBeTrimmed guard!
this.buffer.lines.trimAndRecycle().copyFrom(newLine);
} else {
this.buffer.lines.push(newLine.clone());
}
} else {
this.buffer.lines.push(newLine);
}
} else {
this.buffer.lines.splice(bottomRow + 1, 0, newLine);
this.buffer.lines.splice(bottomRow + 1, 0, (useRecycling) ? newLine.clone() : newLine);
}

// Only adjust ybase and ydisp when the buffer is not trimmed
Expand All @@ -1209,7 +1236,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
// scrollback, instead we can just shift them in-place.
const scrollRegionHeight = bottomRow - topRow + 1/*as it's zero-based*/;
this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1);
this.buffer.lines.set(bottomRow, newLine);
this.buffer.lines.set(bottomRow, (useRecycling) ? newLine.clone() : newLine);
}

// Move the viewport to the bottom of the buffer unless the user is
Expand Down
2 changes: 1 addition & 1 deletion src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ export interface IBufferLine {
replaceCells(start: number, end: number, fill: CharData): void;
resize(cols: number, fill: CharData, shrink?: boolean): void;
fill(fillCharData: CharData): void;
copyFrom(line: IBufferLine): void;
copyFrom(line: IBufferLine): IBufferLine;
clone(): IBufferLine;
}

Expand Down
16 changes: 16 additions & 0 deletions src/common/CircularList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,4 +257,20 @@ describe('CircularList', () => {
assert.equal(list.get(3), 4);
});
});
describe('trimAndRecycle', function(): void {
it('should return correct element', function(): void {
const list = new CircularList<number[]>(5);
list.push([0]);
list.push([1]);
list.push([2]);
list.push([3]);
list.push([4]);
assert.equal(list.trimAndRecycle()[0], 0);
assert.equal(list.trimAndRecycle()[0], 1);
assert.equal(list.trimAndRecycle()[0], 2);
assert.equal(list.trimAndRecycle()[0], 3);
assert.equal(list.trimAndRecycle()[0], 4);
assert.equal(list.trimAndRecycle()[0], 0);
});
});
});
25 changes: 20 additions & 5 deletions src/common/CircularList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,21 @@ export class CircularList<T> extends EventEmitter implements ICircularList<T> {
}
}

/**
* Recycling trim.
* This is used to recycle buffer lines in Terminal.scroll when
* the list is at maxLength as a push replacement.
* Returns the old line as new one to be recycled.
* Note: There are no bound checks for performance reasons,
* the method is a special optimization for Terminal.scroll,
* do not use it anywhere else.
*/
public trimAndRecycle(): T | undefined {
this._startIndex = ++this._startIndex % this._maxLength;
this.emit('trim', 1);
return this._array[this._getCyclicIndex(this._length - 1)];
}

/**
* Removes and returns the last value on the list.
* @return The popped value.
Expand Down Expand Up @@ -136,10 +151,10 @@ export class CircularList<T> extends EventEmitter implements ICircularList<T> {
}

// Adjust length as needed
if (this._length + items.length > this.maxLength) {
const countToTrim = (this._length + items.length) - this.maxLength;
if (this._length + items.length > this._maxLength) {
const countToTrim = (this._length + items.length) - this._maxLength;
this._startIndex += countToTrim;
this._length = this.maxLength;
this._length = this._maxLength;
this.emit('trim', countToTrim);
} else {
this._length += items.length;
Expand Down Expand Up @@ -178,7 +193,7 @@ export class CircularList<T> extends EventEmitter implements ICircularList<T> {
const expandListBy = (start + count + offset) - this._length;
if (expandListBy > 0) {
this._length += expandListBy;
while (this._length > this.maxLength) {
while (this._length > this._maxLength) {
this._length--;
this._startIndex++;
this.emit('trim', 1);
Expand All @@ -198,6 +213,6 @@ export class CircularList<T> extends EventEmitter implements ICircularList<T> {
* @returns The cyclic index.
*/
private _getCyclicIndex(index: number): number {
return (this._startIndex + index) % this.maxLength;
return (this._startIndex + index) % this._maxLength;
}
}
1 change: 1 addition & 0 deletions src/common/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface ICircularList<T> extends IEventEmitter {
get(index: number): T | undefined;
set(index: number, value: T): void;
push(value: T): void;
trimAndRecycle(): T | undefined;
pop(): T | undefined;
splice(start: number, deleteCount: number, ...items: T[]): void;
trimStart(count: number): void;
Expand Down
2 changes: 2 additions & 0 deletions typings/xterm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ declare module 'xterm' {
*/
experimentalBufferLineImpl?: 'JsArray' | 'TypedArray';

experimentalPushRecycling?: boolean;

/**
* The font size used to render text.
*/
Expand Down

0 comments on commit c0c067a

Please sign in to comment.