mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #839 from Tyriar/335_xterm_to_ts
Convert xterm.js to TypeScript
This commit is contained in:
+123
-6
@@ -1,31 +1,148 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { ITerminal } from './Interfaces';
|
||||
import { Buffer } from './Buffer';
|
||||
import { CircularList } from './utils/CircularList';
|
||||
import { MockTerminal } from './utils/TestUtils';
|
||||
|
||||
const INIT_COLS = 80;
|
||||
const INIT_ROWS = 24;
|
||||
|
||||
describe('Buffer', () => {
|
||||
let terminal: ITerminal;
|
||||
let buffer: Buffer;
|
||||
|
||||
beforeEach(() => {
|
||||
terminal = <any>{
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
scrollback: 1000
|
||||
};
|
||||
terminal = new MockTerminal();
|
||||
terminal.cols = INIT_COLS;
|
||||
terminal.rows = INIT_ROWS;
|
||||
terminal.options.scrollback = 1000;
|
||||
buffer = new Buffer(terminal);
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should create a CircularList with max length equal to scrollback, for its lines', () => {
|
||||
assert.instanceOf(buffer.lines, CircularList);
|
||||
assert.equal(buffer.lines.maxLength, terminal.scrollback);
|
||||
assert.equal(buffer.lines.maxLength, terminal.options.scrollback);
|
||||
});
|
||||
it('should set the Buffer\'s scrollBottom value equal to the terminal\'s rows -1', () => {
|
||||
assert.equal(buffer.scrollBottom, terminal.rows - 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fillViewportRows', () => {
|
||||
it('should fill the buffer with blank lines based on the size of the viewport', () => {
|
||||
const blankLineChar = terminal.blankLine()[0];
|
||||
buffer.fillViewportRows();
|
||||
assert.equal(buffer.lines.length, INIT_ROWS);
|
||||
for (let y = 0; y < INIT_ROWS; y++) {
|
||||
assert.equal(buffer.lines.get(y).length, INIT_COLS);
|
||||
for (let x = 0; x < INIT_COLS; x++) {
|
||||
assert.deepEqual(buffer.lines.get(y)[x], blankLineChar);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('resize', () => {
|
||||
describe('column size is reduced', () => {
|
||||
it('should not trim the data in the buffer', () => {
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(INIT_COLS / 2, INIT_ROWS);
|
||||
assert.equal(buffer.lines.length, INIT_ROWS);
|
||||
for (let i = 0; i < INIT_ROWS; i++) {
|
||||
assert.equal(buffer.lines.get(i).length, INIT_COLS);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('column size is increased', () => {
|
||||
it('should add pad columns', () => {
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(INIT_COLS + 10, INIT_ROWS);
|
||||
assert.equal(buffer.lines.length, INIT_ROWS);
|
||||
for (let i = 0; i < INIT_ROWS; i++) {
|
||||
assert.equal(buffer.lines.get(i).length, INIT_COLS + 10);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('row size reduced', () => {
|
||||
it('should trim blank lines from the end', () => {
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(INIT_COLS, INIT_ROWS - 10);
|
||||
assert.equal(buffer.lines.length, INIT_ROWS - 10);
|
||||
});
|
||||
|
||||
it('should move the viewport down when it\'s at the end', () => {
|
||||
buffer.fillViewportRows();
|
||||
// Set cursor y to have 5 blank lines below it
|
||||
buffer.y = INIT_ROWS - 5 - 1;
|
||||
buffer.resize(INIT_COLS, INIT_ROWS - 10);
|
||||
// Trim 5 rows
|
||||
assert.equal(buffer.lines.length, INIT_ROWS - 5);
|
||||
// Shift the viewport down 5 rows
|
||||
assert.equal(buffer.ydisp, 5);
|
||||
assert.equal(buffer.ybase, 5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('row size increased', () => {
|
||||
describe('empty buffer', () => {
|
||||
it('should add blank lines to end', () => {
|
||||
buffer.fillViewportRows();
|
||||
assert.equal(buffer.ydisp, 0);
|
||||
buffer.resize(INIT_COLS, INIT_ROWS + 10);
|
||||
assert.equal(buffer.ydisp, 0);
|
||||
assert.equal(buffer.lines.length, INIT_ROWS + 10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filled buffer', () => {
|
||||
it('should show more of the buffer above', () => {
|
||||
buffer.fillViewportRows();
|
||||
// Create 10 extra blank lines
|
||||
for (let i = 0; i < 10; i++) {
|
||||
buffer.lines.push(terminal.blankLine());
|
||||
}
|
||||
// Set cursor to the bottom of the buffer
|
||||
buffer.y = INIT_ROWS - 1;
|
||||
// Scroll down 10 lines
|
||||
buffer.ybase = 10;
|
||||
buffer.ydisp = 10;
|
||||
assert.equal(buffer.lines.length, INIT_ROWS + 10);
|
||||
buffer.resize(INIT_COLS, INIT_ROWS + 5);
|
||||
// Should be should 5 more lines
|
||||
assert.equal(buffer.ydisp, 5);
|
||||
assert.equal(buffer.ybase, 5);
|
||||
// Should not trim the buffer
|
||||
assert.equal(buffer.lines.length, INIT_ROWS + 10);
|
||||
});
|
||||
|
||||
it('should show more of the buffer below when the viewport is at the top of the buffer', () => {
|
||||
buffer.fillViewportRows();
|
||||
// Create 10 extra blank lines
|
||||
for (let i = 0; i < 10; i++) {
|
||||
buffer.lines.push(terminal.blankLine());
|
||||
}
|
||||
// Set cursor to the bottom of the buffer
|
||||
buffer.y = INIT_ROWS - 1;
|
||||
// Scroll down 10 lines
|
||||
buffer.ybase = 10;
|
||||
buffer.ydisp = 0;
|
||||
assert.equal(buffer.lines.length, INIT_ROWS + 10);
|
||||
buffer.resize(INIT_COLS, INIT_ROWS + 5);
|
||||
// The viewport should remain at the top
|
||||
assert.equal(buffer.ydisp, 0);
|
||||
// The buffer ybase should move up 5 lines
|
||||
assert.equal(buffer.ybase, 5);
|
||||
// Should not trim the buffer
|
||||
assert.equal(buffer.lines.length, INIT_ROWS + 10);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+110
-14
@@ -2,8 +2,9 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal } from './Interfaces';
|
||||
import { ITerminal, IBuffer } from './Interfaces';
|
||||
import { CircularList } from './utils/CircularList';
|
||||
import { LineData, CharData } from './Types';
|
||||
|
||||
/**
|
||||
* This class represents a terminal buffer (an internal state of the terminal), where the
|
||||
@@ -12,31 +13,126 @@ import { CircularList } from './utils/CircularList';
|
||||
* - cursor position
|
||||
* - scroll position
|
||||
*/
|
||||
export class Buffer {
|
||||
public lines: CircularList<[number, string, number][]>;
|
||||
export class Buffer implements IBuffer {
|
||||
private _lines: CircularList<LineData>;
|
||||
|
||||
public ydisp: number;
|
||||
public ybase: number;
|
||||
public y: number;
|
||||
public x: number;
|
||||
public scrollBottom: number;
|
||||
public scrollTop: number;
|
||||
public tabs: any;
|
||||
public savedY: number;
|
||||
public savedX: number;
|
||||
|
||||
/**
|
||||
* Create a new Buffer.
|
||||
* @param {Terminal} terminal - The terminal the Buffer will belong to
|
||||
* @param {Terminal} _terminal - The terminal the Buffer will belong to
|
||||
* @param {number} ydisp - The scroll position of the Buffer in the viewport
|
||||
* @param {number} ybase - The scroll position of the y cursor (ybase + y = the y position within the Buffer)
|
||||
* @param {number} y - The cursor's y position after ybase
|
||||
* @param {number} x - The cursor's x position after ybase
|
||||
*/
|
||||
constructor(
|
||||
private terminal: ITerminal,
|
||||
public ydisp: number = 0,
|
||||
public ybase: number = 0,
|
||||
public y: number = 0,
|
||||
public x: number = 0,
|
||||
public scrollBottom: number = 0,
|
||||
public scrollTop: number = 0,
|
||||
public tabs: any = {},
|
||||
private _terminal: ITerminal
|
||||
) {
|
||||
this.lines = new CircularList<[number, string, number][]>(this.terminal.scrollback);
|
||||
this.scrollBottom = this.terminal.rows - 1;
|
||||
this.clear();
|
||||
}
|
||||
|
||||
public get lines(): CircularList<LineData> {
|
||||
return this._lines;
|
||||
}
|
||||
|
||||
public fillViewportRows(): void {
|
||||
if (this._lines.length === 0) {
|
||||
let i = this._terminal.rows;
|
||||
while (i--) {
|
||||
this.lines.push(this._terminal.blankLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.ydisp = 0;
|
||||
this.ybase = 0;
|
||||
this.y = 0;
|
||||
this.x = 0;
|
||||
this.scrollBottom = 0;
|
||||
this.scrollTop = 0;
|
||||
this.tabs = {};
|
||||
this._lines = new CircularList<LineData>(this._terminal.options.scrollback);
|
||||
this.scrollBottom = this._terminal.rows - 1;
|
||||
}
|
||||
|
||||
public resize(newCols: number, newRows: number): void {
|
||||
// Don't resize the buffer if it's empty and hasn't been used yet.
|
||||
if (this._lines.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Deal with columns increasing (we don't do anything when columns reduce)
|
||||
if (this._terminal.cols < newCols) {
|
||||
const ch: CharData = [this._terminal.defAttr, ' ', 1]; // does xterm use the default attr?
|
||||
for (let i = 0; i < this._lines.length; i++) {
|
||||
if (this._lines.get(i) === undefined) {
|
||||
this._lines.set(i, this._terminal.blankLine());
|
||||
}
|
||||
while (this._lines.get(i).length < newCols) {
|
||||
this._lines.get(i).push(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resize rows in both directions as needed
|
||||
let addToY = 0;
|
||||
if (this._terminal.rows < newRows) {
|
||||
for (let y = this._terminal.rows; y < newRows; y++) {
|
||||
if (this._lines.length < newRows + this.ybase) {
|
||||
if (this.ybase > 0 && this._lines.length <= this.ybase + this.y + addToY + 1) {
|
||||
// There is room above the buffer and there are no empty elements below the line,
|
||||
// scroll up
|
||||
this.ybase--;
|
||||
addToY++;
|
||||
if (this.ydisp > 0) {
|
||||
// Viewport is at the top of the buffer, must increase downwards
|
||||
this.ydisp--;
|
||||
}
|
||||
} else {
|
||||
// Add a blank line if there is no buffer left at the top to scroll to, or if there
|
||||
// are blank lines after the cursor
|
||||
this._lines.push(this._terminal.blankLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // (this._terminal.rows >= newRows)
|
||||
for (let y = this._terminal.rows; y > newRows; y--) {
|
||||
if (this._lines.length > newRows + this.ybase) {
|
||||
if (this._lines.length > this.ybase + this.y + 1) {
|
||||
// The line is a blank line below the cursor, remove it
|
||||
this._lines.pop();
|
||||
} else {
|
||||
// The line is the cursor, scroll down
|
||||
this.ybase++;
|
||||
this.ydisp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that the cursor stays on screen
|
||||
if (this.y >= newRows) {
|
||||
this.y = newRows - 1;
|
||||
}
|
||||
if (addToY) {
|
||||
this.y += addToY;
|
||||
}
|
||||
|
||||
if (this.x >= newCols) {
|
||||
this.x = newCols - 1;
|
||||
}
|
||||
|
||||
this.scrollTop = 0;
|
||||
this.scrollBottom = newRows - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { ITerminal } from './Interfaces';
|
||||
import { BufferSet } from './BufferSet';
|
||||
import { Buffer } from './Buffer';
|
||||
import { MockTerminal } from './utils/TestUtils';
|
||||
|
||||
describe('BufferSet', () => {
|
||||
let terminal: ITerminal;
|
||||
let bufferSet: BufferSet;
|
||||
|
||||
beforeEach(() => {
|
||||
terminal = <any>{
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
scrollback: 1000
|
||||
};
|
||||
terminal = new MockTerminal();
|
||||
terminal.cols = 80;
|
||||
terminal.rows = 24;
|
||||
terminal.options.scrollback = 1000;
|
||||
bufferSet = new BufferSet(terminal);
|
||||
});
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
constructor(private _terminal: ITerminal) {
|
||||
super();
|
||||
this._normal = new Buffer(this._terminal);
|
||||
this._normal.fillViewportRows();
|
||||
this._alt = new Buffer(this._terminal);
|
||||
this._activeBuffer = this._normal;
|
||||
}
|
||||
@@ -54,6 +55,11 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
* Sets the normal Buffer of the BufferSet as its currently active Buffer
|
||||
*/
|
||||
public activateNormalBuffer(): void {
|
||||
// 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.
|
||||
this._alt.clear();
|
||||
|
||||
this._activeBuffer = this._normal;
|
||||
this.emit('activate', this._normal);
|
||||
}
|
||||
@@ -62,7 +68,16 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
* Sets the alt Buffer of the BufferSet as its currently active Buffer
|
||||
*/
|
||||
public activateAltBuffer(): void {
|
||||
// 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._activeBuffer = this._alt;
|
||||
this.emit('activate', this._alt);
|
||||
}
|
||||
|
||||
public resize(newCols: number, newRows: number): void {
|
||||
this._normal.resize(newCols, newRows);
|
||||
this._alt.resize(newCols, newRows);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -2,17 +2,19 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Charset } from './Types';
|
||||
|
||||
/**
|
||||
* The character sets supported by the terminal. These enable several languages
|
||||
* to be represented within the terminal with only 8-bit encoding. See ISO 2022
|
||||
* for a discussion on character sets. Only VT100 character sets are supported.
|
||||
*/
|
||||
export const CHARSETS: {[key: string]: {[key: string]: string}} = {};
|
||||
export const CHARSETS: { [key: string]: Charset } = {};
|
||||
|
||||
/**
|
||||
* The default character set, US.
|
||||
*/
|
||||
export const DEFAULT_CHARSET = CHARSETS['B'];
|
||||
export const DEFAULT_CHARSET: Charset = CHARSETS['B'];
|
||||
|
||||
/**
|
||||
* DEC Special Character and Line Drawing Set.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { CompositionHelper } from './CompositionHelper';
|
||||
|
||||
@@ -36,7 +40,7 @@ describe('CompositionHelper', () => {
|
||||
return { offsetLeft: 0, offsetTop: 0 };
|
||||
}
|
||||
},
|
||||
handler: function (text) {
|
||||
handler: (text: string) => {
|
||||
handledText += text;
|
||||
}
|
||||
};
|
||||
@@ -45,7 +49,7 @@ describe('CompositionHelper', () => {
|
||||
});
|
||||
|
||||
describe('Input', () => {
|
||||
it('Should insert simple characters', function (done) {
|
||||
it('Should insert simple characters', (done) => {
|
||||
// First character 'ㅇ'
|
||||
compositionHelper.compositionstart();
|
||||
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||
@@ -69,7 +73,7 @@ describe('CompositionHelper', () => {
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('Should insert complex characters', function (done) {
|
||||
it('Should insert complex characters', (done) => {
|
||||
// First character '앙'
|
||||
compositionHelper.compositionstart();
|
||||
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||
@@ -109,7 +113,7 @@ describe('CompositionHelper', () => {
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('Should insert complex characters that change with following character', function (done) {
|
||||
it('Should insert complex characters that change with following character', (done) => {
|
||||
// First character '아'
|
||||
compositionHelper.compositionstart();
|
||||
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||
@@ -138,7 +142,7 @@ describe('CompositionHelper', () => {
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('Should insert multi-characters compositions', function (done) {
|
||||
it('Should insert multi-characters compositions', (done) => {
|
||||
// First character 'だ'
|
||||
compositionHelper.compositionstart();
|
||||
compositionHelper.compositionupdate({ data: 'd' });
|
||||
@@ -161,7 +165,7 @@ describe('CompositionHelper', () => {
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('Should insert multi-character compositions that are converted to other characters with the same length', function (done) {
|
||||
it('Should insert multi-character compositions that are converted to other characters with the same length', (done) => {
|
||||
// First character 'だ'
|
||||
compositionHelper.compositionstart();
|
||||
compositionHelper.compositionupdate({ data: 'd' });
|
||||
@@ -189,7 +193,7 @@ describe('CompositionHelper', () => {
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('Should insert multi-character compositions that are converted to other characters with different lengths', function (done) {
|
||||
it('Should insert multi-character compositions that are converted to other characters with different lengths', (done) => {
|
||||
// First character 'い'
|
||||
compositionHelper.compositionstart();
|
||||
compositionHelper.compositionupdate({ data: 'い' });
|
||||
@@ -217,7 +221,7 @@ describe('CompositionHelper', () => {
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('Should insert non-composition characters input immediately after composition characters', function (done) {
|
||||
it('Should insert non-composition characters input immediately after composition characters', (done) => {
|
||||
// First character 'ㅇ'
|
||||
compositionHelper.compositionstart();
|
||||
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||
|
||||
@@ -51,7 +51,7 @@ export class CompositionHelper {
|
||||
/**
|
||||
* Handles the compositionstart event, activating the composition view.
|
||||
*/
|
||||
public compositionstart() {
|
||||
public compositionstart(): void {
|
||||
this.isComposing = true;
|
||||
this.compositionPosition.start = this.textarea.value.length;
|
||||
this.compositionView.textContent = '';
|
||||
@@ -62,7 +62,7 @@ export class CompositionHelper {
|
||||
* Handles the compositionupdate event, updating the composition view.
|
||||
* @param {CompositionEvent} ev The event.
|
||||
*/
|
||||
public compositionupdate(ev: CompositionEvent) {
|
||||
public compositionupdate(ev: CompositionEvent): void {
|
||||
this.compositionView.textContent = ev.data;
|
||||
this.updateCompositionElements();
|
||||
setTimeout(() => {
|
||||
@@ -74,7 +74,7 @@ export class CompositionHelper {
|
||||
* Handles the compositionend event, hiding the composition view and sending the composition to
|
||||
* the handler.
|
||||
*/
|
||||
public compositionend() {
|
||||
public compositionend(): void {
|
||||
this.finalizeComposition(true);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export class CompositionHelper {
|
||||
* @param ev The keydown event.
|
||||
* @return Whether the Terminal should continue processing the keydown event.
|
||||
*/
|
||||
public keydown(ev: KeyboardEvent) {
|
||||
public keydown(ev: KeyboardEvent): boolean {
|
||||
if (this.isComposing || this.isSendingComposition) {
|
||||
if (ev.keyCode === 229) {
|
||||
// Continue composing if the keyCode is the "composition character"
|
||||
@@ -116,7 +116,7 @@ export class CompositionHelper {
|
||||
* compositionend event is triggered, such as enter, so that the composition is send before
|
||||
* the command is executed.
|
||||
*/
|
||||
private finalizeComposition(waitForPropogation: boolean) {
|
||||
private finalizeComposition(waitForPropogation: boolean): void {
|
||||
this.compositionView.classList.remove('active');
|
||||
this.isComposing = false;
|
||||
this.clearTextareaPosition();
|
||||
@@ -169,7 +169,7 @@ export class CompositionHelper {
|
||||
* character" (229) is triggered, in order to allow non-composition text to be entered when an
|
||||
* IME is active.
|
||||
*/
|
||||
private handleAnyTextareaChanges() {
|
||||
private handleAnyTextareaChanges(): void {
|
||||
const oldValue = this.textarea.value;
|
||||
setTimeout(() => {
|
||||
// Ignore if a composition has started since the timeout
|
||||
@@ -189,7 +189,7 @@ export class CompositionHelper {
|
||||
* @param dontRecurse Whether to use setTimeout to recursively trigger another update, this is
|
||||
* necessary as the IME events across browsers are not consistently triggered.
|
||||
*/
|
||||
public updateCompositionElements(dontRecurse?: boolean) {
|
||||
public updateCompositionElements(dontRecurse?: boolean): void {
|
||||
if (!this.isComposing) {
|
||||
return;
|
||||
}
|
||||
@@ -222,7 +222,7 @@ export class CompositionHelper {
|
||||
* Clears the textarea's position so that the cursor does not blink on IE.
|
||||
* @private
|
||||
*/
|
||||
private clearTextareaPosition() {
|
||||
private clearTextareaPosition(): void {
|
||||
this.textarea.style.left = '';
|
||||
this.textarea.style.top = '';
|
||||
};
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { EventEmitter } from './EventEmitter';
|
||||
|
||||
|
||||
+14
-15
@@ -2,15 +2,10 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IEventEmitter } from './Interfaces';
|
||||
|
||||
interface ListenerType {
|
||||
(): void;
|
||||
listener?: () => void;
|
||||
};
|
||||
import { IEventEmitter, IListenerType } from './Interfaces';
|
||||
|
||||
export class EventEmitter implements IEventEmitter {
|
||||
private _events: {[type: string]: ListenerType[]};
|
||||
private _events: {[type: string]: IListenerType[]};
|
||||
|
||||
constructor() {
|
||||
// Restore the previous events if available, this will happen if the
|
||||
@@ -18,12 +13,12 @@ export class EventEmitter implements IEventEmitter {
|
||||
this._events = this._events || {};
|
||||
}
|
||||
|
||||
public on(type, listener): void {
|
||||
public on(type: string, listener: IListenerType): void {
|
||||
this._events[type] = this._events[type] || [];
|
||||
this._events[type].push(listener);
|
||||
}
|
||||
|
||||
public off(type, listener): void {
|
||||
public off(type: string, listener: IListenerType): void {
|
||||
if (!this._events[type]) {
|
||||
return;
|
||||
}
|
||||
@@ -39,20 +34,20 @@ export class EventEmitter implements IEventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
public removeAllListeners(type): void {
|
||||
public removeAllListeners(type: string): void {
|
||||
if (this._events[type]) {
|
||||
delete this._events[type];
|
||||
}
|
||||
}
|
||||
|
||||
public once(type, listener): any {
|
||||
function on() {
|
||||
public once(type: string, listener: IListenerType): void {
|
||||
function on(): void {
|
||||
let args = Array.prototype.slice.call(arguments);
|
||||
this.off(type, on);
|
||||
return listener.apply(this, args);
|
||||
listener.apply(this, args);
|
||||
}
|
||||
(<any>on).listener = listener;
|
||||
return this.on(type, on);
|
||||
this.on(type, on);
|
||||
}
|
||||
|
||||
public emit(type: string, ...args: any[]): void {
|
||||
@@ -65,7 +60,11 @@ export class EventEmitter implements IEventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
public listeners(type): ListenerType[] {
|
||||
public listeners(type: string): IListenerType[] {
|
||||
return this._events[type] || [];
|
||||
}
|
||||
|
||||
protected destroy(): void {
|
||||
this._events = {};
|
||||
}
|
||||
}
|
||||
|
||||
+50
-44
@@ -1,10 +1,17 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { InputHandler } from './InputHandler';
|
||||
import { wcwidth } from './InputHandler';
|
||||
import { MockInputHandlingTerminal } from './utils/TestUtils';
|
||||
|
||||
describe('InputHandler', () => {
|
||||
describe('save and restore cursor', () => {
|
||||
let terminal = { buffer: { x: 1, y: 2 } };
|
||||
let terminal = new MockInputHandlingTerminal();
|
||||
terminal.buffer.x = 1;
|
||||
terminal.buffer.y = 2;
|
||||
let inputHandler = new InputHandler(terminal);
|
||||
// Save cursor position
|
||||
inputHandler.saveCursor([]);
|
||||
@@ -20,51 +27,47 @@ describe('InputHandler', () => {
|
||||
});
|
||||
describe('setCursorStyle', () => {
|
||||
it('should call Terminal.setOption with correct params', () => {
|
||||
let options = {};
|
||||
let terminal = {
|
||||
setOption: (option, value) => options[option] = value
|
||||
};
|
||||
let terminal = new MockInputHandlingTerminal();
|
||||
let inputHandler = new InputHandler(terminal);
|
||||
|
||||
inputHandler.setCursorStyle([0]);
|
||||
assert.equal(options['cursorStyle'], 'block');
|
||||
assert.equal(options['cursorBlink'], true);
|
||||
assert.equal(terminal.options['cursorStyle'], 'block');
|
||||
assert.equal(terminal.options['cursorBlink'], true);
|
||||
|
||||
options = {};
|
||||
terminal.options = {};
|
||||
inputHandler.setCursorStyle([1]);
|
||||
assert.equal(options['cursorStyle'], 'block');
|
||||
assert.equal(options['cursorBlink'], true);
|
||||
assert.equal(terminal.options['cursorStyle'], 'block');
|
||||
assert.equal(terminal.options['cursorBlink'], true);
|
||||
|
||||
options = {};
|
||||
terminal.options = {};
|
||||
inputHandler.setCursorStyle([2]);
|
||||
assert.equal(options['cursorStyle'], 'block');
|
||||
assert.equal(options['cursorBlink'], false);
|
||||
assert.equal(terminal.options['cursorStyle'], 'block');
|
||||
assert.equal(terminal.options['cursorBlink'], false);
|
||||
|
||||
options = {};
|
||||
terminal.options = {};
|
||||
inputHandler.setCursorStyle([3]);
|
||||
assert.equal(options['cursorStyle'], 'underline');
|
||||
assert.equal(options['cursorBlink'], true);
|
||||
assert.equal(terminal.options['cursorStyle'], 'underline');
|
||||
assert.equal(terminal.options['cursorBlink'], true);
|
||||
|
||||
options = {};
|
||||
terminal.options = {};
|
||||
inputHandler.setCursorStyle([4]);
|
||||
assert.equal(options['cursorStyle'], 'underline');
|
||||
assert.equal(options['cursorBlink'], false);
|
||||
assert.equal(terminal.options['cursorStyle'], 'underline');
|
||||
assert.equal(terminal.options['cursorBlink'], false);
|
||||
|
||||
options = {};
|
||||
terminal.options = {};
|
||||
inputHandler.setCursorStyle([5]);
|
||||
assert.equal(options['cursorStyle'], 'bar');
|
||||
assert.equal(options['cursorBlink'], true);
|
||||
assert.equal(terminal.options['cursorStyle'], 'bar');
|
||||
assert.equal(terminal.options['cursorBlink'], true);
|
||||
|
||||
options = {};
|
||||
terminal.options = {};
|
||||
inputHandler.setCursorStyle([6]);
|
||||
assert.equal(options['cursorStyle'], 'bar');
|
||||
assert.equal(options['cursorBlink'], false);
|
||||
|
||||
assert.equal(terminal.options['cursorStyle'], 'bar');
|
||||
assert.equal(terminal.options['cursorBlink'], false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const old_wcwidth = (function(opts) {
|
||||
const old_wcwidth = (function(opts: {nul: number, control: number}): (ucs: number) => number {
|
||||
// extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
|
||||
// combining characters
|
||||
const COMBINING = [
|
||||
@@ -118,7 +121,7 @@ const old_wcwidth = (function(opts) {
|
||||
[0xE0100, 0xE01EF]
|
||||
];
|
||||
// binary search
|
||||
function bisearch(ucs) {
|
||||
function bisearch(ucs: number): boolean {
|
||||
let min = 0;
|
||||
let max = COMBINING.length - 1;
|
||||
let mid;
|
||||
@@ -135,23 +138,26 @@ const old_wcwidth = (function(opts) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function wcwidth(ucs) {
|
||||
// test for 8-bit control characters
|
||||
if (ucs === 0)
|
||||
return opts.nul;
|
||||
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
|
||||
return opts.control;
|
||||
// binary search in table of non-spacing characters
|
||||
if (bisearch(ucs))
|
||||
return 0;
|
||||
// if we arrive here, ucs is not a combining or C0/C1 control character
|
||||
if (isWide(ucs)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
function wcwidth(ucs: number): number {
|
||||
// test for 8-bit control characters
|
||||
if (ucs === 0) {
|
||||
return opts.nul;
|
||||
}
|
||||
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) {
|
||||
return opts.control;
|
||||
}
|
||||
// binary search in table of non-spacing characters
|
||||
if (bisearch(ucs)) {
|
||||
return 0;
|
||||
}
|
||||
// if we arrive here, ucs is not a combining or C0/C1 control character
|
||||
if (isWide(ucs)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
function isWide(ucs) {
|
||||
return (
|
||||
function isWide(ucs: number): boolean {
|
||||
return (
|
||||
ucs >= 0x1100 && (
|
||||
ucs <= 0x115f || // Hangul Jamo init. consonants
|
||||
ucs === 0x2329 ||
|
||||
|
||||
+48
-59
@@ -2,9 +2,10 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IInputHandler, ITerminal } from './Interfaces';
|
||||
import { IInputHandler, ITerminal, IInputHandlingTerminal } from './Interfaces';
|
||||
import { C0 } from './EscapeSequences';
|
||||
import { DEFAULT_CHARSET } from './Charsets';
|
||||
import { CharData } from './Types';
|
||||
|
||||
/**
|
||||
* The terminal's standard implementation of IInputHandler, this handles all
|
||||
@@ -14,8 +15,7 @@ import { DEFAULT_CHARSET } from './Charsets';
|
||||
* each function's header comment.
|
||||
*/
|
||||
export class InputHandler implements IInputHandler {
|
||||
// TODO: We want to type _terminal when it's pulled into TS
|
||||
constructor(private _terminal: any) { }
|
||||
constructor(private _terminal: IInputHandlingTerminal) { }
|
||||
|
||||
public addChar(char: string, code: number): void {
|
||||
if (char >= ' ') {
|
||||
@@ -61,7 +61,7 @@ export class InputHandler implements IInputHandler {
|
||||
} else {
|
||||
// The line already exists (eg. the initial viewport), mark it as a
|
||||
// wrapped line
|
||||
this._terminal.buffer.lines.get(this._terminal.buffer.y).isWrapped = true;
|
||||
(<any>this._terminal.buffer.lines.get(this._terminal.buffer.y)).isWrapped = true;
|
||||
}
|
||||
} else {
|
||||
if (ch_width === 2) // FIXME: check for xterm behavior
|
||||
@@ -105,12 +105,12 @@ export class InputHandler implements IInputHandler {
|
||||
* Bell (Ctrl-G).
|
||||
*/
|
||||
public bell(): void {
|
||||
if (!this._terminal.visualBell) {
|
||||
if (!this._terminal.options.visualBell) {
|
||||
return;
|
||||
}
|
||||
this._terminal.element.style.borderColor = 'white';
|
||||
setTimeout(() => this._terminal.element.style.borderColor = '', 10);
|
||||
if (this._terminal.popOnBell) {
|
||||
if (this._terminal.options.popOnBell) {
|
||||
this._terminal.focus();
|
||||
}
|
||||
}
|
||||
@@ -189,14 +189,12 @@ export class InputHandler implements IInputHandler {
|
||||
* Insert Ps (Blank) Character(s) (default = 1) (ICH).
|
||||
*/
|
||||
public insertChars(params: number[]): void {
|
||||
let param, row, j, ch;
|
||||
|
||||
param = params[0];
|
||||
let param = params[0];
|
||||
if (param < 1) param = 1;
|
||||
|
||||
row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
j = this._terminal.buffer.x;
|
||||
ch = [this._terminal.eraseAttr(), ' ', 1]; // xterm
|
||||
const row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
let j = this._terminal.buffer.x;
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1]; // xterm
|
||||
|
||||
while (param-- && j < this._terminal.cols) {
|
||||
this._terminal.buffer.lines.get(row).splice(j++, 0, ch);
|
||||
@@ -223,7 +221,7 @@ export class InputHandler implements IInputHandler {
|
||||
* CSI Ps B
|
||||
* Cursor Down Ps Times (default = 1) (CUD).
|
||||
*/
|
||||
public cursorDown(params: number[]) {
|
||||
public cursorDown(params: number[]): void {
|
||||
let param = params[0];
|
||||
if (param < 1) {
|
||||
param = 1;
|
||||
@@ -242,7 +240,7 @@ export class InputHandler implements IInputHandler {
|
||||
* CSI Ps C
|
||||
* Cursor Forward Ps Times (default = 1) (CUF).
|
||||
*/
|
||||
public cursorForward(params: number[]) {
|
||||
public cursorForward(params: number[]): void {
|
||||
let param = params[0];
|
||||
if (param < 1) {
|
||||
param = 1;
|
||||
@@ -257,7 +255,7 @@ export class InputHandler implements IInputHandler {
|
||||
* CSI Ps D
|
||||
* Cursor Backward Ps Times (default = 1) (CUB).
|
||||
*/
|
||||
public cursorBackward(params: number[]) {
|
||||
public cursorBackward(params: number[]): void {
|
||||
let param = params[0];
|
||||
if (param < 1) {
|
||||
param = 1;
|
||||
@@ -325,9 +323,8 @@ export class InputHandler implements IInputHandler {
|
||||
* Cursor Position [row;column] (default = [1,1]) (CUP).
|
||||
*/
|
||||
public cursorPosition(params: number[]): void {
|
||||
let row, col;
|
||||
|
||||
row = params[0] - 1;
|
||||
let col: number;
|
||||
let row: number = params[0] - 1;
|
||||
|
||||
if (params.length >= 2) {
|
||||
col = params[1] - 1;
|
||||
@@ -439,14 +436,13 @@ export class InputHandler implements IInputHandler {
|
||||
* Insert Ps Line(s) (default = 1) (IL).
|
||||
*/
|
||||
public insertLines(params: number[]): void {
|
||||
let param, row, j;
|
||||
|
||||
param = params[0];
|
||||
let param: number = params[0];
|
||||
if (param < 1) {
|
||||
param = 1;
|
||||
}
|
||||
row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
let row: number = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
|
||||
let j: number;
|
||||
j = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom;
|
||||
j = this._terminal.rows - 1 + this._terminal.buffer.ybase - j + 1;
|
||||
|
||||
@@ -475,14 +471,13 @@ export class InputHandler implements IInputHandler {
|
||||
* Delete Ps Line(s) (default = 1) (DL).
|
||||
*/
|
||||
public deleteLines(params: number[]): void {
|
||||
let param, row, j;
|
||||
|
||||
param = params[0];
|
||||
let param = params[0];
|
||||
if (param < 1) {
|
||||
param = 1;
|
||||
}
|
||||
row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
const row: number = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
|
||||
let j: number;
|
||||
j = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom;
|
||||
j = this._terminal.rows - 1 + this._terminal.buffer.ybase - j;
|
||||
|
||||
@@ -509,15 +504,13 @@ export class InputHandler implements IInputHandler {
|
||||
* Delete Ps Character(s) (default = 1) (DCH).
|
||||
*/
|
||||
public deleteChars(params: number[]): void {
|
||||
let param, row, ch;
|
||||
|
||||
param = params[0];
|
||||
let param: number = params[0];
|
||||
if (param < 1) {
|
||||
param = 1;
|
||||
}
|
||||
|
||||
row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
ch = [this._terminal.eraseAttr(), ' ', 1]; // xterm
|
||||
const row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1]; // xterm
|
||||
|
||||
while (param--) {
|
||||
this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 1);
|
||||
@@ -558,16 +551,14 @@ export class InputHandler implements IInputHandler {
|
||||
* Erase Ps Character(s) (default = 1) (ECH).
|
||||
*/
|
||||
public eraseChars(params: number[]): void {
|
||||
let param, row, j, ch;
|
||||
|
||||
param = params[0];
|
||||
let param = params[0];
|
||||
if (param < 1) {
|
||||
param = 1;
|
||||
}
|
||||
|
||||
row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
j = this._terminal.buffer.x;
|
||||
ch = [this._terminal.eraseAttr(), ' ', 1]; // xterm
|
||||
const row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
let j = this._terminal.buffer.x;
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1]; // xterm
|
||||
|
||||
while (param-- && j < this._terminal.cols) {
|
||||
this._terminal.buffer.lines.get(row)[j++] = ch;
|
||||
@@ -619,9 +610,9 @@ export class InputHandler implements IInputHandler {
|
||||
* CSI Ps b Repeat the preceding graphic character Ps times (REP).
|
||||
*/
|
||||
public repeatPrecedingCharacter(params: number[]): void {
|
||||
let param = params[0] || 1
|
||||
, line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + this._terminal.buffer.y)
|
||||
, ch = line[this._terminal.buffer.x - 1] || [this._terminal.defAttr, ' ', 1];
|
||||
let param = params[0] || 1;
|
||||
const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + this._terminal.buffer.y);
|
||||
const ch = line[this._terminal.buffer.x - 1] || [this._terminal.defAttr, ' ', 1];
|
||||
|
||||
while (param--) {
|
||||
line[this._terminal.buffer.x++] = ch;
|
||||
@@ -867,7 +858,7 @@ export class InputHandler implements IInputHandler {
|
||||
this._terminal.insertMode = true;
|
||||
break;
|
||||
case 20:
|
||||
// this._terminal.convertEol = true;
|
||||
// this._t.convertEol = true;
|
||||
break;
|
||||
}
|
||||
} else if (this._terminal.prefix === '?') {
|
||||
@@ -954,7 +945,6 @@ export class InputHandler implements IInputHandler {
|
||||
case 47: // alt screen buffer
|
||||
case 1047: // alt screen buffer
|
||||
this._terminal.buffers.activateAltBuffer();
|
||||
this._terminal.reset();
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
this._terminal.showCursor();
|
||||
break;
|
||||
@@ -1059,7 +1049,7 @@ export class InputHandler implements IInputHandler {
|
||||
this._terminal.insertMode = false;
|
||||
break;
|
||||
case 20:
|
||||
// this._terminal.convertEol = false;
|
||||
// this._t.convertEol = false;
|
||||
break;
|
||||
}
|
||||
} else if (this._terminal.prefix === '?') {
|
||||
@@ -1203,14 +1193,13 @@ export class InputHandler implements IInputHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
let l = params.length
|
||||
, i = 0
|
||||
, flags = this._terminal.curAttr >> 18
|
||||
, fg = (this._terminal.curAttr >> 9) & 0x1ff
|
||||
, bg = this._terminal.curAttr & 0x1ff
|
||||
, p;
|
||||
const l = params.length;
|
||||
let flags = this._terminal.curAttr >> 18;
|
||||
let fg = (this._terminal.curAttr >> 9) & 0x1ff;
|
||||
let bg = this._terminal.curAttr & 0x1ff;
|
||||
let p;
|
||||
|
||||
for (; i < l; i++) {
|
||||
for (let i = 0; i < l; i++) {
|
||||
p = params[i];
|
||||
if (p >= 30 && p <= 37) {
|
||||
// fg color 8
|
||||
@@ -1470,7 +1459,7 @@ export class InputHandler implements IInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
export const wcwidth = (function(opts) {
|
||||
export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: number) => number {
|
||||
// extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
|
||||
// combining characters
|
||||
const COMBINING_BMP = [
|
||||
@@ -1526,7 +1515,7 @@ export const wcwidth = (function(opts) {
|
||||
[0xE0100, 0xE01EF]
|
||||
];
|
||||
// binary search
|
||||
function bisearch(ucs, data) {
|
||||
function bisearch(ucs: number, data: number[][]): boolean {
|
||||
let min = 0;
|
||||
let max = data.length - 1;
|
||||
let mid;
|
||||
@@ -1543,7 +1532,7 @@ export const wcwidth = (function(opts) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function wcwidthBMP(ucs) {
|
||||
function wcwidthBMP(ucs: number): number {
|
||||
// test for 8-bit control characters
|
||||
if (ucs === 0)
|
||||
return opts.nul;
|
||||
@@ -1558,7 +1547,7 @@ export const wcwidth = (function(opts) {
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
function isWideBMP(ucs) {
|
||||
function isWideBMP(ucs: number): boolean {
|
||||
return (
|
||||
ucs >= 0x1100 && (
|
||||
ucs <= 0x115f || // Hangul Jamo init. consonants
|
||||
@@ -1572,7 +1561,7 @@ export const wcwidth = (function(opts) {
|
||||
(ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms
|
||||
(ucs >= 0xffe0 && ucs <= 0xffe6)));
|
||||
}
|
||||
function wcwidthHigh(ucs) {
|
||||
function wcwidthHigh(ucs: number): 0 | 1 | 2 {
|
||||
if (bisearch(ucs, COMBINING_HIGH))
|
||||
return 0;
|
||||
if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) {
|
||||
@@ -1581,8 +1570,8 @@ export const wcwidth = (function(opts) {
|
||||
return 1;
|
||||
}
|
||||
const control = opts.control | 0;
|
||||
let table = null;
|
||||
function init_table() {
|
||||
let table: number[] | Uint32Array = null;
|
||||
function init_table(): number[] | Uint32Array {
|
||||
// lookup table for BMP
|
||||
const CODEPOINTS = 65536; // BMP holds 65536 codepoints
|
||||
const BITWIDTH = 2; // a codepoint can have a width of 0, 1 or 2
|
||||
@@ -1599,7 +1588,7 @@ export const wcwidth = (function(opts) {
|
||||
num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos);
|
||||
table[i] = num;
|
||||
}
|
||||
return table;
|
||||
return table;
|
||||
}
|
||||
// get width from lookup table
|
||||
// position in container : num / CODEPOINTS_PER_ITEM
|
||||
@@ -1613,7 +1602,7 @@ export const wcwidth = (function(opts) {
|
||||
// ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766
|
||||
// we are only interested in 2 LSBs, cut off higher bits
|
||||
// ==> n = n & 3 e.g. 000000000000000000000000000000XX
|
||||
return function (num) {
|
||||
return function (num: number): number {
|
||||
num = num | 0; // get asm.js like optimization under V8
|
||||
if (num < 32)
|
||||
return control | 0;
|
||||
|
||||
+116
-16
@@ -2,8 +2,8 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { LinkMatcherOptions } from './Interfaces';
|
||||
import { LinkMatcherHandler, LinkMatcherValidationCallback } from './Types';
|
||||
import { ILinkMatcherOptions } from './Interfaces';
|
||||
import { LinkMatcherHandler, LinkMatcherValidationCallback, Charset, LineData } from './Types';
|
||||
|
||||
export interface IBrowser {
|
||||
isNode: boolean;
|
||||
@@ -17,7 +17,7 @@ export interface IBrowser {
|
||||
isMSWindows: boolean;
|
||||
}
|
||||
|
||||
export interface ITerminal {
|
||||
export interface ITerminal extends IEventEmitter {
|
||||
element: HTMLElement;
|
||||
rowContainer: HTMLElement;
|
||||
selectionContainer: HTMLElement;
|
||||
@@ -32,7 +32,7 @@ export interface ITerminal {
|
||||
cursorHidden: boolean;
|
||||
cursorState: number;
|
||||
defAttr: number;
|
||||
scrollback: number;
|
||||
options: ITerminalOptions;
|
||||
buffers: IBufferSet;
|
||||
buffer: IBuffer;
|
||||
|
||||
@@ -40,23 +40,110 @@ export interface ITerminal {
|
||||
* Emit the 'data' event and populate the given data.
|
||||
* @param data The data to populate in the event.
|
||||
*/
|
||||
handler(data: string);
|
||||
on(event: string, callback: () => void);
|
||||
scrollDisp(disp: number, suppressScrollEvent: boolean);
|
||||
cancel(ev: Event, force?: boolean);
|
||||
handler(data: string): void;
|
||||
scrollDisp(disp: number, suppressScrollEvent?: boolean): void;
|
||||
cancel(ev: Event, force?: boolean): boolean | void;
|
||||
log(text: string): void;
|
||||
emit(event: string, data: any);
|
||||
reset(): void;
|
||||
showCursor(): void;
|
||||
blankLine(cur?: boolean, isWrapped?: boolean): LineData;
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface encapsulates everything needed from the Terminal by the
|
||||
* InputHandler. This cleanly separates the large amount of methods needed by
|
||||
* InputHandler cleanly from the ITerminal interface.
|
||||
*/
|
||||
export interface IInputHandlingTerminal extends IEventEmitter {
|
||||
element: HTMLElement;
|
||||
options: ITerminalOptions;
|
||||
cols: number;
|
||||
rows: number;
|
||||
charset: Charset;
|
||||
gcharset: number;
|
||||
glevel: number;
|
||||
charsets: Charset[];
|
||||
applicationKeypad: boolean;
|
||||
applicationCursor: boolean;
|
||||
originMode: boolean;
|
||||
insertMode: boolean;
|
||||
wraparoundMode: boolean;
|
||||
defAttr: number;
|
||||
curAttr: number;
|
||||
prefix: string;
|
||||
savedCols: number;
|
||||
x10Mouse: boolean;
|
||||
vt200Mouse: boolean;
|
||||
normalMouse: boolean;
|
||||
mouseEvents: boolean;
|
||||
sendFocus: boolean;
|
||||
utfMouse: boolean;
|
||||
sgrMouse: boolean;
|
||||
urxvtMouse: boolean;
|
||||
cursorHidden: boolean;
|
||||
|
||||
buffers: IBufferSet;
|
||||
buffer: IBuffer;
|
||||
viewport: IViewport;
|
||||
selectionManager: ISelectionManager;
|
||||
|
||||
focus(): void;
|
||||
convertEol: boolean;
|
||||
updateRange(y: number): void;
|
||||
scroll(isWrapped?: boolean): void;
|
||||
nextStop(x?: number): number;
|
||||
setgLevel(g: number): void;
|
||||
eraseAttr(): number;
|
||||
eraseRight(x: number, y: number): void;
|
||||
eraseLine(y: number): void;
|
||||
eraseLeft(x: number, y: number): void;
|
||||
blankLine(cur?: boolean, isWrapped?: boolean): LineData;
|
||||
prevStop(x?: number): number;
|
||||
is(term: string): boolean;
|
||||
send(data: string): void;
|
||||
setgCharset(g: number, charset: Charset): void;
|
||||
resize(x: number, y: number): void;
|
||||
log(text: string, data?: any): void;
|
||||
reset(): void;
|
||||
showCursor(): void;
|
||||
refresh(start: number, end: number): void;
|
||||
matchColor(r1: number, g1: number, b1: number): number;
|
||||
error(text: string, data?: any): void;
|
||||
setOption(key: string, value: any): void;
|
||||
}
|
||||
|
||||
export interface ITerminalOptions {
|
||||
cancelEvents?: boolean;
|
||||
colors?: string[];
|
||||
cols?: number;
|
||||
convertEol?: boolean;
|
||||
cursorBlink?: boolean;
|
||||
cursorStyle?: string;
|
||||
debug?: boolean;
|
||||
disableStdin?: boolean;
|
||||
geometry?: [number, number];
|
||||
handler?: (data: string) => void;
|
||||
popOnBell?: boolean;
|
||||
rows?: number;
|
||||
screenKeys?: boolean;
|
||||
scrollback?: number;
|
||||
tabStopWidth?: number;
|
||||
termName?: string;
|
||||
useFlowControl?: boolean;
|
||||
visualBell?: boolean;
|
||||
}
|
||||
|
||||
export interface IBuffer {
|
||||
lines: ICircularList<[number, string, number][]>;
|
||||
lines: ICircularList<LineData>;
|
||||
ydisp: number;
|
||||
ybase: number;
|
||||
y: number;
|
||||
x: number;
|
||||
tabs: any;
|
||||
scrollBottom: number;
|
||||
scrollTop: number;
|
||||
savedY: number;
|
||||
savedX: number;
|
||||
}
|
||||
|
||||
export interface IBufferSet {
|
||||
@@ -68,12 +155,19 @@ export interface IBufferSet {
|
||||
activateAltBuffer(): void;
|
||||
}
|
||||
|
||||
export interface IViewport {
|
||||
syncScrollArea(): void;
|
||||
}
|
||||
|
||||
export interface ISelectionManager {
|
||||
selectionText: string;
|
||||
selectionStart: [number, number];
|
||||
selectionEnd: [number, number];
|
||||
|
||||
setSelection(row: number, col: number, length: number);
|
||||
disable(): void;
|
||||
enable(): void;
|
||||
setBuffer(buffer: ICircularList<LineData>): void;
|
||||
setSelection(row: number, col: number, length: number): void;
|
||||
}
|
||||
|
||||
export interface ICharMeasure {
|
||||
@@ -85,15 +179,15 @@ export interface ICharMeasure {
|
||||
export interface ILinkifier {
|
||||
linkifyRow(rowIndex: number): void;
|
||||
attachHypertextLinkHandler(handler: LinkMatcherHandler): void;
|
||||
registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: LinkMatcherOptions): number;
|
||||
registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number;
|
||||
deregisterLinkMatcher(matcherId: number): boolean;
|
||||
}
|
||||
|
||||
export interface ICircularList<T> extends IEventEmitter {
|
||||
length: number;
|
||||
maxLength: number;
|
||||
forEach: (callbackfn: (value: T, index: number) => void) => void;
|
||||
|
||||
forEach(callbackfn: (value: T, index: number, array: T[]) => void): void;
|
||||
get(index: number): T;
|
||||
set(index: number, value: T): void;
|
||||
push(value: T): void;
|
||||
@@ -104,11 +198,17 @@ export interface ICircularList<T> extends IEventEmitter {
|
||||
}
|
||||
|
||||
export interface IEventEmitter {
|
||||
on(type, listener): void;
|
||||
off(type, listener): void;
|
||||
on(type: string, listener: IListenerType): void;
|
||||
off(type: string, listener: IListenerType): void;
|
||||
emit(type: string, data?: any): void;
|
||||
}
|
||||
|
||||
export interface LinkMatcherOptions {
|
||||
export interface IListenerType {
|
||||
(data?: any): void;
|
||||
listener?: (data?: any) => void;
|
||||
};
|
||||
|
||||
export interface ILinkMatcherOptions {
|
||||
/**
|
||||
* The index of the link from the regex.match(text) call. This defaults to 0
|
||||
* (for regular expressions without capture groups).
|
||||
|
||||
+13
-12
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import jsdom = require('jsdom');
|
||||
import { assert } from 'chai';
|
||||
import { ITerminal, ILinkifier } from './Interfaces';
|
||||
@@ -32,7 +33,7 @@ describe('Linkifier', () => {
|
||||
linkifier = new TestLinkifier();
|
||||
});
|
||||
|
||||
function addRow(html: string) {
|
||||
function addRow(html: string): void {
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = html;
|
||||
container.appendChild(element);
|
||||
@@ -57,24 +58,24 @@ describe('Linkifier', () => {
|
||||
document.body.appendChild(container);
|
||||
});
|
||||
|
||||
function clickElement(element: Node) {
|
||||
function clickElement(element: Node): void {
|
||||
const event = document.createEvent('MouseEvent');
|
||||
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
element.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function assertLinkifiesEntireRow(uri: string, done: MochaDone) {
|
||||
addRow(uri);
|
||||
linkifier.linkifyRow(0);
|
||||
setTimeout(() => {
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
|
||||
done();
|
||||
}, 0);
|
||||
function assertLinkifiesEntireRow(uri: string, done: MochaDone): void {
|
||||
addRow(uri);
|
||||
linkifier.linkifyRow(0);
|
||||
setTimeout(() => {
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
|
||||
done();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
describe('http links', () => {
|
||||
function assertLinkifiesEntireRow(uri: string, done: MochaDone) {
|
||||
function assertLinkifiesEntireRow(uri: string, done: MochaDone): void {
|
||||
addRow(uri);
|
||||
linkifier.linkifyRow(0);
|
||||
setTimeout(() => {
|
||||
@@ -87,7 +88,7 @@ describe('Linkifier', () => {
|
||||
});
|
||||
|
||||
describe('link matcher', () => {
|
||||
function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, expectedHtml: string, done: MochaDone) {
|
||||
function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, expectedHtml: string, done: MochaDone): void {
|
||||
addRow(rowText);
|
||||
linkifier.registerLinkMatcher(linkMatcherRegex, () => {});
|
||||
linkifier.linkifyRow(0);
|
||||
|
||||
+4
-4
@@ -2,7 +2,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { LinkMatcherOptions } from './Interfaces';
|
||||
import { ILinkMatcherOptions } from './Interfaces';
|
||||
import { LinkMatcher, LinkMatcherHandler, LinkMatcherValidationCallback } from './Types';
|
||||
|
||||
const INVALID_LINK_CLASS = 'xterm-invalid-link';
|
||||
@@ -60,7 +60,7 @@ export class Linkifier {
|
||||
* @param document The document object.
|
||||
* @param rows The array of rows to apply links to.
|
||||
*/
|
||||
public attachToDom(document: Document, rows: HTMLElement[]) {
|
||||
public attachToDom(document: Document, rows: HTMLElement[]): void {
|
||||
this._document = document;
|
||||
this._rows = rows;
|
||||
}
|
||||
@@ -108,10 +108,10 @@ export class Linkifier {
|
||||
* this searches the textContent of the rows. You will want to use \s to match
|
||||
* a space ' ' character for example.
|
||||
* @param {LinkHandler} handler The callback when the link is called.
|
||||
* @param {LinkMatcherOptions} [options] Options for the link matcher.
|
||||
* @param {ILinkMatcherOptions} [options] Options for the link matcher.
|
||||
* @return {number} The ID of the new matcher, this can be used to deregister.
|
||||
*/
|
||||
public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options: LinkMatcherOptions = {}): number {
|
||||
public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options: ILinkMatcherOptions = {}): number {
|
||||
if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) {
|
||||
throw new Error('handler must be defined');
|
||||
}
|
||||
|
||||
+7
-2
@@ -182,7 +182,12 @@ export class Parser {
|
||||
* @param data The data to parse.
|
||||
*/
|
||||
public parse(data: string): ParserState {
|
||||
let l = data.length, j, cs, ch, code, low;
|
||||
const l = data.length;
|
||||
let j;
|
||||
let cs;
|
||||
let ch;
|
||||
let code;
|
||||
let low;
|
||||
|
||||
if (this._terminal.debug) {
|
||||
this._terminal.log('data: ' + data);
|
||||
@@ -608,7 +613,7 @@ export class Parser {
|
||||
*
|
||||
* @param param the parameter.
|
||||
*/
|
||||
public setParam(param: number) {
|
||||
public setParam(param: number): void {
|
||||
this._terminal.currentParam = param;
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -37,7 +37,7 @@ export class Renderer {
|
||||
// Figure out whether boldness affects
|
||||
// the character width of monospace fonts.
|
||||
if (brokenBold === null) {
|
||||
brokenBold = checkBoldBroken((<any>this._terminal).element);
|
||||
brokenBold = checkBoldBroken(this._terminal.element);
|
||||
}
|
||||
this._spanElementObjectPool = new DomElementObjectPool('span');
|
||||
|
||||
@@ -327,7 +327,7 @@ export class Renderer {
|
||||
* @param start The selection start.
|
||||
* @param end The selection end.
|
||||
*/
|
||||
public refreshSelection(start: [number, number], end: [number, number]) {
|
||||
public refreshSelection(start: [number, number], end: [number, number]): void {
|
||||
// Remove all selections
|
||||
while (this._terminal.selectionContainer.children.length) {
|
||||
this._terminal.selectionContainer.removeChild(this._terminal.selectionContainer.children[0]);
|
||||
@@ -385,16 +385,16 @@ export class Renderer {
|
||||
|
||||
|
||||
// If bold is broken, we can't use it in the terminal.
|
||||
function checkBoldBroken(terminal) {
|
||||
const document = terminal.ownerDocument;
|
||||
function checkBoldBroken(terminalElement: HTMLElement): boolean {
|
||||
const document = terminalElement.ownerDocument;
|
||||
const el = document.createElement('span');
|
||||
el.innerHTML = 'hello world';
|
||||
terminal.appendChild(el);
|
||||
terminalElement.appendChild(el);
|
||||
const w1 = el.offsetWidth;
|
||||
const h1 = el.offsetHeight;
|
||||
el.style.fontWeight = 'bold';
|
||||
const w2 = el.offsetWidth;
|
||||
const h2 = el.offsetHeight;
|
||||
terminal.removeChild(el);
|
||||
terminalElement.removeChild(el);
|
||||
return w1 !== w2 || h1 !== h2;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import jsdom = require('jsdom');
|
||||
import { assert } from 'chai';
|
||||
import { ITerminal, ICircularList } from './Interfaces';
|
||||
@@ -9,11 +10,13 @@ import { CircularList } from './utils/CircularList';
|
||||
import { SelectionManager } from './SelectionManager';
|
||||
import { SelectionModel } from './SelectionModel';
|
||||
import { BufferSet } from './BufferSet';
|
||||
import { MockTerminal } from './utils/TestUtils';
|
||||
import { LineData } from './Types';
|
||||
|
||||
class TestSelectionManager extends SelectionManager {
|
||||
constructor(
|
||||
terminal: ITerminal,
|
||||
buffer: ICircularList<[number, string, number][]>,
|
||||
buffer: ICircularList<LineData>,
|
||||
rowContainer: HTMLElement,
|
||||
charMeasure: CharMeasure
|
||||
) {
|
||||
@@ -37,7 +40,7 @@ describe('SelectionManager', () => {
|
||||
let document: Document;
|
||||
|
||||
let terminal: ITerminal;
|
||||
let bufferLines: ICircularList<[number, string, number][]>;
|
||||
let bufferLines: ICircularList<LineData>;
|
||||
let rowContainer: HTMLElement;
|
||||
let selectionManager: TestSelectionManager;
|
||||
|
||||
@@ -46,16 +49,18 @@ describe('SelectionManager', () => {
|
||||
window = dom.window;
|
||||
document = window.document;
|
||||
rowContainer = document.createElement('div');
|
||||
terminal = <any>{ cols: 80, rows: 2 };
|
||||
terminal.scrollback = 100;
|
||||
terminal = new MockTerminal();
|
||||
terminal.cols = 80;
|
||||
terminal.rows = 2;
|
||||
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);
|
||||
});
|
||||
|
||||
function stringToRow(text: string): [number, string, number][] {
|
||||
let result: [number, string, number][] = [];
|
||||
function stringToRow(text: string): LineData {
|
||||
let result: LineData = [];
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
result.push([0, text.charAt(i), 1]);
|
||||
}
|
||||
@@ -64,7 +69,7 @@ describe('SelectionManager', () => {
|
||||
|
||||
describe('_selectWordAt', () => {
|
||||
it('should expand selection for normal width chars', () => {
|
||||
bufferLines.push(stringToRow('foo bar'));
|
||||
bufferLines.set(0, stringToRow('foo bar'));
|
||||
selectionManager.selectWordAt([0, 0]);
|
||||
assert.equal(selectionManager.selectionText, 'foo');
|
||||
selectionManager.selectWordAt([1, 0]);
|
||||
@@ -81,7 +86,7 @@ describe('SelectionManager', () => {
|
||||
assert.equal(selectionManager.selectionText, 'bar');
|
||||
});
|
||||
it('should expand selection for whitespace', () => {
|
||||
bufferLines.push(stringToRow('a b'));
|
||||
bufferLines.set(0, stringToRow('a b'));
|
||||
selectionManager.selectWordAt([0, 0]);
|
||||
assert.equal(selectionManager.selectionText, 'a');
|
||||
selectionManager.selectWordAt([1, 0]);
|
||||
@@ -95,7 +100,7 @@ describe('SelectionManager', () => {
|
||||
});
|
||||
it('should expand selection for wide characters', () => {
|
||||
// Wide characters use a special format
|
||||
bufferLines.push([
|
||||
bufferLines.set(0, [
|
||||
[null, '中', 2],
|
||||
[null, '', 0],
|
||||
[null, '文', 2],
|
||||
@@ -147,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.push(stringToRow('(cd)[ef]{gh}\'ij"'));
|
||||
bufferLines.set(0, stringToRow('(cd)[ef]{gh}\'ij"'));
|
||||
selectionManager.selectWordAt([0, 0]);
|
||||
assert.equal(selectionManager.selectionText, '(cd');
|
||||
selectionManager.selectWordAt([1, 0]);
|
||||
@@ -185,7 +190,7 @@ describe('SelectionManager', () => {
|
||||
|
||||
describe('_selectLineAt', () => {
|
||||
it('should select the entire line', () => {
|
||||
bufferLines.push(stringToRow('foo bar'));
|
||||
bufferLines.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]);
|
||||
@@ -195,11 +200,12 @@ describe('SelectionManager', () => {
|
||||
|
||||
describe('selectAll', () => {
|
||||
it('should select the entire buffer, beyond the viewport', () => {
|
||||
bufferLines.push(stringToRow('1'));
|
||||
bufferLines.push(stringToRow('2'));
|
||||
bufferLines.push(stringToRow('3'));
|
||||
bufferLines.push(stringToRow('4'));
|
||||
bufferLines.push(stringToRow('5'));
|
||||
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'));
|
||||
selectionManager.selectAll();
|
||||
terminal.buffer.ybase = bufferLines.length - terminal.rows;
|
||||
assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5');
|
||||
|
||||
+13
-12
@@ -7,9 +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 } from './Interfaces';
|
||||
import { ITerminal, ICircularList, ISelectionManager } from './Interfaces';
|
||||
import { SelectionModel } from './SelectionModel';
|
||||
import { translateBufferLineToString } from './utils/BufferLine';
|
||||
import { LineData } from './Types';
|
||||
|
||||
/**
|
||||
* The number of pixels the mouse needs to be above or below the viewport in
|
||||
@@ -66,7 +67,7 @@ enum SelectionMode {
|
||||
* not handled by the SelectionManager but a 'refresh' event is fired when the
|
||||
* selection is ready to be redrawn.
|
||||
*/
|
||||
export class SelectionManager extends EventEmitter {
|
||||
export class SelectionManager extends EventEmitter implements ISelectionManager {
|
||||
protected _model: SelectionModel;
|
||||
|
||||
/**
|
||||
@@ -101,7 +102,7 @@ export class SelectionManager extends EventEmitter {
|
||||
|
||||
constructor(
|
||||
private _terminal: ITerminal,
|
||||
private _buffer: ICircularList<[number, string, number][]>,
|
||||
private _buffer: ICircularList<LineData>,
|
||||
private _rowContainer: HTMLElement,
|
||||
private _charMeasure: CharMeasure
|
||||
) {
|
||||
@@ -116,7 +117,7 @@ export class SelectionManager extends EventEmitter {
|
||||
/**
|
||||
* Initializes listener variables.
|
||||
*/
|
||||
private _initListeners() {
|
||||
private _initListeners(): void {
|
||||
this._mouseMoveListener = event => this._onMouseMove(<MouseEvent>event);
|
||||
this._mouseUpListener = event => this._onMouseUp(<MouseEvent>event);
|
||||
|
||||
@@ -133,7 +134,7 @@ export class SelectionManager extends EventEmitter {
|
||||
* Disables the selection manager. This is useful for when terminal mouse
|
||||
* are enabled.
|
||||
*/
|
||||
public disable() {
|
||||
public disable(): void {
|
||||
this.clearSelection();
|
||||
this._enabled = false;
|
||||
}
|
||||
@@ -141,7 +142,7 @@ export class SelectionManager extends EventEmitter {
|
||||
/**
|
||||
* Enable the selection manager.
|
||||
*/
|
||||
public enable() {
|
||||
public enable(): void {
|
||||
this._enabled = true;
|
||||
}
|
||||
|
||||
@@ -150,7 +151,7 @@ export class SelectionManager extends EventEmitter {
|
||||
* switched in or out.
|
||||
* @param buffer The active buffer.
|
||||
*/
|
||||
public setBuffer(buffer: ICircularList<[number, string, number][]>): void {
|
||||
public setBuffer(buffer: ICircularList<LineData>): void {
|
||||
this._buffer = buffer;
|
||||
this.clearSelection();
|
||||
}
|
||||
@@ -267,7 +268,7 @@ export class SelectionManager extends EventEmitter {
|
||||
* Handle the buffer being trimmed, adjust the selection position.
|
||||
* @param amount The amount the buffer is being trimmed.
|
||||
*/
|
||||
private _onTrim(amount: number) {
|
||||
private _onTrim(amount: number): void {
|
||||
const needsRefresh = this._model.onTrim(amount);
|
||||
if (needsRefresh) {
|
||||
this.refresh();
|
||||
@@ -316,7 +317,7 @@ export class SelectionManager extends EventEmitter {
|
||||
* Handles te mousedown event, setting up for a new selection.
|
||||
* @param event The mousedown event.
|
||||
*/
|
||||
private _onMouseDown(event: MouseEvent) {
|
||||
private _onMouseDown(event: MouseEvent): void {
|
||||
// If we have selection, we want the context menu on right click even if the
|
||||
// terminal is in mouse mode.
|
||||
if (event.button === 2 && this.hasSelection) {
|
||||
@@ -455,7 +456,7 @@ export class SelectionManager extends EventEmitter {
|
||||
* end of the selection and refreshing the selection.
|
||||
* @param event The mousemove event.
|
||||
*/
|
||||
private _onMouseMove(event: MouseEvent) {
|
||||
private _onMouseMove(event: MouseEvent): void {
|
||||
// Record the previous position so we know whether to redraw the selection
|
||||
// at the end.
|
||||
const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null;
|
||||
@@ -511,7 +512,7 @@ export class SelectionManager extends EventEmitter {
|
||||
* The callback that occurs every DRAG_SCROLL_INTERVAL ms that does the
|
||||
* scrolling of the viewport.
|
||||
*/
|
||||
private _dragScroll() {
|
||||
private _dragScroll(): void {
|
||||
if (this._dragScrollAmount) {
|
||||
this._terminal.scrollDisp(this._dragScrollAmount, false);
|
||||
// Re-evaluate selection
|
||||
@@ -528,7 +529,7 @@ export class SelectionManager extends EventEmitter {
|
||||
* Handles the mouseup event, removing the mousedown listeners.
|
||||
* @param event The mouseup event.
|
||||
*/
|
||||
private _onMouseUp(event: MouseEvent) {
|
||||
private _onMouseUp(event: MouseEvent): void {
|
||||
this._removeMouseDownListeners();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { ITerminal } from './Interfaces';
|
||||
import { SelectionModel } from './SelectionModel';
|
||||
import {BufferSet} from './BufferSet';
|
||||
import { MockTerminal } from './utils/TestUtils';
|
||||
|
||||
class TestSelectionModel extends SelectionModel {
|
||||
constructor(
|
||||
@@ -22,8 +24,10 @@ describe('SelectionManager', () => {
|
||||
let model: TestSelectionModel;
|
||||
|
||||
beforeEach(() => {
|
||||
terminal = <any>{ cols: 80, rows: 2, ybase: 0 };
|
||||
terminal.scrollback = 10;
|
||||
terminal = new MockTerminal();
|
||||
terminal.cols = 80;
|
||||
terminal.rows = 2;
|
||||
terminal.options.scrollback = 10;
|
||||
terminal.buffers = new BufferSet(terminal);
|
||||
terminal.buffer = terminal.buffers.active;
|
||||
|
||||
|
||||
+2345
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user