@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,5 +1,6 @@
|
|
|
1
|
-
import { CursorSourceBuilder, } from "./mapping/cursor-source-map";
|
|
1
|
+
import { CursorSourceBuilder, createCompositeCursorSourceMap, } from "./mapping/cursor-source-map";
|
|
2
2
|
import { graphemeSegments } from "../shared/segmenter";
|
|
3
|
+
import { getEditorTextModelForDoc, } from "../editor/internal/editor-text-model";
|
|
3
4
|
/** Type guard to check if a command is a structural edit */
|
|
4
5
|
export function isStructuralEdit(command) {
|
|
5
6
|
return (command.type === "insert" ||
|
|
@@ -17,7 +18,6 @@ export function isApplyEditCommand(command) {
|
|
|
17
18
|
command.type === "delete-backward" ||
|
|
18
19
|
command.type === "delete-forward");
|
|
19
20
|
}
|
|
20
|
-
const PLAIN_INSERT_TEXT_PATTERN = /^[\p{L}\p{N}\p{M}]+$/u;
|
|
21
21
|
const defaultSelection = { start: 0, end: 0, affinity: "forward" };
|
|
22
22
|
function removeFromArray(arr, value) {
|
|
23
23
|
const index = arr.indexOf(value);
|
|
@@ -36,6 +36,7 @@ export function createRuntimeForTests(extensions) {
|
|
|
36
36
|
const normalizeBlockFns = [];
|
|
37
37
|
const normalizeInlineFns = [];
|
|
38
38
|
const onEditFns = [];
|
|
39
|
+
const structuralReparsePolicies = [];
|
|
39
40
|
const domInlineRenderers = [];
|
|
40
41
|
const domBlockRenderers = [];
|
|
41
42
|
const editor = {
|
|
@@ -102,6 +103,10 @@ export function createRuntimeForTests(extensions) {
|
|
|
102
103
|
onEditFns.push(fn);
|
|
103
104
|
return () => removeFromArray(onEditFns, fn);
|
|
104
105
|
},
|
|
106
|
+
registerStructuralReparsePolicy: (fn) => {
|
|
107
|
+
structuralReparsePolicies.push(fn);
|
|
108
|
+
return () => removeFromArray(structuralReparsePolicies, fn);
|
|
109
|
+
},
|
|
105
110
|
registerOnPasteText: () => {
|
|
106
111
|
return () => { };
|
|
107
112
|
},
|
|
@@ -136,27 +141,42 @@ export function createRuntimeForTests(extensions) {
|
|
|
136
141
|
normalizeBlockFns,
|
|
137
142
|
normalizeInlineFns,
|
|
138
143
|
onEditFns,
|
|
144
|
+
structuralReparsePolicies,
|
|
139
145
|
domInlineRenderers,
|
|
140
146
|
domBlockRenderers,
|
|
141
147
|
});
|
|
142
148
|
return runtime;
|
|
143
149
|
}
|
|
144
150
|
export function createRuntimeFromRegistry(registry) {
|
|
145
|
-
const { toggleMarkerToSpec, inclusiveAtEndByKind, parseBlockFns, parseInlineFns, serializeBlockFns, serializeInlineFns, normalizeBlockFns, normalizeInlineFns, onEditFns, domInlineRenderers, domBlockRenderers, } = registry;
|
|
151
|
+
const { toggleMarkerToSpec, inclusiveAtEndByKind, parseBlockFns, parseInlineFns, serializeBlockFns, serializeInlineFns, normalizeBlockFns, normalizeInlineFns, onEditFns, structuralReparsePolicies, domInlineRenderers, domBlockRenderers, } = registry;
|
|
146
152
|
const isInclusiveAtEnd = (kind) => inclusiveAtEndByKind.get(kind) ?? true;
|
|
147
|
-
const
|
|
153
|
+
const removedBlockSentinel = Symbol("removed-block");
|
|
154
|
+
const removedInlineSentinel = Symbol("removed-inline");
|
|
155
|
+
const normalizedDocCache = new WeakMap();
|
|
156
|
+
const normalizedBlockCache = new WeakMap();
|
|
157
|
+
const normalizedInlineCache = new WeakMap();
|
|
158
|
+
const serializedDocCache = new WeakMap();
|
|
159
|
+
const serializedBlockCache = new WeakMap();
|
|
160
|
+
const serializedInlineCache = new WeakMap();
|
|
161
|
+
const segmentedDocCache = new WeakMap();
|
|
162
|
+
const emptyCursorMap = new CursorSourceBuilder().build().map;
|
|
163
|
+
const emptySerialized = {
|
|
164
|
+
source: "",
|
|
165
|
+
map: emptyCursorMap,
|
|
166
|
+
};
|
|
167
|
+
const extensionContext = {
|
|
148
168
|
parseInline: (source, start, end) => parseInlineRange(source, start, end),
|
|
149
169
|
serializeInline: (inline) => serializeInline(inline),
|
|
150
170
|
serializeBlock: (block) => serializeBlock(block),
|
|
151
171
|
};
|
|
152
172
|
function parseBlockAt(source, start) {
|
|
153
173
|
for (const parseBlock of parseBlockFns) {
|
|
154
|
-
const result = parseBlock(source, start,
|
|
174
|
+
const result = parseBlock(source, start, extensionContext);
|
|
155
175
|
if (result) {
|
|
156
176
|
return result;
|
|
157
177
|
}
|
|
158
178
|
}
|
|
159
|
-
return parseLiteralBlock(source, start,
|
|
179
|
+
return parseLiteralBlock(source, start, extensionContext);
|
|
160
180
|
}
|
|
161
181
|
function parseInlineRange(source, start, end) {
|
|
162
182
|
const inlines = [];
|
|
@@ -164,7 +184,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
164
184
|
while (pos < end) {
|
|
165
185
|
let matched = false;
|
|
166
186
|
for (const parseInline of parseInlineFns) {
|
|
167
|
-
const result = parseInline(source, pos, end,
|
|
187
|
+
const result = parseInline(source, pos, end, extensionContext);
|
|
168
188
|
if (result) {
|
|
169
189
|
inlines.push(result.inline);
|
|
170
190
|
pos = result.nextPos;
|
|
@@ -200,6 +220,10 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
200
220
|
return { type: "doc", blocks };
|
|
201
221
|
}
|
|
202
222
|
function serialize(doc) {
|
|
223
|
+
const cached = serializedDocCache.get(doc);
|
|
224
|
+
if (cached) {
|
|
225
|
+
return cached;
|
|
226
|
+
}
|
|
203
227
|
const builder = new CursorSourceBuilder();
|
|
204
228
|
const blocks = doc.blocks;
|
|
205
229
|
blocks.forEach((block, index) => {
|
|
@@ -209,41 +233,67 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
209
233
|
builder.appendText("\n");
|
|
210
234
|
}
|
|
211
235
|
});
|
|
212
|
-
|
|
236
|
+
const serialized = builder.build();
|
|
237
|
+
serializedDocCache.set(doc, serialized);
|
|
238
|
+
return serialized;
|
|
213
239
|
}
|
|
214
240
|
function serializeBlock(block) {
|
|
241
|
+
const cached = serializedBlockCache.get(block);
|
|
242
|
+
if (cached) {
|
|
243
|
+
return cached;
|
|
244
|
+
}
|
|
215
245
|
for (const serializeBlockFn of serializeBlockFns) {
|
|
216
|
-
const result = serializeBlockFn(block,
|
|
246
|
+
const result = serializeBlockFn(block, extensionContext);
|
|
217
247
|
if (result) {
|
|
248
|
+
serializedBlockCache.set(block, result);
|
|
218
249
|
return result;
|
|
219
250
|
}
|
|
220
251
|
}
|
|
221
252
|
if (block.type === "paragraph") {
|
|
222
|
-
|
|
253
|
+
const result = serializeParagraph(block, (inline) => serializeInline(inline));
|
|
254
|
+
serializedBlockCache.set(block, result);
|
|
255
|
+
return result;
|
|
223
256
|
}
|
|
224
257
|
if (block.type === "block-wrapper") {
|
|
225
|
-
|
|
258
|
+
const result = serializeBlockWrapper(block, (child) => serializeBlock(child));
|
|
259
|
+
serializedBlockCache.set(block, result);
|
|
260
|
+
return result;
|
|
226
261
|
}
|
|
227
|
-
|
|
262
|
+
serializedBlockCache.set(block, emptySerialized);
|
|
263
|
+
return emptySerialized;
|
|
228
264
|
}
|
|
229
265
|
function serializeInline(inline) {
|
|
266
|
+
const cached = serializedInlineCache.get(inline);
|
|
267
|
+
if (cached) {
|
|
268
|
+
return cached;
|
|
269
|
+
}
|
|
230
270
|
for (const serializeInlineFn of serializeInlineFns) {
|
|
231
|
-
const result = serializeInlineFn(inline,
|
|
271
|
+
const result = serializeInlineFn(inline, extensionContext);
|
|
232
272
|
if (result) {
|
|
273
|
+
serializedInlineCache.set(inline, result);
|
|
233
274
|
return result;
|
|
234
275
|
}
|
|
235
276
|
}
|
|
236
277
|
if (inline.type === "text") {
|
|
237
278
|
const builder = new CursorSourceBuilder();
|
|
238
279
|
builder.appendText(inline.text);
|
|
239
|
-
|
|
280
|
+
const result = builder.build();
|
|
281
|
+
serializedInlineCache.set(inline, result);
|
|
282
|
+
return result;
|
|
240
283
|
}
|
|
241
284
|
if (inline.type === "inline-wrapper") {
|
|
242
|
-
|
|
285
|
+
const result = serializeInlineWrapper(inline, (child) => serializeInline(child));
|
|
286
|
+
serializedInlineCache.set(inline, result);
|
|
287
|
+
return result;
|
|
243
288
|
}
|
|
244
|
-
|
|
289
|
+
serializedInlineCache.set(inline, emptySerialized);
|
|
290
|
+
return emptySerialized;
|
|
245
291
|
}
|
|
246
292
|
function normalize(doc) {
|
|
293
|
+
const cached = normalizedDocCache.get(doc);
|
|
294
|
+
if (cached) {
|
|
295
|
+
return cached;
|
|
296
|
+
}
|
|
247
297
|
let changed = false;
|
|
248
298
|
const blocks = [];
|
|
249
299
|
for (const block of doc.blocks) {
|
|
@@ -257,19 +307,28 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
257
307
|
}
|
|
258
308
|
blocks.push(normalized);
|
|
259
309
|
}
|
|
260
|
-
|
|
261
|
-
|
|
310
|
+
const normalized = changed
|
|
311
|
+
? {
|
|
312
|
+
type: "doc",
|
|
313
|
+
blocks,
|
|
314
|
+
}
|
|
315
|
+
: doc;
|
|
316
|
+
normalizedDocCache.set(doc, normalized);
|
|
317
|
+
if (normalized !== doc) {
|
|
318
|
+
normalizedDocCache.set(normalized, normalized);
|
|
262
319
|
}
|
|
263
|
-
return
|
|
264
|
-
type: "doc",
|
|
265
|
-
blocks,
|
|
266
|
-
};
|
|
320
|
+
return normalized;
|
|
267
321
|
}
|
|
268
322
|
function normalizeBlock(block) {
|
|
323
|
+
const cached = normalizedBlockCache.get(block);
|
|
324
|
+
if (cached !== undefined) {
|
|
325
|
+
return cached === removedBlockSentinel ? null : cached;
|
|
326
|
+
}
|
|
269
327
|
let next = block;
|
|
270
328
|
for (const normalizeBlockFn of normalizeBlockFns) {
|
|
271
329
|
const result = normalizeBlockFn(next);
|
|
272
330
|
if (result === null) {
|
|
331
|
+
normalizedBlockCache.set(block, removedBlockSentinel);
|
|
273
332
|
return null;
|
|
274
333
|
}
|
|
275
334
|
next = result;
|
|
@@ -278,46 +337,53 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
278
337
|
let changed = next !== block;
|
|
279
338
|
const content = [];
|
|
280
339
|
for (const inline of next.content) {
|
|
281
|
-
const
|
|
282
|
-
if (
|
|
340
|
+
const normalizedInline = normalizeInline(inline);
|
|
341
|
+
if (normalizedInline === null) {
|
|
283
342
|
changed = true;
|
|
284
343
|
continue;
|
|
285
344
|
}
|
|
286
|
-
if (
|
|
345
|
+
if (normalizedInline !== inline) {
|
|
287
346
|
changed = true;
|
|
288
347
|
}
|
|
289
|
-
content.push(
|
|
348
|
+
content.push(normalizedInline);
|
|
290
349
|
}
|
|
291
350
|
if (!changed) {
|
|
351
|
+
normalizedBlockCache.set(block, next);
|
|
292
352
|
return next;
|
|
293
353
|
}
|
|
294
|
-
|
|
354
|
+
const normalized = {
|
|
295
355
|
...next,
|
|
296
356
|
content,
|
|
297
357
|
};
|
|
358
|
+
normalizedBlockCache.set(block, normalized);
|
|
359
|
+
return normalized;
|
|
298
360
|
}
|
|
299
361
|
if (next.type === "block-wrapper") {
|
|
300
362
|
let changed = next !== block;
|
|
301
363
|
const blocks = [];
|
|
302
364
|
for (const child of next.blocks) {
|
|
303
|
-
const
|
|
304
|
-
if (
|
|
365
|
+
const normalizedChild = normalizeBlock(child);
|
|
366
|
+
if (normalizedChild === null) {
|
|
305
367
|
changed = true;
|
|
306
368
|
continue;
|
|
307
369
|
}
|
|
308
|
-
if (
|
|
370
|
+
if (normalizedChild !== child) {
|
|
309
371
|
changed = true;
|
|
310
372
|
}
|
|
311
|
-
blocks.push(
|
|
373
|
+
blocks.push(normalizedChild);
|
|
312
374
|
}
|
|
313
375
|
if (!changed) {
|
|
376
|
+
normalizedBlockCache.set(block, next);
|
|
314
377
|
return next;
|
|
315
378
|
}
|
|
316
|
-
|
|
379
|
+
const normalized = {
|
|
317
380
|
...next,
|
|
318
381
|
blocks,
|
|
319
382
|
};
|
|
383
|
+
normalizedBlockCache.set(block, normalized);
|
|
384
|
+
return normalized;
|
|
320
385
|
}
|
|
386
|
+
normalizedBlockCache.set(block, next);
|
|
321
387
|
return next;
|
|
322
388
|
}
|
|
323
389
|
function applyInlineNormalizers(inline) {
|
|
@@ -332,8 +398,13 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
332
398
|
return next;
|
|
333
399
|
}
|
|
334
400
|
function normalizeInline(inline) {
|
|
401
|
+
const cached = normalizedInlineCache.get(inline);
|
|
402
|
+
if (cached !== undefined) {
|
|
403
|
+
return cached === removedInlineSentinel ? null : cached;
|
|
404
|
+
}
|
|
335
405
|
const pre = applyInlineNormalizers(inline);
|
|
336
406
|
if (!pre) {
|
|
407
|
+
normalizedInlineCache.set(inline, removedInlineSentinel);
|
|
337
408
|
return null;
|
|
338
409
|
}
|
|
339
410
|
let next = pre;
|
|
@@ -345,35 +416,193 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
345
416
|
.filter((child) => child !== null),
|
|
346
417
|
};
|
|
347
418
|
}
|
|
348
|
-
|
|
419
|
+
const normalized = applyInlineNormalizers(next);
|
|
420
|
+
normalizedInlineCache.set(inline, normalized ?? removedInlineSentinel);
|
|
421
|
+
return normalized;
|
|
349
422
|
}
|
|
350
|
-
function
|
|
351
|
-
const
|
|
352
|
-
const normalized = normalize(doc);
|
|
353
|
-
const serialized = serialize(normalized);
|
|
423
|
+
function createTopLevelBlockSegment(block) {
|
|
424
|
+
const serialized = serializeBlock(block);
|
|
354
425
|
return {
|
|
426
|
+
block,
|
|
355
427
|
source: serialized.source,
|
|
356
|
-
selection,
|
|
357
428
|
map: serialized.map,
|
|
358
|
-
|
|
359
|
-
|
|
429
|
+
sourceLength: serialized.source.length,
|
|
430
|
+
cursorLength: serialized.map.cursorLength,
|
|
360
431
|
};
|
|
361
432
|
}
|
|
362
|
-
function
|
|
433
|
+
function hasSharedTopLevelBlockIdentity(previousDoc, nextDoc) {
|
|
434
|
+
if (previousDoc === nextDoc) {
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
if (previousDoc.blocks.length === 0 || nextDoc.blocks.length === 0) {
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
const previousBlocks = new Set(previousDoc.blocks);
|
|
441
|
+
return nextDoc.blocks.some((block) => previousBlocks.has(block));
|
|
442
|
+
}
|
|
443
|
+
function buildTopLevelSegments(doc, previous) {
|
|
444
|
+
const previousByBlock = new Map();
|
|
445
|
+
if (previous) {
|
|
446
|
+
for (const segment of previous.segments) {
|
|
447
|
+
previousByBlock.set(segment.block, segment);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return doc.blocks.map((block) => {
|
|
451
|
+
const reused = previousByBlock.get(block);
|
|
452
|
+
return reused ?? createTopLevelBlockSegment(block);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
function buildPrefixIndexes(segments) {
|
|
456
|
+
const cursorStarts = [];
|
|
457
|
+
const sourceStarts = [];
|
|
458
|
+
let cursorOffset = 0;
|
|
459
|
+
let sourceOffset = 0;
|
|
460
|
+
for (let i = 0; i < segments.length; i += 1) {
|
|
461
|
+
const segment = segments[i];
|
|
462
|
+
cursorStarts.push(cursorOffset);
|
|
463
|
+
sourceStarts.push(sourceOffset);
|
|
464
|
+
cursorOffset += segment?.cursorLength ?? 0;
|
|
465
|
+
sourceOffset += segment?.sourceLength ?? 0;
|
|
466
|
+
if (i < segments.length - 1) {
|
|
467
|
+
cursorOffset += 1;
|
|
468
|
+
sourceOffset += 1;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
cursorStarts,
|
|
473
|
+
sourceStarts,
|
|
474
|
+
totalCursorLength: cursorOffset,
|
|
475
|
+
totalSourceLength: sourceOffset,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function serializeSegmentRange(segments, startIndex, endIndex) {
|
|
479
|
+
if (startIndex >= endIndex) {
|
|
480
|
+
return "";
|
|
481
|
+
}
|
|
482
|
+
let source = "";
|
|
483
|
+
for (let i = startIndex; i < endIndex; i += 1) {
|
|
484
|
+
const segment = segments[i];
|
|
485
|
+
if (!segment) {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
source += segment.source;
|
|
489
|
+
if (i < segments.length - 1) {
|
|
490
|
+
source += "\n";
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return source;
|
|
494
|
+
}
|
|
495
|
+
function sourceOffsetForBlockStart(state, blockIndex) {
|
|
496
|
+
if (blockIndex <= 0) {
|
|
497
|
+
return 0;
|
|
498
|
+
}
|
|
499
|
+
if (blockIndex >= state.segments.length) {
|
|
500
|
+
return state.source.length;
|
|
501
|
+
}
|
|
502
|
+
return state.sourceStarts[blockIndex] ?? state.source.length;
|
|
503
|
+
}
|
|
504
|
+
function buildSegmentedSource(segments, previous) {
|
|
505
|
+
if (!previous) {
|
|
506
|
+
return serializeSegmentRange(segments, 0, segments.length);
|
|
507
|
+
}
|
|
508
|
+
const previousSegments = previous.segments;
|
|
509
|
+
let prefix = 0;
|
|
510
|
+
const maxPrefix = Math.min(previousSegments.length, segments.length);
|
|
511
|
+
while (prefix < maxPrefix &&
|
|
512
|
+
previousSegments[prefix]?.block === segments[prefix]?.block) {
|
|
513
|
+
prefix += 1;
|
|
514
|
+
}
|
|
515
|
+
let suffix = 0;
|
|
516
|
+
const maxSuffix = Math.min(previousSegments.length - prefix, segments.length - prefix);
|
|
517
|
+
while (suffix < maxSuffix &&
|
|
518
|
+
previousSegments[previousSegments.length - 1 - suffix]?.block ===
|
|
519
|
+
segments[segments.length - 1 - suffix]?.block) {
|
|
520
|
+
suffix += 1;
|
|
521
|
+
}
|
|
522
|
+
if (prefix === previousSegments.length && prefix === segments.length) {
|
|
523
|
+
return previous.source;
|
|
524
|
+
}
|
|
525
|
+
const oldStart = sourceOffsetForBlockStart(previous, prefix);
|
|
526
|
+
const oldEnd = sourceOffsetForBlockStart(previous, previousSegments.length - suffix);
|
|
527
|
+
const middle = serializeSegmentRange(segments, prefix, segments.length - suffix);
|
|
528
|
+
return (previous.source.slice(0, oldStart) +
|
|
529
|
+
middle +
|
|
530
|
+
previous.source.slice(oldEnd));
|
|
531
|
+
}
|
|
532
|
+
function buildSegmentedDocState(doc, previous) {
|
|
533
|
+
const cached = segmentedDocCache.get(doc);
|
|
534
|
+
if (cached) {
|
|
535
|
+
return cached;
|
|
536
|
+
}
|
|
537
|
+
const segments = buildTopLevelSegments(doc, previous);
|
|
538
|
+
const { cursorStarts, sourceStarts, totalCursorLength, totalSourceLength, } = buildPrefixIndexes(segments);
|
|
539
|
+
const source = buildSegmentedSource(segments, previous);
|
|
540
|
+
const map = createCompositeCursorSourceMap({
|
|
541
|
+
segments: segments.map((segment) => ({
|
|
542
|
+
map: segment.map,
|
|
543
|
+
cursorLength: segment.cursorLength,
|
|
544
|
+
sourceLength: segment.sourceLength,
|
|
545
|
+
})),
|
|
546
|
+
cursorStarts,
|
|
547
|
+
sourceStarts,
|
|
548
|
+
cursorLength: totalCursorLength,
|
|
549
|
+
});
|
|
550
|
+
const segmented = {
|
|
551
|
+
doc,
|
|
552
|
+
segments,
|
|
553
|
+
cursorStarts,
|
|
554
|
+
sourceStarts,
|
|
555
|
+
totalCursorLength,
|
|
556
|
+
totalSourceLength,
|
|
557
|
+
source,
|
|
558
|
+
map,
|
|
559
|
+
};
|
|
560
|
+
segmentedDocCache.set(doc, segmented);
|
|
561
|
+
return segmented;
|
|
562
|
+
}
|
|
563
|
+
function buildStateFromDoc(doc, selection, options) {
|
|
363
564
|
const normalized = normalize(doc);
|
|
364
|
-
const
|
|
565
|
+
const mode = options?.mode ?? "incremental";
|
|
566
|
+
const previousSegmented = mode === "incremental" && options?.previousState
|
|
567
|
+
? segmentedDocCache.get(options.previousState.doc)
|
|
568
|
+
: undefined;
|
|
569
|
+
const reusablePrevious = previousSegmented &&
|
|
570
|
+
hasSharedTopLevelBlockIdentity(previousSegmented.doc, normalized)
|
|
571
|
+
? previousSegmented
|
|
572
|
+
: undefined;
|
|
573
|
+
const segmented = buildSegmentedDocState(normalized, reusablePrevious);
|
|
365
574
|
return {
|
|
366
|
-
source:
|
|
575
|
+
source: segmented.source,
|
|
367
576
|
selection,
|
|
368
|
-
map:
|
|
577
|
+
map: segmented.map,
|
|
369
578
|
doc: normalized,
|
|
370
579
|
runtime: runtime,
|
|
371
580
|
};
|
|
372
581
|
}
|
|
373
|
-
function
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
582
|
+
function createState(source, selection = defaultSelection) {
|
|
583
|
+
const doc = parse(source);
|
|
584
|
+
return buildStateFromDoc(doc, selection, { mode: "full" });
|
|
585
|
+
}
|
|
586
|
+
function createStateFromDoc(doc, selection = defaultSelection, options) {
|
|
587
|
+
return buildStateFromDoc(doc, selection, options);
|
|
588
|
+
}
|
|
589
|
+
function isIncrementalDerivationCandidate(command, selection) {
|
|
590
|
+
if (selection.start !== selection.end) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
if (command.type === "insert") {
|
|
594
|
+
return command.text.length > 0 && !command.text.includes("\n");
|
|
595
|
+
}
|
|
596
|
+
return (command.type === "delete-backward" ||
|
|
597
|
+
command.type === "delete-forward" ||
|
|
598
|
+
command.type === "insert-line-break" ||
|
|
599
|
+
command.type === "insert-hard-line-break");
|
|
600
|
+
}
|
|
601
|
+
function shouldReparseAfterStructuralEdit(command) {
|
|
602
|
+
if (structuralReparsePolicies.length === 0) {
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
return structuralReparsePolicies.some((policy) => policy(command));
|
|
377
606
|
}
|
|
378
607
|
function applyEdit(command, state) {
|
|
379
608
|
// Extensions can either:
|
|
@@ -485,14 +714,19 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
485
714
|
}
|
|
486
715
|
return state;
|
|
487
716
|
}
|
|
488
|
-
// Structural edits operate
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
492
|
-
const
|
|
717
|
+
// Structural edits operate on the doc tree first. Common collapsed
|
|
718
|
+
// typing/edit flows stay on the incremental segmented path when policy
|
|
719
|
+
// allows; everything else takes the explicit full fallback path through
|
|
720
|
+
// source + parse.
|
|
721
|
+
const shouldReparse = shouldReparseAfterStructuralEdit(command);
|
|
722
|
+
const useIncrementalSegmentedDerivation = !shouldReparse && isIncrementalDerivationCandidate(command, selection);
|
|
723
|
+
const interim = createStateFromDoc(structural.doc, defaultSelection, {
|
|
724
|
+
mode: useIncrementalSegmentedDerivation ? "incremental" : "full",
|
|
725
|
+
previousState: useIncrementalSegmentedDerivation ? state : undefined,
|
|
726
|
+
});
|
|
493
727
|
const interimAffinity = structural.nextAffinity ?? "forward";
|
|
494
728
|
const caretSource = interim.map.cursorToSource(structural.nextCursor, interimAffinity);
|
|
495
|
-
if (
|
|
729
|
+
if (useIncrementalSegmentedDerivation) {
|
|
496
730
|
const caretCursor = interim.map.sourceToCursor(caretSource, interimAffinity);
|
|
497
731
|
return {
|
|
498
732
|
...interim,
|
|
@@ -530,11 +764,12 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
530
764
|
return state;
|
|
531
765
|
}
|
|
532
766
|
function applyStructuralEdit(command, doc, selection) {
|
|
533
|
-
const
|
|
767
|
+
const textModel = getEditorTextModelForDoc(doc);
|
|
768
|
+
const lines = textModel.getStructuralLines();
|
|
534
769
|
if (lines.length === 0) {
|
|
535
770
|
return null;
|
|
536
771
|
}
|
|
537
|
-
const docCursorLength =
|
|
772
|
+
const docCursorLength = textModel.getCursorLength();
|
|
538
773
|
const cursorStart = Math.max(0, Math.min(docCursorLength, Math.min(selection.start, selection.end)));
|
|
539
774
|
const cursorEnd = Math.max(0, Math.min(docCursorLength, Math.max(selection.start, selection.end)));
|
|
540
775
|
const affinity = selection.affinity ?? "forward";
|
|
@@ -576,12 +811,12 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
576
811
|
const shouldReplacePlaceholder = command.type === "insert" &&
|
|
577
812
|
replaceText.length > 0 &&
|
|
578
813
|
range.start === range.end &&
|
|
579
|
-
|
|
814
|
+
textModel.getGraphemeAtCursor(range.start) === "\u200B";
|
|
580
815
|
const effectiveRange = shouldReplacePlaceholder
|
|
581
816
|
? { start: range.start, end: Math.min(docCursorLength, range.start + 1) }
|
|
582
817
|
: range;
|
|
583
|
-
const startLoc =
|
|
584
|
-
const endLoc =
|
|
818
|
+
const startLoc = textModel.resolveOffsetToLine(effectiveRange.start);
|
|
819
|
+
const endLoc = textModel.resolveOffsetToLine(effectiveRange.end);
|
|
585
820
|
const startLine = lines[startLoc.lineIndex];
|
|
586
821
|
const endLine = lines[endLoc.lineIndex];
|
|
587
822
|
if (!startLine || !endLine) {
|
|
@@ -609,8 +844,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
609
844
|
let nextBlocks = updateBlocksAtPath(doc.blocks, prevLine.parentPath, (blocks) => blocks.map((block, index) => index === prevLine.indexInParent ? nextPrevBlock : block));
|
|
610
845
|
nextBlocks = updateBlocksAtPath(nextBlocks, endLine.parentPath, (blocks) => blocks.filter((_, index) => index !== endLine.indexInParent));
|
|
611
846
|
const nextDoc = { ...doc, blocks: nextBlocks };
|
|
612
|
-
const
|
|
613
|
-
const
|
|
847
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
848
|
+
const nextLines = nextModel.getStructuralLines();
|
|
849
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
614
850
|
const mergedLineIndex = nextLines.findIndex((line) => pathsEqual(line.path, prevLine.path));
|
|
615
851
|
const mergedLine = nextLines[mergedLineIndex];
|
|
616
852
|
const nextCursor = mergedLineIndex >= 0
|
|
@@ -667,8 +903,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
667
903
|
...doc,
|
|
668
904
|
blocks: updateBlocksAtPath(doc.blocks, parentPath, () => nextParentBlocks),
|
|
669
905
|
};
|
|
670
|
-
const
|
|
671
|
-
const
|
|
906
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
907
|
+
const nextLines = nextModel.getStructuralLines();
|
|
908
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
672
909
|
const nextLineIndex = Math.min(nextLines.length - 1, startLoc.lineIndex + 1);
|
|
673
910
|
return {
|
|
674
911
|
doc: nextDoc,
|
|
@@ -687,8 +924,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
687
924
|
...doc,
|
|
688
925
|
blocks: updateBlocksAtPath(doc.blocks, parentPath, () => ensured),
|
|
689
926
|
};
|
|
690
|
-
const
|
|
691
|
-
const
|
|
927
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
928
|
+
const nextLines = nextModel.getStructuralLines();
|
|
929
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
692
930
|
const nextLineIndex = Math.min(nextLines.length - 1, startLoc.lineIndex);
|
|
693
931
|
return {
|
|
694
932
|
doc: nextDoc,
|
|
@@ -718,8 +956,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
718
956
|
...doc,
|
|
719
957
|
blocks: updateBlocksAtPath(doc.blocks, parentPath, () => nextParentBlocks),
|
|
720
958
|
};
|
|
721
|
-
const
|
|
722
|
-
const
|
|
959
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
960
|
+
const nextLines = nextModel.getStructuralLines();
|
|
961
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
723
962
|
const nextLineIndex = Math.max(0, startLoc.lineIndex - 1);
|
|
724
963
|
return {
|
|
725
964
|
doc: nextDoc,
|
|
@@ -776,8 +1015,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
776
1015
|
...doc,
|
|
777
1016
|
blocks: updateBlocksAtPath(doc.blocks, wrapperParentPath, () => nextParentBlocks),
|
|
778
1017
|
};
|
|
779
|
-
const
|
|
780
|
-
const
|
|
1018
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
1019
|
+
const nextLines = nextModel.getStructuralLines();
|
|
1020
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
781
1021
|
const insertedPath = [...wrapperParentPath, wrapperIndexInParent + 1];
|
|
782
1022
|
const insertedLineIndex = nextLines.findIndex((line) => pathsEqual(line.path, insertedPath));
|
|
783
1023
|
const nextCursor = insertedLineIndex >= 0 ? (lineStarts[insertedLineIndex] ?? 0) : 0;
|
|
@@ -790,12 +1030,12 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
790
1030
|
}
|
|
791
1031
|
const hasSelectedText = effectiveRange.start !== effectiveRange.end;
|
|
792
1032
|
const baseMarks = hasSelectedText
|
|
793
|
-
? commonMarksAcrossSelection(lines, effectiveRange.start, effectiveRange.end
|
|
1033
|
+
? commonMarksAcrossSelection(textModel, lines, effectiveRange.start, effectiveRange.end)
|
|
794
1034
|
: marksAtCursor(startRuns, startLoc.offsetInLine, affinity);
|
|
795
1035
|
const insertDoc = parse(replaceText);
|
|
796
|
-
const
|
|
1036
|
+
const insertModel = getEditorTextModelForDoc(insertDoc);
|
|
797
1037
|
const insertBlocks = insertDoc.blocks;
|
|
798
|
-
const insertCursorLength =
|
|
1038
|
+
const insertCursorLength = insertModel.getCursorLength();
|
|
799
1039
|
const replacementBlocks = buildReplacementBlocks({
|
|
800
1040
|
baseMarks,
|
|
801
1041
|
beforeRuns,
|
|
@@ -811,9 +1051,10 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
811
1051
|
...doc,
|
|
812
1052
|
blocks: updateBlocksAtPath(doc.blocks, parentPath, () => nextParentBlocks),
|
|
813
1053
|
};
|
|
814
|
-
const
|
|
1054
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
1055
|
+
const nextDocCursorLength = nextModel.getCursorLength();
|
|
815
1056
|
const nextCursor = Math.max(0, Math.min(nextDocCursorLength, effectiveRange.start + insertCursorLength));
|
|
816
|
-
const nextLines =
|
|
1057
|
+
const nextLines = nextModel.getStructuralLines();
|
|
817
1058
|
const around = marksAroundCursor(nextDoc, nextCursor);
|
|
818
1059
|
const fallbackAffinity = command.type === "delete-backward"
|
|
819
1060
|
? "backward"
|
|
@@ -830,8 +1071,8 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
830
1071
|
// If the cursor is at the start of a non-first line (i.e., right after a
|
|
831
1072
|
// serialized newline), keep affinity "forward" so selection anchors in the
|
|
832
1073
|
// following line, not at the end of the previous one.
|
|
833
|
-
const nextLoc =
|
|
834
|
-
const lineStarts =
|
|
1074
|
+
const nextLoc = nextModel.resolveOffsetToLine(nextCursor);
|
|
1075
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
835
1076
|
if (nextLoc.lineIndex > 0 &&
|
|
836
1077
|
nextLoc.offsetInLine === 0 &&
|
|
837
1078
|
nextCursor === (lineStarts[nextLoc.lineIndex] ?? 0)) {
|
|
@@ -843,128 +1084,6 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
843
1084
|
nextAffinity,
|
|
844
1085
|
};
|
|
845
1086
|
}
|
|
846
|
-
function cursorLengthForLines(lines) {
|
|
847
|
-
let length = 0;
|
|
848
|
-
for (const line of lines) {
|
|
849
|
-
length += line.cursorLength;
|
|
850
|
-
if (line.hasNewline) {
|
|
851
|
-
length += 1;
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
return length;
|
|
855
|
-
}
|
|
856
|
-
function flattenDocToLines(doc) {
|
|
857
|
-
const entries = [];
|
|
858
|
-
const visit = (blocks, prefix) => {
|
|
859
|
-
blocks.forEach((block, index) => {
|
|
860
|
-
const path = [...prefix, index];
|
|
861
|
-
if (block.type === "block-wrapper") {
|
|
862
|
-
visit(block.blocks, path);
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
entries.push({ path, block });
|
|
866
|
-
});
|
|
867
|
-
};
|
|
868
|
-
visit(doc.blocks, []);
|
|
869
|
-
if (entries.length === 0) {
|
|
870
|
-
return [
|
|
871
|
-
{
|
|
872
|
-
path: [0],
|
|
873
|
-
parentPath: [],
|
|
874
|
-
indexInParent: 0,
|
|
875
|
-
block: { type: "paragraph", content: [] },
|
|
876
|
-
text: "",
|
|
877
|
-
cursorLength: 0,
|
|
878
|
-
hasNewline: false,
|
|
879
|
-
},
|
|
880
|
-
];
|
|
881
|
-
}
|
|
882
|
-
return entries.map((entry, i) => {
|
|
883
|
-
const parentPath = entry.path.slice(0, -1);
|
|
884
|
-
const indexInParent = entry.path[entry.path.length - 1] ?? 0;
|
|
885
|
-
const text = blockVisibleText(entry.block);
|
|
886
|
-
return {
|
|
887
|
-
path: entry.path,
|
|
888
|
-
parentPath,
|
|
889
|
-
indexInParent,
|
|
890
|
-
block: entry.block,
|
|
891
|
-
text,
|
|
892
|
-
cursorLength: graphemeSegments(text).length,
|
|
893
|
-
hasNewline: i < entries.length - 1,
|
|
894
|
-
};
|
|
895
|
-
});
|
|
896
|
-
}
|
|
897
|
-
function resolveCursorToLine(lines, cursorOffset) {
|
|
898
|
-
const total = cursorLengthForLines(lines);
|
|
899
|
-
const clamped = Math.max(0, Math.min(cursorOffset, total));
|
|
900
|
-
let start = 0;
|
|
901
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
902
|
-
const line = lines[i];
|
|
903
|
-
const end = start + line.cursorLength;
|
|
904
|
-
if (clamped <= end || i === lines.length - 1) {
|
|
905
|
-
return {
|
|
906
|
-
lineIndex: i,
|
|
907
|
-
offsetInLine: Math.max(0, Math.min(clamped - start, line.cursorLength)),
|
|
908
|
-
};
|
|
909
|
-
}
|
|
910
|
-
start = end + (line.hasNewline ? 1 : 0);
|
|
911
|
-
if (line.hasNewline && clamped === start) {
|
|
912
|
-
return {
|
|
913
|
-
lineIndex: Math.min(lines.length - 1, i + 1),
|
|
914
|
-
offsetInLine: 0,
|
|
915
|
-
};
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
return {
|
|
919
|
-
lineIndex: lines.length - 1,
|
|
920
|
-
offsetInLine: lines[lines.length - 1]?.cursorLength ?? 0,
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
function getLineStartOffsets(lines) {
|
|
924
|
-
const offsets = [];
|
|
925
|
-
let current = 0;
|
|
926
|
-
for (const line of lines) {
|
|
927
|
-
offsets.push(current);
|
|
928
|
-
current += line.cursorLength + (line.hasNewline ? 1 : 0);
|
|
929
|
-
}
|
|
930
|
-
return offsets;
|
|
931
|
-
}
|
|
932
|
-
function graphemeAtCursor(lines, cursorOffset) {
|
|
933
|
-
const loc = resolveCursorToLine(lines, cursorOffset);
|
|
934
|
-
const line = lines[loc.lineIndex];
|
|
935
|
-
if (!line) {
|
|
936
|
-
return null;
|
|
937
|
-
}
|
|
938
|
-
if (loc.offsetInLine >= line.cursorLength) {
|
|
939
|
-
return null;
|
|
940
|
-
}
|
|
941
|
-
const segments = Array.from(graphemeSegments(line.text));
|
|
942
|
-
return segments[loc.offsetInLine]?.segment ?? null;
|
|
943
|
-
}
|
|
944
|
-
function blockVisibleText(block) {
|
|
945
|
-
if (block.type === "paragraph") {
|
|
946
|
-
return block.content.map(inlineVisibleText).join("");
|
|
947
|
-
}
|
|
948
|
-
if (block.type === "block-atom") {
|
|
949
|
-
return "";
|
|
950
|
-
}
|
|
951
|
-
if (block.type === "block-wrapper") {
|
|
952
|
-
return block.blocks.map(blockVisibleText).join("\n");
|
|
953
|
-
}
|
|
954
|
-
return "";
|
|
955
|
-
}
|
|
956
|
-
function inlineVisibleText(inline) {
|
|
957
|
-
if (inline.type === "text") {
|
|
958
|
-
return inline.text;
|
|
959
|
-
}
|
|
960
|
-
if (inline.type === "inline-wrapper") {
|
|
961
|
-
return inline.children.map(inlineVisibleText).join("");
|
|
962
|
-
}
|
|
963
|
-
if (inline.type === "inline-atom") {
|
|
964
|
-
return " ";
|
|
965
|
-
}
|
|
966
|
-
return "";
|
|
967
|
-
}
|
|
968
1087
|
function stableStringify(value) {
|
|
969
1088
|
if (value === null || value === undefined) {
|
|
970
1089
|
return "";
|
|
@@ -1135,13 +1254,14 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1135
1254
|
return null;
|
|
1136
1255
|
}
|
|
1137
1256
|
function marksAroundCursor(doc, cursorOffset) {
|
|
1138
|
-
const
|
|
1139
|
-
const
|
|
1257
|
+
const textModel = getEditorTextModelForDoc(doc);
|
|
1258
|
+
const lines = textModel.getStructuralLines();
|
|
1259
|
+
const loc = textModel.resolveOffsetToLine(cursorOffset);
|
|
1140
1260
|
const line = lines[loc.lineIndex];
|
|
1141
1261
|
if (!line) {
|
|
1142
1262
|
return { left: [], right: [] };
|
|
1143
1263
|
}
|
|
1144
|
-
const block =
|
|
1264
|
+
const block = line.block;
|
|
1145
1265
|
if (!block || block.type !== "paragraph") {
|
|
1146
1266
|
return { left: [], right: [] };
|
|
1147
1267
|
}
|
|
@@ -1315,19 +1435,19 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1315
1435
|
});
|
|
1316
1436
|
return blocks;
|
|
1317
1437
|
}
|
|
1318
|
-
function commonMarksAcrossSelection(lines, startCursor, endCursor
|
|
1438
|
+
function commonMarksAcrossSelection(textModel, lines, startCursor, endCursor) {
|
|
1319
1439
|
if (startCursor === endCursor) {
|
|
1320
1440
|
return [];
|
|
1321
1441
|
}
|
|
1322
|
-
const startLoc =
|
|
1323
|
-
const endLoc =
|
|
1442
|
+
const startLoc = textModel.resolveOffsetToLine(startCursor);
|
|
1443
|
+
const endLoc = textModel.resolveOffsetToLine(endCursor);
|
|
1324
1444
|
const slices = [];
|
|
1325
1445
|
for (let lineIndex = startLoc.lineIndex; lineIndex <= endLoc.lineIndex; lineIndex += 1) {
|
|
1326
1446
|
const line = lines[lineIndex];
|
|
1327
1447
|
if (!line) {
|
|
1328
1448
|
continue;
|
|
1329
1449
|
}
|
|
1330
|
-
const block =
|
|
1450
|
+
const block = line.block;
|
|
1331
1451
|
if (!block || block.type !== "paragraph") {
|
|
1332
1452
|
continue;
|
|
1333
1453
|
}
|
|
@@ -1539,8 +1659,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1539
1659
|
// otherwise we create ambiguous marker sequences (e.g., *italic***** doesn't parse).
|
|
1540
1660
|
const betweenLen = insertAtForward - insertAtBackward;
|
|
1541
1661
|
const preferBackward = insertAtBackward !== insertAtForward && openLen <= betweenLen;
|
|
1542
|
-
const insertAt = placeholderPos ??
|
|
1543
|
-
(preferBackward ? insertAtBackward : insertAtForward);
|
|
1662
|
+
const insertAt = placeholderPos ?? (preferBackward ? insertAtBackward : insertAtForward);
|
|
1544
1663
|
const nextSource = placeholderPos !== null
|
|
1545
1664
|
? source.slice(0, insertAt) +
|
|
1546
1665
|
openMarker +
|
|
@@ -1566,9 +1685,10 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1566
1685
|
}
|
|
1567
1686
|
const cursorStart = Math.min(selection.start, selection.end);
|
|
1568
1687
|
const cursorEnd = Math.max(selection.start, selection.end);
|
|
1569
|
-
const
|
|
1570
|
-
const
|
|
1571
|
-
const
|
|
1688
|
+
const selectionModel = getEditorTextModelForDoc(state.doc);
|
|
1689
|
+
const linesForSelection = selectionModel.getStructuralLines();
|
|
1690
|
+
const startLoc = selectionModel.resolveOffsetToLine(cursorStart);
|
|
1691
|
+
const endLoc = selectionModel.resolveOffsetToLine(cursorEnd);
|
|
1572
1692
|
const splitRunsOnNewlines = (runs) => {
|
|
1573
1693
|
const split = [];
|
|
1574
1694
|
for (const run of runs) {
|
|
@@ -1606,7 +1726,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1606
1726
|
if (startInLine === endInLine) {
|
|
1607
1727
|
continue;
|
|
1608
1728
|
}
|
|
1609
|
-
const block =
|
|
1729
|
+
const block = line.block;
|
|
1610
1730
|
if (!block || block.type !== "paragraph") {
|
|
1611
1731
|
continue;
|
|
1612
1732
|
}
|
|
@@ -1743,22 +1863,23 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1743
1863
|
}
|
|
1744
1864
|
function serializeSelection(state, selection) {
|
|
1745
1865
|
const normalized = normalizeSelection(selection);
|
|
1746
|
-
const
|
|
1747
|
-
const
|
|
1866
|
+
const textModel = getEditorTextModelForDoc(state.doc);
|
|
1867
|
+
const lines = textModel.getStructuralLines();
|
|
1868
|
+
const docCursorLength = textModel.getCursorLength();
|
|
1748
1869
|
const cursorStart = Math.max(0, Math.min(docCursorLength, Math.min(normalized.start, normalized.end)));
|
|
1749
1870
|
const cursorEnd = Math.max(0, Math.min(docCursorLength, Math.max(normalized.start, normalized.end)));
|
|
1750
1871
|
if (cursorStart === cursorEnd) {
|
|
1751
1872
|
return "";
|
|
1752
1873
|
}
|
|
1753
|
-
const startLoc =
|
|
1754
|
-
const endLoc =
|
|
1874
|
+
const startLoc = textModel.resolveOffsetToLine(cursorStart);
|
|
1875
|
+
const endLoc = textModel.resolveOffsetToLine(cursorEnd);
|
|
1755
1876
|
const blocks = [];
|
|
1756
1877
|
for (let lineIndex = startLoc.lineIndex; lineIndex <= endLoc.lineIndex; lineIndex += 1) {
|
|
1757
1878
|
const line = lines[lineIndex];
|
|
1758
1879
|
if (!line) {
|
|
1759
1880
|
continue;
|
|
1760
1881
|
}
|
|
1761
|
-
const block =
|
|
1882
|
+
const block = line.block;
|
|
1762
1883
|
if (!block || block.type !== "paragraph") {
|
|
1763
1884
|
continue;
|
|
1764
1885
|
}
|
|
@@ -1827,15 +1948,16 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1827
1948
|
}
|
|
1828
1949
|
function serializeSelectionToHtml(state, selection) {
|
|
1829
1950
|
const normalized = normalizeSelection(selection);
|
|
1830
|
-
const
|
|
1831
|
-
const
|
|
1951
|
+
const textModel = getEditorTextModelForDoc(state.doc);
|
|
1952
|
+
const lines = textModel.getStructuralLines();
|
|
1953
|
+
const docCursorLength = textModel.getCursorLength();
|
|
1832
1954
|
const cursorStart = Math.max(0, Math.min(docCursorLength, Math.min(normalized.start, normalized.end)));
|
|
1833
1955
|
const cursorEnd = Math.max(0, Math.min(docCursorLength, Math.max(normalized.start, normalized.end)));
|
|
1834
1956
|
if (cursorStart === cursorEnd) {
|
|
1835
1957
|
return "";
|
|
1836
1958
|
}
|
|
1837
|
-
const startLoc =
|
|
1838
|
-
const endLoc =
|
|
1959
|
+
const startLoc = textModel.resolveOffsetToLine(cursorStart);
|
|
1960
|
+
const endLoc = textModel.resolveOffsetToLine(cursorEnd);
|
|
1839
1961
|
let html = "";
|
|
1840
1962
|
let activeList = null;
|
|
1841
1963
|
const closeList = () => {
|
|
@@ -1859,7 +1981,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1859
1981
|
if (!line) {
|
|
1860
1982
|
continue;
|
|
1861
1983
|
}
|
|
1862
|
-
const block =
|
|
1984
|
+
const block = line.block;
|
|
1863
1985
|
if (!block || block.type !== "paragraph") {
|
|
1864
1986
|
continue;
|
|
1865
1987
|
}
|
|
@@ -1942,6 +2064,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1942
2064
|
parse,
|
|
1943
2065
|
serialize,
|
|
1944
2066
|
createState,
|
|
2067
|
+
createStateFromDoc,
|
|
1945
2068
|
updateSelection,
|
|
1946
2069
|
serializeSelection,
|
|
1947
2070
|
serializeSelectionToHtml,
|