Skip to content

Commit

Permalink
Merge pull request #861 from Tyriar/791_buffer_refactor
Browse files Browse the repository at this point in the history
Refactor buffer in preparation for true color and buffer performance improvements
  • Loading branch information
Tyriar authored Aug 7, 2017
2 parents 091854d + fd61c8e commit f52b43b
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 229 deletions.
51 changes: 51 additions & 0 deletions src/Buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { ITerminal, IBuffer } from './Interfaces';
import { CircularList } from './utils/CircularList';
import { LineData, CharData } from './Types';

export const CHAR_DATA_CHAR_INDEX = 1;
export const CHAR_DATA_WIDTH_INDEX = 2;

/**
* This class represents a terminal buffer (an internal state of the terminal), where the
* following information is stored (in high-level):
Expand Down Expand Up @@ -146,4 +149,52 @@ export class Buffer implements IBuffer {
this.scrollTop = 0;
this.scrollBottom = newRows - 1;
}

/**
* Translates a buffer line to a string, with optional start and end columns.
* Wide characters will count as two columns in the resulting string. This
* function is useful for getting the actual text underneath the raw selection
* position.
* @param line The line being translated.
* @param trimRight Whether to trim whitespace to the right.
* @param startCol The column to start at.
* @param endCol The column to end at.
*/
public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol: number = 0, endCol: number = null): string {
// Get full line
let lineString = '';
let widthAdjustedStartCol = startCol;
let widthAdjustedEndCol = endCol;
const line = this.lines.get(lineIndex);
for (let i = 0; i < line.length; i++) {
const char = line[i];
lineString += char[CHAR_DATA_CHAR_INDEX];
// Adjust start and end cols for wide characters if they affect their
// column indexes
if (char[CHAR_DATA_WIDTH_INDEX] === 0) {
if (startCol >= i) {
widthAdjustedStartCol--;
}
if (endCol >= i) {
widthAdjustedEndCol--;
}
}
}

// Calculate the final end col by trimming whitespace on the right of the
// line if needed.
let finalEndCol = widthAdjustedEndCol || line.length;
if (trimRight) {
const rightWhitespaceIndex = lineString.search(/\s+$/);
if (rightWhitespaceIndex !== -1) {
finalEndCol = Math.min(finalEndCol, rightWhitespaceIndex);
}
// Return the empty string if only trimmed whitespace is selected
if (finalEndCol <= widthAdjustedStartCol) {
return '';
}
}

return lineString.substring(widthAdjustedStartCol, finalEndCol);
}
}
14 changes: 8 additions & 6 deletions src/InputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IInputHandler, ITerminal, IInputHandlingTerminal } from './Interfaces';
import { C0 } from './EscapeSequences';
import { DEFAULT_CHARSET } from './Charsets';
import { CharData } from './Types';
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from './Buffer';

