From 1877e7019d479724ae45568d0b99c080a3bd949c Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Mon, 6 Nov 2017 23:20:57 +0000 Subject: [PATCH 01/11] Implement #778 --- src/Interfaces.ts | 3 +++ src/SelectionManager.ts | 26 +++++++++++++++++++++----- src/Terminal.ts | 7 ++++--- src/handlers/Clipboard.ts | 6 +++++- typings/xterm.d.ts | 4 ++-- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/Interfaces.ts b/src/Interfaces.ts index e98385c11..ea1d00a0e 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -149,6 +149,7 @@ export interface ITerminalOptions { termName?: string; theme?: ITheme; useFlowControl?: boolean; + rightClickSelectsWord?: boolean; } export interface IBuffer { @@ -194,11 +195,13 @@ export interface ISelectionManager { selectionText: string; selectionStart: [number, number]; selectionEnd: [number, number]; + hasSelection: boolean; disable(): void; enable(): void; setBuffer(buffer: IBuffer): void; setSelection(row: number, col: number, length: number): void; + selectWordAtCursor(event: MouseEvent): void; } export interface ICompositionHelper { diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index bf5144cba..87b71a129 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -249,6 +249,18 @@ export class SelectionManager extends EventEmitter implements ISelectionManager this.emit('refresh', { start: this._model.finalSelectionStart, end: this._model.finalSelectionEnd }); } + /** + * Selects word at the current mouse event coordenates. + * @param event The mouse event. + */ + public selectWordAtCursor(event: MouseEvent): void { + const coords = this._getMouseBufferCoords(event); + if (coords) { + this._selectWordAt(coords, false); + this.refresh(true); + } + } + /** * Selects all text within the terminal. */ @@ -439,7 +451,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager const coords = this._getMouseBufferCoords(event); if (coords) { this._activeSelectionMode = SelectionMode.WORD; - this._selectWordAt(coords); + this._selectWordAt(coords, true); } } @@ -578,7 +590,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager * Gets positional information for the word at the coordinated specified. * @param coords The coordinates to get the word at. */ - private _getWordAt(coords: [number, number]): IWordPosition { + private _getWordAt(coords: [number, number], selectWhiteSpace: boolean): IWordPosition { const bufferLine = this._buffer.lines.get(coords[1]); if (!bufferLine) { return null; @@ -684,15 +696,19 @@ export class SelectionManager extends EventEmitter implements ISelectionManager - leftLongCharOffset // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) - rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis) + if (!selectWhiteSpace && line.slice(startIndex, endIndex).trim() === '') + return null; + return { start, length }; } /** * Selects the word at the coordinates specified. * @param coords The coordinates to get the word at. + * @param selectWhiteSpace If whitespace should be selected */ - protected _selectWordAt(coords: [number, number]): void { - const wordPosition = this._getWordAt(coords); + protected _selectWordAt(coords: [number, number], selectWhiteSpace: boolean): void { + const wordPosition = this._getWordAt(coords, selectWhiteSpace); if (wordPosition) { this._model.selectionStart = [wordPosition.start, coords[1]]; this._model.selectionStartLength = wordPosition.length; @@ -704,7 +720,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager * @param coords The coordinates to get the word at. */ private _selectToWordAt(coords: [number, number]): void { - const wordPosition = this._getWordAt(coords); + const wordPosition = this._getWordAt(coords, true); if (wordPosition) { this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : (wordPosition.start + wordPosition.length), coords[1]]; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 5ac8a1a0c..7dbdeb760 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -89,7 +89,8 @@ const DEFAULT_OPTIONS: ITerminalOptions = { disableStdin: false, useFlowControl: false, tabStopWidth: 8, - theme: null + theme: null, + rightClickSelectsWord: Browser.isMac // programFeatures: false, // focusKeys: false, }; @@ -492,12 +493,12 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT // Firefox doesn't appear to fire the contextmenu event on right click on(this.element, 'mousedown', (event: MouseEvent) => { if (event.button === 2) { - rightClickHandler(event, this.textarea, this.selectionManager); + rightClickHandler(event, this.textarea, this.selectionManager, this.options.rightClickSelectsWord); } }); } else { on(this.element, 'contextmenu', (event: MouseEvent) => { - rightClickHandler(event, this.textarea, this.selectionManager); + rightClickHandler(event, this.textarea, this.selectionManager, this.options.rightClickSelectsWord); }); } diff --git a/src/handlers/Clipboard.ts b/src/handlers/Clipboard.ts index a97353520..86a604f28 100644 --- a/src/handlers/Clipboard.ts +++ b/src/handlers/Clipboard.ts @@ -103,10 +103,14 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA * @param ev The original right click event to be handled. * @param textarea The terminal's textarea. * @param selectionManager The terminal's selection manager. + * @param shouldSelectWord If true and there is no selection the current word will be selected */ -export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager): void { +export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager, shouldSelectWord: boolean): void { moveTextAreaUnderMouseCursor(ev, textarea); + if (shouldSelectWord && !selectionManager.hasSelection) + selectionManager.selectWordAtCursor(ev); + // Get textarea ready to copy from the context menu textarea.value = selectionManager.selectionText; textarea.select(); diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 9ab61ce67..a5137797d 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -409,7 +409,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' | 'rightClickSelectsWord'): boolean; /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -459,7 +459,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' | 'rightClickSelectsWord', value: boolean): void; /** * Sets an option on the terminal. * @param key The option key. From 2865d935b646a43b2dbaa7f8e5f176b4d2f90518 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Tue, 7 Nov 2017 21:21:44 +0000 Subject: [PATCH 02/11] Adress feedback --- fixtures/typings-test/typings-test.ts | 2 ++ src/SelectionManager.test.ts | 2 +- src/SelectionManager.ts | 13 +++++++------ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/fixtures/typings-test/typings-test.ts b/fixtures/typings-test/typings-test.ts index 9d73535ec..d61b22f20 100644 --- a/fixtures/typings-test/typings-test.ts +++ b/fixtures/typings-test/typings-test.ts @@ -143,6 +143,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('rightClickSelectsWord'); } { const t: Terminal = new Terminal(); @@ -177,6 +178,7 @@ namespace methods_core { t.setOption('lineHeight', 1); t.setOption('fontFamily', 'foo'); t.setOption('theme', {background: '#ff0000'}); + t.setOption('rightClickSelectsWord', false); } } namespace scrolling { diff --git a/src/SelectionManager.test.ts b/src/SelectionManager.test.ts index 8c6ea3e18..e229889ee 100644 --- a/src/SelectionManager.test.ts +++ b/src/SelectionManager.test.ts @@ -26,7 +26,7 @@ class TestSelectionManager extends SelectionManager { public get model(): SelectionModel { return this._model; } public selectLineAt(line: number): void { this._selectLineAt(line); } - public selectWordAt(coords: [number, number]): void { this._selectWordAt(coords); } + public selectWordAt(coords: [number, number]): void { this._selectWordAt(coords, true); } // Disable DOM interaction public enable(): void {} diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 87b71a129..1fccc8b17 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -250,7 +250,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager } /** - * Selects word at the current mouse event coordenates. + * Selects word at the current mouse event coordinates. * @param event The mouse event. */ public selectWordAtCursor(event: MouseEvent): void { @@ -590,7 +590,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager * Gets positional information for the word at the coordinated specified. * @param coords The coordinates to get the word at. */ - private _getWordAt(coords: [number, number], selectWhiteSpace: boolean): IWordPosition { + private _getWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean): IWordPosition { const bufferLine = this._buffer.lines.get(coords[1]); if (!bufferLine) { return null; @@ -696,8 +696,9 @@ export class SelectionManager extends EventEmitter implements ISelectionManager - leftLongCharOffset // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) - rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis) - if (!selectWhiteSpace && line.slice(startIndex, endIndex).trim() === '') + if (!allowWhitespaceOnlySelection && line.slice(startIndex, endIndex).trim() === '') { return null; + } return { start, length }; } @@ -705,10 +706,10 @@ export class SelectionManager extends EventEmitter implements ISelectionManager /** * Selects the word at the coordinates specified. * @param coords The coordinates to get the word at. - * @param selectWhiteSpace If whitespace should be selected + * @param allowWhitespaceOnlySelection If whitespace should be selected */ - protected _selectWordAt(coords: [number, number], selectWhiteSpace: boolean): void { - const wordPosition = this._getWordAt(coords, selectWhiteSpace); + protected _selectWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean): void { + const wordPosition = this._getWordAt(coords, allowWhitespaceOnlySelection); if (wordPosition) { this._model.selectionStart = [wordPosition.start, coords[1]]; this._model.selectionStartLength = wordPosition.length; From 18b86a1cbc4cb1d99f67599992985b733fa6e1a8 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Wed, 8 Nov 2017 23:27:00 +0000 Subject: [PATCH 03/11] Do the selection even if there is already a selection --- src/Interfaces.ts | 1 + src/SelectionManager.ts | 26 +++++++++++++++++++++++--- src/handlers/Clipboard.ts | 2 +- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Interfaces.ts b/src/Interfaces.ts index ea1d00a0e..69e7fcb2b 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -201,6 +201,7 @@ export interface ISelectionManager { enable(): void; setBuffer(buffer: IBuffer): void; setSelection(row: number, col: number, length: number): void; + isClickInSelection(event: MouseEvent): boolean; selectWordAtCursor(event: MouseEvent): void; } diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 1fccc8b17..3bf366ce1 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -249,6 +249,22 @@ export class SelectionManager extends EventEmitter implements ISelectionManager this.emit('refresh', { start: this._model.finalSelectionStart, end: this._model.finalSelectionEnd }); } + /** + * Checks if the current click was inside the current selection + * @param event The mouse event + */ + public isClickInSelection(event: MouseEvent): boolean { + const coords = this._getMouseBufferCoords(event); + const start = this._model.finalSelectionStart; + const end = this._model.finalSelectionEnd; + + if (!start || !end) { + return false; + } + + return (start[1] < coords[1] && end[1] > coords[1]) || (start[1] === coords[1] && coords[0] > start[0]) || (end[1] === coords[1] && coords[0] < end[0]); + } + /** * Selects word at the current mouse event coordinates. * @param event The mouse event. @@ -256,10 +272,14 @@ export class SelectionManager extends EventEmitter implements ISelectionManager public selectWordAtCursor(event: MouseEvent): void { const coords = this._getMouseBufferCoords(event); if (coords) { - this._selectWordAt(coords, false); - this.refresh(true); + const wordPosition = this._getWordAt(coords, true); + if (wordPosition) { + this._model.selectionStart = [wordPosition.start, coords[1]]; + this._model.selectionStartLength = wordPosition.length; + this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : (wordPosition.start + wordPosition.length), coords[1]]; + this.refresh(true); + } } - } /** * Selects all text within the terminal. diff --git a/src/handlers/Clipboard.ts b/src/handlers/Clipboard.ts index 86a604f28..6814d269c 100644 --- a/src/handlers/Clipboard.ts +++ b/src/handlers/Clipboard.ts @@ -108,7 +108,7 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager, shouldSelectWord: boolean): void { moveTextAreaUnderMouseCursor(ev, textarea); - if (shouldSelectWord && !selectionManager.hasSelection) + if (shouldSelectWord && (!selectionManager.hasSelection || !selectionManager.isClickInSelection(ev))) selectionManager.selectWordAtCursor(ev); // Get textarea ready to copy from the context menu From 6822070222945574d4b017b4bb80b725e133a255 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Sun, 12 Nov 2017 23:15:09 +0000 Subject: [PATCH 04/11] Fix typo + Cleanup logic --- src/Interfaces.ts | 1 - src/SelectionManager.ts | 1 + src/handlers/Clipboard.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 69e7fcb2b..39b81baf9 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -195,7 +195,6 @@ export interface ISelectionManager { selectionText: string; selectionStart: [number, number]; selectionEnd: [number, number]; - hasSelection: boolean; disable(): void; enable(): void; diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 3bf366ce1..7e04119e2 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -280,6 +280,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager this.refresh(true); } } + } /** * Selects all text within the terminal. diff --git a/src/handlers/Clipboard.ts b/src/handlers/Clipboard.ts index 6814d269c..f110ed1fa 100644 --- a/src/handlers/Clipboard.ts +++ b/src/handlers/Clipboard.ts @@ -108,7 +108,7 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager, shouldSelectWord: boolean): void { moveTextAreaUnderMouseCursor(ev, textarea); - if (shouldSelectWord && (!selectionManager.hasSelection || !selectionManager.isClickInSelection(ev))) + if (shouldSelectWord && !selectionManager.isClickInSelection(ev)) selectionManager.selectWordAtCursor(ev); // Get textarea ready to copy from the context menu From 9c62a817d0b8a6c6623a24c45cd383bc496df8d6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 25 Jan 2018 12:01:51 -0800 Subject: [PATCH 05/11] Fix build after merge --- src/Interfaces.ts | 378 ---------------------------------------------- src/Types.ts | 2 + 2 files changed, 2 insertions(+), 378 deletions(-) delete mode 100644 src/Interfaces.ts diff --git a/src/Interfaces.ts b/src/Interfaces.ts deleted file mode 100644 index 2524d2349..000000000 --- a/src/Interfaces.ts +++ /dev/null @@ -1,378 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { ICharset, ILinkMatcherOptions } from './Interfaces'; -import { LinkMatcherHandler, LinkMatcherValidationCallback, LineData } from './Types'; -import { IColorSet, IRenderer } from './renderer/Interfaces'; -import { IMouseZoneManager } from './input/Interfaces'; - -export interface IBrowser { - isNode: boolean; - userAgent: string; - platform: string; - isFirefox: boolean; - isMSIE: boolean; - isMac: boolean; - isIpad: boolean; - isIphone: boolean; - isMSWindows: boolean; -} - -export interface IBufferAccessor { - buffer: IBuffer; -} - -export interface IElementAccessor { - element: HTMLElement; -} - -export interface ILinkifierAccessor { - linkifier: ILinkifier; -} - -export interface ITerminal extends ILinkifierAccessor, IBufferAccessor, IElementAccessor, IEventEmitter { - selectionManager: ISelectionManager; - charMeasure: ICharMeasure; - textarea: HTMLTextAreaElement; - renderer: IRenderer; - rows: number; - cols: number; - browser: IBrowser; - writeBuffer: string[]; - cursorHidden: boolean; - cursorState: number; - defAttr: number; - options: ITerminalOptions; - buffers: IBufferSet; - isFocused: boolean; - mouseHelper: IMouseHelper; - bracketedPasteMode: boolean; - - /** - * Emit the 'data' event and populate the given data. - * @param data The data to populate in the event. - */ - handler(data: string): void; - scrollLines(disp: number, suppressScrollEvent?: boolean): void; - cancel(ev: Event, force?: boolean): boolean | void; - log(text: string): void; - reset(): void; - showCursor(): void; - blankLine(cur?: boolean, isWrapped?: boolean, cols?: number): LineData; - refresh(start: number, end: number): void; -} - -/** - * This interface encapsulates everything needed from the Terminal by the - * InputHandler. This cleanly separates the large amount of methods needed by - * InputHandler cleanly from the ITerminal interface. - */ -export interface IInputHandlingTerminal extends IEventEmitter { - element: HTMLElement; - options: ITerminalOptions; - cols: number; - rows: number; - charset: ICharset; - gcharset: number; - glevel: number; - charsets: ICharset[]; - applicationKeypad: boolean; - applicationCursor: boolean; - originMode: boolean; - insertMode: boolean; - wraparoundMode: boolean; - bracketedPasteMode: boolean; - defAttr: number; - curAttr: number; - prefix: string; - savedCols: number; - x10Mouse: boolean; - vt200Mouse: boolean; - normalMouse: boolean; - mouseEvents: boolean; - sendFocus: boolean; - utfMouse: boolean; - sgrMouse: boolean; - urxvtMouse: boolean; - cursorHidden: boolean; - - buffers: IBufferSet; - buffer: IBuffer; - viewport: IViewport; - selectionManager: ISelectionManager; - - bell(): void; - focus(): void; - convertEol: boolean; - updateRange(y: number): void; - scroll(isWrapped?: boolean): void; - setgLevel(g: number): void; - eraseAttr(): number; - eraseRight(x: number, y: number): void; - eraseLine(y: number): void; - eraseLeft(x: number, y: number): void; - blankLine(cur?: boolean, isWrapped?: boolean): LineData; - is(term: string): boolean; - send(data: string): void; - setgCharset(g: number, charset: ICharset): void; - resize(x: number, y: number): void; - log(text: string, data?: any): void; - reset(): void; - showCursor(): void; - refresh(start: number, end: number): void; - matchColor(r1: number, g1: number, b1: number): number; - error(text: string, data?: any): void; - setOption(key: string, value: any): void; -} - -export interface ITerminalOptions { - bellSound?: string; - bellStyle?: string; - cancelEvents?: boolean; - cols?: number; - convertEol?: boolean; - cursorBlink?: boolean; - cursorStyle?: string; - debug?: boolean; - disableStdin?: boolean; - enableBold?: boolean; - fontSize?: number; - fontFamily?: string; - handler?: (data: string) => void; - letterSpacing?: number; - lineHeight?: number; - rows?: number; - screenKeys?: boolean; - scrollback?: number; - tabStopWidth?: number; - termName?: string; - theme?: ITheme; - useFlowControl?: boolean; - rightClickSelectsWord?: boolean; -} - -export interface IBuffer { - lines: ICircularList; - ydisp: number; - ybase: number; - y: number; - x: number; - tabs: any; - scrollBottom: number; - scrollTop: number; - savedY: number; - savedX: number; - isCursorInViewport: boolean; - translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; - nextStop(x?: number): number; - prevStop(x?: number): number; -} - -export interface IBufferSet { - alt: IBuffer; - normal: IBuffer; - active: IBuffer; - - activateNormalBuffer(): void; - activateAltBuffer(): void; -} - -export interface IMouseHelper { - getCoords(event: {pageX: number, pageY: number}, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number]; - getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number }; -} - -export interface IViewport { - syncScrollArea(): void; - onWheel(ev: WheelEvent): void; - onTouchStart(ev: TouchEvent): void; - onTouchMove(ev: TouchEvent): void; - onThemeChanged(colors: IColorSet): void; -} - -export interface ISelectionManager { - selectionText: string; - selectionStart: [number, number]; - selectionEnd: [number, number]; - - disable(): void; - enable(): void; - setBuffer(buffer: IBuffer): void; - setSelection(row: number, col: number, length: number): void; - isClickInSelection(event: MouseEvent): boolean; - selectWordAtCursor(event: MouseEvent): void; -} - -export interface ICompositionHelper { - compositionstart(): void; - compositionupdate(ev: CompositionEvent): void; - compositionend(): void; - updateCompositionElements(dontRecurse?: boolean): void; - keydown(ev: KeyboardEvent): boolean; -} - -export interface ICharMeasure { - width: number; - height: number; - measure(options: ITerminalOptions): void; -} - -export interface ILinkifier extends IEventEmitter { - attachToDom(mouseZoneManager: IMouseZoneManager): void; - linkifyRows(start: number, end: number): void; - setHypertextLinkHandler(handler: LinkMatcherHandler): void; - setHypertextValidationCallback(callback: LinkMatcherValidationCallback): void; - registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number; - deregisterLinkMatcher(matcherId: number): boolean; -} - -export interface ICircularList extends IEventEmitter { - length: number; - maxLength: number; - forEach: (callbackfn: (value: T, index: number) => void) => void; - - get(index: number): T; - set(index: number, value: T): void; - push(value: T): void; - pop(): T; - splice(start: number, deleteCount: number, ...items: T[]): void; - trimStart(count: number): void; - shiftElements(start: number, count: number, offset: number): void; -} - -export interface IEventEmitter { - on(type: string, listener: IListenerType): void; - off(type: string, listener: IListenerType): void; - emit(type: string, data?: any): void; -} - -export interface IListenerType { - (data?: any): void; - listener?: (data?: any) => void; -} - -export interface ILinkMatcherOptions { - /** - * The index of the link from the regex.match(text) call. This defaults to 0 - * (for regular expressions without capture groups). - */ - matchIndex?: number; - /** - * A callback that validates an individual link, returning true if valid and - * false if invalid. - */ - validationCallback?: LinkMatcherValidationCallback; - /** - * A callback that fires when the mouse hovers over a link. - */ - tooltipCallback?: LinkMatcherHandler; - /** - * A callback that fires when the mouse leaves a link that was hovered. - */ - leaveCallback?: () => void; - /** - * The priority of the link matcher, this defines the order in which the link - * matcher is evaluated relative to others, from highest to lowest. The - * default value is 0. - */ - priority?: number; -} - -/** - * Handles actions generated by the parser. - */ -export interface IInputHandler { - addChar(char: string, code: number): void; - - /** C0 BEL */ bell(): void; - /** C0 LF */ lineFeed(): void; - /** C0 CR */ carriageReturn(): void; - /** C0 BS */ backspace(): void; - /** C0 HT */ tab(): void; - /** C0 SO */ shiftOut(): void; - /** C0 SI */ shiftIn(): void; - - /** CSI @ */ insertChars(params?: number[]): void; - /** CSI A */ cursorUp(params?: number[]): void; - /** CSI B */ cursorDown(params?: number[]): void; - /** CSI C */ cursorForward(params?: number[]): void; - /** CSI D */ cursorBackward(params?: number[]): void; - /** CSI E */ cursorNextLine(params?: number[]): void; - /** CSI F */ cursorPrecedingLine(params?: number[]): void; - /** CSI G */ cursorCharAbsolute(params?: number[]): void; - /** CSI H */ cursorPosition(params?: number[]): void; - /** CSI I */ cursorForwardTab(params?: number[]): void; - /** CSI J */ eraseInDisplay(params?: number[]): void; - /** CSI K */ eraseInLine(params?: number[]): void; - /** CSI L */ insertLines(params?: number[]): void; - /** CSI M */ deleteLines(params?: number[]): void; - /** CSI P */ deleteChars(params?: number[]): void; - /** CSI S */ scrollUp(params?: number[]): void; - /** CSI T */ scrollDown(params?: number[]): void; - /** CSI X */ eraseChars(params?: number[]): void; - /** CSI Z */ cursorBackwardTab(params?: number[]): void; - /** CSI ` */ charPosAbsolute(params?: number[]): void; - /** CSI a */ HPositionRelative(params?: number[]): void; - /** CSI b */ repeatPrecedingCharacter(params?: number[]): void; - /** CSI c */ sendDeviceAttributes(params?: number[]): void; - /** CSI d */ linePosAbsolute(params?: number[]): void; - /** CSI e */ VPositionRelative(params?: number[]): void; - /** CSI f */ HVPosition(params?: number[]): void; - /** CSI g */ tabClear(params?: number[]): void; - /** CSI h */ setMode(params?: number[]): void; - /** CSI l */ resetMode(params?: number[]): void; - /** CSI m */ charAttributes(params?: number[]): void; - /** CSI n */ deviceStatus(params?: number[]): void; - /** CSI p */ softReset(params?: number[]): void; - /** CSI q */ setCursorStyle(params?: number[]): void; - /** CSI r */ setScrollRegion(params?: number[]): void; - /** CSI s */ saveCursor(params?: number[]): void; - /** CSI u */ restoreCursor(params?: number[]): void; -} - -export interface ITheme { - foreground?: string; - background?: string; - cursor?: string; - cursorAccent?: string; - selection?: string; - black?: string; - red?: string; - green?: string; - yellow?: string; - blue?: string; - magenta?: string; - cyan?: string; - white?: string; - brightBlack?: string; - brightRed?: string; - brightGreen?: string; - brightYellow?: string; - brightBlue?: string; - brightMagenta?: string; - brightCyan?: string; - brightWhite?: string; -} - -export interface ILinkMatcher { - id: number; - regex: RegExp; - handler: LinkMatcherHandler; - hoverTooltipCallback?: LinkMatcherHandler; - hoverLeaveCallback?: () => void; - matchIndex?: number; - validationCallback?: LinkMatcherValidationCallback; - priority?: number; -} - -export interface ICharset { - [key: string]: string; -} - -export interface ILinkHoverEvent { - x: number; - y: number; - length: number; -} diff --git a/src/Types.ts b/src/Types.ts index f3e9bb941..f78e52a5e 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -283,6 +283,8 @@ export interface ISelectionManager { disable(): void; enable(): void; setSelection(row: number, col: number, length: number): void; + isClickInSelection(event: MouseEvent): boolean; + selectWordAtCursor(event: MouseEvent): void; } export interface ILinkifier extends IEventEmitter { From e6c6e0766a7d5fd79f808b65814653c0ab709a21 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Thu, 25 Jan 2018 21:00:10 +0000 Subject: [PATCH 06/11] Fix usage of allowWhitespaceOnlySelection variable --- src/SelectionManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index a4a0b80c6..00d22fb6a 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -267,7 +267,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager public selectWordAtCursor(event: MouseEvent): void { const coords = this._getMouseBufferCoords(event); if (coords) { - const wordPosition = this._getWordAt(coords, true); + const wordPosition = this._getWordAt(coords, false); if (wordPosition) { this._model.selectionStart = [wordPosition.start, coords[1]]; this._model.selectionStartLength = wordPosition.length; From bbac1801e5328900328667e129f6fb8428aa93fe Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 28 Jan 2018 17:33:51 -0800 Subject: [PATCH 07/11] Small clean up --- src/SelectionManager.ts | 4 +++- src/handlers/Clipboard.ts | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 00d22fb6a..0d425a914 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -257,7 +257,9 @@ export class SelectionManager extends EventEmitter implements ISelectionManager return false; } - return (start[1] < coords[1] && end[1] > coords[1]) || (start[1] === coords[1] && coords[0] > start[0]) || (end[1] === coords[1] && coords[0] < end[0]); + return (start[1] < coords[1] && end[1] > coords[1]) || + (start[1] === coords[1] && coords[0] > start[0]) || + (end[1] === coords[1] && coords[0] < end[0]); } /** diff --git a/src/handlers/Clipboard.ts b/src/handlers/Clipboard.ts index 807516c78..7ac97714d 100644 --- a/src/handlers/Clipboard.ts +++ b/src/handlers/Clipboard.ts @@ -121,8 +121,9 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager, shouldSelectWord: boolean): void { moveTextAreaUnderMouseCursor(ev, textarea); - if (shouldSelectWord && !selectionManager.isClickInSelection(ev)) + if (shouldSelectWord && !selectionManager.isClickInSelection(ev)) { selectionManager.selectWordAtCursor(ev); + } // Get textarea ready to copy from the context menu textarea.value = selectionManager.selectionText; From 983af8fac218a082741368e591b57108bdfa5802 Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Wed, 31 Jan 2018 00:01:46 +0000 Subject: [PATCH 08/11] Use _selectWordA --- src/SelectionManager.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 0d425a914..c01d7b6d5 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -269,13 +269,8 @@ export class SelectionManager extends EventEmitter implements ISelectionManager public selectWordAtCursor(event: MouseEvent): void { const coords = this._getMouseBufferCoords(event); if (coords) { - const wordPosition = this._getWordAt(coords, false); - if (wordPosition) { - this._model.selectionStart = [wordPosition.start, coords[1]]; - this._model.selectionStartLength = wordPosition.length; - this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : (wordPosition.start + wordPosition.length), coords[1]]; - this.refresh(true); - } + this._selectWordAt(coords, false); + this.refresh(true); } } From 5550bfd90b9b9a96f8344a0d3cd89400a228b61d Mon Sep 17 00:00:00 2001 From: Bruno Ribeito Date: Thu, 1 Feb 2018 21:37:06 +0000 Subject: [PATCH 09/11] Fix is click in selection --- src/SelectionManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index c01d7b6d5..a639a75e4 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -258,8 +258,8 @@ export class SelectionManager extends EventEmitter implements ISelectionManager } return (start[1] < coords[1] && end[1] > coords[1]) || - (start[1] === coords[1] && coords[0] > start[0]) || - (end[1] === coords[1] && coords[0] < end[0]); + (start[1] === end[1] && start[1] === coords[1] && coords[0] > start[0] && coords[0] < end[0]) || + (start[1] < end[1] && end[1] === coords[1] && coords[0] < end[0]); } /** From 3c6cab1491c84e99b0c9de92d20ea0272c99141a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 1 Feb 2018 17:20:33 -0800 Subject: [PATCH 10/11] Clear selectionEnd when selecting word at cursor --- src/SelectionManager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index a639a75e4..f571b74cd 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -270,6 +270,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager const coords = this._getMouseBufferCoords(event); if (coords) { this._selectWordAt(coords, false); + this._model.selectionEnd = null; this.refresh(true); } } From cab097c09c60dfc342eaac2b8d66a3235f2edd96 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 1 Feb 2018 17:27:49 -0800 Subject: [PATCH 11/11] Clean up condition for readability --- src/SelectionManager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 33853036b..11856a922 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -266,9 +266,9 @@ export class SelectionManager extends EventEmitter implements ISelectionManager return false; } - return (start[1] < coords[1] && end[1] > coords[1]) || - (start[1] === end[1] && start[1] === coords[1] && coords[0] > start[0] && coords[0] < end[0]) || - (start[1] < end[1] && end[1] === coords[1] && coords[0] < end[0]); + return (coords[1] > start[1] && coords[1] < end[1]) || + (start[1] === end[1] && coords[1] === start[1] && coords[0] > start[0] && coords[0] < end[0]) || + (start[1] < end[1] && coords[1] === end[1] && coords[0] < end[0]); } /**