Merge remote-tracking branch 'ups/master' into pr/schrej/3205
This commit is contained in:
@@ -89,8 +89,8 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA
|
||||
export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, screenElement: HTMLElement, selectionService: ISelectionService, shouldSelectWord: boolean): void {
|
||||
moveTextAreaUnderMouseCursor(ev, textarea, screenElement);
|
||||
|
||||
if (shouldSelectWord && !selectionService.isClickInSelection(ev)) {
|
||||
selectionService.selectWordAtCursor(ev);
|
||||
if (shouldSelectWord) {
|
||||
selectionService.rightClickSelect(ev);
|
||||
}
|
||||
|
||||
// Get textarea ready to copy from the context menu
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent, ILinkDecorations } from 'browser/Types';
|
||||
import { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent, ILinkDecorations, ILinkWithState } from 'browser/Types';
|
||||
import { IDisposable } from 'common/Types';
|
||||
import { IMouseService, IRenderService } from './services/Services';
|
||||
import { IBufferService } from 'common/services/Services';
|
||||
@@ -11,21 +11,12 @@ import { EventEmitter, IEvent } from 'common/EventEmitter';
|
||||
import { Disposable, getDisposeArrayDisposable, disposeArray } from 'common/Lifecycle';
|
||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
|
||||
interface ILinkState {
|
||||
decorations: ILinkDecorations;
|
||||
isHovered: boolean;
|
||||
}
|
||||
|
||||
interface ILinkWithState {
|
||||
link: ILink;
|
||||
state?: ILinkState;
|
||||
}
|
||||
|
||||
export class Linkifier2 extends Disposable implements ILinkifier2 {
|
||||
private _element: HTMLElement | undefined;
|
||||
private _mouseService: IMouseService | undefined;
|
||||
private _renderService: IRenderService | undefined;
|
||||
private _linkProviders: ILinkProvider[] = [];
|
||||
public get currentLink(): ILinkWithState | undefined { return this._currentLink; }
|
||||
protected _currentLink: ILinkWithState | undefined;
|
||||
private _lastMouseEvent: MouseEvent | undefined;
|
||||
private _linkCacheDisposables: IDisposable[] = [];
|
||||
|
||||
@@ -156,9 +156,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
|
||||
private _onTooltip(e: MouseEvent): void {
|
||||
this._tooltipTimeout = undefined;
|
||||
const zone = this._findZoneEventAt(e);
|
||||
if (zone && zone.tooltipCallback) {
|
||||
zone.tooltipCallback(e);
|
||||
}
|
||||
zone?.tooltipCallback(e);
|
||||
}
|
||||
|
||||
private _onMouseDown(e: MouseEvent): void {
|
||||
|
||||
+382
-369
File diff suppressed because it is too large
Load Diff
+28
-19
@@ -162,11 +162,11 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
private _changeAnsiColor(event: IAnsiColorChangeEvent): void {
|
||||
if (!this._colorManager) { return; }
|
||||
|
||||
event.colors.forEach(ansiColor => {
|
||||
for (const ansiColor of event.colors) {
|
||||
const color = rgba.toColor(ansiColor.red, ansiColor.green, ansiColor.blue);
|
||||
|
||||
this._colorManager!.colors.ansi[ansiColor.colorIndex] = color;
|
||||
});
|
||||
}
|
||||
|
||||
this._renderService?.setColors(this._colorManager!.colors);
|
||||
this.viewport?.onThemeChange(this._colorManager!.colors);
|
||||
@@ -297,19 +297,26 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
}
|
||||
|
||||
private _syncTextArea(): void {
|
||||
if (!this.textarea || !this.buffer.isCursorInViewport || this._compositionHelper!.isComposing) {
|
||||
if (!this.textarea || !this.buffer.isCursorInViewport || this._compositionHelper!.isComposing || !this._renderService) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cellHeight = Math.ceil(this._charSizeService!.height * this.optionsService.options.lineHeight);
|
||||
const cursorTop = this._bufferService.buffer.y * cellHeight;
|
||||
const cursorLeft = this._bufferService.buffer.x * this._charSizeService!.width;
|
||||
const cursorY = this.buffer.ybase + this.buffer.y;
|
||||
const bufferLine = this.buffer.lines.get(cursorY);
|
||||
if (!bufferLine) {
|
||||
return;
|
||||
}
|
||||
const cursorX = Math.min(this.buffer.x, this.cols - 1);
|
||||
const cellHeight = this._renderService.dimensions.actualCellHeight;
|
||||
const width = bufferLine.getWidth(cursorX);
|
||||
const cellWidth = this._renderService.dimensions.actualCellWidth * width;
|
||||
const cursorTop = this.buffer.y * this._renderService.dimensions.actualCellHeight;
|
||||
const cursorLeft = cursorX * this._renderService.dimensions.actualCellWidth;
|
||||
|
||||
// Sync the textarea to the exact position of the composition view so the IME knows where the
|
||||
// text is.
|
||||
this.textarea.style.left = cursorLeft + 'px';
|
||||
this.textarea.style.top = cursorTop + 'px';
|
||||
this.textarea.style.width = this._charSizeService!.width + 'px';
|
||||
this.textarea.style.width = cellWidth + 'px';
|
||||
this.textarea.style.height = cellHeight + 'px';
|
||||
this.textarea.style.lineHeight = cellHeight + 'px';
|
||||
this.textarea.style.zIndex = '-5';
|
||||
@@ -386,7 +393,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
throw new Error('Terminal requires a parent element.');
|
||||
}
|
||||
|
||||
if (!document.body.contains(parent)) {
|
||||
if (!parent.isConnected) {
|
||||
this._logService.debug('Terminal.open was called on an element that was not attached to the DOM');
|
||||
}
|
||||
|
||||
@@ -438,14 +445,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer);
|
||||
this._instantiationService.setService(ICharSizeService, this._charSizeService);
|
||||
|
||||
this._compositionView = document.createElement('div');
|
||||
this._compositionView.classList.add('composition-view');
|
||||
this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
|
||||
this._helperContainer.appendChild(this._compositionView);
|
||||
|
||||
// Performance: Add viewport and helper elements from the fragment
|
||||
this.element.appendChild(fragment);
|
||||
|
||||
this._theme = this.options.theme || this._theme;
|
||||
this._colorManager = new ColorManager(document, this.options.allowTransparency);
|
||||
this.register(this.optionsService.onOptionChange(e => this._colorManager!.onOptionsChange(e)));
|
||||
@@ -457,6 +456,14 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.register(this._renderService.onRenderedBufferChange(e => this._onRender.fire(e)));
|
||||
this.onResize(e => this._renderService!.resize(e.cols, e.rows));
|
||||
|
||||
this._compositionView = document.createElement('div');
|
||||
this._compositionView.classList.add('composition-view');
|
||||
this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
|
||||
this._helperContainer.appendChild(this._compositionView);
|
||||
|
||||
// Performance: Add viewport and helper elements from the fragment
|
||||
this.element.appendChild(fragment);
|
||||
|
||||
this._soundService = this._instantiationService.createInstance(SoundService);
|
||||
this._instantiationService.setService(ISoundService, this._soundService);
|
||||
this._mouseService = this._instantiationService.createInstance(MouseService);
|
||||
@@ -482,7 +489,9 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
this._selectionService = this.register(this._instantiationService.createInstance(SelectionService,
|
||||
this.element,
|
||||
this.screenElement));
|
||||
this.screenElement,
|
||||
this.linkifier2
|
||||
));
|
||||
this._instantiationService.setService(ISelectionService, this._selectionService);
|
||||
this.register(this._selectionService.onRequestScrollLines(e => this.scrollLines(e.amount, e.suppressScrollEvent)));
|
||||
this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
|
||||
@@ -836,7 +845,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
* Change the cursor style for different selection modes
|
||||
*/
|
||||
public updateCursorStyle(ev: KeyboardEvent): void {
|
||||
if (this._selectionService && this._selectionService.shouldColumnSelect(ev)) {
|
||||
if (this._selectionService?.shouldColumnSelect(ev)) {
|
||||
this.element!.classList.add('column-select');
|
||||
} else {
|
||||
this.element!.classList.remove('column-select');
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('Escape Sequence Files', function(): void {
|
||||
let content = '';
|
||||
const OSC_CODE = 12345;
|
||||
await new Promise(resolve => {
|
||||
customHandler = term.addOscHandler(OSC_CODE, () => {
|
||||
customHandler = term.registerOscHandler(OSC_CODE, () => {
|
||||
// grab terminal viewport content
|
||||
content = terminalToString(term);
|
||||
resolve();
|
||||
|
||||
@@ -21,6 +21,9 @@ export class TestTerminal extends Terminal {
|
||||
public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; }
|
||||
public keyDown(ev: any): boolean | undefined { return this._keyDown(ev); }
|
||||
public keyPress(ev: any): boolean { return this._keyPress(ev); }
|
||||
public writeP(data: string | Uint8Array): Promise<void> {
|
||||
return new Promise(r => this.write(data, r));
|
||||
}
|
||||
}
|
||||
|
||||
export class MockTerminal implements ITerminal {
|
||||
@@ -75,16 +78,16 @@ export class MockTerminal implements ITerminal {
|
||||
public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
|
||||
public registerCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean | Promise<boolean>): IDisposable {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
|
||||
public registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean | Promise<boolean>): IDisposable {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
|
||||
public registerEscHandler(id: IFunctionIdentifier, handler: () => boolean | Promise<boolean>): IDisposable {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
public registerOscHandler(ident: number, callback: (data: string) => boolean | Promise<boolean>): IDisposable {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => boolean | void, options?: ILinkMatcherOptions): number {
|
||||
|
||||
Vendored
+14
-4
@@ -52,10 +52,10 @@ export interface IPublicTerminal extends IDisposable {
|
||||
resize(columns: number, rows: number): void;
|
||||
open(parent: HTMLElement): void;
|
||||
attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void;
|
||||
addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable;
|
||||
addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable;
|
||||
addEscHandler(id: IFunctionIdentifier, callback: () => boolean): IDisposable;
|
||||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
|
||||
registerCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean | Promise<boolean>): IDisposable;
|
||||
registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean | Promise<boolean>): IDisposable;
|
||||
registerEscHandler(id: IFunctionIdentifier, callback: () => boolean | Promise<boolean>): IDisposable;
|
||||
registerOscHandler(ident: number, callback: (data: string) => boolean | Promise<boolean>): IDisposable;
|
||||
registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number;
|
||||
deregisterLinkMatcher(matcherId: number): void;
|
||||
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
|
||||
@@ -205,9 +205,19 @@ export interface ILinkifier {
|
||||
deregisterLinkMatcher(matcherId: number): boolean;
|
||||
}
|
||||
|
||||
interface ILinkState {
|
||||
decorations: ILinkDecorations;
|
||||
isHovered: boolean;
|
||||
}
|
||||
export interface ILinkWithState {
|
||||
link: ILink;
|
||||
state?: ILinkState;
|
||||
}
|
||||
|
||||
export interface ILinkifier2 {
|
||||
onShowLinkUnderline: IEvent<ILinkifierEvent>;
|
||||
onHideLinkUnderline: IEvent<ILinkifierEvent>;
|
||||
readonly currentLink: ILinkWithState | undefined;
|
||||
|
||||
attachToDom(element: HTMLElement, mouseService: IMouseService, renderService: IRenderService): void;
|
||||
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { CompositionHelper } from 'browser/input/CompositionHelper';
|
||||
import { MockCharSizeService } from 'browser/TestUtils.test';
|
||||
import { MockRenderService } from 'browser/TestUtils.test';
|
||||
import { MockCoreService, MockBufferService, MockOptionsService } from 'common/TestUtils.test';
|
||||
|
||||
describe('CompositionHelper', () => {
|
||||
@@ -42,7 +42,7 @@ describe('CompositionHelper', () => {
|
||||
};
|
||||
handledText = '';
|
||||
const bufferService = new MockBufferService(10, 5);
|
||||
compositionHelper = new CompositionHelper(textarea, compositionView, bufferService, new MockOptionsService(), new MockCharSizeService(10, 10), coreService);
|
||||
compositionHelper = new CompositionHelper(textarea, compositionView, bufferService, new MockOptionsService(), coreService, new MockRenderService());
|
||||
});
|
||||
|
||||
describe('Input', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ICharSizeService } from 'browser/services/Services';
|
||||
import { IRenderService } from 'browser/services/Services';
|
||||
import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services';
|
||||
|
||||
interface IPosition {
|
||||
@@ -45,8 +45,8 @@ export class CompositionHelper {
|
||||
private readonly _compositionView: HTMLElement,
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||
@ICoreService private readonly _coreService: ICoreService
|
||||
@ICoreService private readonly _coreService: ICoreService,
|
||||
@IRenderService private readonly _renderService: IRenderService
|
||||
) {
|
||||
this._isComposing = false;
|
||||
this._isSendingComposition = false;
|
||||
@@ -207,9 +207,11 @@ export class CompositionHelper {
|
||||
}
|
||||
|
||||
if (this._bufferService.buffer.isCursorInViewport) {
|
||||
const cellHeight = Math.ceil(this._charSizeService.height * this._optionsService.options.lineHeight);
|
||||
const cursorTop = this._bufferService.buffer.y * cellHeight;
|
||||
const cursorLeft = this._bufferService.buffer.x * this._charSizeService.width;
|
||||
const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);
|
||||
|
||||
const cellHeight = this._renderService.dimensions.actualCellHeight;
|
||||
const cursorTop = this._bufferService.buffer.y * this._renderService.dimensions.actualCellHeight;
|
||||
const cursorLeft = cursorX * this._renderService.dimensions.actualCellWidth;
|
||||
|
||||
this._compositionView.style.left = cursorLeft + 'px';
|
||||
this._compositionView.style.top = cursorTop + 'px';
|
||||
@@ -222,8 +224,9 @@ export class CompositionHelper {
|
||||
const compositionViewBounds = this._compositionView.getBoundingClientRect();
|
||||
this._textarea.style.left = cursorLeft + 'px';
|
||||
this._textarea.style.top = cursorTop + 'px';
|
||||
this._textarea.style.width = compositionViewBounds.width + 'px';
|
||||
this._textarea.style.height = compositionViewBounds.height + 'px';
|
||||
// Ensure the text area is at least 1x1, otherwise certain IMEs may break
|
||||
this._textarea.style.width = Math.max(compositionViewBounds.width, 1) + 'px';
|
||||
this._textarea.style.height = Math.max(compositionViewBounds.height, 1) + 'px';
|
||||
this._textarea.style.lineHeight = compositionViewBounds.height + 'px';
|
||||
}
|
||||
|
||||
|
||||
@@ -292,28 +292,28 @@ class BufferLineApiView implements IBufferLineApi {
|
||||
class ParserApi implements IParser {
|
||||
constructor(private _core: ITerminal) { }
|
||||
|
||||
public registerCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
|
||||
return this._core.addCsiHandler(id, (params: IParams) => callback(params.toArray()));
|
||||
public registerCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean | Promise<boolean>): IDisposable {
|
||||
return this._core.registerCsiHandler(id, (params: IParams) => callback(params.toArray()));
|
||||
}
|
||||
public addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
|
||||
public addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean | Promise<boolean>): IDisposable {
|
||||
return this.registerCsiHandler(id, callback);
|
||||
}
|
||||
public registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable {
|
||||
return this._core.addDcsHandler(id, (data: string, params: IParams) => callback(data, params.toArray()));
|
||||
public registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean | Promise<boolean>): IDisposable {
|
||||
return this._core.registerDcsHandler(id, (data: string, params: IParams) => callback(data, params.toArray()));
|
||||
}
|
||||
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable {
|
||||
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean | Promise<boolean>): IDisposable {
|
||||
return this.registerDcsHandler(id, callback);
|
||||
}
|
||||
public registerEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
|
||||
return this._core.addEscHandler(id, handler);
|
||||
public registerEscHandler(id: IFunctionIdentifier, handler: () => boolean | Promise<boolean>): IDisposable {
|
||||
return this._core.registerEscHandler(id, handler);
|
||||
}
|
||||
public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
|
||||
public addEscHandler(id: IFunctionIdentifier, handler: () => boolean | Promise<boolean>): IDisposable {
|
||||
return this.registerEscHandler(id, handler);
|
||||
}
|
||||
public registerOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
return this._core.addOscHandler(ident, callback);
|
||||
public registerOscHandler(ident: number, callback: (data: string) => boolean | Promise<boolean>): IDisposable {
|
||||
return this._core.registerOscHandler(ident, callback);
|
||||
}
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean | Promise<boolean>): IDisposable {
|
||||
return this.registerOscHandler(ident, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +174,10 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
}
|
||||
|
||||
public onDevicePixelRatioChange(): void {
|
||||
// Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable
|
||||
// when devicePixelRatio changes
|
||||
this._charSizeService.measure();
|
||||
|
||||
this._renderer.onDevicePixelRatioChange();
|
||||
this.refreshRows(0, this._rowCount - 1);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class TestSelectionService extends SelectionService {
|
||||
optionsService: IOptionsService,
|
||||
renderService: IRenderService
|
||||
) {
|
||||
super(null!, null!, bufferService, new MockCoreService(), new MockMouseService(), optionsService, renderService);
|
||||
super(null!, null!, null!, bufferService, new MockCoreService(), new MockMouseService(), optionsService, renderService);
|
||||
}
|
||||
|
||||
public get model(): SelectionModel { return this._model; }
|
||||
|
||||
@@ -11,6 +11,7 @@ import { SelectionModel } from 'browser/selection/SelectionModel';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { EventEmitter, IEvent } from 'common/EventEmitter';
|
||||
import { ICharSizeService, IMouseService, ISelectionService, IRenderService } from 'browser/services/Services';
|
||||
import { ILinkifier2 } from 'browser/Types';
|
||||
import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services';
|
||||
import { getCoordsRelativeToElement } from 'browser/input/Mouse';
|
||||
import { moveToCellSequence } from 'browser/input/MoveToCell';
|
||||
@@ -105,6 +106,9 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
private _workCell: CellData = new CellData();
|
||||
|
||||
private _mouseDownTimeStamp: number = 0;
|
||||
private _oldHasSelection: boolean = false;
|
||||
private _oldSelectionStart: [number, number] | undefined = undefined;
|
||||
private _oldSelectionEnd: [number, number] | undefined = undefined;
|
||||
|
||||
private _onLinuxMouseSelection = this.register(new EventEmitter<string>());
|
||||
public get onLinuxMouseSelection(): IEvent<string> { return this._onLinuxMouseSelection.event; }
|
||||
@@ -118,6 +122,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
constructor(
|
||||
private readonly _element: HTMLElement,
|
||||
private readonly _screenElement: HTMLElement,
|
||||
private readonly _linkifier: ILinkifier2,
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@ICoreService private readonly _coreService: ICoreService,
|
||||
@IMouseService private readonly _mouseService: IMouseService,
|
||||
@@ -290,7 +295,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
* Checks if the current click was inside the current selection
|
||||
* @param event The mouse event
|
||||
*/
|
||||
public isClickInSelection(event: MouseEvent): boolean {
|
||||
private _isClickInSelection(event: MouseEvent): boolean {
|
||||
const coords = this._getMouseBufferCoords(event);
|
||||
const start = this._model.finalSelectionStart;
|
||||
const end = this._model.finalSelectionEnd;
|
||||
@@ -313,13 +318,22 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
* Selects word at the current mouse event coordinates.
|
||||
* @param event The mouse event.
|
||||
*/
|
||||
public selectWordAtCursor(event: MouseEvent): void {
|
||||
private _selectWordAtCursor(event: MouseEvent, allowWhitespaceOnlySelection: boolean): boolean {
|
||||
// Check if there is a link under the cursor first and select that if so
|
||||
const range = this._linkifier.currentLink?.link?.range;
|
||||
if (range) {
|
||||
this._model.selectionStart = [range.start.x - 1, range.start.y - 1];
|
||||
this._model.selectionEnd = [range.end.x, range.end.y - 1];
|
||||
return true;
|
||||
}
|
||||
|
||||
const coords = this._getMouseBufferCoords(event);
|
||||
if (coords) {
|
||||
this._selectWordAt(coords, false);
|
||||
this._selectWordAt(coords, allowWhitespaceOnlySelection);
|
||||
this._model.selectionEnd = undefined;
|
||||
this.refresh(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,14 +538,12 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a double click, selecting the current work.
|
||||
* Performs a double click, selecting the current word.
|
||||
* @param event The mouse event.
|
||||
*/
|
||||
private _onDoubleClick(event: MouseEvent): void {
|
||||
const coords = this._getMouseBufferCoords(event);
|
||||
if (coords) {
|
||||
if (this._selectWordAtCursor(event, true)) {
|
||||
this._activeSelectionMode = SelectionMode.WORD;
|
||||
this._selectWordAt(coords, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,11 +693,43 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
this._coreService.triggerDataEvent(sequence, true);
|
||||
}
|
||||
}
|
||||
} else if (this.hasSelection) {
|
||||
this._onSelectionChange.fire();
|
||||
} else {
|
||||
this._fireEventIfSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private _fireEventIfSelectionChanged(): void {
|
||||
const start = this._model.finalSelectionStart;
|
||||
const end = this._model.finalSelectionEnd;
|
||||
const hasSelection = !!start && !!end && (start[0] !== end[0] || start[1] !== end[1]);
|
||||
|
||||
if (!hasSelection) {
|
||||
if (this._oldHasSelection) {
|
||||
this._fireOnSelectionChange(start, end, hasSelection);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check, these should not be undefined as there is a selection
|
||||
if (!start || !end) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._oldSelectionStart || !this._oldSelectionEnd || (
|
||||
start[0] !== this._oldSelectionStart[0] || start[1] !== this._oldSelectionStart[1] ||
|
||||
end[0] !== this._oldSelectionEnd[0] || end[1] !== this._oldSelectionEnd[1])) {
|
||||
|
||||
this._fireOnSelectionChange(start, end, hasSelection);
|
||||
}
|
||||
}
|
||||
|
||||
private _fireOnSelectionChange(start: [number, number] | undefined, end: [number, number] | undefined, hasSelection: boolean): void {
|
||||
this._oldSelectionStart = start;
|
||||
this._oldSelectionEnd = end;
|
||||
this._oldHasSelection = hasSelection;
|
||||
this._onSelectionChange.fire();
|
||||
}
|
||||
|
||||
private _onBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void {
|
||||
this.clearSelection();
|
||||
// Only adjust the selection on trim, shiftElements is rarely used (only in
|
||||
@@ -727,6 +771,15 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
public rightClickSelect(ev: MouseEvent): void {
|
||||
if (!this._isClickInSelection(ev)) {
|
||||
if (this._selectWordAtCursor(ev, false)) {
|
||||
this.refresh(true);
|
||||
}
|
||||
this._fireEventIfSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets positional information for the word at the coordinated specified.
|
||||
* @param coords The coordinates to get the word at.
|
||||
|
||||
@@ -91,8 +91,7 @@ export interface ISelectionService {
|
||||
selectAll(): void;
|
||||
selectLines(start: number, end: number): void;
|
||||
clearSelection(): void;
|
||||
isClickInSelection(event: MouseEvent): boolean;
|
||||
selectWordAtCursor(event: MouseEvent): void;
|
||||
rightClickSelect(event: MouseEvent): void;
|
||||
shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean;
|
||||
shouldForceSelection(event: MouseEvent): boolean;
|
||||
refresh(isLinuxMouseSelection?: boolean): void;
|
||||
|
||||
Reference in New Issue
Block a user