From 9f85790ad15716694d7ca0d2c17002662a816015 Mon Sep 17 00:00:00 2001 From: CHaBou Date: Thu, 14 Dec 2017 15:42:53 +0100 Subject: [PATCH 01/33] Add allowTransparency option Fixes #1004 --- fixtures/typings-test/typings-test.ts | 2 ++ src/Interfaces.ts | 1 + src/Terminal.ts | 5 +++-- src/renderer/CharAtlas.ts | 8 ++++---- src/renderer/ColorManager.ts | 18 +----------------- src/renderer/Renderer.ts | 5 +++-- src/renderer/TextRenderLayer.ts | 4 ++-- typings/xterm.d.ts | 10 ++++++++-- 8 files changed, 24 insertions(+), 29 deletions(-) diff --git a/fixtures/typings-test/typings-test.ts b/fixtures/typings-test/typings-test.ts index 887eb3227..d1fda14dc 100644 --- a/fixtures/typings-test/typings-test.ts +++ b/fixtures/typings-test/typings-test.ts @@ -144,6 +144,7 @@ namespace methods_core { const r20: string = t.getOption('bellStyle'); const r21: boolean = t.getOption('enableBold'); const r22: number = t.getOption('letterSpacing'); + const r23: boolean = t.getOption('allowTransparency'); } { const t: Terminal = new Terminal(); @@ -160,6 +161,7 @@ namespace methods_core { t.setOption('popOnBell', true); t.setOption('screenKeys', true); t.setOption('useFlowControl', true); + t.setOption('allowTransparency', true); t.setOption('visualBell', true); t.setOption('colors', ['a', 'b']); t.setOption('letterSpacing', 1); diff --git a/src/Interfaces.ts b/src/Interfaces.ts index f90cb8c04..2fab9e837 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -150,6 +150,7 @@ export interface ITerminalOptions { termName?: string; theme?: ITheme; useFlowControl?: boolean; + allowTransparency?: boolean; } export interface IBuffer { diff --git a/src/Terminal.ts b/src/Terminal.ts index 9e6312a59..739988da9 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -89,6 +89,7 @@ const DEFAULT_OPTIONS: ITerminalOptions = { cancelEvents: false, disableStdin: false, useFlowControl: false, + allowTransparency: false, tabStopWidth: 8, theme: null // programFeatures: false, @@ -578,7 +579,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT this.document = this.parent.ownerDocument; this.body = this.document.body; - initializeCharAtlas(this.document); + initializeCharAtlas(this.document, this.options.allowTransparency); // Create main element container this.element = this.document.createElement('div'); @@ -629,7 +630,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT this.charMeasure = new CharMeasure(document, this.helperContainer); - this.renderer = new Renderer(this, this.options.theme); + this.renderer = new Renderer(this, this.options.theme, this.options.allowTransparency); this.options.theme = null; this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure); this.viewport.onThemeChanged(this.renderer.colorManager.colors); diff --git a/src/renderer/CharAtlas.ts b/src/renderer/CharAtlas.ts index 9ac1e1618..8cd091195 100644 --- a/src/renderer/CharAtlas.ts +++ b/src/renderer/CharAtlas.ts @@ -110,9 +110,9 @@ let generator: CharAtlasGenerator; * Initializes the char atlas generator. * @param document The document. */ -export function initialize(document: Document): void { +export function initialize(document: Document, alpha: boolean): void { if (!generator) { - generator = new CharAtlasGenerator(document); + generator = new CharAtlasGenerator(document, alpha); } } @@ -120,9 +120,9 @@ class CharAtlasGenerator { private _canvas: HTMLCanvasElement; private _ctx: CanvasRenderingContext2D; - constructor(private _document: Document) { + constructor(private _document: Document, alpha: boolean) { this._canvas = this._document.createElement('canvas'); - this._ctx = this._canvas.getContext('2d', {alpha: false}); + this._ctx = this._canvas.getContext('2d', {alpha}); this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); } diff --git a/src/renderer/ColorManager.ts b/src/renderer/ColorManager.ts index 6c0d29454..ea5f1cb09 100644 --- a/src/renderer/ColorManager.ts +++ b/src/renderer/ColorManager.ts @@ -86,7 +86,7 @@ export class ColorManager implements IColorManager { */ public setTheme(theme: ITheme): void { this.colors.foreground = theme.foreground || DEFAULT_FOREGROUND; - this.colors.background = this._validateColor(theme.background, DEFAULT_BACKGROUND); + this.colors.background = theme.background || DEFAULT_BACKGROUND; this.colors.cursor = theme.cursor || DEFAULT_CURSOR; this.colors.cursorAccent = theme.cursorAccent || DEFAULT_CURSOR_ACCENT; this.colors.selection = theme.selection || DEFAULT_SELECTION; @@ -107,20 +107,4 @@ export class ColorManager implements IColorManager { this.colors.ansi[14] = theme.brightCyan || DEFAULT_ANSI_COLORS[14]; this.colors.ansi[15] = theme.brightWhite || DEFAULT_ANSI_COLORS[15]; } - - private _validateColor(color: string, fallback: string): string { - if (!color) { - return fallback; - } - if (color.length === 7 && color.charAt(0) === '#') { - return color; - } - if (color.length === 4 && color.charAt(0) === '#') { - const r = color.charAt(1); - const g = color.charAt(2); - const b = color.charAt(3); - return `#${r}${r}${g}${g}${b}${b}`; - } - return fallback; - } } diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index ea373e5ef..27ba76ad9 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -25,14 +25,15 @@ export class Renderer extends EventEmitter implements IRenderer { public colorManager: ColorManager; public dimensions: IRenderDimensions; - constructor(private _terminal: ITerminal, theme: ITheme) { + constructor(private _terminal: ITerminal, theme: ITheme, alpha: boolean) { super(); this.colorManager = new ColorManager(); if (theme) { this.colorManager.setTheme(theme); } + this._renderLayers = [ - new TextRenderLayer(this._terminal.element, 0, this.colorManager.colors), + new TextRenderLayer(this._terminal.element, 0, alpha, this.colorManager.colors), new SelectionRenderLayer(this._terminal.element, 1, this.colorManager.colors), new LinkRenderLayer(this._terminal.element, 2, this.colorManager.colors, this._terminal), new CursorRenderLayer(this._terminal.element, 3, this.colorManager.colors) diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index 2ecb6ec6e..fd8acbdfd 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -24,8 +24,8 @@ export class TextRenderLayer extends BaseRenderLayer { private _characterFont: string; private _characterOverlapCache: { [key: string]: boolean } = {}; - constructor(container: HTMLElement, zIndex: number, colors: IColorSet) { - super(container, 'text', zIndex, false, colors); + constructor(container: HTMLElement, zIndex: number, alpha: boolean, colors: IColorSet) { + super(container, 'text', zIndex, alpha, colors); this._state = new GridCache(); } diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 43be69c23..4e26dff16 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -12,6 +12,12 @@ declare module 'xterm' { * An object containing start up options for the terminal. */ export interface ITerminalOptions { + /** + * Whether background should support non-opaque color. It must be set before + * executing open() method and can't be changed later without excuting it again. + * Warning: Enabling this option can reduce performances somewhat. + */ + allowTransparency?: boolean; /** * A data uri of the sound to use for the bell (needs bellStyle = 'sound'). */ @@ -409,7 +415,7 @@ declare module 'xterm' { * Retrieves an option's value from the terminal. * @param key The option key. */ - getOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; + getOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'allowTransparency'): boolean; /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -454,7 +460,7 @@ declare module 'xterm' { * @param key The option key. * @param value The option value. */ - setOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; + setOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'allowTransparency', value: boolean): void; /** * Sets an option on the terminal. * @param key The option key. From 9c5aaed4415428a0a28eac51dd80762520110888 Mon Sep 17 00:00:00 2001 From: CHaBou Date: Fri, 15 Dec 2017 00:02:37 +0100 Subject: [PATCH 02/33] Fix some function parameters --- src/Terminal.ts | 2 +- src/renderer/Renderer.ts | 4 ++-- src/renderer/TextRenderLayer.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 739988da9..c2d04cbb9 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -630,7 +630,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT this.charMeasure = new CharMeasure(document, this.helperContainer); - this.renderer = new Renderer(this, this.options.theme, this.options.allowTransparency); + this.renderer = new Renderer(this, this.options.theme); this.options.theme = null; this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure); this.viewport.onThemeChanged(this.renderer.colorManager.colors); diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 27ba76ad9..3ba896a6b 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -25,7 +25,7 @@ export class Renderer extends EventEmitter implements IRenderer { public colorManager: ColorManager; public dimensions: IRenderDimensions; - constructor(private _terminal: ITerminal, theme: ITheme, alpha: boolean) { + constructor(private _terminal: ITerminal, theme: ITheme) { super(); this.colorManager = new ColorManager(); if (theme) { @@ -33,7 +33,7 @@ export class Renderer extends EventEmitter implements IRenderer { } this._renderLayers = [ - new TextRenderLayer(this._terminal.element, 0, alpha, this.colorManager.colors), + new TextRenderLayer(this._terminal.element, 0, this.colorManager.colors, this._terminal.options.allowTransparency), new SelectionRenderLayer(this._terminal.element, 1, this.colorManager.colors), new LinkRenderLayer(this._terminal.element, 2, this.colorManager.colors, this._terminal), new CursorRenderLayer(this._terminal.element, 3, this.colorManager.colors) diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index fd8acbdfd..c5198e156 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -24,7 +24,7 @@ export class TextRenderLayer extends BaseRenderLayer { private _characterFont: string; private _characterOverlapCache: { [key: string]: boolean } = {}; - constructor(container: HTMLElement, zIndex: number, alpha: boolean, colors: IColorSet) { + constructor(container: HTMLElement, zIndex: number, colors: IColorSet, alpha: boolean) { super(container, 'text', zIndex, alpha, colors); this._state = new GridCache(); } From 14498104fc409d82e15b25b2c9d7a7ca43fad2ee Mon Sep 17 00:00:00 2001 From: npezza93 Date: Thu, 17 Aug 2017 18:19:56 -0400 Subject: [PATCH 03/33] Alt+click will move the prompt cursor to that position Fixes #890 --- src/Terminal.ts | 2 + src/handlers/AltClickHandler.ts | 130 ++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/handlers/AltClickHandler.ts diff --git a/src/Terminal.ts b/src/Terminal.ts index 044f5351f..b6a94ddbb 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -27,6 +27,7 @@ import { CompositionHelper } from './CompositionHelper'; import { EventEmitter } from './EventEmitter'; import { Viewport } from './Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard'; +import { AltClickHandler } from './handlers/AltClickHandler'; import { CircularList } from './utils/CircularList'; import { C0 } from './EscapeSequences'; import { InputHandler } from './InputHandler'; @@ -953,6 +954,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT // if the selection manager is having selection forced (ie. a modifier is // held). if (!this.mouseEvents || this.selectionManager.shouldForceSelection(ev)) { + (new AltClickHandler(ev, this)).move(); return; } diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts new file mode 100644 index 000000000..827e9fdcf --- /dev/null +++ b/src/handlers/AltClickHandler.ts @@ -0,0 +1,130 @@ +/** + * Alt+Click handler module: exports methods for handling all alt+click-related events in the + * terminal. + * @module xterm/handlers/AltClickHandler + * @license MIT + */ + +import { Terminal } from '../Terminal'; +import { CHAR_DATA_WIDTH_INDEX } from '../Buffer'; +import { C0 } from '../EscapeSequences'; + +export class AltClickHandler { + private _terminal: Terminal; + private _mouseRow: number; + private _mouseCol: number; + private _mouseEvent: MouseEvent; + + constructor(mouseEvent: MouseEvent, terminal: Terminal) { + this._terminal = terminal; + + [this._mouseCol, this._mouseRow] = this._terminal.mouseHelper.getCoords( + (this._mouseEvent = mouseEvent), + this._terminal.element, + this._terminal.charMeasure, + this._terminal.options.lineHeight, + this._terminal.cols, + this._terminal.rows, + true + ).map((coordinate: number) => { + return coordinate - 1; + }); + } + + public move(): void { + if (!this._mouseEvent.altKey) return; + + let keyboardArrows; + + if (this._terminal.buffer === this._terminal.buffers.normal) { + keyboardArrows = this.buildArrowSequence(this.normalCharCount(), this.horizontalCursorCommand(this.normalMoveForward())); + } else { + keyboardArrows = this.buildArrowSequence(this.altVerticalCharCount(), this.verticalCursorCommand(this.altMoveUpward())); + keyboardArrows += this.buildArrowSequence(this.altHorizontalCharCount(), this.horizontalCursorCommand(this.altMoveForward())); + } + + this._terminal.send(keyboardArrows); + } + + private buildArrowSequence(count: number, sequence: string): string { + return Array(count).join(sequence); + } + + private altMoveUpward(): boolean { + return this._terminal.buffer.y > this._mouseRow; + } + + private altMoveForward(): boolean { + return this._terminal.buffer.x < this._mouseCol; + } + + private normalMoveForward(): boolean { + return (this._terminal.buffer.x < this._mouseCol && + this._terminal.buffer.y <= this._mouseRow) || // down/right or same row/right + (this._terminal.buffer.x >= this._mouseCol && + this._terminal.buffer.y < this._mouseRow); // down/left or same row/left + } + + private horizontalCursorCommand(moveForward: boolean): string { + let mod = this._terminal.applicationCursor ? 'O' : '['; + + if (moveForward) { + return C0.ESC + mod + 'C'; + } else { + return C0.ESC + mod + 'D'; + } + } + + private verticalCursorCommand(moveUp: boolean): string { + let mod = this._terminal.applicationCursor ? 'O' : '['; + + if (moveUp) { + return C0.ESC + mod + 'A'; + } else { + return C0.ESC + mod + 'B'; + } + } + + private altVerticalCharCount(): number { + return Math.abs(this._terminal.buffer.y - this._mouseRow) + 1; + } + + private altHorizontalCharCount(): number { + return Math.abs(this._terminal.buffer.x - this._mouseCol) + 1; + } + + private normalCharCount(): number { + let currentX = this._terminal.buffer.x; + let currentY = this._terminal.buffer.y; + let startCol = this._terminal.buffer.x; + let bufferStr = ''; + + while (currentX !== this._mouseCol || (currentY !== this._mouseRow)) { + if (this.normalMoveForward()) { + currentX++; + if (currentX > this._terminal.cols - 1) { + bufferStr += this._terminal.buffer.translateBufferLineToString(currentY, false, startCol, currentX); + currentX = 0; + startCol = 0; + currentY++; + } + } else { + currentX--; + if (currentX < 0) { + bufferStr += this._terminal.buffer.translateBufferLineToString(currentY, false, 0, startCol + 1); + currentX = this._terminal.cols - 1; + startCol = currentX; + currentY--; + } + } + } + + if (this.normalMoveForward()) { + currentX++; + } else { + currentX--; + } + bufferStr += this._terminal.buffer.translateBufferLineToString(currentY, false, startCol, currentX); + return bufferStr.length; + } +} From 7e0a4840004cbea7c3efd73d67cf080e17554c7a Mon Sep 17 00:00:00 2001 From: npezza93 Date: Mon, 23 Oct 2017 19:49:39 -0400 Subject: [PATCH 04/33] Handle going back and up in the alt buffer from a long line to a short line --- src/handlers/AltClickHandler.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 827e9fdcf..42fdc4433 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -39,8 +39,14 @@ export class AltClickHandler { if (this._terminal.buffer === this._terminal.buffers.normal) { keyboardArrows = this.buildArrowSequence(this.normalCharCount(), this.horizontalCursorCommand(this.normalMoveForward())); } else { - keyboardArrows = this.buildArrowSequence(this.altVerticalCharCount(), this.verticalCursorCommand(this.altMoveUpward())); - keyboardArrows += this.buildArrowSequence(this.altHorizontalCharCount(), this.horizontalCursorCommand(this.altMoveForward())); + let verticalChars = this.buildArrowSequence(this.altVerticalCharCount(), this.verticalCursorCommand(this.altMoveUpward())); + let horizontalChars = this.buildArrowSequence(this.altHorizontalCharCount(), this.horizontalCursorCommand(this.altMoveForward())); + + if (this.altMoveForward()) { + keyboardArrows = verticalChars + horizontalChars + } else { + keyboardArrows = horizontalChars + verticalChars + } } this._terminal.send(keyboardArrows); From 8d41c4c603a07e5ec3ace62d5eb4a72b9ee0e363 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Mon, 23 Oct 2017 19:55:24 -0400 Subject: [PATCH 05/33] Fix lints --- src/handlers/AltClickHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 42fdc4433..e595ce49d 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -43,9 +43,9 @@ export class AltClickHandler { let horizontalChars = this.buildArrowSequence(this.altHorizontalCharCount(), this.horizontalCursorCommand(this.altMoveForward())); if (this.altMoveForward()) { - keyboardArrows = verticalChars + horizontalChars + keyboardArrows = verticalChars + horizontalChars; } else { - keyboardArrows = horizontalChars + verticalChars + keyboardArrows = horizontalChars + verticalChars; } } From 027bc9535ef66ba59dbc2f2b4f2c64086c1252a2 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Tue, 23 Jan 2018 20:57:31 -0500 Subject: [PATCH 06/33] Pass false for isSelection so the right half of the cell wont select the next element --- src/handlers/AltClickHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index e595ce49d..59962d960 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -25,7 +25,7 @@ export class AltClickHandler { this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, - true + false ).map((coordinate: number) => { return coordinate - 1; }); From b87fe53b94994e97264217a6f02cf092691001eb Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 25 Jan 2018 07:20:41 -0800 Subject: [PATCH 07/33] Remove legacy .gitignore rules --- .gitignore | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitignore b/.gitignore index bf0c3d8ce..788b5159c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,10 +19,5 @@ fixtures/typings-test/*.js # Directories needed for code coverage /coverage/ -# Keep legacy files out of the repo, this can be removed when we merge v3 into master -dist/ -src/utils/TestUtils.ts -src/xterm.js - # Keep the demo builds out of Git demo/dist/ From 2c065b66bec4a84083339575b8163645d0d7ecfe Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 25 Jan 2018 12:31:48 -0800 Subject: [PATCH 08/33] Add SQL Operations Studio to real world uses --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 730fc9af9..a5d34632b 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ computational environment for Jupyter, supporting interactive data science and s - [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server. - [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS - [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker. +- [**Microsoft SQL Operations Studio**](https://github.com/Microsoft/sqlopsstudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. From fe53e1a09136828cb32faab5326d48ec11456c1f Mon Sep 17 00:00:00 2001 From: CHaBou Date: Thu, 25 Jan 2018 23:20:20 +0100 Subject: [PATCH 09/33] Change canvas transparency when option is updated --- src/renderer/BaseRenderLayer.ts | 22 +++++++++++++++++++++- src/renderer/CharAtlas.ts | 3 +++ src/renderer/TextRenderLayer.ts | 4 ++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index bf952d06f..b9627dee0 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -54,6 +54,26 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._refreshCharAtlas(terminal, colorSet); } + protected setTransparency(terminal: ITerminal, alpha: boolean): void { + if (alpha === this._alpha) { + return; + } + this._alpha = alpha + const oldCanvas = this._canvas + // Cloning preserves canvas properties and permits to acquire a new context + this._canvas = this._canvas.cloneNode(); + this._ctx = this._canvas.getContext('2d', {alpha: this._alpha}); + this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); + // Draw the background if this is an opaque layer + if (!this._alpha) { + this.clearAll(); + } + oldCanvas.parentNode.replaceChild(this._canvas, oldCanvas); + this._refreshCharAtlas(terminal, this._colors); + // Force a full redraw + this.onGridChanged(terminal, 0, terminal.rows - 1) + } + /** * Refreshes the char atlas, aquiring a new one if necessary. * @param terminal The terminal. @@ -64,7 +84,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { return; } this._charAtlas = null; - const result = acquireCharAtlas(terminal, this._colors, this._scaledCharWidth, this._scaledCharHeight); + const result = acquireCharAtlas(terminal, colorSet, this._scaledCharWidth, this._scaledCharHeight); if (result instanceof HTMLCanvasElement) { this._charAtlas = result; } else { diff --git a/src/renderer/CharAtlas.ts b/src/renderer/CharAtlas.ts index f5a13492a..1911d4e2f 100644 --- a/src/renderer/CharAtlas.ts +++ b/src/renderer/CharAtlas.ts @@ -17,6 +17,7 @@ interface ICharAtlasConfig { fontWeightBold: string; scaledCharWidth: number; scaledCharHeight: number; + allowTransparency: boolean; colors: IColorSet; } @@ -112,6 +113,7 @@ function generateConfig(scaledCharWidth: number, scaledCharHeight: number, termi fontSize: terminal.options.fontSize, fontWeight: terminal.options.fontWeight, fontWeightBold: terminal.options.fontWeightBold, + allowTransparency: terminal.options.allowTransparency, colors: clonedColors }; } @@ -126,6 +128,7 @@ function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean { a.fontSize === b.fontSize && a.fontWeight === b.fontWeight && a.fontWeightBold === b.fontWeightBold && + a.allowTransparency === b.allowTransparency && a.scaledCharWidth === b.scaledCharWidth && a.scaledCharHeight === b.scaledCharHeight && a.colors.foreground === b.colors.foreground && diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index b2563e975..0617d2d20 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -192,6 +192,10 @@ export class TextRenderLayer extends BaseRenderLayer { } } + public onOptionsChanged(terminal: ITerminal): void { + this.setTransparency(terminal, terminal.options.allowTransparency); + } + /** * Whether a character is overlapping to the next cell. */ From 069b489fb666d467c656f180d6852a3e46a73c55 Mon Sep 17 00:00:00 2001 From: sitzmar Date: Fri, 26 Jan 2018 08:04:59 -0800 Subject: [PATCH 10/33] adjust timeout to ensure right-click event has fired --- src/handlers/Clipboard.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/handlers/Clipboard.ts b/src/handlers/Clipboard.ts index fd2b3d779..035b5fff9 100644 --- a/src/handlers/Clipboard.ts +++ b/src/handlers/Clipboard.ts @@ -100,6 +100,7 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA textarea.focus(); // Reset the terminal textarea's styling + // Timeout needs to be long enough for click event to be handled. setTimeout(() => { textarea.style.position = null; textarea.style.width = null; @@ -107,7 +108,7 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA textarea.style.left = null; textarea.style.top = null; textarea.style.zIndex = null; - }, 4); + }, 200); } /** From 93715c455d687a78c4afe4c9324969f5f7dc6402 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 26 Jan 2018 10:26:31 -0800 Subject: [PATCH 11/33] Add dist/ back --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 788b5159c..e8da2d469 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,6 @@ fixtures/typings-test/*.js # Directories needed for code coverage /coverage/ -# Keep the demo builds out of Git +# Keep bundled code out of Git +dist/ demo/dist/ From 327b16ac2e038892a1bc1fa4ecf619f0f69f77d0 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sat, 27 Jan 2018 21:30:24 -0500 Subject: [PATCH 12/33] Add repeat polyfill for repeating a string --- src/utils/Generic.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/utils/Generic.ts b/src/utils/Generic.ts index 4bc6c487f..9f9d2666f 100644 --- a/src/utils/Generic.ts +++ b/src/utils/Generic.ts @@ -11,3 +11,31 @@ export function contains(arr: any[], el: any): boolean { return arr.indexOf(el) >= 0; } + +/** + * Returns a string repeated a given number of times + * Polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat + * @param {Number} count The number of times to repeat the string + * @param {String} string The string that is to be repeated + */ +export function repeat(count: number, str: string): string { + if (count < 0) throw new RangeError('repeat count must be non-negative'); + if (count === Infinity) throw new RangeError('repeat count must be less than infinity'); + + count = Math.floor(count); + if (str.length === 0 || count === 0) return ''; + + // Ensuring count is a 31-bit integer allows us to heavily optimize the + // main part. But anyway, most current (August 2014) browsers can't handle + // strings 1 << 28 chars or longer, so: + if (str.length * count >= 1 << 28) { + throw new RangeError('repeat count must not overflow maximum string size'); + } + + let rpt = ''; + for (let i = 0; i < count; i++) { + rpt += str; + } + + return rpt; +} From 833d8144990393ec7e50a1ccbf78a6cce3dd4aa1 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sat, 27 Jan 2018 21:30:53 -0500 Subject: [PATCH 13/33] Refactor alt-click to work independently of which buffer is being used --- src/handlers/AltClickHandler.ts | 265 ++++++++++++++++++++++---------- 1 file changed, 182 insertions(+), 83 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 59962d960..829daf97a 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -1,24 +1,32 @@ /** - * Alt+Click handler module: exports methods for handling all alt+click-related events in the - * terminal. + * Alt+Click handler module: exports methods for handling all alt+click-related + * events in the terminal. * @module xterm/handlers/AltClickHandler * @license MIT */ import { Terminal } from '../Terminal'; -import { CHAR_DATA_WIDTH_INDEX } from '../Buffer'; import { C0 } from '../EscapeSequences'; +import { repeat } from '../utils/Generic'; +import { CircularList } from '../utils/CircularList'; +import { LineData } from '../Types'; export class AltClickHandler { private _terminal: Terminal; - private _mouseRow: number; - private _mouseCol: number; + private _startRow: number; + private _startCol: number; + private _endRow: number; + private _endCol: number; + private _lines: CircularList; private _mouseEvent: MouseEvent; constructor(mouseEvent: MouseEvent, terminal: Terminal) { this._terminal = terminal; + this._lines = terminal.buffer.lines; + this._startCol = this._terminal.buffer.x; + this._startRow = this._terminal.buffer.y; - [this._mouseCol, this._mouseRow] = this._terminal.mouseHelper.getCoords( + [this._endCol, this._endRow] = this._terminal.mouseHelper.getCoords( (this._mouseEvent = mouseEvent), this._terminal.element, this._terminal.charMeasure, @@ -31,106 +39,197 @@ export class AltClickHandler { }); } + /** + * Writes the escape sequences of arrows to the terminal + */ public move(): void { - if (!this._mouseEvent.altKey) return; + if (this._mouseEvent.altKey) this._terminal.send(this._arrowSequences()); + } - let keyboardArrows; + /** + * Concatenates all the arrow sequences together. + * Resets the starting row to an unwrapped row, moves to the requested row, + * then moves to requested col. + */ + private _arrowSequences(): string { + return this._resetStartingRow() + + this._moveToRequestedRow() + + this._moveToRequestedCol(); + } - if (this._terminal.buffer === this._terminal.buffers.normal) { - keyboardArrows = this.buildArrowSequence(this.normalCharCount(), this.horizontalCursorCommand(this.normalMoveForward())); - } else { - let verticalChars = this.buildArrowSequence(this.altVerticalCharCount(), this.verticalCursorCommand(this.altMoveUpward())); - let horizontalChars = this.buildArrowSequence(this.altHorizontalCharCount(), this.horizontalCursorCommand(this.altMoveForward())); + /** + * If the initial position of the cursor is on a row that is wrapped, move the + * cursor up to the first row that is not wrapped to have accurate vertical + * positioning. + */ + private _resetStartingRow(): string { + return repeat(this._bufferLine( + this._startCol, this._startRow, this._startCol, + this._startRow - this._wrappedRowsForRow(this._startRow), false + ).length, this._colSequence(false)); + } - if (this.altMoveForward()) { - keyboardArrows = verticalChars + horizontalChars; - } else { - keyboardArrows = horizontalChars + verticalChars; + /** + * Using the reset starting and ending row, move to the requested row, + * ignoring wrapped rows + */ + private _moveToRequestedRow(): string { + let startRow = this._startRow - this._wrappedRowsForRow(this._startRow); + let endRow = this._endRow - this._wrappedRowsForRow(this._endRow); + + let rowsToMove = Math.abs(startRow - endRow) - this._wrappedRowsCount(); + + return repeat(rowsToMove, this._rowSequence(this._shouldMoveUp())); + } + + /** + * Move to the requested col on the ending row + */ + private _moveToRequestedCol(): string { + let startRow = this._endRow - this._wrappedRowsForRow(this._endRow); + let endRow = this._endRow; + let forward = this._shouldMoveForward(); + + return repeat(this._bufferLine( + this._startCol, startRow, this._endCol, endRow, forward + ).length, this._colSequence(forward)); + } + + /** + * Utility functions + */ + + /** + * Calculates the number of wrapped rows between the unwrapped starting and + * ending rows. These rows need to ignored since the cursor skips over them. + */ + private _wrappedRowsCount(): number { + let wrappedRows = 0; + let startRow = this._startRow - this._wrappedRowsForRow(this._startRow); + let endRow = this._endRow - this._wrappedRowsForRow(this._endRow); + + for (let i = 0; i < Math.abs(startRow - endRow); i++) { + let direction = this._shouldMoveUp() ? -1 : 1; + + if (this._lines.get(startRow + (direction * i)).isWrapped) wrappedRows++; + } + + return wrappedRows; + } + + /** + * Calculates the number of wrapped rows that make up a given row. + * @param currentRow The row to determine how many wrapped rows make it up + */ + private _wrappedRowsForRow(currentRow: number): number { + let rowCount = 0; + let lineWraps = this._lines.get(currentRow).isWrapped; + + while (lineWraps && currentRow >= 0 && currentRow < this._terminal.rows) { + rowCount++; + currentRow--; + lineWraps = this._lines.get(currentRow).isWrapped; + } + + return rowCount; + } + + /** + * Direction determiners + */ + + /** + * Determines if the right or left arrow is needed + */ + private _shouldMoveForward(): boolean { + let startRow = this._endRow - this._wrappedRowsForRow(this._endRow); + + return (this._startCol < this._endCol && + startRow <= this._endRow) || // down/right or same y/right + (this._startCol >= this._endCol && + startRow < this._endRow); // down/left or same y/left + } + + /** + * Determines if the up or down arrow is needed + */ + private _shouldMoveUp(): boolean { + return this._startRow > this._endRow; + } + + /** + * Constructs the string of chars in the buffer from a starting row and col + * to an ending row and col + * @param startCol The starting column position + * @param startRow The starting row position + * @param endCol The ending column position + * @param endRow The ending row position + * @param forward Direction to move + */ + private _bufferLine( + startCol: number, + startRow: number, + endCol: number, + endRow: number, + forward: boolean): string { + let currentRow = startCol; + let currentCol = startRow; + let bufferStr = ''; + + while (currentRow !== endCol || (currentCol !== endRow)) { + currentRow += forward ? 1 : -1; + + if (forward && currentRow > this._terminal.cols - 1) { + bufferStr += this._terminal.buffer.translateBufferLineToString( + currentCol, false, startCol, currentRow + ); + currentRow = 0; + startCol = 0; + currentCol++; + } else if (!forward && currentRow < 0) { + bufferStr += this._terminal.buffer.translateBufferLineToString( + currentCol, false, 0, startCol + 1 + ); + currentRow = this._terminal.cols - 1; + startCol = currentRow; + currentCol--; } } - this._terminal.send(keyboardArrows); + return bufferStr + this._terminal.buffer.translateBufferLineToString( + currentCol, false, startCol, currentRow + ); } - private buildArrowSequence(count: number, sequence: string): string { - return Array(count).join(sequence); - } + /** + * Arrow escape sequences + */ - private altMoveUpward(): boolean { - return this._terminal.buffer.y > this._mouseRow; - } - - private altMoveForward(): boolean { - return this._terminal.buffer.x < this._mouseCol; - } - - private normalMoveForward(): boolean { - return (this._terminal.buffer.x < this._mouseCol && - this._terminal.buffer.y <= this._mouseRow) || // down/right or same row/right - (this._terminal.buffer.x >= this._mouseCol && - this._terminal.buffer.y < this._mouseRow); // down/left or same row/left - } - - private horizontalCursorCommand(moveForward: boolean): string { + /** + * Constructs the escape sequence for the left or right arrow + * @param forward Right arrow or left arrow + */ + private _colSequence(forward: boolean): string { let mod = this._terminal.applicationCursor ? 'O' : '['; - if (moveForward) { + if (forward) { return C0.ESC + mod + 'C'; } else { return C0.ESC + mod + 'D'; } } - private verticalCursorCommand(moveUp: boolean): string { + /** + * Constructs the escape sequence for clicking the up or down arrow + * @param up Up arrow or down arrow + */ + private _rowSequence(up: boolean): string { let mod = this._terminal.applicationCursor ? 'O' : '['; - if (moveUp) { + if (up) { return C0.ESC + mod + 'A'; } else { return C0.ESC + mod + 'B'; } } - - private altVerticalCharCount(): number { - return Math.abs(this._terminal.buffer.y - this._mouseRow) + 1; - } - - private altHorizontalCharCount(): number { - return Math.abs(this._terminal.buffer.x - this._mouseCol) + 1; - } - - private normalCharCount(): number { - let currentX = this._terminal.buffer.x; - let currentY = this._terminal.buffer.y; - let startCol = this._terminal.buffer.x; - let bufferStr = ''; - - while (currentX !== this._mouseCol || (currentY !== this._mouseRow)) { - if (this.normalMoveForward()) { - currentX++; - if (currentX > this._terminal.cols - 1) { - bufferStr += this._terminal.buffer.translateBufferLineToString(currentY, false, startCol, currentX); - currentX = 0; - startCol = 0; - currentY++; - } - } else { - currentX--; - if (currentX < 0) { - bufferStr += this._terminal.buffer.translateBufferLineToString(currentY, false, 0, startCol + 1); - currentX = this._terminal.cols - 1; - startCol = currentX; - currentY--; - } - } - } - - if (this.normalMoveForward()) { - currentX++; - } else { - currentX--; - } - bufferStr += this._terminal.buffer.translateBufferLineToString(currentY, false, startCol, currentX); - return bufferStr.length; - } } From dc59f33ccf4a2075461285fff8fd5c1661bd7f57 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sat, 27 Jan 2018 22:06:31 -0500 Subject: [PATCH 14/33] Fix translateBufferToString bug --- src/Buffer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Buffer.ts b/src/Buffer.ts index 3e7d61224..9d47666f4 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -217,7 +217,10 @@ export class Buffer implements IBuffer { // needed here because some chars are 0 characters long (eg. after wide // chars) and some chars are longer than 1 characters long (eg. emojis). let startIndex = startCol; - endCol = endCol || line.length; + // Only set endCol to the line length when it is null. 0 is a valid column. + if (endCol === null) { + endCol = line.length; + } let endIndex = endCol; for (let i = 0; i < line.length; i++) { From a4ee7236651564dcde2554c0968f5fae0c996126 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sat, 27 Jan 2018 22:06:58 -0500 Subject: [PATCH 15/33] Fix backward row/col definition --- src/handlers/AltClickHandler.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 829daf97a..bddde6fbc 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -172,32 +172,32 @@ export class AltClickHandler { endCol: number, endRow: number, forward: boolean): string { - let currentRow = startCol; - let currentCol = startRow; + let currentCol = startCol; + let currentRow = startRow; let bufferStr = ''; - while (currentRow !== endCol || (currentCol !== endRow)) { - currentRow += forward ? 1 : -1; + while (currentCol !== endCol || currentRow !== endRow) { + currentCol += forward ? 1 : -1; - if (forward && currentRow > this._terminal.cols - 1) { + if (forward && currentCol > this._terminal.cols - 1) { bufferStr += this._terminal.buffer.translateBufferLineToString( - currentCol, false, startCol, currentRow + currentRow, false, startCol, currentCol ); - currentRow = 0; + currentCol = 0; startCol = 0; - currentCol++; - } else if (!forward && currentRow < 0) { + currentRow++; + } else if (!forward && currentCol < 0) { bufferStr += this._terminal.buffer.translateBufferLineToString( - currentCol, false, 0, startCol + 1 + currentRow, false, 0, startCol + 1 ); - currentRow = this._terminal.cols - 1; - startCol = currentRow; - currentCol--; + currentCol = this._terminal.cols - 1; + startCol = currentCol; + currentRow--; } } return bufferStr + this._terminal.buffer.translateBufferLineToString( - currentCol, false, startCol, currentRow + currentRow, false, startCol, currentCol ); } From 6a48022a454aff1811bf6349f3389b07db5b81a8 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sat, 27 Jan 2018 22:28:41 -0500 Subject: [PATCH 16/33] Don't reset starting position if the row is not moved. Mainly due to the normal buffer --- src/handlers/AltClickHandler.ts | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index bddde6fbc..94a1a7ecb 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -63,10 +63,17 @@ export class AltClickHandler { * positioning. */ private _resetStartingRow(): string { - return repeat(this._bufferLine( - this._startCol, this._startRow, this._startCol, - this._startRow - this._wrappedRowsForRow(this._startRow), false - ).length, this._colSequence(false)); + let startRow = this._endRow - this._wrappedRowsForRow(this._endRow); + let endRow = this._endRow; + + if (this._moveToRequestedRow().length === 0) { + return "" + } else { + return repeat(this._bufferLine( + this._startCol, this._startRow, this._startCol, + this._startRow - this._wrappedRowsForRow(this._startRow), false + ).length, this._colSequence(false)); + } } /** @@ -86,7 +93,13 @@ export class AltClickHandler { * Move to the requested col on the ending row */ private _moveToRequestedCol(): string { - let startRow = this._endRow - this._wrappedRowsForRow(this._endRow); + let startRow; + if (this._moveToRequestedRow().length > 0) { + startRow = this._endRow - this._wrappedRowsForRow(this._endRow); + } else { + startRow = this._startRow; + } + let endRow = this._endRow; let forward = this._shouldMoveForward(); @@ -142,7 +155,12 @@ export class AltClickHandler { * Determines if the right or left arrow is needed */ private _shouldMoveForward(): boolean { - let startRow = this._endRow - this._wrappedRowsForRow(this._endRow); + let startRow; + if (this._moveToRequestedRow().length > 0) { + startRow = this._endRow - this._wrappedRowsForRow(this._endRow); + } else { + startRow = this._startRow; + } return (this._startCol < this._endCol && startRow <= this._endRow) || // down/right or same y/right From 544fa67851125413f84b110a22bb38ba3c243862 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sat, 27 Jan 2018 22:36:53 -0500 Subject: [PATCH 17/33] Move repeat out of generic --- src/handlers/AltClickHandler.ts | 28 ++++++++++++++++++++++++++++ src/utils/Generic.ts | 28 ---------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 94a1a7ecb..6955423ba 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -251,3 +251,31 @@ export class AltClickHandler { } } } + +/** + * Returns a string repeated a given number of times + * Polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat + * @param {Number} count The number of times to repeat the string + * @param {String} string The string that is to be repeated + */ +function repeat(count: number, str: string): string { + if (count < 0) throw new RangeError('repeat count must be non-negative'); + if (count === Infinity) throw new RangeError('repeat count must be less than infinity'); + + count = Math.floor(count); + if (str.length === 0 || count === 0) return ''; + + // Ensuring count is a 31-bit integer allows us to heavily optimize the + // main part. But anyway, most current (August 2014) browsers can't handle + // strings 1 << 28 chars or longer, so: + if (str.length * count >= 1 << 28) { + throw new RangeError('repeat count must not overflow maximum string size'); + } + + let rpt = ''; + for (let i = 0; i < count; i++) { + rpt += str; + } + + return rpt; +} diff --git a/src/utils/Generic.ts b/src/utils/Generic.ts index 9f9d2666f..4bc6c487f 100644 --- a/src/utils/Generic.ts +++ b/src/utils/Generic.ts @@ -11,31 +11,3 @@ export function contains(arr: any[], el: any): boolean { return arr.indexOf(el) >= 0; } - -/** - * Returns a string repeated a given number of times - * Polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat - * @param {Number} count The number of times to repeat the string - * @param {String} string The string that is to be repeated - */ -export function repeat(count: number, str: string): string { - if (count < 0) throw new RangeError('repeat count must be non-negative'); - if (count === Infinity) throw new RangeError('repeat count must be less than infinity'); - - count = Math.floor(count); - if (str.length === 0 || count === 0) return ''; - - // Ensuring count is a 31-bit integer allows us to heavily optimize the - // main part. But anyway, most current (August 2014) browsers can't handle - // strings 1 << 28 chars or longer, so: - if (str.length * count >= 1 << 28) { - throw new RangeError('repeat count must not overflow maximum string size'); - } - - let rpt = ''; - for (let i = 0; i < count; i++) { - rpt += str; - } - - return rpt; -} From d61aa6e4706bc2b5c46051f5a80e8c90d7a7dda8 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sat, 27 Jan 2018 22:37:31 -0500 Subject: [PATCH 18/33] Fix lints --- src/handlers/AltClickHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 6955423ba..eae491b8b 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -67,7 +67,7 @@ export class AltClickHandler { let endRow = this._endRow; if (this._moveToRequestedRow().length === 0) { - return "" + return ''; } else { return repeat(this._bufferLine( this._startCol, this._startRow, this._startCol, From c41a4b5168ab9b3955b57438b4a7676340e92db2 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sat, 27 Jan 2018 22:37:57 -0500 Subject: [PATCH 19/33] Move repeat import --- src/handlers/AltClickHandler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index eae491b8b..bed9a8631 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -7,7 +7,6 @@ import { Terminal } from '../Terminal'; import { C0 } from '../EscapeSequences'; -import { repeat } from '../utils/Generic'; import { CircularList } from '../utils/CircularList'; import { LineData } from '../Types'; From 65b6901170a0bea3a49491e63461ab3a29dae75d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 27 Jan 2018 21:50:28 -0800 Subject: [PATCH 20/33] Fix compile error --- src/handlers/AltClickHandler.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index bed9a8631..e52bd1a3a 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -123,7 +123,9 @@ export class AltClickHandler { for (let i = 0; i < Math.abs(startRow - endRow); i++) { let direction = this._shouldMoveUp() ? -1 : 1; - if (this._lines.get(startRow + (direction * i)).isWrapped) wrappedRows++; + if ((this._lines.get(startRow + (direction * i))).isWrapped) { + wrappedRows++; + } } return wrappedRows; @@ -135,12 +137,12 @@ export class AltClickHandler { */ private _wrappedRowsForRow(currentRow: number): number { let rowCount = 0; - let lineWraps = this._lines.get(currentRow).isWrapped; + let lineWraps = (this._lines.get(currentRow)).isWrapped; while (lineWraps && currentRow >= 0 && currentRow < this._terminal.rows) { rowCount++; currentRow--; - lineWraps = this._lines.get(currentRow).isWrapped; + lineWraps = (this._lines.get(currentRow)).isWrapped; } return rowCount; From 39abf8aecf7650df4ae8bfb6940345b393846c2c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 27 Jan 2018 22:26:00 -0800 Subject: [PATCH 21/33] Change render layer id to a class It's only used to identify them in DOM explorer currently Fixes #1255 --- src/renderer/BaseRenderLayer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 20a41cfa3..17f6f750b 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -31,7 +31,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { protected _colors: IColorSet ) { this._canvas = document.createElement('canvas'); - this._canvas.id = `xterm-${id}-layer`; + this._canvas.classList.add(`xterm-${id}-layer`); this._canvas.style.zIndex = zIndex.toString(); this._ctx = this._canvas.getContext('2d', {alpha: _alpha}); this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); From edadb6e0f1941b8c05b0eebd1dcc879948a7a7d9 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sun, 28 Jan 2018 10:44:01 -0500 Subject: [PATCH 22/33] Update the copyright header --- src/handlers/AltClickHandler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index e52bd1a3a..471977dda 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -1,7 +1,5 @@ /** - * Alt+Click handler module: exports methods for handling all alt+click-related - * events in the terminal. - * @module xterm/handlers/AltClickHandler + * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ From 0202659c389c84009ca6998b7a8fc80b22276cc5 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sun, 28 Jan 2018 10:46:48 -0500 Subject: [PATCH 23/33] Set terminal and mouseevent inside constructor args --- src/handlers/AltClickHandler.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 471977dda..9cfda3ec5 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -9,22 +9,19 @@ import { CircularList } from '../utils/CircularList'; import { LineData } from '../Types'; export class AltClickHandler { - private _terminal: Terminal; private _startRow: number; private _startCol: number; private _endRow: number; private _endCol: number; private _lines: CircularList; - private _mouseEvent: MouseEvent; - constructor(mouseEvent: MouseEvent, terminal: Terminal) { - this._terminal = terminal; - this._lines = terminal.buffer.lines; + constructor(private _mouseEvent: MouseEvent, private _terminal: Terminal) { + this._lines = this._terminal.buffer.lines; this._startCol = this._terminal.buffer.x; this._startRow = this._terminal.buffer.y; [this._endCol, this._endRow] = this._terminal.mouseHelper.getCoords( - (this._mouseEvent = mouseEvent), + this._mouseEvent, this._terminal.element, this._terminal.charMeasure, this._terminal.options.lineHeight, From 08af2ebeea472bee19b9d0509710eaa9cfb5ee54 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sun, 28 Jan 2018 12:29:52 -0500 Subject: [PATCH 24/33] Utilize enum to simplify sequence generation --- src/handlers/AltClickHandler.ts | 73 +++++++++++++++------------------ 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 9cfda3ec5..c67034855 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -8,6 +8,13 @@ import { C0 } from '../EscapeSequences'; import { CircularList } from '../utils/CircularList'; import { LineData } from '../Types'; +enum Direction { + Up = 'A', + Down = 'B', + Right = 'C', + Left = 'D' +}; + export class AltClickHandler { private _startRow: number; private _startCol: number; @@ -66,7 +73,7 @@ export class AltClickHandler { return repeat(this._bufferLine( this._startCol, this._startRow, this._startCol, this._startRow - this._wrappedRowsForRow(this._startRow), false - ).length, this._colSequence(false)); + ).length, this._sequence(Direction.Left)); } } @@ -80,7 +87,7 @@ export class AltClickHandler { let rowsToMove = Math.abs(startRow - endRow) - this._wrappedRowsCount(); - return repeat(rowsToMove, this._rowSequence(this._shouldMoveUp())); + return repeat(rowsToMove, this._sequence(this._verticalDirection())); } /** @@ -95,11 +102,12 @@ export class AltClickHandler { } let endRow = this._endRow; - let forward = this._shouldMoveForward(); + let direction = this._horizontalDirection(); return repeat(this._bufferLine( - this._startCol, startRow, this._endCol, endRow, forward - ).length, this._colSequence(forward)); + this._startCol, startRow, this._endCol, endRow, + direction === Direction.Right + ).length, this._sequence(direction)); } /** @@ -116,7 +124,7 @@ export class AltClickHandler { let endRow = this._endRow - this._wrappedRowsForRow(this._endRow); for (let i = 0; i < Math.abs(startRow - endRow); i++) { - let direction = this._shouldMoveUp() ? -1 : 1; + let direction = this._verticalDirection() === Direction.Up ? -1 : 1; if ((this._lines.get(startRow + (direction * i))).isWrapped) { wrappedRows++; @@ -150,7 +158,7 @@ export class AltClickHandler { /** * Determines if the right or left arrow is needed */ - private _shouldMoveForward(): boolean { + private _horizontalDirection(): Direction { let startRow; if (this._moveToRequestedRow().length > 0) { startRow = this._endRow - this._wrappedRowsForRow(this._endRow); @@ -158,17 +166,25 @@ export class AltClickHandler { startRow = this._startRow; } - return (this._startCol < this._endCol && + if ((this._startCol < this._endCol && startRow <= this._endRow) || // down/right or same y/right (this._startCol >= this._endCol && - startRow < this._endRow); // down/left or same y/left + startRow < this._endRow)) { // down/left or same y/left + return Direction.Right; + } else { + return Direction.Left; + } } /** * Determines if the up or down arrow is needed */ - private _shouldMoveUp(): boolean { - return this._startRow > this._endRow; + private _verticalDirection(): Direction { + if (this._startRow > this._endRow) { + return Direction.Up; + } else { + return Direction.Down; + } } /** @@ -216,36 +232,13 @@ export class AltClickHandler { } /** - * Arrow escape sequences + * Constructs the escape sequence for clicking an arrow + * @param direction The direction to move */ - - /** - * Constructs the escape sequence for the left or right arrow - * @param forward Right arrow or left arrow - */ - private _colSequence(forward: boolean): string { - let mod = this._terminal.applicationCursor ? 'O' : '['; - - if (forward) { - return C0.ESC + mod + 'C'; - } else { - return C0.ESC + mod + 'D'; - } - } - - /** - * Constructs the escape sequence for clicking the up or down arrow - * @param up Up arrow or down arrow - */ - private _rowSequence(up: boolean): string { - let mod = this._terminal.applicationCursor ? 'O' : '['; - - if (up) { - return C0.ESC + mod + 'A'; - } else { - return C0.ESC + mod + 'B'; - } - } + private _sequence(direction: Direction): string { + const mod = this._terminal.applicationCursor ? 'O' : '['; + return C0.ESC + mod + direction; + } } /** From aeae40e1105f722a0b5e8a38d9c9884e1d2074f5 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sun, 28 Jan 2018 12:38:20 -0500 Subject: [PATCH 25/33] Use the terminal and circular list interface --- src/Types.ts | 3 ++- src/handlers/AltClickHandler.ts | 7 +++---- src/utils/TestUtils.test.ts | 4 ++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Types.ts b/src/Types.ts index f3e9bb941..df52dd8ac 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -188,12 +188,13 @@ export interface ITerminal extends PublicTerminal, IElementAccessor, IBufferAcce isFocused: boolean; mouseHelper: IMouseHelper; bracketedPasteMode: boolean; - + applicationCursor: boolean; /** * Emit the 'data' event and populate the given data. * @param data The data to populate in the event. */ handler(data: string): void; + send(data: string): void; scrollLines(disp: number, suppressScrollEvent?: boolean): void; cancel(ev: Event, force?: boolean): boolean | void; log(text: string): void; diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index c67034855..ccdec7aee 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -3,9 +3,8 @@ * @license MIT */ -import { Terminal } from '../Terminal'; +import { ITerminal, ICircularList } from '../Types'; import { C0 } from '../EscapeSequences'; -import { CircularList } from '../utils/CircularList'; import { LineData } from '../Types'; enum Direction { @@ -20,9 +19,9 @@ export class AltClickHandler { private _startCol: number; private _endRow: number; private _endCol: number; - private _lines: CircularList; + private _lines: ICircularList; - constructor(private _mouseEvent: MouseEvent, private _terminal: Terminal) { + constructor(private _mouseEvent: MouseEvent, private _terminal: ITerminal) { this._lines = this._terminal.buffer.lines; this._startCol = this._terminal.buffer.x; this._startRow = this._terminal.buffer.y; diff --git a/src/utils/TestUtils.test.ts b/src/utils/TestUtils.test.ts index 0744cd142..d53c8c58b 100644 --- a/src/utils/TestUtils.test.ts +++ b/src/utils/TestUtils.test.ts @@ -70,6 +70,9 @@ export class MockTerminal implements ITerminal { write(data: string): void { throw new Error('Method not implemented.'); } + send(data: string): void { + throw new Error('Method not implemented.'); + } bracketedPasteMode: boolean; mouseHelper: IMouseHelper; renderer: IRenderer; @@ -93,6 +96,7 @@ export class MockTerminal implements ITerminal { scrollback: number; buffers: IBufferSet; buffer: IBuffer; + applicationCursor: boolean; handler(data: string): void { throw new Error('Method not implemented.'); } From e60704255f518a9feff106885849147011fe0df5 Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sun, 28 Jan 2018 13:00:11 -0500 Subject: [PATCH 26/33] Fix lints --- src/handlers/AltClickHandler.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index ccdec7aee..307fa14a6 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -3,16 +3,15 @@ * @license MIT */ -import { ITerminal, ICircularList } from '../Types'; +import { ITerminal, ICircularList, LineData } from '../Types'; import { C0 } from '../EscapeSequences'; -import { LineData } from '../Types'; enum Direction { Up = 'A', Down = 'B', Right = 'C', Left = 'D' -}; +} export class AltClickHandler { private _startRow: number; From f726c1fd0d065fa6b43e5b14d7c7194bf754ebca Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sun, 28 Jan 2018 13:06:54 -0500 Subject: [PATCH 27/33] Allow selection and alt click to live conflict free --- src/SelectionManager.ts | 11 ++++++++++- src/Terminal.ts | 2 -- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 10612c5c6..75ada79af 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -11,6 +11,7 @@ import { CircularList } from './utils/CircularList'; import { EventEmitter } from './EventEmitter'; import { SelectionModel } from './SelectionModel'; import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer'; +import { AltClickHandler } from './handlers/AltClickHandler'; /** * The number of pixels the mouse needs to be above or below the viewport in @@ -96,6 +97,8 @@ export class SelectionManager extends EventEmitter implements ISelectionManager private _mouseUpListener: EventListener; private _trimListener: (...args: any[]) => void; + private _mouseDownTimeStamp: number; + constructor( private _terminal: ITerminal, private _charMeasure: CharMeasure @@ -317,6 +320,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager * @param event The mousedown event. */ public onMouseDown(event: MouseEvent): void { + this._mouseDownTimeStamp = event.timeStamp; // If we have selection, we want the context menu on right click even if the // terminal is in mouse mode. if (event.button === 2 && this.hasSelection) { @@ -536,10 +540,15 @@ export class SelectionManager extends EventEmitter implements ISelectionManager * @param event The mouseup event. */ private _onMouseUp(event: MouseEvent): void { + let timeElapsed = event.timeStamp - this._mouseDownTimeStamp; + this._removeMouseDownListeners(); - if (this.hasSelection) + if (this.selectionText.length <= 1 && timeElapsed < 500) { + (new AltClickHandler(event, this._terminal)).move(); + } else if (this.hasSelection) { this._terminal.emit('selection'); + } } private _onBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void { diff --git a/src/Terminal.ts b/src/Terminal.ts index 2977154ee..5536cf03c 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -30,7 +30,6 @@ import { CompositionHelper } from './CompositionHelper'; import { EventEmitter } from './EventEmitter'; import { Viewport } from './Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard'; -import { AltClickHandler } from './handlers/AltClickHandler'; import { CircularList } from './utils/CircularList'; import { C0 } from './EscapeSequences'; import { InputHandler } from './InputHandler'; @@ -953,7 +952,6 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT // if the selection manager is having selection forced (ie. a modifier is // held). if (!this.mouseEvents || this.selectionManager.shouldForceSelection(ev)) { - (new AltClickHandler(ev, this)).move(); return; } From 3313716c87f9c1a25bf8d37c0895503ed0ca276c Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sun, 28 Jan 2018 13:15:42 -0500 Subject: [PATCH 28/33] Add new lines back in types --- src/Types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Types.ts b/src/Types.ts index df52dd8ac..da3cc9d2a 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -189,6 +189,7 @@ export interface ITerminal extends PublicTerminal, IElementAccessor, IBufferAcce mouseHelper: IMouseHelper; bracketedPasteMode: boolean; applicationCursor: boolean; + /** * Emit the 'data' event and populate the given data. * @param data The data to populate in the event. From 3bdc069f1961d7c78773ad099173db97d754d60a Mon Sep 17 00:00:00 2001 From: npezza93 Date: Sun, 28 Jan 2018 13:16:20 -0500 Subject: [PATCH 29/33] Set copyright header to 2018 --- src/handlers/AltClickHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 307fa14a6..e09438838 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ From af375eadb4a14dc5c1b65414e77e9bc774c8cc60 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 28 Jan 2018 12:27:01 -0800 Subject: [PATCH 30/33] Run tsc as a travis job This will hard fail when there are semantic errors. Currently building does not do this and we will merge these in as a result. --- .travis.yml | 1 + package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7a37af1e7..c41ebf797 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ before_install: - npm install -g npm@5.1.0 env: matrix: + - NPM_COMMAND=tsc - NPM_COMMAND=lint - NPM_COMMAND=test notifications: diff --git a/package.json b/package.json index cf3df57be..ba988d613 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "lint": "tslint src/*.ts src/**/*.ts", "test": "gulp test", "build:docs": "jsdoc -c jsdoc.json", + "tsc": "tsc", "build": "gulp build", "prepublish": "npm run build", "coveralls": "gulp coveralls", From fc2af1d806e970b4d6ff331401b93f8417f8b784 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 28 Jan 2018 14:13:25 -0800 Subject: [PATCH 31/33] Fix lint, reduce duplication, don't scale ctx Scaling ctx was breaking the scale when setOption was called, not sure it was doing anything. --- src/renderer/BaseRenderLayer.ts | 36 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 23eb8f213..35e1a57e8 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -24,7 +24,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { private _charAtlas: HTMLCanvasElement | ImageBitmap; constructor( - container: HTMLElement, + private _container: HTMLElement, id: string, zIndex: number, private _alpha: boolean, @@ -33,13 +33,16 @@ export abstract class BaseRenderLayer implements IRenderLayer { this._canvas = document.createElement('canvas'); this._canvas.classList.add(`xterm-${id}-layer`); this._canvas.style.zIndex = zIndex.toString(); - this._ctx = this._canvas.getContext('2d', {alpha: _alpha}); - this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); + this._initCanvas(); + this._container.appendChild(this._canvas); + } + + private _initCanvas(): void { + this._ctx = this._canvas.getContext('2d', {alpha: this._alpha}); // Draw the background if this is an opaque layer - if (!_alpha) { + if (!this._alpha) { this.clearAll(); } - container.appendChild(this._canvas); } public onOptionsChanged(terminal: ITerminal): void {} @@ -54,23 +57,22 @@ export abstract class BaseRenderLayer implements IRenderLayer { } protected setTransparency(terminal: ITerminal, alpha: boolean): void { + // Do nothing when alpha doesn't change if (alpha === this._alpha) { return; } - this._alpha = alpha - const oldCanvas = this._canvas - // Cloning preserves canvas properties and permits to acquire a new context + + // Create new canvas and replace old one + const oldCanvas = this._canvas; + this._alpha = alpha; + // Closing preserves properties this._canvas = this._canvas.cloneNode(); - this._ctx = this._canvas.getContext('2d', {alpha: this._alpha}); - this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - // Draw the background if this is an opaque layer - if (!this._alpha) { - this.clearAll(); - } - oldCanvas.parentNode.replaceChild(this._canvas, oldCanvas); + this._initCanvas(); + this._container.replaceChild(this._canvas, oldCanvas); + + // Regenerate char atlas and force a full redraw this._refreshCharAtlas(terminal, this._colors); - // Force a full redraw - this.onGridChanged(terminal, 0, terminal.rows - 1) + this.onGridChanged(terminal, 0, terminal.rows - 1); } /** From e8666cafc92a21802526c3d9883703ba4ae7d373 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 28 Jan 2018 14:44:33 -0800 Subject: [PATCH 32/33] Small clean up --- src/SelectionManager.ts | 8 ++++++- src/handlers/AltClickHandler.ts | 37 +++++++++++++-------------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 75ada79af..3c927839e 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -29,6 +29,12 @@ const DRAG_SCROLL_MAX_SPEED = 15; */ const DRAG_SCROLL_INTERVAL = 50; +/** + * The maximum amount of time that can have elapsed for an alt click to move the + * cursor. + */ +const ALT_CLICK_MOVE_CURSOR_TIME = 500; + /** * A string containing all characters that are considered word separated by the * double click to select work logic. @@ -544,7 +550,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager this._removeMouseDownListeners(); - if (this.selectionText.length <= 1 && timeElapsed < 500) { + if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME) { (new AltClickHandler(event, this._terminal)).move(); } else if (this.hasSelection) { this._terminal.emit('selection'); diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index e09438838..c9c51cbe5 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -20,7 +20,10 @@ export class AltClickHandler { private _endCol: number; private _lines: ICircularList; - constructor(private _mouseEvent: MouseEvent, private _terminal: ITerminal) { + constructor( + private _mouseEvent: MouseEvent, + private _terminal: ITerminal + ) { this._lines = this._terminal.buffer.lines; this._startCol = this._terminal.buffer.x; this._startRow = this._terminal.buffer.y; @@ -42,7 +45,9 @@ export class AltClickHandler { * Writes the escape sequences of arrows to the terminal */ public move(): void { - if (this._mouseEvent.altKey) this._terminal.send(this._arrowSequences()); + if (this._mouseEvent.altKey) { + this._terminal.send(this._arrowSequences()); + } } /** @@ -199,7 +204,8 @@ export class AltClickHandler { startRow: number, endCol: number, endRow: number, - forward: boolean): string { + forward: boolean + ): string { let currentCol = startCol; let currentRow = startRow; let bufferStr = ''; @@ -233,36 +239,23 @@ export class AltClickHandler { * Constructs the escape sequence for clicking an arrow * @param direction The direction to move */ - private _sequence(direction: Direction): string { - const mod = this._terminal.applicationCursor ? 'O' : '['; - return C0.ESC + mod + direction; - } + private _sequence(direction: Direction): string { + const mod = this._terminal.applicationCursor ? 'O' : '['; + return C0.ESC + mod + direction; + } } /** * Returns a string repeated a given number of times * Polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat - * @param {Number} count The number of times to repeat the string - * @param {String} string The string that is to be repeated + * @param count The number of times to repeat the string + * @param string The string that is to be repeated */ function repeat(count: number, str: string): string { - if (count < 0) throw new RangeError('repeat count must be non-negative'); - if (count === Infinity) throw new RangeError('repeat count must be less than infinity'); - count = Math.floor(count); - if (str.length === 0 || count === 0) return ''; - - // Ensuring count is a 31-bit integer allows us to heavily optimize the - // main part. But anyway, most current (August 2014) browsers can't handle - // strings 1 << 28 chars or longer, so: - if (str.length * count >= 1 << 28) { - throw new RangeError('repeat count must not overflow maximum string size'); - } - let rpt = ''; for (let i = 0; i < count; i++) { rpt += str; } - return rpt; } From 69ebb3e53e3ee8cfe35ab57844850a39b70a8503 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 29 Jan 2018 07:42:59 -0800 Subject: [PATCH 33/33] Fix recent typo in comment --- src/renderer/BaseRenderLayer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 35e1a57e8..9423f65d3 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -65,7 +65,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { // Create new canvas and replace old one const oldCanvas = this._canvas; this._alpha = alpha; - // Closing preserves properties + // Cloning preserves properties this._canvas = this._canvas.cloneNode(); this._initCanvas(); this._container.replaceChild(this._canvas, oldCanvas);