diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index d08e64f03f5..e2e77261a06 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -8,8 +8,8 @@ import { StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent import { TimeoutTimer } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import { HitTestContext, IViewZoneData, MouseTarget, MouseTargetFactory, PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; -import { IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { HitTestContext, MouseTarget, MouseTargetFactory, PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; +import { IMouseTarget, IMouseTargetViewZoneData, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ClientCoordinates, EditorMouseEvent, EditorMouseEventFactory, GlobalEditorMouseMoveMonitor, createEditorPagePosition, createCoordinatesRelativeToEditor } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; @@ -260,7 +260,7 @@ export class MouseHandler extends ViewEventHandler { // Do not steal focus e.preventDefault(); } else if (targetIsViewZone) { - const viewZoneData = t.detail; + const viewZoneData = t.detail; if (this.viewHelper.shouldSuppressMouseDownOnViewZone(viewZoneData.viewZoneId)) { focus(); this._mouseDownOperation.start(t.type, e); @@ -455,7 +455,7 @@ class MouseDownOperation extends Disposable { this._currentSelection = e.selections[0]; } - private _getPositionOutsideEditor(e: EditorMouseEvent): MouseTarget | null { + private _getPositionOutsideEditor(e: EditorMouseEvent): IMouseTarget | null { const editorContent = e.editorPos; const model = this._context.model; const viewLayout = this._context.viewLayout; @@ -468,12 +468,12 @@ class MouseDownOperation extends Disposable { if (viewZoneData) { const newPosition = this._helpPositionJumpOverViewZone(viewZoneData); if (newPosition) { - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); + return MouseTarget.createOutsideEditor(mouseColumn, newPosition); } } const aboveLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(aboveLineNumber, 1)); + return MouseTarget.createOutsideEditor(mouseColumn, new Position(aboveLineNumber, 1)); } if (e.posy > editorContent.y + editorContent.height) { @@ -482,28 +482,28 @@ class MouseDownOperation extends Disposable { if (viewZoneData) { const newPosition = this._helpPositionJumpOverViewZone(viewZoneData); if (newPosition) { - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); + return MouseTarget.createOutsideEditor(mouseColumn, newPosition); } } const belowLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); + return MouseTarget.createOutsideEditor(mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); } const possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + e.relativePos.y); if (e.posx < editorContent.x) { - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, 1)); + return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, 1)); } if (e.posx > editorContent.x + editorContent.width) { - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber))); + return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber))); } return null; } - private _findMousePosition(e: EditorMouseEvent, testEventTarget: boolean): MouseTarget | null { + private _findMousePosition(e: EditorMouseEvent, testEventTarget: boolean): IMouseTarget | null { const positionOutsideEditor = this._getPositionOutsideEditor(e); if (positionOutsideEditor) { return positionOutsideEditor; @@ -516,16 +516,16 @@ class MouseDownOperation extends Disposable { } if (t.type === MouseTargetType.CONTENT_VIEW_ZONE || t.type === MouseTargetType.GUTTER_VIEW_ZONE) { - const newPosition = this._helpPositionJumpOverViewZone(t.detail); + const newPosition = this._helpPositionJumpOverViewZone(t.detail); if (newPosition) { - return new MouseTarget(t.element, t.type, t.mouseColumn, newPosition, null, t.detail); + return MouseTarget.createViewZone(t.type, t.element, t.mouseColumn, newPosition, t.detail); } } return t; } - private _helpPositionJumpOverViewZone(viewZoneData: IViewZoneData): Position | null { + private _helpPositionJumpOverViewZone(viewZoneData: IMouseTargetViewZoneData): Position | null { // Force position on view zones to go above or below depending on where selection started from const selectionStart = new Position(this._currentSelection.selectionStartLineNumber, this._currentSelection.selectionStartColumn); const positionBefore = viewZoneData.positionBefore; @@ -541,7 +541,7 @@ class MouseDownOperation extends Disposable { return null; } - private _dispatchMouse(position: MouseTarget, inSelectionMode: boolean): void { + private _dispatchMouse(position: IMouseTarget, inSelectionMode: boolean): void { if (!position.position) { return; } diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 5aadb8190ae..cf279032cb0 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; -import { IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { IMouseTargetContentEmptyData, IMouseTargetMarginData, IMouseTarget, IMouseTargetContentEmpty, IMouseTargetContentText, IMouseTargetContentWidget, IMouseTargetMargin, IMouseTargetOutsideEditor, IMouseTargetOverlayWidget, IMouseTargetScrollbar, IMouseTargetTextarea, IMouseTargetUnknown, IMouseTargetViewZone, IMouseTargetContentTextData, IMouseTargetViewZoneData, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ClientCoordinates, EditorMouseEvent, EditorPagePosition, PageCoordinates, CoordinatesRelativeToEditor } from 'vs/editor/browser/editorDom'; import { PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; import { ViewLine } from 'vs/editor/browser/viewParts/lines/viewLine'; @@ -21,35 +21,9 @@ import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/ import { PositionAffinity } from 'vs/editor/common/model'; import { InjectedText } from 'vs/editor/common/viewModel/modelLineProjectionData'; -export interface IViewZoneData { - viewZoneId: string; - positionBefore: Position | null; - positionAfter: Position | null; - position: Position; - afterLineNumber: number; -} - -export interface IMarginData { - isAfterLines: boolean; - glyphMarginLeft: number; - glyphMarginWidth: number; - lineNumbersWidth: number; - offsetX: number; -} - -export interface IEmptyContentData { - isAfterLines: boolean; - horizontalDistanceToText?: number; -} - -export interface ITextContentData { - mightBeForeignElement: boolean; - injectedText: InjectedText | null; -} - const enum HitTestResultType { - Unknown = 0, - Content = 1, + Unknown, + Content, } class UnknownHitTestResult { @@ -87,25 +61,46 @@ export class PointerHandlerLastRenderData { ) { } } -export class MouseTarget implements IMouseTarget { +export class MouseTarget { - public readonly element: Element | null; - public readonly type: MouseTargetType; - public readonly mouseColumn: number; - public readonly position: Position | null; - public readonly range: EditorRange | null; - public readonly detail: any; - - constructor(element: Element | null, type: MouseTargetType, mouseColumn: number = 0, position: Position | null = null, range: EditorRange | null = null, detail: any = null) { - this.element = element; - this.type = type; - this.mouseColumn = mouseColumn; - this.position = position; + private static _deduceRage(position: Position): EditorRange; + private static _deduceRage(position: Position, range: EditorRange | null): EditorRange; + private static _deduceRage(position: Position | null): EditorRange | null; + private static _deduceRage(position: Position | null, range: EditorRange | null = null): EditorRange | null { if (!range && position) { - range = new EditorRange(position.lineNumber, position.column, position.lineNumber, position.column); + return new EditorRange(position.lineNumber, position.column, position.lineNumber, position.column); } - this.range = range; - this.detail = detail; + return range ?? null; + } + public static createUnknown(element: Element | null, mouseColumn: number, position: Position | null): IMouseTargetUnknown { + return { type: MouseTargetType.UNKNOWN, element, mouseColumn, position, range: this._deduceRage(position) }; + } + public static createTextarea(element: Element | null, mouseColumn: number): IMouseTargetTextarea { + return { type: MouseTargetType.TEXTAREA, element, mouseColumn, position: null, range: null }; + } + public static createMargin(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, element: Element | null, mouseColumn: number, position: Position, range: EditorRange, detail: IMouseTargetMarginData): IMouseTargetMargin { + return { type, element, mouseColumn, position, range, detail }; + } + public static createViewZone(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, element: Element | null, mouseColumn: number, position: Position, detail: IMouseTargetViewZoneData): IMouseTargetViewZone { + return { type, element, mouseColumn, position, range: this._deduceRage(position), detail }; + } + public static createContentText(element: Element | null, mouseColumn: number, position: Position, range: EditorRange | null, detail: IMouseTargetContentTextData): IMouseTargetContentText { + return { type: MouseTargetType.CONTENT_TEXT, element, mouseColumn, position, range: this._deduceRage(position, range), detail }; + } + public static createContentEmpty(element: Element | null, mouseColumn: number, position: Position, detail: IMouseTargetContentEmptyData): IMouseTargetContentEmpty { + return { type: MouseTargetType.CONTENT_EMPTY, element, mouseColumn, position, range: this._deduceRage(position), detail }; + } + public static createContentWidget(element: Element | null, mouseColumn: number, detail: string): IMouseTargetContentWidget { + return { type: MouseTargetType.CONTENT_WIDGET, element, mouseColumn, position: null, range: null, detail }; + } + public static createScrollbar(element: Element | null, mouseColumn: number, position: Position): IMouseTargetScrollbar { + return { type: MouseTargetType.SCROLLBAR, element, mouseColumn, position, range: this._deduceRage(position) }; + } + public static createOverlayWidget(element: Element | null, mouseColumn: number, detail: string): IMouseTargetOverlayWidget { + return { type: MouseTargetType.OVERLAY_WIDGET, element, mouseColumn, position: null, range: null, detail }; + } + public static createOutsideEditor(mouseColumn: number, position: Position): IMouseTargetOutsideEditor { + return { type: MouseTargetType.OUTSIDE_EDITOR, element: null, mouseColumn, position, range: this._deduceRage(position) }; } private static _typeToString(type: MouseTargetType): string { @@ -149,11 +144,7 @@ export class MouseTarget implements IMouseTarget { } public static toString(target: IMouseTarget): string { - return this._typeToString(target.type) + ': ' + target.position + ' - ' + target.range + ' - ' + target.detail; - } - - public toString(): string { - return MouseTarget.toString(this); + return this._typeToString(target.type) + ': ' + target.position + ' - ' + target.range + ' - ' + JSON.stringify((target).detail); } } @@ -249,11 +240,11 @@ export class HitTestContext { this._viewHelper = viewHelper; } - public getZoneAtCoord(mouseVerticalOffset: number): IViewZoneData | null { + public getZoneAtCoord(mouseVerticalOffset: number): IMouseTargetViewZoneData | null { return HitTestContext.getZoneAtCoord(this._context, mouseVerticalOffset); } - public static getZoneAtCoord(context: ViewContext, mouseVerticalOffset: number): IViewZoneData | null { + public static getZoneAtCoord(context: ViewContext, mouseVerticalOffset: number): IMouseTargetViewZoneData | null { // The target is either a view zone or the empty space after the last view-line const viewZoneWhitespace = context.viewLayout.getWhitespaceAtVerticalOffset(mouseVerticalOffset); @@ -418,24 +409,40 @@ class HitTestRequest extends BareHitTestRequest { return `pos(${this.pos.x},${this.pos.y}), editorPos(${this.editorPos.x},${this.editorPos.y}), relativePos(${this.relativePos.x},${this.relativePos.y}), mouseVerticalOffset: ${this.mouseVerticalOffset}, mouseContentHorizontalOffset: ${this.mouseContentHorizontalOffset}\n\ttarget: ${this.target ? (this.target).outerHTML : null}`; } - public fulfill(type: MouseTargetType.UNKNOWN, position?: Position | null, range?: EditorRange | null): MouseTarget; - public fulfill(type: MouseTargetType.TEXTAREA, position: Position | null): MouseTarget; - public fulfill(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, position: Position, range: EditorRange, detail: IMarginData): MouseTarget; - public fulfill(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, position: Position, range: null, detail: IViewZoneData): MouseTarget; - public fulfill(type: MouseTargetType.CONTENT_TEXT, position: Position | null, range: EditorRange | null, detail: ITextContentData): MouseTarget; - public fulfill(type: MouseTargetType.CONTENT_EMPTY, position: Position | null, range: EditorRange | null, detail: IEmptyContentData): MouseTarget; - public fulfill(type: MouseTargetType.CONTENT_WIDGET, position: null, range: null, detail: string): MouseTarget; - public fulfill(type: MouseTargetType.SCROLLBAR, position: Position): MouseTarget; - public fulfill(type: MouseTargetType.OVERLAY_WIDGET, position: null, range: null, detail: string): MouseTarget; - // public fulfill(type: MouseTargetType.OVERVIEW_RULER, position?: Position | null, range?: EditorRange | null, detail?: any): MouseTarget; - // public fulfill(type: MouseTargetType.OUTSIDE_EDITOR, position?: Position | null, range?: EditorRange | null, detail?: any): MouseTarget; - public fulfill(type: MouseTargetType, position: Position | null = null, range: EditorRange | null = null, detail: any = null): MouseTarget { - let mouseColumn = this.mouseColumn; + private _getMouseColumn(position: Position | null = null): number { if (position && position.column < this._ctx.model.getLineMaxColumn(position.lineNumber)) { // Most likely, the line contains foreign decorations... - mouseColumn = CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getTextModelOptions().tabSize) + 1; + return CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getTextModelOptions().tabSize) + 1; } - return new MouseTarget(this.target, type, mouseColumn, position, range, detail); + return this.mouseColumn; + } + + public fulfillUnknown(position: Position | null = null): IMouseTargetUnknown { + return MouseTarget.createUnknown(this.target, this._getMouseColumn(position), position); + } + public fulfillTextarea(): IMouseTargetTextarea { + return MouseTarget.createTextarea(this.target, this._getMouseColumn()); + } + public fulfillMargin(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, position: Position, range: EditorRange, detail: IMouseTargetMarginData): IMouseTargetMargin { + return MouseTarget.createMargin(type, this.target, this._getMouseColumn(position), position, range, detail); + } + public fulfillViewZone(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, position: Position, detail: IMouseTargetViewZoneData): IMouseTargetViewZone { + return MouseTarget.createViewZone(type, this.target, this._getMouseColumn(position), position, detail); + } + public fulfillContentText(position: Position, range: EditorRange | null, detail: IMouseTargetContentTextData): IMouseTargetContentText { + return MouseTarget.createContentText(this.target, this._getMouseColumn(position), position, range, detail); + } + public fulfillContentEmpty(position: Position, detail: IMouseTargetContentEmptyData): IMouseTargetContentEmpty { + return MouseTarget.createContentEmpty(this.target, this._getMouseColumn(position), position, detail); + } + public fulfillContentWidget(detail: string): IMouseTargetContentWidget { + return MouseTarget.createContentWidget(this.target, this._getMouseColumn(), detail); + } + public fulfillScrollbar(position: Position): IMouseTargetScrollbar { + return MouseTarget.createScrollbar(this.target, this._getMouseColumn(position), position); + } + public fulfillOverlayWidget(detail: string): IMouseTargetOverlayWidget { + return MouseTarget.createOverlayWidget(this.target, this._getMouseColumn(), detail); } public withTarget(target: Element | null): HitTestRequest { @@ -447,9 +454,9 @@ interface ResolvedHitTestRequest extends HitTestRequest { readonly target: Element; } -const EMPTY_CONTENT_AFTER_LINES: IEmptyContentData = { isAfterLines: true }; +const EMPTY_CONTENT_AFTER_LINES: IMouseTargetContentEmptyData = { isAfterLines: true }; -function createEmptyContentDataInLines(horizontalDistanceToText: number): IEmptyContentData { +function createEmptyContentDataInLines(horizontalDistanceToText: number): IMouseTargetContentEmptyData { return { isAfterLines: false, horizontalDistanceToText: horizontalDistanceToText @@ -488,15 +495,15 @@ export class MouseTargetFactory { const request = new HitTestRequest(ctx, editorPos, pos, relativePos, target); try { const r = MouseTargetFactory._createMouseTarget(ctx, request, false); - // console.log(r.toString()); + // console.log(MouseTarget.toString(r)); return r; } catch (err) { // console.log(err); - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } } - private static _createMouseTarget(ctx: HitTestContext, request: HitTestRequest, domHitTestExecuted: boolean): MouseTarget { + private static _createMouseTarget(ctx: HitTestContext, request: HitTestRequest, domHitTestExecuted: boolean): IMouseTarget { // console.log(`${domHitTestExecuted ? '=>' : ''}CAME IN REQUEST: ${request}`); @@ -504,7 +511,7 @@ export class MouseTargetFactory { if (request.target === null) { if (domHitTestExecuted) { // Still no target... and we have already executed hit test... - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } const hitTestResult = MouseTargetFactory._doHitTest(ctx, request); @@ -519,7 +526,7 @@ export class MouseTargetFactory { // we know for a fact that request.target is not null const resolvedRequest = request; - let result: MouseTarget | null = null; + let result: IMouseTarget | null = null; result = result || MouseTargetFactory._hitTestContentWidget(ctx, resolvedRequest); result = result || MouseTargetFactory._hitTestOverlayWidget(ctx, resolvedRequest); @@ -532,36 +539,36 @@ export class MouseTargetFactory { result = result || MouseTargetFactory._hitTestViewLines(ctx, resolvedRequest, domHitTestExecuted); result = result || MouseTargetFactory._hitTestScrollbar(ctx, resolvedRequest); - return (result || request.fulfill(MouseTargetType.UNKNOWN)); + return (result || request.fulfillUnknown()); } - private static _hitTestContentWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestContentWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { // Is it a content widget? if (ElementPath.isChildOfContentWidgets(request.targetPath) || ElementPath.isChildOfOverflowingContentWidgets(request.targetPath)) { const widgetId = ctx.findAttribute(request.target, 'widgetId'); if (widgetId) { - return request.fulfill(MouseTargetType.CONTENT_WIDGET, null, null, widgetId); + return request.fulfillContentWidget(widgetId); } else { - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } } return null; } - private static _hitTestOverlayWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestOverlayWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { // Is it an overlay widget? if (ElementPath.isChildOfOverlayWidgets(request.targetPath)) { const widgetId = ctx.findAttribute(request.target, 'widgetId'); if (widgetId) { - return request.fulfill(MouseTargetType.OVERLAY_WIDGET, null, null, widgetId); + return request.fulfillOverlayWidget(widgetId); } else { - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } } return null; } - private static _hitTestViewCursor(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestViewCursor(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { if (request.target) { // Check if we've hit a painted cursor @@ -570,7 +577,7 @@ export class MouseTargetFactory { for (const d of lastViewCursorsRenderData) { if (request.target === d.domNode) { - return request.fulfill(MouseTargetType.CONTENT_TEXT, d.position, null, { mightBeForeignElement: false, injectedText: null }); + return request.fulfillContentText(d.position, null, { mightBeForeignElement: false, injectedText: null }); } } } @@ -602,7 +609,7 @@ export class MouseTargetFactory { cursorVerticalOffset <= mouseVerticalOffset && mouseVerticalOffset <= cursorVerticalOffset + d.height ) { - return request.fulfill(MouseTargetType.CONTENT_TEXT, d.position, null, { mightBeForeignElement: false, injectedText: null }); + return request.fulfillContentText(d.position, null, { mightBeForeignElement: false, injectedText: null }); } } } @@ -610,33 +617,33 @@ export class MouseTargetFactory { return null; } - private static _hitTestViewZone(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestViewZone(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { const viewZoneData = ctx.getZoneAtCoord(request.mouseVerticalOffset); if (viewZoneData) { const mouseTargetType = (request.isInContentArea ? MouseTargetType.CONTENT_VIEW_ZONE : MouseTargetType.GUTTER_VIEW_ZONE); - return request.fulfill(mouseTargetType, viewZoneData.position, null, viewZoneData); + return request.fulfillViewZone(mouseTargetType, viewZoneData.position, viewZoneData); } return null; } - private static _hitTestTextArea(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestTextArea(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { // Is it the textarea? if (ElementPath.isTextArea(request.targetPath)) { if (ctx.lastRenderData.lastTextareaPosition) { - return request.fulfill(MouseTargetType.CONTENT_TEXT, ctx.lastRenderData.lastTextareaPosition, null, { mightBeForeignElement: false, injectedText: null }); + return request.fulfillContentText(ctx.lastRenderData.lastTextareaPosition, null, { mightBeForeignElement: false, injectedText: null }); } - return request.fulfill(MouseTargetType.TEXTAREA, ctx.lastRenderData.lastTextareaPosition); + return request.fulfillTextarea(); } return null; } - private static _hitTestMargin(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestMargin(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { if (request.isInMarginArea) { const res = ctx.getFullLineRangeAtCoord(request.mouseVerticalOffset); const pos = res.range.getStartPosition(); let offset = Math.abs(request.relativePos.x); - const detail: IMarginData = { + const detail: IMouseTargetMarginData = { isAfterLines: res.isAfterLines, glyphMarginLeft: ctx.layoutInfo.glyphMarginLeft, glyphMarginWidth: ctx.layoutInfo.glyphMarginWidth, @@ -648,29 +655,29 @@ export class MouseTargetFactory { if (offset <= ctx.layoutInfo.glyphMarginWidth) { // On the glyph margin - return request.fulfill(MouseTargetType.GUTTER_GLYPH_MARGIN, pos, res.range, detail); + return request.fulfillMargin(MouseTargetType.GUTTER_GLYPH_MARGIN, pos, res.range, detail); } offset -= ctx.layoutInfo.glyphMarginWidth; if (offset <= ctx.layoutInfo.lineNumbersWidth) { // On the line numbers - return request.fulfill(MouseTargetType.GUTTER_LINE_NUMBERS, pos, res.range, detail); + return request.fulfillMargin(MouseTargetType.GUTTER_LINE_NUMBERS, pos, res.range, detail); } offset -= ctx.layoutInfo.lineNumbersWidth; // On the line decorations - return request.fulfill(MouseTargetType.GUTTER_LINE_DECORATIONS, pos, res.range, detail); + return request.fulfillMargin(MouseTargetType.GUTTER_LINE_DECORATIONS, pos, res.range, detail); } return null; } - private static _hitTestViewLines(ctx: HitTestContext, request: ResolvedHitTestRequest, domHitTestExecuted: boolean): MouseTarget | null { + private static _hitTestViewLines(ctx: HitTestContext, request: ResolvedHitTestRequest, domHitTestExecuted: boolean): IMouseTarget | null { if (!ElementPath.isChildOfViewLines(request.targetPath)) { return null; } if (ctx.isInTopPadding(request.mouseVerticalOffset)) { - return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(1, 1), null, EMPTY_CONTENT_AFTER_LINES); + return request.fulfillContentEmpty(new Position(1, 1), EMPTY_CONTENT_AFTER_LINES); } // Check if it is below any lines and any view zones @@ -678,7 +685,7 @@ export class MouseTargetFactory { // This most likely indicates it happened after the last view-line const lineCount = ctx.model.getLineCount(); const maxLineColumn = ctx.model.getLineMaxColumn(lineCount); - return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineCount, maxLineColumn), null, EMPTY_CONTENT_AFTER_LINES); + return request.fulfillContentEmpty(new Position(lineCount, maxLineColumn), EMPTY_CONTENT_AFTER_LINES); } if (domHitTestExecuted) { @@ -689,19 +696,19 @@ export class MouseTargetFactory { if (ctx.model.getLineLength(lineNumber) === 0) { const lineWidth = ctx.getLineWidth(lineNumber); const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); - return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineNumber, 1), null, detail); + return request.fulfillContentEmpty(new Position(lineNumber, 1), detail); } const lineWidth = ctx.getLineWidth(lineNumber); if (request.mouseContentHorizontalOffset >= lineWidth) { const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); const pos = new Position(lineNumber, ctx.model.getLineMaxColumn(lineNumber)); - return request.fulfill(MouseTargetType.CONTENT_EMPTY, pos, null, detail); + return request.fulfillContentEmpty(pos, detail); } } // We have already executed hit test... - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } const hitTestResult = MouseTargetFactory._doHitTest(ctx, request); @@ -713,36 +720,36 @@ export class MouseTargetFactory { return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true); } - private static _hitTestMinimap(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestMinimap(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { if (ElementPath.isChildOfMinimap(request.targetPath)) { const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); - return request.fulfill(MouseTargetType.SCROLLBAR, new Position(possibleLineNumber, maxColumn)); + return request.fulfillScrollbar(new Position(possibleLineNumber, maxColumn)); } return null; } - private static _hitTestScrollbarSlider(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestScrollbarSlider(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { if (ElementPath.isChildOfScrollableElement(request.targetPath)) { if (request.target && request.target.nodeType === 1) { const className = request.target.className; if (className && /\b(slider|scrollbar)\b/.test(className)) { const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); - return request.fulfill(MouseTargetType.SCROLLBAR, new Position(possibleLineNumber, maxColumn)); + return request.fulfillScrollbar(new Position(possibleLineNumber, maxColumn)); } } } return null; } - private static _hitTestScrollbar(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestScrollbar(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { // Is it the overview ruler? // Is it a child of the scrollable element? if (ElementPath.isChildOfScrollableElement(request.targetPath)) { const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); - return request.fulfill(MouseTargetType.SCROLLBAR, new Position(possibleLineNumber, maxColumn)); + return request.fulfillScrollbar(new Position(possibleLineNumber, maxColumn)); } return null; @@ -763,7 +770,7 @@ export class MouseTargetFactory { return (chars + 1); } - private static createMouseTargetFromHitTestPosition(ctx: HitTestContext, request: HitTestRequest, spanNode: HTMLElement, pos: Position, injectedText: InjectedText | null): MouseTarget { + private static createMouseTargetFromHitTestPosition(ctx: HitTestContext, request: HitTestRequest, spanNode: HTMLElement, pos: Position, injectedText: InjectedText | null): IMouseTarget { const lineNumber = pos.lineNumber; const column = pos.column; @@ -771,19 +778,19 @@ export class MouseTargetFactory { if (request.mouseContentHorizontalOffset > lineWidth) { const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); - return request.fulfill(MouseTargetType.CONTENT_EMPTY, pos, null, detail); + return request.fulfillContentEmpty(pos, detail); } const visibleRange = ctx.visibleRangeForPosition(lineNumber, column); if (!visibleRange) { - return request.fulfill(MouseTargetType.UNKNOWN, pos); + return request.fulfillUnknown(pos); } const columnHorizontalOffset = visibleRange.left; if (request.mouseContentHorizontalOffset === columnHorizontalOffset) { - return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, null, { mightBeForeignElement: !!injectedText, injectedText }); + return request.fulfillContentText(pos, null, { mightBeForeignElement: !!injectedText, injectedText }); } // Let's define a, b, c and check if the offset is in between them... @@ -816,10 +823,10 @@ export class MouseTargetFactory { const curr = points[i]; if (prev.offset <= request.mouseContentHorizontalOffset && request.mouseContentHorizontalOffset <= curr.offset) { const rng = new EditorRange(lineNumber, prev.column, lineNumber, curr.column); - return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, rng, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText }); + return request.fulfillContentText(pos, rng, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText }); } } - return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, null, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText }); + return request.fulfillContentText(pos, null, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText }); } /** diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 8733ada2f71..f606fefe00b 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -19,6 +19,7 @@ import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IDiffComputationResult } from 'vs/editor/common/services/editorWorker'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { InjectedText } from 'vs/editor/common/viewModel/modelLineProjectionData'; /** * A view zone is a full horizontal rectangle that 'pushes' text down. @@ -280,19 +281,11 @@ export const enum MouseTargetType { */ OUTSIDE_EDITOR, } - -/** - * Target hit with the mouse in the editor. - */ -export interface IMouseTarget { +export interface IBaseMouseTarget { /** * The target element */ readonly element: Element | null; - /** - * The target type - */ - readonly type: MouseTargetType; /** * The 'approximate' editor position */ @@ -305,11 +298,103 @@ export interface IMouseTarget { * The 'approximate' editor range */ readonly range: Range | null; - /** - * Some extra detail. - */ - readonly detail: any; } +export interface IMouseTargetUnknown extends IBaseMouseTarget { + readonly type: MouseTargetType.UNKNOWN; +} +export interface IMouseTargetTextarea extends IBaseMouseTarget { + readonly type: MouseTargetType.TEXTAREA; + readonly position: null; + readonly range: null; +} +export interface IMouseTargetMarginData { + readonly isAfterLines: boolean; + readonly glyphMarginLeft: number; + readonly glyphMarginWidth: number; + readonly lineNumbersWidth: number; + readonly offsetX: number; +} +export interface IMouseTargetMargin extends IBaseMouseTarget { + readonly type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS; + readonly position: Position; + readonly range: Range; + readonly detail: IMouseTargetMarginData; +} +export interface IMouseTargetViewZoneData { + readonly viewZoneId: string; + readonly positionBefore: Position | null; + readonly positionAfter: Position | null; + readonly position: Position; + readonly afterLineNumber: number; +} +export interface IMouseTargetViewZone extends IBaseMouseTarget { + readonly type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE; + readonly position: Position; + readonly range: Range; + readonly detail: IMouseTargetViewZoneData; +} +export interface IMouseTargetContentTextData { + readonly mightBeForeignElement: boolean; + /** + * @internal + */ + readonly injectedText: InjectedText | null; +} +export interface IMouseTargetContentText extends IBaseMouseTarget { + readonly type: MouseTargetType.CONTENT_TEXT; + readonly position: Position; + readonly range: Range; + readonly detail: IMouseTargetContentTextData; +} +export interface IMouseTargetContentEmptyData { + readonly isAfterLines: boolean; + readonly horizontalDistanceToText?: number; +} +export interface IMouseTargetContentEmpty extends IBaseMouseTarget { + readonly type: MouseTargetType.CONTENT_EMPTY; + readonly position: Position; + readonly range: Range; + readonly detail: IMouseTargetContentEmptyData; +} +export interface IMouseTargetContentWidget extends IBaseMouseTarget { + readonly type: MouseTargetType.CONTENT_WIDGET; + readonly position: null; + readonly range: null; + readonly detail: string; +} +export interface IMouseTargetOverlayWidget extends IBaseMouseTarget { + readonly type: MouseTargetType.OVERLAY_WIDGET; + readonly position: null; + readonly range: null; + readonly detail: string; +} +export interface IMouseTargetScrollbar extends IBaseMouseTarget { + readonly type: MouseTargetType.SCROLLBAR; + readonly position: Position; + readonly range: Range; +} +export interface IMouseTargetOverviewRuler extends IBaseMouseTarget { + readonly type: MouseTargetType.OVERVIEW_RULER; +} +export interface IMouseTargetOutsideEditor extends IBaseMouseTarget { + readonly type: MouseTargetType.OUTSIDE_EDITOR; +} +/** + * Target hit with the mouse in the editor. + */ +export type IMouseTarget = ( + IMouseTargetUnknown + | IMouseTargetTextarea + | IMouseTargetMargin + | IMouseTargetViewZone + | IMouseTargetContentText + | IMouseTargetContentEmpty + | IMouseTargetContentWidget + | IMouseTargetOverlayWidget + | IMouseTargetScrollbar + | IMouseTargetOverviewRuler + | IMouseTargetOutsideEditor +); /** * A mouse event originating from the editor. */ diff --git a/src/vs/editor/browser/view/viewUserInputEvents.ts b/src/vs/editor/browser/view/viewUserInputEvents.ts index 2a58f89d578..03d7e4ac35c 100644 --- a/src/vs/editor/browser/view/viewUserInputEvents.ts +++ b/src/vs/editor/browser/view/viewUserInputEvents.ts @@ -4,10 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { MouseTarget } from 'vs/editor/browser/controller/mouseTarget'; -import { IEditorMouseEvent, IMouseTarget, IPartialEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { IEditorMouseEvent, IMouseTarget, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; @@ -118,36 +115,13 @@ export class ViewUserInputEvents { } public static convertViewToModelMouseTarget(target: IMouseTarget, coordinatesConverter: ICoordinatesConverter): IMouseTarget { - return new ExternalMouseTarget( - target.element, - target.type, - target.mouseColumn, - target.position ? coordinatesConverter.convertViewPositionToModelPosition(target.position) : null, - target.range ? coordinatesConverter.convertViewRangeToModelRange(target.range) : null, - target.detail - ); - } -} - -class ExternalMouseTarget implements IMouseTarget { - - public readonly element: Element | null; - public readonly type: MouseTargetType; - public readonly mouseColumn: number; - public readonly position: Position | null; - public readonly range: Range | null; - public readonly detail: any; - - constructor(element: Element | null, type: MouseTargetType, mouseColumn: number, position: Position | null, range: Range | null, detail: any) { - this.element = element; - this.type = type; - this.mouseColumn = mouseColumn; - this.position = position; - this.range = range; - this.detail = detail; - } - - public toString(): string { - return MouseTarget.toString(this); + const result = { ...target }; + if (result.position) { + result.position = coordinatesConverter.convertViewPositionToModelPosition(result.position); + } + if (result.range) { + result.range = coordinatesConverter.convertViewRangeToModelRange(result.range); + } + return result; } } diff --git a/src/vs/editor/contrib/colorPicker/colorContributions.ts b/src/vs/editor/contrib/colorPicker/colorContributions.ts index 08671345624..7d1f5b8d2e9 100644 --- a/src/vs/editor/contrib/colorPicker/colorContributions.ts +++ b/src/vs/editor/contrib/colorPicker/colorContributions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { ITextContentData } from 'vs/editor/browser/controller/mouseTarget'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; @@ -30,22 +29,21 @@ export class ColorContribution extends Disposable implements IEditorContribution } private onMouseDown(mouseEvent: IEditorMouseEvent) { - const targetType = mouseEvent.target.type; + const target = mouseEvent.target; - if (targetType !== MouseTargetType.CONTENT_TEXT) { + if (target.type !== MouseTargetType.CONTENT_TEXT) { return; } - const detail = (mouseEvent.target.detail); - if (!detail || !detail.injectedText) { + if (!target.detail.injectedText) { return; } - if (detail.injectedText.options.attachedData !== ColorDecorationInjectedTextMarker) { + if (target.detail.injectedText.options.attachedData !== ColorDecorationInjectedTextMarker) { return; } - if (!mouseEvent.target.range) { + if (!target.range) { return; } @@ -54,7 +52,7 @@ export class ColorContribution extends Disposable implements IEditorContribution return; } if (!hoverController.isColorPickerVisible()) { - const range = new Range(mouseEvent.target.range.startLineNumber, mouseEvent.target.range.startColumn + 1, mouseEvent.target.range.endLineNumber, mouseEvent.target.range.endColumn + 1); + const range = new Range(target.range.startLineNumber, target.range.startColumn + 1, target.range.endLineNumber, target.range.endColumn + 1); hoverController.showContentHover(range, HoverStartMode.Immediate, false); } } diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index 5f4a6e28cf1..00cb71fef2b 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -11,7 +11,6 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; import * as types from 'vs/base/common/types'; import 'vs/css!./folding'; -import { IEmptyContentData, IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorAction, registerEditorAction, registerEditorContribution, registerInstantiatedEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; @@ -397,7 +396,7 @@ export class FoldingController extends Disposable implements IEditorContribution let iconClicked = false; switch (e.target.type) { case MouseTargetType.GUTTER_LINE_DECORATIONS: { - const data = e.target.detail as IMarginData; + const data = e.target.detail; const offsetLeftInGutter = (e.target.element as HTMLElement).offsetLeft; const gutterOffsetX = data.offsetX - offsetLeftInGutter; @@ -413,7 +412,7 @@ export class FoldingController extends Disposable implements IEditorContribution } case MouseTargetType.CONTENT_EMPTY: { if (this._unfoldOnClickAfterEndOfLine && this.hiddenRangeModel.hasRanges()) { - const data = e.target.detail as IEmptyContentData; + const data = e.target.detail; if (!data.isAfterLines) { break; } diff --git a/src/vs/editor/contrib/hover/contentHover.ts b/src/vs/editor/contrib/hover/contentHover.ts index 698c5fd1d98..3c255f0de42 100644 --- a/src/vs/editor/contrib/hover/contentHover.ts +++ b/src/vs/editor/contrib/hover/contentHover.ts @@ -10,7 +10,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Constants } from 'vs/base/common/uint'; -import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; @@ -94,14 +93,14 @@ export class ContentHoverController extends Disposable { } private _shouldShowAt(mouseEvent: IEditorMouseEvent): boolean { - const targetType = mouseEvent.target.type; - if (targetType === MouseTargetType.CONTENT_TEXT) { + const target = mouseEvent.target; + if (target.type === MouseTargetType.CONTENT_TEXT) { return true; } - if (targetType === MouseTargetType.CONTENT_EMPTY) { + if (target.type === MouseTargetType.CONTENT_EMPTY) { const epsilon = this._editor.getOption(EditorOption.fontInfo).typicalHalfwidthCharacterWidth / 2; - const data = mouseEvent.target.detail; + const data = target.detail; if (data && !data.isAfterLines && typeof data.horizontalDistanceToText === 'number' && data.horizontalDistanceToText < epsilon) { // Let hover kick in even when the mouse is technically in the empty area after a line, given the distance is small enough return true; diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 56c389e947c..425626de805 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -100,20 +100,20 @@ export class ModesHoverController implements IEditorContribution { private _onEditorMouseDown(mouseEvent: IEditorMouseEvent): void { this._isMouseDown = true; - const targetType = mouseEvent.target.type; + const target = mouseEvent.target; - if (targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ContentHoverWidget.ID) { + if (target.type === MouseTargetType.CONTENT_WIDGET && target.detail === ContentHoverWidget.ID) { this._hoverClicked = true; // mouse down on top of content hover widget return; } - if (targetType === MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail === MarginHoverWidget.ID) { + if (target.type === MouseTargetType.OVERLAY_WIDGET && target.detail === MarginHoverWidget.ID) { // mouse down on top of overlay hover widget return; } - if (targetType !== MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail !== MarginHoverWidget.ID) { + if (target.type !== MouseTargetType.OVERLAY_WIDGET) { this._hoverClicked = false; } @@ -125,13 +125,13 @@ export class ModesHoverController implements IEditorContribution { } private _onEditorMouseMove(mouseEvent: IEditorMouseEvent): void { - let targetType = mouseEvent.target.type; + const target = mouseEvent.target; if (this._isMouseDown && this._hoverClicked) { return; } - if (this._isHoverSticky && targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ContentHoverWidget.ID) { + if (this._isHoverSticky && target.type === MouseTargetType.CONTENT_WIDGET && target.detail === ContentHoverWidget.ID) { // mouse moved on top of content hover widget return; } @@ -142,14 +142,14 @@ export class ModesHoverController implements IEditorContribution { } if ( - !this._isHoverSticky && targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ContentHoverWidget.ID + !this._isHoverSticky && target.type === MouseTargetType.CONTENT_WIDGET && target.detail === ContentHoverWidget.ID && this._contentWidget?.isColorPickerVisible() ) { // though the hover is not sticky, the color picker needs to. return; } - if (this._isHoverSticky && targetType === MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail === MarginHoverWidget.ID) { + if (this._isHoverSticky && target.type === MouseTargetType.OVERLAY_WIDGET && target.detail === MarginHoverWidget.ID) { // mouse moved on top of overlay hover widget return; } @@ -165,12 +165,12 @@ export class ModesHoverController implements IEditorContribution { return; } - if (targetType === MouseTargetType.GUTTER_GLYPH_MARGIN && mouseEvent.target.position) { + if (target.type === MouseTargetType.GUTTER_GLYPH_MARGIN && target.position) { this._contentWidget?.hide(); if (!this._glyphWidget) { this._glyphWidget = new MarginHoverWidget(this._editor, this._languageService, this._openerService); } - this._glyphWidget.startShowingAt(mouseEvent.target.position.lineNumber); + this._glyphWidget.startShowingAt(target.position.lineNumber); return; } diff --git a/src/vs/editor/contrib/inlayHints/inlayHintsController.ts b/src/vs/editor/contrib/inlayHints/inlayHintsController.ts index b712330af11..2bcd9945105 100644 --- a/src/vs/editor/contrib/inlayHints/inlayHintsController.ts +++ b/src/vs/editor/contrib/inlayHints/inlayHintsController.ts @@ -150,12 +150,12 @@ export class InlayHintsController implements IEditorContribution { gesture.onMouseMoveOrRelevantKeyDown(e => { const [mouseEvent] = e; - if (mouseEvent.target.type !== MouseTargetType.CONTENT_TEXT || typeof mouseEvent.target.detail !== 'object' || !mouseEvent.hasTriggerModifier) { + if (mouseEvent.target.type !== MouseTargetType.CONTENT_TEXT || !mouseEvent.hasTriggerModifier) { removeHighlight(); return; } const model = this._editor.getModel()!; - const options = mouseEvent.target.detail?.injectedText?.options; + const options = mouseEvent.target.detail.injectedText?.options; if (options instanceof ModelDecorationInjectedTextOptions && options.attachedData instanceof InlayHintLabelPart && options.attachedData.href) { this._activeInlayHintPart = options.attachedData; @@ -176,10 +176,10 @@ export class InlayHintsController implements IEditorContribution { }); gesture.onCancel(removeHighlight); gesture.onExecute(e => { - if (e.target.type !== MouseTargetType.CONTENT_TEXT || typeof e.target.detail !== 'object' || !e.hasTriggerModifier) { + if (e.target.type !== MouseTargetType.CONTENT_TEXT || !e.hasTriggerModifier) { return; } - const options = e.target.detail?.injectedText?.options; + const options = e.target.detail.injectedText?.options; if (options instanceof ModelDecorationInjectedTextOptions && options.attachedData instanceof InlayHintLabelPart && options.attachedData.href) { this._openerService.open(options.attachedData.href, { allowCommands: true, openToSide: e.hasSideBySideModifier }); } diff --git a/src/vs/editor/contrib/inlayHints/inlayHintsHover.ts b/src/vs/editor/contrib/inlayHints/inlayHintsHover.ts index cc3ff5c3945..583c3773a94 100644 --- a/src/vs/editor/contrib/inlayHints/inlayHintsHover.ts +++ b/src/vs/editor/contrib/inlayHints/inlayHintsHover.ts @@ -29,10 +29,10 @@ export class InlayHintsHover extends MarkdownHoverParticipant { if (!controller) { return null; } - if (mouseEvent.target.type !== MouseTargetType.CONTENT_TEXT || typeof mouseEvent.target.detail !== 'object') { + if (mouseEvent.target.type !== MouseTargetType.CONTENT_TEXT) { return null; } - const options = mouseEvent.target.detail?.injectedText?.options; + const options = mouseEvent.target.detail.injectedText?.options; if (!(options instanceof ModelDecorationInjectedTextOptions && options.attachedData instanceof InlayHintLabelPart)) { return null; } diff --git a/src/vs/editor/contrib/inlineCompletions/inlineCompletionsHoverParticipant.ts b/src/vs/editor/contrib/inlineCompletions/inlineCompletionsHoverParticipant.ts index 2191515b9aa..ff6652b8f12 100644 --- a/src/vs/editor/contrib/inlineCompletions/inlineCompletionsHoverParticipant.ts +++ b/src/vs/editor/contrib/inlineCompletions/inlineCompletionsHoverParticipant.ts @@ -6,7 +6,6 @@ import * as dom from 'vs/base/browser/dom'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { ITextContentData, IViewZoneData } from 'vs/editor/browser/controller/mouseTarget'; import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; @@ -57,24 +56,25 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan if (!controller) { return null; } - if (mouseEvent.target.type === MouseTargetType.CONTENT_VIEW_ZONE) { + const target = mouseEvent.target; + if (target.type === MouseTargetType.CONTENT_VIEW_ZONE) { // handle the case where the mouse is over the view zone - const viewZoneData = mouseEvent.target.detail; + const viewZoneData = target.detail; if (controller.shouldShowHoverAtViewZone(viewZoneData.viewZoneId)) { return new HoverForeignElementAnchor(1000, this, Range.fromPositions(viewZoneData.positionBefore || viewZoneData.position, viewZoneData.positionBefore || viewZoneData.position)); } } - if (mouseEvent.target.type === MouseTargetType.CONTENT_EMPTY && mouseEvent.target.range) { + if (target.type === MouseTargetType.CONTENT_EMPTY) { // handle the case where the mouse is over the empty portion of a line following ghost text - if (controller.shouldShowHoverAt(mouseEvent.target.range)) { - return new HoverForeignElementAnchor(1000, this, mouseEvent.target.range); + if (controller.shouldShowHoverAt(target.range)) { + return new HoverForeignElementAnchor(1000, this, target.range); } } - if (mouseEvent.target.type === MouseTargetType.CONTENT_TEXT && mouseEvent.target.range && mouseEvent.target.detail) { + if (target.type === MouseTargetType.CONTENT_TEXT) { // handle the case where the mouse is directly over ghost text - const mightBeForeignElement = (mouseEvent.target.detail).mightBeForeignElement; - if (mightBeForeignElement && controller.shouldShowHoverAt(mouseEvent.target.range)) { - return new HoverForeignElementAnchor(1000, this, mouseEvent.target.range); + const mightBeForeignElement = target.detail.mightBeForeignElement; + if (mightBeForeignElement && controller.shouldShowHoverAt(target.range)) { + return new HoverForeignElementAnchor(1000, this, target.range); } } return null; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 8b2707af5f2..b53a6c8b645 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4682,18 +4682,11 @@ declare namespace monaco.editor { OUTSIDE_EDITOR = 13 } - /** - * Target hit with the mouse in the editor. - */ - export interface IMouseTarget { + export interface IBaseMouseTarget { /** * The target element */ readonly element: Element | null; - /** - * The target type - */ - readonly type: MouseTargetType; /** * The 'approximate' editor position */ @@ -4706,12 +4699,104 @@ declare namespace monaco.editor { * The 'approximate' editor range */ readonly range: Range | null; - /** - * Some extra detail. - */ - readonly detail: any; } + export interface IMouseTargetUnknown extends IBaseMouseTarget { + readonly type: MouseTargetType.UNKNOWN; + } + + export interface IMouseTargetTextarea extends IBaseMouseTarget { + readonly type: MouseTargetType.TEXTAREA; + readonly position: null; + readonly range: null; + } + + export interface IMouseTargetMarginData { + readonly isAfterLines: boolean; + readonly glyphMarginLeft: number; + readonly glyphMarginWidth: number; + readonly lineNumbersWidth: number; + readonly offsetX: number; + } + + export interface IMouseTargetMargin extends IBaseMouseTarget { + readonly type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS; + readonly position: Position; + readonly range: Range; + readonly detail: IMouseTargetMarginData; + } + + export interface IMouseTargetViewZoneData { + readonly viewZoneId: string; + readonly positionBefore: Position | null; + readonly positionAfter: Position | null; + readonly position: Position; + readonly afterLineNumber: number; + } + + export interface IMouseTargetViewZone extends IBaseMouseTarget { + readonly type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE; + readonly position: Position; + readonly range: Range; + readonly detail: IMouseTargetViewZoneData; + } + + export interface IMouseTargetContentTextData { + readonly mightBeForeignElement: boolean; + } + + export interface IMouseTargetContentText extends IBaseMouseTarget { + readonly type: MouseTargetType.CONTENT_TEXT; + readonly position: Position; + readonly range: Range; + readonly detail: IMouseTargetContentTextData; + } + + export interface IMouseTargetContentEmptyData { + readonly isAfterLines: boolean; + readonly horizontalDistanceToText?: number; + } + + export interface IMouseTargetContentEmpty extends IBaseMouseTarget { + readonly type: MouseTargetType.CONTENT_EMPTY; + readonly position: Position; + readonly range: Range; + readonly detail: IMouseTargetContentEmptyData; + } + + export interface IMouseTargetContentWidget extends IBaseMouseTarget { + readonly type: MouseTargetType.CONTENT_WIDGET; + readonly position: null; + readonly range: null; + readonly detail: string; + } + + export interface IMouseTargetOverlayWidget extends IBaseMouseTarget { + readonly type: MouseTargetType.OVERLAY_WIDGET; + readonly position: null; + readonly range: null; + readonly detail: string; + } + + export interface IMouseTargetScrollbar extends IBaseMouseTarget { + readonly type: MouseTargetType.SCROLLBAR; + readonly position: Position; + readonly range: Range; + } + + export interface IMouseTargetOverviewRuler extends IBaseMouseTarget { + readonly type: MouseTargetType.OVERVIEW_RULER; + } + + export interface IMouseTargetOutsideEditor extends IBaseMouseTarget { + readonly type: MouseTargetType.OUTSIDE_EDITOR; + } + + /** + * Target hit with the mouse in the editor. + */ + export type IMouseTarget = (IMouseTargetUnknown | IMouseTargetTextarea | IMouseTargetMargin | IMouseTargetViewZone | IMouseTargetContentText | IMouseTargetContentEmpty | IMouseTargetContentWidget | IMouseTargetOverlayWidget | IMouseTargetScrollbar | IMouseTargetOverviewRuler | IMouseTargetOutsideEditor); + /** * A mouse event originating from the editor. */ diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index db1bc3f0159..9247d145ca0 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -13,7 +13,6 @@ import * as strings from 'vs/base/common/strings'; import { withNullAsUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -73,7 +72,7 @@ export function parseMouseDownInfoFromEvent(e: IEditorMouseEvent) { return null; } - const data = e.target.detail as IMarginData; + const data = e.target.detail; const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft; // don't collide with folding and git decorations diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index dc7123b4af8..5042a44eede 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -16,7 +16,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, BreakpointWidgetContext, IBreakpointEditorContribution, IBreakpointUpdateData, IDebugConfiguration, State, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; -import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -209,9 +208,8 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi return; } - const data = e.target.detail as IMarginData; const model = this.editor.getModel(); - if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { + if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.detail.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { return; } const canSetBreakpoints = this.debugService.canSetBreakpointsIn(model); @@ -296,7 +294,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi const model = this.editor.getModel(); if (model && e.target.position && (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) && this.debugService.canSetBreakpointsIn(model) && this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { - const data = e.target.detail as IMarginData; + const data = e.target.detail; if (!data.isAfterLines) { showBreakpointHintAtLineNumber = e.target.position.lineNumber; } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 2b42e4bebb5..555dd515457 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -409,16 +409,16 @@ export class DebugEditorContribution implements IDebugEditorContribution { return; } - const targetType = mouseEvent.target.type; + const target = mouseEvent.target; const stopKey = env.isMacintosh ? 'metaKey' : 'ctrlKey'; - if (targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === DebugHoverWidget.ID && !(mouseEvent.event)[stopKey]) { + if (target.type === MouseTargetType.CONTENT_WIDGET && target.detail === DebugHoverWidget.ID && !(mouseEvent.event)[stopKey]) { // mouse moved on top of debug hover widget return; } - if (targetType === MouseTargetType.CONTENT_TEXT) { - if (mouseEvent.target.range && !mouseEvent.target.range.equalsRange(this.hoverRange)) { - this.hoverRange = mouseEvent.target.range; + if (target.type === MouseTargetType.CONTENT_TEXT) { + if (target.range && !target.range.equalsRange(this.hoverRange)) { + this.hoverRange = target.range; this.hideHoverScheduler.cancel(); this.showHoverScheduler.schedule(); } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index 823d2b66b0b..eea6954dc1c 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -236,7 +236,7 @@ class EditSettingRenderer extends Disposable { private getEditPreferenceWidgetUnderMouse(mouseMoveEvent: IEditorMouseEvent): EditPreferenceWidget | undefined { if (mouseMoveEvent.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN) { - const line = mouseMoveEvent.target.position!.lineNumber; + const line = mouseMoveEvent.target.position.lineNumber; if (this.editPreferenceWidgetForMouseMove.getLine() === line && this.editPreferenceWidgetForMouseMove.isVisible()) { return this.editPreferenceWidgetForMouseMove; } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index e2b01420490..63168702652 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -17,7 +17,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; @@ -506,8 +505,7 @@ export class EditPreferenceWidget extends Disposable { super(); this._editPreferenceDecoration = []; this._register(this.editor.onMouseDown((e: IEditorMouseEvent) => { - const data = e.target.detail as IMarginData; - if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.isVisible()) { + if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.detail.isAfterLines || !this.isVisible()) { return; } this._onClick.fire(e); diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 4d55580fe1c..642ea42611a 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -42,7 +42,6 @@ import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/men import { IChange, IEditorModel, ScrollType, IEditorContribution, IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { OverviewRulerLane, ITextModel, IModelDecorationOptions, MinimapPosition } from 'vs/editor/common/model'; import { sortedDiff } from 'vs/base/common/arrays'; -import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ISplice } from 'vs/base/common/sequence'; import { createStyleSheet } from 'vs/base/browser/dom'; @@ -766,7 +765,7 @@ export class DirtyDiffController extends Disposable implements IEditorContributi return; } - const data = e.target.detail as IMarginData; + const data = e.target.detail; const offsetLeftInGutter = (e.target.element as HTMLElement).offsetLeft; const gutterOffsetX = data.offsetX - offsetLeftInGutter; diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index f9fb536cf5e..8e6c2c8e147 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -680,7 +680,7 @@ abstract class RunTestDecoration { /** * Called when the decoration is clicked on. */ - protected abstract getContextMenuActions(e: IEditorMouseEvent): IReference; + protected abstract getContextMenuActions(): IReference; protected defaultRun() { return this.testService.runTests({ @@ -697,7 +697,7 @@ abstract class RunTestDecoration { } private showContextMenu(e: IEditorMouseEvent) { - let actions = this.getContextMenuActions(e); + let actions = this.getContextMenuActions(); const editor = this.codeEditorService.listCodeEditors().find(e => e.getModel() === this.model); if (editor) { const contribution = editor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID); @@ -844,7 +844,7 @@ class RunSingleTestDecoration extends RunTestDecoration implements ITestDecorati super([{ test, resultItem }], visible, model, codeEditorService, testService, contextMenuService, commandService, configurationService, testProfiles, contextKeyService, menuService); } - protected override getContextMenuActions(e: IEditorMouseEvent) { + protected override getContextMenuActions() { return this.getTestContextMenuActions(this.tests[0].test, this.tests[0].resultItem); } }