From 45157d80c66bdf75c2c79ad8ec81929134e797ee Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 6 Aug 2017 12:59:40 -0700 Subject: [PATCH 01/17] Properly cancel event when in mouse mode Fixes #848 --- src/Terminal.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index cd44e7fb..b6a9d104 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1100,7 +1100,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT if (!this.mouseEvents) return; if (this.x10Mouse || this.vt300Mouse || this.decLocator) return; sendButton(ev); - return this.cancel(ev); + ev.preventDefault(); }); // allow wheel scrolling in @@ -2247,6 +2247,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT this.buffer.tabs[this.buffer.x] = true; } + // TODO: Remove cancel function and cancelEvents option public cancel(ev: Event, force?: boolean): boolean { if (!this.options.cancelEvents && !force) { return; From 63268318ed26469afe9a31898941767a8e284367 Mon Sep 17 00:00:00 2001 From: Peter Baumgarten Date: Sun, 6 Aug 2017 15:09:11 -0500 Subject: [PATCH 02/17] Marking that a C++ compiler is required. On some linux distributions a C++ compiler is not installed by default. This can frustrate some new users. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ef9ea422..5a5f8e51 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,9 @@ Xterm.js works seamlessly in Electron apps and may even work on earlier versions ### Linux or macOS -Run the following commands: +First, be sure that a C++ compiler such as GCC-C++ or Clang is installed + +Then run the following commands: ``` $ npm install From 26977b370ecd77e44503c8de1a4e2fb92d547adb Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 6 Aug 2017 13:47:03 -0700 Subject: [PATCH 03/17] Fix crash on negative tab stop width Fixes #851 --- src/Terminal.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Terminal.ts b/src/Terminal.ts index cd44e7fb..d410f51f 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -438,6 +438,12 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT throw new Error('No option with key "' + key + '"'); } switch (key) { + case 'tabStopWidth': + if (value < 1) { + console.warn(`tabStopWidth cannot be less than 1, value: ${value}`); + return; + } + break; case 'scrollback': if (value < this.rows) { let msg = 'Setting the scrollback value less than the number of rows '; From fb6cbbdaad67801c2850d8f86994dfcf0b327ea0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 6 Aug 2017 19:02:56 -0700 Subject: [PATCH 04/17] Fix vscode debugging This broke when the TS addon was added --- src/addons/search/search.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/addons/search/search.ts b/src/addons/search/search.ts index 1a9ac337..0b506b69 100644 --- a/src/addons/search/search.ts +++ b/src/addons/search/search.ts @@ -11,7 +11,7 @@ declare var require: any; declare var window: any; (function (addon) { - if ('Terminal' in window) { + if (typeof window !== 'undefined' && 'Terminal' in window) { /** * Plain browser environment */ @@ -21,7 +21,7 @@ declare var window: any; * CommonJS environment */ module.exports = addon(require('../../xterm')); - } else if (typeof define == 'function') { + } else if (typeof define === 'function') { /** * Require.js is available */ From 8c46f1b6baab79015877d0a72fa3c3fde39fcdb6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 6 Aug 2017 19:19:49 -0700 Subject: [PATCH 05/17] Remove deprecated attachCustomKeydownHandler Fixes #744 --- src/Terminal.ts | 12 ------------ src/test/test.js | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index cd44e7fb..43d42fbe 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1358,18 +1358,6 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT this.write(data + '\r\n'); } - /** - * DEPRECATED: only for backward compatibility. Please use attachCustomKeyEventHandler() instead. - * @param {function} customKeydownHandler The custom KeyboardEvent handler to attach. This is a - * function that takes a KeyboardEvent, allowing consumers to stop propogation and/or prevent - * the default action. The function returns whether the event should be processed by xterm.js. - */ - public attachCustomKeydownHandler(customKeydownHandler: CustomKeyEventHandler): void { - const message = 'attachCustomKeydownHandler() is DEPRECATED and will be removed soon. Please use attachCustomKeyEventHandler() instead.'; - console.warn(message); - this.attachCustomKeyEventHandler(customKeydownHandler); - } - /** * Attaches a custom key event handler which is run before keys are processed, giving consumers of * xterm.js ultimate control as to what keys should be processed by the terminal and what keys diff --git a/src/test/test.js b/src/test/test.js index 5c4dd5f2..df1dab4b 100644 --- a/src/test/test.js +++ b/src/test/test.js @@ -239,7 +239,7 @@ describe('xterm.js', function() { xterm.writeln('test'); } var startYDisp = (xterm.rows * 2) + 1; - xterm.attachCustomKeydownHandler(function () { + xterm.attachCustomKeyEventHandler(function () { return false; }); From b1c6a9783956d8bde3d7214b6dc6c7c156fd0830 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 6 Aug 2017 19:22:15 -0700 Subject: [PATCH 06/17] No longer focus the terminal by default Fixes #646 --- src/Terminal.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index cd44e7fb..493e48bc 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -759,20 +759,6 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT // Initialize global actions that need to be taken on the document. this.initGlobal(); - /** - * Automatic focus functionality. - * TODO: Default to `false` starting with xterm.js 3.0. - */ - if (typeof focus === 'undefined') { - let message = 'You did not pass the `focus` argument in `Terminal.prototype.open()`.\n'; - - message += 'The `focus` argument now defaults to `true` but starting with xterm.js 3.0 '; - message += 'it will default to `false`.'; - - console.warn(message); - focus = true; - } - if (focus) { this.focus(); } From c2bb507fe4ffa279a15c85deafaa77ca11d12a1a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Aug 2017 15:21:55 -0700 Subject: [PATCH 07/17] Move BufferLines.ts into Buffer.ts Part of #791 --- src/Buffer.ts | 51 +++++++++++++++++++++++++++++++++ src/InputHandler.ts | 3 +- src/Interfaces.ts | 3 +- src/SelectionManager.test.ts | 34 +++++++++++----------- src/SelectionManager.ts | 29 +++++++++---------- src/Terminal.ts | 5 ++-- src/utils/BufferLine.ts | 55 ------------------------------------ src/utils/TestUtils.ts | 3 ++ 8 files changed, 91 insertions(+), 92 deletions(-) delete mode 100644 src/utils/BufferLine.ts diff --git a/src/Buffer.ts b/src/Buffer.ts index eb346d5b..ed3309b6 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -6,6 +6,9 @@ import { ITerminal, IBuffer } from './Interfaces'; import { CircularList } from './utils/CircularList'; import { LineData, CharData } from './Types'; +const CHAR_DATA_CHAR_INDEX = 1; +const CHAR_DATA_WIDTH_INDEX = 2; + /** * This class represents a terminal buffer (an internal state of the terminal), where the * following information is stored (in high-level): @@ -146,4 +149,52 @@ export class Buffer implements IBuffer { this.scrollTop = 0; this.scrollBottom = newRows - 1; } + + /** + * Translates a buffer line to a string, with optional start and end columns. + * Wide characters will count as two columns in the resulting string. This + * function is useful for getting the actual text underneath the raw selection + * position. + * @param line The line being translated. + * @param trimRight Whether to trim whitespace to the right. + * @param startCol The column to start at. + * @param endCol The column to end at. + */ + public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol: number = 0, endCol: number = null): string { + // Get full line + let lineString = ''; + let widthAdjustedStartCol = startCol; + let widthAdjustedEndCol = endCol; + const line = this.lines.get(lineIndex); + for (let i = 0; i < line.length; i++) { + const char = line[i]; + lineString += char[CHAR_DATA_CHAR_INDEX]; + // Adjust start and end cols for wide characters if they affect their + // column indexes + if (char[CHAR_DATA_WIDTH_INDEX] === 0) { + if (startCol >= i) { + widthAdjustedStartCol--; + } + if (endCol >= i) { + widthAdjustedEndCol--; + } + } + } + + // Calculate the final end col by trimming whitespace on the right of the + // line if needed. + let finalEndCol = widthAdjustedEndCol || line.length; + if (trimRight) { + const rightWhitespaceIndex = lineString.search(/\s+$/); + if (rightWhitespaceIndex !== -1) { + finalEndCol = Math.min(finalEndCol, rightWhitespaceIndex); + } + // Return the empty string if only trimmed whitespace is selected + if (finalEndCol <= widthAdjustedStartCol) { + return ''; + } + } + + return lineString.substring(widthAdjustedStartCol, finalEndCol); + } } diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 5362cae9..defff541 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -945,6 +945,7 @@ export class InputHandler implements IInputHandler { case 47: // alt screen buffer case 1047: // alt screen buffer this._terminal.buffers.activateAltBuffer(); + this._terminal.selectionManager.setBuffer(this._terminal.buffer); this._terminal.viewport.syncScrollArea(); this._terminal.showCursor(); break; @@ -1113,7 +1114,7 @@ export class InputHandler implements IInputHandler { // if (params[0] === 1049) { // this.restoreCursor(params); // } - this._terminal.selectionManager.setBuffer(this._terminal.buffer.lines); + this._terminal.selectionManager.setBuffer(this._terminal.buffer); this._terminal.refresh(0, this._terminal.rows - 1); this._terminal.viewport.syncScrollArea(); this._terminal.showCursor(); diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 55317fbc..f97d9294 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -144,6 +144,7 @@ export interface IBuffer { scrollTop: number; savedY: number; savedX: number; + translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; } export interface IBufferSet { @@ -166,7 +167,7 @@ export interface ISelectionManager { disable(): void; enable(): void; - setBuffer(buffer: ICircularList): void; + setBuffer(buffer: IBuffer): void; setSelection(row: number, col: number, length: number): void; } diff --git a/src/SelectionManager.test.ts b/src/SelectionManager.test.ts index 698cda7e..c7fd9aef 100644 --- a/src/SelectionManager.test.ts +++ b/src/SelectionManager.test.ts @@ -4,7 +4,7 @@ import jsdom = require('jsdom'); import { assert } from 'chai'; -import { ITerminal, ICircularList } from './Interfaces'; +import { ITerminal, ICircularList, IBuffer } from './Interfaces'; import { CharMeasure } from './utils/CharMeasure'; import { CircularList } from './utils/CircularList'; import { SelectionManager } from './SelectionManager'; @@ -16,7 +16,7 @@ import { LineData } from './Types'; class TestSelectionManager extends SelectionManager { constructor( terminal: ITerminal, - buffer: ICircularList, + buffer: IBuffer, rowContainer: HTMLElement, charMeasure: CharMeasure ) { @@ -40,7 +40,7 @@ describe('SelectionManager', () => { let document: Document; let terminal: ITerminal; - let bufferLines: ICircularList; + let buffer: IBuffer; let rowContainer: HTMLElement; let selectionManager: TestSelectionManager; @@ -55,8 +55,8 @@ describe('SelectionManager', () => { terminal.options.scrollback = 100; terminal.buffers = new BufferSet(terminal); terminal.buffer = terminal.buffers.active; - bufferLines = terminal.buffer.lines; - selectionManager = new TestSelectionManager(terminal, bufferLines, rowContainer, null); + buffer = terminal.buffer; + selectionManager = new TestSelectionManager(terminal, buffer, rowContainer, null); }); function stringToRow(text: string): LineData { @@ -69,7 +69,7 @@ describe('SelectionManager', () => { describe('_selectWordAt', () => { it('should expand selection for normal width chars', () => { - bufferLines.set(0, stringToRow('foo bar')); + buffer.lines.set(0, stringToRow('foo bar')); selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, 'foo'); selectionManager.selectWordAt([1, 0]); @@ -86,7 +86,7 @@ describe('SelectionManager', () => { assert.equal(selectionManager.selectionText, 'bar'); }); it('should expand selection for whitespace', () => { - bufferLines.set(0, stringToRow('a b')); + buffer.lines.set(0, stringToRow('a b')); selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, 'a'); selectionManager.selectWordAt([1, 0]); @@ -100,7 +100,7 @@ describe('SelectionManager', () => { }); it('should expand selection for wide characters', () => { // Wide characters use a special format - bufferLines.set(0, [ + buffer.lines.set(0, [ [null, '中', 2], [null, '', 0], [null, '文', 2], @@ -152,7 +152,7 @@ describe('SelectionManager', () => { assert.equal(selectionManager.selectionText, 'foo'); }); it('should select up to non-path characters that are commonly adjacent to paths', () => { - bufferLines.set(0, stringToRow('(cd)[ef]{gh}\'ij"')); + buffer.lines.set(0, stringToRow('(cd)[ef]{gh}\'ij"')); selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, '(cd'); selectionManager.selectWordAt([1, 0]); @@ -190,7 +190,7 @@ describe('SelectionManager', () => { describe('_selectLineAt', () => { it('should select the entire line', () => { - bufferLines.set(0, stringToRow('foo bar')); + buffer.lines.set(0, stringToRow('foo bar')); selectionManager.selectLineAt(0); assert.equal(selectionManager.selectionText, 'foo bar', 'The selected text is correct'); assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]); @@ -200,14 +200,14 @@ describe('SelectionManager', () => { describe('selectAll', () => { it('should select the entire buffer, beyond the viewport', () => { - bufferLines.length = 5; - bufferLines.set(0, stringToRow('1')); - bufferLines.set(1, stringToRow('2')); - bufferLines.set(2, stringToRow('3')); - bufferLines.set(3, stringToRow('4')); - bufferLines.set(4, stringToRow('5')); + buffer.lines.length = 5; + buffer.lines.set(0, stringToRow('1')); + buffer.lines.set(1, stringToRow('2')); + buffer.lines.set(2, stringToRow('3')); + buffer.lines.set(3, stringToRow('4')); + buffer.lines.set(4, stringToRow('5')); selectionManager.selectAll(); - terminal.buffer.ybase = bufferLines.length - terminal.rows; + terminal.buffer.ybase = buffer.lines.length - terminal.rows; assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5'); }); }); diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 781a8eda..f217e721 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -7,9 +7,8 @@ import * as Browser from './utils/Browser'; import { CharMeasure } from './utils/CharMeasure'; import { CircularList } from './utils/CircularList'; import { EventEmitter } from './EventEmitter'; -import { ITerminal, ICircularList, ISelectionManager } from './Interfaces'; +import { ITerminal, ICircularList, ISelectionManager, IBuffer } from './Interfaces'; import { SelectionModel } from './SelectionModel'; -import { translateBufferLineToString } from './utils/BufferLine'; import { LineData } from './Types'; /** @@ -102,7 +101,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager constructor( private _terminal: ITerminal, - private _buffer: ICircularList, + private _buffer: IBuffer, private _rowContainer: HTMLElement, private _charMeasure: CharMeasure ) { @@ -127,7 +126,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager // reverseIndex) and delete in a splice is only ever used when the same // number of elements was just added. Given this is could actually be // beneficial to leave the selection as is for these cases. - this._buffer.on('trim', (amount: number) => this._onTrim(amount)); + this._buffer.lines.on('trim', (amount: number) => this._onTrim(amount)); } /** @@ -151,7 +150,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager * switched in or out. * @param buffer The active buffer. */ - public setBuffer(buffer: ICircularList): void { + public setBuffer(buffer: IBuffer): void { this._buffer = buffer; this.clearSelection(); } @@ -184,12 +183,12 @@ export class SelectionManager extends EventEmitter implements ISelectionManager // Get first row const startRowEndCol = start[1] === end[1] ? end[0] : null; let result: string[] = []; - result.push(translateBufferLineToString(this._buffer.get(start[1]), true, start[0], startRowEndCol)); + result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol)); // Get middle rows for (let i = start[1] + 1; i <= end[1] - 1; i++) { - const bufferLine = this._buffer.get(i); - const lineText = translateBufferLineToString(bufferLine, true); + const bufferLine = this._buffer.lines.get(i); + const lineText = this._buffer.translateBufferLineToString(i, true); if ((bufferLine).isWrapped) { result[result.length - 1] += lineText; } else { @@ -199,8 +198,8 @@ export class SelectionManager extends EventEmitter implements ISelectionManager // Get final row if (start[1] !== end[1]) { - const bufferLine = this._buffer.get(end[1]); - const lineText = translateBufferLineToString(bufferLine, true, 0, end[0]); + const bufferLine = this._buffer.lines.get(end[1]); + const lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]); if ((bufferLine).isWrapped) { result[result.length - 1] += lineText; } else { @@ -413,7 +412,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager this._model.selectionEnd = null; // Ensure the line exists - const line = this._buffer.get(this._model.selectionStart[1]); + const line = this._buffer.lines.get(this._model.selectionStart[1]); if (!line) { return; } @@ -493,8 +492,8 @@ export class SelectionManager extends EventEmitter implements ISelectionManager // If the character is a wide character include the cell to the right in the // selection. Note that selections at the very end of the line will never // have a character. - if (this._model.selectionEnd[1] < this._buffer.length) { - const char = this._buffer.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]]; + if (this._model.selectionEnd[1] < this._buffer.lines.length) { + const char = this._buffer.lines.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]]; if (char && char[2] === 0) { this._model.selectionEnd[0]++; } @@ -562,12 +561,12 @@ export class SelectionManager extends EventEmitter implements ISelectionManager * @param coords The coordinates to get the word at. */ private _getWordAt(coords: [number, number]): IWordPosition { - const bufferLine = this._buffer.get(coords[1]); + const bufferLine = this._buffer.lines.get(coords[1]); if (!bufferLine) { return null; } - const line = translateBufferLineToString(bufferLine, false); + const line = this._buffer.translateBufferLineToString(coords[1], false); // Get actual index, taking into consideration wide characters let endIndex = this._convertViewportColToCharacterIndex(bufferLine, coords); diff --git a/src/Terminal.ts b/src/Terminal.ts index 4536733e..31eaef8a 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -28,7 +28,6 @@ import * as Browser from './utils/Browser'; import * as Mouse from './utils/Mouse'; import { CHARSETS } from './Charsets'; import { getRawByteCoords } from './utils/Mouse'; -import { translateBufferLineToString } from './utils/BufferLine'; import { CustomKeyEventHandler, Charset, LinkMatcherHandler, LinkMatcherValidationCallback, CharData, LineData, Option, StringOption, BooleanOption, StringArrayOption, NumberOption, GeometryOption, HandlerOption } from './Types'; import { ITerminal, IBrowser, ITerminalOptions, IInputHandlingTerminal, ILinkMatcherOptions } from './Interfaces'; @@ -379,7 +378,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT // Ensure the selection manager has the correct buffer if (this.selectionManager) { - this.selectionManager.setBuffer(this.buffer.lines); + this.selectionManager.setBuffer(this.buffer); } this.setupStops(); @@ -744,7 +743,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure); this.renderer = new Renderer(this); - this.selectionManager = new SelectionManager(this, this.buffer.lines, this.rowContainer, this.charMeasure); + this.selectionManager = new SelectionManager(this, this.buffer, this.rowContainer, this.charMeasure); this.selectionManager.on('refresh', data => { this.renderer.refreshSelection(data.start, data.end); }); diff --git a/src/utils/BufferLine.ts b/src/utils/BufferLine.ts deleted file mode 100644 index 3dc6525a..00000000 --- a/src/utils/BufferLine.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @license MIT - */ - -// TODO: This module should be merged into a buffer or buffer line class - -const LINE_DATA_CHAR_INDEX = 1; -const LINE_DATA_WIDTH_INDEX = 2; - -/** - * Translates a buffer line to a string, with optional start and end columns. - * Wide characters will count as two columns in the resulting string. This - * function is useful for getting the actual text underneath the raw selection - * position. - * @param line The line being translated. - * @param trimRight Whether to trim whitespace to the right. - * @param startCol The column to start at. - * @param endCol The column to end at. - */ -export function translateBufferLineToString(line: any, trimRight: boolean, startCol: number = 0, endCol: number = null): string { - // Get full line - let lineString = ''; - let widthAdjustedStartCol = startCol; - let widthAdjustedEndCol = endCol; - for (let i = 0; i < line.length; i++) { - const char = line[i]; - lineString += char[LINE_DATA_CHAR_INDEX]; - // Adjust start and end cols for wide characters if they affect their - // column indexes - if (char[LINE_DATA_WIDTH_INDEX] === 0) { - if (startCol >= i) { - widthAdjustedStartCol--; - } - if (endCol >= i) { - widthAdjustedEndCol--; - } - } - } - - // Calculate the final end col by trimming whitespace on the right of the - // line if needed. - let finalEndCol = widthAdjustedEndCol || line.length; - if (trimRight) { - const rightWhitespaceIndex = lineString.search(/\s+$/); - if (rightWhitespaceIndex !== -1) { - finalEndCol = Math.min(finalEndCol, rightWhitespaceIndex); - } - // Return the empty string if only trimmed whitespace is selected - if (finalEndCol <= widthAdjustedStartCol) { - return ''; - } - } - - return lineString.substring(widthAdjustedStartCol, finalEndCol); -} diff --git a/src/utils/TestUtils.ts b/src/utils/TestUtils.ts index 159da055..fc785588 100644 --- a/src/utils/TestUtils.ts +++ b/src/utils/TestUtils.ts @@ -180,4 +180,7 @@ export class MockBuffer implements IBuffer { scrollTop: number; savedY: number; savedX: number; + translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string { + throw new Error('Method not implemented.'); + } } From fd61c8e8742da24c39867eeebf46e3f0ac6728d8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Aug 2017 15:37:22 -0700 Subject: [PATCH 08/17] Use a const for all refs to data char and width Part of #791 --- src/Buffer.ts | 4 +- src/InputHandler.ts | 11 +- src/Renderer.ts | 5 +- src/SelectionManager.ts | 20 +-- src/test/escape-sequences-test.js | 3 +- src/test/test.js | 236 +++++++++++++++--------------- 6 files changed, 140 insertions(+), 139 deletions(-) diff --git a/src/Buffer.ts b/src/Buffer.ts index ed3309b6..46fe6ba8 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -6,8 +6,8 @@ import { ITerminal, IBuffer } from './Interfaces'; import { CircularList } from './utils/CircularList'; import { LineData, CharData } from './Types'; -const CHAR_DATA_CHAR_INDEX = 1; -const CHAR_DATA_WIDTH_INDEX = 2; +export const CHAR_DATA_CHAR_INDEX = 1; +export const CHAR_DATA_WIDTH_INDEX = 2; /** * This class represents a terminal buffer (an internal state of the terminal), where the diff --git a/src/InputHandler.ts b/src/InputHandler.ts index defff541..1c54abef 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -6,6 +6,7 @@ import { IInputHandler, ITerminal, IInputHandlingTerminal } from './Interfaces'; import { C0 } from './EscapeSequences'; import { DEFAULT_CHARSET } from './Charsets'; import { CharData } from './Types'; +import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from './Buffer'; /** * The terminal's standard implementation of IInputHandler, this handles all @@ -34,14 +35,14 @@ export class InputHandler implements IInputHandler { if (!ch_width && this._terminal.buffer.x) { // dont overflow left if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1]) { - if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][2]) { + if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][CHAR_DATA_WIDTH_INDEX]) { // found empty cell after fullwidth, need to go 2 cells back if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2]) - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][1] += char; + this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][CHAR_DATA_CHAR_INDEX] += char; } else { - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][1] += char; + this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][CHAR_DATA_CHAR_INDEX] += char; } this._terminal.updateRange(this._terminal.buffer.y); } @@ -77,9 +78,9 @@ export class InputHandler implements IInputHandler { // remove last cell, if it's width is 0 // we have to adjust the second last cell as well const removed = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).pop(); - if (removed[2] === 0 + if (removed[CHAR_DATA_WIDTH_INDEX] === 0 && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] - && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2][2] === 2) { + && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2][CHAR_DATA_WIDTH_INDEX] === 2) { this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] = [this._terminal.curAttr, ' ', 1]; } diff --git a/src/Renderer.ts b/src/Renderer.ts index f706bffc..f2999afa 100644 --- a/src/Renderer.ts +++ b/src/Renderer.ts @@ -4,6 +4,7 @@ import { ITerminal } from './Interfaces'; import { DomElementObjectPool } from './utils/DomElementObjectPool'; +import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer'; /** * The maximum number of refresh frames to skip when the write buffer is non- @@ -168,8 +169,8 @@ export class Renderer { for (let i = 0; i < width; i++) { // TODO: Could data be a more specific type? let data: any = line[i][0]; - const ch = line[i][1]; - const ch_width: any = line[i][2]; + const ch = line[i][CHAR_DATA_CHAR_INDEX]; + const ch_width: any = line[i][CHAR_DATA_WIDTH_INDEX]; const isCursor: boolean = i === x; if (!ch_width) { continue; diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index f217e721..ee03f79a 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -10,6 +10,7 @@ import { EventEmitter } from './EventEmitter'; import { ITerminal, ICircularList, ISelectionManager, IBuffer } from './Interfaces'; import { SelectionModel } from './SelectionModel'; import { LineData } from './Types'; +import { CHAR_DATA_WIDTH_INDEX } from './Buffer'; /** * The number of pixels the mouse needs to be above or below the viewport in @@ -33,11 +34,6 @@ const DRAG_SCROLL_INTERVAL = 50; */ const WORD_SEPARATORS = ' ()[]{}\'"'; -// TODO: Move these constants elsewhere, they belong in a buffer or buffer -// data/line class. -const LINE_DATA_CHAR_INDEX = 1; -const LINE_DATA_WIDTH_INDEX = 2; - const NON_BREAKING_SPACE_CHAR = String.fromCharCode(160); const ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g'); @@ -420,7 +416,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager // If the mouse is over the second half of a wide character, adjust the // selection to cover the whole character const char = line[this._model.selectionStart[0]]; - if (char[LINE_DATA_WIDTH_INDEX] === 0) { + if (char[CHAR_DATA_WIDTH_INDEX] === 0) { this._model.selectionStart[0]++; } } @@ -494,7 +490,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager // have a character. if (this._model.selectionEnd[1] < this._buffer.lines.length) { const char = this._buffer.lines.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]]; - if (char && char[2] === 0) { + if (char && char[CHAR_DATA_WIDTH_INDEX] === 0) { this._model.selectionEnd[0]++; } } @@ -541,7 +537,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager let charIndex = coords[0]; for (let i = 0; coords[0] >= i; i++) { const char = bufferLine[i]; - if (char[LINE_DATA_WIDTH_INDEX] === 0) { + if (char[CHAR_DATA_WIDTH_INDEX] === 0) { charIndex--; } } @@ -594,17 +590,17 @@ export class SelectionManager extends EventEmitter implements ISelectionManager let endCol = coords[0]; // Consider the initial position, skip it and increment the wide char // variable - if (bufferLine[startCol][LINE_DATA_WIDTH_INDEX] === 0) { + if (bufferLine[startCol][CHAR_DATA_WIDTH_INDEX] === 0) { leftWideCharCount++; startCol--; } - if (bufferLine[endCol][LINE_DATA_WIDTH_INDEX] === 2) { + if (bufferLine[endCol][CHAR_DATA_WIDTH_INDEX] === 2) { rightWideCharCount++; endCol++; } // Expand the string in both directions until a space is hit while (startIndex > 0 && !this._isCharWordSeparator(line.charAt(startIndex - 1))) { - if (bufferLine[startCol - 1][LINE_DATA_WIDTH_INDEX] === 0) { + if (bufferLine[startCol - 1][CHAR_DATA_WIDTH_INDEX] === 0) { // If the next character is a wide char, record it and skip the column leftWideCharCount++; startCol--; @@ -613,7 +609,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager startCol--; } while (endIndex + 1 < line.length && !this._isCharWordSeparator(line.charAt(endIndex + 1))) { - if (bufferLine[endCol + 1][LINE_DATA_WIDTH_INDEX] === 2) { + if (bufferLine[endCol + 1][CHAR_DATA_WIDTH_INDEX] === 2) { // If the next character is a wide char, record it and skip the column rightWideCharCount++; endCol++; diff --git a/src/test/escape-sequences-test.js b/src/test/escape-sequences-test.js index d0447451..51e5d017 100644 --- a/src/test/escape-sequences-test.js +++ b/src/test/escape-sequences-test.js @@ -3,6 +3,7 @@ var fs = require('fs'); var os = require('os'); var pty = require('node-pty'); var Terminal = require('../xterm'); +var CHAR_DATA_CHAR_INDEX = require('../Buffer').CHAR_DATA_CHAR_INDEX; if (os.platform() === 'win32') { // Skip tests on Windows since pty.open isn't supported @@ -65,7 +66,7 @@ function terminalToString(term) { for (var line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) { line_s = ''; for (var cell=0; cell Date: Mon, 7 Aug 2017 22:15:13 -0700 Subject: [PATCH 09/17] addons/test.js and test.js -> Terminal.test.ts Part of #862 --- src/Interfaces.ts | 11 + src/Terminal.test.ts | 907 ++++++++++++++++++++++++++++++++++++++++ src/Terminal.ts | 42 +- src/Viewport.test.ts | 2 +- src/test/addons/test.js | 10 - src/test/test.js | 889 --------------------------------------- src/utils/TestUtils.ts | 36 +- 7 files changed, 974 insertions(+), 923 deletions(-) create mode 100644 src/Terminal.test.ts delete mode 100644 src/test/addons/test.js delete mode 100644 src/test/test.js diff --git a/src/Interfaces.ts b/src/Interfaces.ts index f97d9294..f4ae917f 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -158,6 +158,9 @@ export interface IBufferSet { export interface IViewport { syncScrollArea(): void; + onWheel(ev: WheelEvent): void; + onTouchStart(ev: TouchEvent): void; + onTouchMove(ev: TouchEvent): void; } export interface ISelectionManager { @@ -171,6 +174,14 @@ export interface ISelectionManager { setSelection(row: number, col: number, length: number): void; } +export interface ICompositionHelper { + compositionstart(): void; + compositionupdate(ev: CompositionEvent): void; + compositionend(): void; + updateCompositionElements(dontRecurse?: boolean): void; + keydown(ev: KeyboardEvent): boolean; +} + export interface ICharMeasure { width: number; height: number; diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts new file mode 100644 index 00000000..753733c2 --- /dev/null +++ b/src/Terminal.test.ts @@ -0,0 +1,907 @@ +import { assert, expect } from 'chai'; +import { Terminal } from './Terminal'; +import { MockViewport, MockCompositionHelper } from './utils/TestUtils'; +import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from './Buffer'; + +class TestTerminal extends Terminal { + public evaluateKeyEscapeSequence(ev: any): {cancel: boolean, key: string, scrollDisp: number} { return this._evaluateKeyEscapeSequence(ev); } + public keyDown(ev: any): boolean { return this._keyDown(ev); } + public keyPress(ev: any): boolean { return this._keyPress(ev); } +} + +describe('term.js addons', () => { + let term: TestTerminal; + + beforeEach(() => { + term = new TestTerminal(); + term.refresh = () => {}; + term.viewport = new MockViewport(); + (term).compositionHelper = new MockCompositionHelper(); + // Force synchronous writes + term.write = (data) => { + term.writeBuffer.push(data); + (term).innerWrite(); + }; + (term).element = { + classList: { + toggle: () => {}, + remove: () => {} + } + }; + }); + + it('should load addons with Terminal.loadAddon', () => { + Terminal.loadAddon('attach'); + // Test that addon was loaded successfully, adding attach to Terminal's + // prototype. + assert.equal(typeof (Terminal).prototype.attach, 'function'); + }); + + describe('getOption', () => { + it('should retrieve the option correctly', () => { + // In the `options` namespace. + term.options.cursorBlink = true; + assert.equal(term.getOption('cursorBlink'), true); + + // On the Terminal instance + delete term.options.cursorBlink; + term.options.cursorBlink = false; + assert.equal(term.getOption('cursorBlink'), false); + }); + it('should throw when retrieving a non-existant option', () => { + assert.throws(term.getOption.bind(term, 'fake', true)); + }); + }); + + describe('attachCustomKeyEventHandler', () => { + let evKeyDown = { + preventDefault: () => {}, + stopPropagation: () => {}, + type: 'keydown', + keyCode: 77 + }; + let evKeyPress = { + preventDefault: () => {}, + stopPropagation: () => {}, + type: 'keypress', + keyCode: 77 + }; + + beforeEach(() => { + term.handler = () => {}; + term.showCursor = () => {}; + term.clearSelection = () => {}; + }); + + it('should process the keydown/keypress event based on what the handler returns', () => { + assert.equal(term.keyDown(evKeyDown), true); + assert.equal(term.keyPress(evKeyPress), true); + term.attachCustomKeyEventHandler(ev => ev.keyCode === 77); + assert.equal(term.keyDown(evKeyDown), true); + assert.equal(term.keyPress(evKeyPress), true); + term.attachCustomKeyEventHandler(ev => ev.keyCode !== 77); + assert.equal(term.keyDown(evKeyDown), false); + assert.equal(term.keyPress(evKeyPress), false); + }); + + it('should alive after reset(ESC c Full Reset (RIS))', () => { + term.attachCustomKeyEventHandler(ev => ev.keyCode !== 77); + assert.equal(term.keyDown(evKeyDown), false); + assert.equal(term.keyPress(evKeyPress), false); + term.reset(); + assert.equal(term.keyDown(evKeyDown), false); + assert.equal(term.keyPress(evKeyPress), false); + }); + }); + + describe('setOption', () => { + let originalWarn; + let warnCallCount; + beforeEach(() => { + originalWarn = console.warn; + warnCallCount = 0; + console.warn = () => warnCallCount++; + }); + afterEach(() => { + console.warn = originalWarn; + }); + it('should set the option correctly', () => { + term.setOption('cursorBlink', true); + assert.equal(term.options.cursorBlink, true); + term.setOption('cursorBlink', false); + assert.equal(term.options.cursorBlink, false); + }); + it('should throw when setting a non-existant option', () => { + assert.throws(term.setOption.bind(term, 'fake', true)); + }); + it('should warn and do nothing when scrollback is less than number of rows', () => { + term.setOption('scrollback', term.rows - 1); + assert.equal(term.getOption('scrollback'), 1000); + assert.equal(warnCallCount, 1); + }); + }); + + describe('clear', () => { + it('should clear a buffer equal to rows', () => { + let promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y); + term.clear(); + assert.equal(term.buffer.y, 0); + assert.equal(term.buffer.ybase, 0); + assert.equal(term.buffer.ydisp, 0); + assert.equal(term.buffer.lines.length, term.rows); + assert.deepEqual(term.buffer.lines.get(0), promptLine); + for (let i = 1; i < term.rows; i++) { + assert.deepEqual(term.buffer.lines.get(i), term.blankLine()); + } + }); + it('should clear a buffer larger than rows', () => { + // Fill the buffer with dummy rows + for (let i = 0; i < term.rows * 2; i++) { + term.write('test\n'); + } + + let promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y); + term.clear(); + assert.equal(term.buffer.y, 0); + assert.equal(term.buffer.ybase, 0); + assert.equal(term.buffer.ydisp, 0); + assert.equal(term.buffer.lines.length, term.rows); + assert.deepEqual(term.buffer.lines.get(0), promptLine); + for (let i = 1; i < term.rows; i++) { + assert.deepEqual(term.buffer.lines.get(i), term.blankLine()); + } + }); + it('should not break the prompt when cleared twice', () => { + let promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y); + term.clear(); + term.clear(); + assert.equal(term.buffer.y, 0); + assert.equal(term.buffer.ybase, 0); + assert.equal(term.buffer.ydisp, 0); + assert.equal(term.buffer.lines.length, term.rows); + assert.deepEqual(term.buffer.lines.get(0), promptLine); + for (let i = 1; i < term.rows; i++) { + assert.deepEqual(term.buffer.lines.get(i), term.blankLine()); + } + }); + }); + + describe('scroll', () => { + describe('scrollDisp', () => { + let startYDisp; + beforeEach(() => { + for (let i = 0; i < term.rows * 2; i++) { + term.writeln('test'); + } + startYDisp = term.rows + 1; + }); + it('should scroll a single line', () => { + assert.equal(term.buffer.ydisp, startYDisp); + term.scrollDisp(-1); + assert.equal(term.buffer.ydisp, startYDisp - 1); + term.scrollDisp(1); + assert.equal(term.buffer.ydisp, startYDisp); + }); + it('should scroll multiple lines', () => { + assert.equal(term.buffer.ydisp, startYDisp); + term.scrollDisp(-5); + assert.equal(term.buffer.ydisp, startYDisp - 5); + term.scrollDisp(5); + assert.equal(term.buffer.ydisp, startYDisp); + }); + it('should not scroll beyond the bounds of the buffer', () => { + assert.equal(term.buffer.ydisp, startYDisp); + term.scrollDisp(1); + assert.equal(term.buffer.ydisp, startYDisp); + for (let i = 0; i < startYDisp; i++) { + term.scrollDisp(-1); + } + assert.equal(term.buffer.ydisp, 0); + term.scrollDisp(-1); + assert.equal(term.buffer.ydisp, 0); + }); + }); + + describe('scrollPages', () => { + let startYDisp; + beforeEach(() => { + for (let i = 0; i < term.rows * 3; i++) { + term.writeln('test'); + } + startYDisp = (term.rows * 2) + 1; + }); + it('should scroll a single page', () => { + assert.equal(term.buffer.ydisp, startYDisp); + term.scrollPages(-1); + assert.equal(term.buffer.ydisp, startYDisp - (term.rows - 1)); + term.scrollPages(1); + assert.equal(term.buffer.ydisp, startYDisp); + }); + it('should scroll a multiple pages', () => { + assert.equal(term.buffer.ydisp, startYDisp); + term.scrollPages(-2); + assert.equal(term.buffer.ydisp, startYDisp - (term.rows - 1) * 2); + term.scrollPages(2); + assert.equal(term.buffer.ydisp, startYDisp); + }); + }); + + describe('scrollToTop', () => { + beforeEach(() => { + for (let i = 0; i < term.rows * 3; i++) { + term.writeln('test'); + } + }); + it('should scroll to the top', () => { + assert.notEqual(term.buffer.ydisp, 0); + term.scrollToTop(); + assert.equal(term.buffer.ydisp, 0); + }); + }); + + describe('scrollToBottom', () => { + let startYDisp; + beforeEach(() => { + for (let i = 0; i < term.rows * 3; i++) { + term.writeln('test'); + } + startYDisp = (term.rows * 2) + 1; + }); + it('should scroll to the bottom', () => { + term.scrollDisp(-1); + term.scrollToBottom(); + assert.equal(term.buffer.ydisp, startYDisp); + term.scrollPages(-1); + term.scrollToBottom(); + assert.equal(term.buffer.ydisp, startYDisp); + term.scrollToTop(); + term.scrollToBottom(); + assert.equal(term.buffer.ydisp, startYDisp); + }); + }); + + describe('keyDown', () => { + it('should scroll down, when a key is pressed and terminal is scrolled up', () => { + // Override _evaluateKeyEscapeSequence to return cancel code + (term)._evaluateKeyEscapeSequence = () => { + return { key: 'a' }; + }; + let event = { + type: 'keydown', + keyCode: 0, + preventDefault: () => {}, + stopPropagation: () => {} + }; + + term.buffer.ydisp = 0; + term.buffer.ybase = 40; + term.keyDown(event); + + // Ensure that now the terminal is scrolled to bottom + assert.equal(term.buffer.ydisp, term.buffer.ybase); + }); + + it('should not scroll down, when a custom keydown handler prevents the event', () => { + // Add some output to the terminal + for (let i = 0; i < term.rows * 3; i++) { + term.writeln('test'); + } + let startYDisp = (term.rows * 2) + 1; + term.attachCustomKeyEventHandler(() => { + return false; + }); + + assert.equal(term.buffer.ydisp, startYDisp); + term.scrollDisp(-1); + assert.equal(term.buffer.ydisp, startYDisp - 1); + term.keyDown({ keyCode: 0 }); + assert.equal(term.buffer.ydisp, startYDisp - 1); + }); + }); + }); + + describe('evaluateKeyEscapeSequence', () => { + it('should return the correct escape sequence for unmodified keys', () => { + // Backspace + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 8 }).key, '\x7f'); // ^? + // Tab + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 9 }).key, '\t'); + // Return/enter + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 13 }).key, '\r'); // CR + // Escape + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 27 }).key, '\x1b'); + // Page up, page down + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 33 }).key, '\x1b[5~'); // CSI 5 ~ + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 34 }).key, '\x1b[6~'); // CSI 6 ~ + // End, Home + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 35 }).key, '\x1b[F'); // SS3 F + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 36 }).key, '\x1b[H'); // SS3 H + // Left, up, right, down arrows + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 37 }).key, '\x1b[D'); // CSI D + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 38 }).key, '\x1b[A'); // CSI A + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 39 }).key, '\x1b[C'); // CSI C + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 40 }).key, '\x1b[B'); // CSI B + // Insert + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 45 }).key, '\x1b[2~'); // CSI 2 ~ + // Delete + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 46 }).key, '\x1b[3~'); // CSI 3 ~ + // F1-F12 + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 112 }).key, '\x1bOP'); // SS3 P + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 113 }).key, '\x1bOQ'); // SS3 Q + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 114 }).key, '\x1bOR'); // SS3 R + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 115 }).key, '\x1bOS'); // SS3 S + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 116 }).key, '\x1b[15~'); // CSI 1 5 ~ + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 117 }).key, '\x1b[17~'); // CSI 1 7 ~ + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 118 }).key, '\x1b[18~'); // CSI 1 8 ~ + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 119 }).key, '\x1b[19~'); // CSI 1 9 ~ + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 120 }).key, '\x1b[20~'); // CSI 2 0 ~ + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 121 }).key, '\x1b[21~'); // CSI 2 1 ~ + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 122 }).key, '\x1b[23~'); // CSI 2 3 ~ + assert.equal(term.evaluateKeyEscapeSequence({ keyCode: 123 }).key, '\x1b[24~'); // CSI 2 4 ~ + }); + it('should return \\x1b[3;5~ for ctrl+delete', () => { + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 46 }).key, '\x1b[3;5~'); + }); + it('should return \\x1b[3;2~ for shift+delete', () => { + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 46 }).key, '\x1b[3;2~'); + }); + it('should return \\x1b[3;3~ for alt+delete', () => { + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 46 }).key, '\x1b[3;3~'); + }); + it('should return \\x1b[5D for ctrl+left', () => { + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 37 }).key, '\x1b[1;5D'); // CSI 5 D + }); + it('should return \\x1b[5C for ctrl+right', () => { + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 39 }).key, '\x1b[1;5C'); // CSI 5 C + }); + it('should return \\x1b[5A for ctrl+up', () => { + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A + }); + it('should return \\x1b[5B for ctrl+down', () => { + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 40 }).key, '\x1b[1;5B'); // CSI 5 B + }); + + describe('On non-macOS platforms', () => { + beforeEach(() => { + term.browser.isMac = false; + }); + // Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 + // http://unix.stackexchange.com/a/108106 + it('should return \\x1b[5D for alt+left', () => { + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 37 }).key, '\x1b[1;5D'); // CSI 5 D + }); + it('should return \\x1b[5C for alt+right', () => { + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 39 }).key, '\x1b[1;5C'); // CSI 5 C + }); + }); + + describe('On macOS platforms', () => { + beforeEach(() => { + term.browser.isMac = true; + }); + it('should return \\x1bb for alt+left', () => { + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 37 }).key, '\x1bb'); // CSI 5 D + }); + it('should return \\x1bf for alt+right', () => { + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 39 }).key, '\x1bf'); // CSI 5 C + }); + }); + + it('should return \\x1b[5A for alt+up', () => { + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A + }); + it('should return \\x1b[5B for alt+down', () => { + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 40 }).key, '\x1b[1;5B'); // CSI 5 B + }); + it('should return the correct escape sequence for modified F1-F12 keys', () => { + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 112 }).key, '\x1b[1;2P'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 113 }).key, '\x1b[1;2Q'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 114 }).key, '\x1b[1;2R'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 115 }).key, '\x1b[1;2S'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 116 }).key, '\x1b[15;2~'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 117 }).key, '\x1b[17;2~'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 118 }).key, '\x1b[18;2~'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 119 }).key, '\x1b[19;2~'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 120 }).key, '\x1b[20;2~'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 121 }).key, '\x1b[21;2~'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 122 }).key, '\x1b[23;2~'); + assert.equal(term.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 123 }).key, '\x1b[24;2~'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 112 }).key, '\x1b[1;3P'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 113 }).key, '\x1b[1;3Q'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 114 }).key, '\x1b[1;3R'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 115 }).key, '\x1b[1;3S'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 116 }).key, '\x1b[15;3~'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 117 }).key, '\x1b[17;3~'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 118 }).key, '\x1b[18;3~'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 119 }).key, '\x1b[19;3~'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 120 }).key, '\x1b[20;3~'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 121 }).key, '\x1b[21;3~'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 122 }).key, '\x1b[23;3~'); + assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 123 }).key, '\x1b[24;3~'); + + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 112 }).key, '\x1b[1;5P'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 113 }).key, '\x1b[1;5Q'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 114 }).key, '\x1b[1;5R'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 115 }).key, '\x1b[1;5S'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 116 }).key, '\x1b[15;5~'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 117 }).key, '\x1b[17;5~'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 118 }).key, '\x1b[18;5~'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 119 }).key, '\x1b[19;5~'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 120 }).key, '\x1b[20;5~'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 121 }).key, '\x1b[21;5~'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 122 }).key, '\x1b[23;5~'); + assert.equal(term.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 123 }).key, '\x1b[24;5~'); + }); + }); + + describe('Third level shift', () => { + let evKeyDown; + let evKeyPress; + + beforeEach(() => { + term.handler = () => {}; + term.showCursor = () => {}; + term.clearSelection = () => {}; + // term.compositionHelper = { + // isComposing: false, + // keydown: { + // bind: () => { + // return () => { return true; }; + // } + // } + // }; + evKeyDown = { + preventDefault: () => {}, + stopPropagation: () => {}, + type: 'keydown', + altKey: null, + keyCode: null + }; + evKeyPress = { + preventDefault: () => {}, + stopPropagation: () => {}, + type: 'keypress', + altKey: null, + charCode: null, + keyCode: null + }; + }); + + describe('On Mac OS', () => { + beforeEach(() => { + term.browser.isMac = true; + }); + + it('should not interfere with the alt key on keyDown', () => { + evKeyDown.altKey = true; + evKeyDown.keyCode = 81; + assert.equal(term.keyDown(evKeyDown), true); + evKeyDown.altKey = true; + evKeyDown.keyCode = 192; + assert.equal(term.keyDown(evKeyDown), true); + }); + + it('should interefere with the alt + arrow keys', () => { + evKeyDown.altKey = true; + evKeyDown.keyCode = 37; + assert.equal(term.keyDown(evKeyDown), false); + evKeyDown.altKey = true; + evKeyDown.keyCode = 39; + assert.equal(term.keyDown(evKeyDown), false); + }); + + it('should emit key with alt + key on keyPress', (done) => { + let keys = ['@', '@', '\\', '\\', '|', '|']; + + term.on('keypress', (key) => { + if (key) { + let index = keys.indexOf(key); + assert(index !== -1, 'Emitted wrong key: ' + key); + keys.splice(index, 1); + } + if (keys.length === 0) done(); + }); + + evKeyPress.altKey = true; + // @ + evKeyPress.charCode = null; + evKeyPress.keyCode = 64; + term.keyPress(evKeyPress); + // Firefox @ + evKeyPress.charCode = 64; + evKeyPress.keyCode = 0; + term.keyPress(evKeyPress); + // \ + evKeyPress.charCode = null; + evKeyPress.keyCode = 92; + term.keyPress(evKeyPress); + // Firefox \ + evKeyPress.charCode = 92; + evKeyPress.keyCode = 0; + term.keyPress(evKeyPress); + // | + evKeyPress.charCode = null; + evKeyPress.keyCode = 124; + term.keyPress(evKeyPress); + // Firefox | + evKeyPress.charCode = 124; + evKeyPress.keyCode = 0; + term.keyPress(evKeyPress); + }); + }); + + describe('On MS Windows', () => { + beforeEach(() => { + term.browser.isMSWindows = true; + }); + + it('should not interfere with the alt + ctrl key on keyDown', () => { + evKeyPress.altKey = true; + evKeyPress.ctrlKey = true; + evKeyPress.keyCode = 81; + assert.equal(term.keyDown(evKeyPress), true); + evKeyDown.altKey = true; + evKeyDown.ctrlKey = true; + evKeyDown.keyCode = 81; + assert.equal(term.keyDown(evKeyDown), true); + }); + + it('should interefere with the alt + ctrl + arrow keys', () => { + evKeyDown.altKey = true; + evKeyDown.ctrlKey = true; + + evKeyDown.keyCode = 37; + assert.equal(term.keyDown(evKeyDown), false); + evKeyDown.keyCode = 39; + assert.equal(term.keyDown(evKeyDown), false); + }); + + it('should emit key with alt + ctrl + key on keyPress', (done) => { + let keys = ['@', '@', '\\', '\\', '|', '|']; + + term.on('keypress', (key) => { + if (key) { + let index = keys.indexOf(key); + assert(index !== -1, 'Emitted wrong key: ' + key); + keys.splice(index, 1); + } + if (keys.length === 0) done(); + }); + + evKeyPress.altKey = true; + evKeyPress.ctrlKey = true; + + // @ + evKeyPress.charCode = null; + evKeyPress.keyCode = 64; + term.keyPress(evKeyPress); + // Firefox @ + evKeyPress.charCode = 64; + evKeyPress.keyCode = 0; + term.keyPress(evKeyPress); + // \ + evKeyPress.charCode = null; + evKeyPress.keyCode = 92; + term.keyPress(evKeyPress); + // Firefox \ + evKeyPress.charCode = 92; + evKeyPress.keyCode = 0; + term.keyPress(evKeyPress); + // | + evKeyPress.charCode = null; + evKeyPress.keyCode = 124; + term.keyPress(evKeyPress); + // Firefox | + evKeyPress.charCode = 124; + evKeyPress.keyCode = 0; + term.keyPress(evKeyPress); + }); + }); + }); + + describe('unicode - surrogates', () => { + it('2 characters per cell', function (): void { + this.timeout(10000); // This is needed because istanbul patches code and slows it down + let high = String.fromCharCode(0xD800); + for (let i = 0xDC00; i <= 0xDCFF; ++i) { + term.write(high + String.fromCharCode(i)); + let tchar = term.buffer.lines.get(0)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); + expect(term.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); + term.reset(); + } + }); + it('2 characters at last cell', () => { + let high = String.fromCharCode(0xD800); + for (let i = 0xDC00; i <= 0xDCFF; ++i) { + term.buffer.x = term.cols - 1; + term.write(high + String.fromCharCode(i)); + expect(term.buffer.lines.get(0)[term.buffer.x - 1][CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); + expect(term.buffer.lines.get(0)[term.buffer.x - 1][CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(term.buffer.lines.get(1)[0][CHAR_DATA_CHAR_INDEX]).eql(' '); + term.reset(); + } + }); + it('2 characters per cell over line end with autowrap', () => { + let high = String.fromCharCode(0xD800); + for (let i = 0xDC00; i <= 0xDCFF; ++i) { + term.buffer.x = term.cols - 1; + term.wraparoundMode = true; + term.write('a' + high + String.fromCharCode(i)); + expect(term.buffer.lines.get(0)[term.cols - 1][CHAR_DATA_CHAR_INDEX]).eql('a'); + expect(term.buffer.lines.get(1)[0][CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); + expect(term.buffer.lines.get(1)[0][CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(term.buffer.lines.get(1)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); + term.reset(); + } + }); + it('2 characters per cell over line end without autowrap', () => { + let high = String.fromCharCode(0xD800); + for (let i = 0xDC00; i <= 0xDCFF; ++i) { + term.buffer.x = term.cols - 1; + term.wraparoundMode = false; + term.write('a' + high + String.fromCharCode(i)); + // auto wraparound mode should cut off the rest of the line + expect(term.buffer.lines.get(0)[term.cols - 1][CHAR_DATA_CHAR_INDEX]).eql('a'); + expect(term.buffer.lines.get(0)[term.cols - 1][CHAR_DATA_CHAR_INDEX].length).eql(1); + expect(term.buffer.lines.get(1)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); + term.reset(); + } + }); + it('splitted surrogates', () => { + let high = String.fromCharCode(0xD800); + for (let i = 0xDC00; i <= 0xDCFF; ++i) { + term.write(high); + term.write(String.fromCharCode(i)); + let tchar = term.buffer.lines.get(0)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); + expect(term.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); + term.reset(); + } + }); + }); + + describe('unicode - combining characters', () => { + it('café', () => { + term.write('cafe\u0301'); + expect(term.buffer.lines.get(0)[3][CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); + expect(term.buffer.lines.get(0)[3][CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(term.buffer.lines.get(0)[3][CHAR_DATA_WIDTH_INDEX]).eql(1); + }); + it('café - end of line', () => { + term.buffer.x = term.cols - 1 - 3; + term.write('cafe\u0301'); + expect(term.buffer.lines.get(0)[term.cols - 1][CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); + expect(term.buffer.lines.get(0)[term.cols - 1][CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(term.buffer.lines.get(0)[term.cols - 1][CHAR_DATA_WIDTH_INDEX]).eql(1); + expect(term.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); + expect(term.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX].length).eql(1); + expect(term.buffer.lines.get(0)[1][CHAR_DATA_WIDTH_INDEX]).eql(1); + }); + it('multiple combined é', () => { + term.wraparoundMode = true; + term.write(Array(100).join('e\u0301')); + for (let i = 0; i < term.cols; ++i) { + let tchar = term.buffer.lines.get(0)[i]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); + } + let tchar = term.buffer.lines.get(1)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); + }); + it('multiple surrogate with combined', () => { + term.wraparoundMode = true; + term.write(Array(100).join('\uD800\uDC00\u0301')); + for (let i = 0; i < term.cols; ++i) { + let tchar = term.buffer.lines.get(0)[i]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\uD800\uDC00\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); + } + let tchar = term.buffer.lines.get(1)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\uD800\uDC00\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); + }); + }); + + describe('unicode - fullwidth characters', () => { + it('cursor movement even', () => { + expect(term.buffer.x).eql(0); + term.write('¥'); + expect(term.buffer.x).eql(2); + }); + it('cursor movement odd', () => { + term.buffer.x = 1; + expect(term.buffer.x).eql(1); + term.write('¥'); + expect(term.buffer.x).eql(3); + }); + it('line of ¥ even', () => { + term.wraparoundMode = true; + term.write(Array(50).join('¥')); + for (let i = 0; i < term.cols; ++i) { + let tchar = term.buffer.lines.get(0)[i]; + if (i % 2) { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); + } else { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + } + } + let tchar = term.buffer.lines.get(1)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + }); + it('line of ¥ odd', () => { + term.wraparoundMode = true; + term.buffer.x = 1; + term.write(Array(50).join('¥')); + for (let i = 1; i < term.cols - 1; ++i) { + let tchar = term.buffer.lines.get(0)[i]; + if (!(i % 2)) { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); + } else { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + } + } + let tchar = term.buffer.lines.get(0)[term.cols - 1]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(' '); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); + tchar = term.buffer.lines.get(1)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + }); + it('line of ¥ with combining odd', () => { + term.wraparoundMode = true; + term.buffer.x = 1; + term.write(Array(50).join('¥\u0301')); + for (let i = 1; i < term.cols - 1; ++i) { + let tchar = term.buffer.lines.get(0)[i]; + if (!(i % 2)) { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); + } else { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + } + } + let tchar = term.buffer.lines.get(0)[term.cols - 1]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(' '); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); + tchar = term.buffer.lines.get(1)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + }); + it('line of ¥ with combining even', () => { + term.wraparoundMode = true; + term.write(Array(50).join('¥\u0301')); + for (let i = 0; i < term.cols; ++i) { + let tchar = term.buffer.lines.get(0)[i]; + if (i % 2) { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); + } else { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + } + } + let tchar = term.buffer.lines.get(1)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + }); + it('line of surrogate fullwidth with combining odd', () => { + term.wraparoundMode = true; + term.buffer.x = 1; + term.write(Array(50).join('\ud843\ude6d\u0301')); + for (let i = 1; i < term.cols - 1; ++i) { + let tchar = term.buffer.lines.get(0)[i]; + if (!(i % 2)) { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); + } else { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + } + } + let tchar = term.buffer.lines.get(0)[term.cols - 1]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(' '); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); + tchar = term.buffer.lines.get(1)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + }); + it('line of surrogate fullwidth with combining even', () => { + term.wraparoundMode = true; + term.write(Array(50).join('\ud843\ude6d\u0301')); + for (let i = 0; i < term.cols; ++i) { + let tchar = term.buffer.lines.get(0)[i]; + if (i % 2) { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); + } else { + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + } + } + let tchar = term.buffer.lines.get(1)[0]; + expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301'); + expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); + expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); + }); + }); + + describe('insert mode', () => { + it('halfwidth - all', () => { + term.write(Array(9).join('0123456789').slice(-80)); + term.buffer.x = 10; + term.buffer.y = 0; + term.insertMode = true; + term.write('abcde'); + expect(term.buffer.lines.get(0).length).eql(term.cols); + expect(term.buffer.lines.get(0)[10][CHAR_DATA_CHAR_INDEX]).eql('a'); + expect(term.buffer.lines.get(0)[14][CHAR_DATA_CHAR_INDEX]).eql('e'); + expect(term.buffer.lines.get(0)[15][CHAR_DATA_CHAR_INDEX]).eql('0'); + expect(term.buffer.lines.get(0)[79][CHAR_DATA_CHAR_INDEX]).eql('4'); + }); + it('fullwidth - insert', () => { + term.write(Array(9).join('0123456789').slice(-80)); + term.buffer.x = 10; + term.buffer.y = 0; + term.insertMode = true; + term.write('¥¥¥'); + expect(term.buffer.lines.get(0).length).eql(term.cols); + expect(term.buffer.lines.get(0)[10][CHAR_DATA_CHAR_INDEX]).eql('¥'); + expect(term.buffer.lines.get(0)[11][CHAR_DATA_CHAR_INDEX]).eql(''); + expect(term.buffer.lines.get(0)[14][CHAR_DATA_CHAR_INDEX]).eql('¥'); + expect(term.buffer.lines.get(0)[15][CHAR_DATA_CHAR_INDEX]).eql(''); + expect(term.buffer.lines.get(0)[79][CHAR_DATA_CHAR_INDEX]).eql('3'); + }); + it('fullwidth - right border', () => { + term.write(Array(41).join('¥')); + term.buffer.x = 10; + term.buffer.y = 0; + term.insertMode = true; + term.write('a'); + expect(term.buffer.lines.get(0).length).eql(term.cols); + expect(term.buffer.lines.get(0)[10][CHAR_DATA_CHAR_INDEX]).eql('a'); + expect(term.buffer.lines.get(0)[11][CHAR_DATA_CHAR_INDEX]).eql('¥'); + expect(term.buffer.lines.get(0)[79][CHAR_DATA_CHAR_INDEX]).eql(' '); // fullwidth char got replaced + term.write('b'); + expect(term.buffer.lines.get(0).length).eql(term.cols); + expect(term.buffer.lines.get(0)[11][CHAR_DATA_CHAR_INDEX]).eql('b'); + expect(term.buffer.lines.get(0)[12][CHAR_DATA_CHAR_INDEX]).eql('¥'); + expect(term.buffer.lines.get(0)[79][CHAR_DATA_CHAR_INDEX]).eql(''); // empty cell after fullwidth + }); + }); +}); diff --git a/src/Terminal.ts b/src/Terminal.ts index 31eaef8a..a7b4b336 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -29,7 +29,7 @@ import * as Mouse from './utils/Mouse'; import { CHARSETS } from './Charsets'; import { getRawByteCoords } from './utils/Mouse'; import { CustomKeyEventHandler, Charset, LinkMatcherHandler, LinkMatcherValidationCallback, CharData, LineData, Option, StringOption, BooleanOption, StringArrayOption, NumberOption, GeometryOption, HandlerOption } from './Types'; -import { ITerminal, IBrowser, ITerminalOptions, IInputHandlingTerminal, ILinkMatcherOptions } from './Interfaces'; +import { ITerminal, IBrowser, ITerminalOptions, IInputHandlingTerminal, ILinkMatcherOptions, IViewport, ICompositionHelper } from './Interfaces'; // Declare for RequireJS in loadAddon declare var define: any; @@ -259,8 +259,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT private linkifier: Linkifier; public buffers: BufferSet; public buffer: Buffer; - public viewport: Viewport; - private compositionHelper: CompositionHelper; + public viewport: IViewport; + private compositionHelper: ICompositionHelper; public charMeasure: CharMeasure; public cols: number; @@ -601,14 +601,14 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT if (document.activeElement !== this) { return; } - self.keyDown(ev); + self._keyDown(ev); }, true); on(this.element, 'keypress', function (ev: KeyboardEvent): void { if (document.activeElement !== this) { return; } - self.keyPress(ev); + self._keyPress(ev); }, true); on(this.element, 'keyup', (ev: KeyboardEvent) => { @@ -618,19 +618,19 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT }, true); on(this.textarea, 'keydown', (ev: KeyboardEvent) => { - this.keyDown(ev); + this._keyDown(ev); }, true); on(this.textarea, 'keypress', (ev: KeyboardEvent) => { - this.keyPress(ev); + this._keyPress(ev); // Truncate the textarea's value, since it is not needed this.textarea.value = ''; }, true); - on(this.textarea, 'compositionstart', this.compositionHelper.compositionstart.bind(this.compositionHelper)); - on(this.textarea, 'compositionupdate', this.compositionHelper.compositionupdate.bind(this.compositionHelper)); - on(this.textarea, 'compositionend', this.compositionHelper.compositionend.bind(this.compositionHelper)); - this.on('refresh', this.compositionHelper.updateCompositionElements.bind(this.compositionHelper)); + on(this.textarea, 'compositionstart', () => this.compositionHelper.compositionstart()); + on(this.textarea, 'compositionupdate', (e: CompositionEvent) => this.compositionHelper.compositionupdate(e)); + on(this.textarea, 'compositionend', () => this.compositionHelper.compositionend()); + this.on('refresh', () => this.compositionHelper.updateCompositionElements()); this.on('refresh', (data) => this.queueLinkification(data.start, data.end)); } @@ -1474,21 +1474,21 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent * @param {KeyboardEvent} ev The keydown event to be handled. */ - private keyDown(ev: KeyboardEvent): boolean { + protected _keyDown(ev: KeyboardEvent): boolean { if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) { return false; } this.restartCursorBlinking(); - if (!this.compositionHelper.keydown.bind(this.compositionHelper)(ev)) { + if (!this.compositionHelper.keydown(ev)) { if (this.buffer.ybase !== this.buffer.ydisp) { this.scrollToBottom(); } return false; } - const result = this.evaluateKeyEscapeSequence(ev); + const result = this._evaluateKeyEscapeSequence(ev); if (result.key === C0.DC3) { // XOFF this.writeStopped = true; @@ -1501,7 +1501,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT return this.cancel(ev, true); } - if (isThirdLevelShift(ev)) { + if (isThirdLevelShift(this.browser, ev)) { return true; } @@ -1529,7 +1529,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * @param ev The keyboard event to be translated to key escape sequence. */ - private evaluateKeyEscapeSequence(ev: KeyboardEvent): {cancel: boolean, key: string, scrollDisp: number} { + protected _evaluateKeyEscapeSequence(ev: KeyboardEvent): {cancel: boolean, key: string, scrollDisp: number} { const result: {cancel: boolean, key: string, scrollDisp: number} = { // Whether to cancel event propogation (NOTE: this may not be needed since the event is // canceled at the end of keyDown @@ -1836,7 +1836,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent * @param {KeyboardEvent} ev The keypress event to be handled. */ - private keyPress(ev: KeyboardEvent): boolean { + protected _keyPress(ev: KeyboardEvent): boolean { let key; if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) { @@ -1856,7 +1856,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT } if (!key || ( - (ev.altKey || ev.ctrlKey || ev.metaKey) && !isThirdLevelShift(ev) + (ev.altKey || ev.ctrlKey || ev.metaKey) && !isThirdLevelShift(this.browser, ev) )) { return false; } @@ -2309,10 +2309,10 @@ function off(el: any, type: string, handler: (event: Event) => any, capture: boo el.removeEventListener(type, handler, capture); } -function isThirdLevelShift(ev: KeyboardEvent): boolean { +function isThirdLevelShift(browser: IBrowser, ev: KeyboardEvent): boolean { const thirdLevelKey = - (Browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) || - (Browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey); + (browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) || + (browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey); if (ev.type === 'keypress') { return thirdLevelKey; diff --git a/src/Viewport.test.ts b/src/Viewport.test.ts index 8c7a914b..428ef098 100644 --- a/src/Viewport.test.ts +++ b/src/Viewport.test.ts @@ -4,7 +4,7 @@ import { assert } from 'chai'; import { Viewport } from './Viewport'; -import {BufferSet} from './BufferSet'; +import { BufferSet } from './BufferSet'; describe('Viewport', () => { let terminal; diff --git a/src/test/addons/test.js b/src/test/addons/test.js deleted file mode 100644 index 42ede9db..00000000 --- a/src/test/addons/test.js +++ /dev/null @@ -1,10 +0,0 @@ -var assert = require('chai').assert; -var Terminal = require('../../xterm'); - -describe('xterm.js addons', function() { - it('should load addons with Terminal.loadAddon', function () { - Terminal.loadAddon('attach'); - // Test that addon was loaded successfully - assert.equal(typeof Terminal.prototype.attach, 'function'); - }); -}); diff --git a/src/test/test.js b/src/test/test.js deleted file mode 100644 index 2c44f1e7..00000000 --- a/src/test/test.js +++ /dev/null @@ -1,889 +0,0 @@ -var assert = require('chai').assert; -var expect = require('chai').expect; -var Terminal = require('../xterm'); -var CHAR_DATA_CHAR_INDEX = require('../Buffer').CHAR_DATA_CHAR_INDEX; -var CHAR_DATA_WIDTH_INDEX = require('../Buffer').CHAR_DATA_WIDTH_INDEX; - -describe('xterm.js', function() { - var xterm; - - beforeEach(function () { - xterm = new Terminal(); - xterm.refresh = function(){}; - xterm.viewport = { - syncScrollArea: function(){} - }; - xterm.compositionHelper = { - keydown: function(){ return true; } - }; - // Force synchronous writes - xterm.write = function(data) { - xterm.writeBuffer.push(data); - xterm.innerWrite(); - }; - xterm.element = { - classList: { - toggle: function(){}, - remove: function(){} - } - }; - }); - - describe('getOption', function() { - it('should retrieve the option correctly', function() { - // In the `options` namespace. - xterm.options.cursorBlink = true; - assert.equal(xterm.getOption('cursorBlink'), true); - - // On the Terminal instance - delete xterm.options.cursorBlink; - xterm.cursorBlink = false; - assert.equal(xterm.getOption('cursorBlink'), false); - }); - it('should throw when retrieving a non-existant option', function() { - assert.throws(xterm.getOption.bind(xterm, 'fake', true)); - }); - }); - - describe('setOption', function() { - let originalWarn; - let warnCallCount; - beforeEach(() => { - originalWarn = console.warn; - warnCallCount = 0; - console.warn = () => warnCallCount++; - }); - afterEach(() => { - console.warn = originalWarn; - }); - it('should set the option correctly', function() { - xterm.setOption('cursorBlink', true); - assert.equal(xterm.cursorBlink, true); - assert.equal(xterm.options.cursorBlink, true); - xterm.setOption('cursorBlink', false); - assert.equal(xterm.cursorBlink, false); - assert.equal(xterm.options.cursorBlink, false); - }); - it('should throw when setting a non-existant option', function() { - assert.throws(xterm.setOption.bind(xterm, 'fake', true)); - }); - it('should warn and do nothing when scrollback is less than number of rows', function() { - xterm.setOption('scrollback', xterm.rows - 1); - assert.equal(xterm.getOption('scrollback'), 1000); - assert.equal(warnCallCount, 1); - }); - }); - - describe('clear', function() { - it('should clear a buffer equal to rows', function() { - var promptLine = xterm.buffer.lines.get(xterm.buffer.ybase + xterm.buffer.y); - xterm.clear(); - assert.equal(xterm.buffer.y, 0); - assert.equal(xterm.buffer.ybase, 0); - assert.equal(xterm.buffer.ydisp, 0); - assert.equal(xterm.buffer.lines.length, xterm.rows); - assert.deepEqual(xterm.buffer.lines.get(0), promptLine); - for (var i = 1; i < xterm.rows; i++) { - assert.deepEqual(xterm.buffer.lines.get(i), xterm.blankLine()); - } - }); - it('should clear a buffer larger than rows', function() { - // Fill the buffer with dummy rows - for (var i = 0; i < xterm.rows * 2; i++) { - xterm.write('test\n'); - } - - var promptLine = xterm.buffer.lines.get(xterm.buffer.ybase + xterm.buffer.y); - xterm.clear(); - assert.equal(xterm.buffer.y, 0); - assert.equal(xterm.buffer.ybase, 0); - assert.equal(xterm.buffer.ydisp, 0); - assert.equal(xterm.buffer.lines.length, xterm.rows); - assert.deepEqual(xterm.buffer.lines.get(0), promptLine); - for (var i = 1; i < xterm.rows; i++) { - assert.deepEqual(xterm.buffer.lines.get(i), xterm.blankLine()); - } - }); - it('should not break the prompt when cleared twice', function() { - var promptLine = xterm.buffer.lines.get(xterm.buffer.ybase + xterm.buffer.y); - xterm.clear(); - xterm.clear(); - assert.equal(xterm.buffer.y, 0); - assert.equal(xterm.buffer.ybase, 0); - assert.equal(xterm.buffer.ydisp, 0); - assert.equal(xterm.buffer.lines.length, xterm.rows); - assert.deepEqual(xterm.buffer.lines.get(0), promptLine); - for (var i = 1; i < xterm.rows; i++) { - assert.deepEqual(xterm.buffer.lines.get(i), xterm.blankLine()); - } - }); - }); - - describe('scroll', function() { - describe('scrollDisp', function() { - var startYDisp; - beforeEach(function() { - for (var i = 0; i < xterm.rows * 2; i++) { - xterm.writeln('test'); - } - startYDisp = xterm.rows + 1; - }); - it('should scroll a single line', function() { - assert.equal(xterm.buffer.ydisp, startYDisp); - xterm.scrollDisp(-1); - assert.equal(xterm.buffer.ydisp, startYDisp - 1); - xterm.scrollDisp(1); - assert.equal(xterm.buffer.ydisp, startYDisp); - }); - it('should scroll multiple lines', function() { - assert.equal(xterm.buffer.ydisp, startYDisp); - xterm.scrollDisp(-5); - assert.equal(xterm.buffer.ydisp, startYDisp - 5); - xterm.scrollDisp(5); - assert.equal(xterm.buffer.ydisp, startYDisp); - }); - it('should not scroll beyond the bounds of the buffer', function() { - assert.equal(xterm.buffer.ydisp, startYDisp); - xterm.scrollDisp(1); - assert.equal(xterm.buffer.ydisp, startYDisp); - for (var i = 0; i < startYDisp; i++) { - xterm.scrollDisp(-1); - } - assert.equal(xterm.buffer.ydisp, 0); - xterm.scrollDisp(-1); - assert.equal(xterm.buffer.ydisp, 0); - }); - }); - - describe('scrollPages', function() { - var startYDisp; - beforeEach(function() { - for (var i = 0; i < xterm.rows * 3; i++) { - xterm.writeln('test'); - } - startYDisp = (xterm.rows * 2) + 1; - }); - it('should scroll a single page', function() { - assert.equal(xterm.buffer.ydisp, startYDisp); - xterm.scrollPages(-1); - assert.equal(xterm.buffer.ydisp, startYDisp - (xterm.rows - 1)); - xterm.scrollPages(1); - assert.equal(xterm.buffer.ydisp, startYDisp); - }); - it('should scroll a multiple pages', function() { - assert.equal(xterm.buffer.ydisp, startYDisp); - xterm.scrollPages(-2); - assert.equal(xterm.buffer.ydisp, startYDisp - (xterm.rows - 1) * 2); - xterm.scrollPages(2); - assert.equal(xterm.buffer.ydisp, startYDisp); - }); - }); - - describe('scrollToTop', function() { - beforeEach(function() { - for (var i = 0; i < xterm.rows * 3; i++) { - xterm.writeln('test'); - } - }); - it('should scroll to the top', function() { - assert.notEqual(xterm.buffer.ydisp, 0); - xterm.scrollToTop(); - assert.equal(xterm.buffer.ydisp, 0); - }); - }); - - describe('scrollToBottom', function() { - var startYDisp; - beforeEach(function() { - for (var i = 0; i < xterm.rows * 3; i++) { - xterm.writeln('test'); - } - startYDisp = (xterm.rows * 2) + 1; - }); - it('should scroll to the bottom', function() { - xterm.scrollDisp(-1); - xterm.scrollToBottom(); - assert.equal(xterm.buffer.ydisp, startYDisp); - xterm.scrollPages(-1); - xterm.scrollToBottom(); - assert.equal(xterm.buffer.ydisp, startYDisp); - xterm.scrollToTop(); - xterm.scrollToBottom(); - assert.equal(xterm.buffer.ydisp, startYDisp); - }); - }); - - describe('keyDown', function () { - it('should scroll down, when a key is pressed and terminal is scrolled up', function () { - // Override evaluateKeyEscapeSequence to return cancel code - xterm.evaluateKeyEscapeSequence = function() { - return { key: 'a' }; - }; - var event = { - type: 'keydown', - keyCode: 0, - preventDefault: function(){}, - stopPropagation: function(){} - }; - - xterm.buffer.ydisp = 0; - xterm.buffer.ybase = 40; - assert.notEqual(xterm.buffer.ydisp, xterm.buffer.ybase); - xterm.keyDown(event); - - // Ensure that now the terminal is scrolled to bottom - assert.equal(xterm.buffer.ydisp, xterm.buffer.ybase); - }); - - it('should not scroll down, when a custom keydown handler prevents the event', function () { - // Add some output to the terminal - for (var i = 0; i < xterm.rows * 3; i++) { - xterm.writeln('test'); - } - var startYDisp = (xterm.rows * 2) + 1; - xterm.attachCustomKeyEventHandler(function () { - return false; - }); - - assert.equal(xterm.buffer.ydisp, startYDisp); - xterm.scrollDisp(-1); - assert.equal(xterm.buffer.ydisp, startYDisp - 1); - xterm.keyDown({ keyCode: 0 }); - assert.equal(xterm.buffer.ydisp, startYDisp - 1); - }); - }); - }); - - describe('evaluateKeyEscapeSequence', function() { - it('should return the correct escape sequence for unmodified keys', function() { - // Backspace - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 8 }).key, '\x7f'); // ^? - // Tab - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 9 }).key, '\t'); - // Return/enter - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 13 }).key, '\r'); // CR - // Escape - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 27 }).key, '\x1b'); - // Page up, page down - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 33 }).key, '\x1b[5~'); // CSI 5 ~ - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 34 }).key, '\x1b[6~'); // CSI 6 ~ - // End, Home - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 35 }).key, '\x1b[F'); // SS3 F - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 36 }).key, '\x1b[H'); // SS3 H - // Left, up, right, down arrows - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 37 }).key, '\x1b[D'); // CSI D - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 38 }).key, '\x1b[A'); // CSI A - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 39 }).key, '\x1b[C'); // CSI C - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 40 }).key, '\x1b[B'); // CSI B - // Insert - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 45 }).key, '\x1b[2~'); // CSI 2 ~ - // Delete - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 46 }).key, '\x1b[3~'); // CSI 3 ~ - // F1-F12 - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 112 }).key, '\x1bOP'); // SS3 P - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 113 }).key, '\x1bOQ'); // SS3 Q - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 114 }).key, '\x1bOR'); // SS3 R - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 115 }).key, '\x1bOS'); // SS3 S - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 116 }).key, '\x1b[15~'); // CSI 1 5 ~ - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 117 }).key, '\x1b[17~'); // CSI 1 7 ~ - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 118 }).key, '\x1b[18~'); // CSI 1 8 ~ - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 119 }).key, '\x1b[19~'); // CSI 1 9 ~ - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 120 }).key, '\x1b[20~'); // CSI 2 0 ~ - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 121 }).key, '\x1b[21~'); // CSI 2 1 ~ - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 122 }).key, '\x1b[23~'); // CSI 2 3 ~ - assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 123 }).key, '\x1b[24~'); // CSI 2 4 ~ - }); - it('should return \\x1b[3;5~ for ctrl+delete', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 46 }).key, '\x1b[3;5~'); - }); - it('should return \\x1b[3;2~ for shift+delete', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 46 }).key, '\x1b[3;2~'); - }); - it('should return \\x1b[3;3~ for alt+delete', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 46 }).key, '\x1b[3;3~'); - }); - it('should return \\x1b[5D for ctrl+left', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 37 }).key, '\x1b[1;5D'); // CSI 5 D - }); - it('should return \\x1b[5C for ctrl+right', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 39 }).key, '\x1b[1;5C'); // CSI 5 C - }); - it('should return \\x1b[5A for ctrl+up', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A - }); - it('should return \\x1b[5B for ctrl+down', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 40 }).key, '\x1b[1;5B'); // CSI 5 B - }); - - describe('On non-macOS platforms', function() { - beforeEach(function() { - xterm.browser.isMac = false; - }); - // Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 - // http://unix.stackexchange.com/a/108106 - it('should return \\x1b[5D for alt+left', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 37 }).key, '\x1b[1;5D'); // CSI 5 D - }); - it('should return \\x1b[5C for alt+right', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 39 }).key, '\x1b[1;5C'); // CSI 5 C - }); - }); - - describe('On macOS platforms', function() { - beforeEach(function() { - xterm.browser.isMac = true; - }); - it('should return \\x1bb for alt+left', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 37 }).key, '\x1bb'); // CSI 5 D - }); - it('should return \\x1bf for alt+right', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 39 }).key, '\x1bf'); // CSI 5 C - }); - }); - - it('should return \\x1b[5A for alt+up', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A - }); - it('should return \\x1b[5B for alt+down', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 40 }).key, '\x1b[1;5B'); // CSI 5 B - }); - it('should return the correct escape sequence for modified F1-F12 keys', function() { - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 112 }).key, '\x1b[1;2P'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 113 }).key, '\x1b[1;2Q'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 114 }).key, '\x1b[1;2R'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 115 }).key, '\x1b[1;2S'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 116 }).key, '\x1b[15;2~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 117 }).key, '\x1b[17;2~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 118 }).key, '\x1b[18;2~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 119 }).key, '\x1b[19;2~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 120 }).key, '\x1b[20;2~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 121 }).key, '\x1b[21;2~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 122 }).key, '\x1b[23;2~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 123 }).key, '\x1b[24;2~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 112 }).key, '\x1b[1;3P'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 113 }).key, '\x1b[1;3Q'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 114 }).key, '\x1b[1;3R'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 115 }).key, '\x1b[1;3S'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 116 }).key, '\x1b[15;3~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 117 }).key, '\x1b[17;3~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 118 }).key, '\x1b[18;3~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 119 }).key, '\x1b[19;3~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 120 }).key, '\x1b[20;3~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 121 }).key, '\x1b[21;3~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 122 }).key, '\x1b[23;3~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 123 }).key, '\x1b[24;3~'); - - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 112 }).key, '\x1b[1;5P'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 113 }).key, '\x1b[1;5Q'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 114 }).key, '\x1b[1;5R'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 115 }).key, '\x1b[1;5S'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 116 }).key, '\x1b[15;5~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 117 }).key, '\x1b[17;5~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 118 }).key, '\x1b[18;5~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 119 }).key, '\x1b[19;5~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 120 }).key, '\x1b[20;5~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 121 }).key, '\x1b[21;5~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 122 }).key, '\x1b[23;5~'); - assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 123 }).key, '\x1b[24;5~'); - }); - }); - - describe('attachCustomKeyEventHandler', function () { - var evKeyDown = { - preventDefault: function() {}, - stopPropagation: function() {}, - type: 'keydown' - } - var evKeyPress = { - preventDefault: function() {}, - stopPropagation: function() {}, - type: 'keypress' - } - - beforeEach(function() { - xterm.handler = function() {}; - xterm.showCursor = function() {}; - xterm.clearSelection = function() {}; - xterm.compositionHelper = { - keydown: { - bind: function() { - return function () { return true; } - } - }, - keypress: { - bind: function() { - return function () { return true; } - } - } - } - }); - - it('should process the keydown/keypress event based on what the handler returns', function () { - assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), true); - assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), true); - xterm.attachCustomKeyEventHandler(function (ev) { - return ev.keyCode === 77; - }); - assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), true); - assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), true); - xterm.attachCustomKeyEventHandler(function (ev) { - return ev.keyCode !== 77; - }); - assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), false); - assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), false); - }); - - it('should alive after reset(ESC c Full Reset (RIS))', function () { - xterm.attachCustomKeyEventHandler(function (ev) { - return ev.keyCode !== 77; - }); - assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), false); - assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), false); - xterm.reset(); - assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), false); - assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), false); - }); - }); - - describe('Third level shift', function() { - var evKeyDown = { - preventDefault: function() {}, - stopPropagation: function() {}, - type: 'keydown' - }, - evKeyPress = { - preventDefault: function() {}, - stopPropagation: function() {}, - type: 'keypress' - }; - - beforeEach(function() { - xterm.handler = function() {}; - xterm.showCursor = function() {}; - xterm.clearSelection = function() {}; - xterm.compositionHelper = { - isComposing: false, - keydown: { - bind: function() { - return function() { return true; }; - } - } - }; - }); - - describe('On Mac OS', function() { - beforeEach(function() { - xterm.browser.isMac = true; - }); - - it('should not interfere with the alt key on keyDown', function() { - assert.equal( - xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, keyCode: 81 })), - true - ); - assert.equal( - xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, keyCode: 192 })), - true - ); - }); - - it('should interefere with the alt + arrow keys', function() { - assert.equal( - xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, keyCode: 37 })), - false - ); - assert.equal( - xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, keyCode: 39 })), - false - ); - }); - - it('should emit key with alt + key on keyPress', function(done) { - var keys = ['@', '@', '\\', '\\', '|', '|']; - - xterm.on('keypress', function(key) { - if (key) { - var index = keys.indexOf(key); - assert(index !== -1, "Emitted wrong key: " + key); - keys.splice(index, 1); - } - if (keys.length === 0) done(); - }); - - xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, keyCode: 64 })); // @ - // Firefox - xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, charCode: 64, keyCode: 0 })); - xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, keyCode: 92 })); // \ - xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, charCode: 92, keyCode: 0 })); - xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, keyCode: 124 })); // | - xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, charCode: 124, keyCode: 0 })); - }); - }); - - describe('On MS Windows', function() { - beforeEach(function() { - xterm.browser.isMSWindows = true; - }); - - it('should not interfere with the alt + ctrl key on keyDown', function() { - assert.equal( - xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, ctrlKey: true, keyCode: 81 })), - true - ); - assert.equal( - xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, ctrlKey: true, keyCode: 192 })), - true - ); - }); - - it('should interefere with the alt + ctrl + arrow keys', function() { - assert.equal( - xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, ctrlKey: true, keyCode: 37 })), - false - ); - assert.equal( - xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, ctrlKey: true, keyCode: 39 })), - false - ); - }); - - it('should emit key with alt + ctrl + key on keyPress', function(done) { - var keys = ['@', '@', '\\', '\\', '|', '|']; - - xterm.on('keypress', function(key) { - if (key) { - var index = keys.indexOf(key); - assert(index !== -1, "Emitted wrong key: " + key); - keys.splice(index, 1); - } - if (keys.length === 0) done(); - }); - - xterm.keyPress( - Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, keyCode: 64 }) - ); // @ - xterm.keyPress( - Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, charCode: 64, keyCode: 0 }) - ); - xterm.keyPress( - Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, keyCode: 92 }) - ); // \ - xterm.keyPress( - Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, charCode: 92, keyCode: 0 }) - ); - xterm.keyPress( - Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, keyCode: 124 }) - ); // | - xterm.keyPress( - Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, charCode: 124, keyCode: 0 }) - ); - }); - }); - }); - - describe('unicode - surrogates', function() { - it('2 characters per cell', function () { - this.timeout(10000); // This is needed because istanbul patches code and slows it down - var high = String.fromCharCode(0xD800); - for (var i=0xDC00; i<=0xDCFF; ++i) { - xterm.write(high + String.fromCharCode(i)); - var tchar = xterm.buffer.lines.get(0)[0]; - expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); - expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); - expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); - expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); - xterm.reset(); - } - }); - it('2 characters at last cell', function() { - var high = String.fromCharCode(0xD800); - for (var i=0xDC00; i<=0xDCFF; ++i) { - xterm.buffer.x = xterm.cols - 1; - xterm.write(high + String.fromCharCode(i)); - expect(xterm.buffer.lines.get(0)[xterm.buffer.x-1][CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); - expect(xterm.buffer.lines.get(0)[xterm.buffer.x-1][CHAR_DATA_CHAR_INDEX].length).eql(2); - expect(xterm.buffer.lines.get(1)[0][CHAR_DATA_CHAR_INDEX]).eql(' '); - xterm.reset(); - } - }); - it('2 characters per cell over line end with autowrap', function() { - var high = String.fromCharCode(0xD800); - for (var i=0xDC00; i<=0xDCFF; ++i) { - xterm.buffer.x = xterm.cols - 1; - xterm.wraparoundMode = true; - xterm.write('a' + high + String.fromCharCode(i)); - expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX]).eql('a'); - expect(xterm.buffer.lines.get(1)[0][CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); - expect(xterm.buffer.lines.get(1)[0][CHAR_DATA_CHAR_INDEX].length).eql(2); - expect(xterm.buffer.lines.get(1)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); - xterm.reset(); - } - }); - it('2 characters per cell over line end without autowrap', function() { - var high = String.fromCharCode(0xD800); - for (var i=0xDC00; i<=0xDCFF; ++i) { - xterm.buffer.x = xterm.cols - 1; - xterm.wraparoundMode = false; - xterm.write('a' + high + String.fromCharCode(i)); - // auto wraparound mode should cut off the rest of the line - expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX]).eql('a'); - expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX].length).eql(1); - expect(xterm.buffer.lines.get(1)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); - xterm.reset(); - } - }); - it('splitted surrogates', function() { - var high = String.fromCharCode(0xD800); - for (var i=0xDC00; i<=0xDCFF; ++i) { - xterm.write(high); - xterm.write(String.fromCharCode(i)); - var tchar = xterm.buffer.lines.get(0)[0]; - expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); - expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); - expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); - expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); - xterm.reset(); - } - }); - }); - - describe('unicode - combining characters', function() { - it('café', function () { - xterm.write('cafe\u0301'); - expect(xterm.buffer.lines.get(0)[3][CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); - expect(xterm.buffer.lines.get(0)[3][CHAR_DATA_CHAR_INDEX].length).eql(2); - expect(xterm.buffer.lines.get(0)[3][CHAR_DATA_WIDTH_INDEX]).eql(1); - }); - it('café - end of line', function() { - xterm.buffer.x = xterm.cols - 1 - 3; - xterm.write('cafe\u0301'); - expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); - expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_CHAR_INDEX].length).eql(2); - expect(xterm.buffer.lines.get(0)[xterm.cols-1][CHAR_DATA_WIDTH_INDEX]).eql(1); - expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX]).eql(' '); - expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_CHAR_INDEX].length).eql(1); - expect(xterm.buffer.lines.get(0)[1][CHAR_DATA_WIDTH_INDEX]).eql(1); - }); - it('multiple combined é', function() { - xterm.wraparoundMode = true; - xterm.write(Array(100).join('e\u0301')); - for (var i=0; iBrowser; writeBuffer: string[]; children: HTMLElement[]; cursorHidden: boolean; @@ -184,3 +185,34 @@ export class MockBuffer implements IBuffer { throw new Error('Method not implemented.'); } } + +export class MockViewport implements IViewport { + onWheel(ev: WheelEvent): void { + throw new Error('Method not implemented.'); + } + onTouchStart(ev: TouchEvent): void { + throw new Error('Method not implemented.'); + } + onTouchMove(ev: TouchEvent): void { + throw new Error('Method not implemented.'); + } + syncScrollArea(): void { } +} + +export class MockCompositionHelper implements ICompositionHelper { + compositionstart(): void { + throw new Error('Method not implemented.'); + } + compositionupdate(ev: CompositionEvent): void { + throw new Error('Method not implemented.'); + } + compositionend(): void { + throw new Error('Method not implemented.'); + } + updateCompositionElements(dontRecurse?: boolean): void { + throw new Error('Method not implemented.'); + } + keydown(ev: KeyboardEvent): boolean { + return true; + } +} From 28c85b8fb920d69581816cd166d7f6ef459656d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Aug 2017 22:24:22 -0700 Subject: [PATCH 10/17] Convert escape sequences test to TS Part of #862 --- src/test/escape-sequences-test.js | 141 ----------------------------- src/test/escape-sequences-test.ts | 144 ++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 141 deletions(-) delete mode 100644 src/test/escape-sequences-test.js create mode 100644 src/test/escape-sequences-test.ts diff --git a/src/test/escape-sequences-test.js b/src/test/escape-sequences-test.js deleted file mode 100644 index 51e5d017..00000000 --- a/src/test/escape-sequences-test.js +++ /dev/null @@ -1,141 +0,0 @@ -var glob = require('glob'); -var fs = require('fs'); -var os = require('os'); -var pty = require('node-pty'); -var Terminal = require('../xterm'); -var CHAR_DATA_CHAR_INDEX = require('../Buffer').CHAR_DATA_CHAR_INDEX; - -if (os.platform() === 'win32') { - // Skip tests on Windows since pty.open isn't supported - return; -} - -var CONSOLE_LOG = console.log; - -// expect files need terminal at 80x25! -var COLS = 80; -var ROWS = 25; - -/** some helpers for pty interaction */ -// we need a pty in between to get the termios decorations -// for the basic test cases a raw pty device is enough -var primitive_pty = pty.native.open(COLS, ROWS); - -// fake sychronous pty write - read -// we just pipe the data from slave to master as a child program would do -// pty.js opens pipe fds with O_NONBLOCK -// just wait 10ms instead of setting fds to blocking mode -function ptyWriteRead(s, cb) { - fs.writeSync(primitive_pty.slave, s); - setTimeout(() => { - var b = Buffer(64000); - var bytes = fs.readSync(primitive_pty.master, b, 0, 64000); - cb(b.toString('utf8', 0, bytes)); - }); -} - -// make sure raw pty is at x=0 and has no pending data -function ptyReset(cb) { - ptyWriteRead('\r\n', cb); -} - -/* debug helpers */ -// generate colorful noisy output to compare xterm and emulator cell states -function formatError(in_, out_, expected) { - function addLineNumber(start, color) { - var counter = start || 0; - return function(s) { - counter += 1; - return '\x1b[33m' + (' ' + counter).slice(-2) + color + s; - } - } - var line80 = '12345678901234567890123456789012345678901234567890123456789012345678901234567890'; - var s = ''; - s += '\n\x1b[34m' + JSON.stringify(in_); - s += '\n\x1b[33m ' + line80 + '\n'; - s += out_.split('\n').map(addLineNumber(0, '\x1b[31m')).join('\n'); - s += '\n\x1b[33m ' + line80 + '\n'; - s += expected.split('\n').map(addLineNumber(0, '\x1b[32m')).join('\n'); - return s; -} - -// simple debug output of terminal cells -function terminalToString(term) { - var result = ''; - var line_s = ''; - for (var line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) { - line_s = ''; - for (var cell=0; cell= 0) { - continue; - } - (function(filename) { - it(filename.split('/').slice(-1)[0], done => { - ptyReset(() => { - var in_file = fs.readFileSync(filename, 'utf8'); - ptyWriteRead(in_file, from_pty => { - // uncomment this to get log from terminal - //console.log = function(){}; - - // Perform a synchronous .write(data) - xterm.writeBuffer.push(from_pty); - xterm.innerWrite(); - - var from_emulator = terminalToString(xterm); - console.log = CONSOLE_LOG; - var expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8'); - // Some of the tests have whitespace on the right of lines, we trim all the linex - // from xterm.js so ignore this for now at least. - var expectedRightTrimmed = expected.split('\n').map(function (l) { - return l.replace(/\s+$/, ''); - }).join('\n'); - if (from_emulator != expectedRightTrimmed) { - // uncomment to get noisy output - throw new Error(formatError(in_file, from_emulator, expected)); - // throw new Error('mismatch'); - } - done(); - }); - }); - }); - })(files[i]); - } -}); - diff --git a/src/test/escape-sequences-test.ts b/src/test/escape-sequences-test.ts new file mode 100644 index 00000000..554298d0 --- /dev/null +++ b/src/test/escape-sequences-test.ts @@ -0,0 +1,144 @@ +/** + * @license MIT + * + * This file contains integration tests for xterm.js + */ + +import * as glob from 'glob'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as pty from 'node-pty'; +import { Terminal } from '../Terminal'; +import { CHAR_DATA_CHAR_INDEX } from '../Buffer'; + +let primitive_pty: any; + +// fake sychronous pty write - read +// we just pipe the data from slave to master as a child program would do +// pty.js opens pipe fds with O_NONBLOCK +// just wait 10ms instead of setting fds to blocking mode +function ptyWriteRead(data: string, cb: (result: string) => void): void { + fs.writeSync(primitive_pty.slave, data); + setTimeout(() => { + let b = new Buffer(64000); + let bytes = fs.readSync(primitive_pty.master, b, 0, 64000, null); + cb(b.toString('utf8', 0, bytes)); + }); +} + +// make sure raw pty is at x=0 and has no pending data +function ptyReset(cb: (result: string) => void): void { + ptyWriteRead('\r\n', cb); +} + +/* debug helpers */ +// generate colorful noisy output to compare xterm and emulator cell states +function formatError(in_: string, out_: string, expected: string): string { + function addLineNumber(start: number, color: string): (s: string) => string { + let counter = start || 0; + return function(s: string): string { + counter += 1; + return '\x1b[33m' + (' ' + counter).slice(-2) + color + s; + }; + } + let line80 = '12345678901234567890123456789012345678901234567890123456789012345678901234567890'; + let s = ''; + s += '\n\x1b[34m' + JSON.stringify(in_); + s += '\n\x1b[33m ' + line80 + '\n'; + s += out_.split('\n').map(addLineNumber(0, '\x1b[31m')).join('\n'); + s += '\n\x1b[33m ' + line80 + '\n'; + s += expected.split('\n').map(addLineNumber(0, '\x1b[32m')).join('\n'); + return s; +} + +// simple debug output of terminal cells +function terminalToString(term: Terminal): string { + let result = ''; + let line_s = ''; + for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) { + line_s = ''; + for (let cell = 0; cell < term.cols; ++cell) { + line_s += term.buffer.lines.get(line)[cell][CHAR_DATA_CHAR_INDEX]; + } + // rtrim empty cells as xterm does + line_s = line_s.replace(/\s+$/, ''); + result += line_s; + result += '\n'; + } + return result; +} + +// Skip tests on Windows since pty.open isn't supported +if (os.platform() !== 'win32') { + let CONSOLE_LOG = console.log; + + // expect files need terminal at 80x25! + let COLS = 80; + let ROWS = 25; + + /** some helpers for pty interaction */ + // we need a pty in between to get the termios decorations + // for the basic test cases a raw pty device is enough + primitive_pty = pty.native.open(COLS, ROWS); + + /** tests */ + describe('xterm output comparison', () => { + let xterm; + + beforeEach(() => { + xterm = new Terminal({ cols: COLS, rows: ROWS }); + xterm.refresh = () => {}; + xterm.viewport = { + syncScrollArea: () => {} + }; + }); + + // omit stack trace for escape sequence files + Error.stackTraceLimit = 0; + let files = glob.sync('**/escape_sequence_files/*.in'); + // only successful tests for now + let skip = [ + 10, 16, 17, 19, 32, 33, 34, 35, 36, 39, + 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, + 63, 68 + ]; + if (os.platform() === 'darwin') { + // These are failing on macOS only + skip.push(3, 7, 11, 67); + } + for (let i = 0; i < files.length; i++) { + if (skip.indexOf(i) >= 0) { + continue; + } + ((filename: string) => { + it(filename.split('/').slice(-1)[0], done => { + ptyReset(() => { + let in_file = fs.readFileSync(filename, 'utf8'); + ptyWriteRead(in_file, from_pty => { + // uncomment this to get log from terminal + // console.log = function(){}; + + // Perform a synchronous .write(data) + xterm.writeBuffer.push(from_pty); + xterm.innerWrite(); + + let from_emulator = terminalToString(xterm); + console.log = CONSOLE_LOG; + let expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8'); + // Some of the tests have whitespace on the right of lines, we trim all the linex + // from xterm.js so ignore this for now at least. + let expectedRightTrimmed = expected.split('\n').map(l => l.replace(/\s+$/, '')).join('\n'); + if (from_emulator !== expectedRightTrimmed) { + // uncomment to get noisy output + throw new Error(formatError(in_file, from_emulator, expected)); + // throw new Error('mismatch'); + } + done(); + }); + }); + }); + })(files[i]); + } + }); +} From b3ad3eff8b14fdab9aa5adebd240518926e1ddc3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Aug 2017 22:38:42 -0700 Subject: [PATCH 11/17] Shuffle files around src/test/escape-sequences-test.ts -> src/Terminal.integration.ts src/test/escape_sequence_files/* -> fixtures/escape_sequence_files/ --- {src/test => fixtures}/escape_sequence_files/NOTES | 0 .../escape_sequence_files/t0001-all_printable.in | 0 .../escape_sequence_files/t0001-all_printable.text | 0 .../escape_sequence_files/t0002-history.in | 0 .../escape_sequence_files/t0002-history.text | 0 .../escape_sequence_files/t0002j-simple_string.in | 0 .../escape_sequence_files/t0002j-simple_string.text | 0 .../escape_sequence_files/t0003-line_wrap.in | 0 .../escape_sequence_files/t0003-line_wrap.text | 0 .../test => fixtures}/escape_sequence_files/t0003j-LF.in | 0 .../escape_sequence_files/t0003j-LF.text | 0 {src/test => fixtures}/escape_sequence_files/t0004-LF.in | 0 .../escape_sequence_files/t0004-LF.text | 0 .../test => fixtures}/escape_sequence_files/t0004j-CR.in | 0 .../escape_sequence_files/t0004j-CR.text | 0 {src/test => fixtures}/escape_sequence_files/t0005-CR.in | 0 .../escape_sequence_files/t0005-CR.text | 0 .../test => fixtures}/escape_sequence_files/t0006-IND.in | 0 .../escape_sequence_files/t0006-IND.text | 0 .../escape_sequence_files/t0007-space_at_end.in | 0 .../escape_sequence_files/t0007-space_at_end.text | 0 {src/test => fixtures}/escape_sequence_files/t0008-BS.in | 0 .../escape_sequence_files/t0008-BS.text | 0 .../test => fixtures}/escape_sequence_files/t0009-NEL.in | 0 .../escape_sequence_files/t0009-NEL.text | 0 {src/test => fixtures}/escape_sequence_files/t0010-RI.in | 0 .../escape_sequence_files/t0010-RI.text | 0 .../escape_sequence_files/t0011-RI_scroll.in | 0 .../escape_sequence_files/t0011-RI_scroll.text | 0 {src/test => fixtures}/escape_sequence_files/t0012-VT.in | 0 .../escape_sequence_files/t0012-VT.text | 0 {src/test => fixtures}/escape_sequence_files/t0013-FF.in | 0 .../escape_sequence_files/t0013-FF.text | 0 .../test => fixtures}/escape_sequence_files/t0014-CAN.in | 0 .../escape_sequence_files/t0014-CAN.text | 0 .../test => fixtures}/escape_sequence_files/t0015-SUB.in | 0 .../escape_sequence_files/t0015-SUB.text | 0 {src/test => fixtures}/escape_sequence_files/t0016-SU.in | 0 .../escape_sequence_files/t0016-SU.text | 0 {src/test => fixtures}/escape_sequence_files/t0017-SD.in | 0 .../escape_sequence_files/t0017-SD.text | 0 .../test => fixtures}/escape_sequence_files/t0020-CUF.in | 0 .../escape_sequence_files/t0020-CUF.text | 0 .../test => fixtures}/escape_sequence_files/t0021-CUB.in | 0 .../escape_sequence_files/t0021-CUB.text | 0 .../test => fixtures}/escape_sequence_files/t0022-CUU.in | 0 .../escape_sequence_files/t0022-CUU.text | 0 .../escape_sequence_files/t0023-CUU_scroll.in | 0 .../escape_sequence_files/t0023-CUU_scroll.text | 0 .../test => fixtures}/escape_sequence_files/t0024-CUD.in | 0 .../escape_sequence_files/t0024-CUD.text | 0 .../test => fixtures}/escape_sequence_files/t0025-CUP.in | 0 .../escape_sequence_files/t0025-CUP.text | 0 .../test => fixtures}/escape_sequence_files/t0026-CNL.in | 0 .../escape_sequence_files/t0026-CNL.text | 0 .../test => fixtures}/escape_sequence_files/t0027-CPL.in | 0 .../escape_sequence_files/t0027-CPL.text | 0 .../test => fixtures}/escape_sequence_files/t0030-HPR.in | 0 .../escape_sequence_files/t0030-HPR.text | 0 .../escape_sequence_files/t0031-HPB.in_ | 0 .../escape_sequence_files/t0031-HPB.text | 0 .../test => fixtures}/escape_sequence_files/t0032-VPB.in | 0 .../escape_sequence_files/t0032-VPB.text | 0 .../escape_sequence_files/t0033-VPB_scroll.in | 0 .../escape_sequence_files/t0033-VPB_scroll.text | 0 .../test => fixtures}/escape_sequence_files/t0034-VPR.in | 0 .../escape_sequence_files/t0034-VPR.text | 0 .../test => fixtures}/escape_sequence_files/t0035-HVP.in | 0 .../escape_sequence_files/t0035-HVP.text | 0 .../test => fixtures}/escape_sequence_files/t0040-REP.in | 0 .../escape_sequence_files/t0040-REP.text | 0 .../test => fixtures}/escape_sequence_files/t0050-ICH.in | 0 .../escape_sequence_files/t0050-ICH.text | 0 {src/test => fixtures}/escape_sequence_files/t0051-IL.in | 0 .../escape_sequence_files/t0051-IL.text | 0 {src/test => fixtures}/escape_sequence_files/t0052-DL.in | 0 .../escape_sequence_files/t0052-DL.text | 0 .../test => fixtures}/escape_sequence_files/t0053-DCH.in | 0 .../escape_sequence_files/t0053-DCH.text | 0 .../test => fixtures}/escape_sequence_files/t0054-ECH.in | 0 .../escape_sequence_files/t0054-ECH.text | 0 {src/test => fixtures}/escape_sequence_files/t0055-EL.in | 0 .../escape_sequence_files/t0055-EL.text | 0 {src/test => fixtures}/escape_sequence_files/t0056-ED.in | 0 .../escape_sequence_files/t0056-ED.text | 0 .../test => fixtures}/escape_sequence_files/t0057-ED3.in | 0 .../escape_sequence_files/t0057-ED3.note | 0 .../escape_sequence_files/t0057-ED3.text | 0 .../escape_sequence_files/t0060-DECSC.in | 0 .../escape_sequence_files/t0060-DECSC.text | 0 .../escape_sequence_files/t0061-CSI_s.in | 0 .../escape_sequence_files/t0061-CSI_s.text | 0 .../escape_sequence_files/t0070-DECSTBM_LF.in | 0 .../escape_sequence_files/t0070-DECSTBM_LF.text | 0 .../escape_sequence_files/t0071-DECSTBM_IND.in | 0 .../escape_sequence_files/t0071-DECSTBM_IND.text | 0 .../escape_sequence_files/t0072-DECSTBM_NEL.in | 0 .../escape_sequence_files/t0072-DECSTBM_NEL.text | 0 .../escape_sequence_files/t0073-DECSTBM_RI.in | 0 .../escape_sequence_files/t0073-DECSTBM_RI.text | 0 .../escape_sequence_files/t0074-DECSTBM_SU_SD.in | 0 .../escape_sequence_files/t0074-DECSTBM_SU_SD.text | 0 .../escape_sequence_files/t0075-DECSTBM_CUU_CUD.in | 0 .../escape_sequence_files/t0075-DECSTBM_CUU_CUD.text | 0 .../escape_sequence_files/t0076-DECSTBM_IL_DL.in | 0 .../escape_sequence_files/t0076-DECSTBM_IL_DL.text | 0 .../escape_sequence_files/t0077-DECSTBM_quirks.in | 0 .../escape_sequence_files/t0077-DECSTBM_quirks.text | 0 {src/test => fixtures}/escape_sequence_files/t0080-HT.in | 0 .../escape_sequence_files/t0080-HT.text | 0 .../test => fixtures}/escape_sequence_files/t0081-TBC.in | 0 .../escape_sequence_files/t0081-TBC.text | 0 .../test => fixtures}/escape_sequence_files/t0082-HTS.in | 0 .../escape_sequence_files/t0082-HTS.text | 0 .../test => fixtures}/escape_sequence_files/t0083-CHT.in | 0 .../escape_sequence_files/t0083-CHT.text | 0 .../test => fixtures}/escape_sequence_files/t0084-CBT.in | 0 .../escape_sequence_files/t0084-CBT.text | 0 .../escape_sequence_files/t0084-CBT.text-xterm | 0 .../escape_sequence_files/t0090-alt_screen.in | 0 .../escape_sequence_files/t0090-alt_screen.text | 0 .../escape_sequence_files/t0091-alt_screen_ED3.in | 0 .../escape_sequence_files/t0091-alt_screen_ED3.text | 0 .../escape_sequence_files/t0092-alt_screen_DECSC.in | 0 .../escape_sequence_files/t0092-alt_screen_DECSC.text | 0 .../test => fixtures}/escape_sequence_files/t0100-IRM.in | 0 .../escape_sequence_files/t0100-IRM.text | 0 .../test => fixtures}/escape_sequence_files/t0101-NLM.in | 0 .../escape_sequence_files/t0101-NLM.text | 0 .../escape_sequence_files/t0102-DECAWM.in | 0 .../escape_sequence_files/t0102-DECAWM.text | 0 .../escape_sequence_files/t0103-reverse_wrap.in | 0 .../escape_sequence_files/t0103-reverse_wrap.text | 0 .../escape_sequence_files/t0200-SGR.html | 0 .../escape_sequence_files/t0200-SGR.in_ | 0 .../escape_sequence_files/t0220-SGR_inverse.html | 0 .../escape_sequence_files/t0220-SGR_inverse.in_ | 0 .../escape_sequence_files/t0500-bash_long_line.in | 0 .../escape_sequence_files/t0500-bash_long_line.text | 0 .../escape_sequence_files/t0501-bash_ls.in | 0 .../escape_sequence_files/t0501-bash_ls.text | 0 .../escape_sequence_files/t0502-bash_ls_color.in | 0 .../escape_sequence_files/t0502-bash_ls_color.text | 0 .../escape_sequence_files/t0503-zsh_ls_color.in | 0 .../escape_sequence_files/t0503-zsh_ls_color.text | 0 .../test => fixtures}/escape_sequence_files/t0504-vim.in | 0 .../escape_sequence_files/t0504-vim.text | 0 gulpfile.js | 7 ++++++- .../escape-sequences-test.ts => Terminal.integration.ts} | 9 +++++---- 149 files changed, 11 insertions(+), 5 deletions(-) rename {src/test => fixtures}/escape_sequence_files/NOTES (100%) rename {src/test => fixtures}/escape_sequence_files/t0001-all_printable.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0001-all_printable.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0002-history.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0002-history.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0002j-simple_string.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0002j-simple_string.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0003-line_wrap.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0003-line_wrap.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0003j-LF.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0003j-LF.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0004-LF.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0004-LF.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0004j-CR.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0004j-CR.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0005-CR.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0005-CR.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0006-IND.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0006-IND.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0007-space_at_end.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0007-space_at_end.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0008-BS.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0008-BS.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0009-NEL.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0009-NEL.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0010-RI.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0010-RI.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0011-RI_scroll.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0011-RI_scroll.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0012-VT.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0012-VT.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0013-FF.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0013-FF.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0014-CAN.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0014-CAN.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0015-SUB.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0015-SUB.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0016-SU.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0016-SU.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0017-SD.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0017-SD.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0020-CUF.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0020-CUF.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0021-CUB.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0021-CUB.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0022-CUU.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0022-CUU.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0023-CUU_scroll.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0023-CUU_scroll.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0024-CUD.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0024-CUD.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0025-CUP.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0025-CUP.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0026-CNL.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0026-CNL.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0027-CPL.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0027-CPL.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0030-HPR.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0030-HPR.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0031-HPB.in_ (100%) rename {src/test => fixtures}/escape_sequence_files/t0031-HPB.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0032-VPB.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0032-VPB.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0033-VPB_scroll.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0033-VPB_scroll.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0034-VPR.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0034-VPR.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0035-HVP.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0035-HVP.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0040-REP.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0040-REP.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0050-ICH.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0050-ICH.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0051-IL.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0051-IL.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0052-DL.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0052-DL.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0053-DCH.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0053-DCH.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0054-ECH.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0054-ECH.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0055-EL.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0055-EL.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0056-ED.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0056-ED.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0057-ED3.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0057-ED3.note (100%) rename {src/test => fixtures}/escape_sequence_files/t0057-ED3.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0060-DECSC.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0060-DECSC.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0061-CSI_s.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0061-CSI_s.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0070-DECSTBM_LF.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0070-DECSTBM_LF.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0071-DECSTBM_IND.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0071-DECSTBM_IND.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0072-DECSTBM_NEL.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0072-DECSTBM_NEL.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0073-DECSTBM_RI.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0073-DECSTBM_RI.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0074-DECSTBM_SU_SD.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0074-DECSTBM_SU_SD.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0076-DECSTBM_IL_DL.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0076-DECSTBM_IL_DL.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0077-DECSTBM_quirks.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0077-DECSTBM_quirks.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0080-HT.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0080-HT.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0081-TBC.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0081-TBC.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0082-HTS.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0082-HTS.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0083-CHT.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0083-CHT.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0084-CBT.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0084-CBT.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0084-CBT.text-xterm (100%) rename {src/test => fixtures}/escape_sequence_files/t0090-alt_screen.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0090-alt_screen.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0091-alt_screen_ED3.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0091-alt_screen_ED3.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0092-alt_screen_DECSC.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0092-alt_screen_DECSC.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0100-IRM.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0100-IRM.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0101-NLM.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0101-NLM.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0102-DECAWM.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0102-DECAWM.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0103-reverse_wrap.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0103-reverse_wrap.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0200-SGR.html (100%) rename {src/test => fixtures}/escape_sequence_files/t0200-SGR.in_ (100%) rename {src/test => fixtures}/escape_sequence_files/t0220-SGR_inverse.html (100%) rename {src/test => fixtures}/escape_sequence_files/t0220-SGR_inverse.in_ (100%) rename {src/test => fixtures}/escape_sequence_files/t0500-bash_long_line.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0500-bash_long_line.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0501-bash_ls.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0501-bash_ls.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0502-bash_ls_color.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0502-bash_ls_color.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0503-zsh_ls_color.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0503-zsh_ls_color.text (100%) rename {src/test => fixtures}/escape_sequence_files/t0504-vim.in (100%) rename {src/test => fixtures}/escape_sequence_files/t0504-vim.text (100%) rename src/{test/escape-sequences-test.ts => Terminal.integration.ts} (94%) diff --git a/src/test/escape_sequence_files/NOTES b/fixtures/escape_sequence_files/NOTES similarity index 100% rename from src/test/escape_sequence_files/NOTES rename to fixtures/escape_sequence_files/NOTES diff --git a/src/test/escape_sequence_files/t0001-all_printable.in b/fixtures/escape_sequence_files/t0001-all_printable.in similarity index 100% rename from src/test/escape_sequence_files/t0001-all_printable.in rename to fixtures/escape_sequence_files/t0001-all_printable.in diff --git a/src/test/escape_sequence_files/t0001-all_printable.text b/fixtures/escape_sequence_files/t0001-all_printable.text similarity index 100% rename from src/test/escape_sequence_files/t0001-all_printable.text rename to fixtures/escape_sequence_files/t0001-all_printable.text diff --git a/src/test/escape_sequence_files/t0002-history.in b/fixtures/escape_sequence_files/t0002-history.in similarity index 100% rename from src/test/escape_sequence_files/t0002-history.in rename to fixtures/escape_sequence_files/t0002-history.in diff --git a/src/test/escape_sequence_files/t0002-history.text b/fixtures/escape_sequence_files/t0002-history.text similarity index 100% rename from src/test/escape_sequence_files/t0002-history.text rename to fixtures/escape_sequence_files/t0002-history.text diff --git a/src/test/escape_sequence_files/t0002j-simple_string.in b/fixtures/escape_sequence_files/t0002j-simple_string.in similarity index 100% rename from src/test/escape_sequence_files/t0002j-simple_string.in rename to fixtures/escape_sequence_files/t0002j-simple_string.in diff --git a/src/test/escape_sequence_files/t0002j-simple_string.text b/fixtures/escape_sequence_files/t0002j-simple_string.text similarity index 100% rename from src/test/escape_sequence_files/t0002j-simple_string.text rename to fixtures/escape_sequence_files/t0002j-simple_string.text diff --git a/src/test/escape_sequence_files/t0003-line_wrap.in b/fixtures/escape_sequence_files/t0003-line_wrap.in similarity index 100% rename from src/test/escape_sequence_files/t0003-line_wrap.in rename to fixtures/escape_sequence_files/t0003-line_wrap.in diff --git a/src/test/escape_sequence_files/t0003-line_wrap.text b/fixtures/escape_sequence_files/t0003-line_wrap.text similarity index 100% rename from src/test/escape_sequence_files/t0003-line_wrap.text rename to fixtures/escape_sequence_files/t0003-line_wrap.text diff --git a/src/test/escape_sequence_files/t0003j-LF.in b/fixtures/escape_sequence_files/t0003j-LF.in similarity index 100% rename from src/test/escape_sequence_files/t0003j-LF.in rename to fixtures/escape_sequence_files/t0003j-LF.in diff --git a/src/test/escape_sequence_files/t0003j-LF.text b/fixtures/escape_sequence_files/t0003j-LF.text similarity index 100% rename from src/test/escape_sequence_files/t0003j-LF.text rename to fixtures/escape_sequence_files/t0003j-LF.text diff --git a/src/test/escape_sequence_files/t0004-LF.in b/fixtures/escape_sequence_files/t0004-LF.in similarity index 100% rename from src/test/escape_sequence_files/t0004-LF.in rename to fixtures/escape_sequence_files/t0004-LF.in diff --git a/src/test/escape_sequence_files/t0004-LF.text b/fixtures/escape_sequence_files/t0004-LF.text similarity index 100% rename from src/test/escape_sequence_files/t0004-LF.text rename to fixtures/escape_sequence_files/t0004-LF.text diff --git a/src/test/escape_sequence_files/t0004j-CR.in b/fixtures/escape_sequence_files/t0004j-CR.in similarity index 100% rename from src/test/escape_sequence_files/t0004j-CR.in rename to fixtures/escape_sequence_files/t0004j-CR.in diff --git a/src/test/escape_sequence_files/t0004j-CR.text b/fixtures/escape_sequence_files/t0004j-CR.text similarity index 100% rename from src/test/escape_sequence_files/t0004j-CR.text rename to fixtures/escape_sequence_files/t0004j-CR.text diff --git a/src/test/escape_sequence_files/t0005-CR.in b/fixtures/escape_sequence_files/t0005-CR.in similarity index 100% rename from src/test/escape_sequence_files/t0005-CR.in rename to fixtures/escape_sequence_files/t0005-CR.in diff --git a/src/test/escape_sequence_files/t0005-CR.text b/fixtures/escape_sequence_files/t0005-CR.text similarity index 100% rename from src/test/escape_sequence_files/t0005-CR.text rename to fixtures/escape_sequence_files/t0005-CR.text diff --git a/src/test/escape_sequence_files/t0006-IND.in b/fixtures/escape_sequence_files/t0006-IND.in similarity index 100% rename from src/test/escape_sequence_files/t0006-IND.in rename to fixtures/escape_sequence_files/t0006-IND.in diff --git a/src/test/escape_sequence_files/t0006-IND.text b/fixtures/escape_sequence_files/t0006-IND.text similarity index 100% rename from src/test/escape_sequence_files/t0006-IND.text rename to fixtures/escape_sequence_files/t0006-IND.text diff --git a/src/test/escape_sequence_files/t0007-space_at_end.in b/fixtures/escape_sequence_files/t0007-space_at_end.in similarity index 100% rename from src/test/escape_sequence_files/t0007-space_at_end.in rename to fixtures/escape_sequence_files/t0007-space_at_end.in diff --git a/src/test/escape_sequence_files/t0007-space_at_end.text b/fixtures/escape_sequence_files/t0007-space_at_end.text similarity index 100% rename from src/test/escape_sequence_files/t0007-space_at_end.text rename to fixtures/escape_sequence_files/t0007-space_at_end.text diff --git a/src/test/escape_sequence_files/t0008-BS.in b/fixtures/escape_sequence_files/t0008-BS.in similarity index 100% rename from src/test/escape_sequence_files/t0008-BS.in rename to fixtures/escape_sequence_files/t0008-BS.in diff --git a/src/test/escape_sequence_files/t0008-BS.text b/fixtures/escape_sequence_files/t0008-BS.text similarity index 100% rename from src/test/escape_sequence_files/t0008-BS.text rename to fixtures/escape_sequence_files/t0008-BS.text diff --git a/src/test/escape_sequence_files/t0009-NEL.in b/fixtures/escape_sequence_files/t0009-NEL.in similarity index 100% rename from src/test/escape_sequence_files/t0009-NEL.in rename to fixtures/escape_sequence_files/t0009-NEL.in diff --git a/src/test/escape_sequence_files/t0009-NEL.text b/fixtures/escape_sequence_files/t0009-NEL.text similarity index 100% rename from src/test/escape_sequence_files/t0009-NEL.text rename to fixtures/escape_sequence_files/t0009-NEL.text diff --git a/src/test/escape_sequence_files/t0010-RI.in b/fixtures/escape_sequence_files/t0010-RI.in similarity index 100% rename from src/test/escape_sequence_files/t0010-RI.in rename to fixtures/escape_sequence_files/t0010-RI.in diff --git a/src/test/escape_sequence_files/t0010-RI.text b/fixtures/escape_sequence_files/t0010-RI.text similarity index 100% rename from src/test/escape_sequence_files/t0010-RI.text rename to fixtures/escape_sequence_files/t0010-RI.text diff --git a/src/test/escape_sequence_files/t0011-RI_scroll.in b/fixtures/escape_sequence_files/t0011-RI_scroll.in similarity index 100% rename from src/test/escape_sequence_files/t0011-RI_scroll.in rename to fixtures/escape_sequence_files/t0011-RI_scroll.in diff --git a/src/test/escape_sequence_files/t0011-RI_scroll.text b/fixtures/escape_sequence_files/t0011-RI_scroll.text similarity index 100% rename from src/test/escape_sequence_files/t0011-RI_scroll.text rename to fixtures/escape_sequence_files/t0011-RI_scroll.text diff --git a/src/test/escape_sequence_files/t0012-VT.in b/fixtures/escape_sequence_files/t0012-VT.in similarity index 100% rename from src/test/escape_sequence_files/t0012-VT.in rename to fixtures/escape_sequence_files/t0012-VT.in diff --git a/src/test/escape_sequence_files/t0012-VT.text b/fixtures/escape_sequence_files/t0012-VT.text similarity index 100% rename from src/test/escape_sequence_files/t0012-VT.text rename to fixtures/escape_sequence_files/t0012-VT.text diff --git a/src/test/escape_sequence_files/t0013-FF.in b/fixtures/escape_sequence_files/t0013-FF.in similarity index 100% rename from src/test/escape_sequence_files/t0013-FF.in rename to fixtures/escape_sequence_files/t0013-FF.in diff --git a/src/test/escape_sequence_files/t0013-FF.text b/fixtures/escape_sequence_files/t0013-FF.text similarity index 100% rename from src/test/escape_sequence_files/t0013-FF.text rename to fixtures/escape_sequence_files/t0013-FF.text diff --git a/src/test/escape_sequence_files/t0014-CAN.in b/fixtures/escape_sequence_files/t0014-CAN.in similarity index 100% rename from src/test/escape_sequence_files/t0014-CAN.in rename to fixtures/escape_sequence_files/t0014-CAN.in diff --git a/src/test/escape_sequence_files/t0014-CAN.text b/fixtures/escape_sequence_files/t0014-CAN.text similarity index 100% rename from src/test/escape_sequence_files/t0014-CAN.text rename to fixtures/escape_sequence_files/t0014-CAN.text diff --git a/src/test/escape_sequence_files/t0015-SUB.in b/fixtures/escape_sequence_files/t0015-SUB.in similarity index 100% rename from src/test/escape_sequence_files/t0015-SUB.in rename to fixtures/escape_sequence_files/t0015-SUB.in diff --git a/src/test/escape_sequence_files/t0015-SUB.text b/fixtures/escape_sequence_files/t0015-SUB.text similarity index 100% rename from src/test/escape_sequence_files/t0015-SUB.text rename to fixtures/escape_sequence_files/t0015-SUB.text diff --git a/src/test/escape_sequence_files/t0016-SU.in b/fixtures/escape_sequence_files/t0016-SU.in similarity index 100% rename from src/test/escape_sequence_files/t0016-SU.in rename to fixtures/escape_sequence_files/t0016-SU.in diff --git a/src/test/escape_sequence_files/t0016-SU.text b/fixtures/escape_sequence_files/t0016-SU.text similarity index 100% rename from src/test/escape_sequence_files/t0016-SU.text rename to fixtures/escape_sequence_files/t0016-SU.text diff --git a/src/test/escape_sequence_files/t0017-SD.in b/fixtures/escape_sequence_files/t0017-SD.in similarity index 100% rename from src/test/escape_sequence_files/t0017-SD.in rename to fixtures/escape_sequence_files/t0017-SD.in diff --git a/src/test/escape_sequence_files/t0017-SD.text b/fixtures/escape_sequence_files/t0017-SD.text similarity index 100% rename from src/test/escape_sequence_files/t0017-SD.text rename to fixtures/escape_sequence_files/t0017-SD.text diff --git a/src/test/escape_sequence_files/t0020-CUF.in b/fixtures/escape_sequence_files/t0020-CUF.in similarity index 100% rename from src/test/escape_sequence_files/t0020-CUF.in rename to fixtures/escape_sequence_files/t0020-CUF.in diff --git a/src/test/escape_sequence_files/t0020-CUF.text b/fixtures/escape_sequence_files/t0020-CUF.text similarity index 100% rename from src/test/escape_sequence_files/t0020-CUF.text rename to fixtures/escape_sequence_files/t0020-CUF.text diff --git a/src/test/escape_sequence_files/t0021-CUB.in b/fixtures/escape_sequence_files/t0021-CUB.in similarity index 100% rename from src/test/escape_sequence_files/t0021-CUB.in rename to fixtures/escape_sequence_files/t0021-CUB.in diff --git a/src/test/escape_sequence_files/t0021-CUB.text b/fixtures/escape_sequence_files/t0021-CUB.text similarity index 100% rename from src/test/escape_sequence_files/t0021-CUB.text rename to fixtures/escape_sequence_files/t0021-CUB.text diff --git a/src/test/escape_sequence_files/t0022-CUU.in b/fixtures/escape_sequence_files/t0022-CUU.in similarity index 100% rename from src/test/escape_sequence_files/t0022-CUU.in rename to fixtures/escape_sequence_files/t0022-CUU.in diff --git a/src/test/escape_sequence_files/t0022-CUU.text b/fixtures/escape_sequence_files/t0022-CUU.text similarity index 100% rename from src/test/escape_sequence_files/t0022-CUU.text rename to fixtures/escape_sequence_files/t0022-CUU.text diff --git a/src/test/escape_sequence_files/t0023-CUU_scroll.in b/fixtures/escape_sequence_files/t0023-CUU_scroll.in similarity index 100% rename from src/test/escape_sequence_files/t0023-CUU_scroll.in rename to fixtures/escape_sequence_files/t0023-CUU_scroll.in diff --git a/src/test/escape_sequence_files/t0023-CUU_scroll.text b/fixtures/escape_sequence_files/t0023-CUU_scroll.text similarity index 100% rename from src/test/escape_sequence_files/t0023-CUU_scroll.text rename to fixtures/escape_sequence_files/t0023-CUU_scroll.text diff --git a/src/test/escape_sequence_files/t0024-CUD.in b/fixtures/escape_sequence_files/t0024-CUD.in similarity index 100% rename from src/test/escape_sequence_files/t0024-CUD.in rename to fixtures/escape_sequence_files/t0024-CUD.in diff --git a/src/test/escape_sequence_files/t0024-CUD.text b/fixtures/escape_sequence_files/t0024-CUD.text similarity index 100% rename from src/test/escape_sequence_files/t0024-CUD.text rename to fixtures/escape_sequence_files/t0024-CUD.text diff --git a/src/test/escape_sequence_files/t0025-CUP.in b/fixtures/escape_sequence_files/t0025-CUP.in similarity index 100% rename from src/test/escape_sequence_files/t0025-CUP.in rename to fixtures/escape_sequence_files/t0025-CUP.in diff --git a/src/test/escape_sequence_files/t0025-CUP.text b/fixtures/escape_sequence_files/t0025-CUP.text similarity index 100% rename from src/test/escape_sequence_files/t0025-CUP.text rename to fixtures/escape_sequence_files/t0025-CUP.text diff --git a/src/test/escape_sequence_files/t0026-CNL.in b/fixtures/escape_sequence_files/t0026-CNL.in similarity index 100% rename from src/test/escape_sequence_files/t0026-CNL.in rename to fixtures/escape_sequence_files/t0026-CNL.in diff --git a/src/test/escape_sequence_files/t0026-CNL.text b/fixtures/escape_sequence_files/t0026-CNL.text similarity index 100% rename from src/test/escape_sequence_files/t0026-CNL.text rename to fixtures/escape_sequence_files/t0026-CNL.text diff --git a/src/test/escape_sequence_files/t0027-CPL.in b/fixtures/escape_sequence_files/t0027-CPL.in similarity index 100% rename from src/test/escape_sequence_files/t0027-CPL.in rename to fixtures/escape_sequence_files/t0027-CPL.in diff --git a/src/test/escape_sequence_files/t0027-CPL.text b/fixtures/escape_sequence_files/t0027-CPL.text similarity index 100% rename from src/test/escape_sequence_files/t0027-CPL.text rename to fixtures/escape_sequence_files/t0027-CPL.text diff --git a/src/test/escape_sequence_files/t0030-HPR.in b/fixtures/escape_sequence_files/t0030-HPR.in similarity index 100% rename from src/test/escape_sequence_files/t0030-HPR.in rename to fixtures/escape_sequence_files/t0030-HPR.in diff --git a/src/test/escape_sequence_files/t0030-HPR.text b/fixtures/escape_sequence_files/t0030-HPR.text similarity index 100% rename from src/test/escape_sequence_files/t0030-HPR.text rename to fixtures/escape_sequence_files/t0030-HPR.text diff --git a/src/test/escape_sequence_files/t0031-HPB.in_ b/fixtures/escape_sequence_files/t0031-HPB.in_ similarity index 100% rename from src/test/escape_sequence_files/t0031-HPB.in_ rename to fixtures/escape_sequence_files/t0031-HPB.in_ diff --git a/src/test/escape_sequence_files/t0031-HPB.text b/fixtures/escape_sequence_files/t0031-HPB.text similarity index 100% rename from src/test/escape_sequence_files/t0031-HPB.text rename to fixtures/escape_sequence_files/t0031-HPB.text diff --git a/src/test/escape_sequence_files/t0032-VPB.in b/fixtures/escape_sequence_files/t0032-VPB.in similarity index 100% rename from src/test/escape_sequence_files/t0032-VPB.in rename to fixtures/escape_sequence_files/t0032-VPB.in diff --git a/src/test/escape_sequence_files/t0032-VPB.text b/fixtures/escape_sequence_files/t0032-VPB.text similarity index 100% rename from src/test/escape_sequence_files/t0032-VPB.text rename to fixtures/escape_sequence_files/t0032-VPB.text diff --git a/src/test/escape_sequence_files/t0033-VPB_scroll.in b/fixtures/escape_sequence_files/t0033-VPB_scroll.in similarity index 100% rename from src/test/escape_sequence_files/t0033-VPB_scroll.in rename to fixtures/escape_sequence_files/t0033-VPB_scroll.in diff --git a/src/test/escape_sequence_files/t0033-VPB_scroll.text b/fixtures/escape_sequence_files/t0033-VPB_scroll.text similarity index 100% rename from src/test/escape_sequence_files/t0033-VPB_scroll.text rename to fixtures/escape_sequence_files/t0033-VPB_scroll.text diff --git a/src/test/escape_sequence_files/t0034-VPR.in b/fixtures/escape_sequence_files/t0034-VPR.in similarity index 100% rename from src/test/escape_sequence_files/t0034-VPR.in rename to fixtures/escape_sequence_files/t0034-VPR.in diff --git a/src/test/escape_sequence_files/t0034-VPR.text b/fixtures/escape_sequence_files/t0034-VPR.text similarity index 100% rename from src/test/escape_sequence_files/t0034-VPR.text rename to fixtures/escape_sequence_files/t0034-VPR.text diff --git a/src/test/escape_sequence_files/t0035-HVP.in b/fixtures/escape_sequence_files/t0035-HVP.in similarity index 100% rename from src/test/escape_sequence_files/t0035-HVP.in rename to fixtures/escape_sequence_files/t0035-HVP.in diff --git a/src/test/escape_sequence_files/t0035-HVP.text b/fixtures/escape_sequence_files/t0035-HVP.text similarity index 100% rename from src/test/escape_sequence_files/t0035-HVP.text rename to fixtures/escape_sequence_files/t0035-HVP.text diff --git a/src/test/escape_sequence_files/t0040-REP.in b/fixtures/escape_sequence_files/t0040-REP.in similarity index 100% rename from src/test/escape_sequence_files/t0040-REP.in rename to fixtures/escape_sequence_files/t0040-REP.in diff --git a/src/test/escape_sequence_files/t0040-REP.text b/fixtures/escape_sequence_files/t0040-REP.text similarity index 100% rename from src/test/escape_sequence_files/t0040-REP.text rename to fixtures/escape_sequence_files/t0040-REP.text diff --git a/src/test/escape_sequence_files/t0050-ICH.in b/fixtures/escape_sequence_files/t0050-ICH.in similarity index 100% rename from src/test/escape_sequence_files/t0050-ICH.in rename to fixtures/escape_sequence_files/t0050-ICH.in diff --git a/src/test/escape_sequence_files/t0050-ICH.text b/fixtures/escape_sequence_files/t0050-ICH.text similarity index 100% rename from src/test/escape_sequence_files/t0050-ICH.text rename to fixtures/escape_sequence_files/t0050-ICH.text diff --git a/src/test/escape_sequence_files/t0051-IL.in b/fixtures/escape_sequence_files/t0051-IL.in similarity index 100% rename from src/test/escape_sequence_files/t0051-IL.in rename to fixtures/escape_sequence_files/t0051-IL.in diff --git a/src/test/escape_sequence_files/t0051-IL.text b/fixtures/escape_sequence_files/t0051-IL.text similarity index 100% rename from src/test/escape_sequence_files/t0051-IL.text rename to fixtures/escape_sequence_files/t0051-IL.text diff --git a/src/test/escape_sequence_files/t0052-DL.in b/fixtures/escape_sequence_files/t0052-DL.in similarity index 100% rename from src/test/escape_sequence_files/t0052-DL.in rename to fixtures/escape_sequence_files/t0052-DL.in diff --git a/src/test/escape_sequence_files/t0052-DL.text b/fixtures/escape_sequence_files/t0052-DL.text similarity index 100% rename from src/test/escape_sequence_files/t0052-DL.text rename to fixtures/escape_sequence_files/t0052-DL.text diff --git a/src/test/escape_sequence_files/t0053-DCH.in b/fixtures/escape_sequence_files/t0053-DCH.in similarity index 100% rename from src/test/escape_sequence_files/t0053-DCH.in rename to fixtures/escape_sequence_files/t0053-DCH.in diff --git a/src/test/escape_sequence_files/t0053-DCH.text b/fixtures/escape_sequence_files/t0053-DCH.text similarity index 100% rename from src/test/escape_sequence_files/t0053-DCH.text rename to fixtures/escape_sequence_files/t0053-DCH.text diff --git a/src/test/escape_sequence_files/t0054-ECH.in b/fixtures/escape_sequence_files/t0054-ECH.in similarity index 100% rename from src/test/escape_sequence_files/t0054-ECH.in rename to fixtures/escape_sequence_files/t0054-ECH.in diff --git a/src/test/escape_sequence_files/t0054-ECH.text b/fixtures/escape_sequence_files/t0054-ECH.text similarity index 100% rename from src/test/escape_sequence_files/t0054-ECH.text rename to fixtures/escape_sequence_files/t0054-ECH.text diff --git a/src/test/escape_sequence_files/t0055-EL.in b/fixtures/escape_sequence_files/t0055-EL.in similarity index 100% rename from src/test/escape_sequence_files/t0055-EL.in rename to fixtures/escape_sequence_files/t0055-EL.in diff --git a/src/test/escape_sequence_files/t0055-EL.text b/fixtures/escape_sequence_files/t0055-EL.text similarity index 100% rename from src/test/escape_sequence_files/t0055-EL.text rename to fixtures/escape_sequence_files/t0055-EL.text diff --git a/src/test/escape_sequence_files/t0056-ED.in b/fixtures/escape_sequence_files/t0056-ED.in similarity index 100% rename from src/test/escape_sequence_files/t0056-ED.in rename to fixtures/escape_sequence_files/t0056-ED.in diff --git a/src/test/escape_sequence_files/t0056-ED.text b/fixtures/escape_sequence_files/t0056-ED.text similarity index 100% rename from src/test/escape_sequence_files/t0056-ED.text rename to fixtures/escape_sequence_files/t0056-ED.text diff --git a/src/test/escape_sequence_files/t0057-ED3.in b/fixtures/escape_sequence_files/t0057-ED3.in similarity index 100% rename from src/test/escape_sequence_files/t0057-ED3.in rename to fixtures/escape_sequence_files/t0057-ED3.in diff --git a/src/test/escape_sequence_files/t0057-ED3.note b/fixtures/escape_sequence_files/t0057-ED3.note similarity index 100% rename from src/test/escape_sequence_files/t0057-ED3.note rename to fixtures/escape_sequence_files/t0057-ED3.note diff --git a/src/test/escape_sequence_files/t0057-ED3.text b/fixtures/escape_sequence_files/t0057-ED3.text similarity index 100% rename from src/test/escape_sequence_files/t0057-ED3.text rename to fixtures/escape_sequence_files/t0057-ED3.text diff --git a/src/test/escape_sequence_files/t0060-DECSC.in b/fixtures/escape_sequence_files/t0060-DECSC.in similarity index 100% rename from src/test/escape_sequence_files/t0060-DECSC.in rename to fixtures/escape_sequence_files/t0060-DECSC.in diff --git a/src/test/escape_sequence_files/t0060-DECSC.text b/fixtures/escape_sequence_files/t0060-DECSC.text similarity index 100% rename from src/test/escape_sequence_files/t0060-DECSC.text rename to fixtures/escape_sequence_files/t0060-DECSC.text diff --git a/src/test/escape_sequence_files/t0061-CSI_s.in b/fixtures/escape_sequence_files/t0061-CSI_s.in similarity index 100% rename from src/test/escape_sequence_files/t0061-CSI_s.in rename to fixtures/escape_sequence_files/t0061-CSI_s.in diff --git a/src/test/escape_sequence_files/t0061-CSI_s.text b/fixtures/escape_sequence_files/t0061-CSI_s.text similarity index 100% rename from src/test/escape_sequence_files/t0061-CSI_s.text rename to fixtures/escape_sequence_files/t0061-CSI_s.text diff --git a/src/test/escape_sequence_files/t0070-DECSTBM_LF.in b/fixtures/escape_sequence_files/t0070-DECSTBM_LF.in similarity index 100% rename from src/test/escape_sequence_files/t0070-DECSTBM_LF.in rename to fixtures/escape_sequence_files/t0070-DECSTBM_LF.in diff --git a/src/test/escape_sequence_files/t0070-DECSTBM_LF.text b/fixtures/escape_sequence_files/t0070-DECSTBM_LF.text similarity index 100% rename from src/test/escape_sequence_files/t0070-DECSTBM_LF.text rename to fixtures/escape_sequence_files/t0070-DECSTBM_LF.text diff --git a/src/test/escape_sequence_files/t0071-DECSTBM_IND.in b/fixtures/escape_sequence_files/t0071-DECSTBM_IND.in similarity index 100% rename from src/test/escape_sequence_files/t0071-DECSTBM_IND.in rename to fixtures/escape_sequence_files/t0071-DECSTBM_IND.in diff --git a/src/test/escape_sequence_files/t0071-DECSTBM_IND.text b/fixtures/escape_sequence_files/t0071-DECSTBM_IND.text similarity index 100% rename from src/test/escape_sequence_files/t0071-DECSTBM_IND.text rename to fixtures/escape_sequence_files/t0071-DECSTBM_IND.text diff --git a/src/test/escape_sequence_files/t0072-DECSTBM_NEL.in b/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.in similarity index 100% rename from src/test/escape_sequence_files/t0072-DECSTBM_NEL.in rename to fixtures/escape_sequence_files/t0072-DECSTBM_NEL.in diff --git a/src/test/escape_sequence_files/t0072-DECSTBM_NEL.text b/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.text similarity index 100% rename from src/test/escape_sequence_files/t0072-DECSTBM_NEL.text rename to fixtures/escape_sequence_files/t0072-DECSTBM_NEL.text diff --git a/src/test/escape_sequence_files/t0073-DECSTBM_RI.in b/fixtures/escape_sequence_files/t0073-DECSTBM_RI.in similarity index 100% rename from src/test/escape_sequence_files/t0073-DECSTBM_RI.in rename to fixtures/escape_sequence_files/t0073-DECSTBM_RI.in diff --git a/src/test/escape_sequence_files/t0073-DECSTBM_RI.text b/fixtures/escape_sequence_files/t0073-DECSTBM_RI.text similarity index 100% rename from src/test/escape_sequence_files/t0073-DECSTBM_RI.text rename to fixtures/escape_sequence_files/t0073-DECSTBM_RI.text diff --git a/src/test/escape_sequence_files/t0074-DECSTBM_SU_SD.in b/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.in similarity index 100% rename from src/test/escape_sequence_files/t0074-DECSTBM_SU_SD.in rename to fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.in diff --git a/src/test/escape_sequence_files/t0074-DECSTBM_SU_SD.text b/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text similarity index 100% rename from src/test/escape_sequence_files/t0074-DECSTBM_SU_SD.text rename to fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text diff --git a/src/test/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in b/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in similarity index 100% rename from src/test/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in rename to fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in diff --git a/src/test/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text b/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text similarity index 100% rename from src/test/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text rename to fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text diff --git a/src/test/escape_sequence_files/t0076-DECSTBM_IL_DL.in b/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.in similarity index 100% rename from src/test/escape_sequence_files/t0076-DECSTBM_IL_DL.in rename to fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.in diff --git a/src/test/escape_sequence_files/t0076-DECSTBM_IL_DL.text b/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text similarity index 100% rename from src/test/escape_sequence_files/t0076-DECSTBM_IL_DL.text rename to fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text diff --git a/src/test/escape_sequence_files/t0077-DECSTBM_quirks.in b/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.in similarity index 100% rename from src/test/escape_sequence_files/t0077-DECSTBM_quirks.in rename to fixtures/escape_sequence_files/t0077-DECSTBM_quirks.in diff --git a/src/test/escape_sequence_files/t0077-DECSTBM_quirks.text b/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.text similarity index 100% rename from src/test/escape_sequence_files/t0077-DECSTBM_quirks.text rename to fixtures/escape_sequence_files/t0077-DECSTBM_quirks.text diff --git a/src/test/escape_sequence_files/t0080-HT.in b/fixtures/escape_sequence_files/t0080-HT.in similarity index 100% rename from src/test/escape_sequence_files/t0080-HT.in rename to fixtures/escape_sequence_files/t0080-HT.in diff --git a/src/test/escape_sequence_files/t0080-HT.text b/fixtures/escape_sequence_files/t0080-HT.text similarity index 100% rename from src/test/escape_sequence_files/t0080-HT.text rename to fixtures/escape_sequence_files/t0080-HT.text diff --git a/src/test/escape_sequence_files/t0081-TBC.in b/fixtures/escape_sequence_files/t0081-TBC.in similarity index 100% rename from src/test/escape_sequence_files/t0081-TBC.in rename to fixtures/escape_sequence_files/t0081-TBC.in diff --git a/src/test/escape_sequence_files/t0081-TBC.text b/fixtures/escape_sequence_files/t0081-TBC.text similarity index 100% rename from src/test/escape_sequence_files/t0081-TBC.text rename to fixtures/escape_sequence_files/t0081-TBC.text diff --git a/src/test/escape_sequence_files/t0082-HTS.in b/fixtures/escape_sequence_files/t0082-HTS.in similarity index 100% rename from src/test/escape_sequence_files/t0082-HTS.in rename to fixtures/escape_sequence_files/t0082-HTS.in diff --git a/src/test/escape_sequence_files/t0082-HTS.text b/fixtures/escape_sequence_files/t0082-HTS.text similarity index 100% rename from src/test/escape_sequence_files/t0082-HTS.text rename to fixtures/escape_sequence_files/t0082-HTS.text diff --git a/src/test/escape_sequence_files/t0083-CHT.in b/fixtures/escape_sequence_files/t0083-CHT.in similarity index 100% rename from src/test/escape_sequence_files/t0083-CHT.in rename to fixtures/escape_sequence_files/t0083-CHT.in diff --git a/src/test/escape_sequence_files/t0083-CHT.text b/fixtures/escape_sequence_files/t0083-CHT.text similarity index 100% rename from src/test/escape_sequence_files/t0083-CHT.text rename to fixtures/escape_sequence_files/t0083-CHT.text diff --git a/src/test/escape_sequence_files/t0084-CBT.in b/fixtures/escape_sequence_files/t0084-CBT.in similarity index 100% rename from src/test/escape_sequence_files/t0084-CBT.in rename to fixtures/escape_sequence_files/t0084-CBT.in diff --git a/src/test/escape_sequence_files/t0084-CBT.text b/fixtures/escape_sequence_files/t0084-CBT.text similarity index 100% rename from src/test/escape_sequence_files/t0084-CBT.text rename to fixtures/escape_sequence_files/t0084-CBT.text diff --git a/src/test/escape_sequence_files/t0084-CBT.text-xterm b/fixtures/escape_sequence_files/t0084-CBT.text-xterm similarity index 100% rename from src/test/escape_sequence_files/t0084-CBT.text-xterm rename to fixtures/escape_sequence_files/t0084-CBT.text-xterm diff --git a/src/test/escape_sequence_files/t0090-alt_screen.in b/fixtures/escape_sequence_files/t0090-alt_screen.in similarity index 100% rename from src/test/escape_sequence_files/t0090-alt_screen.in rename to fixtures/escape_sequence_files/t0090-alt_screen.in diff --git a/src/test/escape_sequence_files/t0090-alt_screen.text b/fixtures/escape_sequence_files/t0090-alt_screen.text similarity index 100% rename from src/test/escape_sequence_files/t0090-alt_screen.text rename to fixtures/escape_sequence_files/t0090-alt_screen.text diff --git a/src/test/escape_sequence_files/t0091-alt_screen_ED3.in b/fixtures/escape_sequence_files/t0091-alt_screen_ED3.in similarity index 100% rename from src/test/escape_sequence_files/t0091-alt_screen_ED3.in rename to fixtures/escape_sequence_files/t0091-alt_screen_ED3.in diff --git a/src/test/escape_sequence_files/t0091-alt_screen_ED3.text b/fixtures/escape_sequence_files/t0091-alt_screen_ED3.text similarity index 100% rename from src/test/escape_sequence_files/t0091-alt_screen_ED3.text rename to fixtures/escape_sequence_files/t0091-alt_screen_ED3.text diff --git a/src/test/escape_sequence_files/t0092-alt_screen_DECSC.in b/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.in similarity index 100% rename from src/test/escape_sequence_files/t0092-alt_screen_DECSC.in rename to fixtures/escape_sequence_files/t0092-alt_screen_DECSC.in diff --git a/src/test/escape_sequence_files/t0092-alt_screen_DECSC.text b/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.text similarity index 100% rename from src/test/escape_sequence_files/t0092-alt_screen_DECSC.text rename to fixtures/escape_sequence_files/t0092-alt_screen_DECSC.text diff --git a/src/test/escape_sequence_files/t0100-IRM.in b/fixtures/escape_sequence_files/t0100-IRM.in similarity index 100% rename from src/test/escape_sequence_files/t0100-IRM.in rename to fixtures/escape_sequence_files/t0100-IRM.in diff --git a/src/test/escape_sequence_files/t0100-IRM.text b/fixtures/escape_sequence_files/t0100-IRM.text similarity index 100% rename from src/test/escape_sequence_files/t0100-IRM.text rename to fixtures/escape_sequence_files/t0100-IRM.text diff --git a/src/test/escape_sequence_files/t0101-NLM.in b/fixtures/escape_sequence_files/t0101-NLM.in similarity index 100% rename from src/test/escape_sequence_files/t0101-NLM.in rename to fixtures/escape_sequence_files/t0101-NLM.in diff --git a/src/test/escape_sequence_files/t0101-NLM.text b/fixtures/escape_sequence_files/t0101-NLM.text similarity index 100% rename from src/test/escape_sequence_files/t0101-NLM.text rename to fixtures/escape_sequence_files/t0101-NLM.text diff --git a/src/test/escape_sequence_files/t0102-DECAWM.in b/fixtures/escape_sequence_files/t0102-DECAWM.in similarity index 100% rename from src/test/escape_sequence_files/t0102-DECAWM.in rename to fixtures/escape_sequence_files/t0102-DECAWM.in diff --git a/src/test/escape_sequence_files/t0102-DECAWM.text b/fixtures/escape_sequence_files/t0102-DECAWM.text similarity index 100% rename from src/test/escape_sequence_files/t0102-DECAWM.text rename to fixtures/escape_sequence_files/t0102-DECAWM.text diff --git a/src/test/escape_sequence_files/t0103-reverse_wrap.in b/fixtures/escape_sequence_files/t0103-reverse_wrap.in similarity index 100% rename from src/test/escape_sequence_files/t0103-reverse_wrap.in rename to fixtures/escape_sequence_files/t0103-reverse_wrap.in diff --git a/src/test/escape_sequence_files/t0103-reverse_wrap.text b/fixtures/escape_sequence_files/t0103-reverse_wrap.text similarity index 100% rename from src/test/escape_sequence_files/t0103-reverse_wrap.text rename to fixtures/escape_sequence_files/t0103-reverse_wrap.text diff --git a/src/test/escape_sequence_files/t0200-SGR.html b/fixtures/escape_sequence_files/t0200-SGR.html similarity index 100% rename from src/test/escape_sequence_files/t0200-SGR.html rename to fixtures/escape_sequence_files/t0200-SGR.html diff --git a/src/test/escape_sequence_files/t0200-SGR.in_ b/fixtures/escape_sequence_files/t0200-SGR.in_ similarity index 100% rename from src/test/escape_sequence_files/t0200-SGR.in_ rename to fixtures/escape_sequence_files/t0200-SGR.in_ diff --git a/src/test/escape_sequence_files/t0220-SGR_inverse.html b/fixtures/escape_sequence_files/t0220-SGR_inverse.html similarity index 100% rename from src/test/escape_sequence_files/t0220-SGR_inverse.html rename to fixtures/escape_sequence_files/t0220-SGR_inverse.html diff --git a/src/test/escape_sequence_files/t0220-SGR_inverse.in_ b/fixtures/escape_sequence_files/t0220-SGR_inverse.in_ similarity index 100% rename from src/test/escape_sequence_files/t0220-SGR_inverse.in_ rename to fixtures/escape_sequence_files/t0220-SGR_inverse.in_ diff --git a/src/test/escape_sequence_files/t0500-bash_long_line.in b/fixtures/escape_sequence_files/t0500-bash_long_line.in similarity index 100% rename from src/test/escape_sequence_files/t0500-bash_long_line.in rename to fixtures/escape_sequence_files/t0500-bash_long_line.in diff --git a/src/test/escape_sequence_files/t0500-bash_long_line.text b/fixtures/escape_sequence_files/t0500-bash_long_line.text similarity index 100% rename from src/test/escape_sequence_files/t0500-bash_long_line.text rename to fixtures/escape_sequence_files/t0500-bash_long_line.text diff --git a/src/test/escape_sequence_files/t0501-bash_ls.in b/fixtures/escape_sequence_files/t0501-bash_ls.in similarity index 100% rename from src/test/escape_sequence_files/t0501-bash_ls.in rename to fixtures/escape_sequence_files/t0501-bash_ls.in diff --git a/src/test/escape_sequence_files/t0501-bash_ls.text b/fixtures/escape_sequence_files/t0501-bash_ls.text similarity index 100% rename from src/test/escape_sequence_files/t0501-bash_ls.text rename to fixtures/escape_sequence_files/t0501-bash_ls.text diff --git a/src/test/escape_sequence_files/t0502-bash_ls_color.in b/fixtures/escape_sequence_files/t0502-bash_ls_color.in similarity index 100% rename from src/test/escape_sequence_files/t0502-bash_ls_color.in rename to fixtures/escape_sequence_files/t0502-bash_ls_color.in diff --git a/src/test/escape_sequence_files/t0502-bash_ls_color.text b/fixtures/escape_sequence_files/t0502-bash_ls_color.text similarity index 100% rename from src/test/escape_sequence_files/t0502-bash_ls_color.text rename to fixtures/escape_sequence_files/t0502-bash_ls_color.text diff --git a/src/test/escape_sequence_files/t0503-zsh_ls_color.in b/fixtures/escape_sequence_files/t0503-zsh_ls_color.in similarity index 100% rename from src/test/escape_sequence_files/t0503-zsh_ls_color.in rename to fixtures/escape_sequence_files/t0503-zsh_ls_color.in diff --git a/src/test/escape_sequence_files/t0503-zsh_ls_color.text b/fixtures/escape_sequence_files/t0503-zsh_ls_color.text similarity index 100% rename from src/test/escape_sequence_files/t0503-zsh_ls_color.text rename to fixtures/escape_sequence_files/t0503-zsh_ls_color.text diff --git a/src/test/escape_sequence_files/t0504-vim.in b/fixtures/escape_sequence_files/t0504-vim.in similarity index 100% rename from src/test/escape_sequence_files/t0504-vim.in rename to fixtures/escape_sequence_files/t0504-vim.in diff --git a/src/test/escape_sequence_files/t0504-vim.text b/fixtures/escape_sequence_files/t0504-vim.text similarity index 100% rename from src/test/escape_sequence_files/t0504-vim.text rename to fixtures/escape_sequence_files/t0504-vim.text diff --git a/gulpfile.js b/gulpfile.js index 6e211023..8469c66c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -121,7 +121,12 @@ gulp.task('instrument-test', function () { }); gulp.task('mocha', ['instrument-test'], function () { - return gulp.src([`${outDir}/*test.js`, `${outDir}/**/*test.js`], {read: false}) + return gulp.src([ + `${outDir}/*test.js`, + `${outDir}/**/*test.js`, + `${outDir}/*integration.js`, + `${outDir}/**/*integration.js` + ], {read: false}) .pipe(mocha()) .once('error', () => process.exit(1)) .pipe(istanbul.writeReports()); diff --git a/src/test/escape-sequences-test.ts b/src/Terminal.integration.ts similarity index 94% rename from src/test/escape-sequences-test.ts rename to src/Terminal.integration.ts index 554298d0..2155d0e0 100644 --- a/src/test/escape-sequences-test.ts +++ b/src/Terminal.integration.ts @@ -1,15 +1,16 @@ /** * @license MIT * - * This file contains integration tests for xterm.js + * This file contains integration tests for xterm.js. */ import * as glob from 'glob'; import * as fs from 'fs'; import * as os from 'os'; +import * as path from 'path'; import * as pty from 'node-pty'; -import { Terminal } from '../Terminal'; -import { CHAR_DATA_CHAR_INDEX } from '../Buffer'; +import { Terminal } from './Terminal'; +import { CHAR_DATA_CHAR_INDEX } from './Buffer'; let primitive_pty: any; @@ -95,7 +96,7 @@ if (os.platform() !== 'win32') { // omit stack trace for escape sequence files Error.stackTraceLimit = 0; - let files = glob.sync('**/escape_sequence_files/*.in'); + let files = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')}); // only successful tests for now let skip = [ 10, 16, 17, 19, 32, 33, 34, 35, 36, 39, From b42a9039cccabfa84797a915817523dda7f567e2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Aug 2017 22:40:15 -0700 Subject: [PATCH 12/17] Rename TestUtils.ts to TestUtils.test.ts This will allow it to be excluded when we do #863 --- src/Buffer.test.ts | 2 +- src/BufferSet.test.ts | 2 +- src/InputHandler.test.ts | 2 +- src/SelectionManager.test.ts | 2 +- src/SelectionModel.test.ts | 2 +- src/Terminal.test.ts | 2 +- src/utils/{TestUtils.ts => TestUtils.test.ts} | 0 7 files changed, 6 insertions(+), 6 deletions(-) rename src/utils/{TestUtils.ts => TestUtils.test.ts} (100%) diff --git a/src/Buffer.test.ts b/src/Buffer.test.ts index 99bd6d0f..5ac71996 100644 --- a/src/Buffer.test.ts +++ b/src/Buffer.test.ts @@ -6,7 +6,7 @@ import { assert } from 'chai'; import { ITerminal } from './Interfaces'; import { Buffer } from './Buffer'; import { CircularList } from './utils/CircularList'; -import { MockTerminal } from './utils/TestUtils'; +import { MockTerminal } from './utils/TestUtils.test'; const INIT_COLS = 80; const INIT_ROWS = 24; diff --git a/src/BufferSet.test.ts b/src/BufferSet.test.ts index 97c07995..320241ca 100644 --- a/src/BufferSet.test.ts +++ b/src/BufferSet.test.ts @@ -6,7 +6,7 @@ import { assert } from 'chai'; import { ITerminal } from './Interfaces'; import { BufferSet } from './BufferSet'; import { Buffer } from './Buffer'; -import { MockTerminal } from './utils/TestUtils'; +import { MockTerminal } from './utils/TestUtils.test'; describe('BufferSet', () => { let terminal: ITerminal; diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 043b7919..f6e07f7e 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -5,7 +5,7 @@ import { assert } from 'chai'; import { InputHandler } from './InputHandler'; import { wcwidth } from './InputHandler'; -import { MockInputHandlingTerminal } from './utils/TestUtils'; +import { MockInputHandlingTerminal } from './utils/TestUtils.test'; describe('InputHandler', () => { describe('save and restore cursor', () => { diff --git a/src/SelectionManager.test.ts b/src/SelectionManager.test.ts index c7fd9aef..4774b3bb 100644 --- a/src/SelectionManager.test.ts +++ b/src/SelectionManager.test.ts @@ -10,7 +10,7 @@ import { CircularList } from './utils/CircularList'; import { SelectionManager } from './SelectionManager'; import { SelectionModel } from './SelectionModel'; import { BufferSet } from './BufferSet'; -import { MockTerminal } from './utils/TestUtils'; +import { MockTerminal } from './utils/TestUtils.test'; import { LineData } from './Types'; class TestSelectionManager extends SelectionManager { diff --git a/src/SelectionModel.test.ts b/src/SelectionModel.test.ts index 9da84478..6c750ed2 100644 --- a/src/SelectionModel.test.ts +++ b/src/SelectionModel.test.ts @@ -6,7 +6,7 @@ import { assert } from 'chai'; import { ITerminal } from './Interfaces'; import { SelectionModel } from './SelectionModel'; import {BufferSet} from './BufferSet'; -import { MockTerminal } from './utils/TestUtils'; +import { MockTerminal } from './utils/TestUtils.test'; class TestSelectionModel extends SelectionModel { constructor( diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 753733c2..f045a37e 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -1,6 +1,6 @@ import { assert, expect } from 'chai'; import { Terminal } from './Terminal'; -import { MockViewport, MockCompositionHelper } from './utils/TestUtils'; +import { MockViewport, MockCompositionHelper } from './utils/TestUtils.test'; import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from './Buffer'; class TestTerminal extends Terminal { diff --git a/src/utils/TestUtils.ts b/src/utils/TestUtils.test.ts similarity index 100% rename from src/utils/TestUtils.ts rename to src/utils/TestUtils.test.ts From a0ce9a79a9daf8bca5038d68be46ec8f8bdb35b9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Aug 2017 23:14:14 -0700 Subject: [PATCH 13/17] Build and export TS declaration files Fixes #866 --- gulpfile.js | 5 ++++- package.json | 5 ++++- src/Parser.ts | 2 +- tsconfig.json | 4 ++-- typings.json | 6 ------ 5 files changed, 11 insertions(+), 11 deletions(-) delete mode 100644 typings.json diff --git a/gulpfile.js b/gulpfile.js index 6e211023..72c74651 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -39,7 +39,10 @@ gulp.task('tsc', function () { // Build all TypeScript files (including tests) to ${outDir}/, based on the configuration defined in // `tsconfig.json`. let tsResult = tsProject.src().pipe(sourcemaps.init()).pipe(tsProject()); - let tsc = tsResult.js.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''})).pipe(gulp.dest(outDir)); + let tsc = merge( + tsResult.js.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''})).pipe(gulp.dest(outDir)), + tsResult.dts.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''})).pipe(gulp.dest(outDir)) + ); fs.emptyDirSync(`${outDir}/addons/search`); let tsResultSearchAddon = tsProjectSearchAddon.src().pipe(sourcemaps.init()).pipe(tsProjectSearchAddon()); diff --git a/package.json b/package.json index d0ba5c01..a5f0bd75 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "test", ".gitignore" ], - "main": "lib/xterm.js", + "main": "lib/Terminal.js", + "types": "lib/Terminal.d.ts", "repository": "https://github.com/sourcelair/xterm.js", "license": "MIT", "files": [ @@ -21,8 +22,10 @@ "dist/**/*.js.map", "lib/*.css", "lib/**/*.css", + "lib/*.d.ts", "lib/*.js", "lib/*.js.map", + "lib/**/*.d.ts", "lib/**/*.js", "lib/**/*.js.map", "src/*.css", diff --git a/src/Parser.ts b/src/Parser.ts index 90d413a7..3f05992b 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -148,7 +148,7 @@ csiStateHandler['s'] = (handler, params) => handler.saveCursor(params); csiStateHandler['u'] = (handler, params) => handler.restoreCursor(params); csiStateHandler[C0.CAN] = (handler, params, prefix, postfix, parser) => parser.setState(ParserState.NORMAL); -enum ParserState { +export enum ParserState { NORMAL = 0, ESCAPED = 1, CSI_PARAM = 2, diff --git a/tsconfig.json b/tsconfig.json index 2e9703dd..a3aa790c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,10 +3,10 @@ "module": "commonjs", "target": "es5", "rootDir": "src", - "allowJs": true, "outDir": "lib", "sourceMap": true, - "removeComments": true + "removeComments": true, + "declaration": true }, "include": [ "src/**/*" diff --git a/typings.json b/typings.json deleted file mode 100644 index 8210c007..00000000 --- a/typings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "xterm", - "globalDependencies": { - "node": "registry:env/node#6.0.0+20160918225031" - } -} From 3b787824d37280a8e8e03c3df3b8be513173880b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 8 Aug 2017 00:08:04 -0700 Subject: [PATCH 14/17] Fix import for CommonJS consumers CommonJS now imports the Terminal object directly as the trick in xterm.ts wasn't working properly. Fixes #865 --- README.md | 12 ++++++++++++ src/Terminal.ts | 9 +++++++++ src/addons/attach/attach.js | 12 ++++++------ src/addons/fit/fit.js | 8 ++++---- src/addons/fullscreen/fullscreen.js | 8 ++++---- src/addons/search/search.ts | 2 +- src/addons/terminado/terminado.js | 10 +++++----- src/handlers/Clipboard.test.ts | 2 +- src/xterm.ts | 13 ++----------- 9 files changed, 44 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 5a5f8e51..e1217bcd 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,18 @@ var xterm = new Terminal(); xterm.fit(); ``` +## CommonJS + +Importing xterm.js in a CommonJS environment (eg. [Electron](https://electron.atom.io/)) can be done like so: + +```ts +// JavaScript +var Terminal = require('xterm').Terminal; + +// TypeScript +import { Terminal } from 'xterm'; +``` + ## Releases Xterm.js follows a monthly release cycle roughly. diff --git a/src/Terminal.ts b/src/Terminal.ts index 31eaef8a..d8e88b2d 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -8,6 +8,15 @@ * has been extended to include xterm CSI codes, among * other features. * @license MIT + * + * Terminal Emulation References: + * http://vt100.net/ + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * http://invisible-island.net/vttest/ + * http://www.inwap.com/pdp10/ansicode.txt + * http://linux.die.net/man/4/console_codes + * http://linux.die.net/man/7/urxvt */ import { BufferSet } from './BufferSet'; diff --git a/src/addons/attach/attach.js b/src/addons/attach/attach.js index c2a7989b..34dc4f68 100644 --- a/src/addons/attach/attach.js +++ b/src/addons/attach/attach.js @@ -9,7 +9,7 @@ /* * CommonJS environment */ - module.exports = attach(require('../../xterm')); + module.exports = attach(require('../../Terminal').Terminal); } else if (typeof define == 'function') { /* * Require.js is available @@ -21,7 +21,7 @@ */ attach(window.Terminal); } -})(function (Xterm) { +})(function (Terminal) { 'use strict'; var exports = {}; @@ -29,7 +29,7 @@ /** * Attaches the given terminal to the given socket. * - * @param {Xterm} term - The terminal to be attached to the given socket. + * @param {Terminal} term - The terminal to be attached to the given socket. * @param {WebSocket} socket - The socket to attach the current terminal. * @param {boolean} bidirectional - Whether the terminal should send data * to the socket as well. @@ -82,7 +82,7 @@ /** * Detaches the given terminal from the given socket * - * @param {Xterm} term - The terminal to be detached from the given socket. + * @param {Terminal} term - The terminal to be detached from the given socket. * @param {WebSocket} socket - The socket from which to detach the current * terminal. */ @@ -108,7 +108,7 @@ * should happen instantly or at a maximum * frequency of 1 rendering per 10ms. */ - Xterm.prototype.attach = function (socket, bidirectional, buffered) { + Terminal.prototype.attach = function (socket, bidirectional, buffered) { return exports.attach(this, socket, bidirectional, buffered); }; @@ -118,7 +118,7 @@ * @param {WebSocket} socket - The socket from which to detach the current * terminal. */ - Xterm.prototype.detach = function (socket) { + Terminal.prototype.detach = function (socket) { return exports.detach(this, socket); }; diff --git a/src/addons/fit/fit.js b/src/addons/fit/fit.js index 46b79e9b..da66802d 100644 --- a/src/addons/fit/fit.js +++ b/src/addons/fit/fit.js @@ -16,7 +16,7 @@ /* * CommonJS environment */ - module.exports = fit(require('../../xterm')); + module.exports = fit(require('../../Terminal').Terminal); } else if (typeof define == 'function') { /* * Require.js is available @@ -28,7 +28,7 @@ */ fit(window.Terminal); } -})(function (Xterm) { +})(function (Terminal) { var exports = {}; exports.proposeGeometry = function (term) { @@ -74,11 +74,11 @@ } }; - Xterm.prototype.proposeGeometry = function () { + Terminal.prototype.proposeGeometry = function () { return exports.proposeGeometry(this); }; - Xterm.prototype.fit = function () { + Terminal.prototype.fit = function () { return exports.fit(this); }; diff --git a/src/addons/fullscreen/fullscreen.js b/src/addons/fullscreen/fullscreen.js index e8e34ea3..344669f6 100644 --- a/src/addons/fullscreen/fullscreen.js +++ b/src/addons/fullscreen/fullscreen.js @@ -8,7 +8,7 @@ /* * CommonJS environment */ - module.exports = fullscreen(require('../../xterm')); + module.exports = fullscreen(require('../../Terminal').Terminal); } else if (typeof define == 'function') { /* * Require.js is available @@ -20,12 +20,12 @@ */ fullscreen(window.Terminal); } -})(function (Xterm) { +})(function (Terminal) { var exports = {}; /** * Toggle the given terminal's fullscreen mode. - * @param {Xterm} term - The terminal to toggle full screen mode + * @param {Terminal} term - The terminal to toggle full screen mode * @param {boolean} fullscreen - Toggle fullscreen on (true) or off (false) */ exports.toggleFullScreen = function (term, fullscreen) { @@ -42,7 +42,7 @@ term.element.classList[fn]('fullscreen'); }; - Xterm.prototype.toggleFullscreen = function (fullscreen) { + Terminal.prototype.toggleFullscreen = function (fullscreen) { exports.toggleFullScreen(this, fullscreen); }; diff --git a/src/addons/search/search.ts b/src/addons/search/search.ts index 0b506b69..879ca4ee 100644 --- a/src/addons/search/search.ts +++ b/src/addons/search/search.ts @@ -20,7 +20,7 @@ declare var window: any; /** * CommonJS environment */ - module.exports = addon(require('../../xterm')); + module.exports = addon(require('../../Terminal').Terminal); } else if (typeof define === 'function') { /** * Require.js is available diff --git a/src/addons/terminado/terminado.js b/src/addons/terminado/terminado.js index 86e6ea2e..d1414c86 100644 --- a/src/addons/terminado/terminado.js +++ b/src/addons/terminado/terminado.js @@ -10,7 +10,7 @@ /* * CommonJS environment */ - module.exports = attach(require('../../xterm')); + module.exports = attach(require('../../Terminal').Terminal); } else if (typeof define == 'function') { /* * Require.js is available @@ -22,7 +22,7 @@ */ attach(window.Terminal); } -})(function (Xterm) { +})(function (Terminal) { 'use strict'; var exports = {}; @@ -30,7 +30,7 @@ /** * Attaches the given terminal to the given socket. * - * @param {Xterm} term - The terminal to be attached to the given socket. + * @param {Terminal} term - The terminal to be attached to the given socket. * @param {WebSocket} socket - The socket to attach the current terminal. * @param {boolean} bidirectional - Whether the terminal should send data * to the socket as well. @@ -117,7 +117,7 @@ * should happen instantly or at a maximum * frequency of 1 rendering per 10ms. */ - Xterm.prototype.terminadoAttach = function (socket, bidirectional, buffered) { + Terminal.prototype.terminadoAttach = function (socket, bidirectional, buffered) { return exports.terminadoAttach(this, socket, bidirectional, buffered); }; @@ -127,7 +127,7 @@ * @param {WebSocket} socket - The socket from which to detach the current * terminal. */ - Xterm.prototype.terminadoDetach = function (socket) { + Terminal.prototype.terminadoDetach = function (socket) { return exports.terminadoDetach(this, socket); }; diff --git a/src/handlers/Clipboard.test.ts b/src/handlers/Clipboard.test.ts index e5bc3581..bd2cea03 100644 --- a/src/handlers/Clipboard.test.ts +++ b/src/handlers/Clipboard.test.ts @@ -1,5 +1,5 @@ import { assert } from 'chai'; -import * as Terminal from '../xterm'; +import * as Terminal from '../Terminal'; import * as Clipboard from './Clipboard'; describe('evaluatePastedTextProcessing', () => { diff --git a/src/xterm.ts b/src/xterm.ts index 49e44151..50ac5592 100644 --- a/src/xterm.ts +++ b/src/xterm.ts @@ -1,18 +1,9 @@ /** * @license MIT + * + * This file is the entry point for browserify. */ import { Terminal } from './Terminal'; -/** - * Terminal Emulation References: - * http://vt100.net/ - * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt - * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html - * http://invisible-island.net/vttest/ - * http://www.inwap.com/pdp10/ansicode.txt - * http://linux.die.net/man/4/console_codes - * http://linux.die.net/man/7/urxvt - */ - module.exports = Terminal; From f21d5f25ecaa6c1c843ee6b2761c7958e1eb16ed Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 8 Aug 2017 00:29:22 -0700 Subject: [PATCH 15/17] Fix search addon after TS refactor Fixes #867 --- src/addons/search/SearchHelper.ts | 5 ++--- src/addons/search/search.ts | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/addons/search/SearchHelper.ts b/src/addons/search/SearchHelper.ts index 43d6dc18..26e4e8a7 100644 --- a/src/addons/search/SearchHelper.ts +++ b/src/addons/search/SearchHelper.ts @@ -15,7 +15,7 @@ interface ISearchResult { * A class that knows how to search the terminal and how to display the results. */ export class SearchHelper { - constructor(private _terminal: any, private _translateBufferLineToString: any) { + constructor(private _terminal: any) { // TODO: Search for multiple instances on 1 line // TODO: Don't use the actual selection, instead use a "find selection" so multiple instances can be highlighted // TODO: Highlight other instances in the viewport @@ -111,8 +111,7 @@ export class SearchHelper { * @return The search result if it was found. */ private _findInLine(term: string, y: number): ISearchResult { - const bufferLine = this._terminal.buffer.lines.get(y); - const lowerStringLine = this._translateBufferLineToString(bufferLine, true).toLowerCase(); + const lowerStringLine = this._terminal.buffer.translateBufferLineToString(y, true).toLowerCase(); const lowerTerm = term.toLowerCase(); const searchIndex = lowerStringLine.indexOf(lowerTerm); if (searchIndex >= 0) { diff --git a/src/addons/search/search.ts b/src/addons/search/search.ts index 879ca4ee..34223c4e 100644 --- a/src/addons/search/search.ts +++ b/src/addons/search/search.ts @@ -36,7 +36,7 @@ declare var window: any; */ Terminal.prototype.findNext = function(term: string): boolean { if (!this._searchHelper) { - this.searchHelper = new SearchHelper(this, Terminal.translateBufferLineToString); + this.searchHelper = new SearchHelper(this); } return (this.searchHelper).findNext(term); }; @@ -49,7 +49,7 @@ declare var window: any; */ Terminal.prototype.findPrevious = function(term: string): boolean { if (!this._searchHelper) { - this.searchHelper = new SearchHelper(this, Terminal.translateBufferLineToString); + this.searchHelper = new SearchHelper(this); } return (this.searchHelper).findPrevious(term); }; From 9659f29e5015df417be211806a63c23e649bacb1 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 8 Aug 2017 00:59:14 -0700 Subject: [PATCH 16/17] Remove Terminal.open focus param --- src/Terminal.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 493e48bc..58fde2f9 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -649,9 +649,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT * Opens the terminal within an element. * * @param {HTMLElement} parent The element to create the terminal within. - * @param {boolean} focus Focus the terminal, after it gets instantiated in the DOM */ - private open(parent: HTMLElement, focus?: boolean): void { + public open(parent: HTMLElement): void { let i = 0; let div; @@ -759,10 +758,6 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT // Initialize global actions that need to be taken on the document. this.initGlobal(); - if (focus) { - this.focus(); - } - // Listen for mouse events and translate // them into terminal mouse protocols. this.bindMouse(); From 0902b92f79c712efb3bb6151373d85882046bb5c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 8 Aug 2017 01:05:24 -0700 Subject: [PATCH 17/17] Revert "Disable selection override on Windows/Linux" This reverts commit 3479614c732305b4174bf438e1ac3022aa3b3aa4. --- src/SelectionManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index ee03f79a..f3aa5f82 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -327,7 +327,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager // Allow selection when using a specific modifier key, even when disabled if (!this._enabled) { - const shouldForceSelection = Browser.isMac && event.altKey; + const shouldForceSelection = Browser.isMac ? event.altKey : event.shiftKey; if (!shouldForceSelection) { return;