From ec78fc7e7c86ff9b0266e23db3a60945d75b54d0 Mon Sep 17 00:00:00 2001 From: Jon Bockhorst Date: Fri, 1 Nov 2019 13:42:02 -0500 Subject: [PATCH] Remove usage of ! operator --- .../src/WebLinkProvider.ts | 310 +----------------- .../src/WebLinksAddon.ts | 8 +- addons/xterm-addon-web-links/src/charCode.ts | 237 ------------- .../src/characterClassifier.ts | 116 ------- src/browser/Linkifier2.ts | 56 ++-- 5 files changed, 46 insertions(+), 681 deletions(-) delete mode 100644 addons/xterm-addon-web-links/src/charCode.ts delete mode 100644 addons/xterm-addon-web-links/src/characterClassifier.ts diff --git a/addons/xterm-addon-web-links/src/WebLinkProvider.ts b/addons/xterm-addon-web-links/src/WebLinkProvider.ts index f025f2b95..9c9a4e6f9 100644 --- a/addons/xterm-addon-web-links/src/WebLinkProvider.ts +++ b/addons/xterm-addon-web-links/src/WebLinkProvider.ts @@ -1,217 +1,23 @@ import { ILinkProvider, IBufferCellPosition, ILink, Terminal, IBuffer } from 'xterm'; -import { CharCode } from './charCode'; -import { CharacterClassifier } from './characterClassifier'; - export default class WebLinkProvider implements ILinkProvider { constructor( private readonly _terminal: Terminal, + private readonly _regex: RegExp, private readonly _handler: (event: MouseEvent, uri: string) => void ) { } provideLink(position: IBufferCellPosition, callback: (link: ILink | undefined) => void): void { - const link = LinkComputer.computeLink(position, this._terminal.buffer, this._handler); - - callback(link); + callback(LinkComputer.computeLink(position, this._regex, this._terminal.buffer, this._handler)); } } -export const enum State { - INVALID = 0, - START = 1, - H = 2, - HT = 3, - HTT = 4, - HTTP = 5, - BEFORE_COLON = 9, - AFTER_COLON = 10, - ALMOST_THERE = 11, - END = 12, - ACCEPT = 13, - LAST_KNOWN_STATE = 14 // marker, custom states may follow -} - -export type Edge = [State, number, State]; - -export class Uint8Matrix { - - private readonly _data: Uint8Array; - public readonly rows: number; - public readonly cols: number; - - constructor(rows: number, cols: number, defaultValue: number) { - const data = new Uint8Array(rows * cols); - const len = rows * cols; - for (let i = 0; i < len; i++) { - data[i] = defaultValue; - } - - this._data = data; - this.rows = rows; - this.cols = cols; - } - - public get(row: number, col: number): number { - return this._data[row * this.cols + col]; - } - - public set(row: number, col: number, value: number): void { - this._data[row * this.cols + col] = value; - } -} - -export class StateMachine { - - private readonly _states: Uint8Matrix; - private readonly _maxCharCode: number; - - constructor(edges: Edge[]) { - let maxCharCode = 0; - let maxState = State.INVALID; - for (let i = 0; i < edges.length; i++) { - const [from, chCode, to] = edges[i]; - if (chCode > maxCharCode) { - maxCharCode = chCode; - } - if (from > maxState) { - maxState = from; - } - if (to > maxState) { - maxState = to; - } - } - - maxCharCode++; - maxState++; - - const states = new Uint8Matrix(maxState, maxCharCode, State.INVALID); - for (let i = 0; i < edges.length; i++) { - const [from, chCode, to] = edges[i]; - states.set(from, chCode, to); - } - - this._states = states; - this._maxCharCode = maxCharCode; - } - - public nextState(currentState: State, chCode: number): State { - if (chCode < 0 || chCode >= this._maxCharCode) { - return State.INVALID; - } - return this._states.get(currentState, chCode); - } -} - -// State machine for http:// or https:// or file:// -let stateMachine: StateMachine | null = null; -function getStateMachine(): StateMachine { - if (stateMachine === null) { - stateMachine = new StateMachine([ - [State.START, CharCode.h, State.H], - [State.START, CharCode.H, State.H], - - [State.H, CharCode.t, State.HT], - [State.H, CharCode.T, State.HT], - - [State.HT, CharCode.t, State.HTT], - [State.HT, CharCode.T, State.HTT], - - [State.HTT, CharCode.p, State.HTTP], - [State.HTT, CharCode.P, State.HTTP], - - [State.HTTP, CharCode.s, State.BEFORE_COLON], - [State.HTTP, CharCode.S, State.BEFORE_COLON], - [State.HTTP, CharCode.COLON, State.AFTER_COLON], - - [State.BEFORE_COLON, CharCode.COLON, State.AFTER_COLON], - - [State.AFTER_COLON, CharCode.SLASH, State.ALMOST_THERE], - - [State.ALMOST_THERE, CharCode.SLASH, State.END] - ]); - } - return stateMachine; -} - - -const enum CharacterClass { - NONE = 0, - FORCE_TERMINATION = 1, - CANNOT_END_IN = 2 -} - -let classifier: CharacterClassifier | null = null; -function getClassifier(): CharacterClassifier { - if (classifier === null) { - classifier = new CharacterClassifier(CharacterClass.NONE); - - const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…'; - for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) { - classifier.set(FORCE_TERMINATION_CHARACTERS.charCodeAt(i), CharacterClass.FORCE_TERMINATION); - } - - const CANNOT_END_WITH_CHARACTERS = '.,;'; - for (let i = 0; i < CANNOT_END_WITH_CHARACTERS.length; i++) { - classifier.set(CANNOT_END_WITH_CHARACTERS.charCodeAt(i), CharacterClass.CANNOT_END_IN); - } - } - return classifier; -} - export class LinkComputer { - - private static _createLink(classifier: CharacterClassifier, line: string, lineNumber: number, linkBeginIndex: number, linkEndIndex: number, handler: (event: MouseEvent, link: string) => void): ILink { - // Do not allow to end link in certain characters... - let lastIncludedCharIndex = linkEndIndex - 1; - do { - const chCode = line.charCodeAt(lastIncludedCharIndex); - const chClass = classifier.get(chCode); - if (chClass !== CharacterClass.CANNOT_END_IN) { - break; - } - lastIncludedCharIndex--; - } while (lastIncludedCharIndex > linkBeginIndex); - - // Handle links enclosed in parens, square brackets and curlys. - if (linkBeginIndex > 0) { - const charCodeBeforeLink = line.charCodeAt(linkBeginIndex - 1); - const lastCharCodeInLink = line.charCodeAt(lastIncludedCharIndex); - - if ( - (charCodeBeforeLink === CharCode.OPEN_PAREN && lastCharCodeInLink === CharCode.CLOSE_PAREN) - || (charCodeBeforeLink === CharCode.OPEN_SQUARE_BRACKET && lastCharCodeInLink === CharCode.CLOSE_SQUARE_BRACKET) - || (charCodeBeforeLink === CharCode.OPEN_CURLY_BRACE && lastCharCodeInLink === CharCode.CLOSE_CURLY_BRACE) - ) { - // Do not end in ) if ( is before the link start - // Do not end in ] if [ is before the link start - // Do not end in } if { is before the link start - lastIncludedCharIndex--; - } - } - - return { - range: { - start: { - x: linkBeginIndex + 1, - y: lineNumber - }, - end: { - x: lastIncludedCharIndex + 2, - y: lineNumber - } - }, - url: line.substring(linkBeginIndex, lastIncludedCharIndex + 1), - handle: handler - }; - } - - public static computeLink(position: IBufferCellPosition, buffer: IBuffer, handler: (event: MouseEvent, link: string) => void): ILink | undefined { - const stateMachine: StateMachine = getStateMachine(); - const classifier = getClassifier(); - + public static computeLink(position: IBufferCellPosition, regex: RegExp, buffer: IBuffer, handler: (event: MouseEvent, uri: string) => void): ILink | undefined { + const rex = new RegExp(regex.source, (regex.flags || '') + 'g'); const bufferLine = buffer.getLine(position.y - 1); if (!bufferLine) { @@ -219,111 +25,13 @@ export class LinkComputer { } const line = bufferLine.translateToString(); - const len = line.length; - const i = position.y; + let match; + let stringIndex = -1; - let linkBeginIndex = position.x - 1; - let state = State.START; - let hasOpenParens = false; - let hasOpenSquareBracket = false; - let hasOpenCurlyBracket = false; + // while ((match = rex.exec(line)) !== null) { + // const uri = match[1]; + // } - while (linkBeginIndex >= 0) { - let j = linkBeginIndex; - while (j < len) { - const linkBeginChCode = line.charCodeAt(j); - const chCode = line.charCodeAt(j); - - if (state === State.ACCEPT) { - let chClass: CharacterClass; - switch (chCode) { - case CharCode.OPEN_PAREN: - hasOpenParens = true; - chClass = CharacterClass.NONE; - break; - case CharCode.CLOSE_PAREN: - chClass = (hasOpenParens ? CharacterClass.NONE : CharacterClass.FORCE_TERMINATION); - break; - case CharCode.OPEN_SQUARE_BRACKET: - hasOpenSquareBracket = true; - chClass = CharacterClass.NONE; - break; - case CharCode.CLOSE_SQUARE_BRACKET: - chClass = (hasOpenSquareBracket ? CharacterClass.NONE : CharacterClass.FORCE_TERMINATION); - break; - case CharCode.OPEN_CURLY_BRACE: - hasOpenCurlyBracket = true; - chClass = CharacterClass.NONE; - break; - case CharCode.CLOSE_CURLY_BRACE: - chClass = (hasOpenCurlyBracket ? CharacterClass.NONE : CharacterClass.FORCE_TERMINATION); - break; - /* The following three rules make it that ' or " or ` are allowed inside links if the link began with a different one */ - case CharCode.SINGLE_QUOTE: - chClass = (linkBeginChCode === CharCode.DOUBLE_QUOTE || linkBeginChCode === CharCode.BACK_TICK) ? CharacterClass.NONE : CharacterClass.FORCE_TERMINATION; - break; - case CharCode.DOUBLE_QUOTE: - chClass = (linkBeginChCode === CharCode.SINGLE_QUOTE || linkBeginChCode === CharCode.BACK_TICK) ? CharacterClass.NONE : CharacterClass.FORCE_TERMINATION; - break; - case CharCode.BACK_TICK: - chClass = (linkBeginChCode === CharCode.SINGLE_QUOTE || linkBeginChCode === CharCode.DOUBLE_QUOTE) ? CharacterClass.NONE : CharacterClass.FORCE_TERMINATION; - break; - case CharCode.ASTERISK: - // `*` terminates a link if the link began with `*` - chClass = (linkBeginChCode === CharCode.ASTERISK) ? CharacterClass.FORCE_TERMINATION : CharacterClass.NONE; - break; - default: - chClass = classifier.get(chCode); - } - - // Check if character terminates link - if (chClass === CharacterClass.FORCE_TERMINATION) { - return LinkComputer._createLink(classifier, line, i, linkBeginIndex, j, handler); - } - } else if (state === State.END) { - - let chClass: CharacterClass; - if (chCode === CharCode.OPEN_SQUARE_BRACKET) { - // Allow for the authority part to contain ipv6 addresses which contain [ and ] - hasOpenSquareBracket = true; - chClass = CharacterClass.NONE; - } else { - chClass = classifier.get(chCode); - } - - // Check if character terminates link - if (chClass === CharacterClass.FORCE_TERMINATION) { - return; - } - - state = State.ACCEPT; - } else { - state = stateMachine.nextState(state, chCode); - if (state === State.INVALID) { - // Two spaces in a row, return - if (chCode === CharCode.SPACE && j > 0 && line.charCodeAt(j - 1) === CharCode.SPACE) { - return; - } - - // Reset state machine - state = State.START; - hasOpenParens = false; - hasOpenSquareBracket = false; - hasOpenCurlyBracket = false; - - // Move to the left - linkBeginIndex--; - break; - } - } - - j++; - } - - if (state === State.ACCEPT) { - return LinkComputer._createLink(classifier, line, i, linkBeginIndex, len, handler); - } - } } } diff --git a/addons/xterm-addon-web-links/src/WebLinksAddon.ts b/addons/xterm-addon-web-links/src/WebLinksAddon.ts index bc6becc89..e09e6225c 100644 --- a/addons/xterm-addon-web-links/src/WebLinksAddon.ts +++ b/addons/xterm-addon-web-links/src/WebLinksAddon.ts @@ -44,9 +44,13 @@ export class WebLinksAddon implements ITerminalAddon { public activate(terminal: Terminal): void { this._terminal = terminal; - this._linkProvider = new WebLinkProvider(this._terminal, this._handler); - // this._linkMatcherId = this._terminal.registerLinkMatcher(strictUrlRegex, this._handler, this._options); + + // if ('registerLinkProvider' in this._terminal) { + this._linkProvider = new WebLinkProvider(this._terminal, strictUrlRegex, this._handler); this._terminal.registerLinkProvider(this._linkProvider); + // } else { + // this._linkMatcherId = this._terminal.registerLinkMatcher(strictUrlRegex, this._handler, this._options); + // } } public dispose(): void { diff --git a/addons/xterm-addon-web-links/src/charCode.ts b/addons/xterm-addon-web-links/src/charCode.ts deleted file mode 100644 index bc72c3733..000000000 --- a/addons/xterm-addon-web-links/src/charCode.ts +++ /dev/null @@ -1,237 +0,0 @@ -// Names from https://blog.codinghorror.com/ascii-pronunciation-rules-for-programmers/ - -/** - * An inlined enum containing useful character codes (to be used with String.charCodeAt). - * Please leave the const keyword such that it gets inlined when compiled to JavaScript! - */ -export const enum CharCode { - NULL = 0, - /** - * The `\b` character. - */ - BACKSPACE = 8, - /** - * The `\t` character. - */ - TAB = 9, - /** - * The `\n` character. - */ - LINE_FEED = 10, - /** - * The `\r` character. - */ - CARRIAGE_RETURN = 13, - SPACE = 32, - /** - * The `!` character. - */ - EXCLAMATION_MARK = 33, - /** - * The `"` character. - */ - DOUBLE_QUOTE = 34, - /** - * The `#` character. - */ - HASH = 35, - /** - * The `$` character. - */ - DOLLAR_SIGN = 36, - /** - * The `%` character. - */ - PERCENT_SIGN = 37, - /** - * The `&` character. - */ - AMPERSAND = 38, - /** - * The `'` character. - */ - SINGLE_QUOTE = 39, - /** - * The `(` character. - */ - OPEN_PAREN = 40, - /** - * The `)` character. - */ - CLOSE_PAREN = 41, - /** - * The `*` character. - */ - ASTERISK = 42, - /** - * The `+` character. - */ - PLUS = 43, - /** - * The `,` character. - */ - COMMA = 44, - /** - * The `-` character. - */ - DASH = 45, - /** - * The `.` character. - */ - PERIOD = 46, - /** - * The `/` character. - */ - SLASH = 47, - - DIGIT_0 = 48, - DIGIT_1 = 49, - DIGIT_2 = 50, - DIGIT_3 = 51, - DIGIT_4 = 52, - DIGIT_5 = 53, - DIGIT_6 = 54, - DIGIT_7 = 55, - DIGIT_8 = 56, - DIGIT_9 = 57, - - /** - * The `:` character. - */ - COLON = 58, - /** - * The `;` character. - */ - SEMICOLON = 59, - /** - * The `<` character. - */ - LESS_THAN = 60, - /** - * The `=` character. - */ - EQUALS = 61, - /** - * The `>` character. - */ - GREATER_THAN = 62, - /** - * The `?` character. - */ - QUESTION_MARK = 63, - /** - * The `@` character. - */ - AT_SIGN = 64, - - A = 65, - B = 66, - C = 67, - D = 68, - E = 69, - F = 70, - G = 71, - H = 72, - I = 73, - J = 74, - K = 75, - L = 76, - M = 77, - N = 78, - O = 79, - P = 80, - Q = 81, - R = 82, - S = 83, - T = 84, - U = 85, - V = 86, - W = 87, - X = 88, - Y = 89, - Z = 90, - - /** - * The `[` character. - */ - OPEN_SQUARE_BRACKET = 91, - /** - * The `\` character. - */ - BACK_SLASH = 92, - /** - * The `]` character. - */ - CLOSE_SQUARE_BRACKET = 93, - /** - * The `^` character. - */ - CARET = 94, - /** - * The `_` character. - */ - UNDERLINE = 95, - /** - * The ``(`)`` character. - */ - BACK_TICK = 96, - - a = 97, - b = 98, - c = 99, - d = 100, - e = 101, - f = 102, - g = 103, - h = 104, - i = 105, - j = 106, - k = 107, - l = 108, - m = 109, - n = 110, - o = 111, - p = 112, - q = 113, - r = 114, - s = 115, - t = 116, - u = 117, - v = 118, - w = 119, - x = 120, - y = 121, - z = 122, - - /** - * The `{` character. - */ - OPEN_CURLY_BRACE = 123, - /** - * The `|` character. - */ - PIPE = 124, - /** - * The `}` character. - */ - CLOSE_CURLY_BRACE = 125, - /** - * The `~` character. - */ - TILDE = 126, - - /** - * Unicode Character 'LINE SEPARATOR' (U+2028) - * http://www.fileformat.info/info/unicode/char/2028/index.htm - */ - LINE_SEPARATOR_2028 = 8232, - - U_OVERLINE = 0x203E, // Unicode Character 'OVERLINE' - - /** - * UTF-8 BOM - * Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF) - * http://www.fileformat.info/info/unicode/char/feff/index.htm - */ - UTF8_BOM = 65279 -} diff --git a/addons/xterm-addon-web-links/src/characterClassifier.ts b/addons/xterm-addon-web-links/src/characterClassifier.ts deleted file mode 100644 index 2ca6f2aa9..000000000 --- a/addons/xterm-addon-web-links/src/characterClassifier.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * A fast character classifier that uses a compact array for ASCII values. - */ -export class CharacterClassifier { - /** - * Maintain a compact (fully initialized ASCII map for quickly classifying ASCII characters - used more often in code). - */ - private _asciiMap: Uint8Array; - - /** - * The entire map (sparse array). - */ - private _map: Map; - - private _defaultValue: number; - - constructor(newDefaultValue: T) { - const defaultValue = toUint8(newDefaultValue); - - this._defaultValue = defaultValue; - this._asciiMap = CharacterClassifier._createAsciiMap(defaultValue); - this._map = new Map(); - } - - private static _createAsciiMap(defaultValue: number): Uint8Array { - const asciiMap: Uint8Array = new Uint8Array(256); - for (let i = 0; i < 256; i++) { - asciiMap[i] = defaultValue; - } - return asciiMap; - } - - public set(charCode: number, newValue: T): void { - const value = toUint8(newValue); - - if (charCode >= 0 && charCode < 256) { - this._asciiMap[charCode] = value; - } else { - this._map.set(charCode, value); - } - } - - public get(charCode: number): T { - if (charCode >= 0 && charCode < 256) { - return this._asciiMap[charCode]; - } - return (this._map.get(charCode) || this._defaultValue); - } -} - -const enum Boolean { - FALSE = 0, - TRUE = 1 -} - -export class CharacterSet { - - private readonly _actual: CharacterClassifier; - - constructor() { - this._actual = new CharacterClassifier(Boolean.FALSE); - } - - public add(charCode: number): void { - this._actual.set(charCode, Boolean.TRUE); - } - - public has(charCode: number): boolean { - return (this._actual.get(charCode) === Boolean.TRUE); - } -} - -export const enum Constants { - /** - * MAX SMI (SMall Integer) as defined in v8. - * one bit is lost for boxing/unboxing flag. - * one bit is lost for sign flag. - * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values - */ - MAX_SAFE_SMALL_INTEGER = 1 << 30, - - /** - * MIN SMI (SMall Integer) as defined in v8. - * one bit is lost for boxing/unboxing flag. - * one bit is lost for sign flag. - * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values - */ - MIN_SAFE_SMALL_INTEGER = -(1 << 30), - - /** - * Max unsigned integer that fits on 8 bits. - */ - MAX_UINT_8 = 255, // 2^8 - 1 - - /** - * Max unsigned integer that fits on 16 bits. - */ - MAX_UINT_16 = 65535, // 2^16 - 1 - - /** - * Max unsigned integer that fits on 32 bits. - */ - MAX_UINT_32 = 4294967295, // 2^32 - 1 - - UNICODE_SUPPLEMENTARY_PLANE_BEGIN = 0x010000 -} - -export function toUint8(v: number): number { - if (v < 0) { - return 0; - } - if (v > Constants.MAX_UINT_8) { - return Constants.MAX_UINT_8; - } - return v | 0; -} diff --git a/src/browser/Linkifier2.ts b/src/browser/Linkifier2.ts index 8f794fc2b..aeb3cf6d9 100644 --- a/src/browser/Linkifier2.ts +++ b/src/browser/Linkifier2.ts @@ -43,7 +43,11 @@ export class Linkifier2 implements ILinkifier2 { } private _onMouseMove(event: MouseEvent): void { - const position = this._positionFromMouseEvent(event); + if (!this._element || !this._mouseService) { + return; + } + + const position = this._positionFromMouseEvent(event, this._element, this._mouseService); if (!position) { return; @@ -53,32 +57,34 @@ export class Linkifier2 implements ILinkifier2 { // Check the cache for a link and determine if we need to show or hide tooltip let foundLink = false; - this._linkCache.forEach((cachedLink, i) => { - const isInPosition = this._linkAtPosition(cachedLink.link, position); - const range = cachedLink.link.range; - if (isInPosition && !cachedLink.mouseOver) { + for (let i = 0; i < this._linkCache.length; i++) { + const cachedLink = this._linkCache[i].link; + const isInPosition = this._linkAtPosition(cachedLink, position); + const range = cachedLink.range; + + if (isInPosition && !this._linkCache[i].mouseOver) { // Show the tooltip this._onShowTooltip.fire(this._createLinkHoverEvent(range.start.x - 1, range.start.y - scrollOffset - 1, range.end.x - 1, range.end.y - scrollOffset - 1, undefined)); - this._element!.classList.add('xterm-cursor-pointer'); + this._element.classList.add('xterm-cursor-pointer'); - if (cachedLink.link.showTooltip) { - cachedLink.link.showTooltip(event, cachedLink.link.url); + if (cachedLink.showTooltip) { + cachedLink.showTooltip(event, cachedLink.url); } this._linkCache[i].mouseOver = true; foundLink = true; - } else if (!isInPosition && cachedLink.mouseOver) { + } else if (!isInPosition && this._linkCache[i].mouseOver) { // Hide the tooltip this._onHideTooltip.fire(this._createLinkHoverEvent(range.start.x - 1, range.start.y - scrollOffset - 1, range.end.x - 1, range.end.y - scrollOffset - 1, undefined)); - this._element!.classList.remove('xterm-cursor-pointer'); + this._element.classList.remove('xterm-cursor-pointer'); - if (cachedLink.link.hideTooltip) { - cachedLink.link.hideTooltip(event, cachedLink.link.url); + if (cachedLink.hideTooltip) { + cachedLink.hideTooltip(event, cachedLink.url); } this._linkCache[i].mouseOver = false; } - }); + } if (foundLink) { return; @@ -91,7 +97,11 @@ export class Linkifier2 implements ILinkifier2 { } private _onMouseDown(event: MouseEvent): void { - const position = this._positionFromMouseEvent(event); + if (!this._element || !this._mouseService) { + return; + } + + const position = this._positionFromMouseEvent(event, this._element, this._mouseService); if (!position) { return; @@ -106,7 +116,7 @@ export class Linkifier2 implements ILinkifier2 { private _handleNewLink(link: ILink | undefined): void { if (link && !this._linkCache.find(cachedLink => cachedLink.link = link)) { - this._linkCache.push({ link: link, mouseOver: false }); + this._linkCache.push({ link, mouseOver: false }); } } @@ -116,22 +126,18 @@ export class Linkifier2 implements ILinkifier2 { * @param position */ private _linkAtPosition(link: ILink, position: IBufferCellPosition): boolean { - return link.range.start.x <= position.x - && link.range.start.y <= position.y - && link.range.end.x >= position.x - && link.range.end.y >= position.y; + return link.range.start.x <= position.x && + link.range.start.y <= position.y && + link.range.end.x >= position.x && + link.range.end.y >= position.y; } /** * Get the buffer position from a mouse event * @param event */ - private _positionFromMouseEvent(event: MouseEvent): IBufferCellPosition | undefined { - if (!this._element) { - return; - } - - const coords = this._mouseService!.getCoords(event, this._element, this._bufferService.cols, this._bufferService.rows); + private _positionFromMouseEvent(event: MouseEvent, element: HTMLElement, mouseService: IMouseService): IBufferCellPosition | undefined { + const coords = mouseService.getCoords(event, element, this._bufferService.cols, this._bufferService.rows); if (!coords) { return; }