From 9890a8b20ff245a4dd3b639801e28c721c43bede Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 26 May 2018 07:32:46 -0700 Subject: [PATCH 01/21] Fix valid lib values in tsconfig Mixed case is not valid --- tsconfig.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index e56930e6..bcba358d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,10 +3,10 @@ "module": "commonjs", "target": "es5", "lib": [ - "DOM", - "ES5", - "ScriptHost", - "ES2015.Promise" + "dom", + "es5", + "scripthost", + "es2015.promise" ], "rootDir": "src", "outDir": "lib", From e9af4d0e80d87cf7e55ffb525e511c3a112b732f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 7 Jun 2018 09:22:14 +0200 Subject: [PATCH 02/21] Fix copyright line breaks --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ae003783..30717086 100644 --- a/README.md +++ b/README.md @@ -256,6 +256,6 @@ To contribute either code, documentation or issues to xterm.js please read the [ If you contribute code to this project, you are implicitly allowing your code to be distributed under the MIT license. You are also implicitly verifying that all code is your original work. -Copyright (c) 2017-2018, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License) -Copyright (c) 2014-2017, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License) +Copyright (c) 2017-2018, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License)
+Copyright (c) 2014-2017, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) From 3637b83bbbd2057617e359fcb9496cf9278af184 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 8 Jun 2018 09:04:37 +0200 Subject: [PATCH 03/21] Add more granular github issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 17 +++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 7 +++++++ .github/ISSUE_TEMPLATE/question.md | 8 ++++++++ 3 files changed, 32 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/question.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..6a96665d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,17 @@ +--- +name: Bug report +about: Create a bug report +--- + + + + +## Details +- Browser and browser version: +- OS version: +- xterm.js version: + +### Steps to reproduce + +1. +2. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..979f35aa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,7 @@ +--- +name: Feature request +about: Suggest a feature or enhancement +--- + + + diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 00000000..10eee3d9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,8 @@ +--- +name: Question +about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/xtermjs or https://gitter.im/sourcelair/xterm.js. +--- + +🛑 The issue tracker is not for questions 🛑 + +If you have a question, please ask it on https://stackoverflow.com/questions/tagged/xtermjs or https://gitter.im/sourcelair/xterm.js. From 2f1c1cd1723f808083619cb769a5901f636a74da Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Jun 2018 17:01:37 +0200 Subject: [PATCH 04/21] Remove old issue template --- .github/ISSUE_TEMPLATE.md | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 35935355..00000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,11 +0,0 @@ - - -## Details -- Browser and browser version: -- OS version: -- xterm.js version: - -### Steps to reproduce - -1. -2. From b6481f120bc31bb35edf5dbc68ccd9775fcb9037 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Jun 2018 11:03:37 +0200 Subject: [PATCH 05/21] Introduce IKeyEvent --- src/Terminal.ts | 39 +++++++++++++++++++++++++++------------ src/base/Types.ts | 14 ++++++++++++++ 2 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 src/base/Types.ts diff --git a/src/Terminal.ts b/src/Terminal.ts index 8a71df96..db4ceee3 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -50,6 +50,7 @@ import { ScreenDprMonitor } from './utils/ScreenDprMonitor'; import { ITheme, ILocalizableStrings, IMarker, IDisposable } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; +import { IKeyEvent } from './base/Types'; // reg + shift key mappings for digits and special chars const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { @@ -1430,25 +1431,39 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II } } + private _convertDomKeyEvent(event: KeyboardEvent): IKeyEvent { + return { + altKey: event.altKey, + ctrlKey: event.ctrlKey, + shiftKey: event.shiftKey, + metaKey: event.metaKey, + keyCode: event.keyCode, + key: event.key, + type: event.type + }; + } + /** * Handle a keydown event * Key Resources: * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent * @param {KeyboardEvent} ev The keydown event to be handled. */ - protected _keyDown(ev: KeyboardEvent): boolean { - if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) { + protected _keyDown(domEvent: KeyboardEvent): boolean { + if (this._customKeyEventHandler && this._customKeyEventHandler(domEvent) === false) { return false; } - if (!this._compositionHelper.keydown(ev)) { + if (!this._compositionHelper.keydown(domEvent)) { if (this.buffer.ybase !== this.buffer.ydisp) { this.scrollToBottom(); } return false; } - const result = this._evaluateKeyEscapeSequence(ev); + const event = this._convertDomKeyEvent(domEvent); + + const result = this._evaluateKeyEscapeSequence(event); // if (result.key === C0.DC3) { // XOFF // this._writeStopped = true; @@ -1458,31 +1473,31 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II if (result.scrollLines) { this.scrollLines(result.scrollLines); - return this.cancel(ev, true); + return this.cancel(domEvent, true); } - if (this._isThirdLevelShift(this.browser, ev)) { + if (this._isThirdLevelShift(this.browser, event)) { return true; } if (result.cancel) { // The event is canceled at the end already, is this necessary? - this.cancel(ev, true); + this.cancel(domEvent, true); } if (!result.key) { return true; } - this.emit('keydown', ev); - this.emit('key', result.key, ev); + this.emit('keydown', domEvent); + this.emit('key', result.key, domEvent); this.showCursor(); this.handler(result.key); - return this.cancel(ev, true); + return this.cancel(domEvent, true); } - private _isThirdLevelShift(browser: IBrowser, ev: KeyboardEvent): boolean { + private _isThirdLevelShift(browser: IBrowser, ev: IKeyEvent): boolean { const thirdLevelKey = (browser.isMac && !this.options.macOptionIsMeta && ev.altKey && !ev.ctrlKey && !ev.metaKey) || (browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey); @@ -1502,7 +1517,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * @param ev The keyboard event to be translated to key escape sequence. */ - protected _evaluateKeyEscapeSequence(ev: KeyboardEvent): {cancel: boolean, key: string, scrollLines: number} { + protected _evaluateKeyEscapeSequence(ev: IKeyEvent): {cancel: boolean, key: string, scrollLines: number} { const result: {cancel: boolean, key: string, scrollLines: number} = { // Whether to cancel event propogation (NOTE: this may not be needed since the event is // canceled at the end of keyDown diff --git a/src/base/Types.ts b/src/base/Types.ts new file mode 100644 index 00000000..162aa4cd --- /dev/null +++ b/src/base/Types.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +export interface IKeyEvent { + altKey: boolean; + ctrlKey: boolean; + shiftKey: boolean; + metaKey: boolean; + keyCode: number; + key: string; + type: string; +} From e167686b6da8422194d0ac0d36b86e2fb4e9a649 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Jun 2018 11:45:50 +0200 Subject: [PATCH 06/21] Remove conversion step, KeyboardEvent implements IKeyboardEvent --- src/Terminal.ts | 36 +++++++++++------------------------- src/base/Types.ts | 6 +++++- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index db4ceee3..9f396af0 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -50,7 +50,7 @@ import { ScreenDprMonitor } from './utils/ScreenDprMonitor'; import { ITheme, ILocalizableStrings, IMarker, IDisposable } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; -import { IKeyEvent } from './base/Types'; +import { IKeyboardEvent } from './base/Types'; // reg + shift key mappings for digits and special chars const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { @@ -1431,38 +1431,24 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II } } - private _convertDomKeyEvent(event: KeyboardEvent): IKeyEvent { - return { - altKey: event.altKey, - ctrlKey: event.ctrlKey, - shiftKey: event.shiftKey, - metaKey: event.metaKey, - keyCode: event.keyCode, - key: event.key, - type: event.type - }; - } - /** * Handle a keydown event * Key Resources: * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent * @param {KeyboardEvent} ev The keydown event to be handled. */ - protected _keyDown(domEvent: KeyboardEvent): boolean { - if (this._customKeyEventHandler && this._customKeyEventHandler(domEvent) === false) { + protected _keyDown(event: KeyboardEvent): boolean { + if (this._customKeyEventHandler && this._customKeyEventHandler(event) === false) { return false; } - if (!this._compositionHelper.keydown(domEvent)) { + if (!this._compositionHelper.keydown(event)) { if (this.buffer.ybase !== this.buffer.ydisp) { this.scrollToBottom(); } return false; } - const event = this._convertDomKeyEvent(domEvent); - const result = this._evaluateKeyEscapeSequence(event); // if (result.key === C0.DC3) { // XOFF @@ -1473,7 +1459,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II if (result.scrollLines) { this.scrollLines(result.scrollLines); - return this.cancel(domEvent, true); + return this.cancel(event, true); } if (this._isThirdLevelShift(this.browser, event)) { @@ -1482,22 +1468,22 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II if (result.cancel) { // The event is canceled at the end already, is this necessary? - this.cancel(domEvent, true); + this.cancel(event, true); } if (!result.key) { return true; } - this.emit('keydown', domEvent); - this.emit('key', result.key, domEvent); + this.emit('keydown', event); + this.emit('key', result.key, event); this.showCursor(); this.handler(result.key); - return this.cancel(domEvent, true); + return this.cancel(event, true); } - private _isThirdLevelShift(browser: IBrowser, ev: IKeyEvent): boolean { + private _isThirdLevelShift(browser: IBrowser, ev: IKeyboardEvent): boolean { const thirdLevelKey = (browser.isMac && !this.options.macOptionIsMeta && ev.altKey && !ev.ctrlKey && !ev.metaKey) || (browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey); @@ -1517,7 +1503,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * @param ev The keyboard event to be translated to key escape sequence. */ - protected _evaluateKeyEscapeSequence(ev: IKeyEvent): {cancel: boolean, key: string, scrollLines: number} { + protected _evaluateKeyEscapeSequence(ev: IKeyboardEvent): {cancel: boolean, key: string, scrollLines: number} { const result: {cancel: boolean, key: string, scrollLines: number} = { // Whether to cancel event propogation (NOTE: this may not be needed since the event is // canceled at the end of keyDown diff --git a/src/base/Types.ts b/src/base/Types.ts index 162aa4cd..98cb296d 100644 --- a/src/base/Types.ts +++ b/src/base/Types.ts @@ -3,7 +3,11 @@ * @license MIT */ -export interface IKeyEvent { +/** + * A keyboard event interface which does not depend on the DOM, KeyboardEvent implicitly extends + * this event. + */ +export interface IKeyboardEvent { altKey: boolean; ctrlKey: boolean; shiftKey: boolean; From b4ccbe042bdd2f8046fe346f99e114b8a533ef2b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Jun 2018 11:52:25 +0200 Subject: [PATCH 07/21] Pull keyboard event translation into core --- src/Terminal.ts | 354 +------------------------------------ src/core/Types.ts | 10 ++ src/core/input/Keyboard.ts | 353 ++++++++++++++++++++++++++++++++++++ 3 files changed, 365 insertions(+), 352 deletions(-) create mode 100644 src/core/Types.ts create mode 100644 src/core/input/Keyboard.ts diff --git a/src/Terminal.ts b/src/Terminal.ts index 9f396af0..1b7a0c1a 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -51,34 +51,7 @@ import { ITheme, ILocalizableStrings, IMarker, IDisposable } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; import { IKeyboardEvent } from './base/Types'; - -// reg + shift key mappings for digits and special chars -const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { - // digits 0-9 - 48: ['0', ')'], - 49: ['1', '!'], - 50: ['2', '@'], - 51: ['3', '#'], - 52: ['4', '$'], - 53: ['5', '%'], - 54: ['6', '^'], - 55: ['7', '&'], - 56: ['8', '*'], - 57: ['9', '('], - - // special chars - 186: [';', ':'], - 187: ['=', '+'], - 188: [',', '<'], - 189: ['-', '_'], - 190: ['.', '>'], - 191: ['/', '?'], - 192: ['`', '~'], - 219: ['[', '{'], - 220: ['\\', '|'], - 221: [']', '}'], - 222: ['\'', '"'] -}; +import { evaluateKeyboardEvent } from './core/input/Keyboard'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -1449,7 +1422,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II return false; } - const result = this._evaluateKeyEscapeSequence(event); + const result = evaluateKeyboardEvent(event); // if (result.key === C0.DC3) { // XOFF // this._writeStopped = true; @@ -1496,329 +1469,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47); } - /** - * Returns an object that determines how a KeyboardEvent should be handled. The key of the - * returned value is the new key code to pass to the PTY. - * - * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html - * @param ev The keyboard event to be translated to key escape sequence. - */ - protected _evaluateKeyEscapeSequence(ev: IKeyboardEvent): {cancel: boolean, key: string, scrollLines: number} { - const result: {cancel: boolean, key: string, scrollLines: number} = { - // Whether to cancel event propogation (NOTE: this may not be needed since the event is - // canceled at the end of keyDown - cancel: false, - // The new key even to emit - key: undefined, - // The number of characters to scroll, if this is defined it will cancel the event - scrollLines: undefined - }; - const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0); - switch (ev.keyCode) { - case 0: - if (ev.key === 'UIKeyInputUpArrow') { - if (this.applicationCursor) { - result.key = C0.ESC + 'OA'; - } else { - result.key = C0.ESC + '[A'; - } - } - else if (ev.key === 'UIKeyInputLeftArrow') { - if (this.applicationCursor) { - result.key = C0.ESC + 'OD'; - } else { - result.key = C0.ESC + '[D'; - } - } - else if (ev.key === 'UIKeyInputRightArrow') { - if (this.applicationCursor) { - result.key = C0.ESC + 'OC'; - } else { - result.key = C0.ESC + '[C'; - } - } - else if (ev.key === 'UIKeyInputDownArrow') { - if (this.applicationCursor) { - result.key = C0.ESC + 'OB'; - } else { - result.key = C0.ESC + '[B'; - } - } - break; - case 8: - // backspace - if (ev.shiftKey) { - result.key = C0.BS; // ^H - break; - } else if (ev.altKey) { - result.key = C0.ESC + C0.DEL; // \e ^? - break; - } - result.key = C0.DEL; // ^? - break; - case 9: - // tab - if (ev.shiftKey) { - result.key = C0.ESC + '[Z'; - break; - } - result.key = C0.HT; - result.cancel = true; - break; - case 13: - // return/enter - result.key = C0.CR; - result.cancel = true; - break; - case 27: - // escape - result.key = C0.ESC; - result.cancel = true; - break; - case 37: - // left-arrow - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D'; - // HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards - // http://unix.stackexchange.com/a/108106 - // macOS uses different escape sequences than linux - if (result.key === C0.ESC + '[1;3D') { - result.key = (this.browser.isMac) ? C0.ESC + 'b' : C0.ESC + '[1;5D'; - } - } else if (this.applicationCursor) { - result.key = C0.ESC + 'OD'; - } else { - result.key = C0.ESC + '[D'; - } - break; - case 39: - // right-arrow - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C'; - // HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward - // http://unix.stackexchange.com/a/108106 - // macOS uses different escape sequences than linux - if (result.key === C0.ESC + '[1;3C') { - result.key = (this.browser.isMac) ? C0.ESC + 'f' : C0.ESC + '[1;5C'; - } - } else if (this.applicationCursor) { - result.key = C0.ESC + 'OC'; - } else { - result.key = C0.ESC + '[C'; - } - break; - case 38: - // up-arrow - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A'; - // HACK: Make Alt + up-arrow behave like Ctrl + up-arrow - // http://unix.stackexchange.com/a/108106 - if (result.key === C0.ESC + '[1;3A') { - result.key = C0.ESC + '[1;5A'; - } - } else if (this.applicationCursor) { - result.key = C0.ESC + 'OA'; - } else { - result.key = C0.ESC + '[A'; - } - break; - case 40: - // down-arrow - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B'; - // HACK: Make Alt + down-arrow behave like Ctrl + down-arrow - // http://unix.stackexchange.com/a/108106 - if (result.key === C0.ESC + '[1;3B') { - result.key = C0.ESC + '[1;5B'; - } - } else if (this.applicationCursor) { - result.key = C0.ESC + 'OB'; - } else { - result.key = C0.ESC + '[B'; - } - break; - case 45: - // insert - if (!ev.shiftKey && !ev.ctrlKey) { - // or + are used to - // copy-paste on some systems. - result.key = C0.ESC + '[2~'; - } - break; - case 46: - // delete - if (modifiers) { - result.key = C0.ESC + '[3;' + (modifiers + 1) + '~'; - } else { - result.key = C0.ESC + '[3~'; - } - break; - case 36: - // home - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'H'; - } else if (this.applicationCursor) { - result.key = C0.ESC + 'OH'; - } else { - result.key = C0.ESC + '[H'; - } - break; - case 35: - // end - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'F'; - } else if (this.applicationCursor) { - result.key = C0.ESC + 'OF'; - } else { - result.key = C0.ESC + '[F'; - } - break; - case 33: - // page up - if (ev.shiftKey) { - result.scrollLines = -(this.rows - 1); - } else { - result.key = C0.ESC + '[5~'; - } - break; - case 34: - // page down - if (ev.shiftKey) { - result.scrollLines = this.rows - 1; - } else { - result.key = C0.ESC + '[6~'; - } - break; - case 112: - // F1-F12 - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'P'; - } else { - result.key = C0.ESC + 'OP'; - } - break; - case 113: - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'Q'; - } else { - result.key = C0.ESC + 'OQ'; - } - break; - case 114: - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'R'; - } else { - result.key = C0.ESC + 'OR'; - } - break; - case 115: - if (modifiers) { - result.key = C0.ESC + '[1;' + (modifiers + 1) + 'S'; - } else { - result.key = C0.ESC + 'OS'; - } - break; - case 116: - if (modifiers) { - result.key = C0.ESC + '[15;' + (modifiers + 1) + '~'; - } else { - result.key = C0.ESC + '[15~'; - } - break; - case 117: - if (modifiers) { - result.key = C0.ESC + '[17;' + (modifiers + 1) + '~'; - } else { - result.key = C0.ESC + '[17~'; - } - break; - case 118: - if (modifiers) { - result.key = C0.ESC + '[18;' + (modifiers + 1) + '~'; - } else { - result.key = C0.ESC + '[18~'; - } - break; - case 119: - if (modifiers) { - result.key = C0.ESC + '[19;' + (modifiers + 1) + '~'; - } else { - result.key = C0.ESC + '[19~'; - } - break; - case 120: - if (modifiers) { - result.key = C0.ESC + '[20;' + (modifiers + 1) + '~'; - } else { - result.key = C0.ESC + '[20~'; - } - break; - case 121: - if (modifiers) { - result.key = C0.ESC + '[21;' + (modifiers + 1) + '~'; - } else { - result.key = C0.ESC + '[21~'; - } - break; - case 122: - if (modifiers) { - result.key = C0.ESC + '[23;' + (modifiers + 1) + '~'; - } else { - result.key = C0.ESC + '[23~'; - } - break; - case 123: - if (modifiers) { - result.key = C0.ESC + '[24;' + (modifiers + 1) + '~'; - } else { - result.key = C0.ESC + '[24~'; - } - break; - default: - // a-z and space - if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { - if (ev.keyCode >= 65 && ev.keyCode <= 90) { - result.key = String.fromCharCode(ev.keyCode - 64); - } else if (ev.keyCode === 32) { - // NUL - result.key = String.fromCharCode(0); - } else if (ev.keyCode >= 51 && ev.keyCode <= 55) { - // escape, file sep, group sep, record sep, unit sep - result.key = String.fromCharCode(ev.keyCode - 51 + 27); - } else if (ev.keyCode === 56) { - // delete - result.key = String.fromCharCode(127); - } else if (ev.keyCode === 219) { - // ^[ - Control Sequence Introducer (CSI) - result.key = String.fromCharCode(27); - } else if (ev.keyCode === 220) { - // ^\ - String Terminator (ST) - result.key = String.fromCharCode(28); - } else if (ev.keyCode === 221) { - // ^] - Operating System Command (OSC) - result.key = String.fromCharCode(29); - } - } else if ((!this.browser.isMac || this.options.macOptionIsMeta) && ev.altKey && !ev.metaKey) { - // On macOS this is a third level shift when !macOptionIsMeta. Use instead. - const keyMapping = KEYCODE_KEY_MAPPINGS[ev.keyCode]; - const key = keyMapping && keyMapping[!ev.shiftKey ? 0 : 1]; - if (key) { - result.key = C0.ESC + key; - } else if (ev.keyCode >= 65 && ev.keyCode <= 90) { - const keyCode = ev.ctrlKey ? ev.keyCode - 64 : ev.keyCode + 32; - result.key = C0.ESC + String.fromCharCode(keyCode); - } - } else if (this.browser.isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) { - if (ev.keyCode === 65) { // cmd + a - this.selectAll(); - } - } - break; - } - - return result; - } - /** * Set the G level of the terminal * @param g diff --git a/src/core/Types.ts b/src/core/Types.ts new file mode 100644 index 00000000..374b8f7d --- /dev/null +++ b/src/core/Types.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +export interface IKeyboardResult { + cancel: boolean; + key: string; + scrollLines: number; +} diff --git a/src/core/input/Keyboard.ts b/src/core/input/Keyboard.ts new file mode 100644 index 00000000..caaa9189 --- /dev/null +++ b/src/core/input/Keyboard.ts @@ -0,0 +1,353 @@ +/** + * Copyright (c) 2014 The xterm.js authors. All rights reserved. + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * @license MIT + */ + +import { IKeyboardEvent } from '../../base/Types'; +import { IKeyboardResult } from '../Types'; +import { C0 } from '../../EscapeSequences'; + +// reg + shift key mappings for digits and special chars +const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { + // digits 0-9 + 48: ['0', ')'], + 49: ['1', '!'], + 50: ['2', '@'], + 51: ['3', '#'], + 52: ['4', '$'], + 53: ['5', '%'], + 54: ['6', '^'], + 55: ['7', '&'], + 56: ['8', '*'], + 57: ['9', '('], + + // special chars + 186: [';', ':'], + 187: ['=', '+'], + 188: [',', '<'], + 189: ['-', '_'], + 190: ['.', '>'], + 191: ['/', '?'], + 192: ['`', '~'], + 219: ['[', '{'], + 220: ['\\', '|'], + 221: [']', '}'], + 222: ['\'', '"'] +}; + +export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { + const result: IKeyboardResult = { + // Whether to cancel event propogation (NOTE: this may not be needed since the event is + // canceled at the end of keyDown + cancel: false, + // The new key even to emit + key: undefined, + // The number of characters to scroll, if this is defined it will cancel the event + scrollLines: undefined + }; + const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0); + switch (ev.keyCode) { + case 0: + if (ev.key === 'UIKeyInputUpArrow') { + if (this.applicationCursor) { + result.key = C0.ESC + 'OA'; + } else { + result.key = C0.ESC + '[A'; + } + } + else if (ev.key === 'UIKeyInputLeftArrow') { + if (this.applicationCursor) { + result.key = C0.ESC + 'OD'; + } else { + result.key = C0.ESC + '[D'; + } + } + else if (ev.key === 'UIKeyInputRightArrow') { + if (this.applicationCursor) { + result.key = C0.ESC + 'OC'; + } else { + result.key = C0.ESC + '[C'; + } + } + else if (ev.key === 'UIKeyInputDownArrow') { + if (this.applicationCursor) { + result.key = C0.ESC + 'OB'; + } else { + result.key = C0.ESC + '[B'; + } + } + break; + case 8: + // backspace + if (ev.shiftKey) { + result.key = C0.BS; // ^H + break; + } else if (ev.altKey) { + result.key = C0.ESC + C0.DEL; // \e ^? + break; + } + result.key = C0.DEL; // ^? + break; + case 9: + // tab + if (ev.shiftKey) { + result.key = C0.ESC + '[Z'; + break; + } + result.key = C0.HT; + result.cancel = true; + break; + case 13: + // return/enter + result.key = C0.CR; + result.cancel = true; + break; + case 27: + // escape + result.key = C0.ESC; + result.cancel = true; + break; + case 37: + // left-arrow + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D'; + // HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards + // http://unix.stackexchange.com/a/108106 + // macOS uses different escape sequences than linux + if (result.key === C0.ESC + '[1;3D') { + result.key = (this.browser.isMac) ? C0.ESC + 'b' : C0.ESC + '[1;5D'; + } + } else if (this.applicationCursor) { + result.key = C0.ESC + 'OD'; + } else { + result.key = C0.ESC + '[D'; + } + break; + case 39: + // right-arrow + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C'; + // HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward + // http://unix.stackexchange.com/a/108106 + // macOS uses different escape sequences than linux + if (result.key === C0.ESC + '[1;3C') { + result.key = (this.browser.isMac) ? C0.ESC + 'f' : C0.ESC + '[1;5C'; + } + } else if (this.applicationCursor) { + result.key = C0.ESC + 'OC'; + } else { + result.key = C0.ESC + '[C'; + } + break; + case 38: + // up-arrow + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A'; + // HACK: Make Alt + up-arrow behave like Ctrl + up-arrow + // http://unix.stackexchange.com/a/108106 + if (result.key === C0.ESC + '[1;3A') { + result.key = C0.ESC + '[1;5A'; + } + } else if (this.applicationCursor) { + result.key = C0.ESC + 'OA'; + } else { + result.key = C0.ESC + '[A'; + } + break; + case 40: + // down-arrow + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B'; + // HACK: Make Alt + down-arrow behave like Ctrl + down-arrow + // http://unix.stackexchange.com/a/108106 + if (result.key === C0.ESC + '[1;3B') { + result.key = C0.ESC + '[1;5B'; + } + } else if (this.applicationCursor) { + result.key = C0.ESC + 'OB'; + } else { + result.key = C0.ESC + '[B'; + } + break; + case 45: + // insert + if (!ev.shiftKey && !ev.ctrlKey) { + // or + are used to + // copy-paste on some systems. + result.key = C0.ESC + '[2~'; + } + break; + case 46: + // delete + if (modifiers) { + result.key = C0.ESC + '[3;' + (modifiers + 1) + '~'; + } else { + result.key = C0.ESC + '[3~'; + } + break; + case 36: + // home + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'H'; + } else if (this.applicationCursor) { + result.key = C0.ESC + 'OH'; + } else { + result.key = C0.ESC + '[H'; + } + break; + case 35: + // end + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'F'; + } else if (this.applicationCursor) { + result.key = C0.ESC + 'OF'; + } else { + result.key = C0.ESC + '[F'; + } + break; + case 33: + // page up + if (ev.shiftKey) { + result.scrollLines = -(this.rows - 1); + } else { + result.key = C0.ESC + '[5~'; + } + break; + case 34: + // page down + if (ev.shiftKey) { + result.scrollLines = this.rows - 1; + } else { + result.key = C0.ESC + '[6~'; + } + break; + case 112: + // F1-F12 + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'P'; + } else { + result.key = C0.ESC + 'OP'; + } + break; + case 113: + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'Q'; + } else { + result.key = C0.ESC + 'OQ'; + } + break; + case 114: + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'R'; + } else { + result.key = C0.ESC + 'OR'; + } + break; + case 115: + if (modifiers) { + result.key = C0.ESC + '[1;' + (modifiers + 1) + 'S'; + } else { + result.key = C0.ESC + 'OS'; + } + break; + case 116: + if (modifiers) { + result.key = C0.ESC + '[15;' + (modifiers + 1) + '~'; + } else { + result.key = C0.ESC + '[15~'; + } + break; + case 117: + if (modifiers) { + result.key = C0.ESC + '[17;' + (modifiers + 1) + '~'; + } else { + result.key = C0.ESC + '[17~'; + } + break; + case 118: + if (modifiers) { + result.key = C0.ESC + '[18;' + (modifiers + 1) + '~'; + } else { + result.key = C0.ESC + '[18~'; + } + break; + case 119: + if (modifiers) { + result.key = C0.ESC + '[19;' + (modifiers + 1) + '~'; + } else { + result.key = C0.ESC + '[19~'; + } + break; + case 120: + if (modifiers) { + result.key = C0.ESC + '[20;' + (modifiers + 1) + '~'; + } else { + result.key = C0.ESC + '[20~'; + } + break; + case 121: + if (modifiers) { + result.key = C0.ESC + '[21;' + (modifiers + 1) + '~'; + } else { + result.key = C0.ESC + '[21~'; + } + break; + case 122: + if (modifiers) { + result.key = C0.ESC + '[23;' + (modifiers + 1) + '~'; + } else { + result.key = C0.ESC + '[23~'; + } + break; + case 123: + if (modifiers) { + result.key = C0.ESC + '[24;' + (modifiers + 1) + '~'; + } else { + result.key = C0.ESC + '[24~'; + } + break; + default: + // a-z and space + if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { + if (ev.keyCode >= 65 && ev.keyCode <= 90) { + result.key = String.fromCharCode(ev.keyCode - 64); + } else if (ev.keyCode === 32) { + // NUL + result.key = String.fromCharCode(0); + } else if (ev.keyCode >= 51 && ev.keyCode <= 55) { + // escape, file sep, group sep, record sep, unit sep + result.key = String.fromCharCode(ev.keyCode - 51 + 27); + } else if (ev.keyCode === 56) { + // delete + result.key = String.fromCharCode(127); + } else if (ev.keyCode === 219) { + // ^[ - Control Sequence Introducer (CSI) + result.key = String.fromCharCode(27); + } else if (ev.keyCode === 220) { + // ^\ - String Terminator (ST) + result.key = String.fromCharCode(28); + } else if (ev.keyCode === 221) { + // ^] - Operating System Command (OSC) + result.key = String.fromCharCode(29); + } + } else if ((!this.browser.isMac || this.options.macOptionIsMeta) && ev.altKey && !ev.metaKey) { + // On macOS this is a third level shift when !macOptionIsMeta. Use instead. + const keyMapping = KEYCODE_KEY_MAPPINGS[ev.keyCode]; + const key = keyMapping && keyMapping[!ev.shiftKey ? 0 : 1]; + if (key) { + result.key = C0.ESC + key; + } else if (ev.keyCode >= 65 && ev.keyCode <= 90) { + const keyCode = ev.ctrlKey ? ev.keyCode - 64 : ev.keyCode + 32; + result.key = C0.ESC + String.fromCharCode(keyCode); + } + } else if (this.browser.isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) { + if (ev.keyCode === 65) { // cmd + a + this.selectAll(); + } + } + break; + } + + return result; +} From 95217ca931397bbdcf8254ae4465583a87dcf8ab Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Jun 2018 12:21:06 +0200 Subject: [PATCH 08/21] Remove core/input/Keyboard's dep on Terminal --- src/Terminal.test.ts | 257 +----------------------------- src/Terminal.ts | 13 +- src/core/Types.ts | 11 +- src/core/input/Keyboard.test.ts | 273 ++++++++++++++++++++++++++++++++ src/core/input/Keyboard.ts | 49 +++--- 5 files changed, 324 insertions(+), 279 deletions(-) create mode 100644 src/core/input/Keyboard.test.ts diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index f6cbad52..8974b784 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -13,7 +13,6 @@ const INIT_COLS = 80; const INIT_ROWS = 24; class TestTerminal extends Terminal { - public evaluateKeyEscapeSequence(ev: any): {cancel: boolean, key: string, scrollLines: number} { return this._evaluateKeyEscapeSequence(ev); } public keyDown(ev: any): boolean { return this._keyDown(ev); } public keyPress(ev: any): boolean { return this._keyPress(ev); } } @@ -296,22 +295,19 @@ describe('term.js addons', () => { }); }); - describe('keyDown', () => { + describe('keyPress', () => { 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' }; - }; const event = { type: 'keydown', - keyCode: 0, + key: 'a', + keyCode: 65, preventDefault: () => {}, stopPropagation: () => {} }; term.buffer.ydisp = 0; term.buffer.ybase = 40; - term.keyDown(event); + term.keyPress(event); // Ensure that now the terminal is scrolled to bottom assert.equal(term.buffer.ydisp, term.buffer.ybase); @@ -330,7 +326,7 @@ describe('term.js addons', () => { assert.equal(term.buffer.ydisp, startYDisp); term.scrollLines(-1); assert.equal(term.buffer.ydisp, startYDisp - 1); - term.keyDown({ keyCode: 0 }); + term.keyPress({ keyCode: 0 }); assert.equal(term.buffer.ydisp, startYDisp - 1); }); }); @@ -468,249 +464,6 @@ describe('term.js addons', () => { }); }); - 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 - }); - it('should return \\x1ba for alt+a', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 65 }).key, '\x1ba'); - }); - }); - - 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 undefined for alt+a', () => { - assert.strictEqual(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 65 }).key, undefined); - }); - }); - - describe('with macOptionIsMeta', () => { - beforeEach(() => { - term.browser.isMac = true; - term.setOption('macOptionIsMeta', true); - }); - it('should return \\x1ba for alt+a', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, keyCode: 65 }).key, '\x1ba'); - }); - }); - - 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~'); - }); - - // Characters using ctrl+alt sequences - it('should return proper sequence for ctrl+alt+a', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, ctrlKey: true, keyCode: 65 }).key, '\x1b\x01'); - }); - - // Characters using alt sequences (numbers) - it('should return proper sequences for alt+0', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 48 }).key, '\x1b0'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 48 }).key, '\x1b)'); - }); - it('should return proper sequences for alt+1', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 49 }).key, '\x1b1'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 49 }).key, '\x1b!'); - }); - it('should return proper sequences for alt+2', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 50 }).key, '\x1b2'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 50 }).key, '\x1b@'); - }); - it('should return proper sequences for alt+3', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 51 }).key, '\x1b3'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 51 }).key, '\x1b#'); - }); - it('should return proper sequences for alt+4', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 52 }).key, '\x1b4'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 52 }).key, '\x1b$'); - }); - it('should return proper sequences for alt+5', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 53 }).key, '\x1b5'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 53 }).key, '\x1b%'); - }); - it('should return proper sequences for alt+6', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 54 }).key, '\x1b6'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 54 }).key, '\x1b^'); - }); - it('should return proper sequences for alt+7', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 55 }).key, '\x1b7'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 55 }).key, '\x1b&'); - }); - it('should return proper sequences for alt+8', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 56 }).key, '\x1b8'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 56 }).key, '\x1b*'); - }); - it('should return proper sequences for alt+9', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 57 }).key, '\x1b9'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 57 }).key, '\x1b('); - }); - - // Characters using alt sequences (special chars) - it('should return proper sequences for alt+;', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 186 }).key, '\x1b;'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 186 }).key, '\x1b:'); - }); - it('should return proper sequences for alt+=', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 187 }).key, '\x1b='); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 187 }).key, '\x1b+'); - }); - it('should return proper sequences for alt+,', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 188 }).key, '\x1b,'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 188 }).key, '\x1b<'); - }); - it('should return proper sequences for alt+-', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 189 }).key, '\x1b-'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 189 }).key, '\x1b_'); - }); - it('should return proper sequences for alt+.', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 190 }).key, '\x1b.'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 190 }).key, '\x1b>'); - }); - it('should return proper sequences for alt+/', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 191 }).key, '\x1b/'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 191 }).key, '\x1b?'); - }); - it('should return proper sequences for alt+~', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 192 }).key, '\x1b`'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 192 }).key, '\x1b~'); - }); - it('should return proper sequences for alt+[', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 219 }).key, '\x1b['); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 219 }).key, '\x1b{'); - }); - it('should return proper sequences for alt+\\', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 220 }).key, '\x1b\\'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 220 }).key, '\x1b|'); - }); - it('should return proper sequences for alt+]', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 221 }).key, '\x1b]'); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 221 }).key, '\x1b}'); - }); - it('should return proper sequences for alt+\'', () => { - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: false, keyCode: 222 }).key, '\x1b\''); - assert.equal(term.evaluateKeyEscapeSequence({ altKey: true, shiftKey: true, keyCode: 222 }).key, '\x1b"'); - }); - }); - describe('Third level shift', () => { let evKeyDown: any; let evKeyPress: any; diff --git a/src/Terminal.ts b/src/Terminal.ts index 1b7a0c1a..a1918353 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -52,6 +52,7 @@ import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; import { IKeyboardEvent } from './base/Types'; import { evaluateKeyboardEvent } from './core/input/Keyboard'; +import { KeyboardResultType } from './core/Types'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -1422,7 +1423,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II return false; } - const result = evaluateKeyboardEvent(event); + const result = evaluateKeyboardEvent(event, this.applicationCursor, this.browser.isMac, this.options.macOptionIsMeta); // if (result.key === C0.DC3) { // XOFF // this._writeStopped = true; @@ -1430,11 +1431,17 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II // this._writeStopped = false; // } - if (result.scrollLines) { - this.scrollLines(result.scrollLines); + if (result.type === KeyboardResultType.PAGE_DOWN || result.type === KeyboardResultType.PAGE_UP) { + const scrollCount = this.rows - 1; + this.scrollLines(result.type === KeyboardResultType.PAGE_UP ? -scrollCount : scrollCount); return this.cancel(event, true); } + if (result.type === KeyboardResultType.SELECT_ALL) { + this.selectAll(); + // TODO: Verify cancel behavior is the same as before + } + if (this._isThirdLevelShift(this.browser, event)) { return true; } diff --git a/src/core/Types.ts b/src/core/Types.ts index 374b8f7d..cc5c0d1b 100644 --- a/src/core/Types.ts +++ b/src/core/Types.ts @@ -3,8 +3,15 @@ * @license MIT */ +export const enum KeyboardResultType { + SEND_KEY, + SELECT_ALL, + PAGE_UP, + PAGE_DOWN +} + export interface IKeyboardResult { + type: KeyboardResultType; cancel: boolean; - key: string; - scrollLines: number; + key: string | undefined; } diff --git a/src/core/input/Keyboard.test.ts b/src/core/input/Keyboard.test.ts new file mode 100644 index 00000000..97a12c88 --- /dev/null +++ b/src/core/input/Keyboard.test.ts @@ -0,0 +1,273 @@ + +import { assert } from 'chai'; +import { evaluateKeyboardEvent } from './Keyboard'; +import { IKeyboardResult } from '../Types'; + +/** + * A helper function for testing which allows passing in a partial event and defaults will be filled + * in on it. + */ +function testEvaluateKeyboardEvent(partialEvent: { + altKey?: boolean; + ctrlKey?: boolean; + shiftKey?: boolean; + metaKey?: boolean; + keyCode?: number; + key?: string; + type?: string; +}, partialOptions: { + applicationCursorMode?: boolean; + isMac?: boolean; + macOptionIsMeta?: boolean; +} = {}): IKeyboardResult { + const event = { + altKey: partialEvent.altKey || false, + ctrlKey: partialEvent.ctrlKey || false, + shiftKey: partialEvent.shiftKey || false, + metaKey: partialEvent.metaKey || false, + keyCode: partialEvent.keyCode || undefined, + key: partialEvent.key || '', + type: partialEvent.type || '' + }; + const options = { + applicationCursorMode: partialOptions.applicationCursorMode || false, + isMac: partialOptions.isMac || false, + macOptionIsMeta: partialOptions.macOptionIsMeta || false + }; + return evaluateKeyboardEvent(event, options.applicationCursorMode, options.isMac, options.macOptionIsMeta); +} + +describe('Keyboard', () => { + describe('evaluateKeyEscapeSequence', () => { + it('should return the correct escape sequence for unmodified keys', () => { + // Backspace + assert.equal(testEvaluateKeyboardEvent({ keyCode: 8 }).key, '\x7f'); // ^? + // Tab + assert.equal(testEvaluateKeyboardEvent({ keyCode: 9 }).key, '\t'); + // Return/enter + assert.equal(testEvaluateKeyboardEvent({ keyCode: 13 }).key, '\r'); // CR + // Escape + assert.equal(testEvaluateKeyboardEvent({ keyCode: 27 }).key, '\x1b'); + // Page up, page down + assert.equal(testEvaluateKeyboardEvent({ keyCode: 33 }).key, '\x1b[5~'); // CSI 5 ~ + assert.equal(testEvaluateKeyboardEvent({ keyCode: 34 }).key, '\x1b[6~'); // CSI 6 ~ + // End, Home + assert.equal(testEvaluateKeyboardEvent({ keyCode: 35 }).key, '\x1b[F'); // SS3 F + assert.equal(testEvaluateKeyboardEvent({ keyCode: 36 }).key, '\x1b[H'); // SS3 H + // Left, up, right, down arrows + assert.equal(testEvaluateKeyboardEvent({ keyCode: 37 }).key, '\x1b[D'); // CSI D + assert.equal(testEvaluateKeyboardEvent({ keyCode: 38 }).key, '\x1b[A'); // CSI A + assert.equal(testEvaluateKeyboardEvent({ keyCode: 39 }).key, '\x1b[C'); // CSI C + assert.equal(testEvaluateKeyboardEvent({ keyCode: 40 }).key, '\x1b[B'); // CSI B + // Insert + assert.equal(testEvaluateKeyboardEvent({ keyCode: 45 }).key, '\x1b[2~'); // CSI 2 ~ + // Delete + assert.equal(testEvaluateKeyboardEvent({ keyCode: 46 }).key, '\x1b[3~'); // CSI 3 ~ + // F1-F12 + assert.equal(testEvaluateKeyboardEvent({ keyCode: 112 }).key, '\x1bOP'); // SS3 P + assert.equal(testEvaluateKeyboardEvent({ keyCode: 113 }).key, '\x1bOQ'); // SS3 Q + assert.equal(testEvaluateKeyboardEvent({ keyCode: 114 }).key, '\x1bOR'); // SS3 R + assert.equal(testEvaluateKeyboardEvent({ keyCode: 115 }).key, '\x1bOS'); // SS3 S + assert.equal(testEvaluateKeyboardEvent({ keyCode: 116 }).key, '\x1b[15~'); // CSI 1 5 ~ + assert.equal(testEvaluateKeyboardEvent({ keyCode: 117 }).key, '\x1b[17~'); // CSI 1 7 ~ + assert.equal(testEvaluateKeyboardEvent({ keyCode: 118 }).key, '\x1b[18~'); // CSI 1 8 ~ + assert.equal(testEvaluateKeyboardEvent({ keyCode: 119 }).key, '\x1b[19~'); // CSI 1 9 ~ + assert.equal(testEvaluateKeyboardEvent({ keyCode: 120 }).key, '\x1b[20~'); // CSI 2 0 ~ + assert.equal(testEvaluateKeyboardEvent({ keyCode: 121 }).key, '\x1b[21~'); // CSI 2 1 ~ + assert.equal(testEvaluateKeyboardEvent({ keyCode: 122 }).key, '\x1b[23~'); // CSI 2 3 ~ + assert.equal(testEvaluateKeyboardEvent({ keyCode: 123 }).key, '\x1b[24~'); // CSI 2 4 ~ + }); + it('should return \\x1b[3;5~ for ctrl+delete', () => { + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 46 }).key, '\x1b[3;5~'); + }); + it('should return \\x1b[3;2~ for shift+delete', () => { + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 46 }).key, '\x1b[3;2~'); + }); + it('should return \\x1b[3;3~ for alt+delete', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 46 }).key, '\x1b[3;3~'); + }); + it('should return \\x1b[5D for ctrl+left', () => { + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 37 }).key, '\x1b[1;5D'); // CSI 5 D + }); + it('should return \\x1b[5C for ctrl+right', () => { + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 39 }).key, '\x1b[1;5C'); // CSI 5 C + }); + it('should return \\x1b[5A for ctrl+up', () => { + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A + }); + it('should return \\x1b[5B for ctrl+down', () => { + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 40 }).key, '\x1b[1;5B'); // CSI 5 B + }); + + describe('On non-macOS platforms', () => { + // 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(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: false }).key, '\x1b[1;5D'); // CSI 5 D + }); + it('should return \\x1b[5C for alt+right', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: false }).key, '\x1b[1;5C'); // CSI 5 C + }); + it('should return \\x1ba for alt+a', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: false }).key, '\x1ba'); + }); + }); + + describe('On macOS platforms', () => { + it('should return \\x1bb for alt+left', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: true }).key, '\x1bb'); // CSI 5 D + }); + it('should return \\x1bf for alt+right', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1bf'); // CSI 5 C + }); + it('should return undefined for alt+a', () => { + assert.strictEqual(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true }).key, undefined), { isMac: true }; + }); + }); + + describe('with macOptionIsMeta', () => { + it('should return \\x1ba for alt+a', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true, macOptionIsMeta: true }).key, '\x1ba'); + }); + }); + + it('should return \\x1b[5A for alt+up', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A + }); + it('should return \\x1b[5B for alt+down', () => { + assert.equal(testEvaluateKeyboardEvent({ 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(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 112 }).key, '\x1b[1;2P'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 113 }).key, '\x1b[1;2Q'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 114 }).key, '\x1b[1;2R'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 115 }).key, '\x1b[1;2S'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 116 }).key, '\x1b[15;2~'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 117 }).key, '\x1b[17;2~'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 118 }).key, '\x1b[18;2~'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 119 }).key, '\x1b[19;2~'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 120 }).key, '\x1b[20;2~'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 121 }).key, '\x1b[21;2~'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 122 }).key, '\x1b[23;2~'); + assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 123 }).key, '\x1b[24;2~'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 112 }).key, '\x1b[1;3P'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 113 }).key, '\x1b[1;3Q'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 114 }).key, '\x1b[1;3R'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 115 }).key, '\x1b[1;3S'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 116 }).key, '\x1b[15;3~'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 117 }).key, '\x1b[17;3~'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 118 }).key, '\x1b[18;3~'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 119 }).key, '\x1b[19;3~'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 120 }).key, '\x1b[20;3~'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 121 }).key, '\x1b[21;3~'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 122 }).key, '\x1b[23;3~'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 123 }).key, '\x1b[24;3~'); + + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 112 }).key, '\x1b[1;5P'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 113 }).key, '\x1b[1;5Q'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 114 }).key, '\x1b[1;5R'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 115 }).key, '\x1b[1;5S'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 116 }).key, '\x1b[15;5~'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 117 }).key, '\x1b[17;5~'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 118 }).key, '\x1b[18;5~'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 119 }).key, '\x1b[19;5~'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 120 }).key, '\x1b[20;5~'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 121 }).key, '\x1b[21;5~'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 122 }).key, '\x1b[23;5~'); + assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 123 }).key, '\x1b[24;5~'); + }); + + // Characters using ctrl+alt sequences + it('should return proper sequence for ctrl+alt+a', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, ctrlKey: true, keyCode: 65 }).key, '\x1b\x01'); + }); + + // Characters using alt sequences (numbers) + it('should return proper sequences for alt+0', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 48 }).key, '\x1b0'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 48 }).key, '\x1b)'); + }); + it('should return proper sequences for alt+1', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 49 }).key, '\x1b1'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 49 }).key, '\x1b!'); + }); + it('should return proper sequences for alt+2', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 50 }).key, '\x1b2'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 50 }).key, '\x1b@'); + }); + it('should return proper sequences for alt+3', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 51 }).key, '\x1b3'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 51 }).key, '\x1b#'); + }); + it('should return proper sequences for alt+4', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 52 }).key, '\x1b4'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 52 }).key, '\x1b$'); + }); + it('should return proper sequences for alt+5', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 53 }).key, '\x1b5'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 53 }).key, '\x1b%'); + }); + it('should return proper sequences for alt+6', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 54 }).key, '\x1b6'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 54 }).key, '\x1b^'); + }); + it('should return proper sequences for alt+7', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 55 }).key, '\x1b7'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 55 }).key, '\x1b&'); + }); + it('should return proper sequences for alt+8', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 56 }).key, '\x1b8'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 56 }).key, '\x1b*'); + }); + it('should return proper sequences for alt+9', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 57 }).key, '\x1b9'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 57 }).key, '\x1b('); + }); + + // Characters using alt sequences (special chars) + it('should return proper sequences for alt+;', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 186 }).key, '\x1b;'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 186 }).key, '\x1b:'); + }); + it('should return proper sequences for alt+=', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 187 }).key, '\x1b='); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 187 }).key, '\x1b+'); + }); + it('should return proper sequences for alt+,', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 188 }).key, '\x1b,'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 188 }).key, '\x1b<'); + }); + it('should return proper sequences for alt+-', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 189 }).key, '\x1b-'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 189 }).key, '\x1b_'); + }); + it('should return proper sequences for alt+.', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 190 }).key, '\x1b.'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 190 }).key, '\x1b>'); + }); + it('should return proper sequences for alt+/', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 191 }).key, '\x1b/'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 191 }).key, '\x1b?'); + }); + it('should return proper sequences for alt+~', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 192 }).key, '\x1b`'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 192 }).key, '\x1b~'); + }); + it('should return proper sequences for alt+[', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 219 }).key, '\x1b['); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 219 }).key, '\x1b{'); + }); + it('should return proper sequences for alt+\\', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 220 }).key, '\x1b\\'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 220 }).key, '\x1b|'); + }); + it('should return proper sequences for alt+]', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 221 }).key, '\x1b]'); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 221 }).key, '\x1b}'); + }); + it('should return proper sequences for alt+\'', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 222 }).key, '\x1b\''); + assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 222 }).key, '\x1b"'); + }); + }); +}); diff --git a/src/core/input/Keyboard.ts b/src/core/input/Keyboard.ts index caaa9189..f4393276 100644 --- a/src/core/input/Keyboard.ts +++ b/src/core/input/Keyboard.ts @@ -5,7 +5,7 @@ */ import { IKeyboardEvent } from '../../base/Types'; -import { IKeyboardResult } from '../Types'; +import { IKeyboardResult, KeyboardResultType } from '../Types'; import { C0 } from '../../EscapeSequences'; // reg + shift key mappings for digits and special chars @@ -36,42 +36,46 @@ const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { 222: ['\'', '"'] }; -export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { +export function evaluateKeyboardEvent( + ev: IKeyboardEvent, + applicationCursorMode: boolean, + isMac: boolean, + macOptionIsMeta: boolean +): IKeyboardResult { const result: IKeyboardResult = { + type: KeyboardResultType.SEND_KEY, // Whether to cancel event propogation (NOTE: this may not be needed since the event is // canceled at the end of keyDown cancel: false, // The new key even to emit - key: undefined, - // The number of characters to scroll, if this is defined it will cancel the event - scrollLines: undefined + key: undefined }; const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0); switch (ev.keyCode) { case 0: if (ev.key === 'UIKeyInputUpArrow') { - if (this.applicationCursor) { + if (applicationCursorMode) { result.key = C0.ESC + 'OA'; } else { result.key = C0.ESC + '[A'; } } else if (ev.key === 'UIKeyInputLeftArrow') { - if (this.applicationCursor) { + if (applicationCursorMode) { result.key = C0.ESC + 'OD'; } else { result.key = C0.ESC + '[D'; } } else if (ev.key === 'UIKeyInputRightArrow') { - if (this.applicationCursor) { + if (applicationCursorMode) { result.key = C0.ESC + 'OC'; } else { result.key = C0.ESC + '[C'; } } else if (ev.key === 'UIKeyInputDownArrow') { - if (this.applicationCursor) { + if (applicationCursorMode) { result.key = C0.ESC + 'OB'; } else { result.key = C0.ESC + '[B'; @@ -116,9 +120,9 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { // http://unix.stackexchange.com/a/108106 // macOS uses different escape sequences than linux if (result.key === C0.ESC + '[1;3D') { - result.key = (this.browser.isMac) ? C0.ESC + 'b' : C0.ESC + '[1;5D'; + result.key = isMac ? C0.ESC + 'b' : C0.ESC + '[1;5D'; } - } else if (this.applicationCursor) { + } else if (applicationCursorMode) { result.key = C0.ESC + 'OD'; } else { result.key = C0.ESC + '[D'; @@ -132,9 +136,9 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { // http://unix.stackexchange.com/a/108106 // macOS uses different escape sequences than linux if (result.key === C0.ESC + '[1;3C') { - result.key = (this.browser.isMac) ? C0.ESC + 'f' : C0.ESC + '[1;5C'; + result.key = isMac ? C0.ESC + 'f' : C0.ESC + '[1;5C'; } - } else if (this.applicationCursor) { + } else if (applicationCursorMode) { result.key = C0.ESC + 'OC'; } else { result.key = C0.ESC + '[C'; @@ -149,7 +153,7 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { if (result.key === C0.ESC + '[1;3A') { result.key = C0.ESC + '[1;5A'; } - } else if (this.applicationCursor) { + } else if (applicationCursorMode) { result.key = C0.ESC + 'OA'; } else { result.key = C0.ESC + '[A'; @@ -164,7 +168,7 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { if (result.key === C0.ESC + '[1;3B') { result.key = C0.ESC + '[1;5B'; } - } else if (this.applicationCursor) { + } else if (applicationCursorMode) { result.key = C0.ESC + 'OB'; } else { result.key = C0.ESC + '[B'; @@ -190,7 +194,7 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { // home if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'H'; - } else if (this.applicationCursor) { + } else if (applicationCursorMode) { result.key = C0.ESC + 'OH'; } else { result.key = C0.ESC + '[H'; @@ -200,7 +204,7 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { // end if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'F'; - } else if (this.applicationCursor) { + } else if (applicationCursorMode) { result.key = C0.ESC + 'OF'; } else { result.key = C0.ESC + '[F'; @@ -209,7 +213,7 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { case 33: // page up if (ev.shiftKey) { - result.scrollLines = -(this.rows - 1); + result.type = KeyboardResultType.PAGE_UP; } else { result.key = C0.ESC + '[5~'; } @@ -217,7 +221,7 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { case 34: // page down if (ev.shiftKey) { - result.scrollLines = this.rows - 1; + result.type = KeyboardResultType.PAGE_DOWN; } else { result.key = C0.ESC + '[6~'; } @@ -331,7 +335,7 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { // ^] - Operating System Command (OSC) result.key = String.fromCharCode(29); } - } else if ((!this.browser.isMac || this.options.macOptionIsMeta) && ev.altKey && !ev.metaKey) { + } else if ((!isMac || macOptionIsMeta) && ev.altKey && !ev.metaKey) { // On macOS this is a third level shift when !macOptionIsMeta. Use instead. const keyMapping = KEYCODE_KEY_MAPPINGS[ev.keyCode]; const key = keyMapping && keyMapping[!ev.shiftKey ? 0 : 1]; @@ -341,9 +345,10 @@ export function evaluateKeyboardEvent(ev: IKeyboardEvent): IKeyboardResult { const keyCode = ev.ctrlKey ? ev.keyCode - 64 : ev.keyCode + 32; result.key = C0.ESC + String.fromCharCode(keyCode); } - } else if (this.browser.isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) { + } else if (isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) { if (ev.keyCode === 65) { // cmd + a - this.selectAll(); + result.type = KeyboardResultType.SELECT_ALL; + // TODO: Select all in terminal side } } break; From 20307ef637e9382ce7133726de949c8de1931f3a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Jun 2018 12:43:22 +0200 Subject: [PATCH 09/21] Add tests for mobile key events --- src/core/input/Keyboard.test.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/core/input/Keyboard.test.ts b/src/core/input/Keyboard.test.ts index 97a12c88..1104fc61 100644 --- a/src/core/input/Keyboard.test.ts +++ b/src/core/input/Keyboard.test.ts @@ -25,7 +25,7 @@ function testEvaluateKeyboardEvent(partialEvent: { ctrlKey: partialEvent.ctrlKey || false, shiftKey: partialEvent.shiftKey || false, metaKey: partialEvent.metaKey || false, - keyCode: partialEvent.keyCode || undefined, + keyCode: partialEvent.keyCode !== undefined ? partialEvent.keyCode : undefined, key: partialEvent.key || '', type: partialEvent.type || '' }; @@ -269,5 +269,16 @@ describe('Keyboard', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 222 }).key, '\x1b\''); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 222 }).key, '\x1b"'); }); + + it('should handle mobile arrow events', () => { + assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputUpArrow' }).key, '\x1b[A'); + assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputUpArrow' }, { applicationCursorMode: true }).key, '\x1bOA'); + assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputLeftArrow' }).key, '\x1b[D'); + assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputLeftArrow' }, { applicationCursorMode: true }).key, '\x1bOD'); + assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputRightArrow' }).key, '\x1b[C'); + assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputRightArrow' }, { applicationCursorMode: true }).key, '\x1bOC'); + assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputDownArrow' }).key, '\x1b[B'); + assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputDownArrow' }, { applicationCursorMode: true }).key, '\x1bOB'); + }); }); }); From d29a527724f13767a3a80a4f37fee2f2cea28aff Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Jun 2018 16:40:36 +0100 Subject: [PATCH 10/21] Introduce the public module --- demo/main.js | 6 +- package.json | 2 +- src/Terminal.ts | 6 +- src/addons/fit/fit.ts | 8 +- src/addons/search/Interfaces.ts | 11 +- src/addons/search/SearchHelper.ts | 24 ++--- src/addons/winptyCompat/Interfaces.ts | 6 +- src/public/Terminal.ts | 139 ++++++++++++++++++++++++++ src/xterm.ts | 2 +- 9 files changed, 175 insertions(+), 29 deletions(-) create mode 100644 src/public/Terminal.ts diff --git a/demo/main.js b/demo/main.js index 13fa8e96..1ed6a180 100644 --- a/demo/main.js +++ b/demo/main.js @@ -170,7 +170,7 @@ function initOptions(term) { fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'] }; - var options = Object.keys(term.options); + var options = Object.keys(term._core.options); var booleanOptions = []; var numberOptions = []; options.filter(o => blacklistedOptions.indexOf(o) === -1).forEach(o => { @@ -241,8 +241,8 @@ function initOptions(term) { function updateTerminalSize() { var cols = parseInt(document.getElementById(`opt-cols`).value, 10); var rows = parseInt(document.getElementById(`opt-rows`).value, 10); - var width = (cols * term.renderer.dimensions.actualCellWidth + term.viewport.scrollBarWidth).toString() + 'px'; - var height = (rows * term.renderer.dimensions.actualCellHeight).toString() + 'px'; + var width = (cols * term._core.renderer.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toString() + 'px'; + var height = (rows * term._core.renderer.dimensions.actualCellHeight).toString() + 'px'; terminalContainer.style.width = width; terminalContainer.style.height = height; term.fit(); diff --git a/package.json b/package.json index c7f6f81c..4aaac342 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "xterm", "description": "Full xterm terminal, in your browser", "version": "3.4.0", - "main": "lib/Terminal.js", + "main": "lib/public/Terminal.js", "types": "typings/xterm.d.ts", "repository": "https://github.com/xtermjs/xterm.js", "license": "MIT", diff --git a/src/Terminal.ts b/src/Terminal.ts index a1918353..02e7c3ed 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -751,9 +751,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II * Apply the provided addon on the `Terminal` class. * @param addon The addon to apply. */ - public static applyAddon(addon: any): void { - addon.apply(Terminal); - } + // public static applyAddon(addon: any): void { + // addon.apply(Terminal); + // } /** * XTerm mouse events diff --git a/src/addons/fit/fit.ts b/src/addons/fit/fit.ts index 68e4cfd5..ac978807 100644 --- a/src/addons/fit/fit.ts +++ b/src/addons/fit/fit.ts @@ -37,10 +37,10 @@ export function proposeGeometry(term: Terminal): IGeometry { const elementPaddingVer = elementPadding.top + elementPadding.bottom; const elementPaddingHor = elementPadding.right + elementPadding.left; const availableHeight = parentElementHeight - elementPaddingVer; - const availableWidth = parentElementWidth - elementPaddingHor - (term).viewport.scrollBarWidth; + const availableWidth = parentElementWidth - elementPaddingHor - (term)._core.viewport.scrollBarWidth; const geometry = { - cols: Math.floor(availableWidth / (term).renderer.dimensions.actualCellWidth), - rows: Math.floor(availableHeight / (term).renderer.dimensions.actualCellHeight) + cols: Math.floor(availableWidth / (term)._core.renderer.dimensions.actualCellWidth), + rows: Math.floor(availableHeight / (term)._core.renderer.dimensions.actualCellHeight) }; return geometry; } @@ -50,7 +50,7 @@ export function fit(term: Terminal): void { if (geometry) { // Force a full render if (term.rows !== geometry.rows || term.cols !== geometry.cols) { - (term).renderer.clear(); + (term)._core.renderer.clear(); term.resize(geometry.cols, geometry.rows); } } diff --git a/src/addons/search/Interfaces.ts b/src/addons/search/Interfaces.ts index 6faa03e2..926e3f36 100644 --- a/src/addons/search/Interfaces.ts +++ b/src/addons/search/Interfaces.ts @@ -5,14 +5,17 @@ import { Terminal } from 'xterm'; -export interface ISearchAddonTerminal extends Terminal { - __searchHelper?: ISearchHelper; - - // TODO: Reuse ITerminal from core +// TODO: Don't rely on this private API +export interface ITerminalCore { buffer: any; selectionManager: any; } +export interface ISearchAddonTerminal extends Terminal { + __searchHelper?: ISearchHelper; + _core: ITerminalCore; +} + export interface ISearchHelper { findNext(term: string): boolean; findPrevious(term: string): boolean; diff --git a/src/addons/search/SearchHelper.ts b/src/addons/search/SearchHelper.ts index 2abc3b38..5ba81aa0 100644 --- a/src/addons/search/SearchHelper.ts +++ b/src/addons/search/SearchHelper.ts @@ -35,14 +35,14 @@ export class SearchHelper implements ISearchHelper { let result: ISearchResult; - let startRow = this._terminal.buffer.ydisp; - if (this._terminal.selectionManager.selectionEnd) { + let startRow = this._terminal._core.buffer.ydisp; + if (this._terminal._core.selectionManager.selectionEnd) { // Start from the selection end if there is a selection - startRow = this._terminal.selectionManager.selectionEnd[1]; + startRow = this._terminal._core.selectionManager.selectionEnd[1]; } // Search from ydisp + 1 to end - for (let y = startRow + 1; y < this._terminal.buffer.ybase + this._terminal.rows; y++) { + for (let y = startRow + 1; y < this._terminal._core.buffer.ybase + this._terminal.rows; y++) { result = this._findInLine(term, y); if (result) { break; @@ -76,10 +76,10 @@ export class SearchHelper implements ISearchHelper { let result: ISearchResult; - let startRow = this._terminal.buffer.ydisp; - if (this._terminal.selectionManager.selectionStart) { + let startRow = this._terminal._core.buffer.ydisp; + if (this._terminal._core.selectionManager.selectionStart) { // Start from the selection end if there is a selection - startRow = this._terminal.selectionManager.selectionStart[1]; + startRow = this._terminal._core.selectionManager.selectionStart[1]; } // Search from ydisp + 1 to end @@ -92,7 +92,7 @@ export class SearchHelper implements ISearchHelper { // Search from the top to the current ydisp if (!result) { - for (let y = this._terminal.buffer.ybase + this._terminal.rows - 1; y > startRow; y--) { + for (let y = this._terminal._core.buffer.ybase + this._terminal.rows - 1; y > startRow; y--) { result = this._findInLine(term, y); if (result) { break; @@ -111,11 +111,11 @@ export class SearchHelper implements ISearchHelper { * @return The search result if it was found. */ private _findInLine(term: string, y: number): ISearchResult { - const lowerStringLine = this._terminal.buffer.translateBufferLineToString(y, true).toLowerCase(); + const lowerStringLine = this._terminal._core.buffer.translateBufferLineToString(y, true).toLowerCase(); const lowerTerm = term.toLowerCase(); let searchIndex = lowerStringLine.indexOf(lowerTerm); if (searchIndex >= 0) { - const line = this._terminal.buffer.lines.get(y); + const line = this._terminal._core.buffer.lines.get(y); for (let i = 0; i < searchIndex; i++) { const charData = line[i]; // Adjust the searchIndex to normalize emoji into single chars @@ -147,8 +147,8 @@ export class SearchHelper implements ISearchHelper { if (!result) { return false; } - this._terminal.selectionManager.setSelection(result.col, result.row, result.term.length); - this._terminal.scrollLines(result.row - this._terminal.buffer.ydisp); + this._terminal._core.selectionManager.setSelection(result.col, result.row, result.term.length); + this._terminal.scrollLines(result.row - this._terminal._core.buffer.ydisp); return true; } } diff --git a/src/addons/winptyCompat/Interfaces.ts b/src/addons/winptyCompat/Interfaces.ts index 8e02c64b..6217c860 100644 --- a/src/addons/winptyCompat/Interfaces.ts +++ b/src/addons/winptyCompat/Interfaces.ts @@ -5,6 +5,10 @@ import { Terminal } from 'xterm'; -export interface IWinptyCompatAddonTerminal extends Terminal { +export interface ITerminalCore { buffer: any; } + +export interface IWinptyCompatAddonTerminal extends Terminal { + _core: ITerminalCore; +} diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts new file mode 100644 index 00000000..0d491a69 --- /dev/null +++ b/src/public/Terminal.ts @@ -0,0 +1,139 @@ +import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme } from 'xterm'; +import { ITerminal } from '../Types'; +import { Terminal as TerminalCore } from '../Terminal'; + +export class Terminal implements ITerminalApi { + private _core: ITerminal; + + constructor(options?: ITerminalOptions) { + this._core = new TerminalCore(options); + } + + public get element(): HTMLElement { return this._core.element; } + public get textarea(): HTMLTextAreaElement { return this._core.textarea; } + public get rows(): number { return this._core.rows; } + public get cols(): number { return this._core.cols; } + public get markers(): IMarker[] { return this._core.markers; } + public blur(): void { + this._core.blur(); + } + public focus(): void { + this._core.focus(); + } + public on(type: 'blur' | 'focus' | 'linefeed' | 'selection', listener: () => void): void; + public on(type: 'data', listener: (...args: any[]) => void): void; + public on(type: 'key', listener: (key?: string, event?: KeyboardEvent) => void): void; + public on(type: 'keypress' | 'keydown', listener: (event?: KeyboardEvent) => void): void; + public on(type: 'refresh', listener: (data?: { start: number; end: number; }) => void): void; + public on(type: 'resize', listener: (data?: { cols: number; rows: number; }) => void): void; + public on(type: 'scroll', listener: (ydisp?: number) => void): void; + public on(type: 'title', listener: (title?: string) => void): void; + public on(type: string, listener: (...args: any[]) => void): void; + public on(type: any, listener: any): void { + this._core.on(type, listener); + } + public off(type: string, listener: (...args: any[]) => void): void { + this._core.off(type, listener); + } + public emit(type: string, data?: any): void { + this._core.emit(type, data); + } + public addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable { + return this.addDisposableListener(type, handler); + } + public resize(columns: number, rows: number): void { + this._core.resize(columns, rows); + } + public writeln(data: string): void { + this._core.writeln(data); + } + public open(parent: HTMLElement): void { + this._core.open(parent); + } + public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void { + this._core.attachCustomKeyEventHandler(customKeyEventHandler); + } + public registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number { + return this._core.registerLinkMatcher(regex, handler, options); + } + public deregisterLinkMatcher(matcherId: number): void { + this._core.deregisterLinkMatcher(matcherId); + } + public addMarker(cursorYOffset: number): IMarker { + return this._core.addMarker(cursorYOffset); + } + public hasSelection(): boolean { + return this._core.hasSelection(); + } + public getSelection(): string { + return this._core.getSelection(); + } + public clearSelection(): void { + this._core.clearSelection(); + } + public selectAll(): void { + this._core.selectAll(); + } + public selectLines(start: number, end: number): void { + this._core.selectLines(start, end); + } + public dispose(): void { + this._core.dispose(); + } + public destroy(): void { + this._core.destroy(); + } + public scrollLines(amount: number): void { + this._core.scrollLines(amount); + } + public scrollPages(pageCount: number): void { + this._core.scrollPages(pageCount); + } + public scrollToTop(): void { + this._core.scrollToTop(); + } + public scrollToBottom(): void { + this._core.scrollToBottom(); + } + public scrollToLine(line: number): void { + this._core.scrollToLine(line); + } + public clear(): void { + this._core.clear(); + } + public write(data: string): void { + this._core.write(data); + } + public getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'rendererType' | 'termName'): string; + public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; + public getOption(key: 'colors'): string[]; + public getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number; + public getOption(key: 'handler'): (data: string) => void; + public getOption(key: string): any; + public getOption(key: any): any { + return this._core.getOption(key); + } + public setOption(key: 'bellSound' | 'fontFamily' | 'termName', value: string): void; + public setOption(key: 'fontWeight' | 'fontWeightBold', value: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void; + public setOption(key: 'bellStyle', value: 'none' | 'visual' | 'sound' | 'both'): void; + public setOption(key: 'cursorStyle', value: 'block' | 'underline' | 'bar'): void; + public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; + public setOption(key: 'colors', value: string[]): void; + public setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void; + public setOption(key: 'handler', value: (data: string) => void): void; + public setOption(key: 'theme', value: ITheme): void; + public setOption(key: 'cols' | 'rows', value: number): void; + public setOption(key: string, value: any): void; + public setOption(key: any, value: any): void { + this._core.setOption(key, value); + } + public refresh(start: number, end: number): void { + this._core.refresh(start, end); + } + public reset(): void { + this._core.reset(); + } + public static applyAddon(addon: any): void { + addon.apply(Terminal); + } +} diff --git a/src/xterm.ts b/src/xterm.ts index 6df3a4c0..81b95805 100644 --- a/src/xterm.ts +++ b/src/xterm.ts @@ -5,6 +5,6 @@ * This file is the entry point for browserify. */ -import { Terminal } from './Terminal'; +import { Terminal } from './public/Terminal'; module.exports = Terminal; From 410d506f34e520a9b884b60663c8ac4f1e91a0b9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Jun 2018 16:44:50 +0100 Subject: [PATCH 11/21] Fix applyAddon test --- src/Terminal.test.ts | 8 -------- src/public/Terminal.test.ts | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 src/public/Terminal.test.ts diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 8974b784..0745ddda 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -5,7 +5,6 @@ import { assert, expect } from 'chai'; import { Terminal } from './Terminal'; -import * as attach from './addons/attach/attach'; import { MockViewport, MockCompositionHelper, MockRenderer } from './utils/TestUtils.test'; import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from './Buffer'; @@ -52,13 +51,6 @@ describe('term.js addons', () => { }); }); - it('should apply addons with Terminal.applyAddon', () => { - Terminal.applyAddon(attach); - // Test that addon was applied 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. diff --git a/src/public/Terminal.test.ts b/src/public/Terminal.test.ts new file mode 100644 index 00000000..06c8f1d5 --- /dev/null +++ b/src/public/Terminal.test.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2016 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { Terminal } from './Terminal'; +import * as attach from '../addons/attach/attach'; + +describe('Terminal', () => { + it('should apply addons with Terminal.applyAddon', () => { + Terminal.applyAddon(attach); + // Test that addon was applied successfully, adding attach to Terminal's + // prototype. + assert.equal(typeof (Terminal).prototype.attach, 'function'); + }); +}); From 12e7f80037d66cebe7bb02cc38aa1e5a8cca74c2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Jun 2018 16:53:28 +0100 Subject: [PATCH 12/21] Move Charsets to core --- src/InputHandler.ts | 4 ++-- src/Parser.ts | 2 +- src/Types.ts | 5 +---- src/core/Types.ts | 4 ++++ src/{ => core/data}/Charsets.ts | 2 +- src/public/Terminal.ts | 5 +++++ 6 files changed, 14 insertions(+), 8 deletions(-) rename src/{ => core/data}/Charsets.ts (99%) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index dbf6dfb2..84276b5b 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -4,9 +4,9 @@ * @license MIT */ -import { CharData, IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer, ICharset } from './Types'; +import { CharData, IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer } from './Types'; import { C0, C1 } from './EscapeSequences'; -import { CHARSETS, DEFAULT_CHARSET } from './Charsets'; +import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets'; import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, DEFAULT_ATTR } from './Buffer'; import { FLAGS } from './renderer/Types'; import { wcwidth } from './CharWidth'; diff --git a/src/Parser.ts b/src/Parser.ts index 519b9609..3675e1f0 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -6,7 +6,7 @@ import { C0 } from './EscapeSequences'; import { IInputHandler, IInputHandlingTerminal } from './Types'; -import { CHARSETS, DEFAULT_CHARSET } from './Charsets'; +import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets'; const normalStateHandler: {[key: string]: (parser: Parser, handler: IInputHandler) => void} = {}; normalStateHandler[C0.BEL] = (parser, handler) => handler.bell(); diff --git a/src/Types.ts b/src/Types.ts index 96eec3c4..1de223f7 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -6,6 +6,7 @@ import { Terminal as PublicTerminal, ITerminalOptions as IPublicTerminalOptions, IEventEmitter } from 'xterm'; import { IColorSet, IRenderer } from './renderer/Types'; import { IMouseZoneManager } from './input/Types'; +import { ICharset } from './core/Types'; export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean; @@ -191,10 +192,6 @@ export interface ILinkMatcher { willLinkActivate?: (event: MouseEvent, uri: string) => boolean; } -export interface ICharset { - [key: string]: string; -} - export interface ILinkHoverEvent { x1: number; y1: number; diff --git a/src/core/Types.ts b/src/core/Types.ts index cc5c0d1b..4001e448 100644 --- a/src/core/Types.ts +++ b/src/core/Types.ts @@ -15,3 +15,7 @@ export interface IKeyboardResult { cancel: boolean; key: string | undefined; } + +export interface ICharset { + [key: string]: string; +} diff --git a/src/Charsets.ts b/src/core/data/Charsets.ts similarity index 99% rename from src/Charsets.ts rename to src/core/data/Charsets.ts index fe0112ec..b49ee77f 100644 --- a/src/Charsets.ts +++ b/src/core/data/Charsets.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { ICharset } from './Types'; +import { ICharset } from '../Types'; /** * The character sets supported by the terminal. These enable several languages diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 0d491a69..f137131b 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -1,3 +1,8 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme } from 'xterm'; import { ITerminal } from '../Types'; import { Terminal as TerminalCore } from '../Terminal'; From 6a3ed43ad2cac03d0cc515dd87d931a375a9a39a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 10 Jun 2018 16:56:29 +0100 Subject: [PATCH 13/21] Move EscapeSequences into base --- src/InputHandler.ts | 3 ++- src/Parser.ts | 2 +- src/Terminal.ts | 6 +++--- src/{ => base/data}/EscapeSequences.ts | 0 src/core/input/Keyboard.ts | 2 +- src/handlers/AltClickHandler.ts | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) rename src/{ => base/data}/EscapeSequences.ts (100%) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 84276b5b..39ad6d06 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -5,12 +5,13 @@ */ import { CharData, IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer } from './Types'; -import { C0, C1 } from './EscapeSequences'; +import { C0, C1 } from './base/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets'; import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, DEFAULT_ATTR } from './Buffer'; import { FLAGS } from './renderer/Types'; import { wcwidth } from './CharWidth'; import { EscapeSequenceParser } from './EscapeSequenceParser'; +import { ICharset } from './core/Types'; /** * Map collect to glevel. Used in `selectCharset`. diff --git a/src/Parser.ts b/src/Parser.ts index 3675e1f0..f0ce93dc 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -4,7 +4,7 @@ * @license MIT */ -import { C0 } from './EscapeSequences'; +import { C0 } from './base/data/EscapeSequences'; import { IInputHandler, IInputHandlingTerminal } from './Types'; import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets'; diff --git a/src/Terminal.ts b/src/Terminal.ts index 02e7c3ed..bbbdad7f 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -21,7 +21,7 @@ * http://linux.die.net/man/7/urxvt */ -import { ICharset, IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, LineData } from './Types'; +import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, LineData } from './Types'; import { IMouseZoneManager } from './input/Types'; import { IRenderer } from './renderer/Types'; import { BufferSet } from './BufferSet'; @@ -30,7 +30,7 @@ import { CompositionHelper } from './CompositionHelper'; import { EventEmitter } from './EventEmitter'; import { Viewport } from './Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard'; -import { C0 } from './EscapeSequences'; +import { C0 } from './base/data/EscapeSequences'; import { InputHandler } from './InputHandler'; // import { Parser } from './Parser'; import { Renderer } from './renderer/Renderer'; @@ -52,7 +52,7 @@ import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; import { IKeyboardEvent } from './base/Types'; import { evaluateKeyboardEvent } from './core/input/Keyboard'; -import { KeyboardResultType } from './core/Types'; +import { KeyboardResultType, ICharset } from './core/Types'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; diff --git a/src/EscapeSequences.ts b/src/base/data/EscapeSequences.ts similarity index 100% rename from src/EscapeSequences.ts rename to src/base/data/EscapeSequences.ts diff --git a/src/core/input/Keyboard.ts b/src/core/input/Keyboard.ts index f4393276..a6482311 100644 --- a/src/core/input/Keyboard.ts +++ b/src/core/input/Keyboard.ts @@ -6,7 +6,7 @@ import { IKeyboardEvent } from '../../base/Types'; import { IKeyboardResult, KeyboardResultType } from '../Types'; -import { C0 } from '../../EscapeSequences'; +import { C0 } from '../../base/data/EscapeSequences'; // reg + shift key mappings for digits and special chars const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 8f1cf2df..b6cb0da1 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -4,7 +4,7 @@ */ import { ITerminal, ICircularList, LineData } from '../Types'; -import { C0 } from '../EscapeSequences'; +import { C0 } from '../base/data/EscapeSequences'; const enum Direction { UP = 'A', From 3705d8159dd212509983379ea98a83d44c3f59f5 Mon Sep 17 00:00:00 2001 From: Felipe Gasper Date: Mon, 11 Jun 2018 16:49:09 -0400 Subject: [PATCH 14/21] Add cPanel & WHM to the Real-World Uses list. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 30717086..1cfe2679 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ computational environment for Jupyter, supporting interactive data science and s - [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter. - [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js. - [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator. - +- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice. Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. From 8c3376911f3915865056de3cf8fac7c2552e824d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 12 Jun 2018 17:05:13 +0200 Subject: [PATCH 15/21] Fix winptyCompat compile --- src/addons/winptyCompat/winptyCompat.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/addons/winptyCompat/winptyCompat.ts b/src/addons/winptyCompat/winptyCompat.ts index 9cde62e0..84b21590 100644 --- a/src/addons/winptyCompat/winptyCompat.ts +++ b/src/addons/winptyCompat/winptyCompat.ts @@ -26,11 +26,11 @@ export function winptyCompatInit(terminal: Terminal): void { // Windows when text reaches the end of the terminal it's likely going to be // wrapped. addonTerminal.on('linefeed', () => { - const line = addonTerminal.buffer.lines.get(addonTerminal.buffer.ybase + addonTerminal.buffer.y - 1); + const line = addonTerminal._core.buffer.lines.get(addonTerminal._core.buffer.ybase + addonTerminal._core.buffer.y - 1); const lastChar = line[addonTerminal.cols - 1]; if (lastChar[3] !== 32 /* ' ' */) { - const nextLine = addonTerminal.buffer.lines.get(addonTerminal.buffer.ybase + addonTerminal.buffer.y); + const nextLine = addonTerminal._core.buffer.lines.get(addonTerminal._core.buffer.ybase + addonTerminal._core.buffer.y); (nextLine).isWrapped = true; } }); From bb4db719580fc621d7070e61b562fcac6f003b44 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 12 Jun 2018 17:07:43 +0200 Subject: [PATCH 16/21] Rename base/ to common/ --- src/InputHandler.ts | 2 +- src/Parser.ts | 2 +- src/Terminal.ts | 4 ++-- src/{base => common}/Types.ts | 0 src/{base => common}/data/EscapeSequences.ts | 0 src/core/input/Keyboard.ts | 4 ++-- src/handlers/AltClickHandler.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename src/{base => common}/Types.ts (100%) rename src/{base => common}/data/EscapeSequences.ts (100%) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 39ad6d06..9df24c7d 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -5,7 +5,7 @@ */ import { CharData, IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer } from './Types'; -import { C0, C1 } from './base/data/EscapeSequences'; +import { C0, C1 } from './common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets'; import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, DEFAULT_ATTR } from './Buffer'; import { FLAGS } from './renderer/Types'; diff --git a/src/Parser.ts b/src/Parser.ts index f0ce93dc..4086de19 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -4,7 +4,7 @@ * @license MIT */ -import { C0 } from './base/data/EscapeSequences'; +import { C0 } from './common/data/EscapeSequences'; import { IInputHandler, IInputHandlingTerminal } from './Types'; import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets'; diff --git a/src/Terminal.ts b/src/Terminal.ts index bbbdad7f..cd3f886d 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -30,7 +30,7 @@ import { CompositionHelper } from './CompositionHelper'; import { EventEmitter } from './EventEmitter'; import { Viewport } from './Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard'; -import { C0 } from './base/data/EscapeSequences'; +import { C0 } from './common/data/EscapeSequences'; import { InputHandler } from './InputHandler'; // import { Parser } from './Parser'; import { Renderer } from './renderer/Renderer'; @@ -50,7 +50,7 @@ import { ScreenDprMonitor } from './utils/ScreenDprMonitor'; import { ITheme, ILocalizableStrings, IMarker, IDisposable } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; -import { IKeyboardEvent } from './base/Types'; +import { IKeyboardEvent } from './common/Types'; import { evaluateKeyboardEvent } from './core/input/Keyboard'; import { KeyboardResultType, ICharset } from './core/Types'; diff --git a/src/base/Types.ts b/src/common/Types.ts similarity index 100% rename from src/base/Types.ts rename to src/common/Types.ts diff --git a/src/base/data/EscapeSequences.ts b/src/common/data/EscapeSequences.ts similarity index 100% rename from src/base/data/EscapeSequences.ts rename to src/common/data/EscapeSequences.ts diff --git a/src/core/input/Keyboard.ts b/src/core/input/Keyboard.ts index a6482311..cf290275 100644 --- a/src/core/input/Keyboard.ts +++ b/src/core/input/Keyboard.ts @@ -4,9 +4,9 @@ * @license MIT */ -import { IKeyboardEvent } from '../../base/Types'; +import { IKeyboardEvent } from '../../common/Types'; import { IKeyboardResult, KeyboardResultType } from '../Types'; -import { C0 } from '../../base/data/EscapeSequences'; +import { C0 } from '../../common/data/EscapeSequences'; // reg + shift key mappings for digits and special chars const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index b6cb0da1..344a5543 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -4,7 +4,7 @@ */ import { ITerminal, ICircularList, LineData } from '../Types'; -import { C0 } from '../base/data/EscapeSequences'; +import { C0 } from '../common/data/EscapeSequences'; const enum Direction { UP = 'A', From 57959249d819eca8991eb356ab1942063241d8a3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 12 Jun 2018 17:17:21 +0200 Subject: [PATCH 17/21] Resolve todos --- src/Terminal.ts | 9 --------- src/core/input/Keyboard.ts | 1 - 2 files changed, 10 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index cd3f886d..f57d3c11 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -747,14 +747,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II } } - /** - * Apply the provided addon on the `Terminal` class. - * @param addon The addon to apply. - */ - // public static applyAddon(addon: any): void { - // addon.apply(Terminal); - // } - /** * XTerm mouse events * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking @@ -1439,7 +1431,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II if (result.type === KeyboardResultType.SELECT_ALL) { this.selectAll(); - // TODO: Verify cancel behavior is the same as before } if (this._isThirdLevelShift(this.browser, event)) { diff --git a/src/core/input/Keyboard.ts b/src/core/input/Keyboard.ts index cf290275..8c6f3c59 100644 --- a/src/core/input/Keyboard.ts +++ b/src/core/input/Keyboard.ts @@ -348,7 +348,6 @@ export function evaluateKeyboardEvent( } else if (isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) { if (ev.keyCode === 65) { // cmd + a result.type = KeyboardResultType.SELECT_ALL; - // TODO: Select all in terminal side } } break; From ee935cc68c5628e3293638f3e46a47ab2e7726b3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 12 Jun 2018 17:27:36 +0200 Subject: [PATCH 18/21] Add a null check to charatlas beginFrame call Fixes #1511 --- src/renderer/TextRenderLayer.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index d6f96936..f48c2561 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -208,7 +208,9 @@ export class TextRenderLayer extends BaseRenderLayer { return; } - this._charAtlas.beginFrame(); + if (this._charAtlas) { + this._charAtlas.beginFrame(); + } this.clearCells(0, firstRow, terminal.cols, lastRow - firstRow + 1); this._drawBackground(terminal, firstRow, lastRow); From 72bb1b030a91f0a0ce4f4ccd0fb1a3da728584a7 Mon Sep 17 00:00:00 2001 From: Jon Kohler Date: Mon, 18 Jun 2018 10:05:40 -0400 Subject: [PATCH 19/21] Sort asending for companies in readme and add Nutanix Sorted the company list in the readme asending and added Nutanix to the list of users --- README.md | 71 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 1cfe2679..b5e8ccbd 100644 --- a/README.md +++ b/README.md @@ -110,48 +110,49 @@ Xterm.js works seamlessly in Electron apps and may even work on earlier versions ## Real-world uses Xterm.js is used in several world-class applications to provide great terminal experiences. -- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js -- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js -- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js -- [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies. -- [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. +- [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker. +- [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace. +- [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure. +- [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor. - [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams. - [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by `xterm.js`. -- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using `xterm.js`, socket.io, and ssh2. -- [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE. -- [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor. - [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses `xterm.js`. -- [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R. -- [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor. +- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice. +- [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter. +- [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. - [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy and run in the cloud. +- [**electerm**](http://electerm.html5beta.com): electerm is a terminal/ssh/sftp client(mac, win, linux) based on electron/node-pty/xterm. +- [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies. +- [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users +- [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator. +- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js. - [**Gravitational Teleport**](https://github.com/gravitational/teleport): Gravitational Teleport is a modern SSH server for remotely accessing clusters of Linux servers via SSH or HTTPS. - [**Hexlet**](https://en.hexlet.io): Practical programming courses (JavaScript, PHP, Unix, databases, functional programming). A steady path from the first line of code to the first job. -- [**Selenoid UI**](https://github.com/aerokube/selenoid-ui): Simple UI for the scallable golang implementation of Selenium Hub named Selenoid. We use XTerm for streaming logs over websockets from docker containers. -- [**Portainer**](https://portainer.io): Simple management UI for Docker. -- [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising `xterm.js`, SJCL & websockets. -- [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible -computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages. -- [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript. -- [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations. -- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell. -- [**Script Runner**](https://github.com/ioquatix/script-runner): Run scripts (or a shell) in Atom. -- [**Whack Whack Terminal**](https://github.com/Microsoft/WhackWhackTerminal): Terminal emulator for Visual Studio 2017. -- [**VTerm**](https://github.com/vterm/vterm): Extensible terminal emulator based on Electron and React. -- [**electerm**](http://electerm.html5beta.com): electerm is a terminal/ssh/sftp client(mac, win, linux) based on electron/node-pty/xterm. -- [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters. -- [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure. -- [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace. -- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server. -- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS -- [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker. -- [**Microsoft SQL Operations Studio**](https://github.com/Microsoft/sqlopsstudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux -- [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users -- [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies. - [**Hyper**](https://hyper.is): A terminal built on web technologies -- [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter. -- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js. -- [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator. -- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice. +- [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible +- [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies. +- [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters. +- [**Microsoft SQL Operations Studio**](https://github.com/Microsoft/sqlopsstudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux +- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js +- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js +- [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations. +- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS +- [**Portainer**](https://portainer.io): Simple management UI for Docker. +- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell. +- [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R. +- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server. +- [**Script Runner**](https://github.com/ioquatix/script-runner): Run scripts (or a shell) in Atom. +- [**Selenoid UI**](https://github.com/aerokube/selenoid-ui): Simple UI for the scallable golang implementation of Selenium Hub named Selenoid. We use XTerm for streaming logs over websockets from docker containers. +- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js +- [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE. +- [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising `xterm.js`, SJCL & websockets. +- [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor. +- [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript. +- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js +- [**VTerm**](https://github.com/vterm/vterm): Extensible terminal emulator based on Electron and React. +- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using `xterm.js`, socket.io, and ssh2. +- [**Whack Whack Terminal**](https://github.com/Microsoft/WhackWhackTerminal): Terminal emulator for Visual Studio 2017. +computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages. Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. From 628e648b57107c0caa2c5902418c882549b97be4 Mon Sep 17 00:00:00 2001 From: Jon Kohler Date: Mon, 18 Jun 2018 22:28:25 -0400 Subject: [PATCH 20/21] Update README.md Update readme and add note for future adders to append to the bottom of list --- README.md | 70 +++++++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index b5e8ccbd..1122c59c 100644 --- a/README.md +++ b/README.md @@ -110,51 +110,51 @@ Xterm.js works seamlessly in Electron apps and may even work on earlier versions ## Real-world uses Xterm.js is used in several world-class applications to provide great terminal experiences. -- [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker. -- [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace. -- [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure. -- [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor. +- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js +- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js +- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js +- [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies. +- [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. - [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams. - [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by `xterm.js`. +- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using `xterm.js`, socket.io, and ssh2. +- [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE. +- [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor. - [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses `xterm.js`. -- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice. -- [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter. -- [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. +- [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R. +- [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor. - [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy and run in the cloud. -- [**electerm**](http://electerm.html5beta.com): electerm is a terminal/ssh/sftp client(mac, win, linux) based on electron/node-pty/xterm. -- [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies. -- [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users -- [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator. -- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js. - [**Gravitational Teleport**](https://github.com/gravitational/teleport): Gravitational Teleport is a modern SSH server for remotely accessing clusters of Linux servers via SSH or HTTPS. - [**Hexlet**](https://en.hexlet.io): Practical programming courses (JavaScript, PHP, Unix, databases, functional programming). A steady path from the first line of code to the first job. -- [**Hyper**](https://hyper.is): A terminal built on web technologies -- [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible -- [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies. -- [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters. -- [**Microsoft SQL Operations Studio**](https://github.com/Microsoft/sqlopsstudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux -- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js -- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js -- [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations. -- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS -- [**Portainer**](https://portainer.io): Simple management UI for Docker. -- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell. -- [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R. -- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server. -- [**Script Runner**](https://github.com/ioquatix/script-runner): Run scripts (or a shell) in Atom. - [**Selenoid UI**](https://github.com/aerokube/selenoid-ui): Simple UI for the scallable golang implementation of Selenium Hub named Selenoid. We use XTerm for streaming logs over websockets from docker containers. -- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js -- [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE. +- [**Portainer**](https://portainer.io): Simple management UI for Docker. - [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising `xterm.js`, SJCL & websockets. -- [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor. -- [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript. -- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js -- [**VTerm**](https://github.com/vterm/vterm): Extensible terminal emulator based on Electron and React. -- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using `xterm.js`, socket.io, and ssh2. -- [**Whack Whack Terminal**](https://github.com/Microsoft/WhackWhackTerminal): Terminal emulator for Visual Studio 2017. +- [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages. +- [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript. +- [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations. +- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell. +- [**Script Runner**](https://github.com/ioquatix/script-runner): Run scripts (or a shell) in Atom. +- [**Whack Whack Terminal**](https://github.com/Microsoft/WhackWhackTerminal): Terminal emulator for Visual Studio 2017. +- [**VTerm**](https://github.com/vterm/vterm): Extensible terminal emulator based on Electron and React. +- [**electerm**](http://electerm.html5beta.com): electerm is a terminal/ssh/sftp client(mac, win, linux) based on electron/node-pty/xterm. +- [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters. +- [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure. +- [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace. +- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server. +- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS +- [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker. +- [**Microsoft SQL Operations Studio**](https://github.com/Microsoft/sqlopsstudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux +- [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users +- [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies. +- [**Hyper**](https://hyper.is): A terminal built on web technologies +- [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter. +- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js. +- [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator. +- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice. +- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js -Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. +Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. Note: Please add any new contributions to the end of the list only. ## Demo From 91cf9e1ba8817cf025e7d83e4e2098283857a294 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 20 Jun 2018 19:36:08 +1000 Subject: [PATCH 21/21] Move .strings to public/ --- src/Terminal.ts | 6 +----- src/public/Terminal.ts | 6 +++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index f57d3c11..06f9defc 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -47,7 +47,7 @@ import { DEFAULT_ANSI_COLORS } from './renderer/ColorManager'; import { MouseZoneManager } from './input/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ScreenDprMonitor } from './utils/ScreenDprMonitor'; -import { ITheme, ILocalizableStrings, IMarker, IDisposable } from 'xterm'; +import { ITheme, IMarker, IDisposable } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; import { IKeyboardEvent } from './common/Types'; @@ -328,10 +328,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II return this.buffers.active; } - public static get strings(): ILocalizableStrings { - return Strings; - } - /** * back_color_erase feature for xterm. */ diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index f137131b..76e7fba0 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -3,9 +3,10 @@ * @license MIT */ -import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme } from 'xterm'; +import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings } from 'xterm'; import { ITerminal } from '../Types'; import { Terminal as TerminalCore } from '../Terminal'; +import * as Strings from '../Strings'; export class Terminal implements ITerminalApi { private _core: ITerminal; @@ -141,4 +142,7 @@ export class Terminal implements ITerminalApi { public static applyAddon(addon: any): void { addon.apply(Terminal); } + public static get strings(): ILocalizableStrings { + return Strings; + } }