From 6e350219aaac512c23493252dc556e27cee87518 Mon Sep 17 00:00:00 2001 From: leomoty Date: Mon, 8 Oct 2018 23:17:45 -0300 Subject: [PATCH 01/21] Support for lineHeight in DOM Renderer --- src/renderer/dom/DomRenderer.ts | 27 ++++++++++++++------------- typings/xterm.d.ts | 1 - 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index 8f51c4ef3..bd0a6b029 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -94,22 +94,23 @@ export class DomRenderer extends EventEmitter implements IRenderer { } private _updateDimensions(): void { - this.dimensions.scaledCharWidth = this._terminal.charMeasure.width * window.devicePixelRatio; - this.dimensions.scaledCharHeight = this._terminal.charMeasure.height * window.devicePixelRatio; - this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth; - this.dimensions.scaledCellHeight = this.dimensions.scaledCharHeight; + this.dimensions.scaledCharWidth = Math.floor(this._terminal.charMeasure.width * window.devicePixelRatio); + this.dimensions.scaledCharHeight = Math.ceil(this._terminal.charMeasure.height * window.devicePixelRatio); + this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing); + this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight); this.dimensions.scaledCharLeft = 0; this.dimensions.scaledCharTop = 0; this.dimensions.scaledCanvasWidth = this.dimensions.scaledCellWidth * this._terminal.cols; this.dimensions.scaledCanvasHeight = this.dimensions.scaledCellHeight * this._terminal.rows; - this.dimensions.canvasWidth = this._terminal.charMeasure.width * this._terminal.cols; - this.dimensions.canvasHeight = this._terminal.charMeasure.height * this._terminal.rows; - this.dimensions.actualCellWidth = this._terminal.charMeasure.width; - this.dimensions.actualCellHeight = this._terminal.charMeasure.height; + this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio); + this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio); + this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._terminal.cols; + this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._terminal.rows; this._rowElements.forEach(element => { element.style.width = `${this.dimensions.canvasWidth}px`; - element.style.height = `${this._terminal.charMeasure.height}px`; + element.style.height = `${this.dimensions.actualCellHeight}px`; + element.style.lineHeight = `${this.dimensions.actualCellHeight}px`; }); if (!this._dimensionsStyleElement) { @@ -290,10 +291,10 @@ export class DomRenderer extends EventEmitter implements IRenderer { */ private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement { const element = document.createElement('div'); - element.style.height = `${rowCount * this._terminal.charMeasure.height}px`; - element.style.top = `${row * this._terminal.charMeasure.height}px`; - element.style.left = `${colStart * this._terminal.charMeasure.width}px`; - element.style.width = `${this._terminal.charMeasure.width * (colEnd - colStart)}px`; + element.style.height = `${rowCount * this.dimensions.actualCellHeight}px`; + element.style.top = `${row * this.dimensions.actualCellHeight}px`; + element.style.left = `${colStart * this.dimensions.actualCellWidth}px`; + element.style.width = `${this.dimensions.actualCellWidth * (colEnd - colStart)}px`; return element; } diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index b5a8a1090..f809c2ede 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -161,7 +161,6 @@ declare module 'xterm' { * when canvas is too slow for the environment. The following features do * not work when the DOM renderer is used: * - * - Line height * - Letter spacing * - Cursor blink */ From a0250c5cea4c8c310c9f5b3dca7a630786465c6a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 12 Oct 2018 08:33:59 -0700 Subject: [PATCH 02/21] Improve types for listener args in .d.ts Without this consumers need an undefined check with strictNullChecks turned on. --- typings/xterm.d.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index b5a8a1090..8676a458f 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -388,37 +388,37 @@ declare module 'xterm' { * @param type The type of the event. * @param listener The listener. */ - on(type: 'key', listener: (key?: string, event?: KeyboardEvent) => void): void; + on(type: 'key', listener: (key: string, event: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ - on(type: 'keypress' | 'keydown', listener: (event?: KeyboardEvent) => void): void; + on(type: 'keypress' | 'keydown', listener: (event: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ - on(type: 'refresh', listener: (data?: {start: number, end: number}) => void): void; + on(type: 'refresh', listener: (data: {start: number, end: number}) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ - on(type: 'resize', listener: (data?: {cols: number, rows: number}) => void): void; + on(type: 'resize', listener: (data: {cols: number, rows: number}) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ - on(type: 'scroll', listener: (ydisp?: number) => void): void; + on(type: 'scroll', listener: (ydisp: number) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ - on(type: 'title', listener: (title?: string) => void): void; + on(type: 'title', listener: (title: string) => void): void; /** * Registers an event listener. * @param type The type of the event. From 19f6c0a940e1d3e43e66ced78089ec1d976a72d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 12 Oct 2018 09:01:56 -0700 Subject: [PATCH 03/21] Fix tests --- src/utils/TestUtils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/TestUtils.test.ts b/src/utils/TestUtils.test.ts index f575855a7..353e615fd 100644 --- a/src/utils/TestUtils.test.ts +++ b/src/utils/TestUtils.test.ts @@ -121,7 +121,7 @@ export class MockTerminal implements ITerminal { handler(data: string): void { throw new Error('Method not implemented.'); } - on(event: string, callback: () => void): void { + on(event: string, callback: (...args: any[]) => void): void { throw new Error('Method not implemented.'); } off(type: string, listener: XtermListener): void { From 1208fc9e106513a4979d054029c49765b8f98ff1 Mon Sep 17 00:00:00 2001 From: jrkong Date: Mon, 22 Oct 2018 16:32:04 -0400 Subject: [PATCH 04/21] updated webLinks regex to fix vscode issue#60401 --- src/addons/webLinks/webLinks.test.ts | 4 ++-- src/addons/webLinks/webLinks.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/addons/webLinks/webLinks.test.ts b/src/addons/webLinks/webLinks.test.ts index c84ee1a5e..1ae62b80b 100644 --- a/src/addons/webLinks/webLinks.test.ts +++ b/src/addons/webLinks/webLinks.test.ts @@ -32,11 +32,11 @@ describe('webLinks addon', () => { const term = new MockTerminal(); webLinks.webLinksInit(term); - const row = ' http://foo.com/a~b#c~d?e~f '; + const row = ' http://foo.com/a~b#c~d?e~f: '; const match = row.match(term.regex); const uri = match[term.options.matchIndex]; - assert.equal(uri, 'http://foo.com/a~b#c~d?e~f'); + assert.equal(uri, 'http://foo.com/a~b#c~d?e~f:'); }); }); diff --git a/src/addons/webLinks/webLinks.ts b/src/addons/webLinks/webLinks.ts index a007bbd61..382d5dba2 100644 --- a/src/addons/webLinks/webLinks.ts +++ b/src/addons/webLinks/webLinks.ts @@ -14,7 +14,7 @@ const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})'; const localHostClause = '(localhost)'; const portClause = '(:\\d{1,5})'; const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?'; -const pathClause = '(\\/[\\/\\w\\.\\-%~]*)*'; +const pathClause = '(\\/[\\/\\w\\.\\-%~:]*)*'; const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*'; const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?'; const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?'; From 2efd28ad4b32593bf3403f84399c9cf1698c8c15 Mon Sep 17 00:00:00 2001 From: jrkong Date: Thu, 25 Oct 2018 00:14:20 -0400 Subject: [PATCH 05/21] updated webLinks regex to ignore colon at the end of a line and added the corresponding tests --- src/addons/webLinks/webLinks.test.ts | 28 ++++++++++++++++++++++++++-- src/addons/webLinks/webLinks.ts | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/addons/webLinks/webLinks.test.ts b/src/addons/webLinks/webLinks.test.ts index 1ae62b80b..ce9b8b442 100644 --- a/src/addons/webLinks/webLinks.test.ts +++ b/src/addons/webLinks/webLinks.test.ts @@ -32,11 +32,35 @@ describe('webLinks addon', () => { const term = new MockTerminal(); webLinks.webLinksInit(term); - const row = ' http://foo.com/a~b#c~d?e~f: '; + const row = ' http://foo.com/a~b#c~d?e~f '; const match = row.match(term.regex); const uri = match[term.options.matchIndex]; - assert.equal(uri, 'http://foo.com/a~b#c~d?e~f:'); + assert.equal(uri, 'http://foo.com/a~b#c~d?e~f'); + }); + + it('should allow : character in URI path', () => { + const term = new MockTerminal(); + webLinks.webLinksInit(term); + + const row = ' http://foo.com/colon:test '; + + const match = row.match(term.regex); + const uri = match[term.options.matchIndex]; + + assert.equal(uri, 'http://foo.com/colon:test'); + }); + + it('should not allow : character at the end of a URI path', () => { + const term = new MockTerminal(); + webLinks.webLinksInit(term); + + const row = ' http://foo.com/colon:test: '; + + const match = row.match(term.regex); + const uri = match[term.options.matchIndex]; + + assert.equal(uri, 'http://foo.com/colon:test'); }); }); diff --git a/src/addons/webLinks/webLinks.ts b/src/addons/webLinks/webLinks.ts index 382d5dba2..75d79104f 100644 --- a/src/addons/webLinks/webLinks.ts +++ b/src/addons/webLinks/webLinks.ts @@ -14,7 +14,7 @@ const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})'; const localHostClause = '(localhost)'; const portClause = '(:\\d{1,5})'; const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?'; -const pathClause = '(\\/[\\/\\w\\.\\-%~:]*)*'; +const pathClause = '(\\/[\\/\\w\\.\\-%~:]*)*([^:\\s])'; const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*'; const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?'; const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?'; From 793516060fe95dc305c16799dd73d243e8b75e74 Mon Sep 17 00:00:00 2001 From: leomoty Date: Sun, 28 Oct 2018 14:24:08 -0300 Subject: [PATCH 06/21] Recreate mouseHelper after renderer is disposed --- src/Terminal.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Terminal.ts b/src/Terminal.ts index 63315008b..8a837337f 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -473,6 +473,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II if (this._theme) { this.renderer.setTheme(this._theme); } + this.mouseHelper = new MouseHelper(this.renderer); break; case 'scrollback': this.buffers.resize(this.cols, this.rows); From 41b1c929e0d860100c9dca2f39aa0c08ac04a361 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 28 Oct 2018 10:50:16 -0700 Subject: [PATCH 07/21] Fix screen element remaining large if originally using canvas --- src/renderer/dom/DomRenderer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index d55d2416c..22ecd0cdc 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -131,6 +131,8 @@ export class DomRenderer extends EventEmitter implements IRenderer { this._selectionContainer.style.height = (this._terminal)._viewportElement.style.height; this._rowContainer.style.width = `${this.dimensions.canvasWidth}px`; this._rowContainer.style.height = `${this.dimensions.canvasHeight}px`; + this._terminal.screenElement.style.width = ''; + this._terminal.screenElement.style.height = ''; } public setTheme(theme: ITheme | undefined): IColorSet { From 108438dbebd053df4cc01e3a74174534a3a291d9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 28 Oct 2018 10:51:57 -0700 Subject: [PATCH 08/21] Use setRenderer instead of ctor Just in case something hangs onto MouseHelper --- src/Terminal.ts | 2 +- src/utils/MouseHelper.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 8a837337f..e88714628 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -473,7 +473,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II if (this._theme) { this.renderer.setTheme(this._theme); } - this.mouseHelper = new MouseHelper(this.renderer); + this.mouseHelper.setRenderer(this.renderer); break; case 'scrollback': this.buffers.resize(this.cols, this.rows); diff --git a/src/utils/MouseHelper.ts b/src/utils/MouseHelper.ts index 15f057426..ca1bb27ed 100644 --- a/src/utils/MouseHelper.ts +++ b/src/utils/MouseHelper.ts @@ -9,6 +9,10 @@ import { IRenderer } from '../renderer/Types'; export class MouseHelper { constructor(private _renderer: IRenderer) {} + public setRenderer(renderer: IRenderer): void { + this._renderer = renderer; + } + public static getCoordsRelativeToElement(event: {pageX: number, pageY: number}, element: HTMLElement): [number, number] { // Ignore browsers that don't support MouseEvent.pageX if (event.pageX === null || event.pageX === undefined) { From 160b4a7d669b6c527fec1bc40a18ae79db7d19de Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 30 Oct 2018 11:04:32 -0700 Subject: [PATCH 09/21] Revert "Remove request term info handler" This reverts commit c2994b0ae1b72fffe95312ef74c5d32897ac82a7. --- src/InputHandler.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 091f41cf7..a34590efd 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -24,6 +24,26 @@ const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, * DCS subparser implementations */ + /** + * DCS + q Pt ST (xterm) + * Request Terminfo String + * not supported + */ +class RequestTerminfo implements IDcsHandler { + private _data: string; + constructor(private _terminal: any) { } + hook(collect: string, params: number[], flag: number): void { + this._data = ''; + } + put(data: string, start: number, end: number): void { + this._data += data.substring(start, end); + } + unhook(): void { + // invalid: DCS 0 + r Pt ST + this._terminal.handler(`${C0.ESC}P0+r${this._data}${C0.ESC}\\`); + } +} + /** * DCS $ q Pt ST * DECRQSS (https://vt100.net/docs/vt510-rm/DECRQSS.html) @@ -66,7 +86,7 @@ class DECRQSS implements IDcsHandler { default: // invalid: DCS 0 $ r Pt ST (xterm) this._terminal.error('Unknown DCS $q %s', this._data); - this._terminal.handler(`${C0.ESC}P0$r${C0.ESC}\\`); + this._terminal.handler(`${C0.ESC}P0$r${this._data}${C0.ESC}\\`); } } } @@ -267,6 +287,7 @@ export class InputHandler extends Disposable implements IInputHandler { * DCS handler */ this._parser.setDcsHandler('$q', new DECRQSS(this._terminal)); + this._parser.setDcsHandler('+q', new RequestTerminfo(this._terminal)); } public dispose(): void { From 8acf137d148d430bcbbb2f8627a3726abe4fe1a0 Mon Sep 17 00:00:00 2001 From: Antonin Stefanutti Date: Thu, 8 Nov 2018 14:44:18 +0100 Subject: [PATCH 10/21] Use WheelEvent.deltaY instead of WheelEvent.wheelDeltaY for wheel event Fixes #1781 --- src/Terminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index e88714628..c9bc98ffe 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -985,7 +985,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II : 65; break; case 'wheel': - button = (ev).wheelDeltaY > 0 + button = (ev).deltaY < 0 ? 64 : 65; break; From de7d5e1eecc4398ef8e7e5db0662002631a6508b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 9 Nov 2018 01:03:44 +0100 Subject: [PATCH 11/21] swallow 7bit ST, add missing executables --- src/EscapeSequenceParser.test.ts | 12 ++++++++++-- src/EscapeSequenceParser.ts | 5 ++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/EscapeSequenceParser.test.ts b/src/EscapeSequenceParser.test.ts index 5db6d7ffb..e92f41257 100644 --- a/src/EscapeSequenceParser.test.ts +++ b/src/EscapeSequenceParser.test.ts @@ -369,7 +369,8 @@ describe('EscapeSequenceParser', function (): void { parser.currentState = ParserState.ESCAPE_INTERMEDIATE; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare([['esc', '', collect[i]]]); + // '\x5c' --> ESC + \ (7bit ST) parser does not expose this as it already got handled + testTerminal.compare((collect[i] === '\x5c') ? [] : [['esc', '', collect[i]]]); parser.reset(); testTerminal.clear(); } @@ -1051,6 +1052,13 @@ describe('EscapeSequenceParser', function (): void { ['csi', '<', [0, 0], 'c'] ], null); }); + it('7bit ST should be swallowed', function(): void { + test('abc\x9d123tzf\x1b\\defg', [ + ['print', 'abc'], + ['osc', '123tzf'], + ['print', 'defg'] + ], null); + }); }); }); @@ -1089,7 +1097,7 @@ describe('EscapeSequenceParser', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.GROUND; - parser.parse('\x1e'); + parser.parse('\x9c'); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([]); parser.reset(); diff --git a/src/EscapeSequenceParser.ts b/src/EscapeSequenceParser.ts index bf744e050..b38c50f54 100644 --- a/src/EscapeSequenceParser.ts +++ b/src/EscapeSequenceParser.ts @@ -66,7 +66,7 @@ export class TransitionTable { const PRINTABLES = r(0x20, 0x7f); const EXECUTABLES = r(0x00, 0x18); EXECUTABLES.push(0x19); -EXECUTABLES.concat(r(0x1c, 0x20)); +EXECUTABLES.push.apply(EXECUTABLES, r(0x1c, 0x20)); const DEFAULT_TRANSITION = ParserAction.ERROR << 4 | ParserState.GROUND; /** @@ -261,6 +261,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._dcsHandlers = Object.create(null); this._activeDcsHandler = null; this._errorHandler = this._errorHandlerFb; + + // swallow 7bit ST (ESC+\) + this.setEscHandler('\\', () => {}); } public dispose(): void { From 774793c7ca77093bb9878d654159164ca28aae98 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 8 Nov 2018 19:20:12 -0800 Subject: [PATCH 12/21] Fixing isClickInSelection to cover missing cases The following cases weren't working: - The first character on single line selections - The first row on multi-line selection --- src/SelectionManager.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index bfb571773..47cad7ef5 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -290,8 +290,9 @@ export class SelectionManager extends EventEmitter implements ISelectionManager } return (coords[1] > start[1] && coords[1] < end[1]) || - (start[1] === end[1] && coords[1] === start[1] && coords[0] > start[0] && coords[0] < end[0]) || - (start[1] < end[1] && coords[1] === end[1] && coords[0] < end[0]); + (start[1] === end[1] && coords[1] === start[1] && coords[0] >= start[0] && coords[0] < end[0]) || + (start[1] < end[1] && coords[1] === end[1] && coords[0] < end[0]) || + (start[1] < end[1] && coords[1] === start[1] && coords[0] >= start[0]); } /** From 8ea0bb55b4d2f0e2e1fbbaf79ddaee72938bc1cd Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Thu, 8 Nov 2018 00:11:50 -0600 Subject: [PATCH 13/21] Make scrollbar draggable when using DOM renderer Fixes #1751 --- src/renderer/dom/DomRenderer.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index 22ecd0cdc..fadd9b721 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -123,16 +123,14 @@ export class DomRenderer extends EventEmitter implements IRenderer { ` display: inline-block;` + ` height: 100%;` + ` vertical-align: top;` + - ` width: ${this._terminal.charMeasure.width}px` + + ` width: ${this.dimensions.actualCellWidth}px` + `}`; this._dimensionsStyleElement.innerHTML = styles; this._selectionContainer.style.height = (this._terminal)._viewportElement.style.height; - this._rowContainer.style.width = `${this.dimensions.canvasWidth}px`; - this._rowContainer.style.height = `${this.dimensions.canvasHeight}px`; - this._terminal.screenElement.style.width = ''; - this._terminal.screenElement.style.height = ''; + this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth}px`; + this._terminal.screenElement.style.height = `${this.dimensions.canvasHeight}px`; } public setTheme(theme: ITheme | undefined): IColorSet { From 9faebaf5fe8f7e480c4206a041fc2b67679fb3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 15 Nov 2018 00:16:52 +0100 Subject: [PATCH 14/21] hold single AudioContext instance --- src/SoundManager.ts | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/SoundManager.ts b/src/SoundManager.ts index 4139c207b..ffb01658e 100644 --- a/src/SoundManager.ts +++ b/src/SoundManager.ts @@ -12,7 +12,19 @@ import { ITerminal, ISoundManager } from './Types'; export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg=='; export class SoundManager implements ISoundManager { - private _audioContext: AudioContext; + private static _audioContext: AudioContext; + + static get audioContext(): AudioContext { + if (!SoundManager._audioContext) { + const audioContextCtor: typeof AudioContext = (window).AudioContext || (window).webkitAudioContext; + if (!audioContextCtor) { + console.warn('Web Audio API is not supported by this browser. Consider upgrading to the latest version'); + return null; + } + this._audioContext = new audioContextCtor(); + } + return SoundManager._audioContext; + } constructor( private _terminal: ITerminal @@ -20,22 +32,16 @@ export class SoundManager implements ISoundManager { } public playBellSound(): void { - const audioContextCtor: typeof AudioContext = (window).AudioContext || (window).webkitAudioContext; - if (!this._audioContext && audioContextCtor) { - this._audioContext = new audioContextCtor(); - } - - if (this._audioContext) { - const bellAudioSource = this._audioContext.createBufferSource(); - const context = this._audioContext; - this._audioContext.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._terminal.options.bellSound)), (buffer) => { - bellAudioSource.buffer = buffer; - bellAudioSource.connect(context.destination); - bellAudioSource.start(0); - }); - } else { - console.warn('Sorry, but the Web Audio API is not supported by your browser. Please, consider upgrading to the latest version'); + const context = SoundManager.audioContext; + if (!context) { + return; } + const bellAudioSource = context.createBufferSource(); + context.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._terminal.options.bellSound)), (buffer) => { + bellAudioSource.buffer = buffer; + bellAudioSource.connect(context.destination); + bellAudioSource.start(0); + }); } private _base64ToArrayBuffer(base64: string): ArrayBuffer { From e3146961bef54bb8e44206c90e5901c6d76434fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 15 Nov 2018 00:25:23 +0100 Subject: [PATCH 15/21] make linter happy --- src/SoundManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SoundManager.ts b/src/SoundManager.ts index ffb01658e..a31c7574a 100644 --- a/src/SoundManager.ts +++ b/src/SoundManager.ts @@ -13,7 +13,7 @@ export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBA export class SoundManager implements ISoundManager { private static _audioContext: AudioContext; - + static get audioContext(): AudioContext { if (!SoundManager._audioContext) { const audioContextCtor: typeof AudioContext = (window).AudioContext || (window).webkitAudioContext; From 0e3f9b72d1725f1797992b04e5ce320b69c56e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 15 Nov 2018 00:48:31 +0100 Subject: [PATCH 16/21] allow to set audio context per argument --- src/SoundManager.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/SoundManager.ts b/src/SoundManager.ts index a31c7574a..0ba6e0c2b 100644 --- a/src/SoundManager.ts +++ b/src/SoundManager.ts @@ -14,7 +14,7 @@ export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBA export class SoundManager implements ISoundManager { private static _audioContext: AudioContext; - static get audioContext(): AudioContext { + static get audioContext(): AudioContext | null { if (!SoundManager._audioContext) { const audioContextCtor: typeof AudioContext = (window).AudioContext || (window).webkitAudioContext; if (!audioContextCtor) { @@ -27,19 +27,20 @@ export class SoundManager implements ISoundManager { } constructor( - private _terminal: ITerminal + private _terminal: ITerminal, + private _audioContext?: AudioContext ) { } public playBellSound(): void { - const context = SoundManager.audioContext; - if (!context) { + const ctx = this._audioContext || SoundManager.audioContext; + if (!ctx) { return; } - const bellAudioSource = context.createBufferSource(); - context.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._terminal.options.bellSound)), (buffer) => { + const bellAudioSource = ctx.createBufferSource(); + ctx.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._terminal.options.bellSound)), (buffer) => { bellAudioSource.buffer = buffer; - bellAudioSource.connect(context.destination); + bellAudioSource.connect(ctx.destination); bellAudioSource.start(0); }); } From a480f483cda4d73edd1f2963a1ae61e32acd973c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 16 Nov 2018 17:54:03 -0800 Subject: [PATCH 17/21] Add tests --- src/SelectionManager.test.ts | 13 +++++++++++++ src/SelectionManager.ts | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/src/SelectionManager.test.ts b/src/SelectionManager.test.ts index c42735d52..8735e8945 100644 --- a/src/SelectionManager.test.ts +++ b/src/SelectionManager.test.ts @@ -30,6 +30,7 @@ class TestSelectionManager extends SelectionManager { public selectLineAt(line: number): void { this._selectLineAt(line); } public selectWordAt(coords: [number, number]): void { this._selectWordAt(coords, true); } + public areCoordsInSelection(coords: [number, number], start: [number, number], end: [number, number]): boolean { return this._areCoordsInSelection(coords, start, end); } // Disable DOM interaction public enable(): void {} @@ -478,5 +479,17 @@ describe('SelectionManager', () => { assert.equal(selectionManager.selectionText, 'a\n😁\nc'); }); }); + + describe('_areCoordsInSelection', () => { + it('should return whether coords are in the selection', () => { + assert.isFalse(selectionManager.areCoordsInSelection([0, 0], [2, 0], [2, 1])); + assert.isFalse(selectionManager.areCoordsInSelection([1, 0], [2, 0], [2, 1])); + assert.isTrue(selectionManager.areCoordsInSelection([2, 0], [2, 0], [2, 1])); + assert.isTrue(selectionManager.areCoordsInSelection([10, 0], [2, 0], [2, 1])); + assert.isTrue(selectionManager.areCoordsInSelection([0, 1], [2, 0], [2, 1])); + assert.isTrue(selectionManager.areCoordsInSelection([1, 1], [2, 0], [2, 1])); + assert.isFalse(selectionManager.areCoordsInSelection([2, 1], [2, 0], [2, 1])); + }); + }); }); diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 47cad7ef5..86be0c48b 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -289,6 +289,10 @@ export class SelectionManager extends EventEmitter implements ISelectionManager return false; } + return this._areCoordsInSelection(coords, start, end); + } + + protected _areCoordsInSelection(coords: [number, number], start: [number, number], end: [number, number]): boolean { return (coords[1] > start[1] && coords[1] < end[1]) || (start[1] === end[1] && coords[1] === start[1] && coords[0] >= start[0] && coords[0] < end[0]) || (start[1] < end[1] && coords[1] === end[1] && coords[0] < end[0]) || From 203ee0f644f833d2dd554a147ec3274349272887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 18 Nov 2018 17:47:49 +0100 Subject: [PATCH 18/21] remove ctx args from ctor --- src/SoundManager.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/SoundManager.ts b/src/SoundManager.ts index 0ba6e0c2b..9adbfc4af 100644 --- a/src/SoundManager.ts +++ b/src/SoundManager.ts @@ -27,13 +27,12 @@ export class SoundManager implements ISoundManager { } constructor( - private _terminal: ITerminal, - private _audioContext?: AudioContext + private _terminal: ITerminal ) { } public playBellSound(): void { - const ctx = this._audioContext || SoundManager.audioContext; + const ctx = SoundManager.audioContext; if (!ctx) { return; } From 9286ea3eafe61c3ea9170378568d5fe6f46809f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 18 Nov 2018 17:50:29 +0100 Subject: [PATCH 19/21] make statix usage explicit --- src/SoundManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SoundManager.ts b/src/SoundManager.ts index 9adbfc4af..6084edcb0 100644 --- a/src/SoundManager.ts +++ b/src/SoundManager.ts @@ -21,7 +21,7 @@ export class SoundManager implements ISoundManager { console.warn('Web Audio API is not supported by this browser. Consider upgrading to the latest version'); return null; } - this._audioContext = new audioContextCtor(); + SoundManager._audioContext = new audioContextCtor(); } return SoundManager._audioContext; } From 006869704b41732cae36822b05e978c8884c29ad Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 20 Nov 2018 09:44:10 -0800 Subject: [PATCH 20/21] Add help wanted/good first issue links --- CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4b3a9f21..5c389f5b7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,7 +30,8 @@ opening an issue, read these pointers. ## Contributing code -- Make sure you have a [GitHub account](https://github.com/join) +You can find issues to work on my 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 From 98a3892e52408afcbc599dc6c7943b9558e1a060 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 22 Nov 2018 09:44:13 -0800 Subject: [PATCH 21/21] Fix typo --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5c389f5b7..e79240275 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,7 +30,7 @@ opening an issue, read these pointers. ## Contributing code -You can find issues to work on my 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: +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))