Change:
<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes/No <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: 1.88.0-insider in Safari OS Version: macOS Steps to Reproduce: Safari open a file with: to the right of any the cursor goes to the beginning of the line not the end This does not repro in stable
I didn't do any changes the last couple of milestones that could (easily) explain this. Can you reproduce this in the monaco editor? If so, can you do a bisect?
I bisected it to this commit: (the commit before it) does not repro the issue. This wasn't in Monaco editor, I just used run code web:
This commit is just a rename refactoring. It is very unlikely it causes this... This does not reproduce in chrome, right?
The bug does not reproduce in Edge on macOS, but it repros in Safari.
Unfortunately, I don't have access to Safari. Can you double check that this is caused by that commit? Can you locally rename the class back to the original name and see if it still happens? It does not make any sense that this commit would cause this behavior only on Safari.
Ok and I paired a bit on this and are really stumped. Bad news: That commit I called out seems to be a red herring. Good news: we can repro in the Monaco Editor Playground. Here's a side by side of two versions of the dev build: <img width="1680" alt="image" src=""You can see when I click to the right of a line, the older (working) build has the correct position here, while the newer broken build has column 1. Here are the links to these versions: Working: Broken: I believe this is out of both of our depths at this point so I'm passing to you This is a regression so I think it should be fixed before we release (assuming I'm not the only one to repro)
Did a bisect using and it points to
In that case, it is most likely .

Discussion:
import { PositionAffinity } from 'vs/editor/common/model'; import { InjectedText } from 'vs/editor/common/modelLineProjectionData'; import { Mutable } from 'vs/base/common/types'; <ins> import { Lazy } from 'vs/base/common/lazy'; </ins> const enum HitTestResultType { Unknown,

class ContentHitTestResult { readonly type = HitTestResultType.Content; <ins> get hitTarget(): HTMLElement { return this.spanNode; } </ins> constructor( readonly position: Position, readonly spanNode: HTMLElement,

class HitTestRequest extends BareHitTestRequest { private readonly _ctx: HitTestContext; <del> public readonly target: HTMLElement | null; public readonly targetPath: Uint8Array; </del> <ins> private readonly _eventTarget: HTMLElement | null; public readonly hitTestResult = new Lazy(() => MouseTargetFactory.doHitTest(this._ctx, this)); private _useHitTestTarget: boolean; private _targetPathCacheElement: HTMLElement | null = null; private _targetPathCacheValue: Uint8Array = new Uint8Array(0); public get target(): HTMLElement | null { if (this._useHitTestTarget) { return this.hitTestResult.value.hitTarget; } return this._eventTarget; } </ins> <del> constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, target: HTMLElement | null) { </del> <ins> public get targetPath(): Uint8Array { if (this._targetPathCacheElement !== this.target) { this._targetPathCacheElement = this.target; this._targetPathCacheValue = PartFingerprints.collect(this.target, this._ctx.viewDomNode); } return this._targetPathCacheValue; } constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, eventTarget: HTMLElement | null) { </ins> super(ctx, editorPos, pos, relativePos); this._ctx = ctx; <ins> this._eventTarget = eventTarget; </ins> <del> if (target) { this.target = target; this.targetPath = PartFingerprints.collect(target, ctx.viewDomNode); } else { this.target = null; this.targetPath = new Uint8Array(0); } </del> <ins> // If no event target is passed in, we will use the hit test target const hasEventTarget = Boolean(this._eventTarget); this._useHitTestTarget = !hasEventTarget; </ins> } public override toString(): string { 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}nttarget: ${this.target ? (<HTMLElement>this.target).outerHTML : null}`; } <ins> public get wouldBenefitFromHitTestTargetSwitch(): boolean { return ( !this._useHitTestTarget && this.hitTestResult.value.hitTarget !== null && this.target !== this.hitTestResult.value.hitTarget ); } public switchToHitTestTarget(): void { this._useHitTestTarget = true; } </ins> private _getMouseColumn(position: Position | null = null): number { if (position && position.column < this._ctx.viewModel.getLineMaxColumn(position.lineNumber)) { // Most likely, the line contains foreign decorations...

public fulfillOverlayWidget(detail: string): IMouseTargetOverlayWidget { return MouseTarget.createOverlayWidget(this.target, this._getMouseColumn(), detail); } <del> public withTarget(target: HTMLElement | null): HitTestRequest { return new HitTestRequest(this._ctx, this.editorPos, this.pos, this.relativePos, target); } </del> } interface ResolvedHitTestRequest extends HitTestRequest {

const ctx = new HitTestContext(this._context, this._viewHelper, lastRenderData); const request = new HitTestRequest(ctx, editorPos, pos, relativePos, target); try { <del> const r = MouseTargetFactory._createMouseTarget(ctx, request, false); </del> <ins> const r = MouseTargetFactory._createMouseTarget(ctx, request); </ins> if (r.type === MouseTargetType.CONTENT_TEXT) { // Snap to the nearest soft tab boundary if atomic soft tabs are enabled.

} } <del> private static _createMouseTarget(ctx: HitTestContext, request: HitTestRequest, domHitTestExecuted: boolean): IMouseTarget { </del> <ins> private static _createMouseTarget(ctx: HitTestContext, request: HitTestRequest): IMouseTarget { </ins> // console.log(`${domHitTestExecuted ? '=>' : ''}CAME IN REQUEST: ${request}`); <del> // First ensure the request has a target </del> if (request.target === null) { <del> if (domHitTestExecuted) { // Still no target... and we have already executed hit test... return request.fulfillUnknown(); } const hitTestResult = MouseTargetFactory._doHitTest(ctx, request); if (hitTestResult.type === HitTestResultType.Content) { return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.spanNode, hitTestResult.position, hitTestResult.injectedText); } return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true); </del> <ins> // No target return request.fulfillUnknown(); </ins> } // we know for a fact that request.target is not null

result = result || MouseTargetFactory._hitTestMargin(ctx, resolvedRequest); result = result || MouseTargetFactory._hitTestViewCursor(ctx, resolvedRequest); result = result || MouseTargetFactory._hitTestTextArea(ctx, resolvedRequest); <del> result = result || MouseTargetFactory._hitTestViewLines(ctx, resolvedRequest, domHitTestExecuted); </del> <ins> result = result || MouseTargetFactory._hitTestViewLines(ctx, resolvedRequest); </ins> result = result || MouseTargetFactory._hitTestScrollbar(ctx, resolvedRequest); return (result || request.fulfillUnknown());

return null; } <del> private static _hitTestViewLines(ctx: HitTestContext, request: ResolvedHitTestRequest, domHitTestExecuted: boolean): IMouseTarget | null { </del> <ins> private static _hitTestViewLines(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { </ins> if (!ElementPath.isChildOfViewLines(request.targetPath)) { return null; }

return request.fulfillContentEmpty(new Position(lineCount, maxLineColumn), EMPTY_CONTENT_AFTER_LINES); } <del> if (domHitTestExecuted) { // Check if we are hitting a view-line (can happen in the case of inline decorations on empty lines) // See https://github.com/microsoft/vscode/issues/46942 if (ElementPath.isStrictChildOfViewLines(request.targetPath)) { const lineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); if (ctx.viewModel.getLineLength(lineNumber) === 0) { const lineWidth = ctx.getLineWidth(lineNumber); const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); return request.fulfillContentEmpty(new Position(lineNumber, 1), detail); } </del> <ins> // Check if we are hitting a view-line (can happen in the case of inline decorations on empty lines) // See https://github.com/microsoft/vscode/issues/46942 if (ElementPath.isStrictChildOfViewLines(request.targetPath)) { const lineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); if (ctx.viewModel.getLineLength(lineNumber) === 0) { </ins> const lineWidth = ctx.getLineWidth(lineNumber); <del> if (request.mouseContentHorizontalOffset >= lineWidth) { const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); const pos = new Position(lineNumber, ctx.viewModel.getLineMaxColumn(lineNumber)); return request.fulfillContentEmpty(pos, detail); } </del> <ins> const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); return request.fulfillContentEmpty(new Position(lineNumber, 1), detail); </ins> } <del> // We have already executed hit test... return request.fulfillUnknown(); </del> <ins> const lineWidth = ctx.getLineWidth(lineNumber); if (request.mouseContentHorizontalOffset >= lineWidth) { // TODO: This is wrong for RTL const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); const pos = new Position(lineNumber, ctx.viewModel.getLineMaxColumn(lineNumber)); return request.fulfillContentEmpty(pos, detail); } </ins> } <del> const hitTestResult = MouseTargetFactory._doHitTest(ctx, request); </del> <ins> // Do the hit test (if not already done) const hitTestResult = request.hitTestResult.value; </ins> if (hitTestResult.type === HitTestResultType.Content) { return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.spanNode, hitTestResult.position, hitTestResult.injectedText); } <del> return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true); </del> <ins> // We didn't hit content... if (request.wouldBenefitFromHitTestTargetSwitch) { // We actually hit something different... Give it one last change by trying again with this new target request.switchToHitTestTarget(); return this._createMouseTarget(ctx, request); } // We have tried everything... return request.fulfillUnknown(); </ins> } private static _hitTestMinimap(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null {

return position; } <del> private static _doHitTest(ctx: HitTestContext, request: BareHitTestRequest): HitTestResult { </del> <ins> public static doHitTest(ctx: HitTestContext, request: BareHitTestRequest): HitTestResult { </ins> let result: HitTestResult = new UnknownHitTestResult(); if (typeof (<any>ctx.viewDomNode.ownerDocument).caretRangeFromPoint === 'function') {