mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #861 from Tyriar/791_buffer_refactor
Refactor buffer in preparation for true color and buffer performance improvements
This commit is contained in:
@@ -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):
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-6
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -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-
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
@@ -16,7 +16,7 @@ import { LineData } from './Types';
|
||||
class TestSelectionManager extends SelectionManager {
|
||||
constructor(
|
||||
terminal: ITerminal,
|
||||
buffer: ICircularList<LineData>,
|
||||
buffer: IBuffer,
|
||||
rowContainer: HTMLElement,
|
||||
charMeasure: CharMeasure
|
||||
) {
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
@@ -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]);
|
||||
@@ -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]);
|
||||
@@ -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],
|
||||
@@ -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]);
|
||||
@@ -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]);
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
+22
-27
@@ -7,10 +7,10 @@ import * as Browser from './utils/Browser';
|
||||
import { CharMeasure } from './utils/CharMeasure';
|
||||
import { CircularList } from './utils/CircularList';
|
||||
import { EventEmitter } from './EventEmitter';
|
||||
import { ITerminal, ICircularList, ISelectionManager } from './Interfaces';
|
||||
import { ITerminal, ICircularList, ISelectionManager, IBuffer } from './Interfaces';
|
||||
import { SelectionModel } from './SelectionModel';
|
||||
import { translateBufferLineToString } from './utils/BufferLine';
|
||||
import { LineData } from './Types';
|
||||
import { CHAR_DATA_WIDTH_INDEX } from './Buffer';
|
||||
|
||||
/**
|
||||
* The number of pixels the mouse needs to be above or below the viewport in
|
||||
@@ -34,11 +34,6 @@ const DRAG_SCROLL_INTERVAL = 50;
|
||||
*/
|
||||
const WORD_SEPARATORS = ' ()[]{}\'"';
|
||||
|
||||
// TODO: Move these constants elsewhere, they belong in a buffer or buffer
|
||||
// data/line class.
|
||||
const LINE_DATA_CHAR_INDEX = 1;
|
||||
const LINE_DATA_WIDTH_INDEX = 2;
|
||||
|
||||
const NON_BREAKING_SPACE_CHAR = String.fromCharCode(160);
|
||||
const ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g');
|
||||
|
||||
@@ -102,7 +97,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
|
||||
constructor(
|
||||
private _terminal: ITerminal,
|
||||
private _buffer: ICircularList<LineData>,
|
||||
private _buffer: IBuffer,
|
||||
private _rowContainer: HTMLElement,
|
||||
private _charMeasure: CharMeasure
|
||||
) {
|
||||
@@ -127,7 +122,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
// reverseIndex) and delete in a splice is only ever used when the same
|
||||
// number of elements was just added. Given this is could actually be
|
||||
// beneficial to leave the selection as is for these cases.
|
||||
this._buffer.on('trim', (amount: number) => this._onTrim(amount));
|
||||
this._buffer.lines.on('trim', (amount: number) => this._onTrim(amount));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +146,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
* switched in or out.
|
||||
* @param buffer The active buffer.
|
||||
*/
|
||||
public setBuffer(buffer: ICircularList<LineData>): void {
|
||||
public setBuffer(buffer: IBuffer): void {
|
||||
this._buffer = buffer;
|
||||
this.clearSelection();
|
||||
}
|
||||
@@ -184,12 +179,12 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
// Get first row
|
||||
const startRowEndCol = start[1] === end[1] ? end[0] : null;
|
||||
let result: string[] = [];
|
||||
result.push(translateBufferLineToString(this._buffer.get(start[1]), true, start[0], startRowEndCol));
|
||||
result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol));
|
||||
|
||||
// Get middle rows
|
||||
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
|
||||
const bufferLine = this._buffer.get(i);
|
||||
const lineText = translateBufferLineToString(bufferLine, true);
|
||||
const bufferLine = this._buffer.lines.get(i);
|
||||
const lineText = this._buffer.translateBufferLineToString(i, true);
|
||||
if ((<any>bufferLine).isWrapped) {
|
||||
result[result.length - 1] += lineText;
|
||||
} else {
|
||||
@@ -199,8 +194,8 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
|
||||
// Get final row
|
||||
if (start[1] !== end[1]) {
|
||||
const bufferLine = this._buffer.get(end[1]);
|
||||
const lineText = translateBufferLineToString(bufferLine, true, 0, end[0]);
|
||||
const bufferLine = this._buffer.lines.get(end[1]);
|
||||
const lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]);
|
||||
if ((<any>bufferLine).isWrapped) {
|
||||
result[result.length - 1] += lineText;
|
||||
} else {
|
||||
@@ -413,7 +408,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
this._model.selectionEnd = null;
|
||||
|
||||
// Ensure the line exists
|
||||
const line = this._buffer.get(this._model.selectionStart[1]);
|
||||
const line = this._buffer.lines.get(this._model.selectionStart[1]);
|
||||
if (!line) {
|
||||
return;
|
||||
}
|
||||
@@ -421,7 +416,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
// If the mouse is over the second half of a wide character, adjust the
|
||||
// selection to cover the whole character
|
||||
const char = line[this._model.selectionStart[0]];
|
||||
if (char[LINE_DATA_WIDTH_INDEX] === 0) {
|
||||
if (char[CHAR_DATA_WIDTH_INDEX] === 0) {
|
||||
this._model.selectionStart[0]++;
|
||||
}
|
||||
}
|
||||
@@ -493,9 +488,9 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
// If the character is a wide character include the cell to the right in the
|
||||
// selection. Note that selections at the very end of the line will never
|
||||
// have a character.
|
||||
if (this._model.selectionEnd[1] < this._buffer.length) {
|
||||
const char = this._buffer.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]];
|
||||
if (char && char[2] === 0) {
|
||||
if (this._model.selectionEnd[1] < this._buffer.lines.length) {
|
||||
const char = this._buffer.lines.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]];
|
||||
if (char && char[CHAR_DATA_WIDTH_INDEX] === 0) {
|
||||
this._model.selectionEnd[0]++;
|
||||
}
|
||||
}
|
||||
@@ -542,7 +537,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
let charIndex = coords[0];
|
||||
for (let i = 0; coords[0] >= i; i++) {
|
||||
const char = bufferLine[i];
|
||||
if (char[LINE_DATA_WIDTH_INDEX] === 0) {
|
||||
if (char[CHAR_DATA_WIDTH_INDEX] === 0) {
|
||||
charIndex--;
|
||||
}
|
||||
}
|
||||
@@ -562,12 +557,12 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
* @param coords The coordinates to get the word at.
|
||||
*/
|
||||
private _getWordAt(coords: [number, number]): IWordPosition {
|
||||
const bufferLine = this._buffer.get(coords[1]);
|
||||
const bufferLine = this._buffer.lines.get(coords[1]);
|
||||
if (!bufferLine) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const line = translateBufferLineToString(bufferLine, false);
|
||||
const line = this._buffer.translateBufferLineToString(coords[1], false);
|
||||
|
||||
// Get actual index, taking into consideration wide characters
|
||||
let endIndex = this._convertViewportColToCharacterIndex(bufferLine, coords);
|
||||
@@ -595,17 +590,17 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
let endCol = coords[0];
|
||||
// Consider the initial position, skip it and increment the wide char
|
||||
// variable
|
||||
if (bufferLine[startCol][LINE_DATA_WIDTH_INDEX] === 0) {
|
||||
if (bufferLine[startCol][CHAR_DATA_WIDTH_INDEX] === 0) {
|
||||
leftWideCharCount++;
|
||||
startCol--;
|
||||
}
|
||||
if (bufferLine[endCol][LINE_DATA_WIDTH_INDEX] === 2) {
|
||||
if (bufferLine[endCol][CHAR_DATA_WIDTH_INDEX] === 2) {
|
||||
rightWideCharCount++;
|
||||
endCol++;
|
||||
}
|
||||
// Expand the string in both directions until a space is hit
|
||||
while (startIndex > 0 && !this._isCharWordSeparator(line.charAt(startIndex - 1))) {
|
||||
if (bufferLine[startCol - 1][LINE_DATA_WIDTH_INDEX] === 0) {
|
||||
if (bufferLine[startCol - 1][CHAR_DATA_WIDTH_INDEX] === 0) {
|
||||
// If the next character is a wide char, record it and skip the column
|
||||
leftWideCharCount++;
|
||||
startCol--;
|
||||
@@ -614,7 +609,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
startCol--;
|
||||
}
|
||||
while (endIndex + 1 < line.length && !this._isCharWordSeparator(line.charAt(endIndex + 1))) {
|
||||
if (bufferLine[endCol + 1][LINE_DATA_WIDTH_INDEX] === 2) {
|
||||
if (bufferLine[endCol + 1][CHAR_DATA_WIDTH_INDEX] === 2) {
|
||||
// If the next character is a wide char, record it and skip the column
|
||||
rightWideCharCount++;
|
||||
endCol++;
|
||||
|
||||
+2
-3
@@ -28,7 +28,6 @@ import * as Browser from './utils/Browser';
|
||||
import * as Mouse from './utils/Mouse';
|
||||
import { CHARSETS } from './Charsets';
|
||||
import { getRawByteCoords } from './utils/Mouse';
|
||||
import { translateBufferLineToString } from './utils/BufferLine';
|
||||
import { CustomKeyEventHandler, Charset, LinkMatcherHandler, LinkMatcherValidationCallback, CharData, LineData, Option, StringOption, BooleanOption, StringArrayOption, NumberOption, GeometryOption, HandlerOption } from './Types';
|
||||
import { ITerminal, IBrowser, ITerminalOptions, IInputHandlingTerminal, ILinkMatcherOptions } from './Interfaces';
|
||||
|
||||
@@ -379,7 +378,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
|
||||
// Ensure the selection manager has the correct buffer
|
||||
if (this.selectionManager) {
|
||||
this.selectionManager.setBuffer(this.buffer.lines);
|
||||
this.selectionManager.setBuffer(this.buffer);
|
||||
}
|
||||
|
||||
this.setupStops();
|
||||
@@ -744,7 +743,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
|
||||
this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure);
|
||||
this.renderer = new Renderer(this);
|
||||
this.selectionManager = new SelectionManager(this, this.buffer.lines, this.rowContainer, this.charMeasure);
|
||||
this.selectionManager = new SelectionManager(this, this.buffer, this.rowContainer, this.charMeasure);
|
||||
this.selectionManager.on('refresh', data => {
|
||||
this.renderer.refreshSelection(data.start, data.end);
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ var fs = require('fs');
|
||||
var os = require('os');
|
||||
var pty = require('node-pty');
|
||||
var Terminal = require('../xterm');
|
||||
var CHAR_DATA_CHAR_INDEX = require('../Buffer').CHAR_DATA_CHAR_INDEX;
|
||||
|
||||
if (os.platform() === 'win32') {
|
||||
// Skip tests on Windows since pty.open isn't supported
|
||||
@@ -65,7 +66,7 @@ function terminalToString(term) {
|
||||
for (var line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) {
|
||||
line_s = '';
|
||||
for (var cell=0; cell<term.cols; ++cell) {
|
||||
line_s += term.buffer.lines.get(line)[cell][1];
|
||||
line_s += term.buffer.lines.get(line)[cell][CHAR_DATA_CHAR_INDEX];
|
||||
}
|
||||
// rtrim empty cells as xterm does
|
||||
line_s = line_s.replace(/\s+$/, '');
|
||||
|
||||
+119
-117
@@ -1,6 +1,8 @@
|
||||
var assert = require('chai').assert;
|
||||
var expect = require('chai').expect;
|
||||
var Terminal = require('../xterm');
|
||||
var CHAR_DATA_CHAR_INDEX = require('../Buffer').CHAR_DATA_CHAR_INDEX;
|
||||
var CHAR_DATA_WIDTH_INDEX = require('../Buffer').CHAR_DATA_WIDTH_INDEX;
|
||||
|
||||
describe('xterm.js', function() {
|
||||
var xterm;
|
||||
@@ -586,10 +588,10 @@ describe('xterm.js', function() {
|
||||
for (var i=0xDC00; i<=0xDCFF; ++i) {
|
||||
xterm.write(high + String.fromCharCode(i));
|
||||
var tchar = xterm.buffer.lines.get(0)[0];
|
||||
expect(tchar[1]).eql(high + String.fromCharCode(i));
|
||||
expect(tchar[1].length).eql(2);
|
||||
expect(tchar[2]).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[1][1]).eql(' ');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i));
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX]).eql(' ');
|
||||
xterm.reset();
|
||||
}
|
||||
});
|
||||
@@ -598,9 +600,9 @@ describe('xterm.js', function() {
|
||||
for (var i=0xDC00; i<=0xDCFF; ++i) {
|
||||
xterm.buffer.x = xterm.cols - 1;
|
||||
xterm.write(high + String.fromCharCode(i));
|
||||
expect(xterm.buffer.lines.get(0)[xterm.buffer.x-1][1]).eql(high + String.fromCharCode(i));
|
||||
expect(xterm.buffer.lines.get(0)[xterm.buffer.x-1][1].length).eql(2);
|
||||
expect(xterm.buffer.lines.get(1)[0][1]).eql(' ');
|
||||
expect(xterm.buffer.lines.get(0)[xterm.buffer.x-1][CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i));
|
||||
expect(xterm.buffer.lines.get(0)[xterm.buffer.x-1][CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(xterm.buffer.lines.get(1)[0][CHAR_DATA_CHAR_INDEX]).eql(' ');
|
||||
xterm.reset();
|
||||
}
|
||||
});
|
||||
@@ -610,10 +612,10 @@ describe('xterm.js', function() {
|
||||
xterm.buffer.x = xterm.cols - 1;
|
||||
xterm.wraparoundMode = true;
|
||||
xterm.write('a' + high + String.fromCharCode(i));
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][1]).eql('a');
|
||||
expect(xterm.buffer.lines.get(1)[0][1]).eql(high + String.fromCharCode(i));
|
||||
expect(xterm.buffer.lines.get(1)[0][1].length).eql(2);
|
||||
expect(xterm.buffer.lines.get(1)[1][1]).eql(' ');
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX]).eql('a');
|
||||
expect(xterm.buffer.lines.get(1)[0][CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i));
|
||||
expect(xterm.buffer.lines.get(1)[0][CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(xterm.buffer.lines.get(1)[1][CHAR_DATA_CHAR_INDEX]).eql(' ');
|
||||
xterm.reset();
|
||||
}
|
||||
});
|
||||
@@ -624,9 +626,9 @@ describe('xterm.js', function() {
|
||||
xterm.wraparoundMode = false;
|
||||
xterm.write('a' + high + String.fromCharCode(i));
|
||||
// auto wraparound mode should cut off the rest of the line
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][1]).eql('a');
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][1].length).eql(1);
|
||||
expect(xterm.buffer.lines.get(1)[1][1]).eql(' ');
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX]).eql('a');
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX].length).eql(1);
|
||||
expect(xterm.buffer.lines.get(1)[1][CHAR_DATA_CHAR_INDEX]).eql(' ');
|
||||
xterm.reset();
|
||||
}
|
||||
});
|
||||
@@ -636,10 +638,10 @@ describe('xterm.js', function() {
|
||||
xterm.write(high);
|
||||
xterm.write(String.fromCharCode(i));
|
||||
var tchar = xterm.buffer.lines.get(0)[0];
|
||||
expect(tchar[1]).eql(high + String.fromCharCode(i));
|
||||
expect(tchar[1].length).eql(2);
|
||||
expect(tchar[2]).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[1][1]).eql(' ');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i));
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX]).eql(' ');
|
||||
xterm.reset();
|
||||
}
|
||||
});
|
||||
@@ -648,47 +650,47 @@ describe('xterm.js', function() {
|
||||
describe('unicode - combining characters', function() {
|
||||
it('café', function () {
|
||||
xterm.write('cafe\u0301');
|
||||
expect(xterm.buffer.lines.get(0)[3][1]).eql('e\u0301');
|
||||
expect(xterm.buffer.lines.get(0)[3][1].length).eql(2);
|
||||
expect(xterm.buffer.lines.get(0)[3][2]).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[3][CHAR_DATA_CHAR_INDEX]).eql('e\u0301');
|
||||
expect(xterm.buffer.lines.get(0)[3][CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(xterm.buffer.lines.get(0)[3][CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
});
|
||||
it('café - end of line', function() {
|
||||
xterm.buffer.x = xterm.cols - 1 - 3;
|
||||
xterm.write('cafe\u0301');
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][1]).eql('e\u0301');
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][1].length).eql(2);
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][2]).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[1][1]).eql(' ');
|
||||
expect(xterm.buffer.lines.get(0)[1][1].length).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[1][2]).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX]).eql('e\u0301');
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX]).eql(' ');
|
||||
expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX].length).eql(1);
|
||||
expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
});
|
||||
it('multiple combined é', function() {
|
||||
xterm.wraparoundMode = true;
|
||||
xterm.write(Array(100).join('e\u0301'));
|
||||
for (var i=0; i<xterm.cols; ++i) {
|
||||
var tchar = xterm.buffer.lines.get(0)[i];
|
||||
expect(tchar[1]).eql('e\u0301');
|
||||
expect(tchar[1].length).eql(2);
|
||||
expect(tchar[2]).eql(1);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('e\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
}
|
||||
tchar = xterm.buffer.lines.get(1)[0];
|
||||
expect(tchar[1]).eql('e\u0301');
|
||||
expect(tchar[1].length).eql(2);
|
||||
expect(tchar[2]).eql(1);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('e\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
});
|
||||
it('multiple surrogate with combined', function() {
|
||||
xterm.wraparoundMode = true;
|
||||
xterm.write(Array(100).join('\uD800\uDC00\u0301'));
|
||||
for (var i=0; i<xterm.cols; ++i) {
|
||||
var tchar = xterm.buffer.lines.get(0)[i];
|
||||
expect(tchar[1]).eql('\uD800\uDC00\u0301');
|
||||
expect(tchar[1].length).eql(3);
|
||||
expect(tchar[2]).eql(1);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\uD800\uDC00\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
}
|
||||
tchar = xterm.buffer.lines.get(1)[0];
|
||||
expect(tchar[1]).eql('\uD800\uDC00\u0301');
|
||||
expect(tchar[1].length).eql(3);
|
||||
expect(tchar[2]).eql(1);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\uD800\uDC00\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -710,19 +712,19 @@ describe('xterm.js', function() {
|
||||
for (var i=0; i<xterm.cols; ++i) {
|
||||
var tchar = xterm.buffer.lines.get(0)[i];
|
||||
if (i % 2) {
|
||||
expect(tchar[1]).eql('');
|
||||
expect(tchar[1].length).eql(0);
|
||||
expect(tchar[2]).eql(0);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0);
|
||||
} else {
|
||||
expect(tchar[1]).eql('¥');
|
||||
expect(tchar[1].length).eql(1);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
}
|
||||
}
|
||||
tchar = xterm.buffer.lines.get(1)[0];
|
||||
expect(tchar[1]).eql('¥');
|
||||
expect(tchar[1].length).eql(1);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
});
|
||||
it('line of ¥ odd', function() {
|
||||
xterm.wraparoundMode = true;
|
||||
@@ -731,23 +733,23 @@ describe('xterm.js', function() {
|
||||
for (var i=1; i<xterm.cols-1; ++i) {
|
||||
var tchar = xterm.buffer.lines.get(0)[i];
|
||||
if (!(i % 2)) {
|
||||
expect(tchar[1]).eql('');
|
||||
expect(tchar[1].length).eql(0);
|
||||
expect(tchar[2]).eql(0);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0);
|
||||
} else {
|
||||
expect(tchar[1]).eql('¥');
|
||||
expect(tchar[1].length).eql(1);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
}
|
||||
}
|
||||
tchar = xterm.buffer.lines.get(0)[xterm.cols-1];
|
||||
expect(tchar[1]).eql(' ');
|
||||
expect(tchar[1].length).eql(1);
|
||||
expect(tchar[2]).eql(1);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(' ');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
tchar = xterm.buffer.lines.get(1)[0];
|
||||
expect(tchar[1]).eql('¥');
|
||||
expect(tchar[1].length).eql(1);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
});
|
||||
it('line of ¥ with combining odd', function() {
|
||||
xterm.wraparoundMode = true;
|
||||
@@ -756,23 +758,23 @@ describe('xterm.js', function() {
|
||||
for (var i=1; i<xterm.cols-1; ++i) {
|
||||
var tchar = xterm.buffer.lines.get(0)[i];
|
||||
if (!(i % 2)) {
|
||||
expect(tchar[1]).eql('');
|
||||
expect(tchar[1].length).eql(0);
|
||||
expect(tchar[2]).eql(0);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0);
|
||||
} else {
|
||||
expect(tchar[1]).eql('¥\u0301');
|
||||
expect(tchar[1].length).eql(2);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
}
|
||||
}
|
||||
tchar = xterm.buffer.lines.get(0)[xterm.cols-1];
|
||||
expect(tchar[1]).eql(' ');
|
||||
expect(tchar[1].length).eql(1);
|
||||
expect(tchar[2]).eql(1);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(' ');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
tchar = xterm.buffer.lines.get(1)[0];
|
||||
expect(tchar[1]).eql('¥\u0301');
|
||||
expect(tchar[1].length).eql(2);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
});
|
||||
it('line of ¥ with combining even', function() {
|
||||
xterm.wraparoundMode = true;
|
||||
@@ -780,19 +782,19 @@ describe('xterm.js', function() {
|
||||
for (var i=0; i<xterm.cols; ++i) {
|
||||
var tchar = xterm.buffer.lines.get(0)[i];
|
||||
if (i % 2) {
|
||||
expect(tchar[1]).eql('');
|
||||
expect(tchar[1].length).eql(0);
|
||||
expect(tchar[2]).eql(0);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0);
|
||||
} else {
|
||||
expect(tchar[1]).eql('¥\u0301');
|
||||
expect(tchar[1].length).eql(2);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
}
|
||||
}
|
||||
tchar = xterm.buffer.lines.get(1)[0];
|
||||
expect(tchar[1]).eql('¥\u0301');
|
||||
expect(tchar[1].length).eql(2);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
});
|
||||
it('line of surrogate fullwidth with combining odd', function() {
|
||||
xterm.wraparoundMode = true;
|
||||
@@ -801,23 +803,23 @@ describe('xterm.js', function() {
|
||||
for (var i=1; i<xterm.cols-1; ++i) {
|
||||
var tchar = xterm.buffer.lines.get(0)[i];
|
||||
if (!(i % 2)) {
|
||||
expect(tchar[1]).eql('');
|
||||
expect(tchar[1].length).eql(0);
|
||||
expect(tchar[2]).eql(0);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0);
|
||||
} else {
|
||||
expect(tchar[1]).eql('\ud843\ude6d\u0301');
|
||||
expect(tchar[1].length).eql(3);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
}
|
||||
}
|
||||
tchar = xterm.buffer.lines.get(0)[xterm.cols-1];
|
||||
expect(tchar[1]).eql(' ');
|
||||
expect(tchar[1].length).eql(1);
|
||||
expect(tchar[2]).eql(1);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(' ');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1);
|
||||
tchar = xterm.buffer.lines.get(1)[0];
|
||||
expect(tchar[1]).eql('\ud843\ude6d\u0301');
|
||||
expect(tchar[1].length).eql(3);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
});
|
||||
it('line of surrogate fullwidth with combining even', function() {
|
||||
xterm.wraparoundMode = true;
|
||||
@@ -825,19 +827,19 @@ describe('xterm.js', function() {
|
||||
for (var i=0; i<xterm.cols; ++i) {
|
||||
var tchar = xterm.buffer.lines.get(0)[i];
|
||||
if (i % 2) {
|
||||
expect(tchar[1]).eql('');
|
||||
expect(tchar[1].length).eql(0);
|
||||
expect(tchar[2]).eql(0);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0);
|
||||
} else {
|
||||
expect(tchar[1]).eql('\ud843\ude6d\u0301');
|
||||
expect(tchar[1].length).eql(3);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
}
|
||||
}
|
||||
tchar = xterm.buffer.lines.get(1)[0];
|
||||
expect(tchar[1]).eql('\ud843\ude6d\u0301');
|
||||
expect(tchar[1].length).eql(3);
|
||||
expect(tchar[2]).eql(2);
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301');
|
||||
expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3);
|
||||
expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -849,10 +851,10 @@ describe('xterm.js', function() {
|
||||
xterm.insertMode = true;
|
||||
xterm.write('abcde');
|
||||
expect(xterm.buffer.lines.get(0).length).eql(xterm.cols);
|
||||
expect(xterm.buffer.lines.get(0)[10][1]).eql('a');
|
||||
expect(xterm.buffer.lines.get(0)[14][1]).eql('e');
|
||||
expect(xterm.buffer.lines.get(0)[15][1]).eql('0');
|
||||
expect(xterm.buffer.lines.get(0)[79][1]).eql('4');
|
||||
expect(xterm.buffer.lines.get(0)[10][CHAR_DATA_CHAR_INDEX]).eql('a');
|
||||
expect(xterm.buffer.lines.get(0)[14][CHAR_DATA_CHAR_INDEX]).eql('e');
|
||||
expect(xterm.buffer.lines.get(0)[15][CHAR_DATA_CHAR_INDEX]).eql('0');
|
||||
expect(xterm.buffer.lines.get(0)[79][CHAR_DATA_CHAR_INDEX]).eql('4');
|
||||
});
|
||||
it('fullwidth - insert', function() {
|
||||
xterm.write(Array(9).join('0123456789').slice(-80));
|
||||
@@ -861,11 +863,11 @@ describe('xterm.js', function() {
|
||||
xterm.insertMode = true;
|
||||
xterm.write('¥¥¥');
|
||||
expect(xterm.buffer.lines.get(0).length).eql(xterm.cols);
|
||||
expect(xterm.buffer.lines.get(0)[10][1]).eql('¥');
|
||||
expect(xterm.buffer.lines.get(0)[11][1]).eql('');
|
||||
expect(xterm.buffer.lines.get(0)[14][1]).eql('¥');
|
||||
expect(xterm.buffer.lines.get(0)[15][1]).eql('');
|
||||
expect(xterm.buffer.lines.get(0)[79][1]).eql('3');
|
||||
expect(xterm.buffer.lines.get(0)[10][CHAR_DATA_CHAR_INDEX]).eql('¥');
|
||||
expect(xterm.buffer.lines.get(0)[11][CHAR_DATA_CHAR_INDEX]).eql('');
|
||||
expect(xterm.buffer.lines.get(0)[14][CHAR_DATA_CHAR_INDEX]).eql('¥');
|
||||
expect(xterm.buffer.lines.get(0)[15][CHAR_DATA_CHAR_INDEX]).eql('');
|
||||
expect(xterm.buffer.lines.get(0)[79][CHAR_DATA_CHAR_INDEX]).eql('3');
|
||||
});
|
||||
it('fullwidth - right border', function() {
|
||||
xterm.write(Array(41).join('¥'));
|
||||
@@ -874,14 +876,14 @@ describe('xterm.js', function() {
|
||||
xterm.insertMode = true;
|
||||
xterm.write('a');
|
||||
expect(xterm.buffer.lines.get(0).length).eql(xterm.cols);
|
||||
expect(xterm.buffer.lines.get(0)[10][1]).eql('a');
|
||||
expect(xterm.buffer.lines.get(0)[11][1]).eql('¥');
|
||||
expect(xterm.buffer.lines.get(0)[79][1]).eql(' '); // fullwidth char got replaced
|
||||
expect(xterm.buffer.lines.get(0)[10][CHAR_DATA_CHAR_INDEX]).eql('a');
|
||||
expect(xterm.buffer.lines.get(0)[11][CHAR_DATA_CHAR_INDEX]).eql('¥');
|
||||
expect(xterm.buffer.lines.get(0)[79][CHAR_DATA_CHAR_INDEX]).eql(' '); // fullwidth char got replaced
|
||||
xterm.write('b');
|
||||
expect(xterm.buffer.lines.get(0).length).eql(xterm.cols);
|
||||
expect(xterm.buffer.lines.get(0)[11][1]).eql('b');
|
||||
expect(xterm.buffer.lines.get(0)[12][1]).eql('¥');
|
||||
expect(xterm.buffer.lines.get(0)[79][1]).eql(''); // empty cell after fullwidth
|
||||
expect(xterm.buffer.lines.get(0)[11][CHAR_DATA_CHAR_INDEX]).eql('b');
|
||||
expect(xterm.buffer.lines.get(0)[12][CHAR_DATA_CHAR_INDEX]).eql('¥');
|
||||
expect(xterm.buffer.lines.get(0)[79][CHAR_DATA_CHAR_INDEX]).eql(''); // empty cell after fullwidth
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// TODO: This module should be merged into a buffer or buffer line class
|
||||
|
||||
const LINE_DATA_CHAR_INDEX = 1;
|
||||
const LINE_DATA_WIDTH_INDEX = 2;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export function translateBufferLineToString(line: any, trimRight: boolean, startCol: number = 0, endCol: number = null): string {
|
||||
// Get full line
|
||||
let lineString = '';
|
||||
let widthAdjustedStartCol = startCol;
|
||||
let widthAdjustedEndCol = endCol;
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const char = line[i];
|
||||
lineString += char[LINE_DATA_CHAR_INDEX];
|
||||
// Adjust start and end cols for wide characters if they affect their
|
||||
// column indexes
|
||||
if (char[LINE_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);
|
||||
}
|
||||
@@ -180,4 +180,7 @@ export class MockBuffer implements IBuffer {
|
||||
scrollTop: number;
|
||||
savedY: number;
|
||||
savedX: number;
|
||||
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user