/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Emitter } from '../../../../../base/common/event.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import * as platform from '../../../../../base/common/platform.js'; function hasModifier(e, modifier) { return !!e[modifier]; } /** * An event that encapsulates the various trigger modifiers logic needed for go to definition. */ export class ClickLinkMouseEvent { constructor(source, opts) { this.target = source.target; this.hasTriggerModifier = hasModifier(source.event, opts.triggerModifier); this.hasSideBySideModifier = hasModifier(source.event, opts.triggerSideBySideModifier); this.isNoneOrSingleMouseDown = (source.event.detail <= 1); } } /** * An event that encapsulates the various trigger modifiers logic needed for go to definition. */ export class ClickLinkKeyboardEvent { constructor(source, opts) { this.keyCodeIsTriggerKey = (source.keyCode === opts.triggerKey); this.keyCodeIsSideBySideKey = (source.keyCode === opts.triggerSideBySideKey); this.hasTriggerModifier = hasModifier(source, opts.triggerModifier); } } export class ClickLinkOptions { constructor(triggerKey, triggerModifier, triggerSideBySideKey, triggerSideBySideModifier) { this.triggerKey = triggerKey; this.triggerModifier = triggerModifier; this.triggerSideBySideKey = triggerSideBySideKey; this.triggerSideBySideModifier = triggerSideBySideModifier; } equals(other) { return (this.triggerKey === other.triggerKey && this.triggerModifier === other.triggerModifier && this.triggerSideBySideKey === other.triggerSideBySideKey && this.triggerSideBySideModifier === other.triggerSideBySideModifier); } } function createOptions(multiCursorModifier) { if (multiCursorModifier === 'altKey') { if (platform.isMacintosh) { return new ClickLinkOptions(57 /* KeyCode.Meta */, 'metaKey', 6 /* KeyCode.Alt */, 'altKey'); } return new ClickLinkOptions(5 /* KeyCode.Ctrl */, 'ctrlKey', 6 /* KeyCode.Alt */, 'altKey'); } if (platform.isMacintosh) { return new ClickLinkOptions(6 /* KeyCode.Alt */, 'altKey', 57 /* KeyCode.Meta */, 'metaKey'); } return new ClickLinkOptions(6 /* KeyCode.Alt */, 'altKey', 5 /* KeyCode.Ctrl */, 'ctrlKey'); } export class ClickLinkGesture extends Disposable { constructor(editor) { super(); this._onMouseMoveOrRelevantKeyDown = this._register(new Emitter()); this.onMouseMoveOrRelevantKeyDown = this._onMouseMoveOrRelevantKeyDown.event; this._onExecute = this._register(new Emitter()); this.onExecute = this._onExecute.event; this._onCancel = this._register(new Emitter()); this.onCancel = this._onCancel.event; this._editor = editor; this._opts = createOptions(this._editor.getOption(72 /* EditorOption.multiCursorModifier */)); this._lastMouseMoveEvent = null; this._hasTriggerKeyOnMouseDown = false; this._lineNumberOnMouseDown = 0; this._register(this._editor.onDidChangeConfiguration((e) => { if (e.hasChanged(72 /* EditorOption.multiCursorModifier */)) { const newOpts = createOptions(this._editor.getOption(72 /* EditorOption.multiCursorModifier */)); if (this._opts.equals(newOpts)) { return; } this._opts = newOpts; this._lastMouseMoveEvent = null; this._hasTriggerKeyOnMouseDown = false; this._lineNumberOnMouseDown = 0; this._onCancel.fire(); } })); this._register(this._editor.onMouseMove((e) => this._onEditorMouseMove(new ClickLinkMouseEvent(e, this._opts)))); this._register(this._editor.onMouseDown((e) => this._onEditorMouseDown(new ClickLinkMouseEvent(e, this._opts)))); this._register(this._editor.onMouseUp((e) => this._onEditorMouseUp(new ClickLinkMouseEvent(e, this._opts)))); this._register(this._editor.onKeyDown((e) => this._onEditorKeyDown(new ClickLinkKeyboardEvent(e, this._opts)))); this._register(this._editor.onKeyUp((e) => this._onEditorKeyUp(new ClickLinkKeyboardEvent(e, this._opts)))); this._register(this._editor.onMouseDrag(() => this._resetHandler())); this._register(this._editor.onDidChangeCursorSelection((e) => this._onDidChangeCursorSelection(e))); this._register(this._editor.onDidChangeModel((e) => this._resetHandler())); this._register(this._editor.onDidChangeModelContent(() => this._resetHandler())); this._register(this._editor.onDidScrollChange((e) => { if (e.scrollTopChanged || e.scrollLeftChanged) { this._resetHandler(); } })); } _onDidChangeCursorSelection(e) { if (e.selection && e.selection.startColumn !== e.selection.endColumn) { this._resetHandler(); // immediately stop this feature if the user starts to select (https://github.com/microsoft/vscode/issues/7827) } } _onEditorMouseMove(mouseEvent) { this._lastMouseMoveEvent = mouseEvent; this._onMouseMoveOrRelevantKeyDown.fire([mouseEvent, null]); } _onEditorMouseDown(mouseEvent) { // We need to record if we had the trigger key on mouse down because someone might select something in the editor // holding the mouse down and then while mouse is down start to press Ctrl/Cmd to start a copy operation and then // release the mouse button without wanting to do the navigation. // With this flag we prevent goto definition if the mouse was down before the trigger key was pressed. this._hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier; this._lineNumberOnMouseDown = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0; } _onEditorMouseUp(mouseEvent) { const currentLineNumber = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0; if (this._hasTriggerKeyOnMouseDown && this._lineNumberOnMouseDown && this._lineNumberOnMouseDown === currentLineNumber) { this._onExecute.fire(mouseEvent); } } _onEditorKeyDown(e) { if (this._lastMouseMoveEvent && (e.keyCodeIsTriggerKey // User just pressed Ctrl/Cmd (normal goto definition) || (e.keyCodeIsSideBySideKey && e.hasTriggerModifier) // User pressed Ctrl/Cmd+Alt (goto definition to the side) )) { this._onMouseMoveOrRelevantKeyDown.fire([this._lastMouseMoveEvent, e]); } else if (e.hasTriggerModifier) { this._onCancel.fire(); // remove decorations if user holds another key with ctrl/cmd to prevent accident goto declaration } } _onEditorKeyUp(e) { if (e.keyCodeIsTriggerKey) { this._onCancel.fire(); } } _resetHandler() { this._lastMouseMoveEvent = null; this._hasTriggerKeyOnMouseDown = false; this._onCancel.fire(); } }