From fd34caee2322af515fc1e94785d3a41dbc7e85da Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Fri, 7 Dec 2018 22:50:59 -0500 Subject: [PATCH 01/47] Use a time-based limit to Terminal._innerWrite The idea is that it should run for a bit and then let the renderer draw a frame so that the terminal look responsive. The existing approach limits the work done using a fixed number elements from the write buffer so the duration of a frame can vary widely. This approach looks at the clock to determine when to stop, we basically allocate an amount of time each frame to write, while the rest can be used for rendering. From my tests this change makes the terminal feel a lot smoother. --- src/Terminal.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 2cfc1ca88..a641a86b0 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -64,10 +64,12 @@ const document = (typeof window !== 'undefined') ? window.document : null; const WRITE_BUFFER_PAUSE_THRESHOLD = 5; /** - * The number of writes to perform in a single batch before allowing the - * renderer to catch up with a 0ms setTimeout. + * The max number of ms to spend on writes before allowing the renderer to + * catch up with a 0ms setTimeout. A value of < 33 to keep us close to + * 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS + * depends on the time it takes for the renderer to draw the frame. */ -const WRITE_BATCH_SIZE = 300; +const WRITE_TIMEOUT_MS = 12; /** * The set of options that only have an effect when set in the Terminal constructor. @@ -1358,13 +1360,13 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this.writeBuffer = []; } - const writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE); - while (writeBatch.length > 0) { - const data = writeBatch.shift(); + const time = Date.now(); + while (this.writeBuffer.length > 0) { + const data = this.writeBuffer.shift(); // If XOFF was sent in order to catch up with the pty process, resume it if // the writeBuffer is empty to allow more data to come in. - if (this._xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) { + if (this._xoffSentToCatchUp && this.writeBuffer.length === 0 && this.writeBuffer.length === 0) { this.handler(C0.DC1); this._xoffSentToCatchUp = false; } @@ -1382,6 +1384,10 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this.updateRange(this.buffer.y); this.refresh(this._refreshStart, this._refreshEnd); + + if (Date.now() - time >= WRITE_TIMEOUT_MS) { + break; + } } if (this.writeBuffer.length > 0) { // Allow renderer to catch up before processing the next batch From 3d2ae2b01edd34e9475c7d58884a501c2426237f Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Sun, 9 Dec 2018 17:58:44 -0500 Subject: [PATCH 02/47] Removing redundant condition. Clearer variable name --- src/Terminal.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index a641a86b0..2c7f648bb 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1360,13 +1360,13 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this.writeBuffer = []; } - const time = Date.now(); + const startTime = Date.now(); while (this.writeBuffer.length > 0) { const data = this.writeBuffer.shift(); // If XOFF was sent in order to catch up with the pty process, resume it if // the writeBuffer is empty to allow more data to come in. - if (this._xoffSentToCatchUp && this.writeBuffer.length === 0 && this.writeBuffer.length === 0) { + if (this._xoffSentToCatchUp && this.writeBuffer.length === 0) { this.handler(C0.DC1); this._xoffSentToCatchUp = false; } @@ -1385,7 +1385,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this.updateRange(this.buffer.y); this.refresh(this._refreshStart, this._refreshEnd); - if (Date.now() - time >= WRITE_TIMEOUT_MS) { + if (Date.now() - startTime >= WRITE_TIMEOUT_MS) { break; } } From fc1692b3eb10cacb52284dd11e7e343c40ace716 Mon Sep 17 00:00:00 2001 From: Nikita Chuklinov Date: Wed, 30 Jan 2019 20:34:16 +0300 Subject: [PATCH 03/47] clear state if selection doesn't exists --- src/renderer/SelectionRenderLayer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/SelectionRenderLayer.ts b/src/renderer/SelectionRenderLayer.ts index 81782ee8d..fc16a2fb6 100644 --- a/src/renderer/SelectionRenderLayer.ts +++ b/src/renderer/SelectionRenderLayer.ts @@ -55,6 +55,7 @@ export class SelectionRenderLayer extends BaseRenderLayer { // Selection does not exist if (!start || !end) { + this._clearState(); return; } From 54a1319f57c4a8f38f91209705d4d85f402940b0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 1 Feb 2019 18:23:10 -0800 Subject: [PATCH 04/47] v3.11.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ed3a4b80..fa9cc0706 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "xterm", "description": "Full xterm terminal, in your browser", - "version": "3.10.0", + "version": "3.11.0", "main": "lib/public/Terminal.js", "types": "typings/xterm.d.ts", "repository": "https://github.com/xtermjs/xterm.js", From 1829cb7609029405230e69803c0262f2768a40ff Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 1 Feb 2019 19:28:07 -0800 Subject: [PATCH 05/47] Add roadmap wiki link to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 35ccc9b6b..f22c6c00b 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ Do you use xterm.js in your application as well? Please [open a Pull Request](ht Xterm.js follows a monthly release cycle roughly. -All current and past releases are available on this repo's [Releases page](https://github.com/sourcelair/xterm.js/releases), while a rough roadmap is available by looking through [Milestones](https://github.com/sourcelair/xterm.js/milestones). +All current and past releases are available on this repo's [Releases page](https://github.com/sourcelair/xterm.js/releases), you can view the [high-level roadmap on the wiki](https://github.com/xtermjs/xterm.js/wiki/Roadmap) and see what we're working on now by looking through [Milestones](https://github.com/sourcelair/xterm.js/milestones). ## Contributing From f3aac10bbd32efb4c5f648d7fe7b7cdd10a25a55 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 3 Feb 2019 10:46:20 -0800 Subject: [PATCH 06/47] Update license year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 28adbdadd..4472336c9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017-2018, The xterm.js authors (https://github.com/xtermjs/xterm.js) +Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com) Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/) From 69d3a4667f6d3ade4a89e71be066de0e69d82d16 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2019 13:28:14 +0200 Subject: [PATCH 07/47] fix: Renderer: IntersectionObserver can produce more then 1 entry --- src/renderer/Renderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 023288776..b8ef87aab 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -70,7 +70,7 @@ export class Renderer extends EventEmitter implements IRenderer { // Detect whether IntersectionObserver is detected and enable renderer pause // and resume based on terminal visibility if so if ('IntersectionObserver' in window) { - const observer = new IntersectionObserver(e => this.onIntersectionChange(e[0]), { threshold: 0 }); + const observer = new IntersectionObserver(e => this.onIntersectionChange(e[e.length - 1]), { threshold: 0 }); observer.observe(this._terminal.element); this.register({ dispose: () => observer.disconnect() }); } From b88230db8ba0d54fb194f5b903215a53b017a743 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Feb 2019 08:20:16 -0800 Subject: [PATCH 08/47] Make sure the viewport is filled when reflowing a row change Fixes #1926 --- src/Buffer.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Buffer.ts b/src/Buffer.ts index 7f4b6071c..e40fa8b41 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -233,22 +233,22 @@ export class Buffer implements IBuffer { // Iterate through rows, ignore the last one as it cannot be wrapped if (newCols > this._cols) { - this._reflowLarger(newCols); + this._reflowLarger(newCols, newRows); } else { this._reflowSmaller(newCols, newRows); } } - private _reflowLarger(newCols: number): void { + private _reflowLarger(newCols: number, newRows: number): void { const toRemove: number[] = reflowLargerGetLinesToRemove(this.lines, newCols, this.ybase + this.y); if (toRemove.length > 0) { const newLayoutResult = reflowLargerCreateNewLayout(this.lines, toRemove); reflowLargerApplyNewLayout(this.lines, newLayoutResult.layout); - this._reflowLargerAdjustViewport(newCols, newLayoutResult.countRemoved); + this._reflowLargerAdjustViewport(newCols, newRows, newLayoutResult.countRemoved); } } - private _reflowLargerAdjustViewport(newCols: number, countRemoved: number): void { + private _reflowLargerAdjustViewport(newCols: number, newRows: number, countRemoved: number): void { // Adjust viewport based on number of items removed let viewportAdjustments = countRemoved; while (viewportAdjustments-- > 0) { @@ -256,7 +256,7 @@ export class Buffer implements IBuffer { if (this.y > 0) { this.y--; } - if (this.lines.length < this._rows) { + if (this.lines.length < newRows) { // Add an extra row at the bottom of the viewport this.lines.push(new BufferLine(newCols, FILL_CHAR_DATA)); } From d17dfbc73fc61d467ad079e4e0b90ae778687a88 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 8 Feb 2019 12:13:00 -0800 Subject: [PATCH 09/47] Cover a case when resizing smaller making y go out of bounds --- src/Buffer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Buffer.ts b/src/Buffer.ts index e40fa8b41..d017aa7e8 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -360,7 +360,7 @@ export class Buffer implements IBuffer { let viewportAdjustments = linesToAdd - trimmedLines; while (viewportAdjustments-- > 0) { if (this.ybase === 0) { - if (this.y < this._rows - 1) { + if (this.y < newRows - 1) { this.y++; this.lines.pop(); } else { From 92be01dd8188b9b2eb730048526f51443b4fac6c Mon Sep 17 00:00:00 2001 From: Ahtsham Raziq Date: Sat, 9 Feb 2019 23:24:32 +0500 Subject: [PATCH 10/47] Compose file: fix variable substitution --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8e6a2f46b..6eefed896 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: volumes: - ./:/usr/src/app ports: - - ${XTERMJS_PORT:3000}:3000 + - ${XTERMJS_PORT:-3000}:3000 command: ["npm", "start"] watch: From 84d7bfeacce308c0dc762e038a20ea36b03b44cd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 11 Feb 2019 05:14:55 -0800 Subject: [PATCH 11/47] Remove font-family from .css file Fixes #1935 --- src/xterm.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/xterm.css b/src/xterm.css index 24cd475f0..2e47b1a17 100644 --- a/src/xterm.css +++ b/src/xterm.css @@ -36,7 +36,6 @@ */ .xterm { - font-family: courier-new, courier, monospace; font-feature-settings: "liga" 0; position: relative; user-select: none; From 817401bbcd08c45ffa8169341213d49b7de81823 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 11 Feb 2019 05:32:47 -0800 Subject: [PATCH 12/47] Align y draw coord with how cache draws it Fixes #1937 --- src/renderer/BaseRenderLayer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 3e0b86430..a609d79ce 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -241,7 +241,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._ctx.fillText( charData[CHAR_DATA_CHAR_INDEX], x * this._scaledCellWidth + this._scaledCharLeft, - (y + 0.5) * this._scaledCellHeight + this._scaledCharTop); + y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight / 2); } /** @@ -316,7 +316,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._ctx.fillText( chars, x * this._scaledCellWidth + this._scaledCharLeft, - (y + 0.5) * this._scaledCellHeight + this._scaledCharTop); + y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight / 2); this._ctx.restore(); } From 78426d8a12c56f40cf1c2d74fb53c23f2982ebb5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 11 Feb 2019 06:05:14 -0800 Subject: [PATCH 13/47] Make the composition view use the same font as the terminal --- src/CompositionHelper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CompositionHelper.ts b/src/CompositionHelper.ts index 31ad866b4..5f838c7a0 100644 --- a/src/CompositionHelper.ts +++ b/src/CompositionHelper.ts @@ -203,6 +203,7 @@ export class CompositionHelper { this._compositionView.style.top = cursorTop + 'px'; this._compositionView.style.height = cellHeight + 'px'; this._compositionView.style.lineHeight = cellHeight + 'px'; + this._compositionView.style.fontFamily = this._terminal.options.fontFamily; // Sync the textarea to the exact position of the composition view so the IME knows where the // text is. const compositionViewBounds = this._compositionView.getBoundingClientRect(); From c934200c86ccaca419cae6aae210880a52791c46 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 11 Feb 2019 06:07:31 -0800 Subject: [PATCH 14/47] Also set font size --- src/CompositionHelper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CompositionHelper.ts b/src/CompositionHelper.ts index 5f838c7a0..840bef550 100644 --- a/src/CompositionHelper.ts +++ b/src/CompositionHelper.ts @@ -204,6 +204,7 @@ export class CompositionHelper { this._compositionView.style.height = cellHeight + 'px'; this._compositionView.style.lineHeight = cellHeight + 'px'; this._compositionView.style.fontFamily = this._terminal.options.fontFamily; + this._compositionView.style.fontSize = this._terminal.options.fontSize + 'px'; // Sync the textarea to the exact position of the composition view so the IME knows where the // text is. const compositionViewBounds = this._compositionView.getBoundingClientRect(); From 9f29eeed2338dbc8861c65a1a35631fc34d980e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E8=99=AB?= Date: Tue, 19 Feb 2019 17:19:19 +0800 Subject: [PATCH 15/47] Update README.md (Jumpserver)[https://github.com/jumpserver/] use xterm.js --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f22c6c00b..59e406e94 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js - [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js +- [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use xterm.js for web terminal emulation. - [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js - [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies. - [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. From e1c1c7a4f217f75eea4ed71fd0afcf5a1f14a93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E8=99=AB?= Date: Wed, 20 Feb 2019 14:57:16 +0800 Subject: [PATCH 16/47] Update README.md move it to the bottom --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 59e406e94..17bc9fb4a 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,6 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js - [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js -- [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use xterm.js for web terminal emulation. - [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js - [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies. - [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. @@ -155,6 +154,7 @@ computational environment for Jupyter, supporting interactive data science and s - [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom. - [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client. - [**info-beamer hosted**](https://info-beamer.com): Uses Xterm.js to manage digital signage devices from the web dashboard. +- [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use xterm.js for web terminal emulation. [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) From 3285374618a2ea112e5124c3b551ae0ac0761035 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 20 Feb 2019 07:03:40 -0800 Subject: [PATCH 17/47] Disable reflow when winptyCompat is on Fixes #1943 --- src/Buffer.ts | 6 +++++- src/addons/winptyCompat/winptyCompat.ts | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Buffer.ts b/src/Buffer.ts index 7f4b6071c..bf0bfe170 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -211,7 +211,7 @@ export class Buffer implements IBuffer { this.scrollBottom = newRows - 1; - if (this._hasScrollback) { + if (this._isReflowEnabled) { this._reflow(newCols, newRows); // Trim the end of the line off if cols shrunk @@ -226,6 +226,10 @@ export class Buffer implements IBuffer { this._rows = newRows; } + private get _isReflowEnabled(): boolean { + return this._hasScrollback && !(this._terminal as any).isWinptyCompatEnabled; + } + private _reflow(newCols: number, newRows: number): void { if (this._cols === newCols) { return; diff --git a/src/addons/winptyCompat/winptyCompat.ts b/src/addons/winptyCompat/winptyCompat.ts index aec580edb..d162f4e92 100644 --- a/src/addons/winptyCompat/winptyCompat.ts +++ b/src/addons/winptyCompat/winptyCompat.ts @@ -19,6 +19,8 @@ export function winptyCompatInit(terminal: Terminal): void { return; } + (addonTerminal._core as any).isWinptyCompatEnabled = true; + // Winpty does not support wraparound mode which means that lines will never // be marked as wrapped. This causes issues for things like copying a line // retaining the wrapped new line characters or if consumers are listening From 0854f846533689b253e3a6b6924216b2b52592f3 Mon Sep 17 00:00:00 2001 From: Sebastian Pfitzner Date: Tue, 26 Feb 2019 11:28:51 +0100 Subject: [PATCH 18/47] actually fix mouse handler before term attached --- src/InputHandler.ts | 8 ++++++-- src/Terminal.ts | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 2f53cfcbb..7405ff9f0 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1284,7 +1284,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (this._terminal.element) { this._terminal.element.classList.add('enable-mouse-events'); } - this._terminal.selectionManager.disable(); + if (this._terminal.selectionManager) { + this._terminal.selectionManager.disable(); + } this._terminal.log('Binding to mouse events.'); break; case 1004: // send focusin/focusout events @@ -1474,7 +1476,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (this._terminal.element) { this._terminal.element.classList.remove('enable-mouse-events'); } - this._terminal.selectionManager.enable(); + if (this._terminal.selectionManager) { + this._terminal.selectionManager.enable(); + } break; case 1004: // send focusin/focusout events this._terminal.sendFocus = false; diff --git a/src/Terminal.ts b/src/Terminal.ts index c1fc8ec86..cb9bc6758 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -738,6 +738,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this.mouseHelper = new MouseHelper(this.renderer); // apply mouse event classes set by escape codes before terminal was attached this.element.classList.toggle('enable-mouse-events', this.mouseEvents); + if (this.mouseEvents) { + this.selectionManager.disable() + } else { + this.selectionManager.enable() + } if (this.options.screenReaderMode) { // Note that this must be done *after* the renderer is created in order to From e178139907a8a9a098a249849931faf89bdec5dc Mon Sep 17 00:00:00 2001 From: Nick Shaffner Date: Wed, 27 Feb 2019 22:37:22 -0800 Subject: [PATCH 19/47] Fix for issue #812: Xterm.js's encoding of mouse coordinate See: https://github.com/xtermjs/xterm.js/issues/812 Changed the utf-8 mouse encoding to match iTerm --- src/Terminal.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index c1fc8ec86..0539a9040 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -854,16 +854,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II if (ch > 127) ch = 127; data.push(ch); } else { - if (ch === 2047) { - data.push(0); + if (ch > 2047) { + data.push(2047); return; - } - if (ch < 127) { - data.push(ch); } else { - if (ch > 2047) ch = 2047; - data.push(0xC0 | (ch >> 6)); - data.push(0x80 | (ch & 0x3F)); + data.push(ch); } } } From 4e479b455a659a53c7a26d05549bd38ba301c9be Mon Sep 17 00:00:00 2001 From: Ioannis Cherouvim <743305+cherouvim@users.noreply.github.com> Date: Fri, 1 Mar 2019 14:40:21 +0200 Subject: [PATCH 20/47] docs: Consistent style on lists. Added some missing dots, capitalized a couple of lines. --- CONTRIBUTING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e79240275..e8102a2ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,21 +33,21 @@ opening an issue, read these pointers. You can find issues to work on by looking at the [help wanted](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [good first issue](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) issues. It's a good idea to comment on the issue saying that you're taking it, just in case someone else comes along and you duplicate work. Once you have your issue, here are the steps to contribute: - Fork [xterm.js](https://github.com/sourcelair/xterm.js/) - ([how to fork a repo](https://help.github.com/articles/fork-a-repo)) -- Get the [xterm.js demo](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo) running -- Make your changes + ([how to fork a repo](https://help.github.com/articles/fork-a-repo)). +- Get the [xterm.js demo](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo) running. +- Make your changes. - If your changes are easy to test or likely to regress, add tests. Tests go into `test`, directory. - Follow the general code style of the rest of the project (see below). - Submit a pull request ([how to create a pull request](https://help.github.com/articles/fork-a-repo)). Don't put more than one feature/fix in a single pull request. -By contributing code to xterm.js you +By contributing code to xterm.js you: - - agree to license the contributed code under xterm.js' [MIT + - Agree to license the contributed code under xterm.js' [MIT license](LICENSE). - - confirm that you have the right to contribute and license the code + - Confirm that you have the right to contribute and license the code in question. (Either you hold all rights on the code, or the rights holder has explicitly granted the right to use it like this, through a compatible open source license or through a direct From a8a0344d1ac5ea411ea244a73754a06ffda2d308 Mon Sep 17 00:00:00 2001 From: Ioannis Cherouvim <743305+cherouvim@users.noreply.github.com> Date: Fri, 1 Mar 2019 14:51:19 +0200 Subject: [PATCH 21/47] docs: style improvements - Added a trailing dot at the end of each list line. - Consistent usage of `xterm.js` in "Real-world uses". --- README.md | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 17bc9fb4a..d70e8b7e0 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,17 @@ Xterm.js is a front-end component written in TypeScript that lets applications b ## Features -- **Terminal apps just work**: Xterm.js works with most terminal apps such as `bash`, `vim` and `tmux`, this includes support for curses-based apps and mouse event support -- **Perfomant**: Xterm.js is *really* fast, it even includes a GPU-accelerated renderer -- **Rich unicode support**: Supports CJK, emojis and IMEs -- **Self-contained**: Requires zero dependencies to work -- **Accessible**: Screen reader support can be turned on using the `screenReaderMode` option +- **Terminal apps just work**: Xterm.js works with most terminal apps such as `bash`, `vim` and `tmux`, this includes support for curses-based apps and mouse event support. +- **Perfomant**: Xterm.js is *really* fast, it even includes a GPU-accelerated renderer. +- **Rich unicode support**: Supports CJK, emojis and IMEs. +- **Self-contained**: Requires zero dependencies to work. +- **Accessible**: Screen reader support can be turned on using the `screenReaderMode` option. - **And much more**: Links, theming, addons, well documented API, etc. ## What xterm.js is not -- Xterm.js is not a terminal application that you can download and use on your computer -- Xterm.js is not `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output) +- Xterm.js is not a terminal application that you can download and use on your computer. +- Xterm.js is not `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output). ## Getting Started @@ -41,7 +41,7 @@ To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to t @@ -106,9 +106,9 @@ Note that some APIs are marked *experimental*, these are added to enable experim ## Real-world uses Xterm.js is used in several world-class applications to provide great terminal experiences. -- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js -- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js -- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js +- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on `xterm.js`. +- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on `xterm.js`. +- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on `xterm.js`. - [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies. - [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. - [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams. @@ -125,11 +125,10 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**Selenoid UI**](https://github.com/aerokube/selenoid-ui): Simple UI for the scallable golang implementation of Selenium Hub named Selenoid. We use XTerm for streaming logs over websockets from docker containers. - [**Portainer**](https://portainer.io): Simple management UI for Docker. - [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising `xterm.js`, SJCL & websockets. -- [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible -computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages. +- [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages. - [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript. - [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations. -- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell. +- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses `xterm.js` for container terminals and the host shell. - [**Script Runner**](https://github.com/ioquatix/script-runner): Run scripts (or a shell) in Atom. - [**Whack Whack Terminal**](https://github.com/Microsoft/WhackWhackTerminal): Terminal emulator for Visual Studio 2017. - [**VTerm**](https://github.com/vterm/vterm): Extensible terminal emulator based on Electron and React. @@ -138,23 +137,23 @@ computational environment for Jupyter, supporting interactive data science and s - [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure. - [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace. - [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server. -- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS +- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS. - [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker. - [**Azure Data Studio**](https://github.com/Microsoft/azuredatastudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux. -- [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users +- [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users. - [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies. -- [**Hyper**](https://hyper.is): A terminal built on web technologies +- [**Hyper**](https://hyper.is): A terminal built on web technologies. - [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter. -- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js. +- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on `xterm.js`. - [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator. - [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice. -- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js +- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to `xterm.js`. - [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP. - [**Shellvault**](https://www.shellvault.io): The cloud-based SSH terminal you can access from anywhere. - [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom. - [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client. -- [**info-beamer hosted**](https://info-beamer.com): Uses Xterm.js to manage digital signage devices from the web dashboard. -- [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use xterm.js for web terminal emulation. +- [**info-beamer hosted**](https://info-beamer.com): Uses `xterm.js` to manage digital signage devices from the web dashboard. +- [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use `xterm.js` for web terminal emulation. [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) From 09754fddf8e3ba0dbbd3840b65a69822482fb64b Mon Sep 17 00:00:00 2001 From: Ioannis Cherouvim <743305+cherouvim@users.noreply.github.com> Date: Fri, 1 Mar 2019 21:15:20 +0200 Subject: [PATCH 22/47] xterm.js references to the library should not be backticked --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d70e8b7e0..5378f4430 100644 --- a/README.md +++ b/README.md @@ -106,17 +106,17 @@ Note that some APIs are marked *experimental*, these are added to enable experim ## Real-world uses Xterm.js is used in several world-class applications to provide great terminal experiences. -- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on `xterm.js`. -- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on `xterm.js`. -- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on `xterm.js`. +- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js. +- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js. +- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js. - [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies. - [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. - [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams. -- [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by `xterm.js`. -- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using `xterm.js`, socket.io, and ssh2. +- [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by xterm.js. +- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using xterm.js, socket.io, and ssh2. - [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE. - [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor. -- [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses `xterm.js`. +- [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses xterm.js. - [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R. - [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor. - [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy and run in the cloud. @@ -124,11 +124,11 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**Hexlet**](https://en.hexlet.io): Practical programming courses (JavaScript, PHP, Unix, databases, functional programming). A steady path from the first line of code to the first job. - [**Selenoid UI**](https://github.com/aerokube/selenoid-ui): Simple UI for the scallable golang implementation of Selenium Hub named Selenoid. We use XTerm for streaming logs over websockets from docker containers. - [**Portainer**](https://portainer.io): Simple management UI for Docker. -- [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising `xterm.js`, SJCL & websockets. +- [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising xterm.js, SJCL & websockets. - [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages. - [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript. - [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations. -- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses `xterm.js` for container terminals and the host shell. +- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell. - [**Script Runner**](https://github.com/ioquatix/script-runner): Run scripts (or a shell) in Atom. - [**Whack Whack Terminal**](https://github.com/Microsoft/WhackWhackTerminal): Terminal emulator for Visual Studio 2017. - [**VTerm**](https://github.com/vterm/vterm): Extensible terminal emulator based on Electron and React. @@ -144,16 +144,16 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies. - [**Hyper**](https://hyper.is): A terminal built on web technologies. - [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter. -- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on `xterm.js`. +- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js. - [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator. - [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice. -- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to `xterm.js`. +- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js. - [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP. - [**Shellvault**](https://www.shellvault.io): The cloud-based SSH terminal you can access from anywhere. - [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom. - [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client. -- [**info-beamer hosted**](https://info-beamer.com): Uses `xterm.js` to manage digital signage devices from the web dashboard. -- [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use `xterm.js` for web terminal emulation. +- [**info-beamer hosted**](https://info-beamer.com): Uses xterm.js to manage digital signage devices from the web dashboard. +- [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use xterm.js for web terminal emulation. [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) From c63f15a9b26c770dcbcceca6dfaf33acb3121d67 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 10:00:04 -0800 Subject: [PATCH 23/47] Fix lint --- src/Terminal.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index cb9bc6758..5de369feb 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -739,9 +739,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II // apply mouse event classes set by escape codes before terminal was attached this.element.classList.toggle('enable-mouse-events', this.mouseEvents); if (this.mouseEvents) { - this.selectionManager.disable() + this.selectionManager.disable(); } else { - this.selectionManager.enable() + this.selectionManager.enable(); } if (this.options.screenReaderMode) { From 32e157bfaa43164171bac02798fc293df30d151c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 10:04:52 -0800 Subject: [PATCH 24/47] Remove unnecessary else --- src/Terminal.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 0539a9040..25c92fdfa 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -857,9 +857,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II if (ch > 2047) { data.push(2047); return; - } else { - data.push(ch); } + data.push(ch); } } From 07430a4892365e65946c05b34d51ba668fbc2b9f Mon Sep 17 00:00:00 2001 From: Jesse Stolwijk Date: Tue, 5 Mar 2019 00:00:54 +0100 Subject: [PATCH 25/47] Replace array shift with offset (#1955) --- src/Terminal.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 4d480eb6e..19b5f1256 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1350,19 +1350,20 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II } } - protected _innerWrite(): void { + protected _innerWrite(bufferOffset: number = 0): void { // Ensure the terminal isn't disposed if (this._isDisposed) { this.writeBuffer = []; } const startTime = Date.now(); - while (this.writeBuffer.length > 0) { - const data = this.writeBuffer.shift(); + while (this.writeBuffer.length > bufferOffset) { + const data = this.writeBuffer[bufferOffset]; + bufferOffset++; // If XOFF was sent in order to catch up with the pty process, resume it if - // the writeBuffer is empty to allow more data to come in. - if (this._xoffSentToCatchUp && this.writeBuffer.length === 0) { + // we reached the end of the writeBuffer to allow more data to come in. + if (this._xoffSentToCatchUp && this.writeBuffer.length === bufferOffset) { this.handler(C0.DC1); this._xoffSentToCatchUp = false; } @@ -1385,11 +1386,12 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II break; } } - if (this.writeBuffer.length > 0) { + if (this.writeBuffer.length > bufferOffset) { // Allow renderer to catch up before processing the next batch - setTimeout(() => this._innerWrite(), 0); + setTimeout(() => this._innerWrite(bufferOffset), 0); } else { this._writeInProgress = false; + this.writeBuffer = []; } } From 51e2cdfafb697a8076d9122fcf0e7bbc8f1840e8 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Wed, 6 Mar 2019 21:45:53 +0000 Subject: [PATCH 26/47] WIP: First draft --- src/addons/webLinks/webLinks.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/addons/webLinks/webLinks.ts b/src/addons/webLinks/webLinks.ts index f0d69cc58..b9f2925a2 100644 --- a/src/addons/webLinks/webLinks.ts +++ b/src/addons/webLinks/webLinks.ts @@ -36,6 +36,13 @@ function handleLink(event: MouseEvent, uri: string): void { */ export function webLinksInit(term: Terminal, handler: (event: MouseEvent, uri: string) => void = handleLink, options: ILinkMatcherOptions = {}): void { options.matchIndex = 1; + + handler = (event, uri) => { + if (!term.hasSelection()) { + window.open(uri, '_blank'); + } + }; + term.registerLinkMatcher(strictUrlRegex, handler, options); } From bc41cc7d279e7edd2b7f50b8032252efc4d88f31 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Thu, 7 Mar 2019 00:15:03 +0000 Subject: [PATCH 27/47] Fix #1908 --- src/ui/MouseZoneManager.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/ui/MouseZoneManager.ts b/src/ui/MouseZoneManager.ts index a232f5b94..790227239 100644 --- a/src/ui/MouseZoneManager.ts +++ b/src/ui/MouseZoneManager.ts @@ -23,6 +23,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { private _areZonesActive: boolean = false; private _mouseMoveListener: (e: MouseEvent) => any; + private _mouseLeaveListener: (e: MouseEvent) => any; private _clickListener: (e: MouseEvent) => any; private _tooltipTimeout: number = null; @@ -38,6 +39,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { // These events are expensive, only listen to it when mouse zones are active this._mouseMoveListener = e => this._onMouseMove(e); + this._mouseLeaveListener = e => this._onMouseLeave(e); this._clickListener = e => this._onClick(e); } @@ -89,6 +91,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { if (!this._areZonesActive) { this._areZonesActive = true; this._terminal.element.addEventListener('mousemove', this._mouseMoveListener); + this._terminal.element.addEventListener('mouseleave', this._mouseLeaveListener); this._terminal.element.addEventListener('click', this._clickListener); } } @@ -97,6 +100,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { if (this._areZonesActive) { this._areZonesActive = false; this._terminal.element.removeEventListener('mousemove', this._mouseMoveListener); + this._terminal.element.removeEventListener('mouseleave', this._mouseLeaveListener); this._terminal.element.removeEventListener('click', this._clickListener); } } @@ -169,6 +173,18 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } } + private _onMouseLeave(e: MouseEvent): void { + // Fire the hover end callback and cancel any existing timer if the mouse + // leaves the terminal element + if (this._currentZone) { + this._currentZone.leaveCallback(); + this._currentZone = null; + if (this._tooltipTimeout) { + clearTimeout(this._tooltipTimeout); + } + } + } + private _onClick(e: MouseEvent): void { // Find the active zone and click it if found const zone = this._findZoneEventAt(e); From 5878aa099dcf093a9480bf110cde28f8ee011b69 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Thu, 7 Mar 2019 21:00:07 +0000 Subject: [PATCH 28/47] Cleaner aproach --- src/addons/webLinks/webLinks.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/addons/webLinks/webLinks.ts b/src/addons/webLinks/webLinks.ts index b9f2925a2..19200c902 100644 --- a/src/addons/webLinks/webLinks.ts +++ b/src/addons/webLinks/webLinks.ts @@ -24,9 +24,7 @@ const start = '(?:^|' + negatedDomainCharacterSet + ')('; const end = ')($|' + negatedPathCharacterSet + ')'; const strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end); -function handleLink(event: MouseEvent, uri: string): void { - window.open(uri, '_blank'); -} +let handleLink: (event: MouseEvent, uri: string) => void; /** * Initialize the web links addon, registering the link matcher. @@ -37,17 +35,17 @@ function handleLink(event: MouseEvent, uri: string): void { export function webLinksInit(term: Terminal, handler: (event: MouseEvent, uri: string) => void = handleLink, options: ILinkMatcherOptions = {}): void { options.matchIndex = 1; - handler = (event, uri) => { - if (!term.hasSelection()) { - window.open(uri, '_blank'); - } - }; - term.registerLinkMatcher(strictUrlRegex, handler, options); } export function apply(terminalConstructor: typeof Terminal): void { (terminalConstructor.prototype).webLinksInit = function (handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void { + handleLink = (event, uri) => { + if (!this.hasSelection()) { + window.open(uri, '_blank'); + } + }; + webLinksInit(this, handler, options); }; } From 525af9c36a3b57702d6048737d37aeb394464892 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Thu, 7 Mar 2019 21:30:32 +0000 Subject: [PATCH 29/47] Fix #1773 --- src/renderer/dom/DomRenderer.ts | 2 +- src/renderer/dom/DomRendererRowFactory.test.ts | 11 +++++++++-- src/renderer/dom/DomRendererRowFactory.ts | 7 ++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index c5ef212da..5bff3d1c6 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -75,7 +75,7 @@ export class DomRenderer extends EventEmitter implements IRenderer { this._updateDimensions(); this._renderDebouncer = new RenderDebouncer(this._terminal, this._renderRows.bind(this)); - this._rowFactory = new DomRendererRowFactory(document); + this._rowFactory = new DomRendererRowFactory(_terminal, document); this._terminal.element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass); this._terminal.screenElement.appendChild(this._rowContainer); diff --git a/src/renderer/dom/DomRendererRowFactory.test.ts b/src/renderer/dom/DomRendererRowFactory.test.ts index 67342da01..ab3080771 100644 --- a/src/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/renderer/dom/DomRendererRowFactory.test.ts @@ -9,17 +9,24 @@ import { DomRendererRowFactory } from './DomRendererRowFactory'; import { DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from '../../Buffer'; import { FLAGS } from '../Types'; import { BufferLine } from '../../BufferLine'; -import { IBufferLine } from '../../Types'; +import { IBufferLine, ITerminal } from '../../Types'; import { DEFAULT_COLOR } from '../atlas/Types'; +import { MockTerminal } from '../../ui/TestUtils.test'; describe('DomRendererRowFactory', () => { let dom: jsdom.JSDOM; + let term: ITerminal; let rowFactory: DomRendererRowFactory; let lineData: IBufferLine; beforeEach(() => { dom = new jsdom.JSDOM(''); - rowFactory = new DomRendererRowFactory(dom.window.document); + + term = new MockTerminal(); + term.options.enableBold = true; + term.options.drawBoldTextInBrightColors = true; + + rowFactory = new DomRendererRowFactory(term, dom.window.document); lineData = createEmptyLineData(2); }); diff --git a/src/renderer/dom/DomRendererRowFactory.ts b/src/renderer/dom/DomRendererRowFactory.ts index 8bcde39a0..f1865175e 100644 --- a/src/renderer/dom/DomRendererRowFactory.ts +++ b/src/renderer/dom/DomRendererRowFactory.ts @@ -5,7 +5,7 @@ import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_ATTR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CHAR } from '../../Buffer'; import { FLAGS } from '../Types'; -import { IBufferLine } from '../../Types'; +import { IBufferLine, ITerminal } from '../../Types'; import { DEFAULT_COLOR, INVERTED_DEFAULT_COLOR } from '../atlas/Types'; export const BOLD_CLASS = 'xterm-bold'; @@ -17,6 +17,7 @@ export const CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline'; export class DomRendererRowFactory { constructor( + private _terminal: ITerminal, private _document: Document ) { } @@ -88,10 +89,10 @@ export class DomRendererRowFactory { } } - if (flags & FLAGS.BOLD) { + if (flags & FLAGS.BOLD && this._terminal.options.enableBold) { // Convert the FG color to the bold variant. This should not happen when // the fg is the inverse default color as there is no bold variant. - if (fg < 8) { + if (fg < 8 && this._terminal.options.drawBoldTextInBrightColors) { fg += 8; } charElement.classList.add(BOLD_CLASS); From 4046d682c9770276240746cd5d46647ddfb2e10d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 8 Mar 2019 09:21:01 -0800 Subject: [PATCH 30/47] v3.12.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa9cc0706..c5fad5159 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "xterm", "description": "Full xterm terminal, in your browser", - "version": "3.11.0", + "version": "3.12.0", "main": "lib/public/Terminal.js", "types": "typings/xterm.d.ts", "repository": "https://github.com/xtermjs/xterm.js", From b4bef0986cb99f2b43e4cb8ab372ec839bdb0422 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Sun, 10 Mar 2019 22:03:38 +0000 Subject: [PATCH 31/47] Remove circular dependency --- src/renderer/dom/DomRenderer.ts | 2 +- src/renderer/dom/DomRendererRowFactory.test.ts | 12 +++++------- src/renderer/dom/DomRendererRowFactory.ts | 8 ++++---- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index 5bff3d1c6..3a5f29e64 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -75,7 +75,7 @@ export class DomRenderer extends EventEmitter implements IRenderer { this._updateDimensions(); this._renderDebouncer = new RenderDebouncer(this._terminal, this._renderRows.bind(this)); - this._rowFactory = new DomRendererRowFactory(_terminal, document); + this._rowFactory = new DomRendererRowFactory(_terminal.options, document); this._terminal.element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass); this._terminal.screenElement.appendChild(this._rowContainer); diff --git a/src/renderer/dom/DomRendererRowFactory.test.ts b/src/renderer/dom/DomRendererRowFactory.test.ts index ab3080771..76c07781d 100644 --- a/src/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/renderer/dom/DomRendererRowFactory.test.ts @@ -9,24 +9,22 @@ import { DomRendererRowFactory } from './DomRendererRowFactory'; import { DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from '../../Buffer'; import { FLAGS } from '../Types'; import { BufferLine } from '../../BufferLine'; -import { IBufferLine, ITerminal } from '../../Types'; +import { IBufferLine, ITerminalOptions } from '../../Types'; import { DEFAULT_COLOR } from '../atlas/Types'; -import { MockTerminal } from '../../ui/TestUtils.test'; describe('DomRendererRowFactory', () => { let dom: jsdom.JSDOM; - let term: ITerminal; + const options: ITerminalOptions = {}; let rowFactory: DomRendererRowFactory; let lineData: IBufferLine; beforeEach(() => { dom = new jsdom.JSDOM(''); - term = new MockTerminal(); - term.options.enableBold = true; - term.options.drawBoldTextInBrightColors = true; + options.enableBold = true; + options.drawBoldTextInBrightColors = true; - rowFactory = new DomRendererRowFactory(term, dom.window.document); + rowFactory = new DomRendererRowFactory(options, dom.window.document); lineData = createEmptyLineData(2); }); diff --git a/src/renderer/dom/DomRendererRowFactory.ts b/src/renderer/dom/DomRendererRowFactory.ts index f1865175e..206c0c812 100644 --- a/src/renderer/dom/DomRendererRowFactory.ts +++ b/src/renderer/dom/DomRendererRowFactory.ts @@ -5,7 +5,7 @@ import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_ATTR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CHAR } from '../../Buffer'; import { FLAGS } from '../Types'; -import { IBufferLine, ITerminal } from '../../Types'; +import { IBufferLine, ITerminalOptions } from '../../Types'; import { DEFAULT_COLOR, INVERTED_DEFAULT_COLOR } from '../atlas/Types'; export const BOLD_CLASS = 'xterm-bold'; @@ -17,7 +17,7 @@ export const CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline'; export class DomRendererRowFactory { constructor( - private _terminal: ITerminal, + private _terminalOptions: ITerminalOptions, private _document: Document ) { } @@ -89,10 +89,10 @@ export class DomRendererRowFactory { } } - if (flags & FLAGS.BOLD && this._terminal.options.enableBold) { + if (flags & FLAGS.BOLD && this._terminalOptions.enableBold) { // Convert the FG color to the bold variant. This should not happen when // the fg is the inverse default color as there is no bold variant. - if (fg < 8 && this._terminal.options.drawBoldTextInBrightColors) { + if (fg < 8 && this._terminalOptions.drawBoldTextInBrightColors) { fg += 8; } charElement.classList.add(BOLD_CLASS); From 19d36f92286267e43fe94ee010e3743c421810a3 Mon Sep 17 00:00:00 2001 From: turtle0x1 <12494629+turtle0x1@users.noreply.github.com> Date: Mon, 11 Mar 2019 13:47:25 +0000 Subject: [PATCH 32/47] Update readme with link for lxdmosaic --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5378f4430..481590a75 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 - [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client. - [**info-beamer hosted**](https://info-beamer.com): Uses xterm.js to manage digital signage devices from the web dashboard. - [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use xterm.js for web terminal emulation. +- [**LxdMosaic**](https://github.com/turtle0x1/LxdMosaic): Uses xterm.js to give terminal access to containers through LXD [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) From 62cfa642cbe5b2bce1d8cc0d71c2a8cdc1198bdb Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Mon, 11 Mar 2019 20:54:17 +0000 Subject: [PATCH 33/47] Better aproach, check i a selection is being performed --- src/addons/webLinks/webLinks.ts | 11 +++-------- src/ui/MouseZoneManager.ts | 12 ++++++++++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/addons/webLinks/webLinks.ts b/src/addons/webLinks/webLinks.ts index 19200c902..f0d69cc58 100644 --- a/src/addons/webLinks/webLinks.ts +++ b/src/addons/webLinks/webLinks.ts @@ -24,7 +24,9 @@ const start = '(?:^|' + negatedDomainCharacterSet + ')('; const end = ')($|' + negatedPathCharacterSet + ')'; const strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end); -let handleLink: (event: MouseEvent, uri: string) => void; +function handleLink(event: MouseEvent, uri: string): void { + window.open(uri, '_blank'); +} /** * Initialize the web links addon, registering the link matcher. @@ -34,18 +36,11 @@ let handleLink: (event: MouseEvent, uri: string) => void; */ export function webLinksInit(term: Terminal, handler: (event: MouseEvent, uri: string) => void = handleLink, options: ILinkMatcherOptions = {}): void { options.matchIndex = 1; - term.registerLinkMatcher(strictUrlRegex, handler, options); } export function apply(terminalConstructor: typeof Terminal): void { (terminalConstructor.prototype).webLinksInit = function (handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void { - handleLink = (event, uri) => { - if (!this.hasSelection()) { - window.open(uri, '_blank'); - } - }; - webLinksInit(this, handler, options); }; } diff --git a/src/ui/MouseZoneManager.ts b/src/ui/MouseZoneManager.ts index 790227239..3b848795e 100644 --- a/src/ui/MouseZoneManager.ts +++ b/src/ui/MouseZoneManager.ts @@ -29,6 +29,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { private _tooltipTimeout: number = null; private _currentZone: IMouseZone = null; private _lastHoverCoords: [number, number] = [null, null]; + private _initialSelectionLenght: number; constructor( private _terminal: ITerminal @@ -157,6 +158,10 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } private _onMouseDown(e: MouseEvent): void { + // Store current terminal selection length, to check if we're performing + // a selection operation + this._initialSelectionLenght = this._terminal.getSelection().length; + // Ignore the event if there are no zones active if (!this._areZonesActive) { return; @@ -186,9 +191,12 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } private _onClick(e: MouseEvent): void { - // Find the active zone and click it if found + // Find the active zone and click it if found and no selection was + // being performed const zone = this._findZoneEventAt(e); - if (zone) { + const currentSelectionLength = this._terminal.getSelection().length; + + if (zone && currentSelectionLength === this._initialSelectionLenght) { zone.clickCallback(e); e.preventDefault(); e.stopImmediatePropagation(); From 27ebe8c353cb8988f71e665ce4765e9c5f02c597 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Mon, 11 Mar 2019 20:58:42 +0000 Subject: [PATCH 34/47] Use new vscode serverReadyAction --- .vscode/launch.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c7bf73813..2ec26fcaf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -38,7 +38,11 @@ "run", "start-debug" ], - "port": 9229 + "port": 9229, + "serverReadyAction": { + "action": "openExternally", + "pattern": "App listening to (http://.*?:[0-9]+)" + } } ] } From 0d63cecc6bed2561cae1ca99e6c64d3f35a3cea6 Mon Sep 17 00:00:00 2001 From: Jianhui Zhao Date: Tue, 19 Mar 2019 14:04:16 +0800 Subject: [PATCH 35/47] Modify uses Signed-off-by: Jianhui Zhao --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 481590a75..40de04cf4 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters. - [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure. - [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace. -- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server. +- [**rtty**](https://github.com/zhaojh329/rtty): Access your terminals from anywhere via the web. - [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS. - [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker. - [**Azure Data Studio**](https://github.com/Microsoft/azuredatastudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux. From 131e4f78bdc1e036feed1a0aee03cddee25f6e63 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 23 Mar 2019 09:08:55 -0700 Subject: [PATCH 36/47] Fix renderer pausing to not full refresh every time Fixes #1975 --- src/renderer/Renderer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index b8ef87aab..2c1b516ae 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -85,6 +85,7 @@ export class Renderer extends EventEmitter implements IRenderer { this._isPaused = entry.intersectionRatio === 0; if (!this._isPaused && this._needsFullRefresh) { this._terminal.refresh(0, this._terminal.rows - 1); + this._needsFullRefresh = false; } } From 4dbe8ec1db2d6005186b547cd2fb87f710aeb48c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 23 Mar 2019 10:58:26 -0700 Subject: [PATCH 37/47] Let consumers decide whether winptyCompat should be active --- demo/client.ts | 5 ++++- src/addons/winptyCompat/winptyCompat.ts | 6 ------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index a3a912f63..70996c4a7 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -30,7 +30,10 @@ Terminal.applyAddon(fit); Terminal.applyAddon(fullscreen); Terminal.applyAddon(search); Terminal.applyAddon(webLinks); -Terminal.applyAddon(winptyCompat); +const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; +if (isWindows) { + Terminal.applyAddon(winptyCompat); +} let term; diff --git a/src/addons/winptyCompat/winptyCompat.ts b/src/addons/winptyCompat/winptyCompat.ts index d162f4e92..58f59fd90 100644 --- a/src/addons/winptyCompat/winptyCompat.ts +++ b/src/addons/winptyCompat/winptyCompat.ts @@ -13,12 +13,6 @@ const WHITESPACE_CELL_CODE = 32; export function winptyCompatInit(terminal: Terminal): void { const addonTerminal = terminal; - // Don't do anything when the platform is not Windows - const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; - if (!isWindows) { - return; - } - (addonTerminal._core as any).isWinptyCompatEnabled = true; // Winpty does not support wraparound mode which means that lines will never From f9df863ee05244f34536ae9c64606d38abd04a61 Mon Sep 17 00:00:00 2001 From: Jesse Stolwijk Date: Mon, 25 Mar 2019 22:17:16 +0100 Subject: [PATCH 38/47] Add blinking cursor to DomRenderer --- src/renderer/dom/DomRenderer.ts | 15 +++++++-- .../dom/DomRendererRowFactory.test.ts | 31 ++++++++++++------- src/renderer/dom/DomRendererRowFactory.ts | 7 ++++- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index c5ef212da..1da20d957 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -9,7 +9,7 @@ import { ITheme } from 'xterm'; import { EventEmitter } from '../../common/EventEmitter'; import { ColorManager } from '../ColorManager'; import { RenderDebouncer } from '../../ui/RenderDebouncer'; -import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from './DomRendererRowFactory'; +import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from './DomRendererRowFactory'; import { INVERTED_DEFAULT_COLOR } from '../atlas/Types'; const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; @@ -165,12 +165,22 @@ export class DomRenderer extends EventEmitter implements IRenderer { `${this._terminalSelector} span.${ITALIC_CLASS} {` + ` font-style: italic;` + `}`; + // Blink animation + styles += + `@keyframes blink {` + + ` 0 % { opacity: 1.0; }` + + ` 50% { opacity: 0.0; }` + + ` 100 % { opacity: 1.0; }` + + `}`; // Cursor styles += `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS} {` + ` outline: 1px solid ${this.colorManager.colors.cursor.css};` + ` outline-offset: -1px;` + `}` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS} {` + + ` animation: blink 1s step-end infinite;` + + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + ` background-color: ${this.colorManager.colors.cursor.css};` + ` color: ${this.colorManager.colors.cursorAccent.css};` + @@ -328,6 +338,7 @@ export class DomRenderer extends EventEmitter implements IRenderer { const cursorAbsoluteY = terminal.buffer.ybase + terminal.buffer.y; const cursorX = this._terminal.buffer.x; + const cursorBlink = this._terminal.options.cursorBlink; for (let y = start; y <= end; y++) { const rowElement = this._rowElements[y]; @@ -336,7 +347,7 @@ export class DomRenderer extends EventEmitter implements IRenderer { const row = y + terminal.buffer.ydisp; const lineData = terminal.buffer.lines.get(row); const cursorStyle = terminal.options.cursorStyle; - rowElement.appendChild(this._rowFactory.createRow(lineData, row === cursorAbsoluteY, cursorStyle, cursorX, this.dimensions.actualCellWidth, terminal.cols)); + rowElement.appendChild(this._rowFactory.createRow(lineData, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, terminal.cols)); } this._terminal.emit('refresh', {start, end}); diff --git a/src/renderer/dom/DomRendererRowFactory.test.ts b/src/renderer/dom/DomRendererRowFactory.test.ts index 67342da01..07e7686d0 100644 --- a/src/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/renderer/dom/DomRendererRowFactory.test.ts @@ -25,7 +25,7 @@ describe('DomRendererRowFactory', () => { describe('createRow', () => { it('should not create anything for an empty row', () => { - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), '' ); @@ -35,7 +35,7 @@ describe('DomRendererRowFactory', () => { lineData.set(0, [DEFAULT_ATTR, '語', 2, '語'.charCodeAt(0)]); // There should be no element for the following "empty" cell lineData.set(1, [DEFAULT_ATTR, '', 0, undefined]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), '語' ); @@ -43,17 +43,24 @@ describe('DomRendererRowFactory', () => { it('should add class for cursor and cursor style', () => { for (const style of ['block', 'bar', 'underline']) { - const fragment = rowFactory.createRow(lineData, true, style, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, true, style, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), ` ` ); } }); + it('should add class for cursor blink', () => { + const fragment = rowFactory.createRow(lineData, true, 'block', 0, true, 5, 20); + assert.equal(getFragmentHtml(fragment), + ` ` + ); + }); + it('should not render cells that go beyond the terminal\'s columns', () => { lineData.set(0, [DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]); lineData.set(1, [DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 1); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 1); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -62,7 +69,7 @@ describe('DomRendererRowFactory', () => { describe('attributes', () => { it('should add class for bold', () => { lineData.set(0, [DEFAULT_ATTR | (FLAGS.BOLD << 18), 'a', 1, 'a'.charCodeAt(0)]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -70,7 +77,7 @@ describe('DomRendererRowFactory', () => { it('should add class for italic', () => { lineData.set(0, [DEFAULT_ATTR | (FLAGS.ITALIC << 18), 'a', 1, 'a'.charCodeAt(0)]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -80,7 +87,7 @@ describe('DomRendererRowFactory', () => { const defaultAttrNoFgColor = (0 << 9) | (DEFAULT_COLOR << 0); for (let i = 0; i < 256; i++) { lineData.set(0, [defaultAttrNoFgColor | (i << 9), 'a', 1, 'a'.charCodeAt(0)]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), `a` ); @@ -91,7 +98,7 @@ describe('DomRendererRowFactory', () => { const defaultAttrNoBgColor = (DEFAULT_ATTR << 9) | (0 << 0); for (let i = 0; i < 256; i++) { lineData.set(0, [defaultAttrNoBgColor | (i << 0), 'a', 1, 'a'.charCodeAt(0)]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), `a` ); @@ -100,7 +107,7 @@ describe('DomRendererRowFactory', () => { it('should correctly invert colors', () => { lineData.set(0, [(FLAGS.INVERSE << 18) | (2 << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -108,7 +115,7 @@ describe('DomRendererRowFactory', () => { it('should correctly invert default fg color', () => { lineData.set(0, [(FLAGS.INVERSE << 18) | (DEFAULT_ATTR << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -116,7 +123,7 @@ describe('DomRendererRowFactory', () => { it('should correctly invert default bg color', () => { lineData.set(0, [(FLAGS.INVERSE << 18) | (1 << 9) | (DEFAULT_COLOR << 0), 'a', 1, 'a'.charCodeAt(0)]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -125,7 +132,7 @@ describe('DomRendererRowFactory', () => { it('should turn bold fg text bright', () => { for (let i = 0; i < 8; i++) { lineData.set(0, [(FLAGS.BOLD << 18) | (i << 9) | (DEFAULT_COLOR << 0), 'a', 1, 'a'.charCodeAt(0)]); - const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); + const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20); assert.equal(getFragmentHtml(fragment), `a` ); diff --git a/src/renderer/dom/DomRendererRowFactory.ts b/src/renderer/dom/DomRendererRowFactory.ts index 8bcde39a0..fd263b21c 100644 --- a/src/renderer/dom/DomRendererRowFactory.ts +++ b/src/renderer/dom/DomRendererRowFactory.ts @@ -11,6 +11,7 @@ import { DEFAULT_COLOR, INVERTED_DEFAULT_COLOR } from '../atlas/Types'; export const BOLD_CLASS = 'xterm-bold'; export const ITALIC_CLASS = 'xterm-italic'; export const CURSOR_CLASS = 'xterm-cursor'; +export const CURSOR_BLINK_CLASS = 'xterm-cursor-blink'; export const CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block'; export const CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar'; export const CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline'; @@ -21,7 +22,7 @@ export class DomRendererRowFactory { ) { } - public createRow(lineData: IBufferLine, isCursorRow: boolean, cursorStyle: string | undefined, cursorX: number, cellWidth: number, cols: number): DocumentFragment { + public createRow(lineData: IBufferLine, isCursorRow: boolean, cursorStyle: string | undefined, cursorX: number, cursorBlink: boolean, cellWidth: number, cols: number): DocumentFragment { const fragment = this._document.createDocumentFragment(); // Find the line length first, this prevents the need to output a bunch of @@ -62,6 +63,10 @@ export class DomRendererRowFactory { if (isCursorRow && x === cursorX) { charElement.classList.add(CURSOR_CLASS); + if (cursorBlink) { + charElement.classList.add(CURSOR_BLINK_CLASS); + } + switch (cursorStyle) { case 'bar': charElement.classList.add(CURSOR_STYLE_BAR_CLASS); From e346f8bba37ee094f4710cd1c8dc43438fa01a79 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 29 Mar 2019 19:46:24 -0700 Subject: [PATCH 39/47] Prevent scroll on focus Fixes #1981 --- src/Terminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 4a85758f0..c2497df44 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -343,7 +343,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II */ public focus(): void { if (this.textarea) { - this.textarea.focus(); + this.textarea.focus({ preventScroll: true }); } } From 0b78011fa34e2f79dd3f2cd68c111a171f45c68c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 30 Mar 2019 14:05:24 -0700 Subject: [PATCH 40/47] Adopt project references Recent versions of TypeScript has improved the performance of project references so they are now viable to switch over to. --- demo/index.html | 4 ++-- demo/server.js | 1 + gulpfile.js | 10 +++++----- package.json | 10 +++------- src/common/tsconfig.json | 14 ++------------ src/core/tsconfig.json | 20 ++++++-------------- src/tsconfig-base.json | 15 +++++++++++++++ src/tsconfig-library-base.json | 11 +++++++++++ src/tsconfig.all.json | 16 ++++++++++++++++ tsconfig.json => src/tsconfig.json | 21 ++++++++++++--------- yarn.lock | 8 ++++---- 11 files changed, 77 insertions(+), 53 deletions(-) create mode 100644 src/tsconfig-base.json create mode 100644 src/tsconfig-library-base.json create mode 100644 src/tsconfig.all.json rename tsconfig.json => src/tsconfig.json (53%) diff --git a/demo/index.html b/demo/index.html index 12f7cb983..370a51ed5 100644 --- a/demo/index.html +++ b/demo/index.html @@ -2,8 +2,8 @@ xterm.js demo - - + + diff --git a/demo/server.js b/demo/server.js index 5ff9ca61a..37df9916c 100644 --- a/demo/server.js +++ b/demo/server.js @@ -11,6 +11,7 @@ function startServer() { logs = {}; app.use('/build', express.static(__dirname + '/../build')); + app.use('/src', express.static(__dirname + '/../src')); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); diff --git a/gulpfile.js b/gulpfile.js index 9af8d6e4a..bbb4d6e16 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,9 +16,9 @@ const ts = require('gulp-typescript'); const util = require('gulp-util'); const buildDir = process.env.BUILD_DIR || 'build'; -const tsProject = ts.createProject('tsconfig.json'); -let srcDir = tsProject.config.compilerOptions.rootDir; -let outDir = tsProject.config.compilerOptions.outDir; +const tsProject = ts.createProject('src/tsconfig.json'); +let srcDir = './src'; +let outDir = './lib'; const addons = fs.readdirSync(`${__dirname}/src/addons`); @@ -61,7 +61,7 @@ gulp.task('browserify', function() { }; let bundleStream = browserify(browserifyOptions) .bundle() - .pipe(source('xterm.js')) + .pipe(source(`xterm.js`)) .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true, sourceRoot: '..'})) .pipe(sourcemaps.write('./')) @@ -136,6 +136,6 @@ gulp.task('sorcery-addons', ['browserify-addons'], function () { }) }); -gulp.task('build', ['sorcery', 'sorcery-addons']); +gulp.task('build', ['css', 'sorcery', 'sorcery-addons']); gulp.task('test', ['mocha']); gulp.task('default', ['build']); diff --git a/package.json b/package.json index c5fad5159..946db6094 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "ts-loader": "^4.5.0", "tslint": "^5.9.1", "tslint-consistent-codestyle": "^1.13.0", - "typescript": "3.1", + "typescript": "3.4", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", "webpack": "^4.17.1", @@ -50,20 +50,16 @@ "start-debug": "node --inspect-brk demo/start", "start-zmodem": "node demo/zmodem/app", "lint": "tslint 'src/**/*.ts' './demo/**/*.ts'", - "pretest": "npm run layering", "test": "npm run mocha", "posttest": "npm run lint", "test-debug": "node --inspect-brk node_modules/.bin/gulp test", "test-suite": "gulp mocha-suite --test", "test-coverage": "nyc -x gulpfile.js -x '**/*test*' npm run mocha", "mocha": "gulp test", - "tsc": "tsc", - "prebuild": "concurrently --kill-others-on-fail --names \"lib,attach,fit,fullscreen,search,terminado,webLinks,winptyCompat,zmodem,css\" \"tsc\" \"tsc -p ./src/addons/attach\" \"tsc -p ./src/addons/fit\" \"tsc -p ./src/addons/fullscreen\" \"tsc -p ./src/addons/search\" \"tsc -p ./src/addons/terminado\" \"tsc -p ./src/addons/webLinks\" \"tsc -p ./src/addons/winptyCompat\" \"tsc -p ./src/addons/zmodem\" \"gulp css\"", + "prebuild": "tsc -b ./src/tsconfig.all.json", "build": "gulp build", "prepublish": "npm run build", "coveralls": "nyc report --reporter=text-lcov | coveralls", - "watch": "concurrently --kill-others-on-fail --names \"lib,css\" \"tsc -w\" \"gulp watch-css\"", - "watch-addons": "concurrently --kill-others-on-fail --names \"attach,fit,fullscreen,search,terminado,webLinks,winptyCompat,zmodem\" \"tsc -w -p ./src/addons/attach\" \"tsc -w -p ./src/addons/fit\" \"tsc -w -p ./src/addons/fullscreen\" \"tsc -w -p ./src/addons/search\" \"tsc -w -p ./src/addons/terminado\" \"tsc -w -p ./src/addons/webLinks\" \"tsc -w -p ./src/addons/winptyCompat\" \"tsc -w -p ./src/addons/zmodem\"", - "layering": "concurrently --kill-others-on-fail --names \"common,core\" \"tsc -p ./src/common\" \"tsc -p ./src/core\"" + "watch": "tsc -b -w ./src/tsconfig.all.json" } } diff --git a/src/common/tsconfig.json b/src/common/tsconfig.json index 19dd02731..b40bb2f50 100644 --- a/src/common/tsconfig.json +++ b/src/common/tsconfig.json @@ -1,17 +1,7 @@ { + "extends": "../tsconfig-library-base", "compilerOptions": { - "target": "es5", - "lib": [ - "es5" - ], - "rootDir": ".", - "noEmit": true, - "strict": true, - "pretty": true, - "types": [ - "../../node_modules/@types/mocha", - "../../" - ] + "outDir": "../../lib" }, "include": [ "./**/*" diff --git a/src/core/tsconfig.json b/src/core/tsconfig.json index 4f024a28d..41e41f0c1 100644 --- a/src/core/tsconfig.json +++ b/src/core/tsconfig.json @@ -1,20 +1,12 @@ { + "extends": "../tsconfig-library-base", "compilerOptions": { - "target": "es5", - "lib": [ - "es5" - ], - "rootDir": ".", - "noEmit": true, - "strict": true, - "pretty": true, - "types": [ - "../../node_modules/@types/mocha", - "../../" - ] + "outDir": "../../lib" }, "include": [ - "./**/*", - "../common/**/*" + "./**/*" + ], + "references": [ + { "path": "../common" } ] } diff --git a/src/tsconfig-base.json b/src/tsconfig-base.json new file mode 100644 index 000000000..5c6afcc5b --- /dev/null +++ b/src/tsconfig-base.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ "es5" ], + "rootDir": ".", + + "sourceMap": true, + "removeComments": true, + "pretty": true, + + "incremental": true, + + "skipLibCheck": true + } +} diff --git a/src/tsconfig-library-base.json b/src/tsconfig-library-base.json new file mode 100644 index 000000000..c82e08735 --- /dev/null +++ b/src/tsconfig-library-base.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "types": [ + "../../node_modules/@types/mocha", + "../../" + ], + "composite": true, + "strict": true + } +} diff --git a/src/tsconfig.all.json b/src/tsconfig.all.json new file mode 100644 index 000000000..bee5df323 --- /dev/null +++ b/src/tsconfig.all.json @@ -0,0 +1,16 @@ +{ + "files": [], + "include": [], + "references": [ + { "path": "." }, + { "path": "./addons/attach" }, + { "path": "./addons/fit" }, + { "path": "./addons/fullscreen" }, + { "path": "./addons/search" }, + { "path": "./addons/terminado" }, + { "path": "./addons/webLinks" }, + { "path": "./addons/winptyCompat" }, + { "path": "./addons/zmodem" } + ] +} + \ No newline at end of file diff --git a/tsconfig.json b/src/tsconfig.json similarity index 53% rename from tsconfig.json rename to src/tsconfig.json index 2d1d6e35f..0aa3abb88 100644 --- a/tsconfig.json +++ b/src/tsconfig.json @@ -1,7 +1,7 @@ { + "extends": "./tsconfig-base", "compilerOptions": { "module": "commonjs", - "target": "es5", "lib": [ "dom", "es5", @@ -9,19 +9,22 @@ "scripthost", "es2015.promise" ], - "rootDir": "src", - "outDir": "lib", - "sourceMap": true, - "removeComments": true, - "preserveWatchOutput": true, + "rootDir": ".", + "outDir": "../lib", + "noUnusedLocals": true, "noImplicitAny": true }, "include": [ - "src/**/*", - "typings/xterm.d.ts" + "./**/*", + "../typings/xterm.d.ts" ], "exclude": [ - "src/addons/**/*" + "./addons/**/*" + ], + "references": [ + { "path": "./common" }, + { "path": "./core" } ] } + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 5555321d1..896db4bfd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6633,10 +6633,10 @@ typedarray@^0.0.6, typedarray@~0.0.5: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.1: - version "3.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68" - integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA== +typescript@3.4: + version "3.4.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6" + integrity sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q== uglify-es@^3.3.4: version "3.3.9" From b400f9ca3b74fbcb0c7506ec6b4456d835f1fc5a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 30 Mar 2019 14:08:24 -0700 Subject: [PATCH 41/47] Don't clear terminal on yarn watch --- package.json | 2 +- src/addons/attach/tsconfig.json | 3 +-- src/addons/fit/tsconfig.json | 1 - src/addons/fullscreen/tsconfig.json | 1 - src/addons/search/tsconfig.json | 1 - src/addons/terminado/tsconfig.json | 1 - src/addons/webLinks/tsconfig.json | 1 - src/addons/winptyCompat/tsconfig.json | 1 - src/addons/zmodem/tsconfig.json | 1 - 9 files changed, 2 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 946db6094..d917039b2 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,6 @@ "build": "gulp build", "prepublish": "npm run build", "coveralls": "nyc report --reporter=text-lcov | coveralls", - "watch": "tsc -b -w ./src/tsconfig.all.json" + "watch": "tsc -b -w ./src/tsconfig.all.json --preserveWatchOutput" } } diff --git a/src/addons/attach/tsconfig.json b/src/addons/attach/tsconfig.json index 359fbd240..2f39102ca 100644 --- a/src/addons/attach/tsconfig.json +++ b/src/addons/attach/tsconfig.json @@ -10,8 +10,7 @@ "outDir": "../../../lib/addons/attach/", "sourceMap": true, "removeComments": true, - "declaration": true, - "preserveWatchOutput": true + "declaration": true }, "include": [ "**/*.ts", diff --git a/src/addons/fit/tsconfig.json b/src/addons/fit/tsconfig.json index 489ccdfea..3458d23a7 100644 --- a/src/addons/fit/tsconfig.json +++ b/src/addons/fit/tsconfig.json @@ -11,7 +11,6 @@ "sourceMap": true, "removeComments": true, "declaration": true, - "preserveWatchOutput": true, "types": [ "../../node_modules/@types/mocha" ] diff --git a/src/addons/fullscreen/tsconfig.json b/src/addons/fullscreen/tsconfig.json index 05e6df68d..0c74c25c0 100644 --- a/src/addons/fullscreen/tsconfig.json +++ b/src/addons/fullscreen/tsconfig.json @@ -11,7 +11,6 @@ "sourceMap": true, "removeComments": true, "declaration": true, - "preserveWatchOutput": true, "types": [ "../../node_modules/@types/mocha" ] diff --git a/src/addons/search/tsconfig.json b/src/addons/search/tsconfig.json index 87899cda0..6a1611a51 100644 --- a/src/addons/search/tsconfig.json +++ b/src/addons/search/tsconfig.json @@ -11,7 +11,6 @@ "sourceMap": true, "removeComments": true, "declaration": true, - "preserveWatchOutput": true, "types": [ "../../node_modules/@types/mocha" ] diff --git a/src/addons/terminado/tsconfig.json b/src/addons/terminado/tsconfig.json index 91c18314e..e2e194457 100644 --- a/src/addons/terminado/tsconfig.json +++ b/src/addons/terminado/tsconfig.json @@ -10,7 +10,6 @@ "sourceMap": true, "removeComments": true, "declaration": true, - "preserveWatchOutput": true, "types": [ "../../node_modules/@types/mocha" ] diff --git a/src/addons/webLinks/tsconfig.json b/src/addons/webLinks/tsconfig.json index 18105aa24..9c4f11760 100644 --- a/src/addons/webLinks/tsconfig.json +++ b/src/addons/webLinks/tsconfig.json @@ -11,7 +11,6 @@ "sourceMap": true, "removeComments": true, "declaration": true, - "preserveWatchOutput": true, "types": [ "../../node_modules/@types/mocha" ] diff --git a/src/addons/winptyCompat/tsconfig.json b/src/addons/winptyCompat/tsconfig.json index 9fc4d25e3..fa48c963e 100644 --- a/src/addons/winptyCompat/tsconfig.json +++ b/src/addons/winptyCompat/tsconfig.json @@ -10,7 +10,6 @@ "sourceMap": true, "removeComments": true, "declaration": true, - "preserveWatchOutput": true, "types": [ "../../node_modules/@types/mocha" ] diff --git a/src/addons/zmodem/tsconfig.json b/src/addons/zmodem/tsconfig.json index 2b49f537e..7d821b7c5 100644 --- a/src/addons/zmodem/tsconfig.json +++ b/src/addons/zmodem/tsconfig.json @@ -10,7 +10,6 @@ "sourceMap": true, "removeComments": true, "declaration": true, - "preserveWatchOutput": true, "types": [ "../../node_modules/@types/mocha" ] From 18f4dc6b3eda8432231cc580fa4221c13e1113ec Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 30 Mar 2019 14:09:51 -0700 Subject: [PATCH 42/47] Remove concurrently --- package.json | 1 - yarn.lock | 87 +++------------------------------------------------- 2 files changed, 4 insertions(+), 84 deletions(-) diff --git a/package.json b/package.json index d917039b2..539b96daf 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "@types/webpack": "^4.4.11", "browserify": "^13.3.0", "chai": "3.5.0", - "concurrently": "^3.5.1", "coveralls": "^3.0.1", "express": "4.13.4", "express-ws": "2.0.0-rc.1", diff --git a/yarn.lock b/yarn.lock index 896db4bfd..440aa4f82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1275,11 +1275,6 @@ combined-stream@1.0.6, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d" - integrity sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0= - commander@2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" @@ -1338,21 +1333,6 @@ concat-with-sourcemaps@^1.0.0: dependencies: source-map "^0.6.1" -concurrently@^3.5.1: - version "3.6.0" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.6.0.tgz#c25e34b156a9d5bd4f256a0d85f6192438ae481f" - integrity sha512-6XiIYtYzmGEccNZFkih5JOH92jLA4ulZArAYy5j1uDSdrPLB3KzdE8GW7t2fHPcg9ry2+5LP9IEYzXzxw9lFdA== - dependencies: - chalk "^2.4.1" - commander "2.6.0" - date-fns "^1.23.0" - lodash "^4.5.1" - read-pkg "^3.0.0" - rx "2.3.24" - spawn-command "^0.0.2-1" - supports-color "^3.2.3" - tree-kill "^1.1.0" - configstore@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021" @@ -1595,11 +1575,6 @@ data-urls@^1.0.0: whatwg-mimetype "^2.0.0" whatwg-url "^6.4.0" -date-fns@^1.23.0: - version "1.29.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" - integrity sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw== - date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" @@ -1933,7 +1908,7 @@ errno@^0.1.3, errno@~0.1.7: dependencies: prr "~1.0.1" -error-ex@^1.2.0, error-ex@^1.3.1: +error-ex@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== @@ -3625,7 +3600,7 @@ jsesc@^1.3.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== @@ -3815,16 +3790,6 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" @@ -4037,7 +4002,7 @@ lodash.templatesettings@^3.0.0: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" -lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.5.1: +lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.4: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== @@ -4984,14 +4949,6 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" @@ -5092,13 +5049,6 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" @@ -5384,15 +5334,6 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -5688,11 +5629,6 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rx@2.3.24: - version "2.3.24" - resolved "https://registry.yarnpkg.com/rx/-/rx-2.3.24.tgz#14f950a4217d7e35daa71bbcbe58eff68ea4b2b7" - integrity sha1-FPlQpCF9fjXapxu8vljv9o6ksrc= - rxjs@^6.1.0: version "6.3.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.1.tgz#878a1a8c64b8a5da11dcf74b5033fe944cdafb84" @@ -6025,11 +5961,6 @@ sparkles@^1.0.0: resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== -spawn-command@^0.0.2-1: - version "0.0.2-1" - resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" - integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= - spawn-wrap@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.2.tgz#cff58e73a8224617b6561abdc32586ea0c82248c" @@ -6271,11 +6202,6 @@ strip-bom@^1.0.0: first-chunk-stream "^1.0.0" is-utf8 "^0.2.0" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -6305,7 +6231,7 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^3.1.2, supports-color@^3.2.3: +supports-color@^3.1.2: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= @@ -6521,11 +6447,6 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" -tree-kill@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" - integrity sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg== - trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" From f80924fb8d5155142a3bd258f64a71176680f7de Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 30 Mar 2019 14:12:15 -0700 Subject: [PATCH 43/47] Remove zmodem demo --- demo/server.js | 1 - demo/zmodem/app.js | 87 --------- demo/zmodem/index.html | 128 -------------- demo/zmodem/main.js | 388 ----------------------------------------- 4 files changed, 604 deletions(-) delete mode 100644 demo/zmodem/app.js delete mode 100644 demo/zmodem/index.html delete mode 100644 demo/zmodem/main.js diff --git a/demo/server.js b/demo/server.js index 37df9916c..c41110ff0 100644 --- a/demo/server.js +++ b/demo/server.js @@ -10,7 +10,6 @@ function startServer() { var terminals = {}, logs = {}; - app.use('/build', express.static(__dirname + '/../build')); app.use('/src', express.static(__dirname + '/../src')); app.get('/', function(req, res){ diff --git a/demo/zmodem/app.js b/demo/zmodem/app.js deleted file mode 100644 index 7124c2224..000000000 --- a/demo/zmodem/app.js +++ /dev/null @@ -1,87 +0,0 @@ -var express = require('express'); -var app = express(); -var expressWs = require('express-ws')(app); -var os = require('os'); -var pty = require('node-pty'); - -var terminals = {}, - logs = {}; - -app.use('/build', express.static(__dirname + '/../../build')); -app.use('/demo', express.static(__dirname + '/../../demo')); -app.use('/zmodemjs', express.static(__dirname + '/../../node_modules/zmodem.js/dist')); - -app.get('/', function(req, res){ - res.sendFile(__dirname + '/index.html'); -}); - -app.get('/style.css', function(req, res){ - res.sendFile(__dirname + '../style.css'); -}); - -app.get('/main.js', function(req, res){ - res.sendFile(__dirname + '/main.js'); -}); - -app.post('/terminals', function (req, res) { - var cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { - encoding: null, - name: 'xterm-color', - cols: cols || 80, - rows: rows || 24, - cwd: process.env.PWD, - env: process.env - }); - - console.log('Created terminal with PID: ' + term.pid); - terminals[term.pid] = term; - logs[term.pid] = ''; - term.on('data', function(data) { - logs[term.pid] += data; - }); - res.send(term.pid.toString()); - res.end(); -}); - -app.post('/terminals/:pid/size', function (req, res) { - var pid = parseInt(req.params.pid), - cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = terminals[pid]; - - term.resize(cols, rows); - console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.'); - res.end(); -}); - -app.ws('/terminals/:pid', function (ws, req) { - var term = terminals[parseInt(req.params.pid)]; - console.log('Connected to terminal ' + term.pid); - ws.send(logs[term.pid]); - - term.on('data', function(data) { - try { - ws.send(data); - } catch (ex) { - // The WebSocket is not open, ignore - } - }); - ws.on('message', function(msg) { - term.write(msg); - }); - ws.on('close', function () { - term.kill(); - console.log('Closed terminal ' + term.pid); - // Clean things up - delete terminals[term.pid]; - delete logs[term.pid]; - }); -}); - -var port = process.env.PORT || 3000, - host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0'; - -console.log('App listening to http://' + host + ':' + port); -app.listen(port, host); diff --git a/demo/zmodem/index.html b/demo/zmodem/index.html deleted file mode 100644 index aee7742a5..000000000 --- a/demo/zmodem/index.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - xterm.js demo - - - - - - - - - - - - - - - - -

xterm.js: xterm, in the browser

- -
- -
- - - - - - - - - -
- -
-

Actions

-

- - -

-
-
-

Options

-

- -

-

- -

-

- -

-

- -

-

- -

-

- -

-
-

Size

-
-
- - -
-
- - -
-
-
-
-

Attention: The demo is a barebones implementation and is designed for xterm.js evaluation purposes only. Exposing the demo to the public as is would introduce security risks for the host.

-

* ZMODEM file transfers are supported via an addon. To try it out, install lrzsz onto the remote peer, then run rz to send from your browser or sz <file> to send from the remote peer.

- - - diff --git a/demo/zmodem/main.js b/demo/zmodem/main.js deleted file mode 100644 index 619ef2b80..000000000 --- a/demo/zmodem/main.js +++ /dev/null @@ -1,388 +0,0 @@ -"use strict"; - -var term, - protocol, - socketURL, - socket, - pid; - -Terminal.applyAddon(fit); -Terminal.applyAddon(attach); -Terminal.applyAddon(zmodem); -Terminal.applyAddon(search); - -var terminalContainer = document.getElementById('terminal-container'), - actionElements = { - findNext: document.querySelector('#find-next'), - findPrevious: document.querySelector('#find-previous') - }, - optionElements = { - cursorBlink: document.querySelector('#option-cursor-blink'), - cursorStyle: document.querySelector('#option-cursor-style'), - scrollback: document.querySelector('#option-scrollback'), - tabstopwidth: document.querySelector('#option-tabstopwidth'), - bellStyle: document.querySelector('#option-bell-style') - }, - colsElement = document.getElementById('cols'), - rowsElement = document.getElementById('rows'); - -function setTerminalSize() { - var cols = parseInt(colsElement.value, 10); - var rows = parseInt(rowsElement.value, 10); - var viewportElement = document.querySelector('.xterm-viewport'); - var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth; - var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px'; - var height = (rows * term.charMeasure.height).toString() + 'px'; - - terminalContainer.style.width = width; - terminalContainer.style.height = height; - term.resize(cols, rows); -} - -colsElement.addEventListener('change', setTerminalSize); -rowsElement.addEventListener('change', setTerminalSize); - -actionElements.findNext.addEventListener('keypress', function (e) { - if (e.key === "Enter") { - e.preventDefault(); - term.findNext(actionElements.findNext.value); - } -}); -actionElements.findPrevious.addEventListener('keypress', function (e) { - if (e.key === "Enter") { - e.preventDefault(); - term.findPrevious(actionElements.findPrevious.value); - } -}); - -optionElements.cursorBlink.addEventListener('change', function () { - term.setOption('cursorBlink', optionElements.cursorBlink.checked); -}); -optionElements.cursorStyle.addEventListener('change', function () { - term.setOption('cursorStyle', optionElements.cursorStyle.value); -}); -optionElements.bellStyle.addEventListener('change', function () { - term.setOption('bellStyle', optionElements.bellStyle.value); -}); -optionElements.scrollback.addEventListener('change', function () { - term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10)); -}); -optionElements.tabstopwidth.addEventListener('change', function () { - term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10)); -}); - -createTerminal(); - -function createTerminal() { - // Clean terminal - while (terminalContainer.children.length) { - terminalContainer.removeChild(terminalContainer.children[0]); - } - term = new Terminal({ - cursorBlink: optionElements.cursorBlink.checked, - scrollback: parseInt(optionElements.scrollback.value, 10), - tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10) - }); - term.on('resize', function (size) { - if (!pid) { - return; - } - var cols = size.cols, - rows = size.rows, - url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows; - - fetch(url, {method: 'POST'}); - }); - protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://'; - socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/'; - - term.open(terminalContainer); - term.fit(); - - // fit is called within a setTimeout, cols and rows need this. - setTimeout(function () { - colsElement.value = term.cols; - rowsElement.value = term.rows; - - // Set terminal size again to set the specific dimensions on the demo - setTerminalSize(); - - fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) { - - res.text().then(function (pid) { - window.pid = pid; - socketURL += pid; - socket = new WebSocket(socketURL); - socket.onopen = runRealTerminal; - socket.onclose = runFakeTerminal; - socket.onerror = runFakeTerminal; - - term.zmodemAttach(socket, { - noTerminalWriteOutsideSession: true, - } ); - - term.on("zmodemRetract", () => { - start_form.style.display = "none"; - start_form.onsubmit = null; - }); - - term.on("zmodemDetect", (detection) => { - function do_zmodem() { - term.detach(); - let zsession = detection.confirm(); - - var promise; - - if (zsession.type === "receive") { - promise = _handle_receive_session(zsession); - } - else { - promise = _handle_send_session(zsession); - } - - promise.catch( console.error.bind(console) ).then( () => { - term.attach(socket); - } ); - } - - if (_auto_zmodem()) { - do_zmodem(); - } - else { - start_form.style.display = ""; - start_form.onsubmit = function(e) { - start_form.style.display = "none"; - - if (document.getElementById("zmstart_yes").checked) { - do_zmodem(); - } - else { - detection.deny(); - } - }; - } - }); - }); - }); - }, 0); -} - -//---------------------------------------------------------------------- -// UI STUFF - -function _show_file_info(xfer) { - var file_info = xfer.get_details(); - - document.getElementById("name").textContent = file_info.name; - document.getElementById("size").textContent = file_info.size; - document.getElementById("mtime").textContent = file_info.mtime; - document.getElementById("files_remaining").textContent = file_info.files_remaining; - document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining; - - document.getElementById("mode").textContent = "0" + file_info.mode.toString(8); - - var xfer_opts = xfer.get_options(); - ["conversion", "management", "transport", "sparse"].forEach( (lbl) => { - document.getElementById(`zfile_${lbl}`).textContent = xfer_opts[lbl]; - } ); - - document.getElementById("zm_file").style.display = ""; -} -function _hide_file_info() { - document.getElementById("zm_file").style.display = "none"; -} - -function _save_to_disk(xfer, buffer) { - return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name); -} - -var skipper_button = document.getElementById("zm_progress_skipper"); -var skipper_button_orig_text = skipper_button.textContent; - -function _show_progress() { - skipper_button.disabled = false; - skipper_button.textContent = skipper_button_orig_text; - - document.getElementById("bytes_received").textContent = 0; - document.getElementById("percent_received").textContent = 0; - - document.getElementById("zm_progress").style.display = ""; -} - -function _update_progress(xfer) { - var total_in = xfer.get_offset(); - - document.getElementById("bytes_received").textContent = total_in; - - var percent_received = 100 * total_in / xfer.get_details().size; - document.getElementById("percent_received").textContent = percent_received.toFixed(2); -} - -function _hide_progress() { - document.getElementById("zm_progress").style.display = "none"; -} - -var start_form = document.getElementById("zm_start"); - -function _auto_zmodem() { - return document.getElementById("zmodem-auto").checked; -} - -// END UI STUFF -//---------------------------------------------------------------------- - -function _handle_receive_session(zsession) { - zsession.on("offer", function(xfer) { - current_receive_xfer = xfer; - - _show_file_info(xfer); - - var offer_form = document.getElementById("zm_offer"); - - function on_form_submit() { - offer_form.style.display = "none"; - - //START - //if (offer_form.zmaccept.value) { - if (_auto_zmodem() || document.getElementById("zmaccept_yes").checked) { - _show_progress(); - - var FILE_BUFFER = []; - xfer.on("input", (payload) => { - _update_progress(xfer); - FILE_BUFFER.push( new Uint8Array(payload) ); - }); - xfer.accept().then( - () => { - _save_to_disk(xfer, FILE_BUFFER); - }, - console.error.bind(console) - ); - } - else { - xfer.skip(); - } - //END - } - - if (_auto_zmodem()) { - on_form_submit(); - } - else { - offer_form.onsubmit = on_form_submit; - offer_form.style.display = ""; - } - } ); - - var promise = new Promise( (res) => { - zsession.on("session_end", () => { - _hide_file_info(); - _hide_progress(); - res(); - } ); - } ); - - zsession.start(); - - return promise; -} - -function _handle_send_session(zsession) { - var choose_form = document.getElementById("zm_choose"); - choose_form.style.display = ""; - - var file_el = document.getElementById("zm_files"); - - var promise = new Promise( (res) => { - file_el.onchange = function(e) { - choose_form.style.display = "none"; - - var files_obj = file_el.files; - - Zmodem.Browser.send_files( - zsession, - files_obj, - { - on_offer_response(obj, xfer) { - if (xfer) _show_progress(); - //console.log("offer", xfer ? "accepted" : "skipped"); - }, - on_progress(obj, xfer) { - _update_progress(xfer); - }, - on_file_complete(obj) { - //console.log("COMPLETE", obj); - _hide_progress(); - }, - } - ).then(_hide_progress).then( - zsession.close.bind(zsession), - console.error.bind(console) - ).then( () => { - _hide_file_info(); - _hide_progress(); - res(); - } ); - }; - } ); - - return promise; -} - -//This is here to allow canceling of an in-progress ZMODEM transfer. -var current_receive_xfer; - -//Called from HTML directly. -function skip_current_file() { - current_receive_xfer.skip(); - - skipper_button.disabled = true; - skipper_button.textContent = "Waiting for server to acknowledge skip …"; -} - -function runRealTerminal() { - term.attach(socket); - - term._initialized = true; -} - -function runFakeTerminal() { - if (term._initialized) { - return; - } - - term._initialized = true; - - var shellprompt = '$ '; - - term.prompt = function () { - term.write('\r\n' + shellprompt); - }; - - term.writeln('Welcome to xterm.js'); - term.writeln('This is a local terminal emulation, without a real terminal in the back-end.'); - term.writeln('Type some keys and commands to play around.'); - term.writeln(''); - term.prompt(); - - term.on('key', function (key, ev) { - var printable = ( - !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey - ); - - if (ev.keyCode == 13) { - term.prompt(); - } else if (ev.keyCode == 8) { - // Do not delete the prompt - if (term.x > 2) { - term.write('\b \b'); - } - } else if (printable) { - term.write(key); - } - }); - - term.on('paste', function (data, ev) { - term.write(data); - }); -} From e182e3d4e43af884a61d0fadf0d5f34792a76cd5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 30 Mar 2019 14:52:48 -0700 Subject: [PATCH 44/47] Fix demo on non-Windows Broke in #1978 --- demo/client.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/demo/client.ts b/demo/client.ts index 70996c4a7..aaaf28294 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -102,7 +102,9 @@ function createTerminal(): void { socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/'; term.open(terminalContainer); - term.winptyCompatInit(); + if (isWindows) { + term.winptyCompatInit(); + } term.webLinksInit(); term.fit(); term.focus(); From bf9d879efa9897827fbeb9f58022cbc0960ad439 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 30 Mar 2019 14:59:31 -0700 Subject: [PATCH 45/47] Fix typo --- src/ui/MouseZoneManager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui/MouseZoneManager.ts b/src/ui/MouseZoneManager.ts index 3b848795e..372dccc57 100644 --- a/src/ui/MouseZoneManager.ts +++ b/src/ui/MouseZoneManager.ts @@ -29,7 +29,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { private _tooltipTimeout: number = null; private _currentZone: IMouseZone = null; private _lastHoverCoords: [number, number] = [null, null]; - private _initialSelectionLenght: number; + private _initialSelectionLength: number; constructor( private _terminal: ITerminal @@ -160,7 +160,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { private _onMouseDown(e: MouseEvent): void { // Store current terminal selection length, to check if we're performing // a selection operation - this._initialSelectionLenght = this._terminal.getSelection().length; + this._initialSelectionLength = this._terminal.getSelection().length; // Ignore the event if there are no zones active if (!this._areZonesActive) { @@ -196,7 +196,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { const zone = this._findZoneEventAt(e); const currentSelectionLength = this._terminal.getSelection().length; - if (zone && currentSelectionLength === this._initialSelectionLenght) { + if (zone && currentSelectionLength === this._initialSelectionLength) { zone.clickCallback(e); e.preventDefault(); e.stopImmediatePropagation(); From 4d008c66f11ed06835e6b2270305397033c81ebe Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 30 Mar 2019 15:06:25 -0700 Subject: [PATCH 46/47] Recommend 127.0.0.1:3000 on mac and linux too Fixes #1986 --- .vscode/launch.json | 5 +---- demo/server.js | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 2ec26fcaf..e5bad7b32 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -23,10 +23,7 @@ "type": "chrome", "request": "launch", "name": "Demo Client", - "url": "http://0.0.0.0:3000", - "windows": { - "url": "http://127.0.0.1:3000" - }, + "url": "http://127.0.0.1:3000", "webRoot": "${workspaceFolder}/" }, { diff --git a/demo/server.js b/demo/server.js index 5ff9ca61a..0587977cf 100644 --- a/demo/server.js +++ b/demo/server.js @@ -99,7 +99,7 @@ function startServer() { var port = process.env.PORT || 3000, host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0'; - console.log('App listening to http://' + host + ':' + port); + console.log('App listening to http://127.0.0.1:' + port); app.listen(port, host); } From 5c8c680dac3717e927069699168272ebd23ce54e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 1 Apr 2019 00:01:41 -0700 Subject: [PATCH 47/47] Fix feedback --- src/Buffer.ts | 2 +- src/BufferLine.test.ts | 10 +-- src/BufferLine.ts | 101 +++++++++++----------- src/InputHandler.ts | 10 +-- src/Linkifier.ts | 2 +- src/SelectionManager.ts | 22 ++--- src/Terminal.ts | 2 +- src/Types.ts | 4 +- src/renderer/CharacterJoinerRegistry.ts | 12 +-- src/renderer/TextRenderLayer.ts | 14 +-- src/renderer/dom/DomRendererRowFactory.ts | 13 +-- 11 files changed, 98 insertions(+), 94 deletions(-) diff --git a/src/Buffer.ts b/src/Buffer.ts index 69a36e439..9cc1adba8 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -29,7 +29,7 @@ export const NULL_CELL_WIDTH = 1; export const NULL_CELL_CODE = 0; /** - * Whilespace cell. + * Whitespace cell. * This is meant as a replacement for empty cells when needed * during rendering lines to preserve correct aligment. */ diff --git a/src/BufferLine.test.ts b/src/BufferLine.test.ts index 7dfcbd2c1..29a783aec 100644 --- a/src/BufferLine.test.ts +++ b/src/BufferLine.test.ts @@ -3,7 +3,7 @@ * @license MIT */ import * as chai from 'chai'; -import { BufferLine, CellData, Content } from './BufferLine'; +import { BufferLine, CellData, ContentMasks } from './BufferLine'; import { CharData, IBufferLine } from './Types'; import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR } from './Buffer'; @@ -32,7 +32,7 @@ describe('CellData', () => { // combining cell.setFromCharData([123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]); chai.assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]); - chai.assert.equal(cell.isCombined(), Content.IS_COMBINED); + chai.assert.equal(cell.isCombined(), ContentMasks.IS_COMBINED); // surrogate cell.setFromCharData([123, '𝄞', 1, 0x1D11E]); chai.assert.deepEqual(cell.getAsCharData(), [123, '𝄞', 1, 0x1D11E]); @@ -40,7 +40,7 @@ describe('CellData', () => { // surrogate + combining cell.setFromCharData([123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]); chai.assert.deepEqual(cell.getAsCharData(), [123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]); - chai.assert.equal(cell.isCombined(), Content.IS_COMBINED); + chai.assert.equal(cell.isCombined(), ContentMasks.IS_COMBINED); // wide char cell.setFromCharData([123, '1', 2, '1'.charCodeAt(0)]); chai.assert.deepEqual(cell.getAsCharData(), [123, '1', 2, '1'.charCodeAt(0)]); @@ -350,7 +350,7 @@ describe('BufferLine', function(): void { // width is set to 1 chai.assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]); // do not account a single combining char as combined - chai.assert.equal(cell.isCombined(), Content.IS_COMBINED); + chai.assert.equal(cell.isCombined(), ContentMasks.IS_COMBINED); }); it('should create combining string on taken cell', () => { const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); @@ -363,7 +363,7 @@ describe('BufferLine', function(): void { // width is set to 1 chai.assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]); // do not account a single combining char as combined - chai.assert.equal(cell.isCombined(), Content.IS_COMBINED); + chai.assert.equal(cell.isCombined(), ContentMasks.IS_COMBINED); }); }); }); diff --git a/src/BufferLine.ts b/src/BufferLine.ts index 145184547..c4aa55be0 100644 --- a/src/BufferLine.ts +++ b/src/BufferLine.ts @@ -34,9 +34,9 @@ const enum Cell { } /** - * Bitmasks and helper for accessing data in `content`. + * Bitmasks for accessing data in `content`. */ -export const enum Content { +export const enum ContentMasks { /** * bit 1..21 codepoint, max allowed in UTF32 is 0x10FFFF (21 bits taken) * read: `codepoint = content & Content.codepointMask;` @@ -44,7 +44,7 @@ export const enum Content { * shortcut if precondition `codepoint <= 0x10FFFF` is met: * `content |= codepoint;` */ - CODEPOINT_MASK = 0x1FFFFF, + CODEPOINT = 0x1FFFFF, /** * bit 22 flag indication whether a cell contains combined content @@ -72,10 +72,11 @@ export const enum Content { * shortcut if precondition `0 <= width <= 3` is met: * `content |= width << Content.widthShift;` */ - WIDTH_MASK = 0xC00000, // 3 << 22 - WIDTH_SHIFT = 22 + WIDTH = 0xC00000 // 3 << 22 } +const WIDTH_MASK_SHIFT = 22; + /** * CellData - represents a single Cell in the terminal buffer. */ @@ -96,21 +97,21 @@ export class CellData implements ICellData { /** Whether cell contains a combined string. */ public isCombined(): number { - return this.content & Content.IS_COMBINED; + return this.content & ContentMasks.IS_COMBINED; } /** Width of the cell. */ public getWidth(): number { - return this.content >> Content.WIDTH_SHIFT; + return this.content >> WIDTH_MASK_SHIFT; } /** JS string of the content. */ public getChars(): string { - if (this.content & Content.IS_COMBINED) { + if (this.content & ContentMasks.IS_COMBINED) { return this.combinedData; } - if (this.content & Content.CODEPOINT_MASK) { - return stringFromCodePoint(this.content & Content.CODEPOINT_MASK); + if (this.content & ContentMasks.CODEPOINT) { + return stringFromCodePoint(this.content & ContentMasks.CODEPOINT); } return ''; } @@ -124,7 +125,7 @@ export class CellData implements ICellData { public getCode(): number { return (this.isCombined()) ? this.combinedData.charCodeAt(this.combinedData.length - 1) - : this.content & Content.CODEPOINT_MASK; + : this.content & ContentMasks.CODEPOINT; } /** Set data from CharData */ @@ -143,7 +144,7 @@ export class CellData implements ICellData { if (0xD800 <= code && code <= 0xDBFF) { const second = value[CHAR_DATA_CHAR_INDEX].charCodeAt(1); if (0xDC00 <= second && second <= 0xDFFF) { - this.content = ((code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); + this.content = ((code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000) | (value[CHAR_DATA_WIDTH_INDEX] << WIDTH_MASK_SHIFT); } else { combined = true; } @@ -151,11 +152,11 @@ export class CellData implements ICellData { combined = true; } } else { - this.content = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); + this.content = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << WIDTH_MASK_SHIFT); } if (combined) { this.combinedData = value[CHAR_DATA_CHAR_INDEX]; - this.content = Content.IS_COMBINED | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); + this.content = ContentMasks.IS_COMBINED | (value[CHAR_DATA_WIDTH_INDEX] << WIDTH_MASK_SHIFT); } } @@ -203,14 +204,14 @@ export class BufferLine implements IBufferLine { */ public get(index: number): CharData { const content = this._data[index * CELL_SIZE + Cell.CONTENT]; - const cp = content & Content.CODEPOINT_MASK; + const cp = content & ContentMasks.CODEPOINT; return [ this._data[index * CELL_SIZE + Cell.FG], - (content & Content.IS_COMBINED) + (content & ContentMasks.IS_COMBINED) ? this._combined[index] : (cp) ? stringFromCodePoint(cp) : '', - content >> Content.WIDTH_SHIFT, - (content & Content.IS_COMBINED) + content >> WIDTH_MASK_SHIFT, + (content & ContentMasks.IS_COMBINED) ? this._combined[index].charCodeAt(this._combined[index].length - 1) : cp ]; @@ -224,9 +225,9 @@ export class BufferLine implements IBufferLine { this._data[index * CELL_SIZE + Cell.FG] = value[CHAR_DATA_ATTR_INDEX]; if (value[CHAR_DATA_CHAR_INDEX].length > 1) { this._combined[index] = value[1]; - this._data[index * CELL_SIZE + Cell.CONTENT] = index | Content.IS_COMBINED | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); + this._data[index * CELL_SIZE + Cell.CONTENT] = index | ContentMasks.IS_COMBINED | (value[CHAR_DATA_WIDTH_INDEX] << WIDTH_MASK_SHIFT); } else { - this._data[index * CELL_SIZE + Cell.CONTENT] = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); + this._data[index * CELL_SIZE + Cell.CONTENT] = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << WIDTH_MASK_SHIFT); } } @@ -235,21 +236,21 @@ export class BufferLine implements IBufferLine { * use these when only one value is needed, otherwise use `loadCell` */ public getWidth(index: number): number { - return this._data[index * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT; + return this._data[index * CELL_SIZE + Cell.CONTENT] >> WIDTH_MASK_SHIFT; } /** Test whether content has width. */ public hasWidth(index: number): number { - return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.WIDTH_MASK; + return this._data[index * CELL_SIZE + Cell.CONTENT] & ContentMasks.WIDTH; } /** Get FG cell component. */ - public getFG(index: number): number { + public getFg(index: number): number { return this._data[index * CELL_SIZE + Cell.FG]; } /** Get BG cell component. */ - public getBG(index: number): number { + public getBg(index: number): number { return this._data[index * CELL_SIZE + Cell.BG]; } @@ -259,7 +260,7 @@ export class BufferLine implements IBufferLine { * from real empty cells. * */ public hasContent(index: number): number { - return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT; + return this._data[index * CELL_SIZE + Cell.CONTENT] & ContentMasks.HAS_CONTENT; } /** @@ -269,38 +270,40 @@ export class BufferLine implements IBufferLine { */ public getCodePoint(index: number): number { const content = this._data[index * CELL_SIZE + Cell.CONTENT]; - if (content & Content.IS_COMBINED) { + if (content & ContentMasks.IS_COMBINED) { return this._combined[index].charCodeAt(this._combined[index].length - 1); } - return content & Content.CODEPOINT_MASK; + return content & ContentMasks.CODEPOINT; } /** Test whether the cell contains a combined string. */ public isCombined(index: number): number { - return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.IS_COMBINED; + return this._data[index * CELL_SIZE + Cell.CONTENT] & ContentMasks.IS_COMBINED; } /** Returns the string content of the cell. */ public getString(index: number): string { const content = this._data[index * CELL_SIZE + Cell.CONTENT]; - if (content & Content.IS_COMBINED) { + if (content & ContentMasks.IS_COMBINED) { return this._combined[index]; } - if (content & Content.CODEPOINT_MASK) { - return stringFromCodePoint(content & Content.CODEPOINT_MASK); + if (content & ContentMasks.CODEPOINT) { + return stringFromCodePoint(content & ContentMasks.CODEPOINT); } // return empty string for empty cells return ''; } /** - * Load data at `index` into `cell`. + * Load data at `index` into `cell`. This is used to access cells in a way that's more friendly + * to GC as it significantly reduced the amount of new objects/references needed. */ public loadCell(index: number, cell: ICellData): ICellData { - cell.content = this._data[index * CELL_SIZE + Cell.CONTENT]; - cell.fg = this._data[index * CELL_SIZE + Cell.FG]; - cell.bg = this._data[index * CELL_SIZE + Cell.BG]; - if (cell.content & Content.IS_COMBINED) { + const startIndex = index * CELL_SIZE; + cell.content = this._data[startIndex + Cell.CONTENT]; + cell.fg = this._data[startIndex + Cell.FG]; + cell.bg = this._data[startIndex + Cell.BG]; + if (cell.content & ContentMasks.IS_COMBINED) { cell.combinedData = this._combined[index]; } return cell; @@ -310,7 +313,7 @@ export class BufferLine implements IBufferLine { * Set data at `index` to `cell`. */ public setCell(index: number, cell: ICellData): void { - if (cell.content & Content.IS_COMBINED) { + if (cell.content & ContentMasks.IS_COMBINED) { this._combined[index] = cell.combinedData; } this._data[index * CELL_SIZE + Cell.CONTENT] = cell.content; @@ -324,7 +327,7 @@ export class BufferLine implements IBufferLine { * it gets an optimized access method. */ public setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number): void { - this._data[index * CELL_SIZE + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT); + this._data[index * CELL_SIZE + Cell.CONTENT] = codePoint | (width << WIDTH_MASK_SHIFT); this._data[index * CELL_SIZE + Cell.FG] = fg; this._data[index * CELL_SIZE + Cell.BG] = bg; } @@ -337,21 +340,21 @@ export class BufferLine implements IBufferLine { */ public addCodepointToCell(index: number, codePoint: number): void { let content = this._data[index * CELL_SIZE + Cell.CONTENT]; - if (content & Content.IS_COMBINED) { + if (content & ContentMasks.IS_COMBINED) { // we already have a combined string, simply add this._combined[index] += stringFromCodePoint(codePoint); } else { - if (content & Content.CODEPOINT_MASK) { + if (content & ContentMasks.CODEPOINT) { // normal case for combining chars: // - move current leading char + new one into combined string // - set combined flag - this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint); - content &= ~Content.CODEPOINT_MASK; // set codepoint in buffer to 0 - content |= Content.IS_COMBINED; + this._combined[index] = stringFromCodePoint(content & ContentMasks.CODEPOINT) + stringFromCodePoint(codePoint); + content &= ~ContentMasks.CODEPOINT; // set codepoint in buffer to 0 + content |= ContentMasks.IS_COMBINED; } else { // should not happen - we actually have no data in the cell yet // simply set the data in the cell buffer with a width of 1 - content = codePoint | (1 << Content.WIDTH_SHIFT); + content = codePoint | (1 << WIDTH_MASK_SHIFT); } this._data[index * CELL_SIZE + Cell.CONTENT] = content; } @@ -473,8 +476,8 @@ export class BufferLine implements IBufferLine { public getTrimmedLength(): number { for (let i = this.length - 1; i >= 0; --i) { - if ((this._data[i * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT)) { - return i + (this._data[i * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT); + if ((this._data[i * CELL_SIZE + Cell.CONTENT] & ContentMasks.HAS_CONTENT)) { + return i + (this._data[i * CELL_SIZE + Cell.CONTENT] >> WIDTH_MASK_SHIFT); } } return 0; @@ -513,9 +516,9 @@ export class BufferLine implements IBufferLine { let result = ''; while (startCol < endCol) { const content = this._data[startCol * CELL_SIZE + Cell.CONTENT]; - const cp = content & Content.CODEPOINT_MASK; - result += (content & Content.IS_COMBINED) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR; - startCol += (content >> Content.WIDTH_SHIFT) || 1; // always advance by 1 + const cp = content & ContentMasks.CODEPOINT; + result += (content & ContentMasks.IS_COMBINED) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR; + startCol += (content >> WIDTH_MASK_SHIFT) || 1; // always advance by 1 } return result; } diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 65f4aaaff..37c9bbe57 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -106,7 +106,7 @@ class DECRQSS implements IDcsHandler { export class InputHandler extends Disposable implements IInputHandler { private _parseBuffer: Uint32Array = new Uint32Array(4096); private _stringDecoder: StringToUtf32 = new StringToUtf32(); - private _cell: CellData = new CellData(); + private _workCell: CellData = new CellData(); constructor( protected _terminal: IInputHandlingTerminal, @@ -351,7 +351,7 @@ export class InputHandler extends Disposable implements IInputHandler { // since they always follow a cell consuming char // therefore we can test for buffer.x to avoid overflow left if (!chWidth && buffer.x) { - if (!bufferRow.loadCell(buffer.x - 1, this._cell).getWidth()) { + if (!bufferRow.getWidth(buffer.x - 1)) { // found empty cell after fullwidth, need to go 2 cells back // it is save to step 2 cells back here // since an empty cell is only set by fullwidth chars @@ -400,7 +400,7 @@ export class InputHandler extends Disposable implements IInputHandler { // test last cell - since the last cell has only room for // a halfwidth char any fullwidth shifted there is lost // and will be set to empty cell - if (bufferRow.loadCell(cols - 1, this._cell).getWidth() === 2) { + if (bufferRow.getWidth(cols - 1) === 2) { bufferRow.setCellFromCodePoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr, 0); } } @@ -970,10 +970,10 @@ export class InputHandler extends Disposable implements IInputHandler { // make buffer local for faster access const buffer = this._terminal.buffer; const line = buffer.lines.get(buffer.ybase + buffer.y); - line.loadCell(buffer.x - 1, this._cell); + line.loadCell(buffer.x - 1, this._workCell); line.replaceCells(buffer.x, buffer.x + (params[0] || 1), - (this._cell.content !== undefined) ? this._cell : buffer.getNullCell(DEFAULT_ATTR) + (this._workCell.content !== undefined) ? this._workCell : buffer.getNullCell(DEFAULT_ATTR) ); // FIXME: no updateRange here? } diff --git a/src/Linkifier.ts b/src/Linkifier.ts index a2b9045b6..80399904c 100644 --- a/src/Linkifier.ts +++ b/src/Linkifier.ts @@ -231,7 +231,7 @@ export class Linkifier extends EventEmitter implements ILinkifier { } const line = this._terminal.buffer.lines.get(bufferIndex[0]); - const attr = line.getFG(bufferIndex[1]); + const attr = line.getFg(bufferIndex[1]); let fg: number | undefined; if (attr) { fg = (attr >> 9) & 0x1ff; diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 9d49a860a..361b11235 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -103,7 +103,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager private _mouseMoveListener: EventListener; private _mouseUpListener: EventListener; private _trimListener: XtermListener; - private _cell: CellData = new CellData(); + private _workCell: CellData = new CellData(); private _mouseDownTimeStamp: number; @@ -669,8 +669,8 @@ export class SelectionManager extends EventEmitter implements ISelectionManager private _convertViewportColToCharacterIndex(bufferLine: IBufferLine, coords: [number, number]): number { let charIndex = coords[0]; for (let i = 0; coords[0] >= i; i++) { - const length = bufferLine.loadCell(i, this._cell).getChars().length; - if (this._cell.getWidth() === 0) { + const length = bufferLine.loadCell(i, this._workCell).getChars().length; + if (this._workCell.getWidth() === 0) { // Wide characters aren't included in the line string so decrement the // index so the index is back on the wide character. charIndex--; @@ -755,10 +755,10 @@ export class SelectionManager extends EventEmitter implements ISelectionManager } // Expand the string in both directions until a space is hit - while (startCol > 0 && startIndex > 0 && !this._isCharWordSeparator(bufferLine.loadCell(startCol - 1, this._cell))) { - bufferLine.loadCell(startCol - 1, this._cell); - const length = this._cell.getChars().length; - if (this._cell.getWidth() === 0) { + while (startCol > 0 && startIndex > 0 && !this._isCharWordSeparator(bufferLine.loadCell(startCol - 1, this._workCell))) { + bufferLine.loadCell(startCol - 1, this._workCell); + const length = this._workCell.getChars().length; + if (this._workCell.getWidth() === 0) { // If the next character is a wide char, record it and skip the column leftWideCharCount++; startCol--; @@ -771,10 +771,10 @@ export class SelectionManager extends EventEmitter implements ISelectionManager startIndex--; startCol--; } - while (endCol < bufferLine.length && endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine.loadCell(endCol + 1, this._cell))) { - bufferLine.loadCell(endCol + 1, this._cell); - const length = this._cell.getChars().length; - if (this._cell.getWidth() === 2) { + while (endCol < bufferLine.length && endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine.loadCell(endCol + 1, this._workCell))) { + bufferLine.loadCell(endCol + 1, this._workCell); + const length = this._workCell.getChars().length; + if (this._workCell.getWidth() === 2) { // If the next character is a wide char, record it and skip the column rightWideCharCount++; endCol++; diff --git a/src/Terminal.ts b/src/Terminal.ts index f3ec6e31d..db696bd6f 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1181,7 +1181,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II public scroll(isWrapped: boolean = false): void { let newLine: IBufferLine; newLine = this._blankLine; - if (!newLine || newLine.length !== this.cols || newLine.getFG(0) !== this.eraseAttr()) { + if (!newLine || newLine.length !== this.cols || newLine.getFg(0) !== this.eraseAttr()) { newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped); this._blankLine = newLine; } diff --git a/src/Types.ts b/src/Types.ts index a08bd4853..10665f258 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -560,8 +560,8 @@ export interface IBufferLine { /* direct access to cell attrs */ getWidth(index: number): number; hasWidth(index: number): number; - getFG(index: number): number; - getBG(index: number): number; + getFg(index: number): number; + getBg(index: number): number; hasContent(index: number): number; getCodePoint(index: number): number; isCombined(index: number): number; diff --git a/src/renderer/CharacterJoinerRegistry.ts b/src/renderer/CharacterJoinerRegistry.ts index eb44bb58d..4a899d721 100644 --- a/src/renderer/CharacterJoinerRegistry.ts +++ b/src/renderer/CharacterJoinerRegistry.ts @@ -6,7 +6,7 @@ export class CharacterJoinerRegistry implements ICharacterJoinerRegistry { private _characterJoiners: ICharacterJoiner[] = []; private _nextCharacterJoinerId: number = 0; - private _cell: CellData = new CellData(); + private _workCell: CellData = new CellData(); constructor(private _terminal: ITerminal) { } @@ -52,13 +52,13 @@ export class CharacterJoinerRegistry implements ICharacterJoinerRegistry { let rangeStartColumn = 0; let currentStringIndex = 0; let rangeStartStringIndex = 0; - let rangeAttr = line.getFG(0) >> 9; + let rangeAttr = line.getFg(0) >> 9; for (let x = 0; x < this._terminal.cols; x++) { - line.loadCell(x, this._cell); - const chars = this._cell.getChars(); - const width = this._cell.getWidth(); - const attr = this._cell.fg >> 9; + line.loadCell(x, this._workCell); + const chars = this._workCell.getChars(); + const width = this._workCell.getWidth(); + const attr = this._workCell.fg >> 9; if (width === 0) { // If this character is of width 0, skip it. diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index be022239a..f56ccf3a9 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -25,7 +25,7 @@ export class TextRenderLayer extends BaseRenderLayer { private _characterFont: string; private _characterOverlapCache: { [key: string]: boolean } = {}; private _characterJoinerRegistry: ICharacterJoinerRegistry; - private _cell = new CellData(); + private _workCell = new CellData(); constructor(container: HTMLElement, zIndex: number, colors: IColorSet, characterJoinerRegistry: ICharacterJoinerRegistry, alpha: boolean) { super(container, 'text', zIndex, alpha, colors); @@ -74,14 +74,14 @@ export class TextRenderLayer extends BaseRenderLayer { const line = terminal.buffer.lines.get(row); const joinedRanges = joinerRegistry ? joinerRegistry.getJoinedCharacters(row) : []; for (let x = 0; x < terminal.cols; x++) { - (line as any).loadCell(x, this._cell); - let code: number = this._cell.getCode() || WHITESPACE_CELL_CODE; + line.loadCell(x, this._workCell); + let code: number = this._workCell.getCode() || WHITESPACE_CELL_CODE; // Can either represent character(s) for a single cell or multiple cells // if indicated by a character joiner. - let chars = this._cell.getChars() || WHITESPACE_CELL_CHAR; - const attr = this._cell.fg; - let width = this._cell.getWidth(); + let chars = this._workCell.getChars() || WHITESPACE_CELL_CHAR; + const attr = this._workCell.fg; + let width = this._workCell.getWidth(); // If true, indicates that the current character(s) to draw were joined. let isJoined = false; @@ -127,7 +127,7 @@ export class TextRenderLayer extends BaseRenderLayer { // get removed, and `a` would not re-render because it thinks it's // already in the correct state. // this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA; - if (lastCharX < line.length - 1 && line.loadCell(lastCharX + 1, this._cell).getCode() === NULL_CELL_CODE) { + if (lastCharX < line.length - 1 && line.loadCell(lastCharX + 1, this._workCell).getCode() === NULL_CELL_CODE) { width = 2; // this._clearChar(x + 1, y); // The overlapping char's char data will force a clear and render when the diff --git a/src/renderer/dom/DomRendererRowFactory.ts b/src/renderer/dom/DomRendererRowFactory.ts index dfd3a1542..472329814 100644 --- a/src/renderer/dom/DomRendererRowFactory.ts +++ b/src/renderer/dom/DomRendererRowFactory.ts @@ -18,7 +18,8 @@ export const CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar'; export const CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline'; export class DomRendererRowFactory { - private _cell: CellData = new CellData(); + private _workCell: CellData = new CellData(); + constructor( private _terminalOptions: ITerminalOptions, private _document: Document @@ -35,16 +36,16 @@ export class DomRendererRowFactory { // the viewport). let lineLength = 0; for (let x = Math.min(lineData.length, cols) - 1; x >= 0; x--) { - if (lineData.loadCell(x, this._cell).getCode() !== NULL_CELL_CODE || (isCursorRow && x === cursorX)) { + if (lineData.loadCell(x, this._workCell).getCode() !== NULL_CELL_CODE || (isCursorRow && x === cursorX)) { lineLength = x + 1; break; } } for (let x = 0; x < lineLength; x++) { - lineData.loadCell(x, this._cell); - const attr = this._cell.fg; - const width = this._cell.getWidth(); + lineData.loadCell(x, this._workCell); + const attr = this._workCell.fg; + const width = this._workCell.getWidth(); // The character to the left is a wide character, drawing is owned by the char at x-1 if (width === 0) { @@ -106,7 +107,7 @@ export class DomRendererRowFactory { charElement.classList.add(ITALIC_CLASS); } - charElement.textContent = this._cell.getChars() || WHITESPACE_CELL_CHAR; + charElement.textContent = this._workCell.getChars() || WHITESPACE_CELL_CHAR; if (fg !== DEFAULT_COLOR) { charElement.classList.add(`xterm-fg-${fg}`); }