/**
* The terminal's standard implementation of IInputHandler, this handles all
Expand Down Expand Up @@ -34,14 +35,14 @@ export class InputHandler implements IInputHandler {
if (!ch_width && this._terminal.buffer.x) {
// dont overflow left
if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1]) {
if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][2]) {
if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][CHAR_DATA_WIDTH_INDEX]) {

// found empty cell after fullwidth, need to go 2 cells back
if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2])
this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][1] += char;
this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][CHAR_DATA_CHAR_INDEX] += char;

} else {
this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][1] += char;
this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][CHAR_DATA_CHAR_INDEX] += char;
}
this._terminal.updateRange(this._terminal.buffer.y);
}
Expand Down Expand Up @@ -77,9 +78,9 @@ export class InputHandler implements IInputHandler {
// remove last cell, if it's width is 0
// we have to adjust the second last cell as well
const removed = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).pop();
if (removed[2] === 0
if (removed[CHAR_DATA_WIDTH_INDEX] === 0
&& this._terminal.buffer.lines.get(row)[this._terminal.cols - 2]
&& this._terminal.buffer.lines.get(row)[this._terminal.cols - 2][2] === 2) {
&& this._terminal.buffer.lines.get(row)[this._terminal.cols - 2][CHAR_DATA_WIDTH_INDEX] === 2) {
this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] = [this._terminal.curAttr, ' ', 1];
}

Expand Down Expand Up @@ -945,6 +946,7 @@ export class InputHandler implements IInputHandler {
case 47: // alt screen buffer
case 1047: // alt screen buffer
this._terminal.buffers.activateAltBuffer();
this._terminal.selectionManager.setBuffer(this._terminal.buffer);
this._terminal.viewport.syncScrollArea();
this._terminal.showCursor();
break;
Expand Down Expand Up @@ -1113,7 +1115,7 @@ export class InputHandler implements IInputHandler {
// if (params[0] === 1049) {
// this.restoreCursor(params);
// }
this._terminal.selectionManager.setBuffer(this._terminal.buffer.lines);
this._terminal.selectionManager.setBuffer(this._terminal.buffer);
this._terminal.refresh(0, this._terminal.rows - 1);
this._terminal.viewport.syncScrollArea();
this._terminal.showCursor();
Expand Down
3 changes: 2 additions & 1 deletion src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export interface IBuffer {
scrollTop: number;
savedY: number;
savedX: number;
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string;
}

export interface IBufferSet {
Expand All @@ -166,7 +167,7 @@ export interface ISelectionManager {

disable(): void;
enable(): void;
setBuffer(buffer: ICircularList<LineData>): void;
setBuffer(buffer: IBuffer): void;
setSelection(row: number, col: number, length: number): void;
}

Expand Down
5 changes: 3 additions & 2 deletions src/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { ITerminal } from './Interfaces';
import { DomElementObjectPool } from './utils/DomElementObjectPool';
import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer';

/**
* The maximum number of refresh frames to skip when the write buffer is non-
Expand Down Expand Up @@ -168,8 +169,8 @@ export class Renderer {
for (let i = 0; i < width; i++) {
// TODO: Could data be a more specific type?
let data: any = line[i][0];
const ch = line[i][1];
const ch_width: any = line[i][2];
const ch = line[i][CHAR_DATA_CHAR_INDEX];
const ch_width: any = line[i][CHAR_DATA_WIDTH_INDEX];
const isCursor: boolean = i === x;
if (!ch_width) {
continue;
Expand Down
34 changes: 17 additions & 17 deletions src/SelectionManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import jsdom = require('jsdom');
import { assert } from 'chai';
import { ITerminal, ICircularList } from './Interfaces';
import { ITerminal, ICircularList, IBuffer } from './Interfaces';
import { CharMeasure } from './utils/CharMeasure';
import { CircularList } from './utils/CircularList';
import { SelectionManager } from './SelectionManager';
Expand All @@ -16,7 +16,7 @@ import { LineData } from './Types';
class TestSelectionManager extends SelectionManager {
constructor(
terminal: ITerminal,
buffer: ICircularList<LineData>,
buffer: IBuffer,
rowContainer: HTMLElement,
charMeasure: CharMeasure
) {
Expand All @@ -40,7 +40,7 @@ describe('SelectionManager', () => {
let document: Document;

let terminal: ITerminal;
let bufferLines: ICircularList<LineData>;
let buffer: IBuffer;
let rowContainer: HTMLElement;
let selectionManager: TestSelectionManager;

Expand All @@ -55,8 +55,8 @@ describe('SelectionManager', () => {
terminal.options.scrollback = 100;
terminal.buffers = new BufferSet(terminal);
terminal.buffer = terminal.buffers.active;
bufferLines = terminal.buffer.lines;
selectionManager = new TestSelectionManager(terminal, bufferLines, rowContainer, null);
buffer = terminal.buffer;
selectionManager = new TestSelectionManager(terminal, buffer, rowContainer, null);
});

function stringToRow(text: string): LineData {
Expand All @@ -69,7 +69,7 @@ describe('SelectionManager', () => {

describe('_selectWordAt', () => {
it('should expand selection for normal width chars', () => {
bufferLines.set(0, stringToRow('foo bar'));
buffer.lines.set(0, stringToRow('foo bar'));
selectionManager.selectWordAt([0, 0]);
assert.equal(selectionManager.selectionText, 'foo');
selectionManager.selectWordAt([1, 0]);
Expand All @@ -86,7 +86,7 @@ describe('SelectionManager', () => {
assert.equal(selectionManager.selectionText, 'bar');
});
it('should expand selection for whitespace', () => {
bufferLines.set(0, stringToRow('a b'));
buffer.lines.set(0, stringToRow('a b'));
selectionManager.selectWordAt([0, 0]);
assert.equal(selectionManager.selectionText, 'a');
selectionManager.selectWordAt([1, 0]);
Expand All @@ -100,7 +100,7 @@ describe('SelectionManager', () => {
});
it('should expand selection for wide characters', () => {
// Wide characters use a special format
bufferLines.set(0, [
buffer.lines.set(0, [
[null, '中', 2],
[null, '', 0],
[null, '文', 2],
Expand Down Expand Up @@ -152,7 +152,7 @@ describe('SelectionManager', () => {
assert.equal(selectionManager.selectionText, 'foo');
});
it('should select up to non-path characters that are commonly adjacent to paths', () => {
bufferLines.set(0, stringToRow('(cd)[ef]{gh}\'ij"'));
buffer.lines.set(0, stringToRow('(cd)[ef]{gh}\'ij"'));
selectionManager.selectWordAt([0, 0]);
assert.equal(selectionManager.selectionText, '(cd');
selectionManager.selectWordAt([1, 0]);
Expand Down Expand Up @@ -190,7 +190,7 @@ describe('SelectionManager', () => {

describe('_selectLineAt', () => {
it('should select the entire line', () => {
bufferLines.set(0, stringToRow('foo bar'));
buffer.lines.set(0, stringToRow('foo bar'));
selectionManager.selectLineAt(0);
assert.equal(selectionManager.selectionText, 'foo bar', 'The selected text is correct');
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]);
Expand All @@ -200,14 +200,14 @@ describe('SelectionManager', () => {

describe('selectAll', () => {
it('should select the entire buffer, beyond the viewport', () => {
bufferLines.length = 5;
bufferLines.set(0, stringToRow('1'));
bufferLines.set(1, stringToRow('2'));
bufferLines.set(2, stringToRow('3'));
bufferLines.set(3, stringToRow('4'));
bufferLines.set(4, stringToRow('5'));
buffer.lines.length = 5;
buffer.lines.set(0, stringToRow('1'));
buffer.lines.set(1, stringToRow('2'));
buffer.lines.set(2, stringToRow('3'));
buffer.lines.set(3, stringToRow('4'));
buffer.lines.set(4, stringToRow('5'));
selectionManager.selectAll();
terminal.buffer.ybase = bufferLines.length - terminal.rows;
terminal.buffer.ybase = buffer.lines.length - terminal.rows;
assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5');
});
});
Expand Down
Loading

0 comments on commit f52b43b

Please sign in to comment.