9139fe5cc9225bcc6ccd947be3d3aa491c7d32edde3f66bbc75556692f00d3042107b89a84d79155c2266f0cc013eb8d7a57d586978e0333cf025ab04b849c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { Emitter } from '../../../../../base/common/event.js';
  6. import { Disposable } from '../../../../../base/common/lifecycle.js';
  7. import * as platform from '../../../../../base/common/platform.js';
  8. function hasModifier(e, modifier) {
  9. return !!e[modifier];
  10. }
  11. /**
  12. * An event that encapsulates the various trigger modifiers logic needed for go to definition.
  13. */
  14. export class ClickLinkMouseEvent {
  15. constructor(source, opts) {
  16. this.target = source.target;
  17. this.hasTriggerModifier = hasModifier(source.event, opts.triggerModifier);
  18. this.hasSideBySideModifier = hasModifier(source.event, opts.triggerSideBySideModifier);
  19. this.isNoneOrSingleMouseDown = (source.event.detail <= 1);
  20. }
  21. }
  22. /**
  23. * An event that encapsulates the various trigger modifiers logic needed for go to definition.
  24. */
  25. export class ClickLinkKeyboardEvent {
  26. constructor(source, opts) {
  27. this.keyCodeIsTriggerKey = (source.keyCode === opts.triggerKey);
  28. this.keyCodeIsSideBySideKey = (source.keyCode === opts.triggerSideBySideKey);
  29. this.hasTriggerModifier = hasModifier(source, opts.triggerModifier);
  30. }
  31. }
  32. export class ClickLinkOptions {
  33. constructor(triggerKey, triggerModifier, triggerSideBySideKey, triggerSideBySideModifier) {
  34. this.triggerKey = triggerKey;
  35. this.triggerModifier = triggerModifier;
  36. this.triggerSideBySideKey = triggerSideBySideKey;
  37. this.triggerSideBySideModifier = triggerSideBySideModifier;
  38. }
  39. equals(other) {
  40. return (this.triggerKey === other.triggerKey
  41. && this.triggerModifier === other.triggerModifier
  42. && this.triggerSideBySideKey === other.triggerSideBySideKey
  43. && this.triggerSideBySideModifier === other.triggerSideBySideModifier);
  44. }
  45. }
  46. function createOptions(multiCursorModifier) {
  47. if (multiCursorModifier === 'altKey') {
  48. if (platform.isMacintosh) {
  49. return new ClickLinkOptions(57 /* KeyCode.Meta */, 'metaKey', 6 /* KeyCode.Alt */, 'altKey');
  50. }
  51. return new ClickLinkOptions(5 /* KeyCode.Ctrl */, 'ctrlKey', 6 /* KeyCode.Alt */, 'altKey');
  52. }
  53. if (platform.isMacintosh) {
  54. return new ClickLinkOptions(6 /* KeyCode.Alt */, 'altKey', 57 /* KeyCode.Meta */, 'metaKey');
  55. }
  56. return new ClickLinkOptions(6 /* KeyCode.Alt */, 'altKey', 5 /* KeyCode.Ctrl */, 'ctrlKey');
  57. }
  58. export class ClickLinkGesture extends Disposable {
  59. constructor(editor) {
  60. super();
  61. this._onMouseMoveOrRelevantKeyDown = this._register(new Emitter());
  62. this.onMouseMoveOrRelevantKeyDown = this._onMouseMoveOrRelevantKeyDown.event;
  63. this._onExecute = this._register(new Emitter());
  64. this.onExecute = this._onExecute.event;
  65. this._onCancel = this._register(new Emitter());
  66. this.onCancel = this._onCancel.event;
  67. this._editor = editor;
  68. this._opts = createOptions(this._editor.getOption(72 /* EditorOption.multiCursorModifier */));
  69. this._lastMouseMoveEvent = null;
  70. this._hasTriggerKeyOnMouseDown = false;
  71. this._lineNumberOnMouseDown = 0;
  72. this._register(this._editor.onDidChangeConfiguration((e) => {
  73. if (e.hasChanged(72 /* EditorOption.multiCursorModifier */)) {
  74. const newOpts = createOptions(this._editor.getOption(72 /* EditorOption.multiCursorModifier */));
  75. if (this._opts.equals(newOpts)) {
  76. return;
  77. }
  78. this._opts = newOpts;
  79. this._lastMouseMoveEvent = null;
  80. this._hasTriggerKeyOnMouseDown = false;
  81. this._lineNumberOnMouseDown = 0;
  82. this._onCancel.fire();
  83. }
  84. }));
  85. this._register(this._editor.onMouseMove((e) => this._onEditorMouseMove(new ClickLinkMouseEvent(e, this._opts))));
  86. this._register(this._editor.onMouseDown((e) => this._onEditorMouseDown(new ClickLinkMouseEvent(e, this._opts))));
  87. this._register(this._editor.onMouseUp((e) => this._onEditorMouseUp(new ClickLinkMouseEvent(e, this._opts))));
  88. this._register(this._editor.onKeyDown((e) => this._onEditorKeyDown(new ClickLinkKeyboardEvent(e, this._opts))));
  89. this._register(this._editor.onKeyUp((e) => this._onEditorKeyUp(new ClickLinkKeyboardEvent(e, this._opts))));
  90. this._register(this._editor.onMouseDrag(() => this._resetHandler()));
  91. this._register(this._editor.onDidChangeCursorSelection((e) => this._onDidChangeCursorSelection(e)));
  92. this._register(this._editor.onDidChangeModel((e) => this._resetHandler()));
  93. this._register(this._editor.onDidChangeModelContent(() => this._resetHandler()));
  94. this._register(this._editor.onDidScrollChange((e) => {
  95. if (e.scrollTopChanged || e.scrollLeftChanged) {
  96. this._resetHandler();
  97. }
  98. }));
  99. }
  100. _onDidChangeCursorSelection(e) {
  101. if (e.selection && e.selection.startColumn !== e.selection.endColumn) {
  102. this._resetHandler(); // immediately stop this feature if the user starts to select (https://github.com/microsoft/vscode/issues/7827)
  103. }
  104. }
  105. _onEditorMouseMove(mouseEvent) {
  106. this._lastMouseMoveEvent = mouseEvent;
  107. this._onMouseMoveOrRelevantKeyDown.fire([mouseEvent, null]);
  108. }
  109. _onEditorMouseDown(mouseEvent) {
  110. // We need to record if we had the trigger key on mouse down because someone might select something in the editor
  111. // holding the mouse down and then while mouse is down start to press Ctrl/Cmd to start a copy operation and then
  112. // release the mouse button without wanting to do the navigation.
  113. // With this flag we prevent goto definition if the mouse was down before the trigger key was pressed.
  114. this._hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier;
  115. this._lineNumberOnMouseDown = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0;
  116. }
  117. _onEditorMouseUp(mouseEvent) {
  118. const currentLineNumber = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0;
  119. if (this._hasTriggerKeyOnMouseDown && this._lineNumberOnMouseDown && this._lineNumberOnMouseDown === currentLineNumber) {
  120. this._onExecute.fire(mouseEvent);
  121. }
  122. }
  123. _onEditorKeyDown(e) {
  124. if (this._lastMouseMoveEvent
  125. && (e.keyCodeIsTriggerKey // User just pressed Ctrl/Cmd (normal goto definition)
  126. || (e.keyCodeIsSideBySideKey && e.hasTriggerModifier) // User pressed Ctrl/Cmd+Alt (goto definition to the side)
  127. )) {
  128. this._onMouseMoveOrRelevantKeyDown.fire([this._lastMouseMoveEvent, e]);
  129. }
  130. else if (e.hasTriggerModifier) {
  131. this._onCancel.fire(); // remove decorations if user holds another key with ctrl/cmd to prevent accident goto declaration
  132. }
  133. }
  134. _onEditorKeyUp(e) {
  135. if (e.keyCodeIsTriggerKey) {
  136. this._onCancel.fire();
  137. }
  138. }
  139. _resetHandler() {
  140. this._lastMouseMoveEvent = null;
  141. this._hasTriggerKeyOnMouseDown = false;
  142. this._onCancel.fire();
  143. }
  144. }