@codemirror/view 6.14.0 → 6.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -100,13 +100,15 @@ function windowRect(win) {
100
100
  }
101
101
  function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
102
102
  let doc = dom.ownerDocument, win = doc.defaultView || window;
103
- for (let cur = dom; cur;) {
103
+ for (let cur = dom, stop = false; cur && !stop;) {
104
104
  if (cur.nodeType == 1) { // Element
105
105
  let bounding, top = cur == doc.body;
106
106
  if (top) {
107
107
  bounding = windowRect(win);
108
108
  }
109
109
  else {
110
+ if (/^(fixed|sticky)$/.test(getComputedStyle(cur).position))
111
+ stop = true;
110
112
  if (cur.scrollHeight <= cur.clientHeight && cur.scrollWidth <= cur.clientWidth) {
111
113
  cur = cur.assignedSlot || cur.parentNode;
112
114
  continue;
@@ -320,6 +322,9 @@ function atElementStart(doc, selection) {
320
322
  }
321
323
  }
322
324
  }
325
+ function isScrolledToBottom(elt) {
326
+ return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
327
+ }
323
328
 
324
329
  class DOMPos {
325
330
  constructor(node, offset, precise = true) {
@@ -335,7 +340,7 @@ class ContentView {
335
340
  constructor() {
336
341
  this.parent = null;
337
342
  this.dom = null;
338
- this.dirty = 2 /* Node */;
343
+ this.flags = 2 /* NodeDirty */;
339
344
  }
340
345
  get overrideDOMText() { return null; }
341
346
  get posAtStart() {
@@ -357,18 +362,18 @@ class ContentView {
357
362
  return this.posBefore(view) + view.length;
358
363
  }
359
364
  sync(view, track) {
360
- if (this.dirty & 2 /* Node */) {
365
+ if (this.flags & 2 /* NodeDirty */) {
361
366
  let parent = this.dom;
362
367
  let prev = null, next;
363
368
  for (let child of this.children) {
364
- if (child.dirty) {
369
+ if (child.flags & 7 /* Dirty */) {
365
370
  if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
366
371
  let contentView = ContentView.get(next);
367
372
  if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
368
373
  child.reuseDOM(next);
369
374
  }
370
375
  child.sync(view, track);
371
- child.dirty = 0 /* Not */;
376
+ child.flags &= ~7 /* Dirty */;
372
377
  }
373
378
  next = prev ? prev.nextSibling : parent.firstChild;
374
379
  if (track && !track.written && track.node == parent && next != child.dom)
@@ -388,11 +393,11 @@ class ContentView {
388
393
  while (next)
389
394
  next = rm$1(next);
390
395
  }
391
- else if (this.dirty & 1 /* Child */) {
396
+ else if (this.flags & 1 /* ChildDirty */) {
392
397
  for (let child of this.children)
393
- if (child.dirty) {
398
+ if (child.flags & 7 /* Dirty */) {
394
399
  child.sync(view, track);
395
- child.dirty = 0 /* Not */;
400
+ child.flags &= ~7 /* Dirty */;
396
401
  }
397
402
  }
398
403
  }
@@ -457,23 +462,23 @@ class ContentView {
457
462
  endDOM: toI < this.children.length && toI >= 0 ? this.children[toI].dom : null };
458
463
  }
459
464
  markDirty(andParent = false) {
460
- this.dirty |= 2 /* Node */;
465
+ this.flags |= 2 /* NodeDirty */;
461
466
  this.markParentsDirty(andParent);
462
467
  }
463
468
  markParentsDirty(childList) {
464
469
  for (let parent = this.parent; parent; parent = parent.parent) {
465
470
  if (childList)
466
- parent.dirty |= 2 /* Node */;
467
- if (parent.dirty & 1 /* Child */)
471
+ parent.flags |= 2 /* NodeDirty */;
472
+ if (parent.flags & 1 /* ChildDirty */)
468
473
  return;
469
- parent.dirty |= 1 /* Child */;
474
+ parent.flags |= 1 /* ChildDirty */;
470
475
  childList = false;
471
476
  }
472
477
  }
473
478
  setParent(parent) {
474
479
  if (this.parent != parent) {
475
480
  this.parent = parent;
476
- if (this.dirty)
481
+ if (this.flags & 7 /* Dirty */)
477
482
  this.markParentsDirty(true);
478
483
  }
479
484
  }
@@ -524,7 +529,9 @@ class ContentView {
524
529
  return false;
525
530
  }
526
531
  become(other) { return false; }
527
- canReuseDOM(other) { return other.constructor == this.constructor; }
532
+ canReuseDOM(other) {
533
+ return other.constructor == this.constructor && !((this.flags | other.flags) & 8 /* Composition */);
534
+ }
528
535
  // When this is a zero-length view with a side, this should return a
529
536
  // number <= 0 to indicate it is before its position, or a
530
537
  // number > 0 when after its position.
@@ -648,6 +655,113 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
648
655
  replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
649
656
  }
650
657
 
658
+ const LineBreakPlaceholder = "\uffff";
659
+ class DOMReader {
660
+ constructor(points, state$1) {
661
+ this.points = points;
662
+ this.text = "";
663
+ this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
664
+ }
665
+ append(text) {
666
+ this.text += text;
667
+ }
668
+ lineBreak() {
669
+ this.text += LineBreakPlaceholder;
670
+ }
671
+ readRange(start, end) {
672
+ if (!start)
673
+ return this;
674
+ let parent = start.parentNode;
675
+ for (let cur = start;;) {
676
+ this.findPointBefore(parent, cur);
677
+ let oldLen = this.text.length;
678
+ this.readNode(cur);
679
+ let next = cur.nextSibling;
680
+ if (next == end)
681
+ break;
682
+ let view = ContentView.get(cur), nextView = ContentView.get(next);
683
+ if (view && nextView ? view.breakAfter :
684
+ (view ? view.breakAfter : isBlockElement(cur)) ||
685
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
686
+ this.lineBreak();
687
+ cur = next;
688
+ }
689
+ this.findPointBefore(parent, end);
690
+ return this;
691
+ }
692
+ readTextNode(node) {
693
+ let text = node.nodeValue;
694
+ for (let point of this.points)
695
+ if (point.node == node)
696
+ point.pos = this.text.length + Math.min(point.offset, text.length);
697
+ for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
698
+ let nextBreak = -1, breakSize = 1, m;
699
+ if (this.lineSeparator) {
700
+ nextBreak = text.indexOf(this.lineSeparator, off);
701
+ breakSize = this.lineSeparator.length;
702
+ }
703
+ else if (m = re.exec(text)) {
704
+ nextBreak = m.index;
705
+ breakSize = m[0].length;
706
+ }
707
+ this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
708
+ if (nextBreak < 0)
709
+ break;
710
+ this.lineBreak();
711
+ if (breakSize > 1)
712
+ for (let point of this.points)
713
+ if (point.node == node && point.pos > this.text.length)
714
+ point.pos -= breakSize - 1;
715
+ off = nextBreak + breakSize;
716
+ }
717
+ }
718
+ readNode(node) {
719
+ if (node.cmIgnore)
720
+ return;
721
+ let view = ContentView.get(node);
722
+ let fromView = view && view.overrideDOMText;
723
+ if (fromView != null) {
724
+ this.findPointInside(node, fromView.length);
725
+ for (let i = fromView.iter(); !i.next().done;) {
726
+ if (i.lineBreak)
727
+ this.lineBreak();
728
+ else
729
+ this.append(i.value);
730
+ }
731
+ }
732
+ else if (node.nodeType == 3) {
733
+ this.readTextNode(node);
734
+ }
735
+ else if (node.nodeName == "BR") {
736
+ if (node.nextSibling)
737
+ this.lineBreak();
738
+ }
739
+ else if (node.nodeType == 1) {
740
+ this.readRange(node.firstChild, null);
741
+ }
742
+ }
743
+ findPointBefore(node, next) {
744
+ for (let point of this.points)
745
+ if (point.node == node && node.childNodes[point.offset] == next)
746
+ point.pos = this.text.length;
747
+ }
748
+ findPointInside(node, maxLen) {
749
+ for (let point of this.points)
750
+ if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
751
+ point.pos = this.text.length + Math.min(maxLen, point.offset);
752
+ }
753
+ }
754
+ function isBlockElement(node) {
755
+ return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
756
+ }
757
+ class DOMPoint {
758
+ constructor(node, offset) {
759
+ this.node = node;
760
+ this.offset = offset;
761
+ this.pos = -1;
762
+ }
763
+ }
764
+
651
765
  let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
652
766
  let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
653
767
  const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent);
@@ -701,7 +815,10 @@ class TextView extends ContentView {
701
815
  this.createDOM(dom);
702
816
  }
703
817
  merge(from, to, source) {
704
- if (source && (!(source instanceof TextView) || this.length - (to - from) + source.length > MaxJoinLen))
818
+ if ((this.flags & 8 /* Composition */) ||
819
+ source && (!(source instanceof TextView) ||
820
+ this.length - (to - from) + source.length > MaxJoinLen ||
821
+ (source.flags & 8 /* Composition */)))
705
822
  return false;
706
823
  this.text = this.text.slice(0, from) + (source ? source.text : "") + this.text.slice(to);
707
824
  this.markDirty();
@@ -711,6 +828,7 @@ class TextView extends ContentView {
711
828
  let result = new TextView(this.text.slice(from));
712
829
  this.text = this.text.slice(0, from);
713
830
  this.markDirty();
831
+ result.flags |= this.flags & 8 /* Composition */;
714
832
  return result;
715
833
  }
716
834
  localPosFromDOM(node, offset) {
@@ -742,16 +860,19 @@ class MarkView extends ContentView {
742
860
  dom.setAttribute(name, this.mark.attrs[name]);
743
861
  return dom;
744
862
  }
863
+ canReuseDOM(other) {
864
+ return super.canReuseDOM(other) && !((this.flags | other.flags) & 8 /* Composition */);
865
+ }
745
866
  reuseDOM(node) {
746
867
  if (node.nodeName == this.mark.tagName.toUpperCase()) {
747
868
  this.setDOM(node);
748
- this.dirty |= 4 /* Attrs */ | 2 /* Node */;
869
+ this.flags |= 4 /* AttrsDirty */ | 2 /* NodeDirty */;
749
870
  }
750
871
  }
751
872
  sync(view, track) {
752
873
  if (!this.dom)
753
874
  this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
754
- else if (this.dirty & 4 /* Attrs */)
875
+ else if (this.flags & 4 /* AttrsDirty */)
755
876
  this.setAttrs(this.dom);
756
877
  super.sync(view, track);
757
878
  }
@@ -830,7 +951,7 @@ class WidgetView extends ContentView {
830
951
  this.prevWidget = null;
831
952
  }
832
953
  static create(widget, length, side) {
833
- return new (widget.customView || WidgetView)(widget, length, side);
954
+ return new WidgetView(widget, length, side);
834
955
  }
835
956
  split(from) {
836
957
  let result = WidgetView.create(this.widget, this.length - from, this.side);
@@ -908,129 +1029,6 @@ class WidgetView extends ContentView {
908
1029
  this.widget.destroy(this.dom);
909
1030
  }
910
1031
  }
911
- class CompositionView extends WidgetView {
912
- domAtPos(pos) {
913
- let { topView, text } = this.widget;
914
- if (!topView)
915
- return new DOMPos(text, Math.min(pos, text.nodeValue.length));
916
- return scanCompositionTree(pos, 0, topView, text, this.length - topView.length, (v, p) => v.domAtPos(p), (text, p) => new DOMPos(text, Math.min(p, text.nodeValue.length)));
917
- }
918
- sync() { this.setDOM(this.widget.toDOM()); }
919
- localPosFromDOM(node, offset) {
920
- let { topView, text } = this.widget;
921
- if (!topView)
922
- return Math.min(offset, this.length);
923
- return posFromDOMInCompositionTree(node, offset, topView, text, this.length - topView.length);
924
- }
925
- ignoreMutation() { return false; }
926
- get overrideDOMText() { return null; }
927
- coordsAt(pos, side) {
928
- let { topView, text } = this.widget;
929
- if (!topView)
930
- return textCoords(text, pos, side);
931
- return scanCompositionTree(pos, side, topView, text, this.length - topView.length, (v, pos, side) => v.coordsAt(pos, side), (text, pos, side) => textCoords(text, pos, side));
932
- }
933
- destroy() {
934
- var _a;
935
- super.destroy();
936
- (_a = this.widget.topView) === null || _a === void 0 ? void 0 : _a.destroy();
937
- }
938
- get isEditable() { return true; }
939
- canReuseDOM() { return true; }
940
- }
941
- // Uses the old structure of a chunk of content view frozen for
942
- // composition to try and find a reasonable DOM location for the given
943
- // offset.
944
- function scanCompositionTree(pos, side, view, text, dLen, enterView, fromText) {
945
- if (view instanceof MarkView) {
946
- for (let child = view.dom.firstChild; child; child = child.nextSibling) {
947
- let desc = ContentView.get(child);
948
- if (!desc) {
949
- let inner = scanCompositionNode(pos, side, child, fromText);
950
- if (typeof inner != "number")
951
- return inner;
952
- pos = inner;
953
- }
954
- else {
955
- let hasComp = contains(child, text);
956
- let len = desc.length + (hasComp ? dLen : 0);
957
- if (pos < len || pos == len && desc.getSide() <= 0)
958
- return hasComp ? scanCompositionTree(pos, side, desc, text, dLen, enterView, fromText) : enterView(desc, pos, side);
959
- pos -= len;
960
- }
961
- }
962
- return enterView(view, view.length, -1);
963
- }
964
- else if (view.dom == text) {
965
- return fromText(text, pos, side);
966
- }
967
- else {
968
- return enterView(view, pos, side);
969
- }
970
- }
971
- function scanCompositionNode(pos, side, node, fromText) {
972
- if (node.nodeType == 3) {
973
- let len = node.nodeValue.length;
974
- if (pos <= len)
975
- return fromText(node, pos, side);
976
- pos -= len;
977
- }
978
- else if (node.nodeType == 1 && node.contentEditable != "false") {
979
- for (let child = node.firstChild; child; child = child.nextSibling) {
980
- let inner = scanCompositionNode(pos, side, child, fromText);
981
- if (typeof inner != "number")
982
- return inner;
983
- pos = inner;
984
- }
985
- }
986
- return pos;
987
- }
988
- function posFromDOMInCompositionTree(node, offset, view, text, dLen) {
989
- if (view instanceof MarkView) {
990
- let pos = 0;
991
- for (let child = view.dom.firstChild; child; child = child.nextSibling) {
992
- let childView = ContentView.get(child);
993
- if (childView) {
994
- let hasComp = contains(child, text);
995
- if (contains(child, node))
996
- return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, childView, text, dLen)
997
- : childView.localPosFromDOM(node, offset));
998
- pos += childView.length + (hasComp ? dLen : 0);
999
- }
1000
- else {
1001
- let inner = posFromDOMInOpaqueNode(node, offset, child);
1002
- if (inner.result != null)
1003
- return pos + inner.result;
1004
- pos += inner.size;
1005
- }
1006
- }
1007
- }
1008
- else if (view.dom == text) {
1009
- return Math.min(offset, text.nodeValue.length);
1010
- }
1011
- return view.localPosFromDOM(node, offset);
1012
- }
1013
- function posFromDOMInOpaqueNode(node, offset, target) {
1014
- if (target.nodeType == 3) {
1015
- return node == target ? { result: offset } : { size: target.nodeValue.length };
1016
- }
1017
- else if (target.nodeType == 1 && target.contentEditable != "false") {
1018
- let pos = 0;
1019
- for (let child = target.firstChild, i = 0;; child = child.nextSibling, i++) {
1020
- if (node == target && i == offset)
1021
- return { result: pos };
1022
- if (!child)
1023
- return { size: pos };
1024
- let inner = posFromDOMInOpaqueNode(node, offset, child);
1025
- if (inner.result != null)
1026
- return { result: offset + inner.result };
1027
- pos += inner.size;
1028
- }
1029
- }
1030
- else {
1031
- return target.contains(node) ? { result: 0 } : { size: 0 };
1032
- }
1033
- }
1034
1032
  // These are drawn around uneditable widgets to avoid a number of
1035
1033
  // browser bugs that show up when the cursor is directly next to
1036
1034
  // uneditable inline content.
@@ -1150,16 +1148,20 @@ function combineAttrs(source, target) {
1150
1148
  }
1151
1149
  return target;
1152
1150
  }
1153
- function attrsEq(a, b) {
1151
+ const noAttrs = Object.create(null);
1152
+ function attrsEq(a, b, ignore) {
1154
1153
  if (a == b)
1155
1154
  return true;
1156
- if (!a || !b)
1157
- return false;
1155
+ if (!a)
1156
+ a = noAttrs;
1157
+ if (!b)
1158
+ b = noAttrs;
1158
1159
  let keysA = Object.keys(a), keysB = Object.keys(b);
1159
- if (keysA.length != keysB.length)
1160
+ if (keysA.length - (ignore && keysA.indexOf(ignore) > -1 ? 1 : 0) !=
1161
+ keysB.length - (ignore && keysB.indexOf(ignore) > -1 ? 1 : 0))
1160
1162
  return false;
1161
1163
  for (let key of keysA) {
1162
- if (keysB.indexOf(key) == -1 || a[key] !== b[key])
1164
+ if (key != ignore && (keysB.indexOf(key) == -1 || a[key] !== b[key]))
1163
1165
  return false;
1164
1166
  }
1165
1167
  return true;
@@ -1176,6 +1178,14 @@ function updateAttrs(dom, prev, attrs) {
1176
1178
  dom.setAttribute(changed = name, attrs[name]);
1177
1179
  return !!changed;
1178
1180
  }
1181
+ function getAttrs(dom) {
1182
+ let attrs = Object.create(null);
1183
+ for (let i = 0; i < dom.attributes.length; i++) {
1184
+ let attr = dom.attributes[i];
1185
+ attrs[attr.name] = attr.value;
1186
+ }
1187
+ return attrs;
1188
+ }
1179
1189
 
1180
1190
  /**
1181
1191
  Widgets added to the content are described by subclasses of this
@@ -1240,10 +1250,6 @@ class WidgetType {
1240
1250
  /**
1241
1251
  @internal
1242
1252
  */
1243
- get customView() { return null; }
1244
- /**
1245
- @internal
1246
- */
1247
1253
  get isHidden() { return false; }
1248
1254
  /**
1249
1255
  This is called when the an instance of the widget is removed
@@ -1382,11 +1388,12 @@ class MarkDecoration extends Decoration {
1382
1388
  this.attrs = spec.attributes || null;
1383
1389
  }
1384
1390
  eq(other) {
1391
+ var _a, _b;
1385
1392
  return this == other ||
1386
1393
  other instanceof MarkDecoration &&
1387
1394
  this.tagName == other.tagName &&
1388
- this.class == other.class &&
1389
- attrsEq(this.attrs, other.attrs);
1395
+ (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)) &&
1396
+ attrsEq(this.attrs, other.attrs, "class");
1390
1397
  }
1391
1398
  range(from, to = from) {
1392
1399
  if (from >= to)
@@ -1538,7 +1545,7 @@ class LineView extends ContentView {
1538
1545
  reuseDOM(node) {
1539
1546
  if (node.nodeName == "DIV") {
1540
1547
  this.setDOM(node);
1541
- this.dirty |= 4 /* Attrs */ | 2 /* Node */;
1548
+ this.flags |= 4 /* AttrsDirty */ | 2 /* NodeDirty */;
1542
1549
  }
1543
1550
  }
1544
1551
  sync(view, track) {
@@ -1548,7 +1555,7 @@ class LineView extends ContentView {
1548
1555
  this.dom.className = "cm-line";
1549
1556
  this.prevAttrs = this.attrs ? null : undefined;
1550
1557
  }
1551
- else if (this.dirty & 4 /* Attrs */) {
1558
+ else if (this.flags & 4 /* AttrsDirty */) {
1552
1559
  clearAttributes(this.dom);
1553
1560
  this.dom.className = "cm-line";
1554
1561
  this.prevAttrs = this.attrs ? null : undefined;
@@ -2489,120 +2496,14 @@ function moveVisually(line, order, dir, start, forward) {
2489
2496
  return state.EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
2490
2497
  }
2491
2498
 
2492
- const LineBreakPlaceholder = "\uffff";
2493
- class DOMReader {
2494
- constructor(points, state$1) {
2495
- this.points = points;
2496
- this.text = "";
2497
- this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
2498
- }
2499
- append(text) {
2500
- this.text += text;
2501
- }
2502
- lineBreak() {
2503
- this.text += LineBreakPlaceholder;
2504
- }
2505
- readRange(start, end) {
2506
- if (!start)
2507
- return this;
2508
- let parent = start.parentNode;
2509
- for (let cur = start;;) {
2510
- this.findPointBefore(parent, cur);
2511
- let oldLen = this.text.length;
2512
- this.readNode(cur);
2513
- let next = cur.nextSibling;
2514
- if (next == end)
2515
- break;
2516
- let view = ContentView.get(cur), nextView = ContentView.get(next);
2517
- if (view && nextView ? view.breakAfter :
2518
- (view ? view.breakAfter : isBlockElement(cur)) ||
2519
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
2520
- this.lineBreak();
2521
- cur = next;
2522
- }
2523
- this.findPointBefore(parent, end);
2524
- return this;
2525
- }
2526
- readTextNode(node) {
2527
- let text = node.nodeValue;
2528
- for (let point of this.points)
2529
- if (point.node == node)
2530
- point.pos = this.text.length + Math.min(point.offset, text.length);
2531
- for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
2532
- let nextBreak = -1, breakSize = 1, m;
2533
- if (this.lineSeparator) {
2534
- nextBreak = text.indexOf(this.lineSeparator, off);
2535
- breakSize = this.lineSeparator.length;
2536
- }
2537
- else if (m = re.exec(text)) {
2538
- nextBreak = m.index;
2539
- breakSize = m[0].length;
2540
- }
2541
- this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
2542
- if (nextBreak < 0)
2543
- break;
2544
- this.lineBreak();
2545
- if (breakSize > 1)
2546
- for (let point of this.points)
2547
- if (point.node == node && point.pos > this.text.length)
2548
- point.pos -= breakSize - 1;
2549
- off = nextBreak + breakSize;
2550
- }
2551
- }
2552
- readNode(node) {
2553
- if (node.cmIgnore)
2554
- return;
2555
- let view = ContentView.get(node);
2556
- let fromView = view && view.overrideDOMText;
2557
- if (fromView != null) {
2558
- this.findPointInside(node, fromView.length);
2559
- for (let i = fromView.iter(); !i.next().done;) {
2560
- if (i.lineBreak)
2561
- this.lineBreak();
2562
- else
2563
- this.append(i.value);
2564
- }
2565
- }
2566
- else if (node.nodeType == 3) {
2567
- this.readTextNode(node);
2568
- }
2569
- else if (node.nodeName == "BR") {
2570
- if (node.nextSibling)
2571
- this.lineBreak();
2572
- }
2573
- else if (node.nodeType == 1) {
2574
- this.readRange(node.firstChild, null);
2575
- }
2576
- }
2577
- findPointBefore(node, next) {
2578
- for (let point of this.points)
2579
- if (point.node == node && node.childNodes[point.offset] == next)
2580
- point.pos = this.text.length;
2581
- }
2582
- findPointInside(node, maxLen) {
2583
- for (let point of this.points)
2584
- if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
2585
- point.pos = this.text.length + Math.min(maxLen, point.offset);
2586
- }
2587
- }
2588
- function isBlockElement(node) {
2589
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
2590
- }
2591
- class DOMPoint {
2592
- constructor(node, offset) {
2593
- this.node = node;
2594
- this.offset = offset;
2595
- this.pos = -1;
2596
- }
2597
- }
2598
-
2599
2499
  class DocView extends ContentView {
2600
2500
  constructor(view) {
2601
2501
  super();
2602
2502
  this.view = view;
2603
- this.compositionDeco = Decoration.none;
2604
2503
  this.decorations = [];
2605
2504
  this.dynamicDecorationMap = [];
2505
+ this.hasComposition = false;
2506
+ this.markedForComposition = new Set;
2606
2507
  // Track a minimum width for the editor. When measuring sizes in
2607
2508
  // measureVisibleLineHeights, this is updated to point at the width
2608
2509
  // of a given element and its extent in the document. When a change
@@ -2625,7 +2526,7 @@ class DocView extends ContentView {
2625
2526
  this.children = [new LineView];
2626
2527
  this.children[0].setParent(this);
2627
2528
  this.updateDeco();
2628
- this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0);
2529
+ this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0, null);
2629
2530
  }
2630
2531
  get length() { return this.view.state.doc.length; }
2631
2532
  // Update the document view to a given state.
@@ -2640,26 +2541,26 @@ class DocView extends ContentView {
2640
2541
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2641
2542
  }
2642
2543
  }
2643
- if (this.view.inputState.composing < 0)
2644
- this.compositionDeco = Decoration.none;
2645
- else if (update.transactions.length || this.dirty)
2646
- this.compositionDeco = computeCompositionDeco(this.view, update.changes);
2544
+ let composition = this.view.inputState.composing < 0 ? null : findCompositionRange(this.view, update.changes);
2545
+ if (this.hasComposition)
2546
+ this.markedForComposition.clear();
2547
+ this.hasComposition = !!composition;
2647
2548
  // When the DOM nodes around the selection are moved to another
2648
2549
  // parent, Chrome sometimes reports a different selection through
2649
2550
  // getSelection than the one that it actually shows to the user.
2650
2551
  // This forces a selection update when lines are joined to work
2651
2552
  // around that. Issue #54
2652
- if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
2553
+ if ((browser.ie || browser.chrome) && !composition && update &&
2653
2554
  update.state.doc.lines != update.startState.doc.lines)
2654
2555
  this.forceSelection = true;
2655
2556
  let prevDeco = this.decorations, deco = this.updateDeco();
2656
2557
  let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
2657
2558
  changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
2658
- if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
2559
+ if (!(this.flags & 7 /* Dirty */) && changedRanges.length == 0) {
2659
2560
  return false;
2660
2561
  }
2661
2562
  else {
2662
- this.updateInner(changedRanges, update.startState.doc.length);
2563
+ this.updateInner(changedRanges, update.startState.doc.length, composition);
2663
2564
  if (update.transactions.length)
2664
2565
  this.lastUpdate = Date.now();
2665
2566
  return true;
@@ -2667,9 +2568,9 @@ class DocView extends ContentView {
2667
2568
  }
2668
2569
  // Used by update and the constructor do perform the actual DOM
2669
2570
  // update
2670
- updateInner(changes, oldLength) {
2571
+ updateInner(changes, oldLength, composition) {
2671
2572
  this.view.viewState.mustMeasureContent = true;
2672
- this.updateChildren(changes, oldLength);
2573
+ this.updateChildren(changes, oldLength, composition);
2673
2574
  let { observer } = this.view;
2674
2575
  observer.ignore(() => {
2675
2576
  // Lock the height during redrawing, since Chrome sometimes
@@ -2684,11 +2585,12 @@ class DocView extends ContentView {
2684
2585
  // to detect that situation.
2685
2586
  let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2686
2587
  this.sync(this.view, track);
2687
- this.dirty = 0 /* Not */;
2588
+ this.flags &= ~7 /* Dirty */;
2688
2589
  if (track && (track.written || observer.selectionRange.focusNode != track.node))
2689
2590
  this.forceSelection = true;
2690
2591
  this.dom.style.height = "";
2691
2592
  });
2593
+ this.markedForComposition.forEach(cView => cView.flags &= ~8 /* Composition */);
2692
2594
  let gaps = [];
2693
2595
  if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2694
2596
  for (let child of this.children)
@@ -2696,18 +2598,62 @@ class DocView extends ContentView {
2696
2598
  gaps.push(child.dom);
2697
2599
  observer.updateGaps(gaps);
2698
2600
  }
2699
- updateChildren(changes, oldLength) {
2601
+ updateChildren(changes, oldLength, composition) {
2602
+ let ranges = composition ? composition.range.addToSet(changes.slice()) : changes;
2700
2603
  let cursor = this.childCursor(oldLength);
2701
- for (let i = changes.length - 1;; i--) {
2702
- let next = i >= 0 ? changes[i] : null;
2604
+ for (let i = ranges.length - 1;; i--) {
2605
+ let next = i >= 0 ? ranges[i] : null;
2703
2606
  if (!next)
2704
2607
  break;
2705
- let { fromA, toA, fromB, toB } = next;
2706
- let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap);
2608
+ let { fromA, toA, fromB, toB } = next, content, breakAtStart, openStart, openEnd;
2609
+ if (composition && composition.range.fromB < toB && composition.range.toB > fromB) {
2610
+ let before = ContentBuilder.build(this.view.state.doc, fromB, composition.range.fromB, this.decorations, this.dynamicDecorationMap);
2611
+ let after = ContentBuilder.build(this.view.state.doc, composition.range.toB, toB, this.decorations, this.dynamicDecorationMap);
2612
+ breakAtStart = before.breakAtStart;
2613
+ openStart = before.openStart;
2614
+ openEnd = after.openEnd;
2615
+ let compLine = this.compositionView(composition);
2616
+ compLine.merge(compLine.length, compLine.length, after.content[0], false, after.openStart, 0);
2617
+ compLine.merge(0, 0, before.content[before.content.length - 1], true, 0, before.openEnd);
2618
+ content = before.content.slice(0, before.content.length - 1).concat(compLine).concat(after.content.slice(1));
2619
+ }
2620
+ else {
2621
+ ({ content, breakAtStart, openStart, openEnd } =
2622
+ ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap));
2623
+ }
2707
2624
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2708
2625
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2709
2626
  replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2710
2627
  }
2628
+ if (composition)
2629
+ this.fixCompositionDOM(composition);
2630
+ }
2631
+ compositionView(composition) {
2632
+ let cur = new TextView(composition.text.nodeValue);
2633
+ cur.flags |= 8 /* Composition */;
2634
+ for (let { deco } of composition.marks)
2635
+ cur = new MarkView(deco, [cur], cur.length);
2636
+ let line = new LineView;
2637
+ line.append(cur, 0);
2638
+ return line;
2639
+ }
2640
+ fixCompositionDOM(composition) {
2641
+ let fix = (dom, cView) => {
2642
+ cView.flags |= 8 /* Composition */;
2643
+ this.markedForComposition.add(cView);
2644
+ let prev = ContentView.get(dom);
2645
+ if (prev)
2646
+ prev.dom = null;
2647
+ cView.setDOM(dom);
2648
+ };
2649
+ let pos = this.childPos(composition.range.fromB, 1);
2650
+ let cView = this.children[pos.i];
2651
+ fix(composition.line, cView);
2652
+ for (let i = composition.marks.length - 1; i >= -1; i--) {
2653
+ pos = cView.childPos(pos.off, 1);
2654
+ cView = cView.children[pos.i];
2655
+ fix(i >= 0 ? composition.marks[i].node : composition.text, cView);
2656
+ }
2711
2657
  }
2712
2658
  // Sync the DOM selection to this.state.selection
2713
2659
  updateSelection(mustRead = false, fromPointer = false) {
@@ -2726,7 +2672,7 @@ class DocView extends ContentView {
2726
2672
  let head = main.empty ? anchor : this.domAtPos(main.head);
2727
2673
  // Always reset on Firefox when next to an uneditable node to
2728
2674
  // avoid invisible cursor bugs (#111)
2729
- if (browser.gecko && main.empty && !this.compositionDeco.size && betweenUneditable(anchor)) {
2675
+ if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
2730
2676
  let dummy = document.createTextNode("");
2731
2677
  this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null));
2732
2678
  anchor = head = new DOMPos(dummy, 0);
@@ -2798,7 +2744,7 @@ class DocView extends ContentView {
2798
2744
  this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
2799
2745
  }
2800
2746
  enforceCursorAssoc() {
2801
- if (this.compositionDeco.size)
2747
+ if (this.hasComposition)
2802
2748
  return;
2803
2749
  let { view } = this, cursor = view.state.selection.main;
2804
2750
  let sel = getSelection(view.root);
@@ -2908,6 +2854,7 @@ class DocView extends ContentView {
2908
2854
  let dummy = document.createElement("div"), lineHeight, charWidth, textHeight;
2909
2855
  dummy.className = "cm-line";
2910
2856
  dummy.style.width = "99999px";
2857
+ dummy.style.position = "absolute";
2911
2858
  dummy.textContent = "abc def ghi jkl mno pqr stu";
2912
2859
  this.view.observer.ignore(() => {
2913
2860
  this.dom.appendChild(dummy);
@@ -2957,7 +2904,6 @@ class DocView extends ContentView {
2957
2904
  this.dynamicDecorationMap[i] = false;
2958
2905
  return this.decorations = [
2959
2906
  ...allDeco,
2960
- this.compositionDeco,
2961
2907
  this.computeBlockGapDeco(),
2962
2908
  this.view.viewState.lineGapDeco
2963
2909
  ];
@@ -3000,81 +2946,85 @@ class BlockGapWidget extends WidgetType {
3000
2946
  }
3001
2947
  get estimatedHeight() { return this.height; }
3002
2948
  }
3003
- function compositionSurroundingNode(view) {
2949
+ function findCompositionNode(view) {
3004
2950
  let sel = view.observer.selectionRange;
3005
2951
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
3006
2952
  if (!textNode)
3007
2953
  return null;
3008
- let cView = view.docView.nearest(textNode);
3009
- if (!cView)
3010
- return null;
3011
- if (cView instanceof LineView) {
3012
- let topNode = textNode;
3013
- while (topNode.parentNode != cView.dom)
3014
- topNode = topNode.parentNode;
3015
- let prev = topNode.previousSibling;
3016
- while (prev && !ContentView.get(prev))
3017
- prev = prev.previousSibling;
3018
- let pos = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
3019
- return { from: pos, to: pos, node: topNode, text: textNode };
2954
+ let cView = ContentView.get(textNode);
2955
+ let from, to;
2956
+ if (cView instanceof TextView) {
2957
+ from = cView.posAtStart;
2958
+ to = from + cView.length;
3020
2959
  }
3021
2960
  else {
3022
- for (;;) {
3023
- let { parent } = cView;
3024
- if (!parent)
2961
+ up: for (let offset = 0, node = textNode;;) {
2962
+ for (let sibling = node.previousSibling, cView; sibling; sibling = sibling.previousSibling) {
2963
+ if (cView = ContentView.get(sibling)) {
2964
+ from = to = cView.posAtEnd + offset;
2965
+ break up;
2966
+ }
2967
+ let reader = new DOMReader([], view.state);
2968
+ reader.readNode(sibling);
2969
+ if (reader.text.indexOf(LineBreakPlaceholder) > -1)
2970
+ return null;
2971
+ offset += reader.text.length;
2972
+ }
2973
+ node = node.parentNode;
2974
+ if (!node)
3025
2975
  return null;
3026
- if (parent instanceof LineView)
2976
+ let parentView = ContentView.get(node);
2977
+ if (parentView) {
2978
+ from = to = parentView.posAtStart + offset;
3027
2979
  break;
3028
- cView = parent;
2980
+ }
3029
2981
  }
3030
- let from = cView.posAtStart;
3031
- return { from, to: from + cView.length, node: cView.dom, text: textNode };
3032
2982
  }
2983
+ return { from, to, node: textNode };
3033
2984
  }
3034
- function computeCompositionDeco(view, changes) {
3035
- let surrounding = compositionSurroundingNode(view);
3036
- if (!surrounding)
3037
- return Decoration.none;
3038
- let { from, to, node, text: textNode } = surrounding;
3039
- let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
3040
- let { state } = view, reader = new DOMReader([], state);
3041
- if (node.nodeType == 3)
3042
- reader.readTextNode(node);
3043
- else
3044
- reader.readRange(node.firstChild, null);
3045
- let { text } = reader;
3046
- if (text.indexOf(LineBreakPlaceholder) > -1)
3047
- return Decoration.none; // Don't try to preserve multi-line compositions
3048
- if (newTo - newFrom < text.length) {
3049
- if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
3050
- newTo = newFrom + text.length;
3051
- else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo) == text)
3052
- newFrom = newTo - text.length;
2985
+ function findCompositionRange(view, changes) {
2986
+ let found = findCompositionNode(view);
2987
+ if (!found)
2988
+ return null;
2989
+ let { from: fromA, to: toA, node: textNode } = found;
2990
+ let fromB = changes.mapPos(fromA, -1), toB = changes.mapPos(toA, 1);
2991
+ let text = textNode.nodeValue;
2992
+ // Don't try to preserve multi-line compositions
2993
+ if (/[\n\r]/.test(text))
2994
+ return null;
2995
+ if (toB - fromB != text.length) {
2996
+ // If there is a length mismatch, see if mapping non-inclusively helps
2997
+ let fromB2 = changes.mapPos(fromA, 1), toB2 = changes.mapPos(toA, -1);
2998
+ if (toB2 - fromB2 == text.length)
2999
+ fromB = fromB2, toB = toB2;
3000
+ // See if we can find an instance of the text at either side
3001
+ else if (view.state.doc.sliceString(toB - text.length, toB) == text)
3002
+ fromB = toB - text.length;
3003
+ else if (view.state.doc.sliceString(fromB, fromB + text.length) == text)
3004
+ toB = fromB + text.length;
3005
+ // Not found
3053
3006
  else
3054
- return Decoration.none;
3055
- }
3056
- else if (state.doc.sliceString(newFrom, newTo) != text) {
3057
- return Decoration.none;
3007
+ return null;
3058
3008
  }
3059
- let topView = ContentView.get(node);
3060
- if (topView instanceof CompositionView)
3061
- topView = topView.widget.topView;
3062
- else if (topView)
3063
- topView.parent = null;
3064
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
3065
- .range(newFrom, newTo));
3066
- }
3067
- class CompositionWidget extends WidgetType {
3068
- constructor(top, text, topView) {
3069
- super();
3070
- this.top = top;
3071
- this.text = text;
3072
- this.topView = topView;
3009
+ if (view.state.doc.sliceString(fromB, toB) != text)
3010
+ return null;
3011
+ let marks = [];
3012
+ let range = new ChangedRange(fromA, toA, fromB, toB);
3013
+ for (let parent = textNode.parentNode;; parent = parent.parentNode) {
3014
+ let parentView = ContentView.get(parent);
3015
+ if (parentView instanceof MarkView)
3016
+ marks.push({ node: parent, deco: parentView.mark });
3017
+ else if (parentView instanceof LineView || parent.nodeName == "DIV" && parent.parentNode == view.contentDOM)
3018
+ return { range, text: textNode, marks, line: parent };
3019
+ else if (parent != view.contentDOM)
3020
+ marks.push({ node: parent, deco: new MarkDecoration({
3021
+ inclusive: true,
3022
+ attributes: getAttrs(parent),
3023
+ tagName: parent.tagName.toLowerCase()
3024
+ }) });
3025
+ else
3026
+ return null;
3073
3027
  }
3074
- eq(other) { return this.top == other.top && this.text == other.text; }
3075
- toDOM() { return this.top; }
3076
- ignoreEvent() { return false; }
3077
- get customView() { return CompositionView; }
3078
3028
  }
3079
3029
  function nearbyTextNode(startNode, startOffset, side) {
3080
3030
  if (side <= 0)
@@ -3725,6 +3675,7 @@ class InputState {
3725
3675
  const PendingKeys = [
3726
3676
  { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3727
3677
  { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
3678
+ { key: "Enter", keyCode: 13, inputType: "insertLineBreak" },
3728
3679
  { key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
3729
3680
  ];
3730
3681
  const EmacsyPendingKeys = "dthko";
@@ -3734,9 +3685,13 @@ const dragScrollMargin = 6;
3734
3685
  function dragScrollSpeed(dist) {
3735
3686
  return Math.max(0, dist) * 0.7 + 8;
3736
3687
  }
3688
+ function dist(a, b) {
3689
+ return Math.max(Math.abs(a.clientX - b.clientX), Math.abs(a.clientY - b.clientY));
3690
+ }
3737
3691
  class MouseSelection {
3738
3692
  constructor(view, startEvent, style, mustSelect) {
3739
3693
  this.view = view;
3694
+ this.startEvent = startEvent;
3740
3695
  this.style = style;
3741
3696
  this.mustSelect = mustSelect;
3742
3697
  this.scrollSpeed = { x: 0, y: 0 };
@@ -3763,7 +3718,7 @@ class MouseSelection {
3763
3718
  var _a;
3764
3719
  if (event.buttons == 0)
3765
3720
  return this.destroy();
3766
- if (this.dragging !== false)
3721
+ if (this.dragging || this.dragging == null && dist(this.startEvent, event) < 10)
3767
3722
  return;
3768
3723
  this.select(this.lastEvent = event);
3769
3724
  let sx = 0, sy = 0;
@@ -3842,7 +3797,7 @@ class MouseSelection {
3842
3797
  select(event) {
3843
3798
  let { view } = this, selection = this.skipAtoms(this.style.get(event, this.extend, this.multiple));
3844
3799
  if (this.mustSelect || !selection.eq(view.state.selection) ||
3845
- selection.main.assoc != view.state.selection.main.assoc)
3800
+ selection.main.assoc != view.state.selection.main.assoc && this.dragging === false)
3846
3801
  this.view.dispatch({
3847
3802
  selection,
3848
3803
  userEvent: "select.pointer"
@@ -3969,7 +3924,7 @@ handlers.mousedown = (view, event) => {
3969
3924
  if (!style && event.button == 0)
3970
3925
  style = basicMouseSelection(view, event);
3971
3926
  if (style) {
3972
- let mustFocus = view.root.activeElement != view.contentDOM;
3927
+ let mustFocus = !view.hasFocus;
3973
3928
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
3974
3929
  if (mustFocus)
3975
3930
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
@@ -4261,7 +4216,7 @@ handlers.compositionend = view => {
4261
4216
  // Otherwise, make sure that, if no changes come in soon, the
4262
4217
  // composition view is cleared.
4263
4218
  setTimeout(() => {
4264
- if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
4219
+ if (view.inputState.composing < 0 && view.docView.hasComposition)
4265
4220
  view.update([]);
4266
4221
  }, 50);
4267
4222
  }
@@ -5166,7 +5121,7 @@ class ViewState {
5166
5121
  let contentChanges = update.changedRanges;
5167
5122
  let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : state.ChangeSet.empty(this.state.doc.length)));
5168
5123
  let prevHeight = this.heightMap.height;
5169
- let scrollAnchor = this.scrolledToBottom ? null : this.lineBlockAtHeight(this.scrollTop);
5124
+ let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop);
5170
5125
  this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
5171
5126
  if (this.heightMap.height != prevHeight)
5172
5127
  update.flags |= 2 /* Height */;
@@ -5226,7 +5181,7 @@ class ViewState {
5226
5181
  this.scrollAnchorHeight = -1;
5227
5182
  this.scrollTop = view.scrollDOM.scrollTop;
5228
5183
  }
5229
- this.scrolledToBottom = this.scrollTop > view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight - 4;
5184
+ this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
5230
5185
  // Pixel viewport
5231
5186
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
5232
5187
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -5469,6 +5424,10 @@ class ViewState {
5469
5424
  lineBlockAtHeight(height) {
5470
5425
  return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5471
5426
  }
5427
+ scrollAnchorAt(scrollTop) {
5428
+ let block = this.lineBlockAtHeight(scrollTop + 8);
5429
+ return block.from >= this.viewport.from || this.viewportLines[0].top - scrollTop > 200 ? block : this.viewportLines[0];
5430
+ }
5472
5431
  elementAtHeight(height) {
5473
5432
  return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
5474
5433
  }
@@ -5974,7 +5933,7 @@ function applyDOMChange(view, domChange) {
5974
5933
  if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
5975
5934
  change.to <= sel.to && change.to >= sel.to - 10) {
5976
5935
  let replaced = view.state.sliceDoc(change.from, change.to);
5977
- let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
5936
+ let composition = findCompositionNode(view) || view.state.doc.lineAt(sel.head);
5978
5937
  let offset = sel.to - change.to, size = sel.to - sel.from;
5979
5938
  tr = startState.changeByRange(range => {
5980
5939
  if (range.from == sel.from && range.to == sel.to)
@@ -5985,7 +5944,7 @@ function applyDOMChange(view, domChange) {
5985
5944
  // changes in the same node work without aborting
5986
5945
  // composition, so cursors in the composition range are
5987
5946
  // ignored.
5988
- compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
5947
+ composition && range.to >= composition.from && range.from <= composition.to)
5989
5948
  return { range };
5990
5949
  let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
5991
5950
  return {
@@ -6446,7 +6405,7 @@ class DOMObserver {
6446
6405
  return null;
6447
6406
  cView.markDirty(rec.type == "attributes");
6448
6407
  if (rec.type == "attributes")
6449
- cView.dirty |= 4 /* Attrs */;
6408
+ cView.flags |= 4 /* AttrsDirty */;
6450
6409
  if (rec.type == "childList") {
6451
6410
  let childBefore = findChild(cView, rec.previousSibling || rec.target.previousSibling, -1);
6452
6411
  let childAfter = findChild(cView, rec.nextSibling || rec.target.nextSibling, 1);
@@ -6841,22 +6800,23 @@ class EditorView {
6841
6800
  let updated = null;
6842
6801
  let sDOM = this.scrollDOM, { scrollTop } = sDOM;
6843
6802
  let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
6803
+ if (scrollTop != this.viewState.scrollTop)
6804
+ scrollAnchorHeight = -1;
6844
6805
  this.viewState.scrollAnchorHeight = -1;
6845
- if (scrollAnchorHeight < 0 || scrollTop != this.viewState.scrollTop) {
6846
- if (scrollTop > sDOM.scrollHeight - sDOM.clientHeight - 4) {
6847
- scrollAnchorPos = -1;
6848
- scrollAnchorHeight = this.viewState.heightMap.height;
6849
- }
6850
- else {
6851
- let block = this.viewState.lineBlockAtHeight(scrollTop);
6852
- scrollAnchorPos = block.from;
6853
- scrollAnchorHeight = block.top;
6854
- }
6855
- }
6856
6806
  try {
6857
6807
  for (let i = 0;; i++) {
6808
+ if (scrollAnchorHeight < 0) {
6809
+ if (isScrolledToBottom(sDOM)) {
6810
+ scrollAnchorPos = -1;
6811
+ scrollAnchorHeight = this.viewState.heightMap.height;
6812
+ }
6813
+ else {
6814
+ let block = this.viewState.scrollAnchorAt(scrollTop);
6815
+ scrollAnchorPos = block.from;
6816
+ scrollAnchorHeight = block.top;
6817
+ }
6818
+ }
6858
6819
  this.updateState = 1 /* Measuring */;
6859
- let oldViewport = this.viewport;
6860
6820
  let changed = this.viewState.measure(this);
6861
6821
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6862
6822
  break;
@@ -6879,7 +6839,7 @@ class EditorView {
6879
6839
  return BadMeasure;
6880
6840
  }
6881
6841
  });
6882
- let update = ViewUpdate.create(this, this.state, []), redrawn = false, scrolled = false;
6842
+ let update = ViewUpdate.create(this, this.state, []), redrawn = false;
6883
6843
  update.flags |= changed;
6884
6844
  if (!updated)
6885
6845
  updated = update;
@@ -6903,28 +6863,28 @@ class EditorView {
6903
6863
  logException(this.state, e);
6904
6864
  }
6905
6865
  }
6906
- if (this.viewState.editorHeight) {
6907
- if (this.viewState.scrollTarget) {
6908
- this.docView.scrollIntoView(this.viewState.scrollTarget);
6909
- this.viewState.scrollTarget = null;
6910
- scrolled = true;
6911
- }
6912
- else if (scrollAnchorHeight > -1) {
6913
- let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
6914
- this.viewState.lineBlockAt(scrollAnchorPos).top;
6915
- let diff = newAnchorHeight - scrollAnchorHeight;
6916
- if (diff > 1 || diff < -1) {
6917
- sDOM.scrollTop = scrollTop + diff;
6918
- scrolled = true;
6919
- }
6920
- }
6921
- }
6922
6866
  if (redrawn)
6923
6867
  this.docView.updateSelection(true);
6924
- if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
6925
- !scrolled && this.measureRequests.length == 0)
6868
+ if (!update.viewportChanged && this.measureRequests.length == 0) {
6869
+ if (this.viewState.editorHeight) {
6870
+ if (this.viewState.scrollTarget) {
6871
+ this.docView.scrollIntoView(this.viewState.scrollTarget);
6872
+ this.viewState.scrollTarget = null;
6873
+ continue;
6874
+ }
6875
+ else {
6876
+ let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
6877
+ this.viewState.lineBlockAt(scrollAnchorPos).top;
6878
+ let diff = newAnchorHeight - scrollAnchorHeight;
6879
+ if (diff > 1 || diff < -1) {
6880
+ scrollTop = sDOM.scrollTop = scrollTop + diff;
6881
+ scrollAnchorHeight = -1;
6882
+ continue;
6883
+ }
6884
+ }
6885
+ }
6926
6886
  break;
6927
- scrollAnchorHeight = -1;
6887
+ }
6928
6888
  }
6929
6889
  }
6930
6890
  finally {
@@ -7619,7 +7579,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7619
7579
  else if (current != is)
7620
7580
  throw new Error("Key binding " + name + " is used both as a regular binding and as a multi-stroke prefix");
7621
7581
  };
7622
- let add = (scope, key, command, preventDefault) => {
7582
+ let add = (scope, key, command, preventDefault, stopPropagation) => {
7623
7583
  var _a, _b;
7624
7584
  let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
7625
7585
  let parts = key.split(/ (?!$)/).map(k => normalizeKeyName(k, platform));
@@ -7629,6 +7589,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7629
7589
  if (!scopeObj[prefix])
7630
7590
  scopeObj[prefix] = {
7631
7591
  preventDefault: true,
7592
+ stopPropagation: false,
7632
7593
  run: [(view) => {
7633
7594
  let ourObj = storedPrefix = { view, prefix, scope };
7634
7595
  setTimeout(() => { if (storedPrefix == ourObj)
@@ -7639,11 +7600,17 @@ function buildKeymap(bindings, platform = currentPlatform) {
7639
7600
  }
7640
7601
  let full = parts.join(" ");
7641
7602
  checkPrefix(full, false);
7642
- let binding = scopeObj[full] || (scopeObj[full] = { preventDefault: false, run: ((_b = (_a = scopeObj._any) === null || _a === void 0 ? void 0 : _a.run) === null || _b === void 0 ? void 0 : _b.slice()) || [] });
7603
+ let binding = scopeObj[full] || (scopeObj[full] = {
7604
+ preventDefault: false,
7605
+ stopPropagation: false,
7606
+ run: ((_b = (_a = scopeObj._any) === null || _a === void 0 ? void 0 : _a.run) === null || _b === void 0 ? void 0 : _b.slice()) || []
7607
+ });
7643
7608
  if (command)
7644
7609
  binding.run.push(command);
7645
7610
  if (preventDefault)
7646
7611
  binding.preventDefault = true;
7612
+ if (stopPropagation)
7613
+ binding.stopPropagation = true;
7647
7614
  };
7648
7615
  for (let b of bindings) {
7649
7616
  let scopes = b.scope ? b.scope.split(" ") : ["editor"];
@@ -7651,7 +7618,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7651
7618
  for (let scope of scopes) {
7652
7619
  let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
7653
7620
  if (!scopeObj._any)
7654
- scopeObj._any = { preventDefault: false, run: [] };
7621
+ scopeObj._any = { preventDefault: false, stopPropagation: false, run: [] };
7655
7622
  for (let key in scopeObj)
7656
7623
  scopeObj[key].run.push(b.any);
7657
7624
  }
@@ -7659,9 +7626,9 @@ function buildKeymap(bindings, platform = currentPlatform) {
7659
7626
  if (!name)
7660
7627
  continue;
7661
7628
  for (let scope of scopes) {
7662
- add(scope, name, b.run, b.preventDefault);
7629
+ add(scope, name, b.run, b.preventDefault, b.stopPropagation);
7663
7630
  if (b.shift)
7664
- add(scope, "Shift-" + name, b.shift, b.preventDefault);
7631
+ add(scope, "Shift-" + name, b.shift, b.preventDefault, b.stopPropagation);
7665
7632
  }
7666
7633
  }
7667
7634
  return bound;
@@ -7669,11 +7636,13 @@ function buildKeymap(bindings, platform = currentPlatform) {
7669
7636
  function runHandlers(map, event, view, scope) {
7670
7637
  let name = w3cKeyname.keyName(event);
7671
7638
  let charCode = state.codePointAt(name, 0), isChar = state.codePointSize(charCode) == name.length && name != " ";
7672
- let prefix = "", fallthrough = false;
7639
+ let prefix = "", handled = false, prevented = false, stopPropagation = false;
7673
7640
  if (storedPrefix && storedPrefix.view == view && storedPrefix.scope == scope) {
7674
7641
  prefix = storedPrefix.prefix + " ";
7675
- if (fallthrough = modifierCodes.indexOf(event.keyCode) < 0)
7642
+ if (modifierCodes.indexOf(event.keyCode) < 0) {
7643
+ prevented = true;
7676
7644
  storedPrefix = null;
7645
+ }
7677
7646
  }
7678
7647
  let ran = new Set;
7679
7648
  let runFor = (binding) => {
@@ -7681,36 +7650,49 @@ function runHandlers(map, event, view, scope) {
7681
7650
  for (let cmd of binding.run)
7682
7651
  if (!ran.has(cmd)) {
7683
7652
  ran.add(cmd);
7684
- if (cmd(view, event))
7653
+ if (cmd(view, event)) {
7654
+ if (binding.stopPropagation)
7655
+ stopPropagation = true;
7685
7656
  return true;
7657
+ }
7686
7658
  }
7687
- if (binding.preventDefault)
7688
- fallthrough = true;
7659
+ if (binding.preventDefault) {
7660
+ if (binding.stopPropagation)
7661
+ stopPropagation = true;
7662
+ prevented = true;
7663
+ }
7689
7664
  }
7690
7665
  return false;
7691
7666
  };
7692
7667
  let scopeObj = map[scope], baseName, shiftName;
7693
7668
  if (scopeObj) {
7694
- if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)]))
7695
- return true;
7696
- if (isChar && (event.altKey || event.metaKey || event.ctrlKey) &&
7669
+ if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)])) {
7670
+ handled = true;
7671
+ }
7672
+ else if (isChar && (event.altKey || event.metaKey || event.ctrlKey) &&
7697
7673
  // Ctrl-Alt may be used for AltGr on Windows
7698
7674
  !(browser.windows && event.ctrlKey && event.altKey) &&
7699
7675
  (baseName = w3cKeyname.base[event.keyCode]) && baseName != name) {
7700
- if (runFor(scopeObj[prefix + modifiers(baseName, event, true)]))
7701
- return true;
7676
+ if (runFor(scopeObj[prefix + modifiers(baseName, event, true)])) {
7677
+ handled = true;
7678
+ }
7702
7679
  else if (event.shiftKey && (shiftName = w3cKeyname.shift[event.keyCode]) != name && shiftName != baseName &&
7703
- runFor(scopeObj[prefix + modifiers(shiftName, event, false)]))
7704
- return true;
7680
+ runFor(scopeObj[prefix + modifiers(shiftName, event, false)])) {
7681
+ handled = true;
7682
+ }
7705
7683
  }
7706
- else if (isChar && event.shiftKey) {
7707
- if (runFor(scopeObj[prefix + modifiers(name, event, true)]))
7708
- return true;
7684
+ else if (isChar && event.shiftKey &&
7685
+ runFor(scopeObj[prefix + modifiers(name, event, true)])) {
7686
+ handled = true;
7709
7687
  }
7710
- if (runFor(scopeObj._any))
7711
- return true;
7688
+ if (!handled && runFor(scopeObj._any))
7689
+ handled = true;
7712
7690
  }
7713
- return fallthrough;
7691
+ if (prevented)
7692
+ handled = true;
7693
+ if (handled && stopPropagation)
7694
+ event.stopPropagation();
7695
+ return handled;
7714
7696
  }
7715
7697
 
7716
7698
  /**
@@ -8497,7 +8479,9 @@ function placeholder(content) {
8497
8479
  return ViewPlugin.fromClass(class {
8498
8480
  constructor(view) {
8499
8481
  this.view = view;
8500
- this.placeholder = Decoration.set([Decoration.widget({ widget: new Placeholder(content), side: 1 }).range(0)]);
8482
+ this.placeholder = content
8483
+ ? Decoration.set([Decoration.widget({ widget: new Placeholder(content), side: 1 }).range(0)])
8484
+ : Decoration.none;
8501
8485
  }
8502
8486
  get decorations() { return this.view.state.doc.length ? Decoration.none : this.placeholder; }
8503
8487
  }, { decorations: v => v.decorations });