@blankdotpage/cake 0.1.68 → 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 +4 -0
- package/dist/cake/core/runtime.d.ts.map +1 -1
- package/dist/cake/core/runtime.js +332 -215
- 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 +8 -1
- package/dist/cake/editor/cake-editor.d.ts.map +1 -1
- package/dist/cake/editor/cake-editor.js +172 -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/link/link.d.ts.map +1 -1
- package/dist/cake/extensions/link/link.js +1 -7
- package/dist/cake/extensions/shared/structural-reparse-policy.js +2 -2
- 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" ||
|
|
@@ -149,19 +150,33 @@ export function createRuntimeForTests(extensions) {
|
|
|
149
150
|
export function createRuntimeFromRegistry(registry) {
|
|
150
151
|
const { toggleMarkerToSpec, inclusiveAtEndByKind, parseBlockFns, parseInlineFns, serializeBlockFns, serializeInlineFns, normalizeBlockFns, normalizeInlineFns, onEditFns, structuralReparsePolicies, domInlineRenderers, domBlockRenderers, } = registry;
|
|
151
152
|
const isInclusiveAtEnd = (kind) => inclusiveAtEndByKind.get(kind) ?? true;
|
|
152
|
-
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 = {
|
|
153
168
|
parseInline: (source, start, end) => parseInlineRange(source, start, end),
|
|
154
169
|
serializeInline: (inline) => serializeInline(inline),
|
|
155
170
|
serializeBlock: (block) => serializeBlock(block),
|
|
156
171
|
};
|
|
157
172
|
function parseBlockAt(source, start) {
|
|
158
173
|
for (const parseBlock of parseBlockFns) {
|
|
159
|
-
const result = parseBlock(source, start,
|
|
174
|
+
const result = parseBlock(source, start, extensionContext);
|
|
160
175
|
if (result) {
|
|
161
176
|
return result;
|
|
162
177
|
}
|
|
163
178
|
}
|
|
164
|
-
return parseLiteralBlock(source, start,
|
|
179
|
+
return parseLiteralBlock(source, start, extensionContext);
|
|
165
180
|
}
|
|
166
181
|
function parseInlineRange(source, start, end) {
|
|
167
182
|
const inlines = [];
|
|
@@ -169,7 +184,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
169
184
|
while (pos < end) {
|
|
170
185
|
let matched = false;
|
|
171
186
|
for (const parseInline of parseInlineFns) {
|
|
172
|
-
const result = parseInline(source, pos, end,
|
|
187
|
+
const result = parseInline(source, pos, end, extensionContext);
|
|
173
188
|
if (result) {
|
|
174
189
|
inlines.push(result.inline);
|
|
175
190
|
pos = result.nextPos;
|
|
@@ -205,6 +220,10 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
205
220
|
return { type: "doc", blocks };
|
|
206
221
|
}
|
|
207
222
|
function serialize(doc) {
|
|
223
|
+
const cached = serializedDocCache.get(doc);
|
|
224
|
+
if (cached) {
|
|
225
|
+
return cached;
|
|
226
|
+
}
|
|
208
227
|
const builder = new CursorSourceBuilder();
|
|
209
228
|
const blocks = doc.blocks;
|
|
210
229
|
blocks.forEach((block, index) => {
|
|
@@ -214,41 +233,67 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
214
233
|
builder.appendText("\n");
|
|
215
234
|
}
|
|
216
235
|
});
|
|
217
|
-
|
|
236
|
+
const serialized = builder.build();
|
|
237
|
+
serializedDocCache.set(doc, serialized);
|
|
238
|
+
return serialized;
|
|
218
239
|
}
|
|
219
240
|
function serializeBlock(block) {
|
|
241
|
+
const cached = serializedBlockCache.get(block);
|
|
242
|
+
if (cached) {
|
|
243
|
+
return cached;
|
|
244
|
+
}
|
|
220
245
|
for (const serializeBlockFn of serializeBlockFns) {
|
|
221
|
-
const result = serializeBlockFn(block,
|
|
246
|
+
const result = serializeBlockFn(block, extensionContext);
|
|
222
247
|
if (result) {
|
|
248
|
+
serializedBlockCache.set(block, result);
|
|
223
249
|
return result;
|
|
224
250
|
}
|
|
225
251
|
}
|
|
226
252
|
if (block.type === "paragraph") {
|
|
227
|
-
|
|
253
|
+
const result = serializeParagraph(block, (inline) => serializeInline(inline));
|
|
254
|
+
serializedBlockCache.set(block, result);
|
|
255
|
+
return result;
|
|
228
256
|
}
|
|
229
257
|
if (block.type === "block-wrapper") {
|
|
230
|
-
|
|
258
|
+
const result = serializeBlockWrapper(block, (child) => serializeBlock(child));
|
|
259
|
+
serializedBlockCache.set(block, result);
|
|
260
|
+
return result;
|
|
231
261
|
}
|
|
232
|
-
|
|
262
|
+
serializedBlockCache.set(block, emptySerialized);
|
|
263
|
+
return emptySerialized;
|
|
233
264
|
}
|
|
234
265
|
function serializeInline(inline) {
|
|
266
|
+
const cached = serializedInlineCache.get(inline);
|
|
267
|
+
if (cached) {
|
|
268
|
+
return cached;
|
|
269
|
+
}
|
|
235
270
|
for (const serializeInlineFn of serializeInlineFns) {
|
|
236
|
-
const result = serializeInlineFn(inline,
|
|
271
|
+
const result = serializeInlineFn(inline, extensionContext);
|
|
237
272
|
if (result) {
|
|
273
|
+
serializedInlineCache.set(inline, result);
|
|
238
274
|
return result;
|
|
239
275
|
}
|
|
240
276
|
}
|
|
241
277
|
if (inline.type === "text") {
|
|
242
278
|
const builder = new CursorSourceBuilder();
|
|
243
279
|
builder.appendText(inline.text);
|
|
244
|
-
|
|
280
|
+
const result = builder.build();
|
|
281
|
+
serializedInlineCache.set(inline, result);
|
|
282
|
+
return result;
|
|
245
283
|
}
|
|
246
284
|
if (inline.type === "inline-wrapper") {
|
|
247
|
-
|
|
285
|
+
const result = serializeInlineWrapper(inline, (child) => serializeInline(child));
|
|
286
|
+
serializedInlineCache.set(inline, result);
|
|
287
|
+
return result;
|
|
248
288
|
}
|
|
249
|
-
|
|
289
|
+
serializedInlineCache.set(inline, emptySerialized);
|
|
290
|
+
return emptySerialized;
|
|
250
291
|
}
|
|
251
292
|
function normalize(doc) {
|
|
293
|
+
const cached = normalizedDocCache.get(doc);
|
|
294
|
+
if (cached) {
|
|
295
|
+
return cached;
|
|
296
|
+
}
|
|
252
297
|
let changed = false;
|
|
253
298
|
const blocks = [];
|
|
254
299
|
for (const block of doc.blocks) {
|
|
@@ -262,19 +307,28 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
262
307
|
}
|
|
263
308
|
blocks.push(normalized);
|
|
264
309
|
}
|
|
265
|
-
|
|
266
|
-
|
|
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);
|
|
267
319
|
}
|
|
268
|
-
return
|
|
269
|
-
type: "doc",
|
|
270
|
-
blocks,
|
|
271
|
-
};
|
|
320
|
+
return normalized;
|
|
272
321
|
}
|
|
273
322
|
function normalizeBlock(block) {
|
|
323
|
+
const cached = normalizedBlockCache.get(block);
|
|
324
|
+
if (cached !== undefined) {
|
|
325
|
+
return cached === removedBlockSentinel ? null : cached;
|
|
326
|
+
}
|
|
274
327
|
let next = block;
|
|
275
328
|
for (const normalizeBlockFn of normalizeBlockFns) {
|
|
276
329
|
const result = normalizeBlockFn(next);
|
|
277
330
|
if (result === null) {
|
|
331
|
+
normalizedBlockCache.set(block, removedBlockSentinel);
|
|
278
332
|
return null;
|
|
279
333
|
}
|
|
280
334
|
next = result;
|
|
@@ -283,46 +337,53 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
283
337
|
let changed = next !== block;
|
|
284
338
|
const content = [];
|
|
285
339
|
for (const inline of next.content) {
|
|
286
|
-
const
|
|
287
|
-
if (
|
|
340
|
+
const normalizedInline = normalizeInline(inline);
|
|
341
|
+
if (normalizedInline === null) {
|
|
288
342
|
changed = true;
|
|
289
343
|
continue;
|
|
290
344
|
}
|
|
291
|
-
if (
|
|
345
|
+
if (normalizedInline !== inline) {
|
|
292
346
|
changed = true;
|
|
293
347
|
}
|
|
294
|
-
content.push(
|
|
348
|
+
content.push(normalizedInline);
|
|
295
349
|
}
|
|
296
350
|
if (!changed) {
|
|
351
|
+
normalizedBlockCache.set(block, next);
|
|
297
352
|
return next;
|
|
298
353
|
}
|
|
299
|
-
|
|
354
|
+
const normalized = {
|
|
300
355
|
...next,
|
|
301
356
|
content,
|
|
302
357
|
};
|
|
358
|
+
normalizedBlockCache.set(block, normalized);
|
|
359
|
+
return normalized;
|
|
303
360
|
}
|
|
304
361
|
if (next.type === "block-wrapper") {
|
|
305
362
|
let changed = next !== block;
|
|
306
363
|
const blocks = [];
|
|
307
364
|
for (const child of next.blocks) {
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
365
|
+
const normalizedChild = normalizeBlock(child);
|
|
366
|
+
if (normalizedChild === null) {
|
|
310
367
|
changed = true;
|
|
311
368
|
continue;
|
|
312
369
|
}
|
|
313
|
-
if (
|
|
370
|
+
if (normalizedChild !== child) {
|
|
314
371
|
changed = true;
|
|
315
372
|
}
|
|
316
|
-
blocks.push(
|
|
373
|
+
blocks.push(normalizedChild);
|
|
317
374
|
}
|
|
318
375
|
if (!changed) {
|
|
376
|
+
normalizedBlockCache.set(block, next);
|
|
319
377
|
return next;
|
|
320
378
|
}
|
|
321
|
-
|
|
379
|
+
const normalized = {
|
|
322
380
|
...next,
|
|
323
381
|
blocks,
|
|
324
382
|
};
|
|
383
|
+
normalizedBlockCache.set(block, normalized);
|
|
384
|
+
return normalized;
|
|
325
385
|
}
|
|
386
|
+
normalizedBlockCache.set(block, next);
|
|
326
387
|
return next;
|
|
327
388
|
}
|
|
328
389
|
function applyInlineNormalizers(inline) {
|
|
@@ -337,8 +398,13 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
337
398
|
return next;
|
|
338
399
|
}
|
|
339
400
|
function normalizeInline(inline) {
|
|
401
|
+
const cached = normalizedInlineCache.get(inline);
|
|
402
|
+
if (cached !== undefined) {
|
|
403
|
+
return cached === removedInlineSentinel ? null : cached;
|
|
404
|
+
}
|
|
340
405
|
const pre = applyInlineNormalizers(inline);
|
|
341
406
|
if (!pre) {
|
|
407
|
+
normalizedInlineCache.set(inline, removedInlineSentinel);
|
|
342
408
|
return null;
|
|
343
409
|
}
|
|
344
410
|
let next = pre;
|
|
@@ -350,31 +416,188 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
350
416
|
.filter((child) => child !== null),
|
|
351
417
|
};
|
|
352
418
|
}
|
|
353
|
-
|
|
419
|
+
const normalized = applyInlineNormalizers(next);
|
|
420
|
+
normalizedInlineCache.set(inline, normalized ?? removedInlineSentinel);
|
|
421
|
+
return normalized;
|
|
354
422
|
}
|
|
355
|
-
function
|
|
356
|
-
const
|
|
357
|
-
const normalized = normalize(doc);
|
|
358
|
-
const serialized = serialize(normalized);
|
|
423
|
+
function createTopLevelBlockSegment(block) {
|
|
424
|
+
const serialized = serializeBlock(block);
|
|
359
425
|
return {
|
|
426
|
+
block,
|
|
360
427
|
source: serialized.source,
|
|
361
|
-
selection,
|
|
362
428
|
map: serialized.map,
|
|
363
|
-
|
|
364
|
-
|
|
429
|
+
sourceLength: serialized.source.length,
|
|
430
|
+
cursorLength: serialized.map.cursorLength,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
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,
|
|
365
559
|
};
|
|
560
|
+
segmentedDocCache.set(doc, segmented);
|
|
561
|
+
return segmented;
|
|
366
562
|
}
|
|
367
|
-
function
|
|
563
|
+
function buildStateFromDoc(doc, selection, options) {
|
|
368
564
|
const normalized = normalize(doc);
|
|
369
|
-
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);
|
|
370
574
|
return {
|
|
371
|
-
source:
|
|
575
|
+
source: segmented.source,
|
|
372
576
|
selection,
|
|
373
|
-
map:
|
|
577
|
+
map: segmented.map,
|
|
374
578
|
doc: normalized,
|
|
375
579
|
runtime: runtime,
|
|
376
580
|
};
|
|
377
581
|
}
|
|
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
|
+
}
|
|
378
601
|
function shouldReparseAfterStructuralEdit(command) {
|
|
379
602
|
if (structuralReparsePolicies.length === 0) {
|
|
380
603
|
return true;
|
|
@@ -491,14 +714,19 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
491
714
|
}
|
|
492
715
|
return state;
|
|
493
716
|
}
|
|
494
|
-
// Structural edits operate
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
//
|
|
498
|
-
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
|
+
});
|
|
499
727
|
const interimAffinity = structural.nextAffinity ?? "forward";
|
|
500
728
|
const caretSource = interim.map.cursorToSource(structural.nextCursor, interimAffinity);
|
|
501
|
-
if (
|
|
729
|
+
if (useIncrementalSegmentedDerivation) {
|
|
502
730
|
const caretCursor = interim.map.sourceToCursor(caretSource, interimAffinity);
|
|
503
731
|
return {
|
|
504
732
|
...interim,
|
|
@@ -536,11 +764,12 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
536
764
|
return state;
|
|
537
765
|
}
|
|
538
766
|
function applyStructuralEdit(command, doc, selection) {
|
|
539
|
-
const
|
|
767
|
+
const textModel = getEditorTextModelForDoc(doc);
|
|
768
|
+
const lines = textModel.getStructuralLines();
|
|
540
769
|
if (lines.length === 0) {
|
|
541
770
|
return null;
|
|
542
771
|
}
|
|
543
|
-
const docCursorLength =
|
|
772
|
+
const docCursorLength = textModel.getCursorLength();
|
|
544
773
|
const cursorStart = Math.max(0, Math.min(docCursorLength, Math.min(selection.start, selection.end)));
|
|
545
774
|
const cursorEnd = Math.max(0, Math.min(docCursorLength, Math.max(selection.start, selection.end)));
|
|
546
775
|
const affinity = selection.affinity ?? "forward";
|
|
@@ -582,12 +811,12 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
582
811
|
const shouldReplacePlaceholder = command.type === "insert" &&
|
|
583
812
|
replaceText.length > 0 &&
|
|
584
813
|
range.start === range.end &&
|
|
585
|
-
|
|
814
|
+
textModel.getGraphemeAtCursor(range.start) === "\u200B";
|
|
586
815
|
const effectiveRange = shouldReplacePlaceholder
|
|
587
816
|
? { start: range.start, end: Math.min(docCursorLength, range.start + 1) }
|
|
588
817
|
: range;
|
|
589
|
-
const startLoc =
|
|
590
|
-
const endLoc =
|
|
818
|
+
const startLoc = textModel.resolveOffsetToLine(effectiveRange.start);
|
|
819
|
+
const endLoc = textModel.resolveOffsetToLine(effectiveRange.end);
|
|
591
820
|
const startLine = lines[startLoc.lineIndex];
|
|
592
821
|
const endLine = lines[endLoc.lineIndex];
|
|
593
822
|
if (!startLine || !endLine) {
|
|
@@ -615,8 +844,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
615
844
|
let nextBlocks = updateBlocksAtPath(doc.blocks, prevLine.parentPath, (blocks) => blocks.map((block, index) => index === prevLine.indexInParent ? nextPrevBlock : block));
|
|
616
845
|
nextBlocks = updateBlocksAtPath(nextBlocks, endLine.parentPath, (blocks) => blocks.filter((_, index) => index !== endLine.indexInParent));
|
|
617
846
|
const nextDoc = { ...doc, blocks: nextBlocks };
|
|
618
|
-
const
|
|
619
|
-
const
|
|
847
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
848
|
+
const nextLines = nextModel.getStructuralLines();
|
|
849
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
620
850
|
const mergedLineIndex = nextLines.findIndex((line) => pathsEqual(line.path, prevLine.path));
|
|
621
851
|
const mergedLine = nextLines[mergedLineIndex];
|
|
622
852
|
const nextCursor = mergedLineIndex >= 0
|
|
@@ -673,8 +903,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
673
903
|
...doc,
|
|
674
904
|
blocks: updateBlocksAtPath(doc.blocks, parentPath, () => nextParentBlocks),
|
|
675
905
|
};
|
|
676
|
-
const
|
|
677
|
-
const
|
|
906
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
907
|
+
const nextLines = nextModel.getStructuralLines();
|
|
908
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
678
909
|
const nextLineIndex = Math.min(nextLines.length - 1, startLoc.lineIndex + 1);
|
|
679
910
|
return {
|
|
680
911
|
doc: nextDoc,
|
|
@@ -693,8 +924,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
693
924
|
...doc,
|
|
694
925
|
blocks: updateBlocksAtPath(doc.blocks, parentPath, () => ensured),
|
|
695
926
|
};
|
|
696
|
-
const
|
|
697
|
-
const
|
|
927
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
928
|
+
const nextLines = nextModel.getStructuralLines();
|
|
929
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
698
930
|
const nextLineIndex = Math.min(nextLines.length - 1, startLoc.lineIndex);
|
|
699
931
|
return {
|
|
700
932
|
doc: nextDoc,
|
|
@@ -724,8 +956,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
724
956
|
...doc,
|
|
725
957
|
blocks: updateBlocksAtPath(doc.blocks, parentPath, () => nextParentBlocks),
|
|
726
958
|
};
|
|
727
|
-
const
|
|
728
|
-
const
|
|
959
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
960
|
+
const nextLines = nextModel.getStructuralLines();
|
|
961
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
729
962
|
const nextLineIndex = Math.max(0, startLoc.lineIndex - 1);
|
|
730
963
|
return {
|
|
731
964
|
doc: nextDoc,
|
|
@@ -782,8 +1015,9 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
782
1015
|
...doc,
|
|
783
1016
|
blocks: updateBlocksAtPath(doc.blocks, wrapperParentPath, () => nextParentBlocks),
|
|
784
1017
|
};
|
|
785
|
-
const
|
|
786
|
-
const
|
|
1018
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
1019
|
+
const nextLines = nextModel.getStructuralLines();
|
|
1020
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
787
1021
|
const insertedPath = [...wrapperParentPath, wrapperIndexInParent + 1];
|
|
788
1022
|
const insertedLineIndex = nextLines.findIndex((line) => pathsEqual(line.path, insertedPath));
|
|
789
1023
|
const nextCursor = insertedLineIndex >= 0 ? (lineStarts[insertedLineIndex] ?? 0) : 0;
|
|
@@ -796,12 +1030,12 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
796
1030
|
}
|
|
797
1031
|
const hasSelectedText = effectiveRange.start !== effectiveRange.end;
|
|
798
1032
|
const baseMarks = hasSelectedText
|
|
799
|
-
? commonMarksAcrossSelection(lines, effectiveRange.start, effectiveRange.end
|
|
1033
|
+
? commonMarksAcrossSelection(textModel, lines, effectiveRange.start, effectiveRange.end)
|
|
800
1034
|
: marksAtCursor(startRuns, startLoc.offsetInLine, affinity);
|
|
801
1035
|
const insertDoc = parse(replaceText);
|
|
802
|
-
const
|
|
1036
|
+
const insertModel = getEditorTextModelForDoc(insertDoc);
|
|
803
1037
|
const insertBlocks = insertDoc.blocks;
|
|
804
|
-
const insertCursorLength =
|
|
1038
|
+
const insertCursorLength = insertModel.getCursorLength();
|
|
805
1039
|
const replacementBlocks = buildReplacementBlocks({
|
|
806
1040
|
baseMarks,
|
|
807
1041
|
beforeRuns,
|
|
@@ -817,9 +1051,10 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
817
1051
|
...doc,
|
|
818
1052
|
blocks: updateBlocksAtPath(doc.blocks, parentPath, () => nextParentBlocks),
|
|
819
1053
|
};
|
|
820
|
-
const
|
|
1054
|
+
const nextModel = getEditorTextModelForDoc(nextDoc);
|
|
1055
|
+
const nextDocCursorLength = nextModel.getCursorLength();
|
|
821
1056
|
const nextCursor = Math.max(0, Math.min(nextDocCursorLength, effectiveRange.start + insertCursorLength));
|
|
822
|
-
const nextLines =
|
|
1057
|
+
const nextLines = nextModel.getStructuralLines();
|
|
823
1058
|
const around = marksAroundCursor(nextDoc, nextCursor);
|
|
824
1059
|
const fallbackAffinity = command.type === "delete-backward"
|
|
825
1060
|
? "backward"
|
|
@@ -836,8 +1071,8 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
836
1071
|
// If the cursor is at the start of a non-first line (i.e., right after a
|
|
837
1072
|
// serialized newline), keep affinity "forward" so selection anchors in the
|
|
838
1073
|
// following line, not at the end of the previous one.
|
|
839
|
-
const nextLoc =
|
|
840
|
-
const lineStarts =
|
|
1074
|
+
const nextLoc = nextModel.resolveOffsetToLine(nextCursor);
|
|
1075
|
+
const lineStarts = nextModel.getLineOffsets();
|
|
841
1076
|
if (nextLoc.lineIndex > 0 &&
|
|
842
1077
|
nextLoc.offsetInLine === 0 &&
|
|
843
1078
|
nextCursor === (lineStarts[nextLoc.lineIndex] ?? 0)) {
|
|
@@ -849,128 +1084,6 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
849
1084
|
nextAffinity,
|
|
850
1085
|
};
|
|
851
1086
|
}
|
|
852
|
-
function cursorLengthForLines(lines) {
|
|
853
|
-
let length = 0;
|
|
854
|
-
for (const line of lines) {
|
|
855
|
-
length += line.cursorLength;
|
|
856
|
-
if (line.hasNewline) {
|
|
857
|
-
length += 1;
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
return length;
|
|
861
|
-
}
|
|
862
|
-
function flattenDocToLines(doc) {
|
|
863
|
-
const entries = [];
|
|
864
|
-
const visit = (blocks, prefix) => {
|
|
865
|
-
blocks.forEach((block, index) => {
|
|
866
|
-
const path = [...prefix, index];
|
|
867
|
-
if (block.type === "block-wrapper") {
|
|
868
|
-
visit(block.blocks, path);
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
entries.push({ path, block });
|
|
872
|
-
});
|
|
873
|
-
};
|
|
874
|
-
visit(doc.blocks, []);
|
|
875
|
-
if (entries.length === 0) {
|
|
876
|
-
return [
|
|
877
|
-
{
|
|
878
|
-
path: [0],
|
|
879
|
-
parentPath: [],
|
|
880
|
-
indexInParent: 0,
|
|
881
|
-
block: { type: "paragraph", content: [] },
|
|
882
|
-
text: "",
|
|
883
|
-
cursorLength: 0,
|
|
884
|
-
hasNewline: false,
|
|
885
|
-
},
|
|
886
|
-
];
|
|
887
|
-
}
|
|
888
|
-
return entries.map((entry, i) => {
|
|
889
|
-
const parentPath = entry.path.slice(0, -1);
|
|
890
|
-
const indexInParent = entry.path[entry.path.length - 1] ?? 0;
|
|
891
|
-
const text = blockVisibleText(entry.block);
|
|
892
|
-
return {
|
|
893
|
-
path: entry.path,
|
|
894
|
-
parentPath,
|
|
895
|
-
indexInParent,
|
|
896
|
-
block: entry.block,
|
|
897
|
-
text,
|
|
898
|
-
cursorLength: graphemeSegments(text).length,
|
|
899
|
-
hasNewline: i < entries.length - 1,
|
|
900
|
-
};
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
function resolveCursorToLine(lines, cursorOffset) {
|
|
904
|
-
const total = cursorLengthForLines(lines);
|
|
905
|
-
const clamped = Math.max(0, Math.min(cursorOffset, total));
|
|
906
|
-
let start = 0;
|
|
907
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
908
|
-
const line = lines[i];
|
|
909
|
-
const end = start + line.cursorLength;
|
|
910
|
-
if (clamped <= end || i === lines.length - 1) {
|
|
911
|
-
return {
|
|
912
|
-
lineIndex: i,
|
|
913
|
-
offsetInLine: Math.max(0, Math.min(clamped - start, line.cursorLength)),
|
|
914
|
-
};
|
|
915
|
-
}
|
|
916
|
-
start = end + (line.hasNewline ? 1 : 0);
|
|
917
|
-
if (line.hasNewline && clamped === start) {
|
|
918
|
-
return {
|
|
919
|
-
lineIndex: Math.min(lines.length - 1, i + 1),
|
|
920
|
-
offsetInLine: 0,
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
return {
|
|
925
|
-
lineIndex: lines.length - 1,
|
|
926
|
-
offsetInLine: lines[lines.length - 1]?.cursorLength ?? 0,
|
|
927
|
-
};
|
|
928
|
-
}
|
|
929
|
-
function getLineStartOffsets(lines) {
|
|
930
|
-
const offsets = [];
|
|
931
|
-
let current = 0;
|
|
932
|
-
for (const line of lines) {
|
|
933
|
-
offsets.push(current);
|
|
934
|
-
current += line.cursorLength + (line.hasNewline ? 1 : 0);
|
|
935
|
-
}
|
|
936
|
-
return offsets;
|
|
937
|
-
}
|
|
938
|
-
function graphemeAtCursor(lines, cursorOffset) {
|
|
939
|
-
const loc = resolveCursorToLine(lines, cursorOffset);
|
|
940
|
-
const line = lines[loc.lineIndex];
|
|
941
|
-
if (!line) {
|
|
942
|
-
return null;
|
|
943
|
-
}
|
|
944
|
-
if (loc.offsetInLine >= line.cursorLength) {
|
|
945
|
-
return null;
|
|
946
|
-
}
|
|
947
|
-
const segments = Array.from(graphemeSegments(line.text));
|
|
948
|
-
return segments[loc.offsetInLine]?.segment ?? null;
|
|
949
|
-
}
|
|
950
|
-
function blockVisibleText(block) {
|
|
951
|
-
if (block.type === "paragraph") {
|
|
952
|
-
return block.content.map(inlineVisibleText).join("");
|
|
953
|
-
}
|
|
954
|
-
if (block.type === "block-atom") {
|
|
955
|
-
return "";
|
|
956
|
-
}
|
|
957
|
-
if (block.type === "block-wrapper") {
|
|
958
|
-
return block.blocks.map(blockVisibleText).join("\n");
|
|
959
|
-
}
|
|
960
|
-
return "";
|
|
961
|
-
}
|
|
962
|
-
function inlineVisibleText(inline) {
|
|
963
|
-
if (inline.type === "text") {
|
|
964
|
-
return inline.text;
|
|
965
|
-
}
|
|
966
|
-
if (inline.type === "inline-wrapper") {
|
|
967
|
-
return inline.children.map(inlineVisibleText).join("");
|
|
968
|
-
}
|
|
969
|
-
if (inline.type === "inline-atom") {
|
|
970
|
-
return " ";
|
|
971
|
-
}
|
|
972
|
-
return "";
|
|
973
|
-
}
|
|
974
1087
|
function stableStringify(value) {
|
|
975
1088
|
if (value === null || value === undefined) {
|
|
976
1089
|
return "";
|
|
@@ -1141,13 +1254,14 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1141
1254
|
return null;
|
|
1142
1255
|
}
|
|
1143
1256
|
function marksAroundCursor(doc, cursorOffset) {
|
|
1144
|
-
const
|
|
1145
|
-
const
|
|
1257
|
+
const textModel = getEditorTextModelForDoc(doc);
|
|
1258
|
+
const lines = textModel.getStructuralLines();
|
|
1259
|
+
const loc = textModel.resolveOffsetToLine(cursorOffset);
|
|
1146
1260
|
const line = lines[loc.lineIndex];
|
|
1147
1261
|
if (!line) {
|
|
1148
1262
|
return { left: [], right: [] };
|
|
1149
1263
|
}
|
|
1150
|
-
const block =
|
|
1264
|
+
const block = line.block;
|
|
1151
1265
|
if (!block || block.type !== "paragraph") {
|
|
1152
1266
|
return { left: [], right: [] };
|
|
1153
1267
|
}
|
|
@@ -1321,19 +1435,19 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1321
1435
|
});
|
|
1322
1436
|
return blocks;
|
|
1323
1437
|
}
|
|
1324
|
-
function commonMarksAcrossSelection(lines, startCursor, endCursor
|
|
1438
|
+
function commonMarksAcrossSelection(textModel, lines, startCursor, endCursor) {
|
|
1325
1439
|
if (startCursor === endCursor) {
|
|
1326
1440
|
return [];
|
|
1327
1441
|
}
|
|
1328
|
-
const startLoc =
|
|
1329
|
-
const endLoc =
|
|
1442
|
+
const startLoc = textModel.resolveOffsetToLine(startCursor);
|
|
1443
|
+
const endLoc = textModel.resolveOffsetToLine(endCursor);
|
|
1330
1444
|
const slices = [];
|
|
1331
1445
|
for (let lineIndex = startLoc.lineIndex; lineIndex <= endLoc.lineIndex; lineIndex += 1) {
|
|
1332
1446
|
const line = lines[lineIndex];
|
|
1333
1447
|
if (!line) {
|
|
1334
1448
|
continue;
|
|
1335
1449
|
}
|
|
1336
|
-
const block =
|
|
1450
|
+
const block = line.block;
|
|
1337
1451
|
if (!block || block.type !== "paragraph") {
|
|
1338
1452
|
continue;
|
|
1339
1453
|
}
|
|
@@ -1545,8 +1659,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1545
1659
|
// otherwise we create ambiguous marker sequences (e.g., *italic***** doesn't parse).
|
|
1546
1660
|
const betweenLen = insertAtForward - insertAtBackward;
|
|
1547
1661
|
const preferBackward = insertAtBackward !== insertAtForward && openLen <= betweenLen;
|
|
1548
|
-
const insertAt = placeholderPos ??
|
|
1549
|
-
(preferBackward ? insertAtBackward : insertAtForward);
|
|
1662
|
+
const insertAt = placeholderPos ?? (preferBackward ? insertAtBackward : insertAtForward);
|
|
1550
1663
|
const nextSource = placeholderPos !== null
|
|
1551
1664
|
? source.slice(0, insertAt) +
|
|
1552
1665
|
openMarker +
|
|
@@ -1572,9 +1685,10 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1572
1685
|
}
|
|
1573
1686
|
const cursorStart = Math.min(selection.start, selection.end);
|
|
1574
1687
|
const cursorEnd = Math.max(selection.start, selection.end);
|
|
1575
|
-
const
|
|
1576
|
-
const
|
|
1577
|
-
const
|
|
1688
|
+
const selectionModel = getEditorTextModelForDoc(state.doc);
|
|
1689
|
+
const linesForSelection = selectionModel.getStructuralLines();
|
|
1690
|
+
const startLoc = selectionModel.resolveOffsetToLine(cursorStart);
|
|
1691
|
+
const endLoc = selectionModel.resolveOffsetToLine(cursorEnd);
|
|
1578
1692
|
const splitRunsOnNewlines = (runs) => {
|
|
1579
1693
|
const split = [];
|
|
1580
1694
|
for (const run of runs) {
|
|
@@ -1612,7 +1726,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1612
1726
|
if (startInLine === endInLine) {
|
|
1613
1727
|
continue;
|
|
1614
1728
|
}
|
|
1615
|
-
const block =
|
|
1729
|
+
const block = line.block;
|
|
1616
1730
|
if (!block || block.type !== "paragraph") {
|
|
1617
1731
|
continue;
|
|
1618
1732
|
}
|
|
@@ -1749,22 +1863,23 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1749
1863
|
}
|
|
1750
1864
|
function serializeSelection(state, selection) {
|
|
1751
1865
|
const normalized = normalizeSelection(selection);
|
|
1752
|
-
const
|
|
1753
|
-
const
|
|
1866
|
+
const textModel = getEditorTextModelForDoc(state.doc);
|
|
1867
|
+
const lines = textModel.getStructuralLines();
|
|
1868
|
+
const docCursorLength = textModel.getCursorLength();
|
|
1754
1869
|
const cursorStart = Math.max(0, Math.min(docCursorLength, Math.min(normalized.start, normalized.end)));
|
|
1755
1870
|
const cursorEnd = Math.max(0, Math.min(docCursorLength, Math.max(normalized.start, normalized.end)));
|
|
1756
1871
|
if (cursorStart === cursorEnd) {
|
|
1757
1872
|
return "";
|
|
1758
1873
|
}
|
|
1759
|
-
const startLoc =
|
|
1760
|
-
const endLoc =
|
|
1874
|
+
const startLoc = textModel.resolveOffsetToLine(cursorStart);
|
|
1875
|
+
const endLoc = textModel.resolveOffsetToLine(cursorEnd);
|
|
1761
1876
|
const blocks = [];
|
|
1762
1877
|
for (let lineIndex = startLoc.lineIndex; lineIndex <= endLoc.lineIndex; lineIndex += 1) {
|
|
1763
1878
|
const line = lines[lineIndex];
|
|
1764
1879
|
if (!line) {
|
|
1765
1880
|
continue;
|
|
1766
1881
|
}
|
|
1767
|
-
const block =
|
|
1882
|
+
const block = line.block;
|
|
1768
1883
|
if (!block || block.type !== "paragraph") {
|
|
1769
1884
|
continue;
|
|
1770
1885
|
}
|
|
@@ -1833,15 +1948,16 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1833
1948
|
}
|
|
1834
1949
|
function serializeSelectionToHtml(state, selection) {
|
|
1835
1950
|
const normalized = normalizeSelection(selection);
|
|
1836
|
-
const
|
|
1837
|
-
const
|
|
1951
|
+
const textModel = getEditorTextModelForDoc(state.doc);
|
|
1952
|
+
const lines = textModel.getStructuralLines();
|
|
1953
|
+
const docCursorLength = textModel.getCursorLength();
|
|
1838
1954
|
const cursorStart = Math.max(0, Math.min(docCursorLength, Math.min(normalized.start, normalized.end)));
|
|
1839
1955
|
const cursorEnd = Math.max(0, Math.min(docCursorLength, Math.max(normalized.start, normalized.end)));
|
|
1840
1956
|
if (cursorStart === cursorEnd) {
|
|
1841
1957
|
return "";
|
|
1842
1958
|
}
|
|
1843
|
-
const startLoc =
|
|
1844
|
-
const endLoc =
|
|
1959
|
+
const startLoc = textModel.resolveOffsetToLine(cursorStart);
|
|
1960
|
+
const endLoc = textModel.resolveOffsetToLine(cursorEnd);
|
|
1845
1961
|
let html = "";
|
|
1846
1962
|
let activeList = null;
|
|
1847
1963
|
const closeList = () => {
|
|
@@ -1865,7 +1981,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1865
1981
|
if (!line) {
|
|
1866
1982
|
continue;
|
|
1867
1983
|
}
|
|
1868
|
-
const block =
|
|
1984
|
+
const block = line.block;
|
|
1869
1985
|
if (!block || block.type !== "paragraph") {
|
|
1870
1986
|
continue;
|
|
1871
1987
|
}
|
|
@@ -1948,6 +2064,7 @@ export function createRuntimeFromRegistry(registry) {
|
|
|
1948
2064
|
parse,
|
|
1949
2065
|
serialize,
|
|
1950
2066
|
createState,
|
|
2067
|
+
createStateFromDoc,
|
|
1951
2068
|
updateSelection,
|
|
1952
2069
|
serializeSelection,
|
|
1953
2070
|
serializeSelectionToHtml,
|