mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into 1737_inverse_dynamic_cache
This commit is contained in:
+6
-2
@@ -39,6 +39,7 @@ export class Buffer implements IBuffer {
|
||||
public tabs: any;
|
||||
public savedY: number;
|
||||
public savedX: number;
|
||||
public savedCurAttr: number;
|
||||
public markers: Marker[] = [];
|
||||
private _bufferLineConstructor: IBufferLineConstructor;
|
||||
|
||||
@@ -114,11 +115,14 @@ export class Buffer implements IBuffer {
|
||||
/**
|
||||
* Fills the buffer's viewport with blank lines.
|
||||
*/
|
||||
public fillViewportRows(): void {
|
||||
public fillViewportRows(fillAttr?: number): void {
|
||||
if (this.lines.length === 0) {
|
||||
if (fillAttr === undefined) {
|
||||
fillAttr = DEFAULT_ATTR;
|
||||
}
|
||||
let i = this._terminal.rows;
|
||||
while (i--) {
|
||||
this.lines.push(this.getBlankLine(DEFAULT_ATTR));
|
||||
this.lines.push(this.getBlankLine(fillAttr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,4 +48,30 @@ describe('BufferSet', () => {
|
||||
assert.equal(bufferSet.active, bufferSet.alt);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cursor handling when swapping buffers', () => {
|
||||
beforeEach(() => {
|
||||
bufferSet.normal.x = 0;
|
||||
bufferSet.normal.y = 0;
|
||||
bufferSet.alt.x = 0;
|
||||
bufferSet.alt.y = 0;
|
||||
});
|
||||
|
||||
it('should keep the cursor stationary when activating alt buffer', () => {
|
||||
bufferSet.activateNormalBuffer();
|
||||
bufferSet.active.x = 30;
|
||||
bufferSet.active.y = 10;
|
||||
bufferSet.activateAltBuffer();
|
||||
assert.equal(bufferSet.active.x, 30);
|
||||
assert.equal(bufferSet.active.y, 10);
|
||||
});
|
||||
it('should keep the cursor stationary when activating normal buffer', () => {
|
||||
bufferSet.activateAltBuffer();
|
||||
bufferSet.active.x = 30;
|
||||
bufferSet.active.y = 10;
|
||||
bufferSet.activateNormalBuffer();
|
||||
assert.equal(bufferSet.active.x, 30);
|
||||
assert.equal(bufferSet.active.y, 10);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+6
-2
@@ -61,6 +61,8 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
if (this._activeBuffer === this._normal) {
|
||||
return;
|
||||
}
|
||||
this._normal.x = this._alt.x;
|
||||
this._normal.y = this._alt.y;
|
||||
// The alt buffer should always be cleared when we switch to the normal
|
||||
// buffer. This frees up memory since the alt buffer should always be new
|
||||
// when activated.
|
||||
@@ -75,13 +77,15 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
/**
|
||||
* Sets the alt Buffer of the BufferSet as its currently active Buffer
|
||||
*/
|
||||
public activateAltBuffer(): void {
|
||||
public activateAltBuffer(fillAttr?: number): void {
|
||||
if (this._activeBuffer === this._alt) {
|
||||
return;
|
||||
}
|
||||
// Since the alt buffer is always cleared when the normal buffer is
|
||||
// activated, we want to fill it when switching to it.
|
||||
this._alt.fillViewportRows();
|
||||
this._alt.fillViewportRows(fillAttr);
|
||||
this._alt.x = this._normal.x;
|
||||
this._alt.y = this._normal.y;
|
||||
this._activeBuffer = this._alt;
|
||||
this.emit('activate', {
|
||||
activeBuffer: this._alt,
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { fill } from './core/TypedArrayUtils';
|
||||
import { fill } from './common/TypedArrayUtils';
|
||||
|
||||
export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: number) => number {
|
||||
// extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { assert, expect } from 'chai';
|
||||
import { InputHandler } from './InputHandler';
|
||||
import { MockInputHandlingTerminal } from './utils/TestUtils.test';
|
||||
import { NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, CHAR_DATA_CHAR_INDEX } from './Buffer';
|
||||
import { NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, CHAR_DATA_CHAR_INDEX, CHAR_DATA_ATTR_INDEX, DEFAULT_ATTR } from './Buffer';
|
||||
import { Terminal } from './Terminal';
|
||||
import { IBufferLine } from './Types';
|
||||
|
||||
@@ -506,4 +506,65 @@ describe('InputHandler', () => {
|
||||
inputHandler.print(String.fromCharCode(0x200B), 0, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('alt screen', () => {
|
||||
let term: Terminal;
|
||||
let handler: InputHandler;
|
||||
|
||||
function lineContent(line: IBufferLine): string {
|
||||
let content = '';
|
||||
for (let i = 0; i < line.length; ++i) content += line.get(i)[CHAR_DATA_CHAR_INDEX];
|
||||
return content;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
term = new Terminal();
|
||||
handler = new InputHandler(term);
|
||||
});
|
||||
it('should handle DECSET/DECRST 47 (alt screen buffer)', () => {
|
||||
handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST');
|
||||
expect(lineContent(term.buffer.lines.get(0))).to.equal(Array(term.cols + 1).join(' '));
|
||||
expect(lineContent(term.buffer.lines.get(1))).to.equal(' TEST' + Array(term.cols - 7).join(' '));
|
||||
// Text color of 'TEST' should be red
|
||||
expect((term.buffer.lines.get(1).get(4)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
|
||||
handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
|
||||
expect(lineContent(term.buffer.lines.get(0))).to.equal(Array(term.cols + 1).join(' '));
|
||||
expect(lineContent(term.buffer.lines.get(1))).to.equal(' TEST' + Array(term.cols - 7).join(' '));
|
||||
// Text color of 'TEST' should be red
|
||||
expect((term.buffer.lines.get(1).get(4)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
|
||||
handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
|
||||
expect(lineContent(term.buffer.lines.get(0))).to.equal('TEST' + Array(term.cols - 3).join(' '));
|
||||
expect(lineContent(term.buffer.lines.get(1))).to.equal('JUNK' + Array(term.cols - 3).join(' '));
|
||||
// Text color of 'TEST' should be default
|
||||
expect(term.buffer.lines.get(0).get(0)[CHAR_DATA_ATTR_INDEX]).to.equal(DEFAULT_ATTR);
|
||||
// Text color of 'JUNK' should be red
|
||||
expect((term.buffer.lines.get(1).get(0)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
|
||||
handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
|
||||
expect(lineContent(term.buffer.lines.get(0))).to.equal('TEST' + Array(term.cols - 3).join(' '));
|
||||
expect(lineContent(term.buffer.lines.get(1))).to.equal(Array(term.cols + 1).join(' '));
|
||||
// Text color of 'TEST' should be default
|
||||
expect(term.buffer.lines.get(0).get(0)[CHAR_DATA_ATTR_INDEX]).to.equal(DEFAULT_ATTR);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => {
|
||||
handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST');
|
||||
expect(lineContent(term.buffer.lines.get(0))).to.equal('TEST' + Array(term.cols - 3).join(' '));
|
||||
// Text color of 'TEST' should be default
|
||||
expect(term.buffer.lines.get(0).get(0)[CHAR_DATA_ATTR_INDEX]).to.equal(DEFAULT_ATTR);
|
||||
handler.parse('\x1b[?1049h\x1b[uTEST');
|
||||
expect(lineContent(term.buffer.lines.get(1))).to.equal('TEST' + Array(term.cols - 3).join(' '));
|
||||
// Text color of 'TEST' should be red
|
||||
expect((term.buffer.lines.get(1).get(0)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => {
|
||||
handler.parse('\x1b[42m\x1b[?1049h');
|
||||
// Buffer should be filled with green background
|
||||
expect(term.buffer.lines.get(20).get(10)[CHAR_DATA_ATTR_INDEX] & 0x1ff).to.equal(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+29
-14
@@ -1284,7 +1284,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
case 66:
|
||||
this._terminal.log('Serial port requested application keypad.');
|
||||
this._terminal.applicationKeypad = true;
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
if (this._terminal.viewport) {
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
}
|
||||
break;
|
||||
case 9: // X10 Mouse
|
||||
// no release, no motion, no wheel, no modifiers.
|
||||
@@ -1333,14 +1335,19 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
case 25: // show cursor
|
||||
this._terminal.cursorHidden = false;
|
||||
break;
|
||||
case 1048: // alt screen cursor
|
||||
this.saveCursor(params);
|
||||
break;
|
||||
case 1049: // alt screen buffer cursor
|
||||
// TODO: Not sure if we need to save/restore after switching the buffer
|
||||
// this.saveCursor(params);
|
||||
this.saveCursor(params);
|
||||
// FALL-THROUGH
|
||||
case 47: // alt screen buffer
|
||||
case 1047: // alt screen buffer
|
||||
this._terminal.buffers.activateAltBuffer();
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
this._terminal.buffers.activateAltBuffer(this._terminal.eraseAttr());
|
||||
this._terminal.refresh(0, this._terminal.rows - 1);
|
||||
if (this._terminal.viewport) {
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
}
|
||||
this._terminal.showCursor();
|
||||
break;
|
||||
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
|
||||
@@ -1473,7 +1480,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
case 66:
|
||||
this._terminal.log('Switching back to normal keypad.');
|
||||
this._terminal.applicationKeypad = false;
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
if (this._terminal.viewport) {
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
}
|
||||
break;
|
||||
case 9: // X10 Mouse
|
||||
case 1000: // vt200 mouse
|
||||
@@ -1501,18 +1510,22 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
case 25: // hide cursor
|
||||
this._terminal.cursorHidden = true;
|
||||
break;
|
||||
case 1048: // alt screen cursor
|
||||
this.restoreCursor(params);
|
||||
break;
|
||||
case 1049: // alt screen buffer cursor
|
||||
// FALL-THROUGH
|
||||
case 47: // normal screen buffer
|
||||
case 1047: // normal screen buffer - clearing it first
|
||||
// Ensure the selection manager has the correct buffer
|
||||
this._terminal.buffers.activateNormalBuffer();
|
||||
// TODO: Not sure if we need to save/restore after switching the buffer
|
||||
// if (params[0] === 1049) {
|
||||
// this.restoreCursor(params);
|
||||
// }
|
||||
if (params[0] === 1049) {
|
||||
this.restoreCursor(params);
|
||||
}
|
||||
this._terminal.refresh(0, this._terminal.rows - 1);
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
if (this._terminal.viewport) {
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
}
|
||||
this._terminal.showCursor();
|
||||
break;
|
||||
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
|
||||
@@ -1791,7 +1804,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._terminal.originMode = false;
|
||||
this._terminal.wraparoundMode = true; // defaults: xterm - true, vt100 - false
|
||||
this._terminal.applicationKeypad = false; // ?
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
if (this._terminal.viewport) {
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
}
|
||||
this._terminal.applicationCursor = false;
|
||||
this._terminal.buffer.scrollTop = 0;
|
||||
this._terminal.buffer.scrollBottom = this._terminal.rows - 1;
|
||||
@@ -1858,7 +1873,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public saveCursor(params: number[]): void {
|
||||
this._terminal.buffer.savedX = this._terminal.buffer.x;
|
||||
this._terminal.buffer.savedY = this._terminal.buffer.y;
|
||||
this._terminal.savedCurAttr = this._terminal.curAttr;
|
||||
this._terminal.buffer.savedCurAttr = this._terminal.curAttr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1870,7 +1885,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public restoreCursor(params: number[]): void {
|
||||
this._terminal.buffer.x = this._terminal.buffer.savedX || 0;
|
||||
this._terminal.buffer.y = this._terminal.buffer.savedY || 0;
|
||||
this._terminal.curAttr = this._terminal.savedCurAttr || DEFAULT_ATTR;
|
||||
this._terminal.curAttr = this._terminal.buffer.savedCurAttr || DEFAULT_ATTR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -347,7 +347,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
* @param event The mouse event.
|
||||
*/
|
||||
private _getMouseBufferCoords(event: MouseEvent): [number, number] {
|
||||
const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, true);
|
||||
const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._charMeasure, this._terminal.cols, this._terminal.rows, true);
|
||||
if (!coords) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+2
-3
@@ -170,7 +170,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
public savedCols: number;
|
||||
|
||||
public curAttr: number;
|
||||
public savedCurAttr: number;
|
||||
|
||||
public params: (string | number)[];
|
||||
public currentParam: string | number;
|
||||
@@ -808,7 +807,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
button = getButton(ev);
|
||||
|
||||
// get mouse coordinates
|
||||
pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.options.lineHeight, self.cols, self.rows);
|
||||
pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.cols, self.rows);
|
||||
if (!pos) return;
|
||||
|
||||
sendEvent(button, pos);
|
||||
@@ -834,7 +833,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
// ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
|
||||
function sendMove(ev: MouseEvent): void {
|
||||
let button = pressed;
|
||||
const pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.options.lineHeight, self.cols, self.rows);
|
||||
const pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.cols, self.rows);
|
||||
if (!pos) return;
|
||||
|
||||
// buttons marked as motions
|
||||
|
||||
+4
-4
@@ -49,7 +49,6 @@ export interface IInputHandlingTerminal extends IEventEmitter {
|
||||
wraparoundMode: boolean;
|
||||
bracketedPasteMode: boolean;
|
||||
curAttr: number;
|
||||
savedCurAttr: number;
|
||||
savedCols: number;
|
||||
x10Mouse: boolean;
|
||||
vt200Mouse: boolean;
|
||||
@@ -246,8 +245,8 @@ export interface ILinkifierAccessor {
|
||||
}
|
||||
|
||||
export interface IMouseHelper {
|
||||
getCoords(event: { pageX: number, pageY: number }, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number];
|
||||
getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number };
|
||||
getCoords(event: { pageX: number, pageY: number }, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number, isSelection?: boolean): [number, number];
|
||||
getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number): { x: number, y: number };
|
||||
}
|
||||
|
||||
export interface ICharMeasure {
|
||||
@@ -290,6 +289,7 @@ export interface IBuffer {
|
||||
hasScrollback: boolean;
|
||||
savedY: number;
|
||||
savedX: number;
|
||||
savedCurAttr: number;
|
||||
isCursorInViewport: boolean;
|
||||
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string;
|
||||
getWrappedRangeForLine(y: number): { first: number, last: number };
|
||||
@@ -306,7 +306,7 @@ export interface IBufferSet extends IEventEmitter {
|
||||
active: IBuffer;
|
||||
|
||||
activateNormalBuffer(): void;
|
||||
activateAltBuffer(): void;
|
||||
activateAltBuffer(fillAttr?: number): void;
|
||||
}
|
||||
|
||||
export interface ISelectionManager {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
import { assert } from 'chai';
|
||||
import { fillFallback } from './TypedArrayUtils';
|
||||
|
||||
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray
|
||||
| Int8Array | Int16Array | Int32Array
|
||||
| Float32Array | Float64Array;
|
||||
|
||||
describe('polyfill conformance tests', function(): void {
|
||||
|
||||
function deepEquals(a: TypedArray, b: TypedArray): void {
|
||||
assert.equal(a.length, b.length);
|
||||
for (let i = 0; i < a.length; ++i) {
|
||||
assert.equal(a[i], b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
describe('TypedArray.fill', function(): void {
|
||||
it('should work with all typed array types', function(): void {
|
||||
const u81 = new Uint8Array(5);
|
||||
const u82 = new Uint8Array(5);
|
||||
deepEquals(fillFallback(u81, 2), u82.fill(2));
|
||||
deepEquals(fillFallback(u81, -1), u82.fill(-1));
|
||||
const u161 = new Uint16Array(5);
|
||||
const u162 = new Uint16Array(5);
|
||||
deepEquals(fillFallback(u161, 2), u162.fill(2));
|
||||
deepEquals(fillFallback(u161, 65535), u162.fill(65535));
|
||||
deepEquals(fillFallback(u161, -1), u162.fill(-1));
|
||||
const u321 = new Uint32Array(5);
|
||||
const u322 = new Uint32Array(5);
|
||||
deepEquals(fillFallback(u321, 2), u322.fill(2));
|
||||
deepEquals(fillFallback(u321, 65537), u322.fill(65537));
|
||||
deepEquals(fillFallback(u321, -1), u322.fill(-1));
|
||||
const i81 = new Int8Array(5);
|
||||
const i82 = new Int8Array(5);
|
||||
deepEquals(fillFallback(i81, 2), i82.fill(2));
|
||||
deepEquals(fillFallback(i81, -1), i82.fill(-1));
|
||||
const i161 = new Int16Array(5);
|
||||
const i162 = new Int16Array(5);
|
||||
deepEquals(fillFallback(i161, 2), i162.fill(2));
|
||||
deepEquals(fillFallback(i161, 65535), i162.fill(65535));
|
||||
deepEquals(fillFallback(i161, -1), i162.fill(-1));
|
||||
const i321 = new Int32Array(5);
|
||||
const i322 = new Int32Array(5);
|
||||
deepEquals(fillFallback(i321, 2), i322.fill(2));
|
||||
deepEquals(fillFallback(i321, 65537), i322.fill(65537));
|
||||
deepEquals(fillFallback(i321, -1), i322.fill(-1));
|
||||
const f321 = new Float32Array(5);
|
||||
const f322 = new Float32Array(5);
|
||||
deepEquals(fillFallback(f321, 1.2345), f322.fill(1.2345));
|
||||
const f641 = new Float64Array(5);
|
||||
const f642 = new Float64Array(5);
|
||||
deepEquals(fillFallback(f641, 1.2345), f642.fill(1.2345));
|
||||
const u8Clamped1 = new Uint8ClampedArray(5);
|
||||
const u8Clamped2 = new Uint8ClampedArray(5);
|
||||
deepEquals(fillFallback(u8Clamped1, 2), u8Clamped2.fill(2));
|
||||
deepEquals(fillFallback(u8Clamped1, 257), u8Clamped2.fill(257));
|
||||
});
|
||||
it('start offset', function(): void {
|
||||
for (let i = -2; i < 10; ++i) {
|
||||
const u81 = new Uint8Array(5);
|
||||
const u83 = new Uint8Array(5);
|
||||
deepEquals(fillFallback(u81, 2, i), u83.fill(2, i));
|
||||
deepEquals(fillFallback(u81, -1, i), u83.fill(-1, i));
|
||||
}
|
||||
});
|
||||
it('end offset', function(): void {
|
||||
for (let i = -2; i < 10; ++i) {
|
||||
const u81 = new Uint8Array(5);
|
||||
const u83 = new Uint8Array(5);
|
||||
deepEquals(fillFallback(u81, 2, 0, i), u83.fill(2, 0, i));
|
||||
deepEquals(fillFallback(u81, -1, 0, i), u83.fill(-1, 0, i));
|
||||
}
|
||||
});
|
||||
it('start/end offset', function(): void {
|
||||
for (let i = -2; i < 10; ++i) {
|
||||
for (let j = -2; j < 10; ++j) {
|
||||
const u81 = new Uint8Array(5);
|
||||
const u83 = new Uint8Array(5);
|
||||
deepEquals(fillFallback(u81, 2, i, j), u83.fill(2, i, j));
|
||||
deepEquals(fillFallback(u81, -1, i, j), u83.fill(-1, i, j));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -12,11 +12,15 @@ type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray
|
||||
| Int8Array | Int16Array | Int32Array
|
||||
| Float32Array | Float64Array;
|
||||
|
||||
export function fill(array: TypedArray, value: number, start: number = 0, end?: number | undefined): TypedArray {
|
||||
export function fill<T extends TypedArray>(array: T, value: number, start?: number, end?: number): T {
|
||||
// all modern engines that support .fill
|
||||
if (array.fill) {
|
||||
return array.fill(value, start, end);
|
||||
return array.fill(value, start, end) as T;
|
||||
}
|
||||
return fillFallback(array, value, start, end);
|
||||
}
|
||||
|
||||
export function fillFallback<T extends TypedArray>(array: T, value: number, start: number = 0, end: number = array.length): T {
|
||||
// safari and IE 11
|
||||
// since IE 11 does not support Array.prototype.fill either
|
||||
// we cannot use the suggested polyfill from MDN
|
||||
@@ -25,9 +29,6 @@ export function fill(array: TypedArray, value: number, start: number = 0, end?:
|
||||
return array;
|
||||
}
|
||||
start = (array.length + start) % array.length;
|
||||
if (end === undefined) {
|
||||
end = array.length;
|
||||
}
|
||||
if (end >= array.length) {
|
||||
end = array.length;
|
||||
} else {
|
||||
@@ -1,158 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
import { assert } from 'chai';
|
||||
import { fill } from './TypedArrayUtils';
|
||||
|
||||
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray
|
||||
| Int8Array | Int16Array | Int32Array
|
||||
| Float32Array | Float64Array;
|
||||
|
||||
// we explicitly test against the looping version in the test cases
|
||||
function loopFill(array: TypedArray, value: number, start: number = 0, end?: number | undefined): TypedArray {
|
||||
if (start >= array.length) {
|
||||
return array;
|
||||
}
|
||||
start = (array.length + start) % array.length;
|
||||
if (end === undefined) {
|
||||
end = array.length;
|
||||
}
|
||||
if (end >= array.length) {
|
||||
end = array.length;
|
||||
} else {
|
||||
end = (array.length + end) % array.length;
|
||||
}
|
||||
for (let i = start; i < end; ++i) {
|
||||
array[i] = value;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
describe('polyfill conformance tests', function(): void {
|
||||
|
||||
function deepEquals(a: TypedArray, b: TypedArray): void {
|
||||
assert.equal(a.length, b.length);
|
||||
for (let i = 0; i < a.length; ++i) {
|
||||
assert.equal(a[i], b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
describe('TypedArray.fill', function(): void {
|
||||
it('should work with all typed array types', function(): void {
|
||||
const u81 = new Uint8Array(5);
|
||||
const u82 = new Uint8Array(5);
|
||||
deepEquals(fill(u81, 2), u82.fill(2));
|
||||
deepEquals(fill(u81, -1), u82.fill(-1));
|
||||
const u161 = new Uint16Array(5);
|
||||
const u162 = new Uint16Array(5);
|
||||
deepEquals(fill(u161, 2), u162.fill(2));
|
||||
deepEquals(fill(u161, 65535), u162.fill(65535));
|
||||
deepEquals(fill(u161, -1), u162.fill(-1));
|
||||
const u321 = new Uint32Array(5);
|
||||
const u322 = new Uint32Array(5);
|
||||
deepEquals(fill(u321, 2), u322.fill(2));
|
||||
deepEquals(fill(u321, 65537), u322.fill(65537));
|
||||
deepEquals(fill(u321, -1), u322.fill(-1));
|
||||
const i81 = new Int8Array(5);
|
||||
const i82 = new Int8Array(5);
|
||||
deepEquals(fill(i81, 2), i82.fill(2));
|
||||
deepEquals(fill(i81, -1), i82.fill(-1));
|
||||
const i161 = new Int16Array(5);
|
||||
const i162 = new Int16Array(5);
|
||||
deepEquals(fill(i161, 2), i162.fill(2));
|
||||
deepEquals(fill(i161, 65535), i162.fill(65535));
|
||||
deepEquals(fill(i161, -1), i162.fill(-1));
|
||||
const i321 = new Int32Array(5);
|
||||
const i322 = new Int32Array(5);
|
||||
deepEquals(fill(i321, 2), i322.fill(2));
|
||||
deepEquals(fill(i321, 65537), i322.fill(65537));
|
||||
deepEquals(fill(i321, -1), i322.fill(-1));
|
||||
const f321 = new Float32Array(5);
|
||||
const f322 = new Float32Array(5);
|
||||
deepEquals(fill(f321, 1.2345), f322.fill(1.2345));
|
||||
const f641 = new Float64Array(5);
|
||||
const f642 = new Float64Array(5);
|
||||
deepEquals(fill(f641, 1.2345), f642.fill(1.2345));
|
||||
const u8Clamped1 = new Uint8ClampedArray(5);
|
||||
const u8Clamped2 = new Uint8ClampedArray(5);
|
||||
deepEquals(fill(u8Clamped1, 2), u8Clamped2.fill(2));
|
||||
deepEquals(fill(u8Clamped1, 257), u8Clamped2.fill(257));
|
||||
});
|
||||
it('should work with all typed array types - explicit looping', function(): void {
|
||||
const u81 = new Uint8Array(5);
|
||||
const u82 = new Uint8Array(5);
|
||||
deepEquals(loopFill(u81, 2), u82.fill(2));
|
||||
deepEquals(loopFill(u81, -1), u82.fill(-1));
|
||||
const u161 = new Uint16Array(5);
|
||||
const u162 = new Uint16Array(5);
|
||||
deepEquals(loopFill(u161, 2), u162.fill(2));
|
||||
deepEquals(loopFill(u161, 65535), u162.fill(65535));
|
||||
deepEquals(loopFill(u161, -1), u162.fill(-1));
|
||||
const u321 = new Uint32Array(5);
|
||||
const u322 = new Uint32Array(5);
|
||||
deepEquals(loopFill(u321, 2), u322.fill(2));
|
||||
deepEquals(loopFill(u321, 65537), u322.fill(65537));
|
||||
deepEquals(loopFill(u321, -1), u322.fill(-1));
|
||||
const i81 = new Int8Array(5);
|
||||
const i82 = new Int8Array(5);
|
||||
deepEquals(loopFill(i81, 2), i82.fill(2));
|
||||
deepEquals(loopFill(i81, -1), i82.fill(-1));
|
||||
const i161 = new Int16Array(5);
|
||||
const i162 = new Int16Array(5);
|
||||
deepEquals(loopFill(i161, 2), i162.fill(2));
|
||||
deepEquals(loopFill(i161, 65535), i162.fill(65535));
|
||||
deepEquals(loopFill(i161, -1), i162.fill(-1));
|
||||
const i321 = new Int32Array(5);
|
||||
const i322 = new Int32Array(5);
|
||||
deepEquals(loopFill(i321, 2), i322.fill(2));
|
||||
deepEquals(loopFill(i321, 65537), i322.fill(65537));
|
||||
deepEquals(loopFill(i321, -1), i322.fill(-1));
|
||||
const f321 = new Float32Array(5);
|
||||
const f322 = new Float32Array(5);
|
||||
deepEquals(loopFill(f321, 1.2345), f322.fill(1.2345));
|
||||
const f641 = new Float64Array(5);
|
||||
const f642 = new Float64Array(5);
|
||||
deepEquals(loopFill(f641, 1.2345), f642.fill(1.2345));
|
||||
const u8Clamped1 = new Uint8ClampedArray(5);
|
||||
const u8Clamped2 = new Uint8ClampedArray(5);
|
||||
deepEquals(loopFill(u8Clamped1, 2), u8Clamped2.fill(2));
|
||||
deepEquals(loopFill(u8Clamped1, 257), u8Clamped2.fill(257));
|
||||
});
|
||||
it('start offset', function(): void {
|
||||
for (let i = -2; i < 10; ++i) {
|
||||
const u81 = new Uint8Array(5);
|
||||
const u82 = new Uint8Array(5);
|
||||
const u83 = new Uint8Array(5);
|
||||
deepEquals(fill(u81, 2, i), u83.fill(2, i));
|
||||
deepEquals(fill(u81, -1, i), u83.fill(-1, i));
|
||||
deepEquals(loopFill(u82, 2, i), u83.fill(2, i));
|
||||
deepEquals(loopFill(u82, -1, i), u83.fill(-1, i));
|
||||
}
|
||||
});
|
||||
it('end offset', function(): void {
|
||||
for (let i = -2; i < 10; ++i) {
|
||||
const u81 = new Uint8Array(5);
|
||||
const u82 = new Uint8Array(5);
|
||||
const u83 = new Uint8Array(5);
|
||||
deepEquals(fill(u81, 2, 0, i), u83.fill(2, 0, i));
|
||||
deepEquals(fill(u81, -1, 0, i), u83.fill(-1, 0, i));
|
||||
deepEquals(loopFill(u82, 2, 0, i), u83.fill(2, 0, i));
|
||||
deepEquals(loopFill(u82, -1, 0, i), u83.fill(-1, 0, i));
|
||||
}
|
||||
});
|
||||
it('start/end offset', function(): void {
|
||||
for (let i = -2; i < 10; ++i) {
|
||||
for (let j = -2; j < 10; ++j) {
|
||||
const u81 = new Uint8Array(5);
|
||||
const u82 = new Uint8Array(5);
|
||||
const u83 = new Uint8Array(5);
|
||||
deepEquals(fill(u81, 2, i, j), u83.fill(2, i, j));
|
||||
deepEquals(fill(u81, -1, i, j), u83.fill(-1, i, j));
|
||||
deepEquals(loopFill(u82, 2, i, j), u83.fill(2, i, j));
|
||||
deepEquals(loopFill(u82, -1, i, j), u83.fill(-1, i, j));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -33,7 +33,6 @@ export class AltClickHandler {
|
||||
this._mouseEvent,
|
||||
this._terminal.element,
|
||||
this._terminal.charMeasure,
|
||||
this._terminal.options.lineHeight,
|
||||
this._terminal.cols,
|
||||
this._terminal.rows,
|
||||
false
|
||||
|
||||
@@ -180,7 +180,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
|
||||
}
|
||||
|
||||
private _findZoneEventAt(e: MouseEvent): IMouseZone {
|
||||
const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows);
|
||||
const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.charMeasure, this._terminal.cols, this._terminal.rows);
|
||||
if (!coords) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -37,34 +37,34 @@ describe('MouseHelper.getCoords', () => {
|
||||
describe('when charMeasure is not initialized', () => {
|
||||
it('should return null', () => {
|
||||
charMeasure = new MockCharMeasure();
|
||||
assert.equal(mouseHelper.getCoords({ pageX: 0, pageY: 0 }, document.createElement('div'), charMeasure, 1, 10, 10), null);
|
||||
assert.equal(mouseHelper.getCoords({ pageX: 0, pageY: 0 }, document.createElement('div'), charMeasure, 10, 10), null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when pageX/pageY are not supported', () => {
|
||||
it('should return null', () => {
|
||||
assert.equal(mouseHelper.getCoords({ pageX: undefined, pageY: undefined }, document.createElement('div'), charMeasure, 1, 10, 10), null);
|
||||
assert.equal(mouseHelper.getCoords({ pageX: undefined, pageY: undefined }, document.createElement('div'), charMeasure, 10, 10), null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the cell that was clicked', () => {
|
||||
let coords: [number, number];
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH / 2, pageY: CHAR_HEIGHT / 2 }, document.createElement('div'), charMeasure, 1, 10, 10);
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH / 2, pageY: CHAR_HEIGHT / 2 }, document.createElement('div'), charMeasure, 10, 10);
|
||||
assert.deepEqual(coords, [1, 1]);
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 1, 10, 10);
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 10, 10);
|
||||
assert.deepEqual(coords, [1, 1]);
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT + 1 }, document.createElement('div'), charMeasure, 1, 10, 10);
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT + 1 }, document.createElement('div'), charMeasure, 10, 10);
|
||||
assert.deepEqual(coords, [1, 2]);
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH + 1, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 1, 10, 10);
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH + 1, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 10, 10);
|
||||
assert.deepEqual(coords, [2, 1]);
|
||||
});
|
||||
|
||||
it('should ensure the coordinates are returned within the terminal bounds', () => {
|
||||
let coords: [number, number];
|
||||
coords = mouseHelper.getCoords({ pageX: -1, pageY: -1 }, document.createElement('div'), charMeasure, 1, 10, 10);
|
||||
coords = mouseHelper.getCoords({ pageX: -1, pageY: -1 }, document.createElement('div'), charMeasure, 10, 10);
|
||||
assert.deepEqual(coords, [1, 1]);
|
||||
// Event are double the cols/rows
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH * 20, pageY: CHAR_HEIGHT * 20 }, document.createElement('div'), charMeasure, 1, 10, 10);
|
||||
coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH * 20, pageY: CHAR_HEIGHT * 20 }, document.createElement('div'), charMeasure, 10, 10);
|
||||
assert.deepEqual(coords, [10, 10], 'coordinates should never come back as larger than the terminal');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ export class MouseHelper {
|
||||
* apply an offset to the x value such that the left half of the cell will
|
||||
* select that cell and the right half will select the next cell.
|
||||
*/
|
||||
public getCoords(event: {pageX: number, pageY: number}, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number] {
|
||||
public getCoords(event: {pageX: number, pageY: number}, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number, isSelection?: boolean): [number, number] {
|
||||
// Coordinates cannot be measured if charMeasure has not been initialized
|
||||
if (!charMeasure.width || !charMeasure.height) {
|
||||
return null;
|
||||
@@ -85,8 +85,8 @@ export class MouseHelper {
|
||||
* @param colCount The number of columns in the terminal.
|
||||
* @param rowCount The number of rows in the terminal.
|
||||
*/
|
||||
public getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number } {
|
||||
const coords = this.getCoords(event, element, charMeasure, lineHeight, colCount, rowCount);
|
||||
public getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number): { x: number, y: number } {
|
||||
const coords = this.getCoords(event, element, charMeasure, colCount, rowCount);
|
||||
let x = coords[0];
|
||||
let y = coords[1];
|
||||
|
||||
|
||||
@@ -182,7 +182,6 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
|
||||
wraparoundMode: boolean;
|
||||
bracketedPasteMode: boolean;
|
||||
curAttr: number;
|
||||
savedCurAttr: number;
|
||||
savedCols: number;
|
||||
x10Mouse: boolean;
|
||||
vt200Mouse: boolean;
|
||||
@@ -304,6 +303,7 @@ export class MockBuffer implements IBuffer {
|
||||
scrollTop: number;
|
||||
savedY: number;
|
||||
savedX: number;
|
||||
savedCurAttr: number;
|
||||
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
|
||||
return Buffer.prototype.translateBufferLineToString.apply(this, arguments);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user