Skip to content

Commit fe23048

Browse files
committed
fix: 修复当行内代码存在于本行 x0 处时点击行末尾光标位置异常的问题
1 parent 7b7d86b commit fe23048

3 files changed

Lines changed: 96 additions & 2 deletions

File tree

src/core/decorations/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,19 +487,25 @@ export function computeDecorations(
487487
decorations.push(
488488
Decoration.inline(region.from, region.from + hashEnd, {
489489
class: "milkup-syntax-hidden",
490+
contenteditable: "false",
491+
"aria-hidden": "true",
490492
})
491493
);
492494
} else {
493495
decorations.push(
494496
Decoration.inline(region.from, region.to, {
495497
class: "milkup-syntax-hidden",
498+
contenteditable: "false",
499+
"aria-hidden": "true",
496500
})
497501
);
498502
}
499503
} else {
500504
decorations.push(
501505
Decoration.inline(region.from, region.to, {
502506
class: "milkup-syntax-hidden",
507+
contenteditable: "false",
508+
"aria-hidden": "true",
503509
})
504510
);
505511
}

src/core/editor.ts

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ import {
6161
createTaskListNodeView,
6262
createTaskItemNodeView,
6363
} from "./nodeviews/list";
64-
import { toggleSourceView, setSourceView, decorationPluginKey } from "./decorations";
64+
import {
65+
findSyntaxMarkerRegions,
66+
toggleSourceView,
67+
setSourceView,
68+
decorationPluginKey,
69+
} from "./decorations";
6570
import type { MilkupConfig, MilkupEditor as IMilkupEditor, MilkupPlugin } from "./types";
6671
import {
6772
insertTable,
@@ -133,6 +138,10 @@ export class MilkupEditor implements IMilkupEditor {
133138
private searchInSelection = false;
134139
private searchSelectionRange: { from: number; to: number } | null = null;
135140
private containerKeydownHandler: ((e: KeyboardEvent) => void) | null = null;
141+
private selectionCorrectionHandler: (() => void) | null = null;
142+
private suppressSelectionCorrection = false;
143+
private recentMouseupSelectionHead: number | null = null;
144+
private recentMouseupAt = 0;
136145
private _destroyed = false;
137146

138147
constructor(container: HTMLElement, config: MilkupConfig = {}) {
@@ -370,6 +379,10 @@ export class MilkupEditor implements IMilkupEditor {
370379
this.view.dom.parentElement?.removeEventListener("keydown", this.containerKeydownHandler);
371380
this.containerKeydownHandler = null;
372381
}
382+
if (this.selectionCorrectionHandler) {
383+
document.removeEventListener("selectionchange", this.selectionCorrectionHandler);
384+
this.selectionCorrectionHandler = null;
385+
}
373386
this.searchWrapper?.remove();
374387
this.searchWrapper = null;
375388
this.searchPanel = null;
@@ -392,7 +405,6 @@ export class MilkupEditor implements IMilkupEditor {
392405
private handleEditorClick(view: EditorView, pos: number, event: MouseEvent): boolean {
393406
const { state } = view;
394407
const { doc } = state;
395-
const editorRect = view.dom.getBoundingClientRect();
396408
const clickY = event.clientY;
397409

398410
// 获取第一个和最后一个块节点的位置
@@ -545,6 +557,7 @@ export class MilkupEditor implements IMilkupEditor {
545557
"click",
546558
(e: Event) => {
547559
const me = e as MouseEvent;
560+
this.correctSelectionFromHiddenSyntaxMarker();
548561
const linkEl = this.findLinkElement(me.target as HTMLElement);
549562
if (!linkEl) return;
550563

@@ -554,6 +567,16 @@ export class MilkupEditor implements IMilkupEditor {
554567
true // capture 阶段
555568
);
556569

570+
dom.addEventListener(
571+
"mouseup",
572+
(e: Event) => {
573+
this.recordMouseupSelectionSnapshot();
574+
queueMicrotask(() => this.correctSelectionFromHiddenSyntaxMarker());
575+
setTimeout(() => this.correctSelectionFromHiddenSyntaxMarker(), 0);
576+
},
577+
true
578+
);
579+
557580
// mousemove 检测链接 hover
558581
dom.addEventListener("mousemove", (e: Event) => {
559582
const me = e as MouseEvent;
@@ -582,6 +605,70 @@ export class MilkupEditor implements IMilkupEditor {
582605
passive: true,
583606
});
584607
}
608+
609+
if (!this.selectionCorrectionHandler) {
610+
this.selectionCorrectionHandler = () => {
611+
if (!this.view.hasFocus()) return;
612+
this.correctSelectionFromHiddenSyntaxMarker();
613+
};
614+
document.addEventListener("selectionchange", this.selectionCorrectionHandler);
615+
}
616+
}
617+
618+
private recordMouseupSelectionSnapshot(): void {
619+
const { selection } = this.view.state;
620+
if (!selection.empty) return;
621+
if (this.isPositionInsideSyntaxMarker(selection.head)) return;
622+
this.recentMouseupSelectionHead = selection.head;
623+
this.recentMouseupAt = Date.now();
624+
}
625+
626+
private isPositionInsideSyntaxMarker(pos: number): boolean {
627+
const syntaxRegions = findSyntaxMarkerRegions(this.view.state.doc);
628+
return syntaxRegions.some((region) => pos >= region.from && pos <= region.to);
629+
}
630+
631+
private correctSelectionFromHiddenSyntaxMarker(): void {
632+
if (this.suppressSelectionCorrection) return;
633+
634+
const nativeSelection = window.getSelection();
635+
if (!nativeSelection?.isCollapsed) return;
636+
637+
const anchorNode = nativeSelection.anchorNode;
638+
const now = Date.now();
639+
const recentHead = this.recentMouseupSelectionHead;
640+
if (recentHead === null || now - this.recentMouseupAt > 250) return;
641+
if (this.isPositionInsideSyntaxMarker(recentHead)) return;
642+
643+
let nativePos: number | null = null;
644+
try {
645+
if (anchorNode) {
646+
nativePos = this.view.posAtDOM(anchorNode, nativeSelection.anchorOffset);
647+
}
648+
} catch {
649+
nativePos = null;
650+
}
651+
652+
const { selection } = this.view.state;
653+
const anchorText = anchorNode?.textContent ?? "";
654+
const nativeLooksLikeSyntaxMarker =
655+
(nativePos !== null && this.isPositionInsideSyntaxMarker(nativePos)) ||
656+
(anchorText === "`" && this.isPositionInsideSyntaxMarker(selection.head));
657+
658+
if (!nativeLooksLikeSyntaxMarker) return;
659+
if (selection.head === recentHead) return;
660+
661+
this.suppressSelectionCorrection = true;
662+
try {
663+
this.view.dispatch(
664+
this.view.state.tr.setSelection(TextSelection.create(this.view.state.doc, recentHead))
665+
);
666+
this.view.focus();
667+
} finally {
668+
requestAnimationFrame(() => {
669+
this.suppressSelectionCorrection = false;
670+
});
671+
}
585672
}
586673

587674
private showLinkTooltip(linkEl: HTMLAnchorElement, href: string): void {

src/core/styles/milkup.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
width: 0;
3333
display: inline;
3434
user-select: none;
35+
pointer-events: none;
3536
}
3637

3738
/* 语法标记 - 显示 */

0 commit comments

Comments
 (0)