@codemirror/view 6.38.8 → 6.39.0-beta.1

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/index.cjs CHANGED
@@ -35,1079 +35,6 @@ var browser = {
35
35
  tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size"
36
36
  };
37
37
 
38
- function getSelection(root) {
39
- let target;
40
- // Browsers differ on whether shadow roots have a getSelection
41
- // method. If it exists, use that, otherwise, call it on the
42
- // document.
43
- if (root.nodeType == 11) { // Shadow root
44
- target = root.getSelection ? root : root.ownerDocument;
45
- }
46
- else {
47
- target = root;
48
- }
49
- return target.getSelection();
50
- }
51
- function contains(dom, node) {
52
- return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
53
- }
54
- function hasSelection(dom, selection) {
55
- if (!selection.anchorNode)
56
- return false;
57
- try {
58
- // Firefox will raise 'permission denied' errors when accessing
59
- // properties of `sel.anchorNode` when it's in a generated CSS
60
- // element.
61
- return contains(dom, selection.anchorNode);
62
- }
63
- catch (_) {
64
- return false;
65
- }
66
- }
67
- function clientRectsFor(dom) {
68
- if (dom.nodeType == 3)
69
- return textRange(dom, 0, dom.nodeValue.length).getClientRects();
70
- else if (dom.nodeType == 1)
71
- return dom.getClientRects();
72
- else
73
- return [];
74
- }
75
- // Scans forward and backward through DOM positions equivalent to the
76
- // given one to see if the two are in the same place (i.e. after a
77
- // text node vs at the end of that text node)
78
- function isEquivalentPosition(node, off, targetNode, targetOff) {
79
- return targetNode ? (scanFor(node, off, targetNode, targetOff, -1) ||
80
- scanFor(node, off, targetNode, targetOff, 1)) : false;
81
- }
82
- function domIndex(node) {
83
- for (var index = 0;; index++) {
84
- node = node.previousSibling;
85
- if (!node)
86
- return index;
87
- }
88
- }
89
- function isBlockElement(node) {
90
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
91
- }
92
- function scanFor(node, off, targetNode, targetOff, dir) {
93
- for (;;) {
94
- if (node == targetNode && off == targetOff)
95
- return true;
96
- if (off == (dir < 0 ? 0 : maxOffset(node))) {
97
- if (node.nodeName == "DIV")
98
- return false;
99
- let parent = node.parentNode;
100
- if (!parent || parent.nodeType != 1)
101
- return false;
102
- off = domIndex(node) + (dir < 0 ? 0 : 1);
103
- node = parent;
104
- }
105
- else if (node.nodeType == 1) {
106
- node = node.childNodes[off + (dir < 0 ? -1 : 0)];
107
- if (node.nodeType == 1 && node.contentEditable == "false")
108
- return false;
109
- off = dir < 0 ? maxOffset(node) : 0;
110
- }
111
- else {
112
- return false;
113
- }
114
- }
115
- }
116
- function maxOffset(node) {
117
- return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
118
- }
119
- function flattenRect(rect, left) {
120
- let x = left ? rect.left : rect.right;
121
- return { left: x, right: x, top: rect.top, bottom: rect.bottom };
122
- }
123
- function windowRect(win) {
124
- let vp = win.visualViewport;
125
- if (vp)
126
- return {
127
- left: 0, right: vp.width,
128
- top: 0, bottom: vp.height
129
- };
130
- return { left: 0, right: win.innerWidth,
131
- top: 0, bottom: win.innerHeight };
132
- }
133
- function getScale(elt, rect) {
134
- let scaleX = rect.width / elt.offsetWidth;
135
- let scaleY = rect.height / elt.offsetHeight;
136
- if (scaleX > 0.995 && scaleX < 1.005 || !isFinite(scaleX) || Math.abs(rect.width - elt.offsetWidth) < 1)
137
- scaleX = 1;
138
- if (scaleY > 0.995 && scaleY < 1.005 || !isFinite(scaleY) || Math.abs(rect.height - elt.offsetHeight) < 1)
139
- scaleY = 1;
140
- return { scaleX, scaleY };
141
- }
142
- function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
143
- let doc = dom.ownerDocument, win = doc.defaultView || window;
144
- for (let cur = dom, stop = false; cur && !stop;) {
145
- if (cur.nodeType == 1) { // Element
146
- let bounding, top = cur == doc.body;
147
- let scaleX = 1, scaleY = 1;
148
- if (top) {
149
- bounding = windowRect(win);
150
- }
151
- else {
152
- if (/^(fixed|sticky)$/.test(getComputedStyle(cur).position))
153
- stop = true;
154
- if (cur.scrollHeight <= cur.clientHeight && cur.scrollWidth <= cur.clientWidth) {
155
- cur = cur.assignedSlot || cur.parentNode;
156
- continue;
157
- }
158
- let rect = cur.getBoundingClientRect();
159
- ({ scaleX, scaleY } = getScale(cur, rect));
160
- // Make sure scrollbar width isn't included in the rectangle
161
- bounding = { left: rect.left, right: rect.left + cur.clientWidth * scaleX,
162
- top: rect.top, bottom: rect.top + cur.clientHeight * scaleY };
163
- }
164
- let moveX = 0, moveY = 0;
165
- if (y == "nearest") {
166
- if (rect.top < bounding.top) {
167
- moveY = rect.top - (bounding.top + yMargin);
168
- if (side > 0 && rect.bottom > bounding.bottom + moveY)
169
- moveY = rect.bottom - bounding.bottom + yMargin;
170
- }
171
- else if (rect.bottom > bounding.bottom) {
172
- moveY = rect.bottom - bounding.bottom + yMargin;
173
- if (side < 0 && (rect.top - moveY) < bounding.top)
174
- moveY = rect.top - (bounding.top + yMargin);
175
- }
176
- }
177
- else {
178
- let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top;
179
- let targetTop = y == "center" && rectHeight <= boundingHeight ? rect.top + rectHeight / 2 - boundingHeight / 2 :
180
- y == "start" || y == "center" && side < 0 ? rect.top - yMargin :
181
- rect.bottom - boundingHeight + yMargin;
182
- moveY = targetTop - bounding.top;
183
- }
184
- if (x == "nearest") {
185
- if (rect.left < bounding.left) {
186
- moveX = rect.left - (bounding.left + xMargin);
187
- if (side > 0 && rect.right > bounding.right + moveX)
188
- moveX = rect.right - bounding.right + xMargin;
189
- }
190
- else if (rect.right > bounding.right) {
191
- moveX = rect.right - bounding.right + xMargin;
192
- if (side < 0 && rect.left < bounding.left + moveX)
193
- moveX = rect.left - (bounding.left + xMargin);
194
- }
195
- }
196
- else {
197
- let targetLeft = x == "center" ? rect.left + (rect.right - rect.left) / 2 - (bounding.right - bounding.left) / 2 :
198
- (x == "start") == ltr ? rect.left - xMargin :
199
- rect.right - (bounding.right - bounding.left) + xMargin;
200
- moveX = targetLeft - bounding.left;
201
- }
202
- if (moveX || moveY) {
203
- if (top) {
204
- win.scrollBy(moveX, moveY);
205
- }
206
- else {
207
- let movedX = 0, movedY = 0;
208
- if (moveY) {
209
- let start = cur.scrollTop;
210
- cur.scrollTop += moveY / scaleY;
211
- movedY = (cur.scrollTop - start) * scaleY;
212
- }
213
- if (moveX) {
214
- let start = cur.scrollLeft;
215
- cur.scrollLeft += moveX / scaleX;
216
- movedX = (cur.scrollLeft - start) * scaleX;
217
- }
218
- rect = { left: rect.left - movedX, top: rect.top - movedY,
219
- right: rect.right - movedX, bottom: rect.bottom - movedY };
220
- if (movedX && Math.abs(movedX - moveX) < 1)
221
- x = "nearest";
222
- if (movedY && Math.abs(movedY - moveY) < 1)
223
- y = "nearest";
224
- }
225
- }
226
- if (top)
227
- break;
228
- if (rect.top < bounding.top || rect.bottom > bounding.bottom ||
229
- rect.left < bounding.left || rect.right > bounding.right)
230
- rect = { left: Math.max(rect.left, bounding.left), right: Math.min(rect.right, bounding.right),
231
- top: Math.max(rect.top, bounding.top), bottom: Math.min(rect.bottom, bounding.bottom) };
232
- cur = cur.assignedSlot || cur.parentNode;
233
- }
234
- else if (cur.nodeType == 11) { // A shadow root
235
- cur = cur.host;
236
- }
237
- else {
238
- break;
239
- }
240
- }
241
- }
242
- function scrollableParents(dom) {
243
- let doc = dom.ownerDocument, x, y;
244
- for (let cur = dom.parentNode; cur;) {
245
- if (cur == doc.body || (x && y)) {
246
- break;
247
- }
248
- else if (cur.nodeType == 1) {
249
- if (!y && cur.scrollHeight > cur.clientHeight)
250
- y = cur;
251
- if (!x && cur.scrollWidth > cur.clientWidth)
252
- x = cur;
253
- cur = cur.assignedSlot || cur.parentNode;
254
- }
255
- else if (cur.nodeType == 11) {
256
- cur = cur.host;
257
- }
258
- else {
259
- break;
260
- }
261
- }
262
- return { x, y };
263
- }
264
- class DOMSelectionState {
265
- constructor() {
266
- this.anchorNode = null;
267
- this.anchorOffset = 0;
268
- this.focusNode = null;
269
- this.focusOffset = 0;
270
- }
271
- eq(domSel) {
272
- return this.anchorNode == domSel.anchorNode && this.anchorOffset == domSel.anchorOffset &&
273
- this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset;
274
- }
275
- setRange(range) {
276
- let { anchorNode, focusNode } = range;
277
- // Clip offsets to node size to avoid crashes when Safari reports bogus offsets (#1152)
278
- this.set(anchorNode, Math.min(range.anchorOffset, anchorNode ? maxOffset(anchorNode) : 0), focusNode, Math.min(range.focusOffset, focusNode ? maxOffset(focusNode) : 0));
279
- }
280
- set(anchorNode, anchorOffset, focusNode, focusOffset) {
281
- this.anchorNode = anchorNode;
282
- this.anchorOffset = anchorOffset;
283
- this.focusNode = focusNode;
284
- this.focusOffset = focusOffset;
285
- }
286
- }
287
- let preventScrollSupported = null;
288
- // Safari 26 breaks preventScroll support
289
- if (browser.safari && browser.safari_version >= 26)
290
- preventScrollSupported = false;
291
- // Feature-detects support for .focus({preventScroll: true}), and uses
292
- // a fallback kludge when not supported.
293
- function focusPreventScroll(dom) {
294
- if (dom.setActive)
295
- return dom.setActive(); // in IE
296
- if (preventScrollSupported)
297
- return dom.focus(preventScrollSupported);
298
- let stack = [];
299
- for (let cur = dom; cur; cur = cur.parentNode) {
300
- stack.push(cur, cur.scrollTop, cur.scrollLeft);
301
- if (cur == cur.ownerDocument)
302
- break;
303
- }
304
- dom.focus(preventScrollSupported == null ? {
305
- get preventScroll() {
306
- preventScrollSupported = { preventScroll: true };
307
- return true;
308
- }
309
- } : undefined);
310
- if (!preventScrollSupported) {
311
- preventScrollSupported = false;
312
- for (let i = 0; i < stack.length;) {
313
- let elt = stack[i++], top = stack[i++], left = stack[i++];
314
- if (elt.scrollTop != top)
315
- elt.scrollTop = top;
316
- if (elt.scrollLeft != left)
317
- elt.scrollLeft = left;
318
- }
319
- }
320
- }
321
- let scratchRange;
322
- function textRange(node, from, to = from) {
323
- let range = scratchRange || (scratchRange = document.createRange());
324
- range.setEnd(node, to);
325
- range.setStart(node, from);
326
- return range;
327
- }
328
- function dispatchKey(elt, name, code, mods) {
329
- let options = { key: name, code: name, keyCode: code, which: code, cancelable: true };
330
- if (mods)
331
- ({ altKey: options.altKey, ctrlKey: options.ctrlKey, shiftKey: options.shiftKey, metaKey: options.metaKey } = mods);
332
- let down = new KeyboardEvent("keydown", options);
333
- down.synthetic = true;
334
- elt.dispatchEvent(down);
335
- let up = new KeyboardEvent("keyup", options);
336
- up.synthetic = true;
337
- elt.dispatchEvent(up);
338
- return down.defaultPrevented || up.defaultPrevented;
339
- }
340
- function getRoot(node) {
341
- while (node) {
342
- if (node && (node.nodeType == 9 || node.nodeType == 11 && node.host))
343
- return node;
344
- node = node.assignedSlot || node.parentNode;
345
- }
346
- return null;
347
- }
348
- function clearAttributes(node) {
349
- while (node.attributes.length)
350
- node.removeAttributeNode(node.attributes[0]);
351
- }
352
- function atElementStart(doc, selection) {
353
- let node = selection.focusNode, offset = selection.focusOffset;
354
- if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
355
- return false;
356
- // Safari can report bogus offsets (#1152)
357
- offset = Math.min(offset, maxOffset(node));
358
- for (;;) {
359
- if (offset) {
360
- if (node.nodeType != 1)
361
- return false;
362
- let prev = node.childNodes[offset - 1];
363
- if (prev.contentEditable == "false")
364
- offset--;
365
- else {
366
- node = prev;
367
- offset = maxOffset(node);
368
- }
369
- }
370
- else if (node == doc) {
371
- return true;
372
- }
373
- else {
374
- offset = domIndex(node);
375
- node = node.parentNode;
376
- }
377
- }
378
- }
379
- function isScrolledToBottom(elt) {
380
- return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
381
- }
382
- function textNodeBefore(startNode, startOffset) {
383
- for (let node = startNode, offset = startOffset;;) {
384
- if (node.nodeType == 3 && offset > 0) {
385
- return { node: node, offset: offset };
386
- }
387
- else if (node.nodeType == 1 && offset > 0) {
388
- if (node.contentEditable == "false")
389
- return null;
390
- node = node.childNodes[offset - 1];
391
- offset = maxOffset(node);
392
- }
393
- else if (node.parentNode && !isBlockElement(node)) {
394
- offset = domIndex(node);
395
- node = node.parentNode;
396
- }
397
- else {
398
- return null;
399
- }
400
- }
401
- }
402
- function textNodeAfter(startNode, startOffset) {
403
- for (let node = startNode, offset = startOffset;;) {
404
- if (node.nodeType == 3 && offset < node.nodeValue.length) {
405
- return { node: node, offset: offset };
406
- }
407
- else if (node.nodeType == 1 && offset < node.childNodes.length) {
408
- if (node.contentEditable == "false")
409
- return null;
410
- node = node.childNodes[offset];
411
- offset = 0;
412
- }
413
- else if (node.parentNode && !isBlockElement(node)) {
414
- offset = domIndex(node) + 1;
415
- node = node.parentNode;
416
- }
417
- else {
418
- return null;
419
- }
420
- }
421
- }
422
-
423
- class DOMPos {
424
- constructor(node, offset, precise = true) {
425
- this.node = node;
426
- this.offset = offset;
427
- this.precise = precise;
428
- }
429
- static before(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom), precise); }
430
- static after(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom) + 1, precise); }
431
- }
432
- const noChildren = [];
433
- class ContentView {
434
- constructor() {
435
- this.parent = null;
436
- this.dom = null;
437
- this.flags = 2 /* ViewFlag.NodeDirty */;
438
- }
439
- get overrideDOMText() { return null; }
440
- get posAtStart() {
441
- return this.parent ? this.parent.posBefore(this) : 0;
442
- }
443
- get posAtEnd() {
444
- return this.posAtStart + this.length;
445
- }
446
- posBefore(view) {
447
- let pos = this.posAtStart;
448
- for (let child of this.children) {
449
- if (child == view)
450
- return pos;
451
- pos += child.length + child.breakAfter;
452
- }
453
- throw new RangeError("Invalid child in posBefore");
454
- }
455
- posAfter(view) {
456
- return this.posBefore(view) + view.length;
457
- }
458
- sync(view, track) {
459
- if (this.flags & 2 /* ViewFlag.NodeDirty */) {
460
- let parent = this.dom;
461
- let prev = null, next;
462
- for (let child of this.children) {
463
- if (child.flags & 7 /* ViewFlag.Dirty */) {
464
- if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
465
- let contentView = ContentView.get(next);
466
- if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
467
- child.reuseDOM(next);
468
- }
469
- child.sync(view, track);
470
- child.flags &= ~7 /* ViewFlag.Dirty */;
471
- }
472
- next = prev ? prev.nextSibling : parent.firstChild;
473
- if (track && !track.written && track.node == parent && next != child.dom)
474
- track.written = true;
475
- if (child.dom.parentNode == parent) {
476
- while (next && next != child.dom)
477
- next = rm$1(next);
478
- }
479
- else {
480
- parent.insertBefore(child.dom, next);
481
- }
482
- prev = child.dom;
483
- }
484
- next = prev ? prev.nextSibling : parent.firstChild;
485
- if (next && track && track.node == parent)
486
- track.written = true;
487
- while (next)
488
- next = rm$1(next);
489
- }
490
- else if (this.flags & 1 /* ViewFlag.ChildDirty */) {
491
- for (let child of this.children)
492
- if (child.flags & 7 /* ViewFlag.Dirty */) {
493
- child.sync(view, track);
494
- child.flags &= ~7 /* ViewFlag.Dirty */;
495
- }
496
- }
497
- }
498
- reuseDOM(_dom) { }
499
- localPosFromDOM(node, offset) {
500
- let after;
501
- if (node == this.dom) {
502
- after = this.dom.childNodes[offset];
503
- }
504
- else {
505
- let bias = maxOffset(node) == 0 ? 0 : offset == 0 ? -1 : 1;
506
- for (;;) {
507
- let parent = node.parentNode;
508
- if (parent == this.dom)
509
- break;
510
- if (bias == 0 && parent.firstChild != parent.lastChild) {
511
- if (node == parent.firstChild)
512
- bias = -1;
513
- else
514
- bias = 1;
515
- }
516
- node = parent;
517
- }
518
- if (bias < 0)
519
- after = node;
520
- else
521
- after = node.nextSibling;
522
- }
523
- if (after == this.dom.firstChild)
524
- return 0;
525
- while (after && !ContentView.get(after))
526
- after = after.nextSibling;
527
- if (!after)
528
- return this.length;
529
- for (let i = 0, pos = 0;; i++) {
530
- let child = this.children[i];
531
- if (child.dom == after)
532
- return pos;
533
- pos += child.length + child.breakAfter;
534
- }
535
- }
536
- domBoundsAround(from, to, offset = 0) {
537
- let fromI = -1, fromStart = -1, toI = -1, toEnd = -1;
538
- for (let i = 0, pos = offset, prevEnd = offset; i < this.children.length; i++) {
539
- let child = this.children[i], end = pos + child.length;
540
- if (pos < from && end > to)
541
- return child.domBoundsAround(from, to, pos);
542
- if (end >= from && fromI == -1) {
543
- fromI = i;
544
- fromStart = pos;
545
- }
546
- if (pos > to && child.dom.parentNode == this.dom) {
547
- toI = i;
548
- toEnd = prevEnd;
549
- break;
550
- }
551
- prevEnd = end;
552
- pos = end + child.breakAfter;
553
- }
554
- return { from: fromStart, to: toEnd < 0 ? offset + this.length : toEnd,
555
- startDOM: (fromI ? this.children[fromI - 1].dom.nextSibling : null) || this.dom.firstChild,
556
- endDOM: toI < this.children.length && toI >= 0 ? this.children[toI].dom : null };
557
- }
558
- markDirty(andParent = false) {
559
- this.flags |= 2 /* ViewFlag.NodeDirty */;
560
- this.markParentsDirty(andParent);
561
- }
562
- markParentsDirty(childList) {
563
- for (let parent = this.parent; parent; parent = parent.parent) {
564
- if (childList)
565
- parent.flags |= 2 /* ViewFlag.NodeDirty */;
566
- if (parent.flags & 1 /* ViewFlag.ChildDirty */)
567
- return;
568
- parent.flags |= 1 /* ViewFlag.ChildDirty */;
569
- childList = false;
570
- }
571
- }
572
- setParent(parent) {
573
- if (this.parent != parent) {
574
- this.parent = parent;
575
- if (this.flags & 7 /* ViewFlag.Dirty */)
576
- this.markParentsDirty(true);
577
- }
578
- }
579
- setDOM(dom) {
580
- if (this.dom == dom)
581
- return;
582
- if (this.dom)
583
- this.dom.cmView = null;
584
- this.dom = dom;
585
- dom.cmView = this;
586
- }
587
- get rootView() {
588
- for (let v = this;;) {
589
- let parent = v.parent;
590
- if (!parent)
591
- return v;
592
- v = parent;
593
- }
594
- }
595
- replaceChildren(from, to, children = noChildren) {
596
- this.markDirty();
597
- for (let i = from; i < to; i++) {
598
- let child = this.children[i];
599
- if (child.parent == this && children.indexOf(child) < 0)
600
- child.destroy();
601
- }
602
- if (children.length < 250)
603
- this.children.splice(from, to - from, ...children);
604
- else
605
- this.children = [].concat(this.children.slice(0, from), children, this.children.slice(to));
606
- for (let i = 0; i < children.length; i++)
607
- children[i].setParent(this);
608
- }
609
- ignoreMutation(_rec) { return false; }
610
- ignoreEvent(_event) { return false; }
611
- childCursor(pos = this.length) {
612
- return new ChildCursor(this.children, pos, this.children.length);
613
- }
614
- childPos(pos, bias = 1) {
615
- return this.childCursor().findPos(pos, bias);
616
- }
617
- toString() {
618
- let name = this.constructor.name.replace("View", "");
619
- return name + (this.children.length ? "(" + this.children.join() + ")" :
620
- this.length ? "[" + (name == "Text" ? this.text : this.length) + "]" : "") +
621
- (this.breakAfter ? "#" : "");
622
- }
623
- static get(node) { return node.cmView; }
624
- get isEditable() { return true; }
625
- get isWidget() { return false; }
626
- get isHidden() { return false; }
627
- merge(from, to, source, hasStart, openStart, openEnd) {
628
- return false;
629
- }
630
- become(other) { return false; }
631
- canReuseDOM(other) {
632
- return other.constructor == this.constructor && !((this.flags | other.flags) & 8 /* ViewFlag.Composition */);
633
- }
634
- // When this is a zero-length view with a side, this should return a
635
- // number <= 0 to indicate it is before its position, or a
636
- // number > 0 when after its position.
637
- getSide() { return 0; }
638
- destroy() {
639
- for (let child of this.children)
640
- if (child.parent == this)
641
- child.destroy();
642
- this.parent = null;
643
- }
644
- }
645
- ContentView.prototype.breakAfter = 0;
646
- // Remove a DOM node and return its next sibling.
647
- function rm$1(dom) {
648
- let next = dom.nextSibling;
649
- dom.parentNode.removeChild(dom);
650
- return next;
651
- }
652
- class ChildCursor {
653
- constructor(children, pos, i) {
654
- this.children = children;
655
- this.pos = pos;
656
- this.i = i;
657
- this.off = 0;
658
- }
659
- findPos(pos, bias = 1) {
660
- for (;;) {
661
- if (pos > this.pos || pos == this.pos &&
662
- (bias > 0 || this.i == 0 || this.children[this.i - 1].breakAfter)) {
663
- this.off = pos - this.pos;
664
- return this;
665
- }
666
- let next = this.children[--this.i];
667
- this.pos -= next.length + next.breakAfter;
668
- }
669
- }
670
- }
671
- function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart, openStart, openEnd) {
672
- let { children } = parent;
673
- let before = children.length ? children[fromI] : null;
674
- let last = insert.length ? insert[insert.length - 1] : null;
675
- let breakAtEnd = last ? last.breakAfter : breakAtStart;
676
- // Change within a single child
677
- if (fromI == toI && before && !breakAtStart && !breakAtEnd && insert.length < 2 &&
678
- before.merge(fromOff, toOff, insert.length ? last : null, fromOff == 0, openStart, openEnd))
679
- return;
680
- if (toI < children.length) {
681
- let after = children[toI];
682
- // Make sure the end of the child after the update is preserved in `after`
683
- if (after && (toOff < after.length || after.breakAfter && (last === null || last === void 0 ? void 0 : last.breakAfter))) {
684
- // If we're splitting a child, separate part of it to avoid that
685
- // being mangled when updating the child before the update.
686
- if (fromI == toI) {
687
- after = after.split(toOff);
688
- toOff = 0;
689
- }
690
- // If the element after the replacement should be merged with
691
- // the last replacing element, update `content`
692
- if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
693
- insert[insert.length - 1] = after;
694
- }
695
- else {
696
- // Remove the start of the after element, if necessary, and
697
- // add it to `content`.
698
- if (toOff || after.children.length && !after.children[0].length)
699
- after.merge(0, toOff, null, false, 0, openEnd);
700
- insert.push(after);
701
- }
702
- }
703
- else if (after === null || after === void 0 ? void 0 : after.breakAfter) {
704
- // The element at `toI` is entirely covered by this range.
705
- // Preserve its line break, if any.
706
- if (last)
707
- last.breakAfter = 1;
708
- else
709
- breakAtStart = 1;
710
- }
711
- // Since we've handled the next element from the current elements
712
- // now, make sure `toI` points after that.
713
- toI++;
714
- }
715
- if (before) {
716
- before.breakAfter = breakAtStart;
717
- if (fromOff > 0) {
718
- if (!breakAtStart && insert.length && before.merge(fromOff, before.length, insert[0], false, openStart, 0)) {
719
- before.breakAfter = insert.shift().breakAfter;
720
- }
721
- else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
722
- before.merge(fromOff, before.length, null, false, openStart, 0);
723
- }
724
- fromI++;
725
- }
726
- }
727
- // Try to merge widgets on the boundaries of the replacement
728
- while (fromI < toI && insert.length) {
729
- if (children[toI - 1].become(insert[insert.length - 1])) {
730
- toI--;
731
- insert.pop();
732
- openEnd = insert.length ? 0 : openStart;
733
- }
734
- else if (children[fromI].become(insert[0])) {
735
- fromI++;
736
- insert.shift();
737
- openStart = insert.length ? 0 : openEnd;
738
- }
739
- else {
740
- break;
741
- }
742
- }
743
- if (!insert.length && fromI && toI < children.length && !children[fromI - 1].breakAfter &&
744
- children[toI].merge(0, 0, children[fromI - 1], false, openStart, openEnd))
745
- fromI--;
746
- if (fromI < toI || insert.length)
747
- parent.replaceChildren(fromI, toI, insert);
748
- }
749
- function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
750
- let cur = parent.childCursor();
751
- let { i: toI, off: toOff } = cur.findPos(to, 1);
752
- let { i: fromI, off: fromOff } = cur.findPos(from, -1);
753
- let dLen = from - to;
754
- for (let view of insert)
755
- dLen += view.length;
756
- parent.length += dLen;
757
- replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
758
- }
759
-
760
- const MaxJoinLen = 256;
761
- class TextView extends ContentView {
762
- constructor(text) {
763
- super();
764
- this.text = text;
765
- }
766
- get length() { return this.text.length; }
767
- createDOM(textDOM) {
768
- this.setDOM(textDOM || document.createTextNode(this.text));
769
- }
770
- sync(view, track) {
771
- if (!this.dom)
772
- this.createDOM();
773
- if (this.dom.nodeValue != this.text) {
774
- if (track && track.node == this.dom)
775
- track.written = true;
776
- this.dom.nodeValue = this.text;
777
- }
778
- }
779
- reuseDOM(dom) {
780
- if (dom.nodeType == 3)
781
- this.createDOM(dom);
782
- }
783
- merge(from, to, source) {
784
- if ((this.flags & 8 /* ViewFlag.Composition */) ||
785
- source && (!(source instanceof TextView) ||
786
- this.length - (to - from) + source.length > MaxJoinLen ||
787
- (source.flags & 8 /* ViewFlag.Composition */)))
788
- return false;
789
- this.text = this.text.slice(0, from) + (source ? source.text : "") + this.text.slice(to);
790
- this.markDirty();
791
- return true;
792
- }
793
- split(from) {
794
- let result = new TextView(this.text.slice(from));
795
- this.text = this.text.slice(0, from);
796
- this.markDirty();
797
- result.flags |= this.flags & 8 /* ViewFlag.Composition */;
798
- return result;
799
- }
800
- localPosFromDOM(node, offset) {
801
- return node == this.dom ? offset : offset ? this.text.length : 0;
802
- }
803
- domAtPos(pos) { return new DOMPos(this.dom, pos); }
804
- domBoundsAround(_from, _to, offset) {
805
- return { from: offset, to: offset + this.length, startDOM: this.dom, endDOM: this.dom.nextSibling };
806
- }
807
- coordsAt(pos, side) {
808
- return textCoords(this.dom, pos, side);
809
- }
810
- }
811
- class MarkView extends ContentView {
812
- constructor(mark, children = [], length = 0) {
813
- super();
814
- this.mark = mark;
815
- this.children = children;
816
- this.length = length;
817
- for (let ch of children)
818
- ch.setParent(this);
819
- }
820
- setAttrs(dom) {
821
- clearAttributes(dom);
822
- if (this.mark.class)
823
- dom.className = this.mark.class;
824
- if (this.mark.attrs)
825
- for (let name in this.mark.attrs)
826
- dom.setAttribute(name, this.mark.attrs[name]);
827
- return dom;
828
- }
829
- canReuseDOM(other) {
830
- return super.canReuseDOM(other) && !((this.flags | other.flags) & 8 /* ViewFlag.Composition */);
831
- }
832
- reuseDOM(node) {
833
- if (node.nodeName == this.mark.tagName.toUpperCase()) {
834
- this.setDOM(node);
835
- this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */;
836
- }
837
- }
838
- sync(view, track) {
839
- if (!this.dom)
840
- this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
841
- else if (this.flags & 4 /* ViewFlag.AttrsDirty */)
842
- this.setAttrs(this.dom);
843
- super.sync(view, track);
844
- }
845
- merge(from, to, source, _hasStart, openStart, openEnd) {
846
- if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) ||
847
- (from && openStart <= 0) || (to < this.length && openEnd <= 0)))
848
- return false;
849
- mergeChildrenInto(this, from, to, source ? source.children.slice() : [], openStart - 1, openEnd - 1);
850
- this.markDirty();
851
- return true;
852
- }
853
- split(from) {
854
- let result = [], off = 0, detachFrom = -1, i = 0;
855
- for (let elt of this.children) {
856
- let end = off + elt.length;
857
- if (end > from)
858
- result.push(off < from ? elt.split(from - off) : elt);
859
- if (detachFrom < 0 && off >= from)
860
- detachFrom = i;
861
- off = end;
862
- i++;
863
- }
864
- let length = this.length - from;
865
- this.length = from;
866
- if (detachFrom > -1) {
867
- this.children.length = detachFrom;
868
- this.markDirty();
869
- }
870
- return new MarkView(this.mark, result, length);
871
- }
872
- domAtPos(pos) {
873
- return inlineDOMAtPos(this, pos);
874
- }
875
- coordsAt(pos, side) {
876
- return coordsInChildren(this, pos, side);
877
- }
878
- }
879
- function textCoords(text, pos, side) {
880
- let length = text.nodeValue.length;
881
- if (pos > length)
882
- pos = length;
883
- let from = pos, to = pos, flatten = 0;
884
- if (pos == 0 && side < 0 || pos == length && side >= 0) {
885
- if (!(browser.chrome || browser.gecko)) { // These browsers reliably return valid rectangles for empty ranges
886
- if (pos) {
887
- from--;
888
- flatten = 1;
889
- } // FIXME this is wrong in RTL text
890
- else if (to < length) {
891
- to++;
892
- flatten = -1;
893
- }
894
- }
895
- }
896
- else {
897
- if (side < 0)
898
- from--;
899
- else if (to < length)
900
- to++;
901
- }
902
- let rects = textRange(text, from, to).getClientRects();
903
- if (!rects.length)
904
- return null;
905
- let rect = rects[(flatten ? flatten < 0 : side >= 0) ? 0 : rects.length - 1];
906
- if (browser.safari && !flatten && rect.width == 0)
907
- rect = Array.prototype.find.call(rects, r => r.width) || rect;
908
- return flatten ? flattenRect(rect, flatten < 0) : rect || null;
909
- }
910
- // Also used for collapsed ranges that don't have a placeholder widget!
911
- class WidgetView extends ContentView {
912
- static create(widget, length, side) {
913
- return new WidgetView(widget, length, side);
914
- }
915
- constructor(widget, length, side) {
916
- super();
917
- this.widget = widget;
918
- this.length = length;
919
- this.side = side;
920
- this.prevWidget = null;
921
- }
922
- split(from) {
923
- let result = WidgetView.create(this.widget, this.length - from, this.side);
924
- this.length -= from;
925
- return result;
926
- }
927
- sync(view) {
928
- if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
929
- if (this.dom && this.prevWidget)
930
- this.prevWidget.destroy(this.dom);
931
- this.prevWidget = null;
932
- this.setDOM(this.widget.toDOM(view));
933
- if (!this.widget.editable)
934
- this.dom.contentEditable = "false";
935
- }
936
- }
937
- getSide() { return this.side; }
938
- merge(from, to, source, hasStart, openStart, openEnd) {
939
- if (source && (!(source instanceof WidgetView) || !this.widget.compare(source.widget) ||
940
- from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
941
- return false;
942
- this.length = from + (source ? source.length : 0) + (this.length - to);
943
- return true;
944
- }
945
- become(other) {
946
- if (other instanceof WidgetView && other.side == this.side &&
947
- this.widget.constructor == other.widget.constructor) {
948
- if (!this.widget.compare(other.widget))
949
- this.markDirty(true);
950
- if (this.dom && !this.prevWidget)
951
- this.prevWidget = this.widget;
952
- this.widget = other.widget;
953
- this.length = other.length;
954
- return true;
955
- }
956
- return false;
957
- }
958
- ignoreMutation() { return true; }
959
- ignoreEvent(event) { return this.widget.ignoreEvent(event); }
960
- get overrideDOMText() {
961
- if (this.length == 0)
962
- return state.Text.empty;
963
- let top = this;
964
- while (top.parent)
965
- top = top.parent;
966
- let { view } = top, text = view && view.state.doc, start = this.posAtStart;
967
- return text ? text.slice(start, start + this.length) : state.Text.empty;
968
- }
969
- domAtPos(pos) {
970
- return (this.length ? pos == 0 : this.side > 0)
971
- ? DOMPos.before(this.dom)
972
- : DOMPos.after(this.dom, pos == this.length);
973
- }
974
- domBoundsAround() { return null; }
975
- coordsAt(pos, side) {
976
- let custom = this.widget.coordsAt(this.dom, pos, side);
977
- if (custom)
978
- return custom;
979
- let rects = this.dom.getClientRects(), rect = null;
980
- if (!rects.length)
981
- return null;
982
- let fromBack = this.side ? this.side < 0 : pos > 0;
983
- for (let i = fromBack ? rects.length - 1 : 0;; i += (fromBack ? -1 : 1)) {
984
- rect = rects[i];
985
- if (pos > 0 ? i == 0 : i == rects.length - 1 || rect.top < rect.bottom)
986
- break;
987
- }
988
- return flattenRect(rect, !fromBack);
989
- }
990
- get isEditable() { return false; }
991
- get isWidget() { return true; }
992
- get isHidden() { return this.widget.isHidden; }
993
- destroy() {
994
- super.destroy();
995
- if (this.dom)
996
- this.widget.destroy(this.dom);
997
- }
998
- }
999
- // These are drawn around uneditable widgets to avoid a number of
1000
- // browser bugs that show up when the cursor is directly next to
1001
- // uneditable inline content.
1002
- class WidgetBufferView extends ContentView {
1003
- constructor(side) {
1004
- super();
1005
- this.side = side;
1006
- }
1007
- get length() { return 0; }
1008
- merge() { return false; }
1009
- become(other) {
1010
- return other instanceof WidgetBufferView && other.side == this.side;
1011
- }
1012
- split() { return new WidgetBufferView(this.side); }
1013
- sync() {
1014
- if (!this.dom) {
1015
- let dom = document.createElement("img");
1016
- dom.className = "cm-widgetBuffer";
1017
- dom.setAttribute("aria-hidden", "true");
1018
- this.setDOM(dom);
1019
- }
1020
- }
1021
- getSide() { return this.side; }
1022
- domAtPos(pos) { return this.side > 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom); }
1023
- localPosFromDOM() { return 0; }
1024
- domBoundsAround() { return null; }
1025
- coordsAt(pos) {
1026
- return this.dom.getBoundingClientRect();
1027
- }
1028
- get overrideDOMText() {
1029
- return state.Text.empty;
1030
- }
1031
- get isHidden() { return true; }
1032
- }
1033
- TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
1034
- function inlineDOMAtPos(parent, pos) {
1035
- let dom = parent.dom, { children } = parent, i = 0;
1036
- for (let off = 0; i < children.length; i++) {
1037
- let child = children[i], end = off + child.length;
1038
- if (end == off && child.getSide() <= 0)
1039
- continue;
1040
- if (pos > off && pos < end && child.dom.parentNode == dom)
1041
- return child.domAtPos(pos - off);
1042
- if (pos <= off)
1043
- break;
1044
- off = end;
1045
- }
1046
- for (let j = i; j > 0; j--) {
1047
- let prev = children[j - 1];
1048
- if (prev.dom.parentNode == dom)
1049
- return prev.domAtPos(prev.length);
1050
- }
1051
- for (let j = i; j < children.length; j++) {
1052
- let next = children[j];
1053
- if (next.dom.parentNode == dom)
1054
- return next.domAtPos(0);
1055
- }
1056
- return new DOMPos(dom, 0);
1057
- }
1058
- // Assumes `view`, if a mark view, has precisely 1 child.
1059
- function joinInlineInto(parent, view, open) {
1060
- let last, { children } = parent;
1061
- if (open > 0 && view instanceof MarkView && children.length &&
1062
- (last = children[children.length - 1]) instanceof MarkView && last.mark.eq(view.mark)) {
1063
- joinInlineInto(last, view.children[0], open - 1);
1064
- }
1065
- else {
1066
- children.push(view);
1067
- view.setParent(parent);
1068
- }
1069
- parent.length += view.length;
1070
- }
1071
- function coordsInChildren(view, pos, side) {
1072
- let before = null, beforePos = -1, after = null, afterPos = -1;
1073
- function scan(view, pos) {
1074
- for (let i = 0, off = 0; i < view.children.length && off <= pos; i++) {
1075
- let child = view.children[i], end = off + child.length;
1076
- if (end >= pos) {
1077
- if (child.children.length) {
1078
- scan(child, pos - off);
1079
- }
1080
- else if ((!after || after.isHidden && (side > 0 || onSameLine(after, child))) &&
1081
- (end > pos || off == end && child.getSide() > 0)) {
1082
- after = child;
1083
- afterPos = pos - off;
1084
- }
1085
- else if (off < pos || (off == end && child.getSide() < 0) && !child.isHidden) {
1086
- before = child;
1087
- beforePos = pos - off;
1088
- }
1089
- }
1090
- off = end;
1091
- }
1092
- }
1093
- scan(view, pos);
1094
- let target = (side < 0 ? before : after) || before || after;
1095
- if (target)
1096
- return target.coordsAt(Math.max(0, target == before ? beforePos : afterPos), side);
1097
- return fallbackRect(view);
1098
- }
1099
- function fallbackRect(view) {
1100
- let last = view.dom.lastChild;
1101
- if (!last)
1102
- return view.dom.getBoundingClientRect();
1103
- let rects = clientRectsFor(last);
1104
- return rects[rects.length - 1] || null;
1105
- }
1106
- function onSameLine(a, b) {
1107
- let posA = a.coordsAt(0, 1), posB = b.coordsAt(0, 1);
1108
- return posA && posB && posB.top < posA.bottom;
1109
- }
1110
-
1111
38
  function combineAttrs(source, target) {
1112
39
  for (let name in source) {
1113
40
  if (name == "class" && target.class)
@@ -1137,6 +64,20 @@ function attrsEq(a, b, ignore) {
1137
64
  }
1138
65
  return true;
1139
66
  }
67
+ function setAttrs(dom, attrs) {
68
+ for (let i = dom.attributes.length - 1; i >= 0; i--) {
69
+ let name = dom.attributes[i].name;
70
+ if (attrs[name] == null)
71
+ dom.removeAttribute(name);
72
+ }
73
+ for (let name in attrs) {
74
+ let value = attrs[name];
75
+ if (name == "style")
76
+ dom.style.cssText = value;
77
+ else if (dom.getAttribute(name) != value)
78
+ dom.setAttribute(name, value);
79
+ }
80
+ }
1140
81
  function updateAttrs(dom, prev, attrs) {
1141
82
  let changed = false;
1142
83
  if (prev)
@@ -1369,16 +310,11 @@ class MarkDecoration extends Decoration {
1369
310
  let { start, end } = getInclusive(spec);
1370
311
  super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
1371
312
  this.tagName = spec.tagName || "span";
1372
- this.class = spec.class || "";
1373
- this.attrs = spec.attributes || null;
313
+ this.attrs = spec.class && spec.attributes ? combineAttrs(spec.attributes, { class: spec.class })
314
+ : spec.class ? { class: spec.class } : spec.attributes || noAttrs;
1374
315
  }
1375
316
  eq(other) {
1376
- var _a, _b;
1377
- return this == other ||
1378
- other instanceof MarkDecoration &&
1379
- this.tagName == other.tagName &&
1380
- (this.class || ((_a = this.attrs) === null || _a === void 0 ? void 0 : _a.class)) == (other.class || ((_b = other.attrs) === null || _b === void 0 ? void 0 : _b.class)) &&
1381
- attrsEq(this.attrs, other.attrs, "class");
317
+ return this == other || other instanceof MarkDecoration && this.tagName == other.tagName && attrsEq(this.attrs, other.attrs);
1382
318
  }
1383
319
  range(from, to = from) {
1384
320
  if (from >= to)
@@ -1452,438 +388,427 @@ function addRange(from, to, ranges, margin = 0) {
1452
388
  else
1453
389
  ranges.push(from, to);
1454
390
  }
391
+ /**
392
+ A block wrapper defines a DOM node that wraps lines or other block
393
+ wrappers at the top of the document. It affects any line or block
394
+ widget that starts inside its range, including blocks starting
395
+ directly at `from` but not including `to`.
396
+ */
397
+ class BlockWrapper extends state.RangeValue {
398
+ constructor(tagName, attributes) {
399
+ super();
400
+ this.tagName = tagName;
401
+ this.attributes = attributes;
402
+ }
403
+ eq(other) {
404
+ return other == this ||
405
+ other instanceof BlockWrapper && this.tagName == other.tagName && attrsEq(this.attributes, other.attributes);
406
+ }
407
+ /**
408
+ Create a block wrapper object with the given tag name and
409
+ attributes.
410
+ */
411
+ static create(spec) {
412
+ return new BlockWrapper(spec.tagName, spec.attributes || noAttrs);
413
+ }
414
+ /**
415
+ Create a range set from the given block wrapper ranges.
416
+ */
417
+ static set(of, sort = false) {
418
+ return state.RangeSet.of(of, sort);
419
+ }
420
+ }
421
+ BlockWrapper.prototype.startSide = BlockWrapper.prototype.endSide = -1;
1455
422
 
1456
- class LineView extends ContentView {
1457
- constructor() {
1458
- super(...arguments);
1459
- this.children = [];
1460
- this.length = 0;
1461
- this.prevAttrs = undefined;
1462
- this.attrs = null;
1463
- this.breakAfter = 0;
1464
- }
1465
- // Consumes source
1466
- merge(from, to, source, hasStart, openStart, openEnd) {
1467
- if (source) {
1468
- if (!(source instanceof LineView))
423
+ function getSelection(root) {
424
+ let target;
425
+ // Browsers differ on whether shadow roots have a getSelection
426
+ // method. If it exists, use that, otherwise, call it on the
427
+ // document.
428
+ if (root.nodeType == 11) { // Shadow root
429
+ target = root.getSelection ? root : root.ownerDocument;
430
+ }
431
+ else {
432
+ target = root;
433
+ }
434
+ return target.getSelection();
435
+ }
436
+ function contains(dom, node) {
437
+ return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
438
+ }
439
+ function hasSelection(dom, selection) {
440
+ if (!selection.anchorNode)
441
+ return false;
442
+ try {
443
+ // Firefox will raise 'permission denied' errors when accessing
444
+ // properties of `sel.anchorNode` when it's in a generated CSS
445
+ // element.
446
+ return contains(dom, selection.anchorNode);
447
+ }
448
+ catch (_) {
449
+ return false;
450
+ }
451
+ }
452
+ function clientRectsFor(dom) {
453
+ if (dom.nodeType == 3)
454
+ return textRange(dom, 0, dom.nodeValue.length).getClientRects();
455
+ else if (dom.nodeType == 1)
456
+ return dom.getClientRects();
457
+ else
458
+ return [];
459
+ }
460
+ // Scans forward and backward through DOM positions equivalent to the
461
+ // given one to see if the two are in the same place (i.e. after a
462
+ // text node vs at the end of that text node)
463
+ function isEquivalentPosition(node, off, targetNode, targetOff) {
464
+ return targetNode ? (scanFor(node, off, targetNode, targetOff, -1) ||
465
+ scanFor(node, off, targetNode, targetOff, 1)) : false;
466
+ }
467
+ function domIndex(node) {
468
+ for (var index = 0;; index++) {
469
+ node = node.previousSibling;
470
+ if (!node)
471
+ return index;
472
+ }
473
+ }
474
+ function isBlockElement(node) {
475
+ return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
476
+ }
477
+ function scanFor(node, off, targetNode, targetOff, dir) {
478
+ for (;;) {
479
+ if (node == targetNode && off == targetOff)
480
+ return true;
481
+ if (off == (dir < 0 ? 0 : maxOffset(node))) {
482
+ if (node.nodeName == "DIV")
483
+ return false;
484
+ let parent = node.parentNode;
485
+ if (!parent || parent.nodeType != 1)
1469
486
  return false;
1470
- if (!this.dom)
1471
- source.transferDOM(this); // Reuse source.dom when appropriate
487
+ off = domIndex(node) + (dir < 0 ? 0 : 1);
488
+ node = parent;
489
+ }
490
+ else if (node.nodeType == 1) {
491
+ node = node.childNodes[off + (dir < 0 ? -1 : 0)];
492
+ if (node.nodeType == 1 && node.contentEditable == "false")
493
+ return false;
494
+ off = dir < 0 ? maxOffset(node) : 0;
495
+ }
496
+ else {
497
+ return false;
1472
498
  }
1473
- if (hasStart)
1474
- this.setDeco(source ? source.attrs : null);
1475
- mergeChildrenInto(this, from, to, source ? source.children.slice() : [], openStart, openEnd);
1476
- return true;
1477
499
  }
1478
- split(at) {
1479
- let end = new LineView;
1480
- end.breakAfter = this.breakAfter;
1481
- if (this.length == 0)
1482
- return end;
1483
- let { i, off } = this.childPos(at);
1484
- if (off) {
1485
- end.append(this.children[i].split(off), 0);
1486
- this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
1487
- i++;
1488
- }
1489
- for (let j = i; j < this.children.length; j++)
1490
- end.append(this.children[j], 0);
1491
- while (i > 0 && this.children[i - 1].length == 0)
1492
- this.children[--i].destroy();
1493
- this.children.length = i;
1494
- this.markDirty();
1495
- this.length = at;
1496
- return end;
1497
- }
1498
- transferDOM(other) {
1499
- if (!this.dom)
1500
- return;
1501
- this.markDirty();
1502
- other.setDOM(this.dom);
1503
- other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1504
- this.prevAttrs = undefined;
1505
- this.dom = null;
1506
- }
1507
- setDeco(attrs) {
1508
- if (!attrsEq(this.attrs, attrs)) {
1509
- if (this.dom) {
1510
- this.prevAttrs = this.attrs;
1511
- this.markDirty();
500
+ }
501
+ function maxOffset(node) {
502
+ return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
503
+ }
504
+ function flattenRect(rect, left) {
505
+ let x = left ? rect.left : rect.right;
506
+ return { left: x, right: x, top: rect.top, bottom: rect.bottom };
507
+ }
508
+ function windowRect(win) {
509
+ let vp = win.visualViewport;
510
+ if (vp)
511
+ return {
512
+ left: 0, right: vp.width,
513
+ top: 0, bottom: vp.height
514
+ };
515
+ return { left: 0, right: win.innerWidth,
516
+ top: 0, bottom: win.innerHeight };
517
+ }
518
+ function getScale(elt, rect) {
519
+ let scaleX = rect.width / elt.offsetWidth;
520
+ let scaleY = rect.height / elt.offsetHeight;
521
+ if (scaleX > 0.995 && scaleX < 1.005 || !isFinite(scaleX) || Math.abs(rect.width - elt.offsetWidth) < 1)
522
+ scaleX = 1;
523
+ if (scaleY > 0.995 && scaleY < 1.005 || !isFinite(scaleY) || Math.abs(rect.height - elt.offsetHeight) < 1)
524
+ scaleY = 1;
525
+ return { scaleX, scaleY };
526
+ }
527
+ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
528
+ let doc = dom.ownerDocument, win = doc.defaultView || window;
529
+ for (let cur = dom, stop = false; cur && !stop;) {
530
+ if (cur.nodeType == 1) { // Element
531
+ let bounding, top = cur == doc.body;
532
+ let scaleX = 1, scaleY = 1;
533
+ if (top) {
534
+ bounding = windowRect(win);
535
+ }
536
+ else {
537
+ if (/^(fixed|sticky)$/.test(getComputedStyle(cur).position))
538
+ stop = true;
539
+ if (cur.scrollHeight <= cur.clientHeight && cur.scrollWidth <= cur.clientWidth) {
540
+ cur = cur.assignedSlot || cur.parentNode;
541
+ continue;
542
+ }
543
+ let rect = cur.getBoundingClientRect();
544
+ ({ scaleX, scaleY } = getScale(cur, rect));
545
+ // Make sure scrollbar width isn't included in the rectangle
546
+ bounding = { left: rect.left, right: rect.left + cur.clientWidth * scaleX,
547
+ top: rect.top, bottom: rect.top + cur.clientHeight * scaleY };
548
+ }
549
+ let moveX = 0, moveY = 0;
550
+ if (y == "nearest") {
551
+ if (rect.top < bounding.top) {
552
+ moveY = rect.top - (bounding.top + yMargin);
553
+ if (side > 0 && rect.bottom > bounding.bottom + moveY)
554
+ moveY = rect.bottom - bounding.bottom + yMargin;
555
+ }
556
+ else if (rect.bottom > bounding.bottom) {
557
+ moveY = rect.bottom - bounding.bottom + yMargin;
558
+ if (side < 0 && (rect.top - moveY) < bounding.top)
559
+ moveY = rect.top - (bounding.top + yMargin);
560
+ }
561
+ }
562
+ else {
563
+ let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top;
564
+ let targetTop = y == "center" && rectHeight <= boundingHeight ? rect.top + rectHeight / 2 - boundingHeight / 2 :
565
+ y == "start" || y == "center" && side < 0 ? rect.top - yMargin :
566
+ rect.bottom - boundingHeight + yMargin;
567
+ moveY = targetTop - bounding.top;
568
+ }
569
+ if (x == "nearest") {
570
+ if (rect.left < bounding.left) {
571
+ moveX = rect.left - (bounding.left + xMargin);
572
+ if (side > 0 && rect.right > bounding.right + moveX)
573
+ moveX = rect.right - bounding.right + xMargin;
574
+ }
575
+ else if (rect.right > bounding.right) {
576
+ moveX = rect.right - bounding.right + xMargin;
577
+ if (side < 0 && rect.left < bounding.left + moveX)
578
+ moveX = rect.left - (bounding.left + xMargin);
579
+ }
580
+ }
581
+ else {
582
+ let targetLeft = x == "center" ? rect.left + (rect.right - rect.left) / 2 - (bounding.right - bounding.left) / 2 :
583
+ (x == "start") == ltr ? rect.left - xMargin :
584
+ rect.right - (bounding.right - bounding.left) + xMargin;
585
+ moveX = targetLeft - bounding.left;
586
+ }
587
+ if (moveX || moveY) {
588
+ if (top) {
589
+ win.scrollBy(moveX, moveY);
590
+ }
591
+ else {
592
+ let movedX = 0, movedY = 0;
593
+ if (moveY) {
594
+ let start = cur.scrollTop;
595
+ cur.scrollTop += moveY / scaleY;
596
+ movedY = (cur.scrollTop - start) * scaleY;
597
+ }
598
+ if (moveX) {
599
+ let start = cur.scrollLeft;
600
+ cur.scrollLeft += moveX / scaleX;
601
+ movedX = (cur.scrollLeft - start) * scaleX;
602
+ }
603
+ rect = { left: rect.left - movedX, top: rect.top - movedY,
604
+ right: rect.right - movedX, bottom: rect.bottom - movedY };
605
+ if (movedX && Math.abs(movedX - moveX) < 1)
606
+ x = "nearest";
607
+ if (movedY && Math.abs(movedY - moveY) < 1)
608
+ y = "nearest";
609
+ }
1512
610
  }
1513
- this.attrs = attrs;
611
+ if (top)
612
+ break;
613
+ if (rect.top < bounding.top || rect.bottom > bounding.bottom ||
614
+ rect.left < bounding.left || rect.right > bounding.right)
615
+ rect = { left: Math.max(rect.left, bounding.left), right: Math.min(rect.right, bounding.right),
616
+ top: Math.max(rect.top, bounding.top), bottom: Math.min(rect.bottom, bounding.bottom) };
617
+ cur = cur.assignedSlot || cur.parentNode;
1514
618
  }
1515
- }
1516
- append(child, openStart) {
1517
- joinInlineInto(this, child, openStart);
1518
- }
1519
- // Only called when building a line view in ContentBuilder
1520
- addLineDeco(deco) {
1521
- let attrs = deco.spec.attributes, cls = deco.spec.class;
1522
- if (attrs)
1523
- this.attrs = combineAttrs(attrs, this.attrs || {});
1524
- if (cls)
1525
- this.attrs = combineAttrs({ class: cls }, this.attrs || {});
1526
- }
1527
- domAtPos(pos) {
1528
- return inlineDOMAtPos(this, pos);
1529
- }
1530
- reuseDOM(node) {
1531
- if (node.nodeName == "DIV") {
1532
- this.setDOM(node);
1533
- this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */;
619
+ else if (cur.nodeType == 11) { // A shadow root
620
+ cur = cur.host;
1534
621
  }
1535
- }
1536
- sync(view, track) {
1537
- var _a;
1538
- if (!this.dom) {
1539
- this.setDOM(document.createElement("div"));
1540
- this.dom.className = "cm-line";
1541
- this.prevAttrs = this.attrs ? null : undefined;
1542
- }
1543
- else if (this.flags & 4 /* ViewFlag.AttrsDirty */) {
1544
- clearAttributes(this.dom);
1545
- this.dom.className = "cm-line";
1546
- this.prevAttrs = this.attrs ? null : undefined;
1547
- }
1548
- if (this.prevAttrs !== undefined) {
1549
- updateAttrs(this.dom, this.prevAttrs, this.attrs);
1550
- this.dom.classList.add("cm-line");
1551
- this.prevAttrs = undefined;
1552
- }
1553
- super.sync(view, track);
1554
- let last = this.dom.lastChild;
1555
- while (last && ContentView.get(last) instanceof MarkView)
1556
- last = last.lastChild;
1557
- if (!last || !this.length ||
1558
- last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
1559
- (!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
1560
- let hack = document.createElement("BR");
1561
- hack.cmIgnore = true;
1562
- this.dom.appendChild(hack);
622
+ else {
623
+ break;
1563
624
  }
1564
625
  }
1565
- measureTextSize() {
1566
- if (this.children.length == 0 || this.length > 20)
1567
- return null;
1568
- let totalWidth = 0, textHeight;
1569
- for (let child of this.children) {
1570
- if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
1571
- return null;
1572
- let rects = clientRectsFor(child.dom);
1573
- if (rects.length != 1)
1574
- return null;
1575
- totalWidth += rects[0].width;
1576
- textHeight = rects[0].height;
626
+ }
627
+ function scrollableParents(dom) {
628
+ let doc = dom.ownerDocument, x, y;
629
+ for (let cur = dom.parentNode; cur;) {
630
+ if (cur == doc.body || (x && y)) {
631
+ break;
1577
632
  }
1578
- return !totalWidth ? null : {
1579
- lineHeight: this.dom.getBoundingClientRect().height,
1580
- charWidth: totalWidth / this.length,
1581
- textHeight
1582
- };
1583
- }
1584
- coordsAt(pos, side) {
1585
- let rect = coordsInChildren(this, pos, side);
1586
- // Correct rectangle height for empty lines when the returned
1587
- // height is larger than the text height.
1588
- if (!this.children.length && rect && this.parent) {
1589
- let { heightOracle } = this.parent.view.viewState, height = rect.bottom - rect.top;
1590
- if (Math.abs(height - heightOracle.lineHeight) < 2 && heightOracle.textHeight < height) {
1591
- let dist = (height - heightOracle.textHeight) / 2;
1592
- return { top: rect.top + dist, bottom: rect.bottom - dist, left: rect.left, right: rect.left };
1593
- }
633
+ else if (cur.nodeType == 1) {
634
+ if (!y && cur.scrollHeight > cur.clientHeight)
635
+ y = cur;
636
+ if (!x && cur.scrollWidth > cur.clientWidth)
637
+ x = cur;
638
+ cur = cur.assignedSlot || cur.parentNode;
1594
639
  }
1595
- return rect;
1596
- }
1597
- become(other) {
1598
- return other instanceof LineView && this.children.length == 0 && other.children.length == 0 &&
1599
- attrsEq(this.attrs, other.attrs) && this.breakAfter == other.breakAfter;
1600
- }
1601
- covers() { return true; }
1602
- static find(docView, pos) {
1603
- for (let i = 0, off = 0; i < docView.children.length; i++) {
1604
- let block = docView.children[i], end = off + block.length;
1605
- if (end >= pos) {
1606
- if (block instanceof LineView)
1607
- return block;
1608
- if (end > pos)
1609
- break;
1610
- }
1611
- off = end + block.breakAfter;
640
+ else if (cur.nodeType == 11) {
641
+ cur = cur.host;
642
+ }
643
+ else {
644
+ break;
1612
645
  }
1613
- return null;
1614
646
  }
647
+ return { x, y };
1615
648
  }
1616
- class BlockWidgetView extends ContentView {
1617
- constructor(widget, length, deco) {
1618
- super();
1619
- this.widget = widget;
1620
- this.length = length;
1621
- this.deco = deco;
1622
- this.breakAfter = 0;
1623
- this.prevWidget = null;
649
+ class DOMSelectionState {
650
+ constructor() {
651
+ this.anchorNode = null;
652
+ this.anchorOffset = 0;
653
+ this.focusNode = null;
654
+ this.focusOffset = 0;
1624
655
  }
1625
- merge(from, to, source, _takeDeco, openStart, openEnd) {
1626
- if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) ||
1627
- from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
1628
- return false;
1629
- this.length = from + (source ? source.length : 0) + (this.length - to);
1630
- return true;
656
+ eq(domSel) {
657
+ return this.anchorNode == domSel.anchorNode && this.anchorOffset == domSel.anchorOffset &&
658
+ this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset;
1631
659
  }
1632
- domAtPos(pos) {
1633
- return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length);
660
+ setRange(range) {
661
+ let { anchorNode, focusNode } = range;
662
+ // Clip offsets to node size to avoid crashes when Safari reports bogus offsets (#1152)
663
+ this.set(anchorNode, Math.min(range.anchorOffset, anchorNode ? maxOffset(anchorNode) : 0), focusNode, Math.min(range.focusOffset, focusNode ? maxOffset(focusNode) : 0));
1634
664
  }
1635
- split(at) {
1636
- let len = this.length - at;
1637
- this.length = at;
1638
- let end = new BlockWidgetView(this.widget, len, this.deco);
1639
- end.breakAfter = this.breakAfter;
1640
- return end;
665
+ set(anchorNode, anchorOffset, focusNode, focusOffset) {
666
+ this.anchorNode = anchorNode;
667
+ this.anchorOffset = anchorOffset;
668
+ this.focusNode = focusNode;
669
+ this.focusOffset = focusOffset;
1641
670
  }
1642
- get children() { return noChildren; }
1643
- sync(view) {
1644
- if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
1645
- if (this.dom && this.prevWidget)
1646
- this.prevWidget.destroy(this.dom);
1647
- this.prevWidget = null;
1648
- this.setDOM(this.widget.toDOM(view));
1649
- if (!this.widget.editable)
1650
- this.dom.contentEditable = "false";
1651
- }
671
+ }
672
+ let preventScrollSupported = null;
673
+ // Safari 26 breaks preventScroll support
674
+ if (browser.safari && browser.safari_version >= 26)
675
+ preventScrollSupported = false;
676
+ // Feature-detects support for .focus({preventScroll: true}), and uses
677
+ // a fallback kludge when not supported.
678
+ function focusPreventScroll(dom) {
679
+ if (dom.setActive)
680
+ return dom.setActive(); // in IE
681
+ if (preventScrollSupported)
682
+ return dom.focus(preventScrollSupported);
683
+ let stack = [];
684
+ for (let cur = dom; cur; cur = cur.parentNode) {
685
+ stack.push(cur, cur.scrollTop, cur.scrollLeft);
686
+ if (cur == cur.ownerDocument)
687
+ break;
1652
688
  }
1653
- get overrideDOMText() {
1654
- return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : state.Text.empty;
1655
- }
1656
- domBoundsAround() { return null; }
1657
- become(other) {
1658
- if (other instanceof BlockWidgetView &&
1659
- other.widget.constructor == this.widget.constructor) {
1660
- if (!other.widget.compare(this.widget))
1661
- this.markDirty(true);
1662
- if (this.dom && !this.prevWidget)
1663
- this.prevWidget = this.widget;
1664
- this.widget = other.widget;
1665
- this.length = other.length;
1666
- this.deco = other.deco;
1667
- this.breakAfter = other.breakAfter;
689
+ dom.focus(preventScrollSupported == null ? {
690
+ get preventScroll() {
691
+ preventScrollSupported = { preventScroll: true };
1668
692
  return true;
1669
693
  }
1670
- return false;
1671
- }
1672
- ignoreMutation() { return true; }
1673
- ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1674
- get isEditable() { return false; }
1675
- get isWidget() { return true; }
1676
- coordsAt(pos, side) {
1677
- let custom = this.widget.coordsAt(this.dom, pos, side);
1678
- if (custom)
1679
- return custom;
1680
- if (this.widget instanceof BlockGapWidget)
1681
- return null;
1682
- return flattenRect(this.dom.getBoundingClientRect(), this.length ? pos == 0 : side <= 0);
1683
- }
1684
- destroy() {
1685
- super.destroy();
1686
- if (this.dom)
1687
- this.widget.destroy(this.dom);
1688
- }
1689
- covers(side) {
1690
- let { startSide, endSide } = this.deco;
1691
- return startSide == endSide ? false : side < 0 ? startSide < 0 : endSide > 0;
694
+ } : undefined);
695
+ if (!preventScrollSupported) {
696
+ preventScrollSupported = false;
697
+ for (let i = 0; i < stack.length;) {
698
+ let elt = stack[i++], top = stack[i++], left = stack[i++];
699
+ if (elt.scrollTop != top)
700
+ elt.scrollTop = top;
701
+ if (elt.scrollLeft != left)
702
+ elt.scrollLeft = left;
703
+ }
1692
704
  }
1693
705
  }
1694
- class BlockGapWidget extends WidgetType {
1695
- constructor(height) {
1696
- super();
1697
- this.height = height;
1698
- }
1699
- toDOM() {
1700
- let elt = document.createElement("div");
1701
- elt.className = "cm-gap";
1702
- this.updateDOM(elt);
1703
- return elt;
1704
- }
1705
- eq(other) { return other.height == this.height; }
1706
- updateDOM(elt) {
1707
- elt.style.height = this.height + "px";
1708
- return true;
1709
- }
1710
- get editable() { return true; }
1711
- get estimatedHeight() { return this.height; }
1712
- ignoreEvent() { return false; }
706
+ let scratchRange;
707
+ function textRange(node, from, to = from) {
708
+ let range = scratchRange || (scratchRange = document.createRange());
709
+ range.setEnd(node, to);
710
+ range.setStart(node, from);
711
+ return range;
1713
712
  }
1714
-
1715
- class ContentBuilder {
1716
- constructor(doc, pos, end, disallowBlockEffectsFor) {
1717
- this.doc = doc;
1718
- this.pos = pos;
1719
- this.end = end;
1720
- this.disallowBlockEffectsFor = disallowBlockEffectsFor;
1721
- this.content = [];
1722
- this.curLine = null;
1723
- this.breakAtStart = 0;
1724
- this.pendingBuffer = 0 /* Buf.No */;
1725
- this.bufferMarks = [];
1726
- // Set to false directly after a widget that covers the position after it
1727
- this.atCursorPos = true;
1728
- this.openStart = -1;
1729
- this.openEnd = -1;
1730
- this.text = "";
1731
- this.textOff = 0;
1732
- this.cursor = doc.iter();
1733
- this.skip = pos;
1734
- }
1735
- posCovered() {
1736
- if (this.content.length == 0)
1737
- return !this.breakAtStart && this.doc.lineAt(this.pos).from != this.pos;
1738
- let last = this.content[this.content.length - 1];
1739
- return !(last.breakAfter || last instanceof BlockWidgetView && last.deco.endSide < 0);
713
+ function dispatchKey(elt, name, code, mods) {
714
+ let options = { key: name, code: name, keyCode: code, which: code, cancelable: true };
715
+ if (mods)
716
+ ({ altKey: options.altKey, ctrlKey: options.ctrlKey, shiftKey: options.shiftKey, metaKey: options.metaKey } = mods);
717
+ let down = new KeyboardEvent("keydown", options);
718
+ down.synthetic = true;
719
+ elt.dispatchEvent(down);
720
+ let up = new KeyboardEvent("keyup", options);
721
+ up.synthetic = true;
722
+ elt.dispatchEvent(up);
723
+ return down.defaultPrevented || up.defaultPrevented;
724
+ }
725
+ function getRoot(node) {
726
+ while (node) {
727
+ if (node && (node.nodeType == 9 || node.nodeType == 11 && node.host))
728
+ return node;
729
+ node = node.assignedSlot || node.parentNode;
1740
730
  }
1741
- getLine() {
1742
- if (!this.curLine) {
1743
- this.content.push(this.curLine = new LineView);
1744
- this.atCursorPos = true;
731
+ return null;
732
+ }
733
+ function atElementStart(doc, selection) {
734
+ let node = selection.focusNode, offset = selection.focusOffset;
735
+ if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
736
+ return false;
737
+ // Safari can report bogus offsets (#1152)
738
+ offset = Math.min(offset, maxOffset(node));
739
+ for (;;) {
740
+ if (offset) {
741
+ if (node.nodeType != 1)
742
+ return false;
743
+ let prev = node.childNodes[offset - 1];
744
+ if (prev.contentEditable == "false")
745
+ offset--;
746
+ else {
747
+ node = prev;
748
+ offset = maxOffset(node);
749
+ }
1745
750
  }
1746
- return this.curLine;
1747
- }
1748
- flushBuffer(active = this.bufferMarks) {
1749
- if (this.pendingBuffer) {
1750
- this.curLine.append(wrapMarks(new WidgetBufferView(-1), active), active.length);
1751
- this.pendingBuffer = 0 /* Buf.No */;
751
+ else if (node == doc) {
752
+ return true;
753
+ }
754
+ else {
755
+ offset = domIndex(node);
756
+ node = node.parentNode;
1752
757
  }
1753
758
  }
1754
- addBlockWidget(view) {
1755
- this.flushBuffer();
1756
- this.curLine = null;
1757
- this.content.push(view);
1758
- }
1759
- finish(openEnd) {
1760
- if (this.pendingBuffer && openEnd <= this.bufferMarks.length)
1761
- this.flushBuffer();
1762
- else
1763
- this.pendingBuffer = 0 /* Buf.No */;
1764
- if (!this.posCovered() &&
1765
- !(openEnd && this.content.length && this.content[this.content.length - 1] instanceof BlockWidgetView))
1766
- this.getLine();
1767
- }
1768
- buildText(length, active, openStart) {
1769
- while (length > 0) {
1770
- if (this.textOff == this.text.length) {
1771
- let { value, lineBreak, done } = this.cursor.next(this.skip);
1772
- this.skip = 0;
1773
- if (done)
1774
- throw new Error("Ran out of text content when drawing inline views");
1775
- if (lineBreak) {
1776
- if (!this.posCovered())
1777
- this.getLine();
1778
- if (this.content.length)
1779
- this.content[this.content.length - 1].breakAfter = 1;
1780
- else
1781
- this.breakAtStart = 1;
1782
- this.flushBuffer();
1783
- this.curLine = null;
1784
- this.atCursorPos = true;
1785
- length--;
1786
- continue;
1787
- }
1788
- else {
1789
- this.text = value;
1790
- this.textOff = 0;
1791
- }
1792
- }
1793
- let remaining = Math.min(this.text.length - this.textOff, length);
1794
- let take = Math.min(remaining, 512 /* T.Chunk */);
1795
- this.flushBuffer(active.slice(active.length - openStart));
1796
- this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
1797
- this.atCursorPos = true;
1798
- this.textOff += take;
1799
- length -= take;
1800
- openStart = remaining <= take ? 0 : active.length;
759
+ }
760
+ function isScrolledToBottom(elt) {
761
+ return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
762
+ }
763
+ function textNodeBefore(startNode, startOffset) {
764
+ for (let node = startNode, offset = startOffset;;) {
765
+ if (node.nodeType == 3 && offset > 0) {
766
+ return { node: node, offset: offset };
767
+ }
768
+ else if (node.nodeType == 1 && offset > 0) {
769
+ if (node.contentEditable == "false")
770
+ return null;
771
+ node = node.childNodes[offset - 1];
772
+ offset = maxOffset(node);
773
+ }
774
+ else if (node.parentNode && !isBlockElement(node)) {
775
+ offset = domIndex(node);
776
+ node = node.parentNode;
777
+ }
778
+ else {
779
+ return null;
1801
780
  }
1802
781
  }
1803
- span(from, to, active, openStart) {
1804
- this.buildText(to - from, active, openStart);
1805
- this.pos = to;
1806
- if (this.openStart < 0)
1807
- this.openStart = openStart;
1808
- }
1809
- point(from, to, deco, active, openStart, index) {
1810
- if (this.disallowBlockEffectsFor[index] && deco instanceof PointDecoration) {
1811
- if (deco.block)
1812
- throw new RangeError("Block decorations may not be specified via plugins");
1813
- if (to > this.doc.lineAt(this.pos).to)
1814
- throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
1815
- }
1816
- let len = to - from;
1817
- if (deco instanceof PointDecoration) {
1818
- if (deco.block) {
1819
- if (deco.startSide > 0 && !this.posCovered())
1820
- this.getLine();
1821
- this.addBlockWidget(new BlockWidgetView(deco.widget || NullWidget.block, len, deco));
1822
- }
1823
- else {
1824
- let view = WidgetView.create(deco.widget || NullWidget.inline, len, len ? 0 : deco.startSide);
1825
- let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length &&
1826
- (from < to || deco.startSide > 0);
1827
- let cursorAfter = !view.isEditable && (from < to || openStart > active.length || deco.startSide <= 0);
1828
- let line = this.getLine();
1829
- if (this.pendingBuffer == 2 /* Buf.IfCursor */ && !cursorBefore && !view.isEditable)
1830
- this.pendingBuffer = 0 /* Buf.No */;
1831
- this.flushBuffer(active);
1832
- if (cursorBefore) {
1833
- line.append(wrapMarks(new WidgetBufferView(1), active), openStart);
1834
- openStart = active.length + Math.max(0, openStart - active.length);
1835
- }
1836
- line.append(wrapMarks(view, active), openStart);
1837
- this.atCursorPos = cursorAfter;
1838
- this.pendingBuffer = !cursorAfter ? 0 /* Buf.No */ : from < to || openStart > active.length ? 1 /* Buf.Yes */ : 2 /* Buf.IfCursor */;
1839
- if (this.pendingBuffer)
1840
- this.bufferMarks = active.slice();
1841
- }
1842
- }
1843
- else if (this.doc.lineAt(this.pos).from == this.pos) { // Line decoration
1844
- this.getLine().addLineDeco(deco);
1845
- }
1846
- if (len) {
1847
- // Advance the iterator past the replaced content
1848
- if (this.textOff + len <= this.text.length) {
1849
- this.textOff += len;
1850
- }
1851
- else {
1852
- this.skip += len - (this.text.length - this.textOff);
1853
- this.text = "";
1854
- this.textOff = 0;
1855
- }
1856
- this.pos = to;
782
+ }
783
+ function textNodeAfter(startNode, startOffset) {
784
+ for (let node = startNode, offset = startOffset;;) {
785
+ if (node.nodeType == 3 && offset < node.nodeValue.length) {
786
+ return { node: node, offset: offset };
787
+ }
788
+ else if (node.nodeType == 1 && offset < node.childNodes.length) {
789
+ if (node.contentEditable == "false")
790
+ return null;
791
+ node = node.childNodes[offset];
792
+ offset = 0;
793
+ }
794
+ else if (node.parentNode && !isBlockElement(node)) {
795
+ offset = domIndex(node) + 1;
796
+ node = node.parentNode;
797
+ }
798
+ else {
799
+ return null;
1857
800
  }
1858
- if (this.openStart < 0)
1859
- this.openStart = openStart;
1860
- }
1861
- static build(text, from, to, decorations, dynamicDecorationMap) {
1862
- let builder = new ContentBuilder(text, from, to, dynamicDecorationMap);
1863
- builder.openEnd = state.RangeSet.spans(decorations, from, to, builder);
1864
- if (builder.openStart < 0)
1865
- builder.openStart = builder.openEnd;
1866
- builder.finish(builder.openEnd);
1867
- return builder;
1868
801
  }
1869
802
  }
1870
- function wrapMarks(view, active) {
1871
- for (let mark of active)
1872
- view = new MarkView(mark, [view], view.length);
1873
- return view;
1874
- }
1875
- class NullWidget extends WidgetType {
1876
- constructor(tag) {
1877
- super();
1878
- this.tag = tag;
803
+ class DOMPos {
804
+ constructor(node, offset, precise = true) {
805
+ this.node = node;
806
+ this.offset = offset;
807
+ this.precise = precise;
1879
808
  }
1880
- eq(other) { return other.tag == this.tag; }
1881
- toDOM() { return document.createElement(this.tag); }
1882
- updateDOM(elt) { return elt.nodeName.toLowerCase() == this.tag; }
1883
- get isHidden() { return true; }
809
+ static before(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom), precise); }
810
+ static after(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom) + 1, precise); }
1884
811
  }
1885
- NullWidget.inline = new NullWidget("span");
1886
- NullWidget.block = new NullWidget("div");
1887
812
 
1888
813
  /**
1889
814
  Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
@@ -2445,341 +1370,1467 @@ const viewPlugin = state.Facet.define({
2445
1370
  return true;
2446
1371
  });
2447
1372
  }
2448
- });
1373
+ });
1374
+ /**
1375
+ View plugins associate stateful values with a view. They can
1376
+ influence the way the content is drawn, and are notified of things
1377
+ that happen in the view. They optionally take an argument, in
1378
+ which case you need to call [`of`](https://codemirror.net/6/docs/ref/#view.ViewPlugin.of) to create
1379
+ an extension for the plugin. When the argument type is undefined,
1380
+ you can use the plugin instance as an extension directly.
1381
+ */
1382
+ class ViewPlugin {
1383
+ constructor(
1384
+ /**
1385
+ @internal
1386
+ */
1387
+ id,
1388
+ /**
1389
+ @internal
1390
+ */
1391
+ create,
1392
+ /**
1393
+ @internal
1394
+ */
1395
+ domEventHandlers,
1396
+ /**
1397
+ @internal
1398
+ */
1399
+ domEventObservers, buildExtensions) {
1400
+ this.id = id;
1401
+ this.create = create;
1402
+ this.domEventHandlers = domEventHandlers;
1403
+ this.domEventObservers = domEventObservers;
1404
+ this.baseExtensions = buildExtensions(this);
1405
+ this.extension = this.baseExtensions.concat(viewPlugin.of({ plugin: this, arg: undefined }));
1406
+ }
1407
+ /**
1408
+ Create an extension for this plugin with the given argument.
1409
+ */
1410
+ of(arg) {
1411
+ return this.baseExtensions.concat(viewPlugin.of({ plugin: this, arg }));
1412
+ }
1413
+ /**
1414
+ Define a plugin from a constructor function that creates the
1415
+ plugin's value, given an editor view.
1416
+ */
1417
+ static define(create, spec) {
1418
+ const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {};
1419
+ return new ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, plugin => {
1420
+ let ext = [];
1421
+ if (deco)
1422
+ ext.push(decorations.of(view => {
1423
+ let pluginInst = view.plugin(plugin);
1424
+ return pluginInst ? deco(pluginInst) : Decoration.none;
1425
+ }));
1426
+ if (provide)
1427
+ ext.push(provide(plugin));
1428
+ return ext;
1429
+ });
1430
+ }
1431
+ /**
1432
+ Create a plugin for a class whose constructor takes a single
1433
+ editor view as argument.
1434
+ */
1435
+ static fromClass(cls, spec) {
1436
+ return ViewPlugin.define((view, arg) => new cls(view, arg), spec);
1437
+ }
1438
+ }
1439
+ class PluginInstance {
1440
+ constructor(spec) {
1441
+ this.spec = spec;
1442
+ // When starting an update, all plugins have this field set to the
1443
+ // update object, indicating they need to be updated. When finished
1444
+ // updating, it is set to `null`. Retrieving a plugin that needs to
1445
+ // be updated with `view.plugin` forces an eager update.
1446
+ this.mustUpdate = null;
1447
+ // This is null when the plugin is initially created, but
1448
+ // initialized on the first update.
1449
+ this.value = null;
1450
+ }
1451
+ get plugin() { return this.spec && this.spec.plugin; }
1452
+ update(view) {
1453
+ if (!this.value) {
1454
+ if (this.spec) {
1455
+ try {
1456
+ this.value = this.spec.plugin.create(view, this.spec.arg);
1457
+ }
1458
+ catch (e) {
1459
+ logException(view.state, e, "CodeMirror plugin crashed");
1460
+ this.deactivate();
1461
+ }
1462
+ }
1463
+ }
1464
+ else if (this.mustUpdate) {
1465
+ let update = this.mustUpdate;
1466
+ this.mustUpdate = null;
1467
+ if (this.value.update) {
1468
+ try {
1469
+ this.value.update(update);
1470
+ }
1471
+ catch (e) {
1472
+ logException(update.state, e, "CodeMirror plugin crashed");
1473
+ if (this.value.destroy)
1474
+ try {
1475
+ this.value.destroy();
1476
+ }
1477
+ catch (_) { }
1478
+ this.deactivate();
1479
+ }
1480
+ }
1481
+ }
1482
+ return this;
1483
+ }
1484
+ destroy(view) {
1485
+ var _a;
1486
+ if ((_a = this.value) === null || _a === void 0 ? void 0 : _a.destroy) {
1487
+ try {
1488
+ this.value.destroy();
1489
+ }
1490
+ catch (e) {
1491
+ logException(view.state, e, "CodeMirror plugin crashed");
1492
+ }
1493
+ }
1494
+ }
1495
+ deactivate() {
1496
+ this.spec = this.value = null;
1497
+ }
1498
+ }
1499
+ const editorAttributes = state.Facet.define();
1500
+ const contentAttributes = state.Facet.define();
1501
+ // Provide decorations
1502
+ const decorations = state.Facet.define();
1503
+ const blockWrappers = state.Facet.define();
1504
+ const outerDecorations = state.Facet.define();
1505
+ const atomicRanges = state.Facet.define();
1506
+ const bidiIsolatedRanges = state.Facet.define();
1507
+ function getIsolatedRanges(view, line) {
1508
+ let isolates = view.state.facet(bidiIsolatedRanges);
1509
+ if (!isolates.length)
1510
+ return isolates;
1511
+ let sets = isolates.map(i => i instanceof Function ? i(view) : i);
1512
+ let result = [];
1513
+ state.RangeSet.spans(sets, line.from, line.to, {
1514
+ point() { },
1515
+ span(fromDoc, toDoc, active, open) {
1516
+ let from = fromDoc - line.from, to = toDoc - line.from;
1517
+ let level = result;
1518
+ for (let i = active.length - 1; i >= 0; i--, open--) {
1519
+ let direction = active[i].spec.bidiIsolate, update;
1520
+ if (direction == null)
1521
+ direction = autoDirection(line.text, from, to);
1522
+ if (open > 0 && level.length &&
1523
+ (update = level[level.length - 1]).to == from && update.direction == direction) {
1524
+ update.to = to;
1525
+ level = update.inner;
1526
+ }
1527
+ else {
1528
+ let add = { from, to, direction, inner: [] };
1529
+ level.push(add);
1530
+ level = add.inner;
1531
+ }
1532
+ }
1533
+ }
1534
+ });
1535
+ return result;
1536
+ }
1537
+ const scrollMargins = state.Facet.define();
1538
+ function getScrollMargins(view) {
1539
+ let left = 0, right = 0, top = 0, bottom = 0;
1540
+ for (let source of view.state.facet(scrollMargins)) {
1541
+ let m = source(view);
1542
+ if (m) {
1543
+ if (m.left != null)
1544
+ left = Math.max(left, m.left);
1545
+ if (m.right != null)
1546
+ right = Math.max(right, m.right);
1547
+ if (m.top != null)
1548
+ top = Math.max(top, m.top);
1549
+ if (m.bottom != null)
1550
+ bottom = Math.max(bottom, m.bottom);
1551
+ }
1552
+ }
1553
+ return { left, right, top, bottom };
1554
+ }
1555
+ const styleModule = state.Facet.define();
1556
+ class ChangedRange {
1557
+ constructor(fromA, toA, fromB, toB) {
1558
+ this.fromA = fromA;
1559
+ this.toA = toA;
1560
+ this.fromB = fromB;
1561
+ this.toB = toB;
1562
+ }
1563
+ join(other) {
1564
+ return new ChangedRange(Math.min(this.fromA, other.fromA), Math.max(this.toA, other.toA), Math.min(this.fromB, other.fromB), Math.max(this.toB, other.toB));
1565
+ }
1566
+ addToSet(set) {
1567
+ let i = set.length, me = this;
1568
+ for (; i > 0; i--) {
1569
+ let range = set[i - 1];
1570
+ if (range.fromA > me.toA)
1571
+ continue;
1572
+ if (range.toA < me.fromA)
1573
+ break;
1574
+ me = me.join(range);
1575
+ set.splice(i - 1, 1);
1576
+ }
1577
+ set.splice(i, 0, me);
1578
+ return set;
1579
+ }
1580
+ // Extend a set to cover all the content in `ranges`, which is a
1581
+ // flat array with each pair of numbers representing fromB/toB
1582
+ // positions. These pairs are generated in unchanged ranges, so the
1583
+ // offset between doc A and doc B is the same for their start and
1584
+ // end points.
1585
+ static extendWithRanges(diff, ranges) {
1586
+ if (ranges.length == 0)
1587
+ return diff;
1588
+ let result = [];
1589
+ for (let dI = 0, rI = 0, off = 0;;) {
1590
+ let nextD = dI < diff.length ? diff[dI].fromB : 1e9;
1591
+ let nextR = rI < ranges.length ? ranges[rI] : 1e9;
1592
+ let fromB = Math.min(nextD, nextR);
1593
+ if (fromB == 1e9)
1594
+ break;
1595
+ let fromA = fromB + off, toB = fromB, toA = fromA;
1596
+ for (;;) {
1597
+ if (rI < ranges.length && ranges[rI] <= toB) {
1598
+ let end = ranges[rI + 1];
1599
+ rI += 2;
1600
+ toB = Math.max(toB, end);
1601
+ toA = Math.max(toA, end + off);
1602
+ }
1603
+ else if (dI < diff.length && diff[dI].fromB <= toB) {
1604
+ let next = diff[dI++];
1605
+ toB = Math.max(toB, next.toB);
1606
+ toA = Math.max(toA, next.toA);
1607
+ off = next.toA - next.toB;
1608
+ }
1609
+ else {
1610
+ break;
1611
+ }
1612
+ }
1613
+ result.push(new ChangedRange(fromA, toA, fromB, toB));
1614
+ }
1615
+ return result;
1616
+ }
1617
+ }
2449
1618
  /**
2450
- View plugins associate stateful values with a view. They can
2451
- influence the way the content is drawn, and are notified of things
2452
- that happen in the view. They optionally take an argument, in
2453
- which case you need to call [`of`](https://codemirror.net/6/docs/ref/#view.ViewPlugin.of) to create
2454
- an extension for the plugin. When the argument type is undefined,
2455
- you can use the plugin instance as an extension directly.
1619
+ View [plugins](https://codemirror.net/6/docs/ref/#view.ViewPlugin) are given instances of this
1620
+ class, which describe what happened, whenever the view is updated.
2456
1621
  */
2457
- class ViewPlugin {
1622
+ class ViewUpdate {
2458
1623
  constructor(
2459
1624
  /**
2460
- @internal
1625
+ The editor view that the update is associated with.
2461
1626
  */
2462
- id,
1627
+ view,
2463
1628
  /**
2464
- @internal
1629
+ The new editor state.
2465
1630
  */
2466
- create,
1631
+ state$1,
2467
1632
  /**
2468
- @internal
1633
+ The transactions involved in the update. May be empty.
2469
1634
  */
2470
- domEventHandlers,
1635
+ transactions) {
1636
+ this.view = view;
1637
+ this.state = state$1;
1638
+ this.transactions = transactions;
1639
+ /**
1640
+ @internal
1641
+ */
1642
+ this.flags = 0;
1643
+ this.startState = view.state;
1644
+ this.changes = state.ChangeSet.empty(this.startState.doc.length);
1645
+ for (let tr of transactions)
1646
+ this.changes = this.changes.compose(tr.changes);
1647
+ let changedRanges = [];
1648
+ this.changes.iterChangedRanges((fromA, toA, fromB, toB) => changedRanges.push(new ChangedRange(fromA, toA, fromB, toB)));
1649
+ this.changedRanges = changedRanges;
1650
+ }
2471
1651
  /**
2472
1652
  @internal
2473
1653
  */
2474
- domEventObservers, buildExtensions) {
2475
- this.id = id;
2476
- this.create = create;
2477
- this.domEventHandlers = domEventHandlers;
2478
- this.domEventObservers = domEventObservers;
2479
- this.baseExtensions = buildExtensions(this);
2480
- this.extension = this.baseExtensions.concat(viewPlugin.of({ plugin: this, arg: undefined }));
1654
+ static create(view, state, transactions) {
1655
+ return new ViewUpdate(view, state, transactions);
2481
1656
  }
2482
1657
  /**
2483
- Create an extension for this plugin with the given argument.
1658
+ Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
1659
+ [visible ranges](https://codemirror.net/6/docs/ref/#view.EditorView.visibleRanges) changed in this
1660
+ update.
2484
1661
  */
2485
- of(arg) {
2486
- return this.baseExtensions.concat(viewPlugin.of({ plugin: this, arg }));
1662
+ get viewportChanged() {
1663
+ return (this.flags & 4 /* UpdateFlag.Viewport */) > 0;
2487
1664
  }
2488
1665
  /**
2489
- Define a plugin from a constructor function that creates the
2490
- plugin's value, given an editor view.
1666
+ Returns true when
1667
+ [`viewportChanged`](https://codemirror.net/6/docs/ref/#view.ViewUpdate.viewportChanged) is true
1668
+ and the viewport change is not just the result of mapping it in
1669
+ response to document changes.
2491
1670
  */
2492
- static define(create, spec) {
2493
- const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {};
2494
- return new ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, plugin => {
2495
- let ext = [];
2496
- if (deco)
2497
- ext.push(decorations.of(view => {
2498
- let pluginInst = view.plugin(plugin);
2499
- return pluginInst ? deco(pluginInst) : Decoration.none;
2500
- }));
2501
- if (provide)
2502
- ext.push(provide(plugin));
2503
- return ext;
2504
- });
1671
+ get viewportMoved() {
1672
+ return (this.flags & 8 /* UpdateFlag.ViewportMoved */) > 0;
2505
1673
  }
2506
1674
  /**
2507
- Create a plugin for a class whose constructor takes a single
2508
- editor view as argument.
1675
+ Indicates whether the height of a block element in the editor
1676
+ changed in this update.
2509
1677
  */
2510
- static fromClass(cls, spec) {
2511
- return ViewPlugin.define((view, arg) => new cls(view, arg), spec);
1678
+ get heightChanged() {
1679
+ return (this.flags & 2 /* UpdateFlag.Height */) > 0;
2512
1680
  }
1681
+ /**
1682
+ Returns true when the document was modified or the size of the
1683
+ editor, or elements within the editor, changed.
1684
+ */
1685
+ get geometryChanged() {
1686
+ return this.docChanged || (this.flags & (16 /* UpdateFlag.Geometry */ | 2 /* UpdateFlag.Height */)) > 0;
1687
+ }
1688
+ /**
1689
+ True when this update indicates a focus change.
1690
+ */
1691
+ get focusChanged() {
1692
+ return (this.flags & 1 /* UpdateFlag.Focus */) > 0;
1693
+ }
1694
+ /**
1695
+ Whether the document changed in this update.
1696
+ */
1697
+ get docChanged() {
1698
+ return !this.changes.empty;
1699
+ }
1700
+ /**
1701
+ Whether the selection was explicitly set in this update.
1702
+ */
1703
+ get selectionSet() {
1704
+ return this.transactions.some(tr => tr.selection);
1705
+ }
1706
+ /**
1707
+ @internal
1708
+ */
1709
+ get empty() { return this.flags == 0 && this.transactions.length == 0; }
2513
1710
  }
2514
- class PluginInstance {
2515
- constructor(spec) {
2516
- this.spec = spec;
2517
- // When starting an update, all plugins have this field set to the
2518
- // update object, indicating they need to be updated. When finished
2519
- // updating, it is set to `null`. Retrieving a plugin that needs to
2520
- // be updated with `view.plugin` forces an eager update.
2521
- this.mustUpdate = null;
2522
- // This is null when the plugin is initially created, but
2523
- // initialized on the first update.
2524
- this.value = null;
1711
+
1712
+ const noChildren = [];
1713
+ class Tile {
1714
+ constructor(dom, length, flags = 0) {
1715
+ this.dom = dom;
1716
+ this.length = length;
1717
+ this.flags = flags;
1718
+ this.parent = null;
1719
+ dom.cmTile = this;
2525
1720
  }
2526
- get plugin() { return this.spec && this.spec.plugin; }
2527
- update(view) {
2528
- if (!this.value) {
2529
- if (this.spec) {
2530
- try {
2531
- this.value = this.spec.plugin.create(view, this.spec.arg);
1721
+ get breakAfter() { return (this.flags & 1 /* TileFlag.BreakAfter */); }
1722
+ get children() { return noChildren; }
1723
+ isWidget() { return false; }
1724
+ get isHidden() { return false; }
1725
+ isComposite() { return false; }
1726
+ isLine() { return false; }
1727
+ isText() { return false; }
1728
+ isBlock() { return false; }
1729
+ get domAttrs() { return null; }
1730
+ sync() {
1731
+ this.flags |= 2 /* TileFlag.Synced */;
1732
+ if (this.flags & 4 /* TileFlag.AttrsDirty */) {
1733
+ this.flags &= ~4 /* TileFlag.AttrsDirty */;
1734
+ let attrs = this.domAttrs;
1735
+ if (attrs)
1736
+ setAttrs(this.dom, attrs);
1737
+ }
1738
+ }
1739
+ toString() {
1740
+ return this.constructor.name + (this.children.length ? `(${this.children})` : "") + (this.breakAfter ? "#" : "");
1741
+ }
1742
+ destroy() { this.parent = null; }
1743
+ setDOM(dom) {
1744
+ this.dom = dom;
1745
+ dom.cmTile = this;
1746
+ }
1747
+ get posAtStart() {
1748
+ return this.parent ? this.parent.posBefore(this) : 0;
1749
+ }
1750
+ get posAtEnd() {
1751
+ return this.posAtStart + this.length;
1752
+ }
1753
+ posBefore(tile) {
1754
+ let pos = this.posAtStart;
1755
+ for (let child of this.children) {
1756
+ if (child == tile)
1757
+ return pos;
1758
+ pos += child.length + child.breakAfter;
1759
+ }
1760
+ throw new RangeError("Invalid child in posBefore");
1761
+ }
1762
+ posAfter(tile) {
1763
+ return this.posBefore(tile) + tile.length;
1764
+ }
1765
+ covers(side) { return true; }
1766
+ coordsIn(pos, side) { return null; }
1767
+ domPosFor(off, side) {
1768
+ let index = domIndex(this.dom);
1769
+ let after = this.length ? off > 0 : side > 0;
1770
+ return new DOMPos(this.parent.dom, index + (after ? 1 : 0), off == 0 || off == this.length);
1771
+ }
1772
+ markDirty(attrs) {
1773
+ this.flags &= ~2 /* TileFlag.Synced */;
1774
+ if (attrs)
1775
+ this.flags |= 4 /* TileFlag.AttrsDirty */;
1776
+ if (this.parent && (this.parent.flags & 2 /* TileFlag.Synced */))
1777
+ this.parent.markDirty(false);
1778
+ }
1779
+ get overrideDOMText() { return null; }
1780
+ get root() {
1781
+ for (let t = this; t; t = t.parent)
1782
+ if (t instanceof DocTile)
1783
+ return t;
1784
+ return null;
1785
+ }
1786
+ static get(dom) {
1787
+ return dom.cmTile;
1788
+ }
1789
+ }
1790
+ class CompositeTile extends Tile {
1791
+ constructor(dom) {
1792
+ super(dom, 0);
1793
+ this._children = [];
1794
+ }
1795
+ isComposite() { return true; }
1796
+ get children() { return this._children; }
1797
+ get lastChild() { return this.children.length ? this.children[this.children.length - 1] : null; }
1798
+ append(child) {
1799
+ this.children.push(child);
1800
+ child.parent = this;
1801
+ }
1802
+ sync() {
1803
+ if (this.flags & 2 /* TileFlag.Synced */)
1804
+ return;
1805
+ super.sync();
1806
+ let parent = this.dom, prev = null, next;
1807
+ let length = 0;
1808
+ for (let child of this.children) {
1809
+ child.sync();
1810
+ length += child.length + child.breakAfter;
1811
+ next = prev ? prev.nextSibling : parent.firstChild;
1812
+ if (child.dom.parentNode == parent) {
1813
+ while (next && next != child.dom)
1814
+ next = rm$1(next);
1815
+ }
1816
+ else {
1817
+ parent.insertBefore(child.dom, next);
1818
+ }
1819
+ prev = child.dom;
1820
+ }
1821
+ next = prev ? prev.nextSibling : parent.firstChild;
1822
+ while (next)
1823
+ next = rm$1(next);
1824
+ this.length = length;
1825
+ }
1826
+ }
1827
+ // Remove a DOM node and return its next sibling.
1828
+ function rm$1(dom) {
1829
+ let next = dom.nextSibling;
1830
+ dom.parentNode.removeChild(dom);
1831
+ return next;
1832
+ }
1833
+ // The top-level tile. Its dom property equals view.contentDOM.
1834
+ class DocTile extends CompositeTile {
1835
+ constructor(view, dom) {
1836
+ super(dom);
1837
+ this.view = view;
1838
+ }
1839
+ owns(tile) {
1840
+ for (; tile; tile = tile.parent)
1841
+ if (tile == this)
1842
+ return true;
1843
+ return false;
1844
+ }
1845
+ isBlock() { return true; }
1846
+ nearest(dom) {
1847
+ for (;;) {
1848
+ if (!dom)
1849
+ return null;
1850
+ let tile = Tile.get(dom);
1851
+ if (tile && this.owns(tile))
1852
+ return tile;
1853
+ dom = dom.parentNode;
1854
+ }
1855
+ }
1856
+ blockTiles(f) {
1857
+ for (let stack = [], cur = this, i = 0, pos = 0;;) {
1858
+ if (i == cur.children.length) {
1859
+ if (!stack.length)
1860
+ return;
1861
+ cur = cur.parent;
1862
+ if (cur.breakAfter)
1863
+ pos++;
1864
+ i = stack.pop();
1865
+ }
1866
+ else {
1867
+ let next = cur.children[i++];
1868
+ if (next instanceof BlockWrapperTile) {
1869
+ stack.push(i);
1870
+ cur = next;
1871
+ i = 0;
2532
1872
  }
2533
- catch (e) {
2534
- logException(view.state, e, "CodeMirror plugin crashed");
2535
- this.deactivate();
1873
+ else {
1874
+ let end = pos + next.length;
1875
+ let result = f(next, pos);
1876
+ if (result !== undefined)
1877
+ return result;
1878
+ pos = end + next.breakAfter;
1879
+ }
1880
+ }
1881
+ }
1882
+ }
1883
+ // Find the block at the given position. If side < -1, make sure to
1884
+ // stay before block widgets at that position, if side > 1, after
1885
+ // such widgets (used for selection drawing, which needs to be able
1886
+ // to get coordinates for positions that aren't valid cursor positions).
1887
+ resolveBlock(pos, side) {
1888
+ let before, beforeOff = -1, after, afterOff = -1;
1889
+ this.blockTiles((tile, off) => {
1890
+ let end = off + tile.length;
1891
+ if (pos >= off && pos <= end) {
1892
+ if (tile.isWidget() && side >= -1 && side <= 1) {
1893
+ if (tile.flags & 32 /* TileFlag.After */)
1894
+ return true;
1895
+ if (tile.flags & 16 /* TileFlag.Before */)
1896
+ before = undefined;
1897
+ }
1898
+ if ((off < pos || pos == end && (side < -1 ? tile.length : tile.covers(1))) &&
1899
+ (!before || !tile.isWidget() && before.isWidget())) {
1900
+ before = tile;
1901
+ beforeOff = pos - off;
1902
+ }
1903
+ if ((end > pos || pos == off && (side > 1 ? tile.length : tile.covers(-1))) &&
1904
+ (!after || !tile.isWidget() && after.isWidget())) {
1905
+ after = tile;
1906
+ afterOff = pos - off;
1907
+ }
1908
+ }
1909
+ });
1910
+ if (!before && !after)
1911
+ throw new Error("No tile at position " + pos);
1912
+ return before && side < 0 || !after ? { tile: before, offset: beforeOff } : { tile: after, offset: afterOff };
1913
+ }
1914
+ }
1915
+ class BlockWrapperTile extends CompositeTile {
1916
+ constructor(dom, wrapper) {
1917
+ super(dom);
1918
+ this.wrapper = wrapper;
1919
+ }
1920
+ isBlock() { return true; }
1921
+ covers(side) {
1922
+ if (!this.children.length)
1923
+ return false;
1924
+ return side < 0 ? this.children[0].covers(-1) : this.lastChild.covers(1);
1925
+ }
1926
+ get domAttrs() { return this.wrapper.attributes; }
1927
+ static of(wrapper, dom) {
1928
+ let tile = new BlockWrapperTile(dom || document.createElement(wrapper.tagName), wrapper);
1929
+ if (!dom)
1930
+ tile.flags |= 4 /* TileFlag.AttrsDirty */;
1931
+ return tile;
1932
+ }
1933
+ }
1934
+ class LineTile extends CompositeTile {
1935
+ constructor(dom, attrs) {
1936
+ super(dom);
1937
+ this.attrs = attrs;
1938
+ }
1939
+ isLine() { return true; }
1940
+ static start(attrs, dom, keepAttrs) {
1941
+ let line = new LineTile(dom || document.createElement("div"), attrs);
1942
+ if (!dom || !keepAttrs)
1943
+ line.flags |= 4 /* TileFlag.AttrsDirty */;
1944
+ return line;
1945
+ }
1946
+ get domAttrs() { return this.attrs; }
1947
+ // Find the tile associated with a given position in this line.
1948
+ resolveInline(pos, side, forCoords) {
1949
+ let before = null, beforeOff = -1, after = null, afterOff = -1;
1950
+ function scan(tile, pos) {
1951
+ for (let i = 0, off = 0; i < tile.children.length && off <= pos; i++) {
1952
+ let child = tile.children[i], end = off + child.length;
1953
+ if (end >= pos) {
1954
+ if (child.isComposite()) {
1955
+ scan(child, pos - off);
1956
+ }
1957
+ else if ((!after || after.isHidden && (side > 0 || forCoords && onSameLine(after, child))) &&
1958
+ (end > pos || (child.flags & 32 /* TileFlag.After */))) {
1959
+ after = child;
1960
+ afterOff = pos - off;
1961
+ }
1962
+ else if (off < pos || (child.flags & 16 /* TileFlag.Before */) && !child.isHidden) {
1963
+ before = child;
1964
+ beforeOff = pos - off;
1965
+ }
1966
+ }
1967
+ off = end;
1968
+ }
1969
+ }
1970
+ scan(this, pos);
1971
+ let target = ((side < 0 ? before : after) || before || after);
1972
+ return target ? { tile: target, offset: target == before ? beforeOff : afterOff } : null;
1973
+ }
1974
+ coordsIn(pos, side) {
1975
+ let found = this.resolveInline(pos, side, true);
1976
+ if (!found)
1977
+ return fallbackRect(this);
1978
+ return found.tile.coordsIn(Math.max(0, found.offset), side);
1979
+ }
1980
+ domIn(pos, side) {
1981
+ let found = this.resolveInline(pos, side);
1982
+ if (found) {
1983
+ let { tile, offset } = found;
1984
+ if (this.dom.contains(tile.dom)) {
1985
+ if (tile.isText())
1986
+ return new DOMPos(tile.dom, Math.min(tile.dom.nodeValue.length, offset));
1987
+ return tile.domPosFor(offset, side);
1988
+ }
1989
+ let parent = found.tile.parent, saw = false;
1990
+ for (let ch of parent.children) {
1991
+ if (saw)
1992
+ return new DOMPos(ch.dom, 0);
1993
+ if (ch == found.tile) {
1994
+ saw = true;
1995
+ }
1996
+ }
1997
+ }
1998
+ return new DOMPos(this.dom, 0);
1999
+ }
2000
+ }
2001
+ function fallbackRect(tile) {
2002
+ let last = tile.dom.lastChild;
2003
+ if (!last)
2004
+ return tile.dom.getBoundingClientRect();
2005
+ let rects = clientRectsFor(last);
2006
+ return rects[rects.length - 1] || null;
2007
+ }
2008
+ function onSameLine(a, b) {
2009
+ let posA = a.coordsIn(0, 1), posB = b.coordsIn(0, 1);
2010
+ return posA && posB && posB.top < posA.bottom;
2011
+ }
2012
+ class MarkTile extends CompositeTile {
2013
+ constructor(dom, mark) {
2014
+ super(dom);
2015
+ this.mark = mark;
2016
+ }
2017
+ get domAttrs() { return this.mark.attrs; }
2018
+ static of(mark, dom) {
2019
+ let tile = new MarkTile(dom || document.createElement(mark.tagName), mark);
2020
+ if (!dom)
2021
+ tile.flags |= 4 /* TileFlag.AttrsDirty */;
2022
+ return tile;
2023
+ }
2024
+ }
2025
+ class TextTile extends Tile {
2026
+ constructor(dom, text) {
2027
+ super(dom, text.length);
2028
+ this.text = text;
2029
+ }
2030
+ sync() {
2031
+ if (this.flags & 2 /* TileFlag.Synced */)
2032
+ return;
2033
+ super.sync();
2034
+ if (this.dom.nodeValue != this.text)
2035
+ this.dom.nodeValue = this.text;
2036
+ }
2037
+ isText() { return true; }
2038
+ toString() { return JSON.stringify(this.text); }
2039
+ coordsIn(pos, side) {
2040
+ let length = this.dom.nodeValue.length;
2041
+ if (pos > length)
2042
+ pos = length;
2043
+ let from = pos, to = pos, flatten = 0;
2044
+ if (pos == 0 && side < 0 || pos == length && side >= 0) {
2045
+ if (!(browser.chrome || browser.gecko)) { // These browsers reliably return valid rectangles for empty ranges
2046
+ if (pos) {
2047
+ from--;
2048
+ flatten = 1;
2049
+ } // FIXME this is wrong in RTL text
2050
+ else if (to < length) {
2051
+ to++;
2052
+ flatten = -1;
2536
2053
  }
2537
2054
  }
2538
2055
  }
2539
- else if (this.mustUpdate) {
2540
- let update = this.mustUpdate;
2541
- this.mustUpdate = null;
2542
- if (this.value.update) {
2543
- try {
2544
- this.value.update(update);
2545
- }
2546
- catch (e) {
2547
- logException(update.state, e, "CodeMirror plugin crashed");
2548
- if (this.value.destroy)
2549
- try {
2550
- this.value.destroy();
2551
- }
2552
- catch (_) { }
2553
- this.deactivate();
2554
- }
2555
- }
2056
+ else {
2057
+ if (side < 0)
2058
+ from--;
2059
+ else if (to < length)
2060
+ to++;
2556
2061
  }
2557
- return this;
2062
+ let rects = textRange(this.dom, from, to).getClientRects();
2063
+ if (!rects.length)
2064
+ return null;
2065
+ let rect = rects[(flatten ? flatten < 0 : side >= 0) ? 0 : rects.length - 1];
2066
+ if (browser.safari && !flatten && rect.width == 0)
2067
+ rect = Array.prototype.find.call(rects, r => r.width) || rect;
2068
+ return flatten ? flattenRect(rect, flatten < 0) : rect || null;
2558
2069
  }
2559
- destroy(view) {
2560
- var _a;
2561
- if ((_a = this.value) === null || _a === void 0 ? void 0 : _a.destroy) {
2562
- try {
2563
- this.value.destroy();
2564
- }
2565
- catch (e) {
2566
- logException(view.state, e, "CodeMirror plugin crashed");
2070
+ static of(text, dom) {
2071
+ let tile = new TextTile(dom || document.createTextNode(text), text);
2072
+ if (!dom)
2073
+ tile.flags |= 2 /* TileFlag.Synced */;
2074
+ return tile;
2075
+ }
2076
+ }
2077
+ class WidgetTile extends Tile {
2078
+ constructor(dom, length, widget, flags) {
2079
+ super(dom, length, flags);
2080
+ this.widget = widget;
2081
+ }
2082
+ isWidget() { return true; }
2083
+ get isHidden() { return this.widget.isHidden; }
2084
+ covers(side) {
2085
+ if (this.flags & 48 /* TileFlag.PointWidget */)
2086
+ return false;
2087
+ return (this.flags & (side < 0 ? 64 /* TileFlag.IncStart */ : 128 /* TileFlag.IncEnd */)) > 0;
2088
+ }
2089
+ coordsIn(pos, side) { return this.coordsInWidget(pos, side, false); }
2090
+ coordsInWidget(pos, side, block) {
2091
+ let custom = this.widget.coordsAt(this.dom, pos, side);
2092
+ if (custom)
2093
+ return custom;
2094
+ if (block) {
2095
+ return flattenRect(this.dom.getBoundingClientRect(), this.length ? pos == 0 : side <= 0);
2096
+ }
2097
+ else {
2098
+ let rects = this.dom.getClientRects(), rect = null;
2099
+ if (!rects.length)
2100
+ return null;
2101
+ let fromBack = (this.flags & 16 /* TileFlag.Before */) ? true : (this.flags & 32 /* TileFlag.After */) ? false : pos > 0;
2102
+ for (let i = fromBack ? rects.length - 1 : 0;; i += (fromBack ? -1 : 1)) {
2103
+ rect = rects[i];
2104
+ if (pos > 0 ? i == 0 : i == rects.length - 1 || rect.top < rect.bottom)
2105
+ break;
2567
2106
  }
2107
+ return flattenRect(rect, !fromBack);
2568
2108
  }
2569
2109
  }
2570
- deactivate() {
2571
- this.spec = this.value = null;
2110
+ get overrideDOMText() {
2111
+ if (!this.length)
2112
+ return state.Text.empty;
2113
+ let { root } = this;
2114
+ if (!root)
2115
+ return state.Text.empty;
2116
+ let start = this.posAtStart;
2117
+ return root.view.state.doc.slice(start, start + this.length);
2118
+ }
2119
+ destroy() {
2120
+ super.destroy();
2121
+ this.widget.destroy(this.dom);
2122
+ }
2123
+ static of(widget, view, length, flags, dom) {
2124
+ if (!dom) {
2125
+ dom = widget.toDOM(view);
2126
+ if (!widget.editable)
2127
+ dom.contentEditable = "false";
2128
+ }
2129
+ return new WidgetTile(dom, length, widget, flags);
2572
2130
  }
2573
2131
  }
2574
- const editorAttributes = state.Facet.define();
2575
- const contentAttributes = state.Facet.define();
2576
- // Provide decorations
2577
- const decorations = state.Facet.define();
2578
- const outerDecorations = state.Facet.define();
2579
- const atomicRanges = state.Facet.define();
2580
- const bidiIsolatedRanges = state.Facet.define();
2581
- function getIsolatedRanges(view, line) {
2582
- let isolates = view.state.facet(bidiIsolatedRanges);
2583
- if (!isolates.length)
2584
- return isolates;
2585
- let sets = isolates.map(i => i instanceof Function ? i(view) : i);
2586
- let result = [];
2587
- state.RangeSet.spans(sets, line.from, line.to, {
2588
- point() { },
2589
- span(fromDoc, toDoc, active, open) {
2590
- let from = fromDoc - line.from, to = toDoc - line.from;
2591
- let level = result;
2592
- for (let i = active.length - 1; i >= 0; i--, open--) {
2593
- let direction = active[i].spec.bidiIsolate, update;
2594
- if (direction == null)
2595
- direction = autoDirection(line.text, from, to);
2596
- if (open > 0 && level.length &&
2597
- (update = level[level.length - 1]).to == from && update.direction == direction) {
2598
- update.to = to;
2599
- level = update.inner;
2132
+ // These are drawn around uneditable widgets to avoid a number of
2133
+ // browser bugs that show up when the cursor is directly next to
2134
+ // uneditable inline content.
2135
+ class WidgetBufferTile extends Tile {
2136
+ constructor(flags) {
2137
+ let img = document.createElement("img");
2138
+ img.className = "cm-widgetBuffer";
2139
+ img.setAttribute("aria-hidden", "true");
2140
+ super(img, 0, flags);
2141
+ }
2142
+ get isHidden() { return false; }
2143
+ get overrideDOMText() { return state.Text.empty; }
2144
+ coordsIn(pos) { return this.dom.getBoundingClientRect(); }
2145
+ }
2146
+ // Represents a position in the tile tree.
2147
+ class TilePointer {
2148
+ constructor(top) {
2149
+ this.index = 0;
2150
+ this.beforeBreak = false;
2151
+ this.parents = [];
2152
+ this.tile = top;
2153
+ }
2154
+ // Advance by the given distance. If side is -1, stop leaving or
2155
+ // entering tiles, or skipping zero-length tiles, once the distance
2156
+ // has been traversed. When side is 1, leave, enter, or skip
2157
+ // everything at the end position.
2158
+ advance(dist, side, walker) {
2159
+ let { tile, index, beforeBreak, parents } = this;
2160
+ while (dist || side > 0) {
2161
+ if (!tile.isComposite()) {
2162
+ if (index == tile.length) {
2163
+ beforeBreak = !!tile.breakAfter;
2164
+ ({ tile, index } = parents.pop());
2165
+ index++;
2166
+ }
2167
+ else if (!dist) {
2168
+ break;
2600
2169
  }
2601
2170
  else {
2602
- let add = { from, to, direction, inner: [] };
2603
- level.push(add);
2604
- level = add.inner;
2171
+ let take = Math.min(dist, tile.length - index);
2172
+ if (walker)
2173
+ walker.skip(tile, index, index + take);
2174
+ dist -= take;
2175
+ index += take;
2176
+ }
2177
+ }
2178
+ else if (beforeBreak) {
2179
+ if (!dist)
2180
+ break;
2181
+ if (walker)
2182
+ walker.break();
2183
+ dist--;
2184
+ beforeBreak = false;
2185
+ }
2186
+ else if (index == tile.children.length) {
2187
+ if (!dist && !parents.length)
2188
+ break;
2189
+ if (walker)
2190
+ walker.leave(tile);
2191
+ beforeBreak = !!tile.breakAfter;
2192
+ ({ tile, index } = parents.pop());
2193
+ index++;
2194
+ }
2195
+ else {
2196
+ let next = tile.children[index], brk = next.breakAfter;
2197
+ if ((side > 0 ? next.length <= dist : next.length < dist) &&
2198
+ (!walker || walker.skip(next, 0, next.length) !== false || !next.isComposite)) {
2199
+ beforeBreak = !!brk;
2200
+ index++;
2201
+ dist -= next.length;
2202
+ }
2203
+ else {
2204
+ parents.push({ tile, index });
2205
+ tile = next;
2206
+ index = 0;
2207
+ if (walker && next.isComposite())
2208
+ walker.enter(next);
2605
2209
  }
2606
2210
  }
2607
2211
  }
2608
- });
2609
- return result;
2212
+ this.tile = tile;
2213
+ this.index = index;
2214
+ this.beforeBreak = beforeBreak;
2215
+ return this;
2216
+ }
2217
+ get root() { return (this.parents.length ? this.parents[0].tile : this.tile); }
2610
2218
  }
2611
- const scrollMargins = state.Facet.define();
2612
- function getScrollMargins(view) {
2613
- let left = 0, right = 0, top = 0, bottom = 0;
2614
- for (let source of view.state.facet(scrollMargins)) {
2615
- let m = source(view);
2616
- if (m) {
2617
- if (m.left != null)
2618
- left = Math.max(left, m.left);
2619
- if (m.right != null)
2620
- right = Math.max(right, m.right);
2621
- if (m.top != null)
2622
- top = Math.max(top, m.top);
2623
- if (m.bottom != null)
2624
- bottom = Math.max(bottom, m.bottom);
2625
- }
2219
+
2220
+ // Used to track open block wrappers
2221
+ class OpenWrapper {
2222
+ constructor(from, to, wrapper, rank) {
2223
+ this.from = from;
2224
+ this.to = to;
2225
+ this.wrapper = wrapper;
2226
+ this.rank = rank;
2626
2227
  }
2627
- return { left, right, top, bottom };
2628
2228
  }
2629
- const styleModule = state.Facet.define();
2630
- class ChangedRange {
2631
- constructor(fromA, toA, fromB, toB) {
2632
- this.fromA = fromA;
2633
- this.toA = toA;
2634
- this.fromB = fromB;
2635
- this.toB = toB;
2229
+ // This class builds up a new document tile using input from either
2230
+ // iteration over the old tree or iteration over the document +
2231
+ // decorations. The add* methods emit elements into the tile
2232
+ // structure. To avoid awkward synchronization issues, marks and block
2233
+ // wrappers are treated as belonging to to their content, rather than
2234
+ // opened/closed independently.
2235
+ //
2236
+ // All composite tiles that are touched by changes are rebuilt,
2237
+ // reusing as much of the old tree (either whole nodes or just DOM
2238
+ // elements) as possible. The new tree is built without the Synced
2239
+ // flag, and then synced (during which DOM parent/child relations are
2240
+ // fixed up, text nodes filled in, and attributes added) in a second
2241
+ // phase.
2242
+ class TileBuilder {
2243
+ constructor(cache, root, blockWrappers) {
2244
+ this.cache = cache;
2245
+ this.root = root;
2246
+ this.blockWrappers = blockWrappers;
2247
+ this.curLine = null;
2248
+ this.lastBlock = null;
2249
+ this.afterWidget = null;
2250
+ this.pos = 0;
2251
+ this.wrappers = [];
2252
+ this.wrapperPos = 0;
2636
2253
  }
2637
- join(other) {
2638
- return new ChangedRange(Math.min(this.fromA, other.fromA), Math.max(this.toA, other.toA), Math.min(this.fromB, other.fromB), Math.max(this.toB, other.toB));
2254
+ addText(text, marks, openStart, tile) {
2255
+ var _a;
2256
+ this.flushBuffer();
2257
+ let parent = this.ensureMarks(marks, openStart);
2258
+ let prev = parent.lastChild;
2259
+ if (prev && prev.isText() && !(prev.flags & 8 /* TileFlag.Composition */)) {
2260
+ this.cache.reused.set(prev, 2 /* Reused.DOM */);
2261
+ let tile = parent.children[parent.children.length - 1] = new TextTile(prev.dom, prev.text + text);
2262
+ tile.parent = parent;
2263
+ }
2264
+ else {
2265
+ parent.append(tile || TextTile.of(text, (_a = this.cache.find(TextTile)) === null || _a === void 0 ? void 0 : _a.dom));
2266
+ }
2267
+ this.pos += text.length;
2268
+ this.afterWidget = null;
2269
+ }
2270
+ addComposition(composition, context) {
2271
+ let line = this.curLine;
2272
+ if (line.dom != context.line.dom) {
2273
+ line.setDOM(this.cache.reused.has(context.line) ? freeNode(context.line.dom) : context.line.dom);
2274
+ this.cache.reused.set(context.line, 2 /* Reused.DOM */);
2275
+ }
2276
+ let head = line;
2277
+ for (let i = context.marks.length - 1; i >= 0; i--) {
2278
+ let mark = context.marks[i];
2279
+ let last = head.lastChild;
2280
+ if (last instanceof MarkTile && last.mark.eq(mark.mark)) {
2281
+ if (last.dom != mark.dom)
2282
+ last.setDOM(freeNode(mark.dom));
2283
+ head = last;
2284
+ }
2285
+ else {
2286
+ if (this.cache.reused.get(mark)) {
2287
+ let tile = Tile.get(mark.dom);
2288
+ if (tile)
2289
+ tile.setDOM(freeNode(mark.dom));
2290
+ }
2291
+ let nw = MarkTile.of(mark.mark, mark.dom);
2292
+ head.append(nw);
2293
+ head = nw;
2294
+ }
2295
+ this.cache.reused.set(mark, 2 /* Reused.DOM */);
2296
+ }
2297
+ let text = new TextTile(composition.text, composition.text.nodeValue);
2298
+ text.flags |= 8 /* TileFlag.Composition */;
2299
+ head.append(text);
2639
2300
  }
2640
- addToSet(set) {
2641
- let i = set.length, me = this;
2642
- for (; i > 0; i--) {
2643
- let range = set[i - 1];
2644
- if (range.fromA > me.toA)
2645
- continue;
2646
- if (range.toA < me.fromA)
2647
- break;
2648
- me = me.join(range);
2649
- set.splice(i - 1, 1);
2301
+ addInlineWidget(widget, marks, openStart) {
2302
+ // Adjacent same-side-facing non-replacing widgets don't need buffers between them
2303
+ let noSpace = this.afterWidget && (widget.flags & 48 /* TileFlag.PointWidget */) &&
2304
+ (this.afterWidget.flags & 48 /* TileFlag.PointWidget */) == (widget.flags & 48 /* TileFlag.PointWidget */);
2305
+ if (!noSpace)
2306
+ this.flushBuffer();
2307
+ let parent = this.ensureMarks(marks, openStart);
2308
+ if (!noSpace && !(widget.flags & 16 /* TileFlag.Before */))
2309
+ parent.append(this.getBuffer(1));
2310
+ parent.append(widget);
2311
+ this.pos += widget.length;
2312
+ this.afterWidget = widget;
2313
+ }
2314
+ addMark(tile, marks, openStart) {
2315
+ this.flushBuffer();
2316
+ let parent = this.ensureMarks(marks, openStart);
2317
+ parent.append(tile);
2318
+ this.pos += tile.length;
2319
+ this.afterWidget = null;
2320
+ }
2321
+ addBlockWidget(widget) {
2322
+ this.getBlockPos().append(widget);
2323
+ this.pos += widget.length;
2324
+ this.lastBlock = widget;
2325
+ this.endLine();
2326
+ }
2327
+ continueWidget(length) {
2328
+ let widget = this.afterWidget || this.lastBlock;
2329
+ widget.length += length;
2330
+ this.pos += length;
2331
+ }
2332
+ addLineStart(attrs, dom) {
2333
+ var _a;
2334
+ if (!attrs)
2335
+ attrs = lineBaseAttrs;
2336
+ let tile = LineTile.start(attrs, dom || ((_a = this.cache.find(LineTile)) === null || _a === void 0 ? void 0 : _a.dom), !!dom);
2337
+ this.getBlockPos().append(this.lastBlock = this.curLine = tile);
2338
+ }
2339
+ addLine(tile) {
2340
+ this.getBlockPos().append(tile);
2341
+ this.pos += tile.length;
2342
+ this.lastBlock = tile;
2343
+ this.endLine();
2344
+ }
2345
+ addBreak() {
2346
+ this.lastBlock.flags |= 1 /* TileFlag.BreakAfter */;
2347
+ this.endLine();
2348
+ this.pos++;
2349
+ }
2350
+ addLineStartIfNotCovered(attrs) {
2351
+ if (!this.blockPosCovered())
2352
+ this.addLineStart(attrs);
2353
+ }
2354
+ ensureLine(attrs) {
2355
+ if (!this.curLine)
2356
+ this.addLineStart(attrs);
2357
+ }
2358
+ ensureMarks(marks, openStart) {
2359
+ var _a;
2360
+ let parent = this.curLine;
2361
+ for (let i = marks.length - 1; i >= 0; i--) {
2362
+ let mark = marks[i], last;
2363
+ if (openStart > 0 && (last = parent.lastChild) && last instanceof MarkTile && last.mark.eq(mark)) {
2364
+ parent = last;
2365
+ openStart--;
2366
+ }
2367
+ else {
2368
+ let tile = MarkTile.of(mark, (_a = this.cache.find(MarkTile, m => m.mark.eq(mark))) === null || _a === void 0 ? void 0 : _a.dom);
2369
+ parent.append(tile);
2370
+ parent = tile;
2371
+ openStart = 0;
2372
+ }
2650
2373
  }
2651
- set.splice(i, 0, me);
2652
- return set;
2374
+ return parent;
2653
2375
  }
2654
- static extendWithRanges(diff, ranges) {
2655
- if (ranges.length == 0)
2656
- return diff;
2657
- let result = [];
2658
- for (let dI = 0, rI = 0, posA = 0, posB = 0;; dI++) {
2659
- let next = dI == diff.length ? null : diff[dI], off = posA - posB;
2660
- let end = next ? next.fromB : 1e9;
2661
- while (rI < ranges.length && ranges[rI] < end) {
2662
- let from = ranges[rI], to = ranges[rI + 1];
2663
- let fromB = Math.max(posB, from), toB = Math.min(end, to);
2664
- if (fromB <= toB)
2665
- new ChangedRange(fromB + off, toB + off, fromB, toB).addToSet(result);
2666
- if (to > end)
2667
- break;
2668
- else
2669
- rI += 2;
2376
+ endLine() {
2377
+ if (this.curLine) {
2378
+ this.flushBuffer();
2379
+ let last = this.curLine.lastChild;
2380
+ if (!last || !hasContent(this.curLine, false) ||
2381
+ last.dom.nodeName != "BR" && last.isWidget() && !(browser.ios && hasContent(this.curLine, true)))
2382
+ this.curLine.append(this.cache.findWidget(BreakWidget, 0, 32 /* TileFlag.After */) ||
2383
+ new WidgetTile(BreakWidget.toDOM(), 0, BreakWidget, 32 /* TileFlag.After */));
2384
+ this.curLine = this.afterWidget = null;
2385
+ }
2386
+ }
2387
+ updateBlockWrappers() {
2388
+ if (this.wrapperPos > this.pos + 10000 /* C.WrapperReset */) {
2389
+ this.blockWrappers.goto(this.pos);
2390
+ this.wrappers.length = 0;
2391
+ }
2392
+ for (let i = this.wrappers.length - 1; i >= 0; i--)
2393
+ if (this.wrappers[i].to < this.pos)
2394
+ this.wrappers.splice(i, 1);
2395
+ for (let cur = this.blockWrappers; cur.value && cur.from <= this.pos; cur.next())
2396
+ if (cur.to >= this.pos) {
2397
+ let wrap = new OpenWrapper(cur.from, cur.to, cur.value, cur.rank), i = this.wrappers.length;
2398
+ while (i > 0 && this.wrappers[i - 1].rank < wrap.rank)
2399
+ i--;
2400
+ this.wrappers.splice(i, 0, wrap);
2401
+ }
2402
+ this.wrapperPos = this.pos;
2403
+ }
2404
+ getBlockPos() {
2405
+ var _a;
2406
+ this.updateBlockWrappers();
2407
+ let parent = this.root;
2408
+ for (let wrap of this.wrappers) {
2409
+ let last = parent.lastChild;
2410
+ if (wrap.from < this.pos && last instanceof BlockWrapperTile && last.wrapper.eq(wrap.wrapper)) {
2411
+ parent = last;
2412
+ }
2413
+ else {
2414
+ let tile = BlockWrapperTile.of(wrap.wrapper, (_a = this.cache.find(BlockWrapperTile, t => t.wrapper.eq(wrap.wrapper))) === null || _a === void 0 ? void 0 : _a.dom);
2415
+ parent.append(tile);
2416
+ parent = tile;
2670
2417
  }
2671
- if (!next)
2672
- return result;
2673
- new ChangedRange(next.fromA, next.toA, next.fromB, next.toB).addToSet(result);
2674
- posA = next.toA;
2675
- posB = next.toB;
2676
2418
  }
2419
+ return parent;
2677
2420
  }
2678
- }
2679
- /**
2680
- View [plugins](https://codemirror.net/6/docs/ref/#view.ViewPlugin) are given instances of this
2681
- class, which describe what happened, whenever the view is updated.
2682
- */
2683
- class ViewUpdate {
2684
- constructor(
2685
- /**
2686
- The editor view that the update is associated with.
2687
- */
2688
- view,
2689
- /**
2690
- The new editor state.
2691
- */
2692
- state$1,
2693
- /**
2694
- The transactions involved in the update. May be empty.
2695
- */
2696
- transactions) {
2697
- this.view = view;
2698
- this.state = state$1;
2699
- this.transactions = transactions;
2700
- /**
2701
- @internal
2702
- */
2703
- this.flags = 0;
2704
- this.startState = view.state;
2705
- this.changes = state.ChangeSet.empty(this.startState.doc.length);
2706
- for (let tr of transactions)
2707
- this.changes = this.changes.compose(tr.changes);
2708
- let changedRanges = [];
2709
- this.changes.iterChangedRanges((fromA, toA, fromB, toB) => changedRanges.push(new ChangedRange(fromA, toA, fromB, toB)));
2710
- this.changedRanges = changedRanges;
2421
+ blockPosCovered() {
2422
+ let last = this.lastBlock;
2423
+ return last != null && !last.breakAfter && (!last.isWidget() || (last.flags & (32 /* TileFlag.After */ | 128 /* TileFlag.IncEnd */)) > 0);
2711
2424
  }
2712
- /**
2713
- @internal
2714
- */
2715
- static create(view, state, transactions) {
2716
- return new ViewUpdate(view, state, transactions);
2425
+ getBuffer(side) {
2426
+ let flags = 2 /* TileFlag.Synced */ | (side < 0 ? 16 /* TileFlag.Before */ : 32 /* TileFlag.After */);
2427
+ let found = this.cache.find(WidgetBufferTile, undefined, 1 /* Reused.Full */);
2428
+ if (found)
2429
+ found.flags = flags;
2430
+ return found || new WidgetBufferTile(flags);
2717
2431
  }
2718
- /**
2719
- Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
2720
- [visible ranges](https://codemirror.net/6/docs/ref/#view.EditorView.visibleRanges) changed in this
2721
- update.
2722
- */
2723
- get viewportChanged() {
2724
- return (this.flags & 4 /* UpdateFlag.Viewport */) > 0;
2432
+ flushBuffer() {
2433
+ if (this.afterWidget && !(this.afterWidget.flags & 32 /* TileFlag.After */)) {
2434
+ this.afterWidget.parent.append(this.getBuffer(-1));
2435
+ this.afterWidget = null;
2436
+ }
2725
2437
  }
2726
- /**
2727
- Returns true when
2728
- [`viewportChanged`](https://codemirror.net/6/docs/ref/#view.ViewUpdate.viewportChanged) is true
2729
- and the viewport change is not just the result of mapping it in
2730
- response to document changes.
2731
- */
2732
- get viewportMoved() {
2733
- return (this.flags & 8 /* UpdateFlag.ViewportMoved */) > 0;
2438
+ }
2439
+ // Helps getting efficient access to the document text.
2440
+ class TextStream {
2441
+ constructor(doc) {
2442
+ this.skipCount = 0;
2443
+ this.text = "";
2444
+ this.textOff = 0;
2445
+ this.cursor = doc.iter();
2734
2446
  }
2735
- /**
2736
- Indicates whether the height of a block element in the editor
2737
- changed in this update.
2738
- */
2739
- get heightChanged() {
2740
- return (this.flags & 2 /* UpdateFlag.Height */) > 0;
2447
+ skip(len) {
2448
+ // Advance the iterator past the replaced content
2449
+ if (this.textOff + len <= this.text.length) {
2450
+ this.textOff += len;
2451
+ }
2452
+ else {
2453
+ this.skipCount += len - (this.text.length - this.textOff);
2454
+ this.text = "";
2455
+ this.textOff = 0;
2456
+ }
2457
+ }
2458
+ next(maxLen) {
2459
+ if (this.textOff == this.text.length) {
2460
+ let { value, lineBreak, done } = this.cursor.next(this.skipCount);
2461
+ this.skipCount = 0;
2462
+ if (done)
2463
+ throw new Error("Ran out of text content when drawing inline views");
2464
+ this.text = value;
2465
+ let len = this.textOff = Math.min(maxLen, value.length);
2466
+ return lineBreak ? null : value.slice(0, len);
2467
+ }
2468
+ let end = Math.min(this.text.length, this.textOff + maxLen);
2469
+ let chars = this.text.slice(this.textOff, end);
2470
+ this.textOff = end;
2471
+ return chars;
2472
+ }
2473
+ }
2474
+ // Assign the tile classes bucket numbers for caching.
2475
+ const buckets = [WidgetTile, LineTile, TextTile, MarkTile, WidgetBufferTile, BlockWrapperTile, DocTile];
2476
+ for (let i = 0; i < buckets.length; i++)
2477
+ buckets[i].bucket = i;
2478
+ // Leaf tiles and line tiles may be reused in their entirety. All
2479
+ // others will get new tiles allocated, using the old DOM when
2480
+ // possible.
2481
+ class TileCache {
2482
+ constructor(view) {
2483
+ this.view = view;
2484
+ // Buckets are circular buffers, using `index` as the current
2485
+ // position.
2486
+ this.buckets = buckets.map(() => []);
2487
+ this.index = buckets.map(() => 0);
2488
+ this.reused = new Map;
2489
+ }
2490
+ // Put a tile in the cache.
2491
+ add(tile) {
2492
+ let i = tile.constructor.bucket, bucket = this.buckets[i];
2493
+ if (bucket.length < 6 /* C.Bucket */)
2494
+ bucket.push(tile);
2495
+ else
2496
+ bucket[this.index[i] = (this.index[i] + 1) % 6 /* C.Bucket */] = tile;
2497
+ }
2498
+ find(cls, test, type = 2 /* Reused.DOM */) {
2499
+ let i = cls.bucket;
2500
+ let bucket = this.buckets[i], off = this.index[i];
2501
+ for (let j = bucket.length - 1; j >= 0; j--) {
2502
+ // Look at the most recently added items first (last-in, first-out)
2503
+ let index = (j + off) % bucket.length, tile = bucket[index];
2504
+ if ((!test || test(tile)) && !this.reused.has(tile)) {
2505
+ bucket.splice(index, 1);
2506
+ if (index < off)
2507
+ this.index[i]--;
2508
+ this.reused.set(tile, type);
2509
+ return tile;
2510
+ }
2511
+ }
2512
+ return null;
2513
+ }
2514
+ findWidget(widget, length, flags) {
2515
+ let widgets = this.buckets[0];
2516
+ if (widgets.length)
2517
+ for (let i = 0, pass = 0;; i++) {
2518
+ if (i == widgets.length) {
2519
+ if (pass)
2520
+ return null;
2521
+ pass = 1;
2522
+ i = 0;
2523
+ }
2524
+ let tile = widgets[i];
2525
+ if (!this.reused.has(tile) &&
2526
+ (pass == 0 ? tile.widget.compare(widget)
2527
+ : tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view))) {
2528
+ widgets.splice(i, 1);
2529
+ if (i < this.index[0])
2530
+ this.index[0]--;
2531
+ this.reused.set(tile, 1 /* Reused.Full */);
2532
+ tile.length = length;
2533
+ tile.flags = (tile.flags & ~(496 /* TileFlag.Widget */ | 1 /* TileFlag.BreakAfter */)) | flags;
2534
+ return tile;
2535
+ }
2536
+ }
2741
2537
  }
2742
- /**
2743
- Returns true when the document was modified or the size of the
2744
- editor, or elements within the editor, changed.
2745
- */
2746
- get geometryChanged() {
2747
- return this.docChanged || (this.flags & (16 /* UpdateFlag.Geometry */ | 2 /* UpdateFlag.Height */)) > 0;
2538
+ reuse(tile) {
2539
+ this.reused.set(tile, 1 /* Reused.Full */);
2540
+ return tile;
2748
2541
  }
2749
- /**
2750
- True when this update indicates a focus change.
2751
- */
2752
- get focusChanged() {
2753
- return (this.flags & 1 /* UpdateFlag.Focus */) > 0;
2542
+ maybeReuse(tile, type = 2 /* Reused.DOM */) {
2543
+ if (this.reused.has(tile))
2544
+ return undefined;
2545
+ this.reused.set(tile, type);
2546
+ return tile.dom;
2754
2547
  }
2755
- /**
2756
- Whether the document changed in this update.
2757
- */
2758
- get docChanged() {
2759
- return !this.changes.empty;
2548
+ }
2549
+ // This class organizes a pass over the document, guided by the array
2550
+ // of replaced ranges. For ranges that haven't changed, it iterates
2551
+ // the old tree and copies its content into the new document. For
2552
+ // changed ranges, it runs a decoration iterator to guide generation
2553
+ // of content.
2554
+ class TileUpdate {
2555
+ constructor(view, old, blockWrappers, decorations, disallowBlockEffectsFor) {
2556
+ this.view = view;
2557
+ this.decorations = decorations;
2558
+ this.disallowBlockEffectsFor = disallowBlockEffectsFor;
2559
+ this.openWidget = false;
2560
+ this.openMarks = 0;
2561
+ this.cache = new TileCache(view);
2562
+ this.text = new TextStream(view.state.doc);
2563
+ this.builder = new TileBuilder(this.cache, new DocTile(view, view.contentDOM), state.RangeSet.iter(blockWrappers));
2564
+ this.cache.reused.set(old, 2 /* Reused.DOM */);
2565
+ this.old = new TilePointer(old);
2566
+ }
2567
+ run(changes, composition) {
2568
+ let compositionContext = composition && this.getCompositionContext(composition.text);
2569
+ for (let posA = 0, posB = 0, i = 0;;) {
2570
+ let next = i < changes.length ? changes[i++] : null;
2571
+ let skipA = next ? next.fromA : this.old.root.length;
2572
+ if (skipA > posA) {
2573
+ let len = skipA - posA;
2574
+ this.preserve(len, !i, !next);
2575
+ posA = skipA;
2576
+ posB += len;
2577
+ }
2578
+ if (!next)
2579
+ break;
2580
+ this.forward(next.fromA, next.toA);
2581
+ // Compositions need to be handled specially, forcing the
2582
+ // focused text node and its parent nodes to remain stable at
2583
+ // that point in the document.
2584
+ if (composition && next.fromA <= composition.range.fromA && next.toA >= composition.range.toA) {
2585
+ this.emit(posB, composition.range.fromB);
2586
+ this.builder.addComposition(composition, compositionContext);
2587
+ this.emit(composition.range.toB, next.toB);
2588
+ }
2589
+ else {
2590
+ this.emit(posB, next.toB);
2591
+ }
2592
+ posB = next.toB;
2593
+ posA = next.toA;
2594
+ }
2595
+ if (this.builder.curLine)
2596
+ this.builder.endLine();
2597
+ return this.builder.root;
2598
+ }
2599
+ preserve(length, incStart, incEnd) {
2600
+ let activeMarks = getMarks(this.old), openMarks = this.openMarks;
2601
+ this.old.advance(length, incEnd ? 1 : -1, {
2602
+ skip: (tile, from, to) => {
2603
+ if (tile.isWidget()) {
2604
+ if (this.openWidget) {
2605
+ this.builder.continueWidget(to - from);
2606
+ }
2607
+ else {
2608
+ let widget = to > 0 || from < tile.length
2609
+ ? WidgetTile.of(tile.widget, this.view, to - from, tile.flags & 496 /* TileFlag.Widget */, this.cache.maybeReuse(tile))
2610
+ : this.cache.reuse(tile);
2611
+ if (widget.flags & 256 /* TileFlag.Block */) {
2612
+ widget.flags &= ~1 /* TileFlag.BreakAfter */;
2613
+ this.builder.addBlockWidget(widget);
2614
+ }
2615
+ else {
2616
+ this.builder.addInlineWidget(widget, activeMarks, openMarks);
2617
+ openMarks = activeMarks.length;
2618
+ }
2619
+ }
2620
+ }
2621
+ else if (tile.isText()) {
2622
+ if (!from && to == tile.length) {
2623
+ this.builder.addText(tile.text, activeMarks, openMarks, this.cache.reuse(tile));
2624
+ }
2625
+ else {
2626
+ this.cache.add(tile);
2627
+ this.builder.addText(tile.text.slice(from, to), activeMarks, openMarks);
2628
+ }
2629
+ openMarks = activeMarks.length;
2630
+ }
2631
+ else if (tile.isLine()) {
2632
+ tile.flags &= ~1 /* TileFlag.BreakAfter */;
2633
+ this.cache.reused.set(tile, 1 /* Reused.Full */);
2634
+ this.builder.addLine(tile);
2635
+ }
2636
+ else if (tile instanceof WidgetBufferTile) {
2637
+ this.cache.add(tile);
2638
+ }
2639
+ else if (tile instanceof MarkTile) {
2640
+ this.builder.addMark(tile, activeMarks, openMarks);
2641
+ this.cache.reused.set(tile, 1 /* Reused.Full */);
2642
+ }
2643
+ else {
2644
+ return false;
2645
+ }
2646
+ this.openWidget = false;
2647
+ },
2648
+ enter: (tile) => {
2649
+ if (tile.isLine()) {
2650
+ this.builder.addLineStart(tile.attrs, this.cache.maybeReuse(tile));
2651
+ }
2652
+ else {
2653
+ this.cache.add(tile);
2654
+ if (tile instanceof MarkTile)
2655
+ activeMarks.unshift(tile.mark);
2656
+ }
2657
+ this.openWidget = false;
2658
+ },
2659
+ leave: (tile) => {
2660
+ if (tile.isLine()) {
2661
+ if (activeMarks.length)
2662
+ activeMarks.length = openMarks = 0;
2663
+ }
2664
+ else if (tile instanceof MarkTile) {
2665
+ activeMarks.shift();
2666
+ openMarks = Math.min(openMarks, activeMarks.length);
2667
+ }
2668
+ },
2669
+ break: () => {
2670
+ this.builder.addBreak();
2671
+ this.openWidget = false;
2672
+ },
2673
+ });
2674
+ this.text.skip(length);
2675
+ }
2676
+ emit(from, to) {
2677
+ let pendingLineAttrs = null;
2678
+ let b = this.builder, markCount = 0;
2679
+ let openEnd = state.RangeSet.spans(this.decorations, from, to, {
2680
+ point: (from, to, deco, active, openStart, index) => {
2681
+ if (deco instanceof PointDecoration) {
2682
+ if (this.disallowBlockEffectsFor[index]) {
2683
+ if (deco.block)
2684
+ throw new RangeError("Block decorations may not be specified via plugins");
2685
+ if (to > this.view.state.doc.lineAt(from).to)
2686
+ throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
2687
+ }
2688
+ markCount = active.length;
2689
+ if (openStart > active.length) {
2690
+ b.continueWidget(to - from);
2691
+ }
2692
+ else {
2693
+ let widget = deco.widget || (deco.block ? NullWidget.block : NullWidget.inline);
2694
+ let flags = widgetFlags(deco);
2695
+ let tile = this.cache.findWidget(widget, to - from, flags) || WidgetTile.of(widget, this.view, to - from, flags);
2696
+ if (deco.block) {
2697
+ if (deco.startSide > 0)
2698
+ b.addLineStartIfNotCovered(pendingLineAttrs);
2699
+ b.addBlockWidget(tile);
2700
+ }
2701
+ else {
2702
+ b.ensureLine(pendingLineAttrs);
2703
+ b.addInlineWidget(tile, active, openStart);
2704
+ }
2705
+ }
2706
+ pendingLineAttrs = null;
2707
+ }
2708
+ else {
2709
+ pendingLineAttrs = addLineDeco(pendingLineAttrs, deco);
2710
+ }
2711
+ if (to > from)
2712
+ this.text.skip(to - from);
2713
+ },
2714
+ span: (from, to, active, openStart) => {
2715
+ for (let pos = from; pos < to;) {
2716
+ let chars = this.text.next(Math.min(512 /* C.Chunk */, to - pos));
2717
+ if (chars == null) { // Line break
2718
+ b.addLineStartIfNotCovered(pendingLineAttrs);
2719
+ b.addBreak();
2720
+ pos++;
2721
+ }
2722
+ else {
2723
+ b.ensureLine(pendingLineAttrs);
2724
+ b.addText(chars, active, openStart);
2725
+ pos += chars.length;
2726
+ }
2727
+ pendingLineAttrs = null;
2728
+ }
2729
+ }
2730
+ });
2731
+ b.addLineStartIfNotCovered(pendingLineAttrs);
2732
+ this.openWidget = openEnd > markCount;
2733
+ this.openMarks = openEnd;
2734
+ }
2735
+ forward(from, to) {
2736
+ this.old.advance(to - from, 1, {
2737
+ skip: (tile, from, to) => {
2738
+ if (tile.isText() || to == tile.length)
2739
+ this.cache.add(tile);
2740
+ },
2741
+ enter: tile => this.cache.add(tile),
2742
+ leave: () => { },
2743
+ break: () => { }
2744
+ });
2760
2745
  }
2761
- /**
2762
- Whether the selection was explicitly set in this update.
2763
- */
2764
- get selectionSet() {
2765
- return this.transactions.some(tr => tr.selection);
2746
+ getCompositionContext(text) {
2747
+ let marks = [], line = null;
2748
+ for (let parent = text.parentNode;; parent = parent.parentNode) {
2749
+ let tile = Tile.get(parent);
2750
+ if (parent == this.view.contentDOM)
2751
+ break;
2752
+ if (tile instanceof MarkTile)
2753
+ marks.push(tile);
2754
+ else if (tile === null || tile === void 0 ? void 0 : tile.isLine())
2755
+ line = tile;
2756
+ else if (parent.nodeName == "DIV" && !line && parent != this.view.contentDOM)
2757
+ line = new LineTile(parent, lineBaseAttrs);
2758
+ else
2759
+ marks.push(MarkTile.of(new MarkDecoration({ tagName: parent.nodeName.toLowerCase(), attributes: getAttrs(parent) }), parent));
2760
+ }
2761
+ return { line: line, marks };
2766
2762
  }
2767
- /**
2768
- @internal
2769
- */
2770
- get empty() { return this.flags == 0 && this.transactions.length == 0; }
2771
2763
  }
2764
+ function hasContent(tile, requireText) {
2765
+ let scan = (tile) => {
2766
+ for (let ch of tile.children)
2767
+ if ((requireText ? ch.isText() : ch.length) || scan(ch))
2768
+ return true;
2769
+ return false;
2770
+ };
2771
+ return scan(tile);
2772
+ }
2773
+ function widgetFlags(deco) {
2774
+ let flags = deco.isReplace ? (deco.startSide < 0 ? 64 /* TileFlag.IncStart */ : 0) | (deco.endSide > 0 ? 128 /* TileFlag.IncEnd */ : 0)
2775
+ : (deco.startSide > 0 ? 32 /* TileFlag.After */ : 16 /* TileFlag.Before */);
2776
+ if (deco.block)
2777
+ flags |= 256 /* TileFlag.Block */;
2778
+ return flags;
2779
+ }
2780
+ const lineBaseAttrs = { class: "cm-line" };
2781
+ function addLineDeco(value, deco) {
2782
+ let attrs = deco.spec.attributes, cls = deco.spec.class;
2783
+ if (!attrs && !cls)
2784
+ return value;
2785
+ if (!value)
2786
+ value = { class: "cm-line" };
2787
+ if (attrs)
2788
+ combineAttrs(attrs, value);
2789
+ if (cls)
2790
+ value.class += " " + cls;
2791
+ return value;
2792
+ }
2793
+ function getMarks(ptr) {
2794
+ let found = [];
2795
+ for (let i = ptr.parents.length; i > 1; i--) {
2796
+ let tile = i == ptr.parents.length ? ptr.tile : ptr.parents[i].tile;
2797
+ if (tile instanceof MarkTile)
2798
+ found.push(tile.mark);
2799
+ }
2800
+ return found;
2801
+ }
2802
+ function freeNode(node) {
2803
+ let tile = Tile.get(node);
2804
+ if (tile)
2805
+ tile.setDOM(node.cloneNode());
2806
+ return node;
2807
+ }
2808
+ class NullWidget extends WidgetType {
2809
+ constructor(tag) {
2810
+ super();
2811
+ this.tag = tag;
2812
+ }
2813
+ eq(other) { return other.tag == this.tag; }
2814
+ toDOM() { return document.createElement(this.tag); }
2815
+ updateDOM(elt) { return elt.nodeName.toLowerCase() == this.tag; }
2816
+ get isHidden() { return true; }
2817
+ }
2818
+ NullWidget.inline = new NullWidget("span");
2819
+ NullWidget.block = new NullWidget("div");
2820
+ const BreakWidget = new class extends WidgetType {
2821
+ toDOM() { return document.createElement("br"); }
2822
+ get isHidden() { return true; }
2823
+ get editable() { return true; }
2824
+ };
2772
2825
 
2773
- class DocView extends ContentView {
2774
- get length() { return this.view.state.doc.length; }
2826
+ class DocView {
2775
2827
  constructor(view) {
2776
- super();
2777
2828
  this.view = view;
2778
2829
  this.decorations = [];
2830
+ this.blockWrappers = [];
2779
2831
  this.dynamicDecorationMap = [false];
2780
2832
  this.domChanged = null;
2781
2833
  this.hasComposition = null;
2782
- this.markedForComposition = new Set;
2783
2834
  this.editContextFormatting = Decoration.none;
2784
2835
  this.lastCompositionAfterCursor = false;
2785
2836
  // Track a minimum width for the editor. When measuring sizes in
@@ -2800,11 +2851,9 @@ class DocView extends ContentView {
2800
2851
  // Used by the resize observer to ignore resizes that we caused
2801
2852
  // ourselves
2802
2853
  this.lastUpdate = Date.now();
2803
- this.setDOM(view.contentDOM);
2804
- this.children = [new LineView];
2805
- this.children[0].setParent(this);
2806
2854
  this.updateDeco();
2807
- this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0, null);
2855
+ this.tile = new DocTile(view, view.contentDOM);
2856
+ this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], null);
2808
2857
  }
2809
2858
  // Update the document view to a given state.
2810
2859
  update(update) {
@@ -2830,7 +2879,6 @@ class DocView extends ContentView {
2830
2879
  let composition = readCompositionAt > -1 ? findCompositionRange(this.view, update.changes, readCompositionAt) : null;
2831
2880
  this.domChanged = null;
2832
2881
  if (this.hasComposition) {
2833
- this.markedForComposition.clear();
2834
2882
  let { from, to } = this.hasComposition;
2835
2883
  changedRanges = new ChangedRange(from, to, update.changes.mapPos(from, -1), update.changes.mapPos(to, 1))
2836
2884
  .addToSet(changedRanges.slice());
@@ -2844,14 +2892,21 @@ class DocView extends ContentView {
2844
2892
  if ((browser.ie || browser.chrome) && !composition && update &&
2845
2893
  update.state.doc.lines != update.startState.doc.lines)
2846
2894
  this.forceSelection = true;
2847
- let prevDeco = this.decorations, deco = this.updateDeco();
2848
- let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
2849
- changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
2850
- if (!(this.flags & 7 /* ViewFlag.Dirty */) && changedRanges.length == 0) {
2895
+ let prevDeco = this.decorations, prevWrappers = this.blockWrappers;
2896
+ this.updateDeco();
2897
+ let decoDiff = findChangedDeco(prevDeco, this.decorations, update.changes);
2898
+ if (decoDiff.length)
2899
+ changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
2900
+ let blockDiff = findChangedWrappers(prevWrappers, this.blockWrappers, update.changes);
2901
+ if (blockDiff.length)
2902
+ changedRanges = ChangedRange.extendWithRanges(changedRanges, blockDiff);
2903
+ if (composition && !changedRanges.some(r => r.fromA <= composition.range.fromA && r.toA >= composition.range.toA))
2904
+ changedRanges = composition.range.addToSet(changedRanges.slice());
2905
+ if ((this.tile.flags & 2 /* TileFlag.Synced */) && changedRanges.length == 0) {
2851
2906
  return false;
2852
2907
  }
2853
2908
  else {
2854
- this.updateInner(changedRanges, update.startState.doc.length, composition);
2909
+ this.updateInner(changedRanges, composition);
2855
2910
  if (update.transactions.length)
2856
2911
  this.lastUpdate = Date.now();
2857
2912
  return true;
@@ -2859,76 +2914,39 @@ class DocView extends ContentView {
2859
2914
  }
2860
2915
  // Used by update and the constructor do perform the actual DOM
2861
2916
  // update
2862
- updateInner(changes, oldLength, composition) {
2917
+ updateInner(changes, composition) {
2863
2918
  this.view.viewState.mustMeasureContent = true;
2864
- this.updateChildren(changes, oldLength, composition);
2865
2919
  let { observer } = this.view;
2866
2920
  observer.ignore(() => {
2921
+ if (composition || changes.length) {
2922
+ let oldTile = this.tile;
2923
+ let builder = new TileUpdate(this.view, oldTile, this.blockWrappers, this.decorations, this.dynamicDecorationMap);
2924
+ this.tile = builder.run(changes, composition);
2925
+ destroyDropped(oldTile, builder.cache.reused);
2926
+ }
2867
2927
  // Lock the height during redrawing, since Chrome sometimes
2868
2928
  // messes with the scroll position during DOM mutation (though
2869
2929
  // no relayout is triggered and I cannot imagine how it can
2870
2930
  // recompute the scroll position without a layout)
2871
- this.dom.style.height = this.view.viewState.contentHeight / this.view.scaleY + "px";
2872
- this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2931
+ this.tile.dom.style.height = this.view.viewState.contentHeight / this.view.scaleY + "px";
2932
+ this.tile.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2873
2933
  // Chrome will sometimes, when DOM mutations occur directly
2874
2934
  // around the selection, get confused and report a different
2875
2935
  // selection from the one it displays (issue #218). This tries
2876
2936
  // to detect that situation.
2877
2937
  let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2878
- this.sync(this.view, track);
2879
- this.flags &= ~7 /* ViewFlag.Dirty */;
2938
+ this.tile.sync();
2880
2939
  if (track && (track.written || observer.selectionRange.focusNode != track.node))
2881
2940
  this.forceSelection = true;
2882
- this.dom.style.height = "";
2941
+ this.tile.dom.style.height = "";
2883
2942
  });
2884
- this.markedForComposition.forEach(cView => cView.flags &= ~8 /* ViewFlag.Composition */);
2885
2943
  let gaps = [];
2886
2944
  if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2887
- for (let child of this.children)
2888
- if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
2945
+ for (let child of this.tile.children)
2946
+ if (child.isWidget() && child.widget instanceof BlockGapWidget)
2889
2947
  gaps.push(child.dom);
2890
2948
  observer.updateGaps(gaps);
2891
2949
  }
2892
- updateChildren(changes, oldLength, composition) {
2893
- let ranges = composition ? composition.range.addToSet(changes.slice()) : changes;
2894
- let cursor = this.childCursor(oldLength);
2895
- for (let i = ranges.length - 1;; i--) {
2896
- let next = i >= 0 ? ranges[i] : null;
2897
- if (!next)
2898
- break;
2899
- let { fromA, toA, fromB, toB } = next, content, breakAtStart, openStart, openEnd;
2900
- if (composition && composition.range.fromB < toB && composition.range.toB > fromB) {
2901
- let before = ContentBuilder.build(this.view.state.doc, fromB, composition.range.fromB, this.decorations, this.dynamicDecorationMap);
2902
- let after = ContentBuilder.build(this.view.state.doc, composition.range.toB, toB, this.decorations, this.dynamicDecorationMap);
2903
- breakAtStart = before.breakAtStart;
2904
- openStart = before.openStart;
2905
- openEnd = after.openEnd;
2906
- let compLine = this.compositionView(composition);
2907
- if (after.breakAtStart) {
2908
- compLine.breakAfter = 1;
2909
- }
2910
- else if (after.content.length &&
2911
- compLine.merge(compLine.length, compLine.length, after.content[0], false, after.openStart, 0)) {
2912
- compLine.breakAfter = after.content[0].breakAfter;
2913
- after.content.shift();
2914
- }
2915
- if (before.content.length &&
2916
- compLine.merge(0, 0, before.content[before.content.length - 1], true, 0, before.openEnd)) {
2917
- before.content.pop();
2918
- }
2919
- content = before.content.concat(compLine).concat(after.content);
2920
- }
2921
- else {
2922
- ({ content, breakAtStart, openStart, openEnd } =
2923
- ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap));
2924
- }
2925
- let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2926
- let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2927
- replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2928
- }
2929
- if (composition)
2930
- this.fixCompositionDOM(composition);
2931
- }
2932
2950
  updateEditContextFormatting(update) {
2933
2951
  this.editContextFormatting = this.editContextFormatting.map(update.changes);
2934
2952
  for (let tr of update.transactions)
@@ -2937,47 +2955,26 @@ class DocView extends ContentView {
2937
2955
  this.editContextFormatting = effect.value;
2938
2956
  }
2939
2957
  }
2940
- compositionView(composition) {
2941
- let cur = new TextView(composition.text.nodeValue);
2942
- cur.flags |= 8 /* ViewFlag.Composition */;
2943
- for (let { deco } of composition.marks)
2944
- cur = new MarkView(deco, [cur], cur.length);
2945
- let line = new LineView;
2946
- line.append(cur, 0);
2947
- return line;
2948
- }
2949
- fixCompositionDOM(composition) {
2950
- let fix = (dom, cView) => {
2951
- cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0);
2952
- this.markedForComposition.add(cView);
2953
- let prev = ContentView.get(dom);
2954
- if (prev && prev != cView)
2955
- prev.dom = null;
2956
- cView.setDOM(dom);
2957
- };
2958
- let pos = this.childPos(composition.range.fromB, 1);
2959
- let cView = this.children[pos.i];
2960
- fix(composition.line, cView);
2961
- for (let i = composition.marks.length - 1; i >= -1; i--) {
2962
- pos = cView.childPos(pos.off, 1);
2963
- cView = cView.children[pos.i];
2964
- fix(i >= 0 ? composition.marks[i].node : composition.text, cView);
2965
- }
2966
- }
2967
2958
  // Sync the DOM selection to this.state.selection
2968
2959
  updateSelection(mustRead = false, fromPointer = false) {
2969
2960
  if (mustRead || !this.view.observer.selectionRange.focusNode)
2970
2961
  this.view.observer.readSelectionRange();
2971
- let activeElt = this.view.root.activeElement, focused = activeElt == this.dom;
2972
- let selectionNotFocus = !focused && !(this.view.state.facet(editable) || this.dom.tabIndex > -1) &&
2973
- hasSelection(this.dom, this.view.observer.selectionRange) && !(activeElt && this.dom.contains(activeElt));
2962
+ let { dom } = this.tile;
2963
+ let activeElt = this.view.root.activeElement, focused = activeElt == dom;
2964
+ let selectionNotFocus = !focused && !(this.view.state.facet(editable) || dom.tabIndex > -1) &&
2965
+ hasSelection(dom, this.view.observer.selectionRange) && !(activeElt && dom.contains(activeElt));
2974
2966
  if (!(focused || fromPointer || selectionNotFocus))
2975
2967
  return;
2976
2968
  let force = this.forceSelection;
2977
2969
  this.forceSelection = false;
2978
- let main = this.view.state.selection.main;
2979
- let anchor = this.moveToLine(this.domAtPos(main.anchor));
2980
- let head = main.empty ? anchor : this.moveToLine(this.domAtPos(main.head));
2970
+ let main = this.view.state.selection.main, anchor, head;
2971
+ if (main.empty) {
2972
+ head = anchor = this.inlineDOMNearPos(main.anchor, main.assoc || 1);
2973
+ }
2974
+ else {
2975
+ head = this.inlineDOMNearPos(main.head, main.head == main.from ? 1 : -1);
2976
+ anchor = this.inlineDOMNearPos(main.anchor, main.anchor == main.from ? 1 : -1);
2977
+ }
2981
2978
  // Always reset on Firefox when next to an uneditable node to
2982
2979
  // avoid invisible cursor bugs (#111)
2983
2980
  if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
@@ -2995,10 +2992,10 @@ class DocView extends ContentView {
2995
2992
  // inside an uneditable node, and not bring it back when we
2996
2993
  // move the cursor to its proper position. This tries to
2997
2994
  // restore the keyboard by cycling focus.
2998
- if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) &&
2999
- inUneditable(domSel.focusNode, this.dom)) {
3000
- this.dom.blur();
3001
- this.dom.focus({ preventScroll: true });
2995
+ if (browser.android && browser.chrome && dom.contains(domSel.focusNode) &&
2996
+ inUneditable(domSel.focusNode, dom)) {
2997
+ dom.blur();
2998
+ dom.focus({ preventScroll: true });
3002
2999
  }
3003
3000
  let rawSel = getSelection(this.view.root);
3004
3001
  if (!rawSel) ;
@@ -3039,8 +3036,8 @@ class DocView extends ContentView {
3039
3036
  rawSel.removeAllRanges();
3040
3037
  rawSel.addRange(range);
3041
3038
  }
3042
- if (selectionNotFocus && this.view.root.activeElement == this.dom) {
3043
- this.dom.blur();
3039
+ if (selectionNotFocus && this.view.root.activeElement == dom) {
3040
+ dom.blur();
3044
3041
  if (activeElt)
3045
3042
  activeElt.focus();
3046
3043
  }
@@ -3066,7 +3063,7 @@ class DocView extends ContentView {
3066
3063
  let { anchorNode, anchorOffset } = view.observer.selectionRange;
3067
3064
  if (!sel || !cursor.empty || !cursor.assoc || !sel.modify)
3068
3065
  return;
3069
- let line = LineView.find(this, cursor.head);
3066
+ let line = this.lineAt(cursor.head);
3070
3067
  if (!line)
3071
3068
  return;
3072
3069
  let lineStart = line.posAtStart;
@@ -3075,7 +3072,7 @@ class DocView extends ContentView {
3075
3072
  let before = this.coordsAt(cursor.head, -1), after = this.coordsAt(cursor.head, 1);
3076
3073
  if (!before || !after || before.bottom > after.top)
3077
3074
  return;
3078
- let dom = this.domAtPos(cursor.head + cursor.assoc);
3075
+ let dom = this.domAtPos(cursor.head + cursor.assoc, cursor.assoc);
3079
3076
  sel.collapse(dom.node, dom.offset);
3080
3077
  sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary");
3081
3078
  // This can go wrong in corner cases like single-character lines,
@@ -3085,143 +3082,213 @@ class DocView extends ContentView {
3085
3082
  if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
3086
3083
  sel.collapse(anchorNode, anchorOffset);
3087
3084
  }
3088
- // If a position is in/near a block widget, move it to a nearby text
3089
- // line, since we don't want the cursor inside a block widget.
3090
- moveToLine(pos) {
3091
- // Block widgets will return positions before/after them, which
3092
- // are thus directly in the document DOM element.
3093
- let dom = this.dom, newPos;
3094
- if (pos.node != dom)
3095
- return pos;
3096
- for (let i = pos.offset; !newPos && i < dom.childNodes.length; i++) {
3097
- let view = ContentView.get(dom.childNodes[i]);
3098
- if (view instanceof LineView)
3099
- newPos = view.domAtPos(0);
3085
+ posFromDOM(node, offset) {
3086
+ let tile = this.tile.nearest(node);
3087
+ if (!tile)
3088
+ return this.tile.dom.compareDocumentPosition(node) & 2 /* PRECEDING */ ? 0 : this.view.state.doc.length;
3089
+ let start = tile.posAtStart;
3090
+ if (tile.isComposite()) {
3091
+ let after;
3092
+ if (node == tile.dom) {
3093
+ after = tile.dom.childNodes[offset];
3094
+ }
3095
+ else {
3096
+ let bias = maxOffset(node) == 0 ? 0 : offset == 0 ? -1 : 1;
3097
+ for (;;) {
3098
+ let parent = node.parentNode;
3099
+ if (parent == tile.dom)
3100
+ break;
3101
+ if (bias == 0 && parent.firstChild != parent.lastChild) {
3102
+ if (node == parent.firstChild)
3103
+ bias = -1;
3104
+ else
3105
+ bias = 1;
3106
+ }
3107
+ node = parent;
3108
+ }
3109
+ if (bias < 0)
3110
+ after = node;
3111
+ else
3112
+ after = node.nextSibling;
3113
+ }
3114
+ if (after == tile.dom.firstChild)
3115
+ return start;
3116
+ while (after && !Tile.get(after))
3117
+ after = after.nextSibling;
3118
+ if (!after)
3119
+ return start + tile.length;
3120
+ for (let i = 0, pos = start;; i++) {
3121
+ let child = tile.children[i];
3122
+ if (child.dom == after)
3123
+ return pos;
3124
+ pos += child.length + child.breakAfter;
3125
+ }
3100
3126
  }
3101
- for (let i = pos.offset - 1; !newPos && i >= 0; i--) {
3102
- let view = ContentView.get(dom.childNodes[i]);
3103
- if (view instanceof LineView)
3104
- newPos = view.domAtPos(view.length);
3127
+ else if (tile.isText()) {
3128
+ return node == tile.dom ? start + offset : start + (offset ? tile.length : 0);
3105
3129
  }
3106
- return newPos ? new DOMPos(newPos.node, newPos.offset, true) : pos;
3107
- }
3108
- nearest(dom) {
3109
- for (let cur = dom; cur;) {
3110
- let domView = ContentView.get(cur);
3111
- if (domView && domView.rootView == this)
3112
- return domView;
3113
- cur = cur.parentNode;
3130
+ else {
3131
+ return start;
3114
3132
  }
3115
- return null;
3116
3133
  }
3117
- posFromDOM(node, offset) {
3118
- let view = this.nearest(node);
3119
- if (!view)
3120
- throw new RangeError("Trying to find position for a DOM position outside of the document");
3121
- return view.localPosFromDOM(node, offset) + view.posAtStart;
3122
- }
3123
- domAtPos(pos) {
3124
- let { i, off } = this.childCursor().findPos(pos, -1);
3125
- for (; i < this.children.length - 1;) {
3126
- let child = this.children[i];
3127
- if (off < child.length || child instanceof LineView)
3128
- break;
3129
- i++;
3130
- off = 0;
3131
- }
3132
- return this.children[i].domAtPos(off);
3134
+ domAtPos(pos, side) {
3135
+ let { tile, offset } = this.tile.resolveBlock(pos, side);
3136
+ if (tile.isWidget())
3137
+ return tile.domPosFor(pos, side);
3138
+ return tile.domIn(offset, side);
3133
3139
  }
3134
- coordsAt(pos, side) {
3135
- let best = null, bestPos = 0;
3136
- for (let off = this.length, i = this.children.length - 1; i >= 0; i--) {
3137
- let child = this.children[i], end = off - child.breakAfter, start = end - child.length;
3138
- if (end < pos)
3139
- break;
3140
- if (start <= pos && (start < pos || child.covers(-1)) && (end > pos || child.covers(1)) &&
3141
- (!best || child instanceof LineView && !(best instanceof LineView && side >= 0))) {
3142
- best = child;
3143
- bestPos = start;
3140
+ inlineDOMNearPos(pos, side) {
3141
+ let before, beforeOff = -1, beforeBad = false;
3142
+ let after, afterOff = -1, afterBad = false;
3143
+ this.tile.blockTiles((tile, off) => {
3144
+ if (tile.isWidget()) {
3145
+ if ((tile.flags & 32 /* TileFlag.After */) && before)
3146
+ return true;
3147
+ if (tile.flags & 16 /* TileFlag.Before */)
3148
+ beforeBad = true;
3144
3149
  }
3145
- else if (best && start == pos && end == pos && child instanceof BlockWidgetView && Math.abs(side) < 2) {
3146
- if (child.deco.startSide < 0)
3147
- break;
3148
- else if (i)
3149
- best = null;
3150
+ else {
3151
+ let end = off + tile.length;
3152
+ if (off <= pos) {
3153
+ before = tile;
3154
+ beforeOff = pos - off;
3155
+ beforeBad = end < pos;
3156
+ }
3157
+ if (end >= pos && !after) {
3158
+ after = tile;
3159
+ afterOff = pos - off;
3160
+ afterBad = off > pos;
3161
+ }
3162
+ if (off > pos && after)
3163
+ return true;
3150
3164
  }
3151
- off = start;
3165
+ });
3166
+ if (!before && !after)
3167
+ return this.domAtPos(pos, side);
3168
+ if (beforeBad && after)
3169
+ before = null;
3170
+ else if (afterBad && before)
3171
+ after = null;
3172
+ return before && side < 0 || !after ? before.domIn(beforeOff, side) : after.domIn(afterOff, side);
3173
+ }
3174
+ coordsAt(pos, side) {
3175
+ let { tile, offset } = this.tile.resolveBlock(pos, side);
3176
+ if (tile.isWidget()) {
3177
+ if (tile.widget instanceof BlockGapWidget)
3178
+ return null;
3179
+ return tile.coordsInWidget(offset, side, true);
3152
3180
  }
3153
- return best ? best.coordsAt(pos - bestPos, side) : null;
3181
+ return tile.coordsIn(offset, side);
3182
+ }
3183
+ lineAt(pos) {
3184
+ let { tile } = this.tile.resolveBlock(pos, 1);
3185
+ return tile.isLine() ? tile : null;
3154
3186
  }
3155
3187
  coordsForChar(pos) {
3156
- let { i, off } = this.childPos(pos, 1), child = this.children[i];
3157
- if (!(child instanceof LineView))
3188
+ let { tile, offset } = this.tile.resolveBlock(pos, 1);
3189
+ if (!tile.isLine())
3158
3190
  return null;
3159
- while (child.children.length) {
3160
- let { i, off: childOff } = child.childPos(off, 1);
3161
- for (;; i++) {
3162
- if (i == child.children.length)
3191
+ function scan(tile, offset) {
3192
+ if (tile.isComposite()) {
3193
+ for (let ch of tile.children) {
3194
+ if (ch.length >= offset) {
3195
+ let found = scan(ch, offset);
3196
+ if (found)
3197
+ return found;
3198
+ }
3199
+ offset -= ch.length;
3200
+ if (offset < 0)
3201
+ break;
3202
+ }
3203
+ }
3204
+ else if (tile.isText() && offset < tile.length) {
3205
+ let end = state.findClusterBreak(tile.text, offset);
3206
+ if (end == offset)
3163
3207
  return null;
3164
- if ((child = child.children[i]).length)
3165
- break;
3208
+ let rects = textRange(tile.dom, offset, end).getClientRects();
3209
+ for (let i = 0; i < rects.length; i++) {
3210
+ let rect = rects[i];
3211
+ if (i == rects.length - 1 || rect.top < rect.bottom && rect.left < rect.right)
3212
+ return rect;
3213
+ }
3166
3214
  }
3167
- off = childOff;
3168
- }
3169
- if (!(child instanceof TextView))
3170
3215
  return null;
3171
- let end = state.findClusterBreak(child.text, off);
3172
- if (end == off)
3173
- return null;
3174
- let rects = textRange(child.dom, off, end).getClientRects();
3175
- for (let i = 0; i < rects.length; i++) {
3176
- let rect = rects[i];
3177
- if (i == rects.length - 1 || rect.top < rect.bottom && rect.left < rect.right)
3178
- return rect;
3179
3216
  }
3180
- return null;
3217
+ return scan(tile, offset);
3181
3218
  }
3182
3219
  measureVisibleLineHeights(viewport) {
3183
3220
  let result = [], { from, to } = viewport;
3184
3221
  let contentWidth = this.view.contentDOM.clientWidth;
3185
3222
  let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
3186
3223
  let widest = -1, ltr = this.view.textDirection == exports.Direction.LTR;
3187
- for (let pos = 0, i = 0; i < this.children.length; i++) {
3188
- let child = this.children[i], end = pos + child.length;
3189
- if (end > to)
3190
- break;
3191
- if (pos >= from) {
3192
- let childRect = child.dom.getBoundingClientRect();
3193
- result.push(childRect.height);
3194
- if (isWider) {
3195
- let last = child.dom.lastChild;
3196
- let rects = last ? clientRectsFor(last) : [];
3197
- if (rects.length) {
3198
- let rect = rects[rects.length - 1];
3199
- let width = ltr ? rect.right - childRect.left : childRect.right - rect.left;
3200
- if (width > widest) {
3201
- widest = width;
3202
- this.minWidth = contentWidth;
3203
- this.minWidthFrom = pos;
3204
- this.minWidthTo = end;
3224
+ let spaceAbove = 0;
3225
+ let scan = (tile, pos, measureBounds) => {
3226
+ for (let i = 0; i < tile.children.length; i++) {
3227
+ if (pos > to)
3228
+ break;
3229
+ let child = tile.children[i], end = pos + child.length;
3230
+ if (child instanceof BlockWrapperTile) {
3231
+ if (end > from)
3232
+ scan(child, pos, child.dom.getBoundingClientRect());
3233
+ }
3234
+ else if (pos >= from) {
3235
+ let childRect = child.dom.getBoundingClientRect(), { height } = childRect;
3236
+ if (measureBounds && !i)
3237
+ spaceAbove += childRect.top - measureBounds.top;
3238
+ if (spaceAbove > 0)
3239
+ result.push(-spaceAbove);
3240
+ result.push(height + spaceAbove);
3241
+ spaceAbove = 0;
3242
+ if (measureBounds && i == tile.children.length - 1)
3243
+ spaceAbove += measureBounds.bottom - childRect.bottom;
3244
+ if (isWider) {
3245
+ let last = child.dom.lastChild;
3246
+ let rects = last ? clientRectsFor(last) : [];
3247
+ if (rects.length) {
3248
+ let rect = rects[rects.length - 1];
3249
+ let width = ltr ? rect.right - childRect.left : childRect.right - rect.left;
3250
+ if (width > widest) {
3251
+ widest = width;
3252
+ this.minWidth = contentWidth;
3253
+ this.minWidthFrom = pos;
3254
+ this.minWidthTo = end;
3255
+ }
3205
3256
  }
3206
3257
  }
3207
3258
  }
3259
+ pos = end + child.breakAfter;
3208
3260
  }
3209
- pos = end + child.breakAfter;
3210
- }
3261
+ };
3262
+ scan(this.tile, 0, null);
3211
3263
  return result;
3212
3264
  }
3213
3265
  textDirectionAt(pos) {
3214
- let { i } = this.childPos(pos, 1);
3215
- return getComputedStyle(this.children[i].dom).direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
3266
+ let { tile } = this.tile.resolveBlock(pos, 1);
3267
+ return getComputedStyle(tile.dom).direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
3216
3268
  }
3217
3269
  measureTextSize() {
3218
- for (let child of this.children) {
3219
- if (child instanceof LineView) {
3220
- let measure = child.measureTextSize();
3221
- if (measure)
3222
- return measure;
3270
+ let lineMeasure = this.tile.blockTiles(tile => {
3271
+ if (tile.isLine() && tile.children.length && tile.length <= 20) {
3272
+ let totalWidth = 0, textHeight;
3273
+ for (let child of tile.children) {
3274
+ if (!child.isText() || /[^ -~]/.test(child.text))
3275
+ return undefined;
3276
+ let rects = clientRectsFor(child.dom);
3277
+ if (rects.length != 1)
3278
+ return undefined;
3279
+ totalWidth += rects[0].width;
3280
+ textHeight = rects[0].height;
3281
+ }
3282
+ if (totalWidth)
3283
+ return {
3284
+ lineHeight: tile.dom.getBoundingClientRect().height,
3285
+ charWidth: totalWidth / tile.length,
3286
+ textHeight
3287
+ };
3223
3288
  }
3224
- }
3289
+ });
3290
+ if (lineMeasure)
3291
+ return lineMeasure;
3225
3292
  // If no workable line exists, force a layout of a measurable element
3226
3293
  let dummy = document.createElement("div"), lineHeight, charWidth, textHeight;
3227
3294
  dummy.className = "cm-line";
@@ -3229,7 +3296,7 @@ class DocView extends ContentView {
3229
3296
  dummy.style.position = "absolute";
3230
3297
  dummy.textContent = "abc def ghi jkl mno pqr stu";
3231
3298
  this.view.observer.ignore(() => {
3232
- this.dom.appendChild(dummy);
3299
+ this.tile.dom.appendChild(dummy);
3233
3300
  let rect = clientRectsFor(dummy.firstChild)[0];
3234
3301
  lineHeight = dummy.getBoundingClientRect().height;
3235
3302
  charWidth = rect ? rect.width / 27 : 7;
@@ -3238,20 +3305,11 @@ class DocView extends ContentView {
3238
3305
  });
3239
3306
  return { lineHeight, charWidth, textHeight };
3240
3307
  }
3241
- childCursor(pos = this.length) {
3242
- // Move back to start of last element when possible, so that
3243
- // `ChildCursor.findPos` doesn't have to deal with the edge case
3244
- // of being after the last element.
3245
- let i = this.children.length;
3246
- if (i)
3247
- pos -= this.children[--i].length;
3248
- return new ChildCursor(this.children, pos, i);
3249
- }
3250
3308
  computeBlockGapDeco() {
3251
3309
  let deco = [], vs = this.view.viewState;
3252
3310
  for (let pos = 0, i = 0;; i++) {
3253
3311
  let next = i == vs.viewports.length ? null : vs.viewports[i];
3254
- let end = next ? next.from - 1 : this.length;
3312
+ let end = next ? next.from - 1 : this.view.state.doc.length;
3255
3313
  if (end > pos) {
3256
3314
  let height = (vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top) / this.view.scaleY;
3257
3315
  deco.push(Decoration.replace({
@@ -3291,7 +3349,7 @@ class DocView extends ContentView {
3291
3349
  ];
3292
3350
  while (i < this.decorations.length)
3293
3351
  this.dynamicDecorationMap[i++] = false;
3294
- return this.decorations;
3352
+ this.blockWrappers = this.view.state.facet(blockWrappers).map(v => typeof v == "function" ? v(this.view) : v);
3295
3353
  }
3296
3354
  scrollIntoView(target) {
3297
3355
  if (target.isSnapshot) {
@@ -3325,11 +3383,20 @@ class DocView extends ContentView {
3325
3383
  scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == exports.Direction.LTR);
3326
3384
  }
3327
3385
  lineHasWidget(pos) {
3328
- let { i } = this.childCursor().findPos(pos);
3329
- if (i == this.children.length)
3330
- return false;
3331
- let scan = (child) => child instanceof WidgetView || child.children.some(scan);
3332
- return scan(this.children[i]);
3386
+ let scan = (child) => child.isWidget() || child.children.some(scan);
3387
+ return scan(this.tile.resolveBlock(pos, 1).tile);
3388
+ }
3389
+ destroy() {
3390
+ destroyDropped(this.tile);
3391
+ }
3392
+ }
3393
+ function destroyDropped(tile, reused) {
3394
+ let r = reused === null || reused === void 0 ? void 0 : reused.get(tile);
3395
+ if (r != 1 /* Reused.Full */) {
3396
+ if (r == null)
3397
+ tile.destroy();
3398
+ for (let ch of tile.children)
3399
+ destroyDropped(ch, reused);
3333
3400
  }
3334
3401
  }
3335
3402
  function betweenUneditable(pos) {
@@ -3345,13 +3412,13 @@ function findCompositionNode(view, headPos) {
3345
3412
  let textAfter = textNodeAfter(sel.focusNode, sel.focusOffset);
3346
3413
  let textNode = textBefore || textAfter;
3347
3414
  if (textAfter && textBefore && textAfter.node != textBefore.node) {
3348
- let descAfter = ContentView.get(textAfter.node);
3349
- if (!descAfter || descAfter instanceof TextView && descAfter.text != textAfter.node.nodeValue) {
3415
+ let tileAfter = Tile.get(textAfter.node);
3416
+ if (!tileAfter || tileAfter.isText() && tileAfter.text != textAfter.node.nodeValue) {
3350
3417
  textNode = textAfter;
3351
3418
  }
3352
3419
  else if (view.docView.lastCompositionAfterCursor) {
3353
- let descBefore = ContentView.get(textBefore.node);
3354
- if (!(!descBefore || descBefore instanceof TextView && descBefore.text != textBefore.node.nodeValue))
3420
+ let tileBefore = Tile.get(textBefore.node);
3421
+ if (!(!tileBefore || tileBefore.isText() && tileBefore.text != textBefore.node.nodeValue))
3355
3422
  textNode = textAfter;
3356
3423
  }
3357
3424
  }
@@ -3372,23 +3439,7 @@ function findCompositionRange(view, changes, headPos) {
3372
3439
  if (view.state.doc.sliceString(found.from, found.to) != text)
3373
3440
  return null;
3374
3441
  let inv = changes.invertedDesc;
3375
- let range = new ChangedRange(inv.mapPos(from), inv.mapPos(to), from, to);
3376
- let marks = [];
3377
- for (let parent = textNode.parentNode;; parent = parent.parentNode) {
3378
- let parentView = ContentView.get(parent);
3379
- if (parentView instanceof MarkView)
3380
- marks.push({ node: parent, deco: parentView.mark });
3381
- else if (parentView instanceof LineView || parent.nodeName == "DIV" && parent.parentNode == view.contentDOM)
3382
- return { range, text: textNode, marks, line: parent };
3383
- else if (parent != view.contentDOM)
3384
- marks.push({ node: parent, deco: new MarkDecoration({
3385
- inclusive: true,
3386
- attributes: getAttrs(parent),
3387
- tagName: parent.tagName.toLowerCase()
3388
- }) });
3389
- else
3390
- return null;
3391
- }
3442
+ return { range: new ChangedRange(inv.mapPos(from), inv.mapPos(to), from, to), text: textNode };
3392
3443
  }
3393
3444
  function nextToUneditable(node, offset) {
3394
3445
  if (node.nodeType != 1)
@@ -3409,6 +3460,19 @@ function findChangedDeco(a, b, diff) {
3409
3460
  state.RangeSet.compare(a, b, diff, comp);
3410
3461
  return comp.changes;
3411
3462
  }
3463
+ class WrapperComparator {
3464
+ constructor() {
3465
+ this.changes = [];
3466
+ }
3467
+ compareRange(from, to) { addRange(from, to, this.changes); }
3468
+ comparePoint() { }
3469
+ boundChange(pos) { addRange(pos, pos, this.changes); }
3470
+ }
3471
+ function findChangedWrappers(a, b, diff) {
3472
+ let comp = new WrapperComparator;
3473
+ state.RangeSet.compare(a, b, diff, comp);
3474
+ return comp.changes;
3475
+ }
3412
3476
  function inUneditable(node, inside) {
3413
3477
  for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
3414
3478
  if (cur.nodeType == 1 && cur.contentEditable == 'false') {
@@ -3426,6 +3490,26 @@ function touchesComposition(changes, composition) {
3426
3490
  });
3427
3491
  return touched;
3428
3492
  }
3493
+ class BlockGapWidget extends WidgetType {
3494
+ constructor(height) {
3495
+ super();
3496
+ this.height = height;
3497
+ }
3498
+ toDOM() {
3499
+ let elt = document.createElement("div");
3500
+ elt.className = "cm-gap";
3501
+ this.updateDOM(elt);
3502
+ return elt;
3503
+ }
3504
+ eq(other) { return other.height == this.height; }
3505
+ updateDOM(elt) {
3506
+ elt.style.height = this.height + "px";
3507
+ return true;
3508
+ }
3509
+ get editable() { return true; }
3510
+ get estimatedHeight() { return this.height; }
3511
+ ignoreEvent() { return false; }
3512
+ }
3429
3513
 
3430
3514
  function groupAt(state$1, pos, bias = 1) {
3431
3515
  let categorize = state$1.charCategorizer(pos);
@@ -3561,7 +3645,7 @@ function domPosInText(node, x, y) {
3561
3645
  return { node, offset: closestOffset > -1 ? closestOffset : generalSide > 0 ? node.nodeValue.length : 0 };
3562
3646
  }
3563
3647
  function posAtCoords(view, coords, precise, bias = -1) {
3564
- var _a, _b;
3648
+ var _a;
3565
3649
  let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
3566
3650
  let block, { docHeight } = view.viewState;
3567
3651
  let { x, y } = coords, yOffset = y - docTop;
@@ -3611,7 +3695,7 @@ function posAtCoords(view, coords, precise, bias = -1) {
3611
3695
  // There's visible editor content under the point, so we can try
3612
3696
  // using caret(Position|Range)FromPoint as a shortcut
3613
3697
  let node, offset = -1;
3614
- if (element && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
3698
+ if (element && !((_a = view.docView.tile.nearest(element)) === null || _a === void 0 ? void 0 : _a.isWidget())) {
3615
3699
  if (doc.caretPositionFromPoint) {
3616
3700
  let pos = doc.caretPositionFromPoint(x, y);
3617
3701
  if (pos)
@@ -3633,23 +3717,21 @@ function posAtCoords(view, coords, precise, bias = -1) {
3633
3717
  offset = Math.min(maxOffset(node), offset);
3634
3718
  }
3635
3719
  // No luck, do our own (potentially expensive) search
3636
- if (!node || !view.docView.dom.contains(node)) {
3637
- let line = LineView.find(view.docView, lineStart);
3720
+ if (!node || !view.contentDOM.contains(node)) {
3721
+ let line = view.docView.lineAt(lineStart);
3638
3722
  if (!line)
3639
3723
  return yOffset > block.top + block.height / 2 ? block.to : block.from;
3640
3724
  ({ node, offset } = domPosAtCoords(line.dom, x, y));
3641
3725
  }
3642
- let nearest = view.docView.nearest(node);
3726
+ let nearest = view.docView.tile.nearest(node);
3643
3727
  if (!nearest)
3644
3728
  return null;
3645
- if (nearest.isWidget && ((_b = nearest.dom) === null || _b === void 0 ? void 0 : _b.nodeType) == 1) {
3729
+ if (nearest.isWidget()) {
3646
3730
  let rect = nearest.dom.getBoundingClientRect();
3647
3731
  return coords.y < rect.top || coords.y <= rect.bottom && coords.x <= (rect.left + rect.right) / 2
3648
3732
  ? nearest.posAtStart : nearest.posAtEnd;
3649
3733
  }
3650
- else {
3651
- return nearest.localPosFromDOM(node, offset) + nearest.posAtStart;
3652
- }
3734
+ return view.docView.posFromDOM(node, offset);
3653
3735
  }
3654
3736
  function posAtCoordsImprecise(view, contentRect, block, x, y) {
3655
3737
  let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth);
@@ -3865,13 +3947,16 @@ class DOMReader {
3865
3947
  this.findPointBefore(parent, cur);
3866
3948
  let oldLen = this.text.length;
3867
3949
  this.readNode(cur);
3868
- let next = cur.nextSibling;
3869
- if (next == end)
3950
+ let tile = Tile.get(cur), next = cur.nextSibling;
3951
+ if (next == end) {
3952
+ if (tile === null || tile === void 0 ? void 0 : tile.breakAfter)
3953
+ this.lineBreak();
3870
3954
  break;
3871
- let view = ContentView.get(cur), nextView = ContentView.get(next);
3872
- if ((view && nextView ? view.breakAfter :
3873
- (view ? view.breakAfter : isBlockElement(cur)) ||
3874
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen)) &&
3955
+ }
3956
+ let nextTile = Tile.get(next);
3957
+ if ((tile && nextTile ? tile.breakAfter :
3958
+ (tile ? tile.breakAfter : isBlockElement(cur)) ||
3959
+ (isBlockElement(next) && (cur.nodeName != "BR" || (tile === null || tile === void 0 ? void 0 : tile.isWidget())) && this.text.length > oldLen)) &&
3875
3960
  !isEmptyToEnd(next, end))
3876
3961
  this.lineBreak();
3877
3962
  cur = next;
@@ -3906,10 +3991,8 @@ class DOMReader {
3906
3991
  }
3907
3992
  }
3908
3993
  readNode(node) {
3909
- if (node.cmIgnore)
3910
- return;
3911
- let view = ContentView.get(node);
3912
- let fromView = view && view.overrideDOMText;
3994
+ let tile = Tile.get(node);
3995
+ let fromView = tile && tile.overrideDOMText;
3913
3996
  if (fromView != null) {
3914
3997
  this.findPointInside(node, fromView.length);
3915
3998
  for (let i = fromView.iter(); !i.next().done;) {
@@ -3956,8 +4039,8 @@ function isEmptyToEnd(node, end) {
3956
4039
  for (;; node = node.nextSibling) {
3957
4040
  if (node == end || !node)
3958
4041
  break;
3959
- let view = ContentView.get(node);
3960
- if (!((view === null || view === void 0 ? void 0 : view.isWidget) || node.cmIgnore))
4042
+ let view = Tile.get(node);
4043
+ if (!(view === null || view === void 0 ? void 0 : view.isWidget()))
3961
4044
  return false;
3962
4045
  if (view)
3963
4046
  (widgets || (widgets = [])).push(view);
@@ -3989,7 +4072,7 @@ class DOMChange {
3989
4072
  // Ignore changes when the editor is read-only
3990
4073
  this.newSel = null;
3991
4074
  }
3992
- else if (start > -1 && (this.bounds = view.docView.domBoundsAround(start, end, 0))) {
4075
+ else if (start > -1 && (this.bounds = domBoundsAround(view.docView.tile, start, end, 0))) {
3993
4076
  let selPoints = iHead || iAnchor ? [] : selectionPoints(view);
3994
4077
  let reader = new DOMReader(selPoints, view.state);
3995
4078
  reader.readRange(this.bounds.startDOM, this.bounds.endDOM);
@@ -4027,6 +4110,36 @@ class DOMChange {
4027
4110
  }
4028
4111
  }
4029
4112
  }
4113
+ function domBoundsAround(tile, from, to, offset) {
4114
+ if (tile.isComposite()) {
4115
+ let fromI = -1, fromStart = -1, toI = -1, toEnd = -1;
4116
+ for (let i = 0, pos = offset, prevEnd = offset; i < tile.children.length; i++) {
4117
+ let child = tile.children[i], end = pos + child.length;
4118
+ if (pos < from && end > to)
4119
+ return domBoundsAround(child, from, to, pos);
4120
+ if (end >= from && fromI == -1) {
4121
+ fromI = i;
4122
+ fromStart = pos;
4123
+ }
4124
+ if (pos > to && child.dom.parentNode == tile.dom) {
4125
+ toI = i;
4126
+ toEnd = prevEnd;
4127
+ break;
4128
+ }
4129
+ prevEnd = end;
4130
+ pos = end + child.breakAfter;
4131
+ }
4132
+ return { from: fromStart, to: toEnd < 0 ? offset + tile.length : toEnd,
4133
+ startDOM: (fromI ? tile.children[fromI - 1].dom.nextSibling : null) || tile.dom.firstChild,
4134
+ endDOM: toI < tile.children.length && toI >= 0 ? tile.children[toI].dom : null };
4135
+ }
4136
+ else if (tile.isText()) {
4137
+ return { from: offset, to: offset + tile.length, startDOM: tile.dom, endDOM: tile.dom.nextSibling };
4138
+ }
4139
+ else {
4140
+ return null;
4141
+ }
4142
+ }
4030
4143
  function applyDOMChange(view, domChange) {
4031
4144
  let change;
4032
4145
  let { newSel } = domChange, sel = view.state.selection.main;
@@ -4650,8 +4763,8 @@ function eventBelongsToEditor(view, event) {
4650
4763
  return true;
4651
4764
  if (event.defaultPrevented)
4652
4765
  return false;
4653
- for (let node = event.target, cView; node != view.contentDOM; node = node.parentNode)
4654
- if (!node || node.nodeType == 11 || ((cView = ContentView.get(node)) && cView.ignoreEvent(event)))
4766
+ for (let node = event.target, tile; node != view.contentDOM; node = node.parentNode)
4767
+ if (!node || node.nodeType == 11 || ((tile = Tile.get(node)) && tile.isWidget() && tile.widget.ignoreEvent(event)))
4655
4768
  return false;
4656
4769
  return true;
4657
4770
  }
@@ -4770,7 +4883,7 @@ function rangeForClick(view, pos, bias, type) {
4770
4883
  return groupAt(view.state, pos, bias);
4771
4884
  }
4772
4885
  else { // Triple click
4773
- let visual = LineView.find(view.docView, pos), line = view.state.doc.lineAt(visual ? visual.posAtEnd : pos);
4886
+ let visual = view.docView.lineAt(pos), line = view.state.doc.lineAt(visual ? visual.posAtEnd : pos);
4774
4887
  let from = visual ? visual.posAtStart : line.from, to = visual ? visual.posAtEnd : line.to;
4775
4888
  if (to < view.state.doc.length && to == line.to)
4776
4889
  to++;
@@ -4782,7 +4895,7 @@ let inside = (x, y, rect) => y >= rect.top && y <= rect.bottom && x >= rect.left
4782
4895
  // given position, whether they are related to the element before or
4783
4896
  // the element after the position.
4784
4897
  function findPositionSide(view, pos, x, y) {
4785
- let line = LineView.find(view.docView, pos);
4898
+ let line = view.docView.lineAt(pos);
4786
4899
  if (!line)
4787
4900
  return 1;
4788
4901
  let off = pos - line.posAtStart;
@@ -4792,10 +4905,10 @@ function findPositionSide(view, pos, x, y) {
4792
4905
  if (off == line.length)
4793
4906
  return -1;
4794
4907
  // Positions on top of an element point at that element
4795
- let before = line.coordsAt(off, -1);
4908
+ let before = line.coordsIn(off, -1);
4796
4909
  if (before && inside(x, y, before))
4797
4910
  return -1;
4798
- let after = line.coordsAt(off, 1);
4911
+ let after = line.coordsIn(off, 1);
4799
4912
  if (after && inside(x, y, after))
4800
4913
  return 1;
4801
4914
  // This is probably a line wrap point. Pick before if the point is
@@ -4857,9 +4970,9 @@ function removeRangeAround(sel, pos) {
4857
4970
  handlers.dragstart = (view, event) => {
4858
4971
  let { selection: { main: range } } = view.state;
4859
4972
  if (event.target.draggable) {
4860
- let cView = view.docView.nearest(event.target);
4861
- if (cView && cView.isWidget) {
4862
- let from = cView.posAtStart, to = from + cView.length;
4973
+ let tile = view.docView.tile.nearest(event.target);
4974
+ if (tile && tile.isWidget()) {
4975
+ let from = tile.posAtStart, to = from + tile.length;
4863
4976
  if (from >= range.to || to <= range.from)
4864
4977
  range = state.EditorSelection.range(from, to);
4865
4978
  }
@@ -5211,7 +5324,7 @@ class HeightOracle {
5211
5324
  }
5212
5325
  }
5213
5326
  // This object is used by `updateHeight` to make DOM measurements
5214
- // arrive at the right nides. The `heights` array is a sequence of
5327
+ // arrive at the right nodes. The `heights` array is a sequence of
5215
5328
  // block heights, starting from position `from`.
5216
5329
  class MeasuredHeights {
5217
5330
  constructor(from, heights) {
@@ -5250,7 +5363,7 @@ class BlockInfo {
5250
5363
  /**
5251
5364
  @internal Weird packed field that holds an array of children
5252
5365
  for composite blocks, a decoration for block widgets, and a
5253
- number indicating the amount of widget-create line breaks for
5366
+ number indicating the amount of widget-created line breaks for
5254
5367
  text blocks.
5255
5368
  */
5256
5369
  _content) {
@@ -5354,7 +5467,7 @@ class HeightMap {
5354
5467
  }
5355
5468
  return me.updateHeight(oracle, 0);
5356
5469
  }
5357
- static empty() { return new HeightMapText(0, 0); }
5470
+ static empty() { return new HeightMapText(0, 0, 0); }
5358
5471
  // nodes uses null values to indicate the position of line breaks.
5359
5472
  // There are never line breaks at the start or end of the array, or
5360
5473
  // two line breaks next to each other, and the array isn't allowed
@@ -5418,45 +5531,64 @@ function replace(old, val) {
5418
5531
  return val;
5419
5532
  }
5420
5533
  HeightMap.prototype.size = 1;
5534
+ const SpaceDeco = Decoration.replace({});
5421
5535
  class HeightMapBlock extends HeightMap {
5422
5536
  constructor(length, height, deco) {
5423
5537
  super(length, height);
5424
5538
  this.deco = deco;
5539
+ this.spaceAbove = 0;
5425
5540
  }
5426
- blockAt(_height, _oracle, top, offset) {
5427
- return new BlockInfo(offset, this.length, top, this.height, this.deco || 0);
5541
+ mainBlock(top, offset) {
5542
+ return new BlockInfo(offset, this.length, top + this.spaceAbove, this.height - this.spaceAbove, this.deco || 0);
5543
+ }
5544
+ blockAt(height, _oracle, top, offset) {
5545
+ return this.spaceAbove && height < top + this.spaceAbove ? new BlockInfo(offset, 0, top, this.spaceAbove, SpaceDeco)
5546
+ : this.mainBlock(top, offset);
5428
5547
  }
5429
5548
  lineAt(_value, _type, oracle, top, offset) {
5430
- return this.blockAt(0, oracle, top, offset);
5549
+ let main = this.mainBlock(top, offset);
5550
+ return this.spaceAbove ? this.blockAt(0, oracle, top, offset).join(main) : main;
5431
5551
  }
5432
5552
  forEachLine(from, to, oracle, top, offset, f) {
5433
5553
  if (from <= offset + this.length && to >= offset)
5434
- f(this.blockAt(0, oracle, top, offset));
5554
+ f(this.lineAt(0, QueryType.ByPos, oracle, top, offset));
5555
+ }
5556
+ setMeasuredHeight(measured) {
5557
+ let next = measured.heights[measured.index++];
5558
+ if (next < 0) {
5559
+ this.spaceAbove = -next;
5560
+ next = measured.heights[measured.index++];
5561
+ }
5562
+ else {
5563
+ this.spaceAbove = 0;
5564
+ }
5565
+ this.setHeight(next);
5435
5566
  }
5436
5567
  updateHeight(oracle, offset = 0, _force = false, measured) {
5437
5568
  if (measured && measured.from <= offset && measured.more)
5438
- this.setHeight(measured.heights[measured.index++]);
5569
+ this.setMeasuredHeight(measured);
5439
5570
  this.outdated = false;
5440
5571
  return this;
5441
5572
  }
5442
5573
  toString() { return `block(${this.length})`; }
5443
5574
  }
5444
5575
  class HeightMapText extends HeightMapBlock {
5445
- constructor(length, height) {
5576
+ constructor(length, height, above) {
5446
5577
  super(length, height, null);
5447
5578
  this.collapsed = 0; // Amount of collapsed content in the line
5448
5579
  this.widgetHeight = 0; // Maximum inline widget height
5449
5580
  this.breaks = 0; // Number of widget-introduced line breaks on the line
5581
+ this.spaceAbove = above;
5450
5582
  }
5451
- blockAt(_height, _oracle, top, offset) {
5452
- return new BlockInfo(offset, this.length, top, this.height, this.breaks);
5583
+ mainBlock(top, offset) {
5584
+ return new BlockInfo(offset, this.length, top + this.spaceAbove, this.height - this.spaceAbove, this.breaks);
5453
5585
  }
5454
5586
  replace(_from, _to, nodes) {
5455
5587
  let node = nodes[0];
5456
5588
  if (nodes.length == 1 && (node instanceof HeightMapText || node instanceof HeightMapGap && (node.flags & 4 /* Flag.SingleLine */)) &&
5457
5589
  Math.abs(this.length - node.length) < 10) {
5458
5590
  if (node instanceof HeightMapGap)
5459
- node = new HeightMapText(node.length, this.height);
5591
+ node = new HeightMapText(node.length, this.height, this.spaceAbove);
5460
5592
  else
5461
5593
  node.height = this.height;
5462
5594
  if (!this.outdated)
@@ -5468,11 +5600,14 @@ class HeightMapText extends HeightMapBlock {
5468
5600
  }
5469
5601
  }
5470
5602
  updateHeight(oracle, offset = 0, force = false, measured) {
5471
- if (measured && measured.from <= offset && measured.more)
5472
- this.setHeight(measured.heights[measured.index++]);
5473
- else if (force || this.outdated)
5603
+ if (measured && measured.from <= offset && measured.more) {
5604
+ this.setMeasuredHeight(measured);
5605
+ }
5606
+ else if (force || this.outdated) {
5607
+ this.spaceAbove = 0;
5474
5608
  this.setHeight(Math.max(this.widgetHeight, oracle.heightForLine(this.length - this.collapsed)) +
5475
5609
  this.breaks * oracle.lineHeight);
5610
+ }
5476
5611
  this.outdated = false;
5477
5612
  return this;
5478
5613
  }
@@ -5579,12 +5714,16 @@ class HeightMapGap extends HeightMap {
5579
5714
  let len = oracle.doc.lineAt(pos).length;
5580
5715
  if (nodes.length)
5581
5716
  nodes.push(null);
5582
- let height = measured.heights[measured.index++];
5717
+ let height = measured.heights[measured.index++], above = 0;
5718
+ if (height < 0) {
5719
+ above = -height;
5720
+ height = measured.heights[measured.index++];
5721
+ }
5583
5722
  if (singleHeight == -1)
5584
5723
  singleHeight = height;
5585
5724
  else if (Math.abs(height - singleHeight) >= Epsilon)
5586
5725
  singleHeight = -2;
5587
- let line = new HeightMapText(len, height);
5726
+ let line = new HeightMapText(len, height, above);
5588
5727
  line.outdated = false;
5589
5728
  nodes.push(line);
5590
5729
  pos += len + 1;
@@ -5749,7 +5888,7 @@ class NodeBuilder {
5749
5888
  if (last instanceof HeightMapText)
5750
5889
  last.length += end - this.pos;
5751
5890
  else if (end > this.pos || !this.isCovered)
5752
- this.nodes.push(new HeightMapText(end - this.pos, -1));
5891
+ this.nodes.push(new HeightMapText(end - this.pos, -1, 0));
5753
5892
  this.writtenTo = end;
5754
5893
  if (to > end) {
5755
5894
  this.nodes.push(null);
@@ -5791,7 +5930,7 @@ class NodeBuilder {
5791
5930
  this.nodes.push(null);
5792
5931
  }
5793
5932
  if (this.pos > from)
5794
- this.nodes.push(new HeightMapText(this.pos - from, -1));
5933
+ this.nodes.push(new HeightMapText(this.pos - from, -1, 0));
5795
5934
  this.writtenTo = this.pos;
5796
5935
  }
5797
5936
  blankContent(from, to) {
@@ -5805,7 +5944,7 @@ class NodeBuilder {
5805
5944
  let last = this.nodes.length ? this.nodes[this.nodes.length - 1] : null;
5806
5945
  if (last instanceof HeightMapText)
5807
5946
  return last;
5808
- let line = new HeightMapText(0, -1);
5947
+ let line = new HeightMapText(0, -1, 0);
5809
5948
  this.nodes.push(line);
5810
5949
  return line;
5811
5950
  }
@@ -5830,7 +5969,7 @@ class NodeBuilder {
5830
5969
  finish(from) {
5831
5970
  let last = this.nodes.length == 0 ? null : this.nodes[this.nodes.length - 1];
5832
5971
  if (this.lineStart > -1 && !(last instanceof HeightMapText) && !this.isCovered)
5833
- this.nodes.push(new HeightMapText(0, -1));
5972
+ this.nodes.push(new HeightMapText(0, -1, 0));
5834
5973
  else if (this.writtenTo < this.pos || last == null)
5835
5974
  this.nodes.push(this.blankContent(this.writtenTo, this.pos));
5836
5975
  let pos = from;
@@ -6957,8 +7096,8 @@ class DOMObserver {
6957
7096
  let { view } = this, sel = this.selectionRange;
6958
7097
  if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(this.dom, sel))
6959
7098
  return;
6960
- let context = sel.anchorNode && view.docView.nearest(sel.anchorNode);
6961
- if (context && context.ignoreEvent(event)) {
7099
+ let context = sel.anchorNode && view.docView.tile.nearest(sel.anchorNode);
7100
+ if (context && context.isWidget() && context.widget.ignoreEvent(event)) {
6962
7101
  if (!wasChanged)
6963
7102
  this.selectionChanged = false;
6964
7103
  return;
@@ -7188,20 +7327,18 @@ class DOMObserver {
7188
7327
  return handled;
7189
7328
  }
7190
7329
  readMutation(rec) {
7191
- let cView = this.view.docView.nearest(rec.target);
7192
- if (!cView || cView.ignoreMutation(rec))
7330
+ let tile = this.view.docView.tile.nearest(rec.target);
7331
+ if (!tile || tile.isWidget())
7193
7332
  return null;
7194
- cView.markDirty(rec.type == "attributes");
7195
- if (rec.type == "attributes")
7196
- cView.flags |= 4 /* ViewFlag.AttrsDirty */;
7333
+ tile.markDirty(rec.type == "attributes");
7197
7334
  if (rec.type == "childList") {
7198
- let childBefore = findChild(cView, rec.previousSibling || rec.target.previousSibling, -1);
7199
- let childAfter = findChild(cView, rec.nextSibling || rec.target.nextSibling, 1);
7200
- return { from: childBefore ? cView.posAfter(childBefore) : cView.posAtStart,
7201
- to: childAfter ? cView.posBefore(childAfter) : cView.posAtEnd, typeOver: false };
7335
+ let childBefore = findChild(tile, rec.previousSibling || rec.target.previousSibling, -1);
7336
+ let childAfter = findChild(tile, rec.nextSibling || rec.target.nextSibling, 1);
7337
+ return { from: childBefore ? tile.posAfter(childBefore) : tile.posAtStart,
7338
+ to: childAfter ? tile.posBefore(childAfter) : tile.posAtEnd, typeOver: false };
7202
7339
  }
7203
7340
  else if (rec.type == "characterData") {
7204
- return { from: cView.posAtStart, to: cView.posAtEnd, typeOver: rec.target.nodeValue == rec.oldValue };
7341
+ return { from: tile.posAtStart, to: tile.posAtEnd, typeOver: rec.target.nodeValue == rec.oldValue };
7205
7342
  }
7206
7343
  else {
7207
7344
  return null;
@@ -7266,20 +7403,20 @@ class DOMObserver {
7266
7403
  }
7267
7404
  }
7268
7405
  }
7269
- function findChild(cView, dom, dir) {
7406
+ function findChild(tile, dom, dir) {
7270
7407
  while (dom) {
7271
- let curView = ContentView.get(dom);
7272
- if (curView && curView.parent == cView)
7273
- return curView;
7408
+ let curTile = Tile.get(dom);
7409
+ if (curTile && curTile.parent == tile)
7410
+ return curTile;
7274
7411
  let parent = dom.parentNode;
7275
- dom = parent != cView.dom ? parent : dir > 0 ? dom.nextSibling : dom.previousSibling;
7412
+ dom = parent != tile.dom ? parent : dir > 0 ? dom.nextSibling : dom.previousSibling;
7276
7413
  }
7277
7414
  return null;
7278
7415
  }
7279
7416
  function buildSelectionRangeFromRange(view, range) {
7280
7417
  let anchorNode = range.startContainer, anchorOffset = range.startOffset;
7281
7418
  let focusNode = range.endContainer, focusOffset = range.endOffset;
7282
- let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor);
7419
+ let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor, 1);
7283
7420
  // Since such a range doesn't distinguish between anchor and head,
7284
7421
  // use a heuristic that flips it around if its end matches the
7285
7422
  // current anchor.
@@ -8205,8 +8342,8 @@ class EditorView {
8205
8342
  meaningful (it may just point before or after a placeholder
8206
8343
  element).
8207
8344
  */
8208
- domAtPos(pos) {
8209
- return this.docView.domAtPos(pos);
8345
+ domAtPos(pos, side = 1) {
8346
+ return this.docView.domAtPos(pos, side);
8210
8347
  }
8211
8348
  /**
8212
8349
  Find the document position at the given DOM node. Can be useful
@@ -8474,8 +8611,8 @@ class EditorView {
8474
8611
  static findFromDOM(dom) {
8475
8612
  var _a;
8476
8613
  let content = dom.querySelector(".cm-content");
8477
- let cView = content && ContentView.get(content) || ContentView.get(dom);
8478
- return ((_a = cView === null || cView === void 0 ? void 0 : cView.rootView) === null || _a === void 0 ? void 0 : _a.view) || null;
8614
+ let tile = content && Tile.get(content) || Tile.get(dom);
8615
+ return ((_a = tile === null || tile === void 0 ? void 0 : tile.root) === null || _a === void 0 ? void 0 : _a.view) || null;
8479
8616
  }
8480
8617
  }
8481
8618
  /**
@@ -8587,6 +8724,14 @@ containing the decorations to
8587
8724
  */
8588
8725
  EditorView.decorations = decorations;
8589
8726
  /**
8727
+ [Block wrappers](https://codemirror.net/6/docs/ref/#view.BlockWrapper) provide a way to add DOM
8728
+ structure around editor lines and block widgets. Sets of
8729
+ wrappers are provided in a similar way to decorations, and are
8730
+ nested in a similar way when they overlap. A wrapper affects all
8731
+ lines and block widgets that start inside its range.
8732
+ */
8733
+ EditorView.blockWrappers = blockWrappers;
8734
+ /**
8590
8735
  Facet that works much like
8591
8736
  [`decorations`](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), but puts its
8592
8737
  inputs at the very bottom of the precedence stack, meaning mark
@@ -10406,12 +10551,12 @@ class HoverPlugin {
10406
10551
  startHover() {
10407
10552
  clearTimeout(this.restartTimeout);
10408
10553
  let { view, lastMove } = this;
10409
- let desc = view.docView.nearest(lastMove.target);
10410
- if (!desc)
10554
+ let tile = view.docView.tile.nearest(lastMove.target);
10555
+ if (!tile)
10411
10556
  return;
10412
10557
  let pos, side = 1;
10413
- if (desc instanceof WidgetView) {
10414
- pos = desc.posAtStart;
10558
+ if (tile.isWidget()) {
10559
+ pos = tile.posAtStart;
10415
10560
  }
10416
10561
  else {
10417
10562
  pos = view.posAtCoords(lastMove);
@@ -11479,6 +11624,7 @@ const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRan
11479
11624
 
11480
11625
  exports.BidiSpan = BidiSpan;
11481
11626
  exports.BlockInfo = BlockInfo;
11627
+ exports.BlockWrapper = BlockWrapper;
11482
11628
  exports.Decoration = Decoration;
11483
11629
  exports.EditorView = EditorView;
11484
11630
  exports.GutterMarker = GutterMarker;