From e56f710cd1ce2e4aa15ea8af8bd5b0cc8cfd736e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 21 Jul 2017 05:45:53 -0700 Subject: [PATCH] Make SelectionManager more resilient Fixes #809 --- src/SelectionManager.ts | 34 ++++++++++++++++++++++++++-------- src/utils/Mouse.ts | 8 ++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 78494ae..8f1d0e2 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -277,6 +277,10 @@ export class SelectionManager extends EventEmitter { */ private _getMouseBufferCoords(event: MouseEvent): [number, number] { const coords = Mouse.getCoords(event, this._rowContainer, this._charMeasure, this._terminal.cols, this._terminal.rows, true); + if (!coords) { + return null; + } + // Convert to 0-based coords[0]--; coords[1]--; @@ -377,15 +381,25 @@ export class SelectionManager extends EventEmitter { this._model.selectionStartLength = 0; this._model.isSelectAllActive = false; this._activeSelectionMode = SelectionMode.NORMAL; + + // Initialize the new selection this._model.selectionStart = this._getMouseBufferCoords(event); - if (this._model.selectionStart) { - this._model.selectionEnd = null; - // If the mouse is over the second half of a wide character, adjust the - // selection to cover the whole character - const char = this._buffer.get(this._model.selectionStart[1])[this._model.selectionStart[0]]; - if (char[LINE_DATA_WIDTH_INDEX] === 0) { - this._model.selectionStart[0]++; - } + if (!this._model.selectionStart) { + return; + } + this._model.selectionEnd = null; + + // Ensure the line exists + const line = this._buffer.get(this._model.selectionStart[1]); + if (!line) { + return; + } + + // If the mouse is over the second half of a wide character, adjust the + // selection to cover the whole character + const char = line[this._model.selectionStart[0]]; + if (char[LINE_DATA_WIDTH_INDEX] === 0) { + this._model.selectionStart[0]++; } } @@ -426,6 +440,10 @@ export class SelectionManager extends EventEmitter { // Set the initial selection end based on the mouse coordinates this._model.selectionEnd = this._getMouseBufferCoords(event); + if (!this._model.selectionEnd) { + this.refresh(true); + return; + } // Select the entire line if line select mode is active. if (this._activeSelectionMode === SelectionMode.LINE) { diff --git a/src/utils/Mouse.ts b/src/utils/Mouse.ts index a5d72c1..c61624e 100644 --- a/src/utils/Mouse.ts +++ b/src/utils/Mouse.ts @@ -37,7 +37,15 @@ export function getCoordsRelativeToElement(event: MouseEvent, element: HTMLEleme * select that cell and the right half will select the next cell. */ export function getCoords(event: MouseEvent, rowContainer: HTMLElement, charMeasure: CharMeasure, colCount: number, rowCount: number, isSelection?: boolean): [number, number] { + // Coordinates cannot be measured if charMeasure has not been initialized + if (!charMeasure.width || !charMeasure.height) { + return null; + } + const coords = getCoordsRelativeToElement(event, rowContainer); + if (!coords) { + return null; + } // Convert to cols/rows. coords[0] = Math.ceil((coords[0] + (isSelection ? charMeasure.width / 2 : 0)) / charMeasure.width);