From 73a17cfacba896d8ffe750284d17e7a6338ad871 Mon Sep 17 00:00:00 2001 From: Corentin Surquin Date: Sat, 10 Aug 2019 01:07:43 +0200 Subject: [PATCH 01/56] Support fast scrolling using a modifier key --- demo/client.ts | 1 + src/browser/Viewport.ts | 28 +++++++++++++++++++++++++-- src/common/services/OptionsService.ts | 2 ++ src/common/services/Services.ts | 4 ++++ typings/xterm.d.ts | 10 ++++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 040292e4..88d32c0b 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -228,6 +228,7 @@ function initOptions(term: TerminalType): void { bellSound: null, bellStyle: ['none', 'sound'], cursorStyle: ['block', 'underline', 'bar'], + fastScrollModifier: ['alt', 'ctrl', 'shift'], fontFamily: null, fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 9625588d..94bb0fbf 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -7,7 +7,7 @@ import { Disposable } from 'common/Lifecycle'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { IColorSet, IViewport } from 'browser/Types'; import { ICharSizeService, IRenderService } from 'browser/services/Services'; -import { IBufferService } from 'common/services/Services'; +import { IBufferService, IOptionsService } from 'common/services/Services'; const FALLBACK_SCROLL_BAR_WIDTH = 15; @@ -37,6 +37,7 @@ export class Viewport extends Disposable implements IViewport { private readonly _viewportElement: HTMLElement, private readonly _scrollArea: HTMLElement, @IBufferService private readonly _bufferService: IBufferService, + @IOptionsService private readonly _optionsService: IOptionsService, @ICharSizeService private readonly _charSizeService: ICharSizeService, @IRenderService private readonly _renderService: IRenderService ) { @@ -174,8 +175,31 @@ export class Viewport extends Disposable implements IViewport { return 0; } + const modifier = this._optionsService.options.fastScrollModifier; + const sensitivity = this._optionsService.options.fastScrollSensitivity; + + // Multiply the scroll speed when the modifier is down + let multiplier = 1; + switch (modifier) { + case 'alt': + if (ev.altKey) { + multiplier = sensitivity; + } + break; + case 'ctrl': + if (ev.ctrlKey) { + multiplier = sensitivity; + } + break; + case 'shift': + if (ev.shiftKey) { + multiplier = sensitivity; + } + break; + } + // Fallback to WheelEvent.DOM_DELTA_PIXEL - let amount = ev.deltaY; + let amount = ev.deltaY * multiplier; if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { amount *= this._currentRowHeight; } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 9a5d2151..bde40c11 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -23,6 +23,8 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ bellSound: DEFAULT_BELL_SOUND, bellStyle: 'none', drawBoldTextInBrightColors: true, + fastScrollModifier: 'alt', + fastScrollSensitivity: 5, fontFamily: 'courier-new, courier, monospace', fontSize: 15, fontWeight: 'normal', diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 1af55e9a..184a6f9a 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -149,6 +149,8 @@ export interface IPartialTerminalOptions { cursorStyle?: 'block' | 'underline' | 'bar'; disableStdin?: boolean; drawBoldTextInBrightColors?: boolean; + fastScrollModifier?: 'alt' | 'ctrl' | 'shift'; + fastScrollSensitivity?: number; fontSize?: number; fontFamily?: string; fontWeight?: FontWeight; @@ -178,6 +180,8 @@ export interface ITerminalOptions { cursorStyle: 'block' | 'underline' | 'bar'; disableStdin: boolean; drawBoldTextInBrightColors: boolean; + fastScrollModifier: 'alt' | 'ctrl' | 'shift'; + fastScrollSensitivity: number; fontSize: number; fontFamily: string; fontWeight: FontWeight; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index d9e28b26..97bc4cf0 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -82,6 +82,16 @@ declare module 'xterm' { */ drawBoldTextInBrightColors?: boolean; + /** + * The modifier key hold to multiply scroll speed. + */ + fastScrollModifier?: 'alt' | 'ctrl' | 'shift'; + + /** + * The scroll speed multiplier used for fast scrolling. + */ + fastScrollSensitivity?: number; + /** * The font size used to render text. */ From a353a8cffe45dcba65fde15554c758b5fb6fe509 Mon Sep 17 00:00:00 2001 From: Corentin Surquin Date: Sat, 10 Aug 2019 01:23:45 +0200 Subject: [PATCH 02/56] Allow to disable fast scrolling --- demo/client.ts | 2 +- src/common/services/Services.ts | 2 +- typings/xterm.d.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 88d32c0b..c6f66dc0 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -228,7 +228,7 @@ function initOptions(term: TerminalType): void { bellSound: null, bellStyle: ['none', 'sound'], cursorStyle: ['block', 'underline', 'bar'], - fastScrollModifier: ['alt', 'ctrl', 'shift'], + fastScrollModifier: ['alt', 'ctrl', 'shift', undefined], fontFamily: null, fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 184a6f9a..8f4eb39e 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -180,7 +180,7 @@ export interface ITerminalOptions { cursorStyle: 'block' | 'underline' | 'bar'; disableStdin: boolean; drawBoldTextInBrightColors: boolean; - fastScrollModifier: 'alt' | 'ctrl' | 'shift'; + fastScrollModifier: 'alt' | 'ctrl' | 'shift' | undefined; fastScrollSensitivity: number; fontSize: number; fontFamily: string; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 97bc4cf0..9ea85d07 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -85,7 +85,7 @@ declare module 'xterm' { /** * The modifier key hold to multiply scroll speed. */ - fastScrollModifier?: 'alt' | 'ctrl' | 'shift'; + fastScrollModifier?: 'alt' | 'ctrl' | 'shift' | undefined; /** * The scroll speed multiplier used for fast scrolling. From 92c991132f67334099e9456810e9caec25a8617b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Oct 2019 07:55:48 -0700 Subject: [PATCH 03/56] Upgrade to typescript 3.6 --- package.json | 2 +- yarn.lock | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f52f7b79..c2672325 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "ts-loader": "^6.0.4", "tslint": "^5.18.0", "tslint-consistent-codestyle": "^1.13.0", - "typescript": "3.5", + "typescript": "3.6", "utf8": "^3.0.0", "webpack": "^4.35.3", "webpack-cli": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index 7b987a00..0019809a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4719,7 +4719,12 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.5, typescript@^3.5.1: +typescript@3.6: + version "3.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" + integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== + +typescript@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202" integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw== From 52e211915e2e292084a2005c8949c404187e8bc6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Oct 2019 08:02:58 -0700 Subject: [PATCH 04/56] Don't handle cmd+arrow Fixes #597 --- src/common/input/Keyboard.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index 2f54add6..e4ae3d23 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -113,6 +113,9 @@ export function evaluateKeyboardEvent( break; case 37: // left-arrow + if (ev.metaKey) { + break; + } if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D'; // HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards @@ -129,6 +132,9 @@ export function evaluateKeyboardEvent( break; case 39: // right-arrow + if (ev.metaKey) { + break; + } if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C'; // HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward @@ -145,6 +151,9 @@ export function evaluateKeyboardEvent( break; case 38: // up-arrow + if (ev.metaKey) { + break; + } if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A'; // HACK: Make Alt + up-arrow behave like Ctrl + up-arrow @@ -160,6 +169,9 @@ export function evaluateKeyboardEvent( break; case 40: // down-arrow + if (ev.metaKey) { + break; + } if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B'; // HACK: Make Alt + down-arrow behave like Ctrl + down-arrow From 22398c09279e07a968cef6b7b116f601bc628dff Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Oct 2019 09:42:20 -0700 Subject: [PATCH 05/56] v4.1.0 --- addons/xterm-addon-attach/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-attach/package.json b/addons/xterm-addon-attach/package.json index 5a5c5d75..5718f356 100644 --- a/addons/xterm-addon-attach/package.json +++ b/addons/xterm-addon-attach/package.json @@ -1,6 +1,6 @@ { "name": "xterm-addon-attach", - "version": "0.2.1", + "version": "0.3.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" diff --git a/package.json b/package.json index f52f7b79..03e5951f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "xterm", "description": "Full xterm terminal, in your browser", - "version": "4.0.0", + "version": "4.1.0", "main": "lib/xterm.js", "style": "css/xterm.css", "types": "typings/xterm.d.ts", From 52ae8fc14b333efb909f1f4566d53cff928b405a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Oct 2019 10:21:34 -0700 Subject: [PATCH 06/56] Throw when open is called on element not on DOM Fixes #1158 --- src/public/Terminal.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index b8a70ff7..70a77fd2 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -56,6 +56,9 @@ export class Terminal implements ITerminalApi { this._core.resize(columns, rows); } public open(parent: HTMLElement): void { + if (!document.body.contains(parent)) { + throw new Error('open must be called on an element that is attached to the DOM'); + } this._core.open(parent); } public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void { From 724bcc37661c5af1e6f24ab5aeaf847a4d5d2951 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Oct 2019 10:28:51 -0700 Subject: [PATCH 07/56] Generalize verify integers check Fixes #1416 --- src/public/Terminal.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index b8a70ff7..c177343a 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -178,8 +178,8 @@ export class Terminal implements ITerminalApi { private _verifyIntegers(...values: number[]): void { values.forEach(value => { - if (value % 1 !== 0) { - throw new Error('This API does not accept floating point numbers'); + if (value === Infinity || value === NaN || value % 1 !== 0) { + throw new Error('This API only accepts integers'); } }); } From 6f5e5215f2934ded80c018e1d8ac012c4b532c6a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 7 Oct 2019 14:27:29 -0700 Subject: [PATCH 08/56] Reveal search results in line just below viewport Fixes #2445 --- addons/xterm-addon-search/src/SearchAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 4d3e8841..5893f79f 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -344,7 +344,7 @@ export class SearchAddon implements ITerminalAddon { } terminal.select(result.col, result.row, result.term.length); // If it is not in the viewport then we scroll else it just gets selected - if (result.row > (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) { + if (result.row >= (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) { let scroll = result.row - terminal.buffer.viewportY; scroll = scroll - Math.floor(terminal.rows / 2); terminal.scrollLines(scroll); From 7fc57da85ab19e8963ab63b6c581f816562c3719 Mon Sep 17 00:00:00 2001 From: Sai Sandeep Vaddi Date: Mon, 7 Oct 2019 23:45:03 -0400 Subject: [PATCH 09/56] add ten hands to real-world uses list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 498278b8..3db91513 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**PHP App Server**](https://github.com/cubiclesoft/php-app-server/): Create lightweight, installable almost-native applications for desktop OSes. ExecTerminal (nicely wraps the xterm.js Terminal), TerminalManager, and RunProcessSDK are self-contained, reusable ES5+ compliant Javascript components. - [**NgTerminal**](https://github.com/qwefgh90/ng-terminal): NgTerminal is a web terminal that leverages xterm.js on Angular 7+. You can easily add it into your application by adding `` into your component. - [**tty-share**](https://tty-share.com): Extremely simple terminal sharing over the Internet. +- [**Ten Hands**](https://github.com/saisandeepvaddi/ten-hands): One place to run your command-line tasks. [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) From efbdbd3f3b37f6a8248dad1673c01572780165c6 Mon Sep 17 00:00:00 2001 From: JeffreyCA Date: Mon, 7 Oct 2019 22:25:47 -0700 Subject: [PATCH 10/56] Render non-block cursors as-is when terminal is unfocused --- .../src/renderLayer/CursorRenderLayer.ts | 9 +++++++-- src/renderer/CursorRenderLayer.ts | 9 +++++++-- src/renderer/dom/DomRenderer.ts | 6 +++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts index 6aed5f53..bc518ccc 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts @@ -139,12 +139,17 @@ export class CursorRenderLayer extends BaseRenderLayer { this._clearCursor(); this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; - this._renderBlurCursor(terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell); + const cursorStyle = terminal.getOption('cursorStyle') + if (cursorStyle && cursorStyle != 'block') { + this._cursorRenderers[cursorStyle](terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell); + } else { + this._renderBlurCursor(terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell); + } this._ctx.restore(); this._state.x = terminal.buffer.cursorX; this._state.y = viewportRelativeCursorY; this._state.isFocused = false; - this._state.style = terminal.getOption('cursorStyle'); + this._state.style = cursorStyle; this._state.width = this._cell.getWidth(); return; } diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index f847c5f6..fb3494d3 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -148,12 +148,17 @@ export class CursorRenderLayer extends BaseRenderLayer { this._clearCursor(); this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; - this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell); + const cursorStyle = this._optionsService.options.cursorStyle; + if (cursorStyle && cursorStyle != 'block') { + this._cursorRenderers[cursorStyle](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell); + } else { + this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell); + } this._ctx.restore(); this._state.x = this._bufferService.buffer.x; this._state.y = viewportRelativeCursorY; this._state.isFocused = false; - this._state.style = this._optionsService.options.cursorStyle; + this._state.style = cursorStyle; this._state.width = this._cell.getWidth(); return; } diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index d2c80235..ef927f20 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -183,7 +183,7 @@ export class DomRenderer extends Disposable implements IRenderer { `}`; // Cursor styles += - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + ` outline: 1px solid ${this._colors.cursor.css};` + ` outline-offset: -1px;` + `}` + @@ -197,10 +197,10 @@ export class DomRenderer extends Disposable implements IRenderer { ` background-color: ${this._colors.cursor.css};` + ` color: ${this._colors.cursorAccent.css};` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` + ` box-shadow: 1px 0 0 ${this._colors.cursor.css} inset;` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` + ` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` + `}`; // Selection From 1eb6e371879b0afe02f51b2743fbee7c8878bec5 Mon Sep 17 00:00:00 2001 From: JeffreyCA Date: Mon, 7 Oct 2019 22:35:02 -0700 Subject: [PATCH 11/56] Refresh terminal even if blinking is enabled so cursor changes are immediately visible --- .../xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts | 6 +++--- src/renderer/CursorRenderLayer.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts index bc518ccc..e73f7d71 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts @@ -92,10 +92,10 @@ export class CursorRenderLayer extends BaseRenderLayer { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.dispose(); } - // Request a refresh from the terminal as management of rendering is being - // moved back to the terminal - terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY); } + // Request a refresh from the terminal as management of rendering is being + // moved back to the terminal + terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY); } public onCursorMove(terminal: Terminal): void { diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index fb3494d3..b71e50e6 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -103,10 +103,10 @@ export class CursorRenderLayer extends BaseRenderLayer { this._cursorBlinkStateManager.dispose(); this._cursorBlinkStateManager = null; } - // Request a refresh from the terminal as management of rendering is being - // moved back to the terminal - this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); } + // Request a refresh from the terminal as management of rendering is being + // moved back to the terminal + this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y); } public onCursorMove(): void { From 7c982fc0ceda25723e9fddf1f1951d6155222526 Mon Sep 17 00:00:00 2001 From: JeffreyCA Date: Mon, 7 Oct 2019 22:51:03 -0700 Subject: [PATCH 12/56] Fix lint warnings --- addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts | 4 ++-- src/renderer/CursorRenderLayer.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts index e73f7d71..8a67526e 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts @@ -139,8 +139,8 @@ export class CursorRenderLayer extends BaseRenderLayer { this._clearCursor(); this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; - const cursorStyle = terminal.getOption('cursorStyle') - if (cursorStyle && cursorStyle != 'block') { + const cursorStyle = terminal.getOption('cursorStyle'); + if (cursorStyle && cursorStyle !== 'block') { this._cursorRenderers[cursorStyle](terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell); } else { this._renderBlurCursor(terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell); diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index b71e50e6..1f2791f1 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -149,7 +149,7 @@ export class CursorRenderLayer extends BaseRenderLayer { this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; const cursorStyle = this._optionsService.options.cursorStyle; - if (cursorStyle && cursorStyle != 'block') { + if (cursorStyle && cursorStyle !== 'block') { this._cursorRenderers[cursorStyle](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell); } else { this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell); From 821a7afc0143cf411fecffd0ff030d2a205479c8 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Thu, 10 Oct 2019 13:05:28 -0700 Subject: [PATCH 13/56] Added WebAssembly.sh --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3db91513..5cf2848f 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**NgTerminal**](https://github.com/qwefgh90/ng-terminal): NgTerminal is a web terminal that leverages xterm.js on Angular 7+. You can easily add it into your application by adding `` into your component. - [**tty-share**](https://tty-share.com): Extremely simple terminal sharing over the Internet. - [**Ten Hands**](https://github.com/saisandeepvaddi/ten-hands): One place to run your command-line tasks. +- [**WebAssembly.sh**](https://webassembly.sh): A WebAssembly WASI browser terminal [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) From da11e31fe52ccf209cf3026d38f79a43ae5eefae Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 11 Oct 2019 16:01:28 -0700 Subject: [PATCH 14/56] Don't use ctrl+up/down hack on macOS Fixes #2387 --- src/common/input/Keyboard.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts index e4ae3d23..1bf378c1 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -122,7 +122,7 @@ export function evaluateKeyboardEvent( // http://unix.stackexchange.com/a/108106 // macOS uses different escape sequences than linux if (result.key === C0.ESC + '[1;3D') { - result.key = isMac ? C0.ESC + 'b' : C0.ESC + '[1;5D'; + result.key = C0.ESC + (isMac ? 'b' : '[1;5D'); } } else if (applicationCursorMode) { result.key = C0.ESC + 'OD'; @@ -141,7 +141,7 @@ export function evaluateKeyboardEvent( // http://unix.stackexchange.com/a/108106 // macOS uses different escape sequences than linux if (result.key === C0.ESC + '[1;3C') { - result.key = isMac ? C0.ESC + 'f' : C0.ESC + '[1;5C'; + result.key = C0.ESC + (isMac ? 'f' : '[1;5C'); } } else if (applicationCursorMode) { result.key = C0.ESC + 'OC'; @@ -158,7 +158,8 @@ export function evaluateKeyboardEvent( 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') { + // macOS uses different escape sequences than linux + if (!isMac && result.key === C0.ESC + '[1;3A') { result.key = C0.ESC + '[1;5A'; } } else if (applicationCursorMode) { @@ -176,7 +177,8 @@ export function evaluateKeyboardEvent( 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') { + // macOS uses different escape sequences than linux + if (!isMac && result.key === C0.ESC + '[1;3B') { result.key = C0.ESC + '[1;5B'; } } else if (applicationCursorMode) { From 3d6815ca6f1e03471306f62255c0688ad7e5284c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 11 Oct 2019 16:05:54 -0700 Subject: [PATCH 15/56] Add tests for alt+up/down --- src/common/input/Keyboard.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/common/input/Keyboard.test.ts b/src/common/input/Keyboard.test.ts index 409a3192..a304923e 100644 --- a/src/common/input/Keyboard.test.ts +++ b/src/common/input/Keyboard.test.ts @@ -108,6 +108,12 @@ describe('Keyboard', () => { 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 \\x1b[5D for alt+up', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: false }).key, '\x1b[1;5A'); // CSI 5 D + }); + it('should return \\x1b[5C for alt+down', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: false }).key, '\x1b[1;5B'); // CSI 5 C + }); it('should return \\x1ba for alt+a', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: false }).key, '\x1ba'); }); @@ -120,6 +126,12 @@ describe('Keyboard', () => { it('should return \\x1bf for alt+right', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1bf'); // CSI 5 C }); + it('should return \\x1bb for alt+up', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: true }).key, '\x1b[1;3A'); // CSI 5 D + }); + it('should return \\x1bf for alt+down', () => { + assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: true }).key, '\x1b[1;3B'); // CSI 5 C + }); it('should return undefined for alt+a', () => { assert.strictEqual(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true }).key, undefined), { isMac: true }; }); From ff13f748586e0b43a91176a051a2f843b64a6630 Mon Sep 17 00:00:00 2001 From: Leonardo Bressan Motyczka Date: Mon, 14 Oct 2019 13:16:59 -0300 Subject: [PATCH 16/56] Add integration test for terminal dispose --- test/api/Terminal.api.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts index 034b04c4..edbdf8a3 100644 --- a/test/api/Terminal.api.ts +++ b/test/api/Terminal.api.ts @@ -510,6 +510,20 @@ describe('API Integration Tests', function(): void { }); }); }); + + it('dispose', async function(): Promise { + await page.evaluate(` + window.term = new Terminal(); + window.term.dispose(); + `); + assert.equal(await page.evaluate(`window.term._core._isDisposed`), true); + }); + + it('dispose (opened)', async function(): Promise { + await openTerminal(); + await page.evaluate(`window.term.dispose()`); + assert.equal(await page.evaluate(`window.term._core._isDisposed`), true); + }); }); async function openTerminal(options: ITerminalOptions = {}): Promise { From 040f9f49d91251735e0dc2a78cad1b6d783ba7c4 Mon Sep 17 00:00:00 2001 From: Jon Bockhorst Date: Mon, 14 Oct 2019 14:25:02 -0500 Subject: [PATCH 17/56] Support link position in hover tooltip callback --- src/browser/Linkifier.ts | 2 +- src/browser/Types.d.ts | 12 ++++++++++-- typings/xterm.d.ts | 27 ++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index 53a32aa7..d17de1ca 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -306,7 +306,7 @@ export class Linkifier implements ILinkifier { e => { this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); if (matcher.hoverTooltipCallback) { - matcher.hoverTooltipCallback(e, uri); + matcher.hoverTooltipCallback(e, uri, { startRow: y1, startColumn: x1, endRow: y2, endColumn: x2 }); } }, () => { diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index 894beb36..bd3d8722 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -43,14 +43,22 @@ export interface IViewport extends IDisposable { onThemeChange(colors: IColorSet): void; } +export interface ILinkLocation { + startColumn: number; + startRow: number; + endColumn: number; + endRow: number; +} + export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void; +export type LinkMatcherHoverTooltipCallback = (event: MouseEvent, uri: string, position: ILinkLocation) => void; export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void; export interface ILinkMatcher { id: number; regex: RegExp; handler: LinkMatcherHandler; - hoverTooltipCallback?: LinkMatcherHandler; + hoverTooltipCallback?: LinkMatcherHoverTooltipCallback; hoverLeaveCallback?: () => void; matchIndex?: number; validationCallback?: LinkMatcherValidationCallback; @@ -96,7 +104,7 @@ export interface ILinkMatcherOptions { /** * A callback that fires when the mouse hovers over a link. */ - tooltipCallback?: LinkMatcherHandler; + tooltipCallback?: LinkMatcherHoverTooltipCallback; /** * A callback that fires when the mouse leaves a link that was hovered. */ diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 44ef66bd..2ea8706a 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -269,7 +269,7 @@ declare module 'xterm' { /** * A callback that fires when the mouse hovers over a link for a moment. */ - tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void; + tooltipCallback?: (event: MouseEvent, uri: string, location: ILinkLocation) => boolean | void; /** * A callback that fires when the mouse leaves a link. Note that this can @@ -842,6 +842,31 @@ declare module 'xterm' { endRow: number; } + /** + * An object representing a link location within the terminal. + */ + interface ILinkLocation { + /** + * The start column of the link. + */ + startColumn: number; + + /** + * The start row of the link. + */ + startRow: number; + + /** + * The end column of the link. + */ + endColumn: number; + + /** + * The end row of the link. + */ + endRow: number; + } + /** * Represents a terminal buffer. */ From e76b8f86b5cfe434c0586d46238198d7b5ed7769 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 15 Oct 2019 16:21:17 -0700 Subject: [PATCH 18/56] Pull modifier code into helper, apply to lines scrolled --- src/browser/Viewport.ts | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 8747297a..bea5d99d 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -191,31 +191,9 @@ export class Viewport extends Disposable implements IViewport { return 0; } - const modifier = this._optionsService.options.fastScrollModifier; - const sensitivity = this._optionsService.options.fastScrollSensitivity; - - // Multiply the scroll speed when the modifier is down - let multiplier = 1; - switch (modifier) { - case 'alt': - if (ev.altKey) { - multiplier = sensitivity; - } - break; - case 'ctrl': - if (ev.ctrlKey) { - multiplier = sensitivity; - } - break; - case 'shift': - if (ev.shiftKey) { - multiplier = sensitivity; - } - break; - } // Fallback to WheelEvent.DOM_DELTA_PIXEL - let amount = ev.deltaY * multiplier; + let amount = this._applyFastScrollModifier(ev.deltaY, ev); if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { amount *= this._currentRowHeight; } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { @@ -236,7 +214,7 @@ export class Viewport extends Disposable implements IViewport { } // Fallback to WheelEvent.DOM_DELTA_LINE - let amount = ev.deltaY; + let amount = this._applyFastScrollModifier(ev.deltaY, ev); if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) { amount /= this._currentRowHeight + 0.0; // Prevent integer division this._wheelPartialScroll += amount; @@ -248,6 +226,17 @@ export class Viewport extends Disposable implements IViewport { return amount; } + private _applyFastScrollModifier(amount: number, ev: WheelEvent): number { + const modifier = this._optionsService.options.fastScrollModifier; + // Multiply the scroll speed when the modifier is down + if ((modifier === 'alt' && ev.altKey) || + (modifier === 'ctrl' && ev.ctrlKey) || + (modifier === 'shift' && ev.shiftKey)) { + return amount * this._optionsService.options.fastScrollSensitivity; + } + return amount; + } + /** * Handles the touchstart event, recording the touch occurred. * @param ev The touch event. From 9fdb674f2056258dfe784c9afd7e7d94cce8318e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 15 Oct 2019 16:22:09 -0700 Subject: [PATCH 19/56] Use min of 1 for fast scroll --- src/browser/Viewport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index bea5d99d..063fbc66 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -232,7 +232,7 @@ export class Viewport extends Disposable implements IViewport { if ((modifier === 'alt' && ev.altKey) || (modifier === 'ctrl' && ev.ctrlKey) || (modifier === 'shift' && ev.shiftKey)) { - return amount * this._optionsService.options.fastScrollSensitivity; + return amount * Math.max(1, this._optionsService.options.fastScrollSensitivity); } return amount; } From 4f8cda9f3a477e584eb04ee0053efd214df0e27a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 15 Oct 2019 16:22:41 -0700 Subject: [PATCH 20/56] Remove bad whitespace --- src/browser/Viewport.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 063fbc66..d200ef8c 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -191,7 +191,6 @@ export class Viewport extends Disposable implements IViewport { return 0; } - // Fallback to WheelEvent.DOM_DELTA_PIXEL let amount = this._applyFastScrollModifier(ev.deltaY, ev); if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { From cfe80228675155d93363b3a48b785d793e8ecaea Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 15 Oct 2019 16:29:13 -0700 Subject: [PATCH 21/56] Mark element and textarea APIs as | undefined Didn't fix all the issues in Terminal.ts as they will be covered in layering refactor Fixes #2471 --- addons/xterm-addon-fit/src/FitAddon.ts | 2 +- src/Terminal.ts | 2 +- src/Types.d.ts | 4 ++-- src/public/Terminal.ts | 4 ++-- typings/xterm.d.ts | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/xterm-addon-fit/src/FitAddon.ts b/addons/xterm-addon-fit/src/FitAddon.ts index f23bd161..e25a4783 100644 --- a/addons/xterm-addon-fit/src/FitAddon.ts +++ b/addons/xterm-addon-fit/src/FitAddon.ts @@ -49,7 +49,7 @@ export class FitAddon implements ITerminalAddon { return undefined; } - if (!this._terminal.element.parentElement) { + if (!this._terminal.element || !this._terminal.element.parentElement) { return undefined; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 63400bcb..3a2a1eb3 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -76,7 +76,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp /** * The HTMLElement that the terminal is created in, set by Terminal.open. */ - private _parent: HTMLElement; + private _parent: HTMLElement | null; private _document: Document; private _viewportScrollArea: HTMLElement; private _viewportElement: HTMLElement; diff --git a/src/Types.d.ts b/src/Types.d.ts index cb45d3b1..1b0285b1 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -173,7 +173,7 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc // Portions of the public API that are required by the internal Terminal export interface IPublicTerminal extends IDisposable { - textarea: HTMLTextAreaElement; + textarea: HTMLTextAreaElement | undefined; rows: number; cols: number; buffer: IBuffer; @@ -226,7 +226,7 @@ export interface IBufferAccessor { } export interface IElementAccessor { - readonly element: HTMLElement; + readonly element: HTMLElement | undefined; } export interface ILinkifierAccessor { diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index b8a70ff7..e9192fc9 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -33,14 +33,14 @@ export class Terminal implements ITerminalApi { public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; } public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; } - public get element(): HTMLElement { return this._core.element; } + public get element(): HTMLElement | undefined { return this._core.element; } public get parser(): IParser { if (!this._parser) { this._parser = new ParserApi(this._core); } return this._parser; } - public get textarea(): HTMLTextAreaElement { return this._core.textarea; } + public get textarea(): HTMLTextAreaElement | undefined { return this._core.textarea; } public get rows(): number { return this._core.rows; } public get cols(): number { return this._core.cols; } public get buffer(): IBufferApi { return new BufferApiView(this._core.buffer); } diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 44ef66bd..16c90099 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -352,12 +352,12 @@ declare module 'xterm' { /** * The element containing the terminal. */ - readonly element: HTMLElement; + readonly element: HTMLElement | undefined; /** * The textarea that accepts input for the terminal. */ - readonly textarea: HTMLTextAreaElement; + readonly textarea: HTMLTextAreaElement | undefined; /** * The number of rows in the terminal's viewport. Use From 1bc246323c32c2b9edd9df67916b3b9dd788319f Mon Sep 17 00:00:00 2001 From: Geraldo Neto Date: Tue, 15 Oct 2019 22:42:12 -0300 Subject: [PATCH 22/56] Add comma as a default word separator --- src/common/services/OptionsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index bde40c11..a9b0cc19 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -49,7 +49,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ screenKeys: false, cancelEvents: false, useFlowControl: false, - wordSeparator: ' ()[]{}\'"' + wordSeparator: ' ()[]{}\',"' }); /** From 740d261d7ac575be342b7ad31fb11ffca8177e89 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 16 Oct 2019 11:07:22 -0700 Subject: [PATCH 23/56] Protect against exception when clicking on mouse zones Still not sure how this happens but seems harmless to just not use the row as wrapped here. See microsoft/vscode#82309 --- src/browser/services/SelectionService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 2efb095d..3631bbb7 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -210,7 +210,7 @@ export class SelectionService implements ISelectionService { for (let i = start[1] + 1; i <= end[1] - 1; i++) { const bufferLine = buffer.lines.get(i); const lineText = buffer.translateBufferLineToString(i, true); - if (bufferLine!.isWrapped) { + if (bufferLine && bufferLine.isWrapped) { result[result.length - 1] += lineText; } else { result.push(lineText); @@ -221,7 +221,7 @@ export class SelectionService implements ISelectionService { if (start[1] !== end[1]) { const bufferLine = buffer.lines.get(end[1]); const lineText = buffer.translateBufferLineToString(end[1], true, 0, end[0]); - if (bufferLine!.isWrapped) { + if (bufferLine && bufferLine!.isWrapped) { result[result.length - 1] += lineText; } else { result.push(lineText); From 9f80ce25c20a3b430287f25d2dc310ed92615934 Mon Sep 17 00:00:00 2001 From: Geraldo Neto Date: Wed, 16 Oct 2019 19:23:50 -0300 Subject: [PATCH 24/56] Add : and ; to the word separator --- src/common/services/OptionsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index a9b0cc19..9230421c 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -49,7 +49,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ screenKeys: false, cancelEvents: false, useFlowControl: false, - wordSeparator: ' ()[]{}\',"' + wordSeparator: ' ()[]{}\',:;"' }); /** From 843ea1b61084e2402453a5e81b161e9dc15bef11 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 16 Oct 2019 15:41:47 -0700 Subject: [PATCH 25/56] Fix scrollback going missing when increase size on Windows This also forces the scroll bar to sync after a resize early, this may fix other bugs. Fixes #2459 --- src/Terminal.ts | 4 ++++ src/browser/Types.d.ts | 2 +- src/browser/Viewport.ts | 22 ++++++++++++++-------- src/common/buffer/Buffer.ts | 30 ++++++++++++++++++------------ 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 3a2a1eb3..852d424d 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1469,6 +1469,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._charSizeService.measure(); } + // Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an + // invalid location + this.viewport.syncScrollArea(true); + this.refresh(0, this.rows - 1); this._onResize.fire({ cols: x, rows: y }); } diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index 894beb36..692d6b03 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -35,7 +35,7 @@ export interface IPartialColorSet { export interface IViewport extends IDisposable { scrollBarWidth: number; - syncScrollArea(): void; + syncScrollArea(immediate?: boolean): void; getLinesScrolled(ev: WheelEvent): number; onWheel(ev: WheelEvent): boolean; onTouchStart(ev: TouchEvent): void; diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index d200ef8c..4817ab3b 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -61,7 +61,14 @@ export class Viewport extends Disposable implements IViewport { * Refreshes row height, setting line-height, viewport height and scroll area height if * necessary. */ - private _refresh(): void { + private _refresh(immediate: boolean): void { + if (immediate) { + this._innerRefresh(); + if (this._refreshAnimationFrame !== null) { + cancelAnimationFrame(this._refreshAnimationFrame); + } + return; + } if (this._refreshAnimationFrame === null) { this._refreshAnimationFrame = requestAnimationFrame(() => this._innerRefresh()); } @@ -89,40 +96,39 @@ export class Viewport extends Disposable implements IViewport { this._refreshAnimationFrame = null; } - /** * Updates dimensions and synchronizes the scroll area if necessary. */ - public syncScrollArea(): void { + public syncScrollArea(immediate: boolean = false): void { // If buffer height changed if (this._lastRecordedBufferLength !== this._bufferService.buffer.lines.length) { this._lastRecordedBufferLength = this._bufferService.buffer.lines.length; - this._refresh(); + this._refresh(immediate); return; } // If viewport height changed if (this._lastRecordedViewportHeight !== this._renderService.dimensions.canvasHeight) { - this._refresh(); + this._refresh(immediate); return; } // If the buffer position doesn't match last scroll top const newScrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight; if (this._lastScrollTop !== newScrollTop) { - this._refresh(); + this._refresh(immediate); return; } // If element's scroll top changed, this can happen when hiding the element if (this._lastScrollTop !== this._viewportElement.scrollTop) { - this._refresh(); + this._refresh(immediate); return; } // If row height changed if (this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) { - this._refresh(); + this._refresh(immediate); return; } } diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index 1b3e5d86..152bab7a 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -167,19 +167,25 @@ export class Buffer implements IBuffer { if (this._rows < newRows) { for (let y = this._rows; y < newRows; y++) { if (this.lines.length < newRows + this.ybase) { - if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { - // There is room above the buffer and there are no empty elements below the line, - // scroll up - this.ybase--; - addToY++; - if (this.ydisp > 0) { - // Viewport is at the top of the buffer, must increase downwards - this.ydisp--; - } - } else { - // Add a blank line if there is no buffer left at the top to scroll to, or if there - // are blank lines after the cursor + if (this._optionsService.options.windowsMode) { + // Just add the new missing rows on Windows as conpty reprints the screen with it's + // view of the world. Once a line enters scrollback for conpty it remains there this.lines.push(new BufferLine(newCols, nullCell)); + } else { + if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { + // There is room above the buffer and there are no empty elements below the line, + // scroll up + this.ybase--; + addToY++; + if (this.ydisp > 0) { + // Viewport is at the top of the buffer, must increase downwards + this.ydisp--; + } + } else { + // Add a blank line if there is no buffer left at the top to scroll to, or if there + // are blank lines after the cursor + this.lines.push(new BufferLine(newCols, nullCell)); + } } } } From e8dc563ccfd3caeb0d8da9d7e605517799c4eb06 Mon Sep 17 00:00:00 2001 From: Leonardo Bressan Motyczka Date: Wed, 16 Oct 2019 20:05:45 -0300 Subject: [PATCH 26/56] Include puppeteer deps on devcontainer --- .devcontainer/Dockerfile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4d4e605b..e5736562 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -8,12 +8,19 @@ RUN apt-get update \ # Verify git and process tools are installed RUN apt-get install -y git procps -# Install yarn +# Install yarn, puppeteer deps RUN apt-get install -y curl apt-transport-https lsb-release \ && curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \ && echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ && apt-get update \ - && apt-get -y install --no-install-recommends yarn + && apt-get -y install --no-install-recommends \ + yarn fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \ + # https://github.com/Googlechrome/puppeteer/issues/290#issuecomment-322921352 + gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \ + libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \ + libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \ + libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \ + ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget # Clean up RUN apt-get autoremove -y \ From 3d500ba802343153060c7e9405855378bbb91f15 Mon Sep 17 00:00:00 2001 From: Leonardo Bressan Motyczka Date: Wed, 16 Oct 2019 20:07:56 -0300 Subject: [PATCH 27/56] Launch puppeteer without sandbox --- addons/xterm-addon-attach/src/AttachAddon.api.ts | 2 +- addons/xterm-addon-search/src/SearchAddon.api.ts | 2 +- addons/xterm-addon-web-links/src/WebLinksAddon.api.ts | 2 +- addons/xterm-addon-webgl/src/WebglRenderer.api.ts | 2 +- test/api/CharWidth.api.ts | 2 +- test/api/InputHandler.api.ts | 2 +- test/api/MouseTracking.api.ts | 2 +- test/api/Parser.api.ts | 2 +- test/api/Terminal.api.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/xterm-addon-attach/src/AttachAddon.api.ts b/addons/xterm-addon-attach/src/AttachAddon.api.ts index c5b2d858..94d11139 100644 --- a/addons/xterm-addon-attach/src/AttachAddon.api.ts +++ b/addons/xterm-addon-attach/src/AttachAddon.api.ts @@ -21,7 +21,7 @@ describe('AttachAddon', () => { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, - args: [`--window-size=${width},${height}`] + args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index 14e12a8c..cb5a02b8 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -21,7 +21,7 @@ describe('Search Tests', function (): void { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, - args: [`--window-size=${width},${height}`] + args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); diff --git a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts index 2c0044be..3fb1a536 100644 --- a/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts +++ b/addons/xterm-addon-web-links/src/WebLinksAddon.api.ts @@ -20,7 +20,7 @@ describe('WebLinksAddon', () => { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, - args: [`--window-size=${width},${height}`] + args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index 66be22d9..9303782d 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -22,7 +22,7 @@ describe('WebGL Renderer Integration Tests', function(): void { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, - args: [`--window-size=${width},${height}`] + args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); diff --git a/test/api/CharWidth.api.ts b/test/api/CharWidth.api.ts index 035208cc..0561bf07 100644 --- a/test/api/CharWidth.api.ts +++ b/test/api/CharWidth.api.ts @@ -21,7 +21,7 @@ describe('CharWidth Integration Tests', function(): void { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, - args: [`--window-size=${width},${height}`] + args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); diff --git a/test/api/InputHandler.api.ts b/test/api/InputHandler.api.ts index 05b16010..fe068610 100644 --- a/test/api/InputHandler.api.ts +++ b/test/api/InputHandler.api.ts @@ -21,7 +21,7 @@ describe('InputHandler Integration Tests', function(): void { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, - args: [`--window-size=${width},${height}`] + args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); diff --git a/test/api/MouseTracking.api.ts b/test/api/MouseTracking.api.ts index 90c18f75..602d630c 100644 --- a/test/api/MouseTracking.api.ts +++ b/test/api/MouseTracking.api.ts @@ -216,7 +216,7 @@ describe('Mouse Tracking Tests', function(): void { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, - args: [`--window-size=${width},${height}`] + args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); diff --git a/test/api/Parser.api.ts b/test/api/Parser.api.ts index 28f7d8b3..e46242ab 100644 --- a/test/api/Parser.api.ts +++ b/test/api/Parser.api.ts @@ -21,7 +21,7 @@ describe('Parser Integration Tests', function(): void { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, - args: [`--window-size=${width},${height}`] + args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts index edbdf8a3..08963d9c 100644 --- a/test/api/Terminal.api.ts +++ b/test/api/Terminal.api.ts @@ -21,7 +21,7 @@ describe('API Integration Tests', function(): void { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, - args: [`--window-size=${width},${height}`] + args: [`--window-size=${width},${height}`, `--no-sandbox`] }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); From 07308485a4957896ad942987b4750dbaecfa6e97 Mon Sep 17 00:00:00 2001 From: Leonardo Bressan Motyczka Date: Wed, 16 Oct 2019 20:08:57 -0300 Subject: [PATCH 28/56] Update MouseTracking puppeteer window size This allows all tests to successfully run in devcontainer --- test/api/MouseTracking.api.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/api/MouseTracking.api.ts b/test/api/MouseTracking.api.ts index 602d630c..1372b567 100644 --- a/test/api/MouseTracking.api.ts +++ b/test/api/MouseTracking.api.ts @@ -11,8 +11,10 @@ const APP = 'http://127.0.0.1:3000/test'; let browser: puppeteer.Browser; let page: puppeteer.Page; -const width = 1024; -const height = 768; +// adjusted to work inside devcontainer +// see https://github.com/xtermjs/xterm.js/issues/2379 +const width = 1280; +const height = 960; // adjust terminal row/col size so we can test // >80 up to 223 and >255 From 990be060f2d4124bfa2e50b56d7f7194a8119951 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 17 Oct 2019 10:00:45 -0700 Subject: [PATCH 29/56] Allow builds to specify a force release var This will let us automate patch releases by queuing a manual build Fixes #2154 --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 26f0ca73..b1ccdbf0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -107,7 +107,7 @@ jobs: - Windows - Linux_IntegrationTests - macOS_IntegrationTests - condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['Build.SourceBranch'], 'refs/heads/release/*'))) + condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['FORCE_RELEASE'], 'true'))) pool: vmImage: 'ubuntu-16.04' steps: From 08475eb4954c1fff67ea0ab79bb21cd798d7ddfd Mon Sep 17 00:00:00 2001 From: Jon Bockhorst Date: Thu, 17 Oct 2019 12:25:42 -0500 Subject: [PATCH 30/56] Update API to use IViewportRange and IViewportCellPosition --- src/browser/Linkifier.ts | 2 +- src/browser/Types.d.ts | 15 +++++++++------ typings/xterm.d.ts | 33 +++++++++++++++++++-------------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index d17de1ca..0b51d43f 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -306,7 +306,7 @@ export class Linkifier implements ILinkifier { e => { this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); if (matcher.hoverTooltipCallback) { - matcher.hoverTooltipCallback(e, uri, { startRow: y1, startColumn: x1, endRow: y2, endColumn: x2 }); + matcher.hoverTooltipCallback(e, uri, { start: { row: y1, col: x1 }, end: { row: y2, col: x2 } }); } }, () => { diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index bd3d8722..2b9a4a9b 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -43,15 +43,18 @@ export interface IViewport extends IDisposable { onThemeChange(colors: IColorSet): void; } -export interface ILinkLocation { - startColumn: number; - startRow: number; - endColumn: number; - endRow: number; +export interface IViewportRange { + start: IViewportCellPosition; + end: IViewportCellPosition; +} + +export interface IViewportCellPosition { + col: number; + row: number; } export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void; -export type LinkMatcherHoverTooltipCallback = (event: MouseEvent, uri: string, position: ILinkLocation) => void; +export type LinkMatcherHoverTooltipCallback = (event: MouseEvent, uri: string, position: IViewportRange) => void; export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void; export interface ILinkMatcher { diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 2ea8706a..32dce910 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -269,7 +269,7 @@ declare module 'xterm' { /** * A callback that fires when the mouse hovers over a link for a moment. */ - tooltipCallback?: (event: MouseEvent, uri: string, location: ILinkLocation) => boolean | void; + tooltipCallback?: (event: MouseEvent, uri: string, location: IViewportRange) => boolean | void; /** * A callback that fires when the mouse leaves a link. Note that this can @@ -843,28 +843,33 @@ declare module 'xterm' { } /** - * An object representing a link location within the terminal. + * An object representing a range within the viewport of the terminal. */ - interface ILinkLocation { + interface IViewportRange { /** - * The start column of the link. + * The start cell of the range. */ - startColumn: number; + start: IViewportCellPosition; /** - * The start row of the link. + * The end cell of the range. */ - startRow: number; + end: IViewportCellPosition; + } + + /** + * An object representing a cell within the viewport of the terminal. + */ + interface IViewportCellPosition { + /** + * The column of the cell. + */ + col: number; /** - * The end column of the link. + * The row of the cell. */ - endColumn: number; - - /** - * The end row of the link. - */ - endRow: number; + row: number; } /** From 57581ca4ef4b80794eeed996c2b016e954429687 Mon Sep 17 00:00:00 2001 From: Jon Bockhorst Date: Thu, 17 Oct 2019 12:40:32 -0500 Subject: [PATCH 31/56] Updated IViewportCellPosition wording --- typings/xterm.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 32dce910..e7b2247e 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -858,7 +858,7 @@ declare module 'xterm' { } /** - * An object representing a cell within the viewport of the terminal. + * An object representing a cell position within the viewport of the terminal. */ interface IViewportCellPosition { /** From caf075dcb00f7a785fe763bcabd461e374cd741c Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Oct 2019 11:54:32 -0700 Subject: [PATCH 32/56] Create a PR for website update after a release --- bin/publish.js | 20 +++++++++++++++++++- bin/update-website.sh | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 bin/update-website.sh diff --git a/bin/publish.js b/bin/publish.js index d95ecc57..9d58ed15 100644 --- a/bin/publish.js +++ b/bin/publish.js @@ -5,6 +5,7 @@ const cp = require('child_process'); const fs = require('fs'); +const os = require('os'); const path = require('path'); // Setup auth @@ -18,8 +19,9 @@ if (isDryRun) { const changedFiles = getChangedFilesInCommit('HEAD'); // Publish xterm if any files were changed outside of the addons directory +let isStableRelease = false; if (changedFiles.some(e => e.search(/^addons\//) === -1)) { - checkAndPublishPackage(path.resolve(__dirname, '..')); + isStableRelease = checkAndPublishPackage(path.resolve(__dirname, '..')); } // Publish addons if any files were changed inside of the addon @@ -39,6 +41,11 @@ addonPackageDirs.forEach(p => { } }); +// Publish website if it's a stable release +if (isStableRelease) { + updateWebsite(); +} + function checkAndPublishPackage(packageDir) { const packageJson = require(path.join(packageDir, 'package.json')); @@ -76,6 +83,8 @@ function checkAndPublishPackage(packageDir) { } console.groupEnd(); + + return isStableRelease; } function getNextBetaVersion(packageJson) { @@ -115,3 +124,12 @@ function getChangedFilesInCommit(commit) { const changedFiles = output.split('\n').filter(e => e.length > 0); return changedFiles; } + +function updateWebsite() { + console.log('Updating website'); + const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'website-')); + const packageJson = require(path.join(path.resolve(__dirname, '..'), 'package.json')); + if (!isDryRun) { + cp.spawnSync('sh', [path.join(__dirname, 'update-website.sh'), packageJson.version], { cwd, stdio: [process.stdin, process.stdout, process.stderr] }); + } +} diff --git a/bin/update-website.sh b/bin/update-website.sh new file mode 100644 index 00000000..4379d915 --- /dev/null +++ b/bin/update-website.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +# Name the arguments +VERSION=$1 + +# Clone docs repo and update the documentation +git clone https://github.com/xtermjs/xtermjs.org +cd xtermjs.org +yarn +./bin/update-docs + +# Add changes to index and only proceed if there are changes to commit +touch test-file +git add . +if ! git diff-index --quiet HEAD --; then + + # Delete the upstream branch if it exists for some reason + export BRANCH_NAME=update-$VERSION + git branch -D $BRANCH_NAME || true + git push origin :$BRANCH_NAME || true + + # Create commit and push it to update-x.y.z + git checkout -b $BRANCH_NAME + git config --global user.name Daniel Imms + git config --global user.email tyriar@tyriar.com + git commit -m 'Update docs for v$VERSION' + git push --set-upstream origin update-4.2.0 + git push -f + + # Create a PR in the GitHub repo + curl \ + -H "Authorization: token $GITHUB_TOKEN" \ + -X POST \ + -d "{\"title\":\"Update docs for v$VERSION\",\"base\":\"master\",\"head\":\"xtermjs:$BRANCH_NAME\"}" \ + https://api.github.com/repos/xtermjs/xtermjs.org/pulls + +else + + echo "No changes to commit" + +fi From 83508048585ec5c7d5322c27d9d2c9f7d3949d86 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Oct 2019 12:36:03 -0700 Subject: [PATCH 33/56] Adjust to use 1-based, note in API --- src/browser/Linkifier.ts | 4 +++- typings/xterm.d.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index 0b51d43f..ecaf8af9 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -306,7 +306,9 @@ export class Linkifier implements ILinkifier { e => { this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); if (matcher.hoverTooltipCallback) { - matcher.hoverTooltipCallback(e, uri, { start: { row: y1, col: x1 }, end: { row: y2, col: x2 } }); + // Note that IViewportRange use 1-based coordinates to align with escape sequences such + // as CUP which use 1,1 as the default for row/col + matcher.hoverTooltipCallback(e, uri, { start: { row: y1 + 1, col: x1 + 1 }, end: { row: y2 + 1, col: x2 } }); } }, () => { diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index e7b2247e..c1f5eab9 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -862,12 +862,12 @@ declare module 'xterm' { */ interface IViewportCellPosition { /** - * The column of the cell. + * The column of the cell. Note that this is 1-based; the first column is column 1. */ col: number; /** - * The row of the cell. + * The row of the cell. Note that this is 1-based; the first row is row 1. */ row: number; } From 44a4bd788f83338891265d133a30de915fc65938 Mon Sep 17 00:00:00 2001 From: Leonardo Bressan Motyczka Date: Fri, 18 Oct 2019 14:48:56 -0300 Subject: [PATCH 34/56] Adjust proposeDimensions to behave like resize --- addons/xterm-addon-fit/src/FitAddon.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-fit/src/FitAddon.ts b/addons/xterm-addon-fit/src/FitAddon.ts index e25a4783..ca7e24b1 100644 --- a/addons/xterm-addon-fit/src/FitAddon.ts +++ b/addons/xterm-addon-fit/src/FitAddon.ts @@ -17,6 +17,9 @@ interface ITerminalDimensions { cols: number; } +const MINIMUM_COLS = 2; +const MINIMUM_ROWS = 1; + export class FitAddon implements ITerminalAddon { private _terminal: Terminal | undefined; @@ -71,8 +74,8 @@ export class FitAddon implements ITerminalAddon { const availableHeight = parentElementHeight - elementPaddingVer; const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth; const geometry = { - cols: Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth), - rows: Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight) + cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)), + rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight)) }; return geometry; } From a92635b22c82eda268b2b555131acb10ca99c506 Mon Sep 17 00:00:00 2001 From: Leonardo Bressan Motyczka Date: Fri, 18 Oct 2019 14:49:09 -0300 Subject: [PATCH 35/56] Introduce FitAddon tests --- addons/xterm-addon-fit/src/FitAddon.api.ts | 116 +++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 addons/xterm-addon-fit/src/FitAddon.api.ts diff --git a/addons/xterm-addon-fit/src/FitAddon.api.ts b/addons/xterm-addon-fit/src/FitAddon.api.ts new file mode 100644 index 00000000..49d274bc --- /dev/null +++ b/addons/xterm-addon-fit/src/FitAddon.api.ts @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import * as puppeteer from 'puppeteer'; +import { assert } from 'chai'; +import { ITerminalOptions } from 'xterm'; + +const APP = 'http://127.0.0.1:3000/test'; + +let browser: puppeteer.Browser; +let page: puppeteer.Page; +const width = 1024; +const height = 768; + +describe('FitAddon', () => { + before(async function(): Promise { + this.timeout(20000); + browser = await puppeteer.launch({ + headless: process.argv.indexOf('--headless') !== -1, + slowMo: 80, + args: [`--window-size=${width},${height}`, `--no-sandbox`] + }); + page = (await browser.pages())[0]; + await page.setViewport({ width, height }); + }); + + after(async () => { + await browser.close(); + }); + + beforeEach(async function(): Promise { + this.timeout(20000); + await page.goto(APP); + }); + + describe('proposeDimensions', () => { + it('no terminal', async function(): Promise { + await page.evaluate(`window.fit = new FitAddon();`); + assert.equal(await page.evaluate(`window.fit.proposeDimensions()`), undefined); + }); + + it('default', async function(): Promise { + await openTerminal(); + await loadFit(); + assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), { + cols: 87, + rows: 26 + }); + }); + + it('width', async function(): Promise { + await openTerminal(); + await loadFit(1008); + assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), { + cols: 110, + rows: 26 + }); + }); + + it('small', async function(): Promise { + await openTerminal(); + await loadFit(1, 1); + assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), { + cols: 2, + rows: 1 + }); + }); + }); + + describe('fit', () => { + it('default', async function(): Promise { + await openTerminal(); + await loadFit(); + await page.evaluate(`window.fit.fit()`); + assert.equal(await page.evaluate(`window.term.cols`), 87); + assert.equal(await page.evaluate(`window.term.rows`), 26); + }); + + it('width', async function(): Promise { + await openTerminal(); + await loadFit(1008); + await page.evaluate(`window.fit.fit()`); + assert.equal(await page.evaluate(`window.term.cols`), 110); + assert.equal(await page.evaluate(`window.term.rows`), 26); + }); + + it('small', async function(): Promise { + await openTerminal(); + await loadFit(1, 1); + await page.evaluate(`window.fit.fit()`); + assert.equal(await page.evaluate(`window.term.cols`), 2); + assert.equal(await page.evaluate(`window.term.rows`), 1); + }); + }); +}); + +async function loadFit(width: number = 800, height: number = 450): Promise { + await page.evaluate(` + window.fit = new FitAddon(); + window.term.loadAddon(window.fit); + document.querySelector('#terminal-container').style.width='${width}px'; + document.querySelector('#terminal-container').style.height='${height}px'; + `); +} + +async function openTerminal(options: ITerminalOptions = {}): Promise { + await page.evaluate(`window.term = new Terminal(${JSON.stringify(options)})`); + await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); + if (options.rendererType === 'dom') { + await page.waitForSelector('.xterm-rows'); + } else { + await page.waitForSelector('.xterm-text-layer'); + } +} From f8a62a23e59d8a186b5f7cfc1389b1e3d2a2dccb Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 18 Oct 2019 11:23:11 -0700 Subject: [PATCH 36/56] Speed tests up by avoiding page loads --- addons/xterm-addon-fit/src/FitAddon.api.ts | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/addons/xterm-addon-fit/src/FitAddon.api.ts b/addons/xterm-addon-fit/src/FitAddon.api.ts index 49d274bc..f9972aef 100644 --- a/addons/xterm-addon-fit/src/FitAddon.api.ts +++ b/addons/xterm-addon-fit/src/FitAddon.api.ts @@ -14,7 +14,7 @@ let page: puppeteer.Page; const width = 1024; const height = 768; -describe('FitAddon', () => { +describe.only('FitAddon', () => { before(async function(): Promise { this.timeout(20000); browser = await puppeteer.launch({ @@ -24,25 +24,25 @@ describe('FitAddon', () => { }); page = (await browser.pages())[0]; await page.setViewport({ width, height }); + await page.goto(APP); + await openTerminal(); }); after(async () => { await browser.close(); }); - beforeEach(async function(): Promise { - this.timeout(20000); - await page.goto(APP); + it('no terminal', async function(): Promise { + await page.evaluate(`window.fit = new FitAddon();`); + assert.equal(await page.evaluate(`window.fit.proposeDimensions()`), undefined); }); describe('proposeDimensions', () => { - it('no terminal', async function(): Promise { - await page.evaluate(`window.fit = new FitAddon();`); - assert.equal(await page.evaluate(`window.fit.proposeDimensions()`), undefined); + afterEach(async () => { + return unloadFit(); }); it('default', async function(): Promise { - await openTerminal(); await loadFit(); assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), { cols: 87, @@ -51,7 +51,6 @@ describe('FitAddon', () => { }); it('width', async function(): Promise { - await openTerminal(); await loadFit(1008); assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), { cols: 110, @@ -60,7 +59,6 @@ describe('FitAddon', () => { }); it('small', async function(): Promise { - await openTerminal(); await loadFit(1, 1); assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), { cols: 2, @@ -70,8 +68,11 @@ describe('FitAddon', () => { }); describe('fit', () => { + afterEach(async () => { + return unloadFit(); + }); + it('default', async function(): Promise { - await openTerminal(); await loadFit(); await page.evaluate(`window.fit.fit()`); assert.equal(await page.evaluate(`window.term.cols`), 87); @@ -79,7 +80,6 @@ describe('FitAddon', () => { }); it('width', async function(): Promise { - await openTerminal(); await loadFit(1008); await page.evaluate(`window.fit.fit()`); assert.equal(await page.evaluate(`window.term.cols`), 110); @@ -87,7 +87,6 @@ describe('FitAddon', () => { }); it('small', async function(): Promise { - await openTerminal(); await loadFit(1, 1); await page.evaluate(`window.fit.fit()`); assert.equal(await page.evaluate(`window.term.cols`), 2); @@ -105,6 +104,10 @@ async function loadFit(width: number = 800, height: number = 450): Promise `); } +async function unloadFit(): Promise { + await page.evaluate(`window.fit.dispose();`); +} + async function openTerminal(options: ITerminalOptions = {}): Promise { await page.evaluate(`window.term = new Terminal(${JSON.stringify(options)})`); await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); From f445bc763b2584f74e46d47fcac496114a838bba Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Fri, 18 Oct 2019 13:51:17 -0400 Subject: [PATCH 37/56] Adds scrollSensitivity option for scrolling speed --- demo/client.ts | 4 ++-- src/browser/Viewport.ts | 11 ++++++----- src/common/services/OptionsService.ts | 7 +++++++ src/common/services/Services.ts | 2 ++ typings/xterm.d.ts | 5 +++++ 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index e05a7105..7942cffe 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -254,7 +254,7 @@ function initOptions(term: TerminalType): void { }); html += '
'; numberOptions.forEach(o => { - html += `
`; + html += `
`; }); html += '
'; Object.keys(stringOptions).forEach(o => { @@ -283,7 +283,7 @@ function initOptions(term: TerminalType): void { console.log('change', o, input.value); if (o === 'cols' || o === 'rows') { updateTerminalSize(); - } else if (o === 'lineHeight') { + } else if (o === 'lineHeight' || o === 'scrollSensitivity') { term.setOption(o, parseFloat(input.value)); updateTerminalSize(); } else { diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 4817ab3b..5edd7b80 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -198,7 +198,7 @@ export class Viewport extends Disposable implements IViewport { } // Fallback to WheelEvent.DOM_DELTA_PIXEL - let amount = this._applyFastScrollModifier(ev.deltaY, ev); + let amount = this._applyScrollModifier(ev.deltaY, ev); if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { amount *= this._currentRowHeight; } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { @@ -219,7 +219,7 @@ export class Viewport extends Disposable implements IViewport { } // Fallback to WheelEvent.DOM_DELTA_LINE - let amount = this._applyFastScrollModifier(ev.deltaY, ev); + let amount = this._applyScrollModifier(ev.deltaY, ev); if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) { amount /= this._currentRowHeight + 0.0; // Prevent integer division this._wheelPartialScroll += amount; @@ -231,15 +231,16 @@ export class Viewport extends Disposable implements IViewport { return amount; } - private _applyFastScrollModifier(amount: number, ev: WheelEvent): number { + private _applyScrollModifier(amount: number, ev: WheelEvent): number { const modifier = this._optionsService.options.fastScrollModifier; // Multiply the scroll speed when the modifier is down if ((modifier === 'alt' && ev.altKey) || (modifier === 'ctrl' && ev.ctrlKey) || (modifier === 'shift' && ev.shiftKey)) { - return amount * Math.max(1, this._optionsService.options.fastScrollSensitivity); + return amount * this._optionsService.options.fastScrollSensitivity; } - return amount; + + return amount * this._optionsService.options.scrollSensitivity; } /** diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 9230421c..ccfca6d9 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -33,6 +33,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ letterSpacing: 0, logLevel: 'info', scrollback: 1000, + scrollSensitivity: 1, screenReaderMode: false, macOptionIsMeta: false, macOptionClickForcesSelection: false, @@ -121,6 +122,12 @@ export class OptionsService implements IOptionsService { throw new Error(`${key} cannot be less than 0, value: ${value}`); } break; + case 'fastScrollSensitivity': + case 'scrollSensitivity': + if (value <= 0) { + throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`); + } + break; } return value; } diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 1df59f84..0872d3db 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -196,6 +196,7 @@ export interface IPartialTerminalOptions { rows?: number; screenReaderMode?: boolean; scrollback?: number; + scrollSensitivity?: number; tabStopWidth?: number; theme?: ITheme; windowsMode?: boolean; @@ -227,6 +228,7 @@ export interface ITerminalOptions { rows: number; screenReaderMode: boolean; scrollback: number; + scrollSensitivity: number; tabStopWidth: number; theme: ITheme; windowsMode: boolean; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index af7d5ca8..361bafde 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -183,6 +183,11 @@ declare module 'xterm' { */ scrollback?: number; + /** + * The scrolling speed multiplier used for adjusting normal scrolling speed. + */ + scrollSensitivity?: number; + /** * The size of tab stops in the terminal. */ From fc1081661537c0e698dea2cccce5f6c20440a8cf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 21 Oct 2019 11:27:01 -0700 Subject: [PATCH 38/56] Remove .only from fit test --- addons/xterm-addon-fit/src/FitAddon.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-fit/src/FitAddon.api.ts b/addons/xterm-addon-fit/src/FitAddon.api.ts index f9972aef..4f955868 100644 --- a/addons/xterm-addon-fit/src/FitAddon.api.ts +++ b/addons/xterm-addon-fit/src/FitAddon.api.ts @@ -14,7 +14,7 @@ let page: puppeteer.Page; const width = 1024; const height = 768; -describe.only('FitAddon', () => { +describe('FitAddon', () => { before(async function(): Promise { this.timeout(20000); browser = await puppeteer.launch({ From 449ede1d576a72ed36be2e8f19de99be10c36bad Mon Sep 17 00:00:00 2001 From: miguel Date: Mon, 21 Oct 2019 14:55:22 -0400 Subject: [PATCH 39/56] Prevent search result from being deselected if it is the only result remove leading whitespace prevent single search term deselection in findPrevious --- addons/xterm-addon-search/src/SearchAddon.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 5893f79f..3c12e4f1 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -59,12 +59,12 @@ export class SearchAddon implements ITerminalAddon { let startCol = 0; let startRow = 0; - + let currentSelection = null; if (this._terminal.hasSelection()) { const incremental = searchOptions ? searchOptions.incremental : false; // Start from the selection end if there is a selection // For incremental search, use existing row - const currentSelection = this._terminal.getSelectionPosition()!; + currentSelection = this._terminal.getSelectionPosition()!; startRow = incremental ? currentSelection.startRow : currentSelection.endRow; startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn; } @@ -97,6 +97,9 @@ export class SearchAddon implements ITerminalAddon { } } + // If there is only one result, return false. + if (!result && currentSelection) return false; + // Set selection and scroll if a result was found return this._selectResult(result); } @@ -123,8 +126,9 @@ export class SearchAddon implements ITerminalAddon { let startCol = this._terminal.cols; let result: ISearchResult | undefined = undefined; const incremental = searchOptions ? searchOptions.incremental : false; + let currentSelection = null; if (this._terminal.hasSelection()) { - const currentSelection = this._terminal.getSelectionPosition()!; + currentSelection = this._terminal.getSelectionPosition()!; // Start from selection start if there is a selection startRow = currentSelection.startRow; startCol = currentSelection.startColumn; @@ -161,6 +165,9 @@ export class SearchAddon implements ITerminalAddon { } } + // If there is only one result, return false. + if (!result && currentSelection) return false; + // Set selection and scroll if a result was found return this._selectResult(result); } From d02086e5c66bcdbf07998e4dcf2a6211dd7481d7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 21 Oct 2019 14:42:56 -0700 Subject: [PATCH 40/56] Add test to cover search multiple times --- addons/xterm-addon-search/src/SearchAddon.api.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index cb5a02b8..e3520116 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -15,7 +15,7 @@ const width = 800; const height = 600; describe('Search Tests', function (): void { - this.timeout(200000); + this.timeout(20000); before(async function (): Promise { browser = await puppeteer.launch({ @@ -98,6 +98,14 @@ describe('Search Tests', function (): void { await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true, caseSensitive: true})`); assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'ABCD'); }); + + it('Search for single result twice should not unselect it', async () => { + await writeSync('abc def'); + assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true); + assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); + assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true); + assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); + }); }); async function openTerminal(options: ITerminalOptions = {}): Promise { From 7b752be68a7eb9ae1376ca0eab52bfe539295530 Mon Sep 17 00:00:00 2001 From: Jon Bockhorst Date: Mon, 21 Oct 2019 20:24:03 -0500 Subject: [PATCH 41/56] Expose IViewportRange in the public API --- typings/xterm.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 361bafde..8ed3caa2 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -860,7 +860,7 @@ declare module 'xterm' { /** * An object representing a range within the viewport of the terminal. */ - interface IViewportRange { + export interface IViewportRange { /** * The start cell of the range. */ From 7576e8c20226bb799fb451ba802f90c8909304db Mon Sep 17 00:00:00 2001 From: Leonardo Bressan Motyczka Date: Mon, 21 Oct 2019 22:27:25 -0300 Subject: [PATCH 42/56] test-api: Fail fast if .only is used --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b1ccdbf0..6f4a83af 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -80,7 +80,7 @@ jobs: - script: | yarn start & sleep 10 - yarn test-api --headless + yarn test-api --headless --forbid-only displayName: 'Linux Integration tests' - job: macOS_IntegrationTests @@ -97,7 +97,7 @@ jobs: - script: | yarn start & sleep 10 - yarn test-api --headless + yarn test-api --headless --forbid-only displayName: 'MacOS Integration tests' - job: Release From 09b244ed2fcc69a51de934117c249921c0af6a18 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 21 Oct 2019 18:44:03 -0700 Subject: [PATCH 43/56] Fix NaN comparison --- src/public/Terminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index b72a4cff..fe34cb36 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -181,7 +181,7 @@ export class Terminal implements ITerminalApi { private _verifyIntegers(...values: number[]): void { values.forEach(value => { - if (value === Infinity || value === NaN || value % 1 !== 0) { + if (value === Infinity || isNaN(value) || value % 1 !== 0) { throw new Error('This API only accepts integers'); } }); From bcdebe7648b50975d28e7cf2ad7c2625f63df700 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 21 Oct 2019 18:46:26 -0700 Subject: [PATCH 44/56] Remove duplicate switch case Guessing this one was the result of a bad merge? --- src/Terminal.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 852d424d..597c14cd 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -383,18 +383,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp case 'theme': this._setTheme(this.optionsService.options.theme); break; - case 'scrollback': - const newBufferLength = this.rows + this.optionsService.options.scrollback; - if (this.buffer.lines.length > newBufferLength) { - const amountToTrim = this.buffer.lines.length - newBufferLength; - const needsRefresh = (this.buffer.ydisp - amountToTrim < 0); - this.buffer.lines.trimStart(amountToTrim); - this.buffer.ybase = Math.max(this.buffer.ybase - amountToTrim, 0); - this.buffer.ydisp = Math.max(this.buffer.ydisp - amountToTrim, 0); - if (needsRefresh) { - this.refresh(0, this.rows - 1); - } - } case 'windowsMode': if (this.optionsService.options.windowsMode) { if (!this._windowsMode) { From 6f7c91d0cd3eb4cd4abeb08212fa1373eb06b601 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 21 Oct 2019 18:47:18 -0700 Subject: [PATCH 45/56] Remove useless local var assignment --- src/browser/renderer/CharacterJoinerRegistry.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/browser/renderer/CharacterJoinerRegistry.ts b/src/browser/renderer/CharacterJoinerRegistry.ts index a5abe80f..5385b76b 100644 --- a/src/browser/renderer/CharacterJoinerRegistry.ts +++ b/src/browser/renderer/CharacterJoinerRegistry.ts @@ -303,7 +303,6 @@ export class CharacterJoinerRegistry implements ICharacterJoinerRegistry { // current range ranges[i - 1][1] = Math.max(newRange[1], range[1]); ranges.splice(i, 1); - inRange = false; return ranges; } From 9826f3a14c67ea430fffb269984a72796c65de8a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 21 Oct 2019 18:47:43 -0700 Subject: [PATCH 46/56] Remove unneeded defensive check --- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index 186cb2c1..bd4488dc 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -105,9 +105,6 @@ export class GlyphRenderer { const gl = this._gl; const program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource)); - if (program === undefined) { - throw new Error('Could not create WebGL program'); - } this._program = program; // Uniform locations From cfd158cbff8edae45bbb9aadc48e0275a85f5aef Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 21 Oct 2019 18:48:49 -0700 Subject: [PATCH 47/56] Remove unused imports --- addons/xterm-addon-webgl/src/WebglRenderer.ts | 2 +- src/browser/renderer/dom/DomRendererRowFactory.ts | 2 +- src/common/buffer/BufferLine.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 3d41e1b5..3f8163ba 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -14,7 +14,7 @@ import { IWebGL2RenderingContext } from './Types'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel'; import { Disposable } from 'common/Lifecycle'; -import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants'; +import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants'; import { Terminal } from 'xterm'; import { getLuminance } from './ColorUtils'; import { IRenderLayer } from './renderLayer/Types'; diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 316645f8..3e3b5df0 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -8,7 +8,7 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { AttributeData } from 'common/buffer/AttributeData'; import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; import { CellData } from 'common/buffer/CellData'; -import { ITerminalOptions, IOptionsService } from 'common/services/Services'; +import { IOptionsService } from 'common/services/Services'; export const BOLD_CLASS = 'xterm-bold'; export const DIM_CLASS = 'xterm-dim'; diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 8e742be3..1e95e004 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -5,7 +5,7 @@ import { CharData, IBufferLine, ICellData } from 'common/Types'; import { stringFromCodePoint } from 'common/input/TextDecoder'; -import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants'; +import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants'; import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; From 1dbe482fcb7dbf66320ab593c3f299bca2f2407c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 21 Oct 2019 19:03:53 -0700 Subject: [PATCH 48/56] Suppress server alerts --- demo/server.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/demo/server.js b/demo/server.js index 9a45d06f..f04705aa 100644 --- a/demo/server.js +++ b/demo/server.js @@ -14,24 +14,26 @@ function startServer() { logs = {}; app.use('/xterm.css', express.static(__dirname + '/../css/xterm.css')); - app.get('/logo.png', (req, res) => res.sendFile(__dirname + '/logo.png')); - - app.get('/', function(req, res){ - res.sendFile(__dirname + '/index.html'); + app.get('/logo.png', (req, res) => { + res.sendFile(__dirname + '/logo.png'); // lgtm [js/missing-rate-limiting] }); - app.get('/test', function(req, res){ - res.sendFile(__dirname + '/test.html'); + app.get('/', (req, res) => { + res.sendFile(__dirname + '/index.html'); // lgtm [js/missing-rate-limiting] }); - app.get('/style.css', function(req, res){ - res.sendFile(__dirname + '/style.css'); + app.get('/test', (req, res) => { + res.sendFile(__dirname + '/test.html'); // lgtm [js/missing-rate-limiting] + }); + + app.get('/style.css', (req, res) => { + res.sendFile(__dirname + '/style.css'); // lgtm [js/missing-rate-limiting] }); app.use('/dist', express.static(__dirname + '/dist')); app.use('/src', express.static(__dirname + '/src')); - app.post('/terminals', function (req, res) { + app.post('/terminals', (req, res) => { const env = Object.assign({}, process.env); env['COLORTERM'] = 'truecolor'; var cols = parseInt(req.query.cols), @@ -55,7 +57,7 @@ function startServer() { res.end(); }); - app.post('/terminals/:pid/size', function (req, res) { + app.post('/terminals/:pid/size', (req, res) => { var pid = parseInt(req.params.pid), cols = parseInt(req.query.cols), rows = parseInt(req.query.rows), From 51ef0d0680e53d61836bf85e14acea322df312fa Mon Sep 17 00:00:00 2001 From: Leonardo Bressan Motyczka Date: Tue, 22 Oct 2019 03:00:18 +0000 Subject: [PATCH 49/56] Update test-unit to allow double dash args --- bin/test.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/bin/test.js b/bin/test.js index 775ec004..760908a3 100644 --- a/bin/test.js +++ b/bin/test.js @@ -15,15 +15,22 @@ let testFiles = [ './out/**/*test.js' ]; -// ability to inject particular test files via -// yarn test [testFileA testFileB ...] +let flagArgs = []; + if (process.argv.length > 2) { - testFiles = process.argv.slice(2); + const args = process.argv.slice(2); + flagArgs = args.filter(e => e.startsWith('--')); + // ability to inject particular test files via + // yarn test [testFileA testFileB ...] + files = args.filter(e => !e.startsWith('--')); + if(files.length){ + testFiles = files; + } } const run = cp.spawnSync( path.resolve(__dirname, '../node_modules/.bin/mocha'), - testFiles, + [...testFiles, ...flagArgs], { cwd: path.resolve(__dirname, '..'), env, @@ -31,4 +38,4 @@ const run = cp.spawnSync( } ); -process.exit(run.status); \ No newline at end of file +process.exit(run.status); From 5c2479944ec865f4df2bb33935f32af4ba395563 Mon Sep 17 00:00:00 2001 From: Leonardo Bressan Motyczka Date: Tue, 22 Oct 2019 03:00:40 +0000 Subject: [PATCH 50/56] Ensure test-unit fails fast if .only present --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6f4a83af..a58f7fe0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -20,7 +20,7 @@ jobs: yarn displayName: 'Install dependencies and build' - script: | - yarn test-unit + yarn test-unit --forbid-only displayName: 'Unit tests' - script: | yarn lint @@ -38,7 +38,7 @@ jobs: yarn displayName: 'Install dependencies and build' - script: | - yarn test-unit + yarn test-unit --forbid-only displayName: 'Unit tests' - script: | yarn lint @@ -56,7 +56,7 @@ jobs: yarn displayName: 'Install dependencies and build' - script: | - yarn test-unit + yarn test-unit --forbid-only displayName: 'Unit tests' - script: | yarn lint From 495f97cf95034f36166e3b32978ade076752e096 Mon Sep 17 00:00:00 2001 From: Miguel Roncancio Date: Mon, 21 Oct 2019 23:39:01 -0400 Subject: [PATCH 51/56] change return value, import ISelectionPosition --- addons/xterm-addon-search/src/SearchAddon.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 3c12e4f1..5f85ebbf 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { Terminal, IDisposable, ITerminalAddon } from 'xterm'; +import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition } from 'xterm'; export interface ISearchOptions { regex?: boolean; @@ -59,7 +59,7 @@ export class SearchAddon implements ITerminalAddon { let startCol = 0; let startRow = 0; - let currentSelection = null; + let currentSelection: ISelectionPosition | undefined = undefined; if (this._terminal.hasSelection()) { const incremental = searchOptions ? searchOptions.incremental : false; // Start from the selection end if there is a selection @@ -98,7 +98,7 @@ export class SearchAddon implements ITerminalAddon { } // If there is only one result, return false. - if (!result && currentSelection) return false; + if (!result && currentSelection) return true; // Set selection and scroll if a result was found return this._selectResult(result); @@ -126,7 +126,7 @@ export class SearchAddon implements ITerminalAddon { let startCol = this._terminal.cols; let result: ISearchResult | undefined = undefined; const incremental = searchOptions ? searchOptions.incremental : false; - let currentSelection = null; + let currentSelection: ISelectionPosition | undefined = undefined; if (this._terminal.hasSelection()) { currentSelection = this._terminal.getSelectionPosition()!; // Start from selection start if there is a selection @@ -166,7 +166,7 @@ export class SearchAddon implements ITerminalAddon { } // If there is only one result, return false. - if (!result && currentSelection) return false; + if (!result && currentSelection) return true; // Set selection and scroll if a result was found return this._selectResult(result); From 5d547bffb0fe277ace5be2deb96e12fce2500aa8 Mon Sep 17 00:00:00 2001 From: Miguel Roncancio Date: Mon, 21 Oct 2019 23:39:01 -0400 Subject: [PATCH 52/56] change return value, import ISelectionPosition --- addons/xterm-addon-search/src/SearchAddon.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 3c12e4f1..cfee3cb9 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { Terminal, IDisposable, ITerminalAddon } from 'xterm'; +import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition } from 'xterm'; export interface ISearchOptions { regex?: boolean; @@ -59,7 +59,7 @@ export class SearchAddon implements ITerminalAddon { let startCol = 0; let startRow = 0; - let currentSelection = null; + let currentSelection: ISelectionPosition | undefined = undefined; if (this._terminal.hasSelection()) { const incremental = searchOptions ? searchOptions.incremental : false; // Start from the selection end if there is a selection @@ -97,8 +97,8 @@ export class SearchAddon implements ITerminalAddon { } } - // If there is only one result, return false. - if (!result && currentSelection) return false; + // If there is only one result, return true. + if (!result && currentSelection) return true; // Set selection and scroll if a result was found return this._selectResult(result); @@ -126,7 +126,7 @@ export class SearchAddon implements ITerminalAddon { let startCol = this._terminal.cols; let result: ISearchResult | undefined = undefined; const incremental = searchOptions ? searchOptions.incremental : false; - let currentSelection = null; + let currentSelection: ISelectionPosition | undefined = undefined; if (this._terminal.hasSelection()) { currentSelection = this._terminal.getSelectionPosition()!; // Start from selection start if there is a selection @@ -165,8 +165,8 @@ export class SearchAddon implements ITerminalAddon { } } - // If there is only one result, return false. - if (!result && currentSelection) return false; + // If there is only one result, return true. + if (!result && currentSelection) return true; // Set selection and scroll if a result was found return this._selectResult(result); From 3ce552cdec206e5e6ee7e5782740e9e90861698c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 22 Oct 2019 06:33:07 -0700 Subject: [PATCH 53/56] Warn don't throw when open is called on unattached element Related microsoft/vscode#83016 --- src/Terminal.ts | 4 ++++ src/public/Terminal.ts | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 597c14cd..3e7e55ca 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -508,6 +508,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp throw new Error('Terminal requires a parent element.'); } + if (!document.body.contains(parent)) { + this._logService.warn('Terminal.open was called on an element that was not attached to the DOM'); + } + this._document = this._parent.ownerDocument; // Create main element container diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index fe34cb36..c167bed8 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -56,9 +56,6 @@ export class Terminal implements ITerminalApi { this._core.resize(columns, rows); } public open(parent: HTMLElement): void { - if (!document.body.contains(parent)) { - throw new Error('open must be called on an element that is attached to the DOM'); - } this._core.open(parent); } public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void { From 5e2001f4b57393475d171b2b802e300782cfb7d6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 22 Oct 2019 07:14:32 -0700 Subject: [PATCH 54/56] Space out if --- bin/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/test.js b/bin/test.js index 760908a3..3b08d4a1 100644 --- a/bin/test.js +++ b/bin/test.js @@ -23,7 +23,7 @@ if (process.argv.length > 2) { // ability to inject particular test files via // yarn test [testFileA testFileB ...] files = args.filter(e => !e.startsWith('--')); - if(files.length){ + if (files.length) { testFiles = files; } } From 616d341e8ce6cd1f4b9c59157414882b9c825a5d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 22 Oct 2019 07:17:37 -0700 Subject: [PATCH 55/56] Avoid initializing to undefined --- addons/xterm-addon-search/src/SearchAddon.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index cfee3cb9..91fd3977 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -59,7 +59,7 @@ export class SearchAddon implements ITerminalAddon { let startCol = 0; let startRow = 0; - let currentSelection: ISelectionPosition | undefined = undefined; + let currentSelection: ISelectionPosition | undefined; if (this._terminal.hasSelection()) { const incremental = searchOptions ? searchOptions.incremental : false; // Start from the selection end if there is a selection @@ -124,9 +124,9 @@ export class SearchAddon implements ITerminalAddon { const isReverseSearch = true; let startRow = this._terminal.buffer.baseY + this._terminal.rows; let startCol = this._terminal.cols; - let result: ISearchResult | undefined = undefined; + let result: ISearchResult | undefined; const incremental = searchOptions ? searchOptions.incremental : false; - let currentSelection: ISelectionPosition | undefined = undefined; + let currentSelection: ISelectionPosition | undefined; if (this._terminal.hasSelection()) { currentSelection = this._terminal.getSelectionPosition()!; // Start from selection start if there is a selection From 84e4cf7e9c84f8d28dd6418ae9d91f27dfdb449c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 23 Oct 2019 11:37:17 -0700 Subject: [PATCH 56/56] Remove double space in dts --- typings/xterm.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 8ed3caa2..62670f3d 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -449,7 +449,7 @@ declare module 'xterm' { onLineFeed: IEvent; /** - * Adds an event listener for when a scroll occurs. The event value is the + * Adds an event listener for when a scroll occurs. The event value is the * new position of the viewport. * @returns an `IDisposable` to stop listening. */