@codemirror/view 6.38.8 → 6.39.0-beta.2

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