@blankdotpage/cake 0.1.67 → 0.1.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cake/core/mapping/cursor-source-map.d.ts +11 -0
- package/dist/cake/core/mapping/cursor-source-map.d.ts.map +1 -1
- package/dist/cake/core/mapping/cursor-source-map.js +159 -21
- package/dist/cake/core/runtime.d.ts +6 -0
- package/dist/cake/core/runtime.d.ts.map +1 -1
- package/dist/cake/core/runtime.js +344 -221
- package/dist/cake/dom/render.d.ts +32 -2
- package/dist/cake/dom/render.d.ts.map +1 -1
- package/dist/cake/dom/render.js +401 -118
- package/dist/cake/editor/cake-editor.d.ts +11 -2
- package/dist/cake/editor/cake-editor.d.ts.map +1 -1
- package/dist/cake/editor/cake-editor.js +178 -100
- package/dist/cake/editor/internal/editor-text-model.d.ts +49 -0
- package/dist/cake/editor/internal/editor-text-model.d.ts.map +1 -0
- package/dist/cake/editor/internal/editor-text-model.js +284 -0
- package/dist/cake/editor/selection/selection-geometry-dom.d.ts +5 -1
- package/dist/cake/editor/selection/selection-geometry-dom.d.ts.map +1 -1
- package/dist/cake/editor/selection/selection-geometry-dom.js +4 -5
- package/dist/cake/editor/selection/selection-layout-dom.d.ts.map +1 -1
- package/dist/cake/editor/selection/selection-layout-dom.js +2 -5
- package/dist/cake/editor/selection/selection-layout.d.ts +2 -15
- package/dist/cake/editor/selection/selection-layout.d.ts.map +1 -1
- package/dist/cake/editor/selection/selection-layout.js +1 -99
- package/dist/cake/editor/selection/selection-navigation.d.ts +4 -0
- package/dist/cake/editor/selection/selection-navigation.d.ts.map +1 -1
- package/dist/cake/editor/selection/selection-navigation.js +1 -2
- package/dist/cake/extensions/index.d.ts +2 -1
- package/dist/cake/extensions/index.d.ts.map +1 -1
- package/dist/cake/extensions/index.js +3 -1
- package/dist/cake/extensions/link/link.d.ts.map +1 -1
- package/dist/cake/extensions/link/link.js +1 -7
- package/dist/cake/extensions/shared/structural-reparse-policy.d.ts +7 -0
- package/dist/cake/extensions/shared/structural-reparse-policy.d.ts.map +1 -0
- package/dist/cake/extensions/shared/structural-reparse-policy.js +16 -0
- package/package.json +5 -2
- package/dist/cake/editor/selection/visible-text.d.ts +0 -5
- package/dist/cake/editor/selection/visible-text.d.ts.map +0 -1
- package/dist/cake/editor/selection/visible-text.js +0 -66
- package/dist/cake/engine/cake-engine.d.ts +0 -230
- package/dist/cake/engine/cake-engine.d.ts.map +0 -1
- package/dist/cake/engine/cake-engine.js +0 -3589
- package/dist/cake/engine/selection/selection-geometry-dom.d.ts +0 -24
- package/dist/cake/engine/selection/selection-geometry-dom.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-geometry-dom.js +0 -302
- package/dist/cake/engine/selection/selection-geometry.d.ts +0 -22
- package/dist/cake/engine/selection/selection-geometry.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-geometry.js +0 -158
- package/dist/cake/engine/selection/selection-layout-dom.d.ts +0 -50
- package/dist/cake/engine/selection/selection-layout-dom.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-layout-dom.js +0 -781
- package/dist/cake/engine/selection/selection-layout.d.ts +0 -55
- package/dist/cake/engine/selection/selection-layout.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-layout.js +0 -128
- package/dist/cake/engine/selection/selection-navigation.d.ts +0 -22
- package/dist/cake/engine/selection/selection-navigation.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-navigation.js +0 -229
- package/dist/cake/engine/selection/visible-text.d.ts +0 -5
- package/dist/cake/engine/selection/visible-text.d.ts.map +0 -1
- package/dist/cake/engine/selection/visible-text.js +0 -66
- package/dist/cake/react/CakeEditor.d.ts +0 -58
- package/dist/cake/react/CakeEditor.d.ts.map +0 -1
- package/dist/cake/react/CakeEditor.js +0 -225
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { isApplyEditCommand, createRuntimeFromRegistry, } from "../core/runtime";
|
|
2
|
-
import { renderDocContent } from "../dom/render";
|
|
2
|
+
import { renderDocContent, } from "../dom/render";
|
|
3
3
|
import { applyDomSelection, readDomSelection } from "../dom/dom-selection";
|
|
4
4
|
import { bundledExtensions } from "../extensions";
|
|
5
5
|
import { getCaretRect as getDomCaretRect, getSelectionGeometry, } from "./selection/selection-geometry-dom";
|
|
6
|
-
import { getDocLines, getLineOffsets, resolveOffsetToLine, } from "./selection/selection-layout";
|
|
7
|
-
import { cursorOffsetToVisibleOffset, getVisibleText, visibleOffsetToCursorOffset, } from "./selection/visible-text";
|
|
8
6
|
import { hitTestFromLayout, measureLayoutModelFromDom, } from "./selection/selection-layout-dom";
|
|
9
7
|
import { moveSelectionVertically as moveSelectionVerticallyInLayout } from "./selection/selection-navigation";
|
|
8
|
+
import { EditorTextModel, } from "./internal/editor-text-model";
|
|
10
9
|
import { isMacPlatform } from "../shared/platform";
|
|
11
10
|
import { graphemeCount } from "../shared/segmenter";
|
|
12
11
|
import { getWordBoundaries, nextWordBreak, prevWordBreak, } from "../shared/word-break";
|
|
@@ -15,6 +14,47 @@ const defaultSelection = { start: 0, end: 0, affinity: "forward" };
|
|
|
15
14
|
const COMPOSITION_COMMIT_CLEAR_DELAY_MS = 50;
|
|
16
15
|
const HISTORY_GROUPING_INTERVAL_MS = 500;
|
|
17
16
|
const MAX_UNDO_STACK_SIZE = 100;
|
|
17
|
+
function computeDirtyCursorRange(previous, next) {
|
|
18
|
+
if (!previous) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const prevSource = previous.source;
|
|
22
|
+
const nextSource = next.source;
|
|
23
|
+
if (prevSource === nextSource) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const prevLength = prevSource.length;
|
|
27
|
+
const nextLength = nextSource.length;
|
|
28
|
+
let prefix = 0;
|
|
29
|
+
while (prefix < prevLength &&
|
|
30
|
+
prefix < nextLength &&
|
|
31
|
+
prevSource.charCodeAt(prefix) === nextSource.charCodeAt(prefix)) {
|
|
32
|
+
prefix += 1;
|
|
33
|
+
}
|
|
34
|
+
let prevSuffix = prevLength;
|
|
35
|
+
let nextSuffix = nextLength;
|
|
36
|
+
while (prevSuffix > prefix &&
|
|
37
|
+
nextSuffix > prefix &&
|
|
38
|
+
prevSource.charCodeAt(prevSuffix - 1) ===
|
|
39
|
+
nextSource.charCodeAt(nextSuffix - 1)) {
|
|
40
|
+
prevSuffix -= 1;
|
|
41
|
+
nextSuffix -= 1;
|
|
42
|
+
}
|
|
43
|
+
const previousStart = previous.map.sourceToCursor(prefix, "forward").cursorOffset;
|
|
44
|
+
const previousEnd = previous.map.sourceToCursor(prevSuffix, "backward").cursorOffset;
|
|
45
|
+
const nextStart = next.map.sourceToCursor(prefix, "forward").cursorOffset;
|
|
46
|
+
const nextEnd = next.map.sourceToCursor(nextSuffix, "backward").cursorOffset;
|
|
47
|
+
return {
|
|
48
|
+
previous: {
|
|
49
|
+
start: previousStart,
|
|
50
|
+
end: previousEnd,
|
|
51
|
+
},
|
|
52
|
+
next: {
|
|
53
|
+
start: nextStart,
|
|
54
|
+
end: nextEnd,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
18
58
|
function removeFromArray(arr, value) {
|
|
19
59
|
const index = arr.indexOf(value);
|
|
20
60
|
if (index === -1) {
|
|
@@ -27,7 +67,11 @@ export class CakeEditor {
|
|
|
27
67
|
return this._state;
|
|
28
68
|
}
|
|
29
69
|
set state(value) {
|
|
70
|
+
const previousDoc = this._state?.doc;
|
|
30
71
|
this._state = value;
|
|
72
|
+
if (previousDoc !== value.doc) {
|
|
73
|
+
this.textModel.rebuild(value.doc);
|
|
74
|
+
}
|
|
31
75
|
}
|
|
32
76
|
getLastRenderPerf() {
|
|
33
77
|
return this.lastRenderPerf;
|
|
@@ -69,6 +113,7 @@ export class CakeEditor {
|
|
|
69
113
|
this.normalizeBlockFns = [];
|
|
70
114
|
this.normalizeInlineFns = [];
|
|
71
115
|
this.onEditFns = [];
|
|
116
|
+
this.structuralReparsePolicies = [];
|
|
72
117
|
this.keybindings = [];
|
|
73
118
|
this.keyDownInterceptors = [];
|
|
74
119
|
this.onPasteTextHandlers = [];
|
|
@@ -77,6 +122,7 @@ export class CakeEditor {
|
|
|
77
122
|
this.domBlockRenderers = [];
|
|
78
123
|
this.uiComponents = [];
|
|
79
124
|
this.extensionDisposers = [];
|
|
125
|
+
this.textModel = new EditorTextModel();
|
|
80
126
|
this.contentRoot = null;
|
|
81
127
|
this.domMap = null;
|
|
82
128
|
this.isApplyingSelection = false;
|
|
@@ -116,6 +162,8 @@ export class CakeEditor {
|
|
|
116
162
|
this.lastFocusRect = null;
|
|
117
163
|
this.verticalNavGoalX = null;
|
|
118
164
|
this.lastRenderPerf = null;
|
|
165
|
+
this.renderSnapshot = null;
|
|
166
|
+
this.lastRenderedState = null;
|
|
119
167
|
this.history = {
|
|
120
168
|
undoStack: [],
|
|
121
169
|
redoStack: [],
|
|
@@ -203,6 +251,7 @@ export class CakeEditor {
|
|
|
203
251
|
normalizeBlockFns: this.normalizeBlockFns,
|
|
204
252
|
normalizeInlineFns: this.normalizeInlineFns,
|
|
205
253
|
onEditFns: this.onEditFns,
|
|
254
|
+
structuralReparsePolicies: this.structuralReparsePolicies,
|
|
206
255
|
domInlineRenderers: this.domInlineRenderers,
|
|
207
256
|
domBlockRenderers: this.domBlockRenderers,
|
|
208
257
|
});
|
|
@@ -282,6 +331,10 @@ export class CakeEditor {
|
|
|
282
331
|
this.onEditFns.push(fn);
|
|
283
332
|
return () => removeFromArray(this.onEditFns, fn);
|
|
284
333
|
}
|
|
334
|
+
registerStructuralReparsePolicy(fn) {
|
|
335
|
+
this.structuralReparsePolicies.push(fn);
|
|
336
|
+
return () => removeFromArray(this.structuralReparsePolicies, fn);
|
|
337
|
+
}
|
|
285
338
|
registerOnPasteText(fn) {
|
|
286
339
|
this.onPasteTextHandlers.push(fn);
|
|
287
340
|
return () => removeFromArray(this.onPasteTextHandlers, fn);
|
|
@@ -362,16 +415,26 @@ export class CakeEditor {
|
|
|
362
415
|
return this.state.selection;
|
|
363
416
|
}
|
|
364
417
|
getText() {
|
|
365
|
-
|
|
366
|
-
return getVisibleText(lines);
|
|
418
|
+
return this.textModel.getVisibleText();
|
|
367
419
|
}
|
|
368
420
|
getTextSelection() {
|
|
369
|
-
const lines = getDocLines(this.state.doc);
|
|
370
421
|
return {
|
|
371
|
-
start: cursorOffsetToVisibleOffset(
|
|
372
|
-
end: cursorOffsetToVisibleOffset(
|
|
422
|
+
start: this.textModel.cursorOffsetToVisibleOffset(this.state.selection.start),
|
|
423
|
+
end: this.textModel.cursorOffsetToVisibleOffset(this.state.selection.end),
|
|
373
424
|
};
|
|
374
425
|
}
|
|
426
|
+
rebuildTextModelFallback() {
|
|
427
|
+
// Single fallback branch for complex cases where cursor/visible mapping misses.
|
|
428
|
+
this.textModel.rebuild(this.state.doc);
|
|
429
|
+
}
|
|
430
|
+
visibleOffsetToCursorOffset(visibleOffset) {
|
|
431
|
+
const resolved = this.textModel.visibleOffsetToCursorOffset(visibleOffset);
|
|
432
|
+
if (resolved !== null) {
|
|
433
|
+
return resolved;
|
|
434
|
+
}
|
|
435
|
+
this.rebuildTextModelFallback();
|
|
436
|
+
return this.textModel.visibleOffsetToCursorOffset(visibleOffset);
|
|
437
|
+
}
|
|
375
438
|
getActiveMarks() {
|
|
376
439
|
const { start, end } = this.state.selection;
|
|
377
440
|
if (start === end) {
|
|
@@ -684,9 +747,8 @@ export class CakeEditor {
|
|
|
684
747
|
return { found: false, marks: [], nextOffset: offset };
|
|
685
748
|
}
|
|
686
749
|
setTextSelection(selection) {
|
|
687
|
-
const
|
|
688
|
-
const
|
|
689
|
-
const end = visibleOffsetToCursorOffset(lines, selection.end);
|
|
750
|
+
const start = this.visibleOffsetToCursorOffset(selection.start);
|
|
751
|
+
const end = this.visibleOffsetToCursorOffset(selection.end);
|
|
690
752
|
if (start === null || end === null) {
|
|
691
753
|
return;
|
|
692
754
|
}
|
|
@@ -699,34 +761,25 @@ export class CakeEditor {
|
|
|
699
761
|
});
|
|
700
762
|
}
|
|
701
763
|
getTextBeforeCursor(maxChars = Number.POSITIVE_INFINITY) {
|
|
702
|
-
|
|
703
|
-
const { start } = this.getTextSelection();
|
|
704
|
-
const cursor = Math.max(0, Math.min(start, text.length));
|
|
705
|
-
const length = Math.max(0, maxChars);
|
|
706
|
-
return text.slice(Math.max(0, cursor - length), cursor);
|
|
764
|
+
return this.textModel.getTextBeforeCursor(this.state.selection, maxChars);
|
|
707
765
|
}
|
|
708
766
|
getTextAroundCursor(before, after) {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
const afterLength = Math.max(0, after);
|
|
714
|
-
const beforeText = text.slice(Math.max(0, cursor - beforeLength), cursor);
|
|
715
|
-
const afterText = text.slice(cursor, Math.min(text.length, cursor + afterLength));
|
|
716
|
-
return { before: beforeText, after: afterText };
|
|
767
|
+
return this.textModel.getTextAroundCursor(this.state.selection, before, after);
|
|
768
|
+
}
|
|
769
|
+
getTextForCursorRange(start, end) {
|
|
770
|
+
return this.textModel.getTextForCursorRange(start, end);
|
|
717
771
|
}
|
|
718
772
|
replaceTextBeforeCursor(chars, replacement) {
|
|
719
|
-
const lines = getDocLines(this.state.doc);
|
|
720
773
|
const selection = this.state.selection;
|
|
721
774
|
const focus = selection.start === selection.end
|
|
722
775
|
? selection.start
|
|
723
776
|
: selection.affinity === "backward"
|
|
724
777
|
? selection.start
|
|
725
778
|
: selection.end;
|
|
726
|
-
const focusVisible = cursorOffsetToVisibleOffset(
|
|
779
|
+
const focusVisible = this.textModel.cursorOffsetToVisibleOffset(focus);
|
|
727
780
|
const length = Math.max(0, chars);
|
|
728
781
|
const startVisible = Math.max(0, focusVisible - length);
|
|
729
|
-
const startCursor = visibleOffsetToCursorOffset(
|
|
782
|
+
const startCursor = this.visibleOffsetToCursorOffset(startVisible);
|
|
730
783
|
if (startCursor === null) {
|
|
731
784
|
return;
|
|
732
785
|
}
|
|
@@ -751,7 +804,7 @@ export class CakeEditor {
|
|
|
751
804
|
this.scheduleScrollCaretIntoView();
|
|
752
805
|
}
|
|
753
806
|
getCursorLength() {
|
|
754
|
-
return this.
|
|
807
|
+
return this.textModel.getCursorLength();
|
|
755
808
|
}
|
|
756
809
|
getFocusRect() {
|
|
757
810
|
return this.lastFocusRect;
|
|
@@ -780,12 +833,13 @@ export class CakeEditor {
|
|
|
780
833
|
if (!this.contentRoot) {
|
|
781
834
|
return null;
|
|
782
835
|
}
|
|
783
|
-
const lines =
|
|
836
|
+
const lines = this.textModel.getLines();
|
|
784
837
|
const geometry = getSelectionGeometry({
|
|
785
838
|
root: this.contentRoot,
|
|
786
839
|
container: this.container,
|
|
787
840
|
docLines: lines,
|
|
788
841
|
selection: this.state.selection,
|
|
842
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
789
843
|
});
|
|
790
844
|
const focus = geometry.caretRect ?? geometry.focusRect;
|
|
791
845
|
if (!focus) {
|
|
@@ -815,12 +869,13 @@ export class CakeEditor {
|
|
|
815
869
|
if (!this.contentRoot) {
|
|
816
870
|
return [];
|
|
817
871
|
}
|
|
818
|
-
const lines =
|
|
872
|
+
const lines = this.textModel.getLines();
|
|
819
873
|
const geometry = getSelectionGeometry({
|
|
820
874
|
root: this.contentRoot,
|
|
821
875
|
container: this.container,
|
|
822
876
|
docLines: lines,
|
|
823
877
|
selection: this.state.selection,
|
|
878
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
824
879
|
});
|
|
825
880
|
if (geometry.selectionRects.length === 0) {
|
|
826
881
|
return [];
|
|
@@ -844,7 +899,7 @@ export class CakeEditor {
|
|
|
844
899
|
}));
|
|
845
900
|
}
|
|
846
901
|
getLines() {
|
|
847
|
-
return
|
|
902
|
+
return this.textModel.getLines();
|
|
848
903
|
}
|
|
849
904
|
getOverlayRoot() {
|
|
850
905
|
return this.ensureExtensionsRoot();
|
|
@@ -1242,7 +1297,11 @@ export class CakeEditor {
|
|
|
1242
1297
|
if (perfEnabled) {
|
|
1243
1298
|
renderStart = performance.now();
|
|
1244
1299
|
}
|
|
1245
|
-
const
|
|
1300
|
+
const dirtyCursorRange = computeDirtyCursorRange(this.lastRenderedState, this.state);
|
|
1301
|
+
const { content, map, snapshot } = renderDocContent(this.state.doc, this.runtime.dom, this.contentRoot, {
|
|
1302
|
+
previousSnapshot: this.renderSnapshot,
|
|
1303
|
+
dirtyCursorRange,
|
|
1304
|
+
});
|
|
1246
1305
|
const existingChildren = Array.from(this.contentRoot.childNodes);
|
|
1247
1306
|
const isManagedChild = (node) => node instanceof Element &&
|
|
1248
1307
|
(node.hasAttribute("data-line-index") ||
|
|
@@ -1257,6 +1316,11 @@ export class CakeEditor {
|
|
|
1257
1316
|
this.contentRoot.replaceChildren(...content, ...preservedChildren);
|
|
1258
1317
|
}
|
|
1259
1318
|
this.domMap = map;
|
|
1319
|
+
this.renderSnapshot = snapshot;
|
|
1320
|
+
this.lastRenderedState = {
|
|
1321
|
+
source: this.state.source,
|
|
1322
|
+
map: this.state.map,
|
|
1323
|
+
};
|
|
1260
1324
|
if (perfEnabled) {
|
|
1261
1325
|
renderAndMapMs = performance.now() - renderStart;
|
|
1262
1326
|
}
|
|
@@ -1518,9 +1582,9 @@ export class CakeEditor {
|
|
|
1518
1582
|
if (selection.start !== selection.end) {
|
|
1519
1583
|
return selection;
|
|
1520
1584
|
}
|
|
1521
|
-
const lines =
|
|
1522
|
-
const lineOffsets = getLineOffsets(
|
|
1523
|
-
const { lineIndex } = resolveOffsetToLine(
|
|
1585
|
+
const lines = this.textModel.getLines();
|
|
1586
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
1587
|
+
const { lineIndex } = this.textModel.resolveOffsetToLine(selection.start);
|
|
1524
1588
|
const lineInfo = lines[lineIndex];
|
|
1525
1589
|
if (!lineInfo || !lineInfo.isAtomic) {
|
|
1526
1590
|
return selection;
|
|
@@ -1566,13 +1630,13 @@ export class CakeEditor {
|
|
|
1566
1630
|
if (Number.isNaN(lineIndex)) {
|
|
1567
1631
|
return null;
|
|
1568
1632
|
}
|
|
1569
|
-
const lines =
|
|
1633
|
+
const lines = this.textModel.getLines();
|
|
1570
1634
|
const lineInfo = lines[lineIndex];
|
|
1571
1635
|
if (!lineInfo || !lineInfo.isAtomic) {
|
|
1572
1636
|
return null;
|
|
1573
1637
|
}
|
|
1574
1638
|
// Calculate the selection range for the entire line including newline
|
|
1575
|
-
const lineOffsets = getLineOffsets(
|
|
1639
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
1576
1640
|
const lineStart = lineOffsets[lineIndex] ?? 0;
|
|
1577
1641
|
const lineEnd = lineStart + lineInfo.cursorLength + (lineInfo.hasNewline ? 1 : 0);
|
|
1578
1642
|
return {
|
|
@@ -1703,10 +1767,10 @@ export class CakeEditor {
|
|
|
1703
1767
|
if (!hit) {
|
|
1704
1768
|
return;
|
|
1705
1769
|
}
|
|
1706
|
-
const lines =
|
|
1770
|
+
const lines = this.textModel.getLines();
|
|
1707
1771
|
if (event.detail === 2) {
|
|
1708
|
-
const lineOffsets = getLineOffsets(
|
|
1709
|
-
const { lineIndex } = resolveOffsetToLine(
|
|
1772
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
1773
|
+
const { lineIndex } = this.textModel.resolveOffsetToLine(hit.cursorOffset);
|
|
1710
1774
|
const lineInfo = lines[lineIndex];
|
|
1711
1775
|
if (lineInfo && lineInfo.cursorLength === 0) {
|
|
1712
1776
|
const lineStart = lineOffsets[lineIndex] ?? 0;
|
|
@@ -1724,11 +1788,11 @@ export class CakeEditor {
|
|
|
1724
1788
|
this.suppressSelectionChange = false;
|
|
1725
1789
|
return;
|
|
1726
1790
|
}
|
|
1727
|
-
const visibleText = getVisibleText(
|
|
1728
|
-
const visibleOffset = cursorOffsetToVisibleOffset(
|
|
1791
|
+
const visibleText = this.textModel.getVisibleText();
|
|
1792
|
+
const visibleOffset = this.textModel.cursorOffsetToVisibleOffset(hit.cursorOffset);
|
|
1729
1793
|
const wordBounds = getWordBoundaries(visibleText, visibleOffset);
|
|
1730
|
-
const start = visibleOffsetToCursorOffset(
|
|
1731
|
-
const end = visibleOffsetToCursorOffset(
|
|
1794
|
+
const start = this.visibleOffsetToCursorOffset(wordBounds.start);
|
|
1795
|
+
const end = this.visibleOffsetToCursorOffset(wordBounds.end);
|
|
1732
1796
|
if (start === null || end === null) {
|
|
1733
1797
|
this.suppressSelectionChange = false;
|
|
1734
1798
|
return;
|
|
@@ -1748,8 +1812,8 @@ export class CakeEditor {
|
|
|
1748
1812
|
return;
|
|
1749
1813
|
}
|
|
1750
1814
|
if (event.detail >= 3) {
|
|
1751
|
-
const lineOffsets = getLineOffsets(
|
|
1752
|
-
const { lineIndex } = resolveOffsetToLine(
|
|
1815
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
1816
|
+
const { lineIndex } = this.textModel.resolveOffsetToLine(hit.cursorOffset);
|
|
1753
1817
|
const lineInfo = lines[lineIndex];
|
|
1754
1818
|
if (!lineInfo) {
|
|
1755
1819
|
this.suppressSelectionChange = false;
|
|
@@ -2176,8 +2240,7 @@ export class CakeEditor {
|
|
|
2176
2240
|
if (this.compositionCommit && event.inputType === "insertText") {
|
|
2177
2241
|
this.clearCompositionCommit();
|
|
2178
2242
|
const domText = this.readDomText();
|
|
2179
|
-
const
|
|
2180
|
-
const modelText = getVisibleText(lines);
|
|
2243
|
+
const modelText = this.textModel.getVisibleText();
|
|
2181
2244
|
if (domText === modelText) {
|
|
2182
2245
|
if (this.domMap) {
|
|
2183
2246
|
const domSelection = readDomSelection(this.domMap);
|
|
@@ -2203,8 +2266,7 @@ export class CakeEditor {
|
|
|
2203
2266
|
// we must not drop the edit; reconcile if the DOM diverged from the model.
|
|
2204
2267
|
if (this.beforeInputHandled) {
|
|
2205
2268
|
const domText = this.readDomText();
|
|
2206
|
-
const
|
|
2207
|
-
const modelText = getVisibleText(lines);
|
|
2269
|
+
const modelText = this.textModel.getVisibleText();
|
|
2208
2270
|
if (domText === modelText) {
|
|
2209
2271
|
return;
|
|
2210
2272
|
}
|
|
@@ -2412,8 +2474,8 @@ export class CakeEditor {
|
|
|
2412
2474
|
return false;
|
|
2413
2475
|
}
|
|
2414
2476
|
const lineIndex = this.selectedAtomicLineIndex;
|
|
2415
|
-
const lines =
|
|
2416
|
-
const lineOffsets = getLineOffsets(
|
|
2477
|
+
const lines = this.textModel.getLines();
|
|
2478
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
2417
2479
|
const lineInfo = lines[lineIndex];
|
|
2418
2480
|
if (!lineInfo || !lineInfo.isAtomic) {
|
|
2419
2481
|
this.selectedAtomicLineIndex = null;
|
|
@@ -2461,8 +2523,8 @@ export class CakeEditor {
|
|
|
2461
2523
|
if (selection.start !== selection.end) {
|
|
2462
2524
|
return false;
|
|
2463
2525
|
}
|
|
2464
|
-
const lines =
|
|
2465
|
-
const lineOffsets = getLineOffsets(
|
|
2526
|
+
const lines = this.textModel.getLines();
|
|
2527
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
2466
2528
|
const lineIndex = lineOffsets.findIndex((offset) => offset === selection.start);
|
|
2467
2529
|
if (lineIndex === -1) {
|
|
2468
2530
|
return false;
|
|
@@ -2502,8 +2564,11 @@ export class CakeEditor {
|
|
|
2502
2564
|
sourceLines[swapB] = aSource;
|
|
2503
2565
|
const newSource = sourceLines.join("\n");
|
|
2504
2566
|
const nextState = this.runtime.createState(newSource);
|
|
2505
|
-
|
|
2506
|
-
|
|
2567
|
+
let lineStartSource = 0;
|
|
2568
|
+
for (let index = 0; index < swapA; index += 1) {
|
|
2569
|
+
lineStartSource += (sourceLines[index]?.length ?? 0) + 1;
|
|
2570
|
+
}
|
|
2571
|
+
const cursorPos = nextState.map.sourceToCursor(lineStartSource, "forward").cursorOffset;
|
|
2507
2572
|
this.recordHistory("delete-backward");
|
|
2508
2573
|
this.state = {
|
|
2509
2574
|
...nextState,
|
|
@@ -2554,7 +2619,7 @@ export class CakeEditor {
|
|
|
2554
2619
|
if (!this.contentRoot) {
|
|
2555
2620
|
return null;
|
|
2556
2621
|
}
|
|
2557
|
-
const lines =
|
|
2622
|
+
const lines = this.textModel.getLines();
|
|
2558
2623
|
const layout = measureLayoutModelFromDom({
|
|
2559
2624
|
lines,
|
|
2560
2625
|
root: this.contentRoot,
|
|
@@ -2585,6 +2650,7 @@ export class CakeEditor {
|
|
|
2585
2650
|
layout,
|
|
2586
2651
|
offset: currentPos,
|
|
2587
2652
|
affinity: currentAffinity,
|
|
2653
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2588
2654
|
});
|
|
2589
2655
|
// Arrow left at start of visual row (not at document start):
|
|
2590
2656
|
// Stay at same position but change to backward affinity
|
|
@@ -2598,6 +2664,7 @@ export class CakeEditor {
|
|
|
2598
2664
|
layout,
|
|
2599
2665
|
offset: currentPos,
|
|
2600
2666
|
affinity: "backward",
|
|
2667
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2601
2668
|
});
|
|
2602
2669
|
// If backward affinity puts us on a different row, just change affinity
|
|
2603
2670
|
if (prevBoundaries.rowEnd !== rowEnd ||
|
|
@@ -2616,6 +2683,7 @@ export class CakeEditor {
|
|
|
2616
2683
|
layout,
|
|
2617
2684
|
offset: currentPos,
|
|
2618
2685
|
affinity: "forward",
|
|
2686
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2619
2687
|
});
|
|
2620
2688
|
// If forward affinity puts us on a different row, just change affinity
|
|
2621
2689
|
if (nextBoundaries.rowEnd !== rowEnd ||
|
|
@@ -2632,8 +2700,8 @@ export class CakeEditor {
|
|
|
2632
2700
|
}
|
|
2633
2701
|
moveOffsetByChar(offset, direction) {
|
|
2634
2702
|
const cursorLength = this.state.map.cursorLength;
|
|
2635
|
-
const lines =
|
|
2636
|
-
const lineOffsets = getLineOffsets(
|
|
2703
|
+
const lines = this.textModel.getLines();
|
|
2704
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
2637
2705
|
let nextPos;
|
|
2638
2706
|
if (direction === "forward") {
|
|
2639
2707
|
if (offset >= cursorLength) {
|
|
@@ -2647,7 +2715,7 @@ export class CakeEditor {
|
|
|
2647
2715
|
}
|
|
2648
2716
|
nextPos = offset - 1;
|
|
2649
2717
|
}
|
|
2650
|
-
const { lineIndex: nextLineIndex } = resolveOffsetToLine(
|
|
2718
|
+
const { lineIndex: nextLineIndex } = this.textModel.resolveOffsetToLine(nextPos);
|
|
2651
2719
|
const nextLineInfo = lines[nextLineIndex];
|
|
2652
2720
|
if (nextLineInfo && nextLineInfo.isAtomic) {
|
|
2653
2721
|
const lineStart = lineOffsets[nextLineIndex] ?? 0;
|
|
@@ -2674,7 +2742,7 @@ export class CakeEditor {
|
|
|
2674
2742
|
}
|
|
2675
2743
|
}
|
|
2676
2744
|
const { focus } = resolveSelectionAnchorAndFocus(this.state.selection);
|
|
2677
|
-
const focusResolved = resolveOffsetToLine(
|
|
2745
|
+
const focusResolved = this.textModel.resolveOffsetToLine(focus);
|
|
2678
2746
|
const focusLineLayout = layout.lines[focusResolved.lineIndex];
|
|
2679
2747
|
let focusRowIndex = undefined;
|
|
2680
2748
|
if (focusLineLayout?.rows.length && this.lastFocusRect) {
|
|
@@ -2701,6 +2769,7 @@ export class CakeEditor {
|
|
|
2701
2769
|
lines,
|
|
2702
2770
|
layout,
|
|
2703
2771
|
selection: this.state.selection,
|
|
2772
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2704
2773
|
direction,
|
|
2705
2774
|
goalX: this.verticalNavGoalX,
|
|
2706
2775
|
focusRowIndex,
|
|
@@ -2709,7 +2778,7 @@ export class CakeEditor {
|
|
|
2709
2778
|
if (!hit || !this.contentRoot) {
|
|
2710
2779
|
return null;
|
|
2711
2780
|
}
|
|
2712
|
-
const resolved = resolveOffsetToLine(
|
|
2781
|
+
const resolved = this.textModel.resolveOffsetToLine(hit.cursorOffset);
|
|
2713
2782
|
const lineInfo = lines[resolved.lineIndex];
|
|
2714
2783
|
const lineElement = this.contentRoot.querySelector(`[data-line-index="${resolved.lineIndex}"]`);
|
|
2715
2784
|
if (!lineInfo || !(lineElement instanceof HTMLElement)) {
|
|
@@ -2753,6 +2822,7 @@ export class CakeEditor {
|
|
|
2753
2822
|
layout,
|
|
2754
2823
|
offset: focus,
|
|
2755
2824
|
affinity: "backward",
|
|
2825
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2756
2826
|
});
|
|
2757
2827
|
let target = rowStart;
|
|
2758
2828
|
if (focus === rowStart && focus > 0) {
|
|
@@ -2761,6 +2831,7 @@ export class CakeEditor {
|
|
|
2761
2831
|
layout,
|
|
2762
2832
|
offset: focus - 1,
|
|
2763
2833
|
affinity: "backward",
|
|
2834
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2764
2835
|
});
|
|
2765
2836
|
target = previous.rowStart;
|
|
2766
2837
|
}
|
|
@@ -2782,6 +2853,7 @@ export class CakeEditor {
|
|
|
2782
2853
|
layout,
|
|
2783
2854
|
offset: focus,
|
|
2784
2855
|
affinity: "forward",
|
|
2856
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2785
2857
|
});
|
|
2786
2858
|
let target = rowEnd;
|
|
2787
2859
|
if (focus === rowEnd && focus < this.state.map.cursorLength) {
|
|
@@ -2790,6 +2862,7 @@ export class CakeEditor {
|
|
|
2790
2862
|
layout,
|
|
2791
2863
|
offset: focus + 1,
|
|
2792
2864
|
affinity: "forward",
|
|
2865
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2793
2866
|
});
|
|
2794
2867
|
target = next.rowEnd;
|
|
2795
2868
|
}
|
|
@@ -2809,6 +2882,7 @@ export class CakeEditor {
|
|
|
2809
2882
|
layout,
|
|
2810
2883
|
offset: focus,
|
|
2811
2884
|
affinity,
|
|
2885
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2812
2886
|
});
|
|
2813
2887
|
let target = rowStart;
|
|
2814
2888
|
if (focus === rowStart && focus > 0) {
|
|
@@ -2817,6 +2891,7 @@ export class CakeEditor {
|
|
|
2817
2891
|
layout,
|
|
2818
2892
|
offset: focus - 1,
|
|
2819
2893
|
affinity: "backward",
|
|
2894
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2820
2895
|
});
|
|
2821
2896
|
target = previous.rowStart;
|
|
2822
2897
|
}
|
|
@@ -2836,6 +2911,7 @@ export class CakeEditor {
|
|
|
2836
2911
|
layout,
|
|
2837
2912
|
offset: focus,
|
|
2838
2913
|
affinity,
|
|
2914
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2839
2915
|
});
|
|
2840
2916
|
let target = rowEnd;
|
|
2841
2917
|
if (focus === rowEnd && focus < this.state.map.cursorLength) {
|
|
@@ -2844,6 +2920,7 @@ export class CakeEditor {
|
|
|
2844
2920
|
layout,
|
|
2845
2921
|
offset: focus + 1,
|
|
2846
2922
|
affinity: "forward",
|
|
2923
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2847
2924
|
});
|
|
2848
2925
|
target = next.rowEnd;
|
|
2849
2926
|
}
|
|
@@ -2862,8 +2939,8 @@ export class CakeEditor {
|
|
|
2862
2939
|
if (selection.start === selection.end) {
|
|
2863
2940
|
return null;
|
|
2864
2941
|
}
|
|
2865
|
-
const lines =
|
|
2866
|
-
const lineOffsets = getLineOffsets(
|
|
2942
|
+
const lines = this.textModel.getLines();
|
|
2943
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
2867
2944
|
const selStart = Math.min(selection.start, selection.end);
|
|
2868
2945
|
const selEnd = Math.max(selection.start, selection.end);
|
|
2869
2946
|
const fullLineInfo = this.detectFullLineSelection(selStart, selEnd, lines, lineOffsets);
|
|
@@ -2918,26 +2995,25 @@ export class CakeEditor {
|
|
|
2918
2995
|
return selectionFromAnchor(anchor, nextFocus, direction);
|
|
2919
2996
|
}
|
|
2920
2997
|
moveOffsetByWord(offset, direction) {
|
|
2921
|
-
const
|
|
2922
|
-
const visibleText = getVisibleText(lines);
|
|
2998
|
+
const visibleText = this.textModel.getVisibleText();
|
|
2923
2999
|
if (!visibleText) {
|
|
2924
3000
|
return 0;
|
|
2925
3001
|
}
|
|
2926
|
-
const visibleOffset = cursorOffsetToVisibleOffset(
|
|
3002
|
+
const visibleOffset = this.textModel.cursorOffsetToVisibleOffset(offset);
|
|
2927
3003
|
const nextVisibleOffset = direction === "backward"
|
|
2928
3004
|
? prevWordBreak(visibleText, visibleOffset)
|
|
2929
3005
|
: nextWordBreak(visibleText, visibleOffset);
|
|
2930
|
-
return visibleOffsetToCursorOffset(
|
|
3006
|
+
return this.visibleOffsetToCursorOffset(nextVisibleOffset) ?? offset;
|
|
2931
3007
|
}
|
|
2932
3008
|
deleteToVisualRowStart() {
|
|
2933
3009
|
const selection = this.state.selection;
|
|
2934
|
-
const lines =
|
|
2935
|
-
const { lineIndex, offsetInLine } = resolveOffsetToLine(
|
|
3010
|
+
const lines = this.textModel.getLines();
|
|
3011
|
+
const { lineIndex, offsetInLine } = this.textModel.resolveOffsetToLine(selection.start);
|
|
2936
3012
|
const lineInfo = lines[lineIndex];
|
|
2937
3013
|
if (!lineInfo) {
|
|
2938
3014
|
return;
|
|
2939
3015
|
}
|
|
2940
|
-
const lineOffsets = getLineOffsets(
|
|
3016
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
2941
3017
|
const lineStart = lineOffsets[lineIndex] ?? 0;
|
|
2942
3018
|
const isLineStart = offsetInLine === 0;
|
|
2943
3019
|
const isCollapsed = selection.start === selection.end;
|
|
@@ -2962,6 +3038,7 @@ export class CakeEditor {
|
|
|
2962
3038
|
layout,
|
|
2963
3039
|
offset: selection.start,
|
|
2964
3040
|
affinity: "backward",
|
|
3041
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
2965
3042
|
});
|
|
2966
3043
|
const isVisualRowStart = selection.start === rowStart;
|
|
2967
3044
|
if (isCollapsed && isVisualRowStart) {
|
|
@@ -2978,11 +3055,11 @@ export class CakeEditor {
|
|
|
2978
3055
|
}
|
|
2979
3056
|
handleIndent() {
|
|
2980
3057
|
const selection = this.state.selection;
|
|
2981
|
-
const lines =
|
|
3058
|
+
const lines = this.textModel.getLines();
|
|
2982
3059
|
const TAB_SPACES = " ";
|
|
2983
3060
|
const isCollapsed = selection.start === selection.end;
|
|
2984
|
-
const startLineIndex = resolveOffsetToLine(
|
|
2985
|
-
const endLineIndex = resolveOffsetToLine(
|
|
3061
|
+
const startLineIndex = this.textModel.resolveOffsetToLine(selection.start).lineIndex;
|
|
3062
|
+
const endLineIndex = this.textModel.resolveOffsetToLine(Math.max(selection.start, selection.end - 1)).lineIndex;
|
|
2986
3063
|
const affectsMultipleLines = endLineIndex > startLineIndex;
|
|
2987
3064
|
// Check if the current line is a list item by checking source text
|
|
2988
3065
|
const sourceLines = this.state.source.split("\n");
|
|
@@ -3003,7 +3080,7 @@ export class CakeEditor {
|
|
|
3003
3080
|
// insert at caret position. Otherwise indent at line start.
|
|
3004
3081
|
if (isCollapsed) {
|
|
3005
3082
|
// Check if caret is in middle/end of line (not at start)
|
|
3006
|
-
const lineOffsets = getLineOffsets(
|
|
3083
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
3007
3084
|
const lineStart = lineOffsets[startLineIndex] ?? 0;
|
|
3008
3085
|
const offsetInLine = selection.start - lineStart;
|
|
3009
3086
|
if (offsetInLine > 0) {
|
|
@@ -3055,10 +3132,10 @@ export class CakeEditor {
|
|
|
3055
3132
|
}
|
|
3056
3133
|
handleOutdent() {
|
|
3057
3134
|
const selection = this.state.selection;
|
|
3058
|
-
const lines =
|
|
3135
|
+
const lines = this.textModel.getLines();
|
|
3059
3136
|
const TAB_SPACES = " ";
|
|
3060
|
-
const startLineIndex = resolveOffsetToLine(
|
|
3061
|
-
const endLineIndex = resolveOffsetToLine(
|
|
3137
|
+
const startLineIndex = this.textModel.resolveOffsetToLine(selection.start).lineIndex;
|
|
3138
|
+
const endLineIndex = this.textModel.resolveOffsetToLine(Math.max(selection.start, selection.end - 1)).lineIndex;
|
|
3062
3139
|
const sourceLines = this.state.source.split("\n");
|
|
3063
3140
|
// Check if the current line is a list item by checking source text
|
|
3064
3141
|
const startSourceLine = sourceLines[startLineIndex] ?? "";
|
|
@@ -3152,8 +3229,7 @@ export class CakeEditor {
|
|
|
3152
3229
|
*/
|
|
3153
3230
|
reconcileDomChanges(selection) {
|
|
3154
3231
|
const domText = this.readDomText();
|
|
3155
|
-
const
|
|
3156
|
-
const modelText = getVisibleText(lines);
|
|
3232
|
+
const modelText = this.textModel.getVisibleText();
|
|
3157
3233
|
if (domText === modelText) {
|
|
3158
3234
|
return false;
|
|
3159
3235
|
}
|
|
@@ -3175,8 +3251,8 @@ export class CakeEditor {
|
|
|
3175
3251
|
// The replacement text from DOM
|
|
3176
3252
|
const replacementText = domText.slice(prefixLen, domText.length - suffixLen);
|
|
3177
3253
|
// Convert visible text offsets to cursor offsets
|
|
3178
|
-
const cursorStart = visibleOffsetToCursorOffset(
|
|
3179
|
-
const cursorEnd = visibleOffsetToCursorOffset(
|
|
3254
|
+
const cursorStart = this.visibleOffsetToCursorOffset(prefixLen);
|
|
3255
|
+
const cursorEnd = this.visibleOffsetToCursorOffset(modelText.length - suffixLen);
|
|
3180
3256
|
if (cursorStart === null || cursorEnd === null) {
|
|
3181
3257
|
// Fallback: rebuild state from scratch (loses formatting)
|
|
3182
3258
|
// History was already recorded above
|
|
@@ -3464,12 +3540,13 @@ export class CakeEditor {
|
|
|
3464
3540
|
this.syncSelectionRects([]);
|
|
3465
3541
|
return;
|
|
3466
3542
|
}
|
|
3467
|
-
const lines =
|
|
3543
|
+
const lines = this.textModel.getLines();
|
|
3468
3544
|
const geometry = getSelectionGeometry({
|
|
3469
3545
|
root: this.contentRoot,
|
|
3470
3546
|
container: this.container,
|
|
3471
3547
|
docLines: lines,
|
|
3472
3548
|
selection,
|
|
3549
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
3473
3550
|
});
|
|
3474
3551
|
this.lastFocusRect = geometry.focusRect;
|
|
3475
3552
|
this.syncSelectionRects(geometry.selectionRects);
|
|
@@ -3483,12 +3560,13 @@ export class CakeEditor {
|
|
|
3483
3560
|
return;
|
|
3484
3561
|
}
|
|
3485
3562
|
this.contentRoot.classList.remove("cake-touch-mode");
|
|
3486
|
-
const lines =
|
|
3563
|
+
const lines = this.textModel.getLines();
|
|
3487
3564
|
const geometry = getSelectionGeometry({
|
|
3488
3565
|
root: this.contentRoot,
|
|
3489
3566
|
container: this.container,
|
|
3490
3567
|
docLines: lines,
|
|
3491
3568
|
selection: this.state.selection,
|
|
3569
|
+
resolveOffsetToLine: (offset) => this.textModel.resolveOffsetToLine(offset),
|
|
3492
3570
|
});
|
|
3493
3571
|
this.lastFocusRect = geometry.focusRect;
|
|
3494
3572
|
this.syncSelectionRects(geometry.selectionRects);
|
|
@@ -3673,7 +3751,7 @@ export class CakeEditor {
|
|
|
3673
3751
|
if (!this.contentRoot) {
|
|
3674
3752
|
return null;
|
|
3675
3753
|
}
|
|
3676
|
-
const lines =
|
|
3754
|
+
const lines = this.textModel.getLines();
|
|
3677
3755
|
const hit = hitTestFromLayout({
|
|
3678
3756
|
clientX,
|
|
3679
3757
|
clientY,
|
|
@@ -3736,10 +3814,10 @@ export class CakeEditor {
|
|
|
3736
3814
|
const lineIndexAttr = blockElement?.getAttribute("data-line-index") ?? null;
|
|
3737
3815
|
if (lineIndexAttr !== null) {
|
|
3738
3816
|
const lineIndex = Number.parseInt(lineIndexAttr, 10);
|
|
3739
|
-
const lines =
|
|
3817
|
+
const lines = this.textModel.getLines();
|
|
3740
3818
|
const lineInfo = lines[lineIndex];
|
|
3741
3819
|
if (lineInfo?.isAtomic) {
|
|
3742
|
-
const lineOffsets = getLineOffsets(
|
|
3820
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
3743
3821
|
const lineStart = lineOffsets[lineIndex] ?? 0;
|
|
3744
3822
|
const lineEnd = lineStart + lineInfo.cursorLength + (lineInfo.hasNewline ? 1 : 0);
|
|
3745
3823
|
const atomicSelection = {
|
|
@@ -3845,8 +3923,8 @@ export class CakeEditor {
|
|
|
3845
3923
|
return;
|
|
3846
3924
|
}
|
|
3847
3925
|
// Check if this is a full line selection (required for line drag)
|
|
3848
|
-
const lines =
|
|
3849
|
-
const lineOffsets = getLineOffsets(
|
|
3926
|
+
const lines = this.textModel.getLines();
|
|
3927
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
3850
3928
|
// Find which line the selection starts on
|
|
3851
3929
|
// We need to handle the case where selStart might be at the newline position
|
|
3852
3930
|
// of the previous line (offset - 1) due to DOM selection normalization
|
|
@@ -4258,10 +4336,10 @@ export class CakeEditor {
|
|
|
4258
4336
|
return;
|
|
4259
4337
|
}
|
|
4260
4338
|
// Get the plain text and source text for the selection
|
|
4261
|
-
const lines =
|
|
4262
|
-
const visibleText = getVisibleText(
|
|
4263
|
-
const visibleStart = cursorOffsetToVisibleOffset(
|
|
4264
|
-
const visibleEnd = cursorOffsetToVisibleOffset(
|
|
4339
|
+
const lines = this.textModel.getLines();
|
|
4340
|
+
const visibleText = this.textModel.getVisibleText();
|
|
4341
|
+
const visibleStart = this.textModel.cursorOffsetToVisibleOffset(start);
|
|
4342
|
+
const visibleEnd = this.textModel.cursorOffsetToVisibleOffset(end);
|
|
4265
4343
|
const plainText = visibleText.slice(visibleStart, visibleEnd);
|
|
4266
4344
|
// Get source text for the selection (use backward/forward to capture full markdown syntax)
|
|
4267
4345
|
const cursorSourceMap = this.state.map;
|
|
@@ -4323,10 +4401,10 @@ export class CakeEditor {
|
|
|
4323
4401
|
if (selection.start !== selection.end) {
|
|
4324
4402
|
const start = Math.min(selection.start, selection.end);
|
|
4325
4403
|
const end = Math.max(selection.start, selection.end);
|
|
4326
|
-
const lines =
|
|
4327
|
-
const visibleText = getVisibleText(
|
|
4328
|
-
const visibleStart = cursorOffsetToVisibleOffset(
|
|
4329
|
-
const visibleEnd = cursorOffsetToVisibleOffset(
|
|
4404
|
+
const lines = this.textModel.getLines();
|
|
4405
|
+
const visibleText = this.textModel.getVisibleText();
|
|
4406
|
+
const visibleStart = this.textModel.cursorOffsetToVisibleOffset(start);
|
|
4407
|
+
const visibleEnd = this.textModel.cursorOffsetToVisibleOffset(end);
|
|
4330
4408
|
const plainText = visibleText.slice(visibleStart, visibleEnd);
|
|
4331
4409
|
const cursorSourceMap = this.state.map;
|
|
4332
4410
|
const sourceStart = cursorSourceMap.cursorToSource(start, "backward");
|
|
@@ -4348,8 +4426,8 @@ export class CakeEditor {
|
|
|
4348
4426
|
return;
|
|
4349
4427
|
}
|
|
4350
4428
|
// Check if this is a full-line selection - if so, use line-level move
|
|
4351
|
-
const lines =
|
|
4352
|
-
const lineOffsets = getLineOffsets(
|
|
4429
|
+
const lines = this.textModel.getLines();
|
|
4430
|
+
const lineOffsets = this.textModel.getLineOffsets();
|
|
4353
4431
|
const fullLineInfo = this.detectFullLineSelection(dragStart, dragEnd, lines, lineOffsets);
|
|
4354
4432
|
if (fullLineInfo) {
|
|
4355
4433
|
// Full line drag - use line-level move
|
|
@@ -4500,7 +4578,7 @@ function getVisualRowBoundaries(params) {
|
|
|
4500
4578
|
if (!layout || layout.lines.length === 0) {
|
|
4501
4579
|
return { rowStart: 0, rowEnd: 0 };
|
|
4502
4580
|
}
|
|
4503
|
-
const resolved = resolveOffsetToLine(
|
|
4581
|
+
const resolved = params.resolveOffsetToLine(offset);
|
|
4504
4582
|
const line = layout.lines[resolved.lineIndex];
|
|
4505
4583
|
if (!line) {
|
|
4506
4584
|
return { rowStart: 0, rowEnd: 0 };
|