@codemirror/view 6.14.1 → 6.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -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;
@@ -338,7 +340,7 @@ class ContentView {
338
340
  constructor() {
339
341
  this.parent = null;
340
342
  this.dom = null;
341
- this.dirty = 2 /* Node */;
343
+ this.flags = 2 /* NodeDirty */;
342
344
  }
343
345
  get overrideDOMText() { return null; }
344
346
  get posAtStart() {
@@ -360,18 +362,18 @@ class ContentView {
360
362
  return this.posBefore(view) + view.length;
361
363
  }
362
364
  sync(view, track) {
363
- if (this.dirty & 2 /* Node */) {
365
+ if (this.flags & 2 /* NodeDirty */) {
364
366
  let parent = this.dom;
365
367
  let prev = null, next;
366
368
  for (let child of this.children) {
367
- if (child.dirty) {
368
- if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild) && next != view.docView.compositionNode) {
369
+ if (child.flags & 7 /* Dirty */) {
370
+ if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
369
371
  let contentView = ContentView.get(next);
370
372
  if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
371
373
  child.reuseDOM(next);
372
374
  }
373
375
  child.sync(view, track);
374
- child.dirty = 0 /* Not */;
376
+ child.flags &= ~7 /* Dirty */;
375
377
  }
376
378
  next = prev ? prev.nextSibling : parent.firstChild;
377
379
  if (track && !track.written && track.node == parent && next != child.dom)
@@ -391,11 +393,11 @@ class ContentView {
391
393
  while (next)
392
394
  next = rm$1(next);
393
395
  }
394
- else if (this.dirty & 1 /* Child */) {
396
+ else if (this.flags & 1 /* ChildDirty */) {
395
397
  for (let child of this.children)
396
- if (child.dirty) {
398
+ if (child.flags & 7 /* Dirty */) {
397
399
  child.sync(view, track);
398
- child.dirty = 0 /* Not */;
400
+ child.flags &= ~7 /* Dirty */;
399
401
  }
400
402
  }
401
403
  }
@@ -460,23 +462,23 @@ class ContentView {
460
462
  endDOM: toI < this.children.length && toI >= 0 ? this.children[toI].dom : null };
461
463
  }
462
464
  markDirty(andParent = false) {
463
- this.dirty |= 2 /* Node */;
465
+ this.flags |= 2 /* NodeDirty */;
464
466
  this.markParentsDirty(andParent);
465
467
  }
466
468
  markParentsDirty(childList) {
467
469
  for (let parent = this.parent; parent; parent = parent.parent) {
468
470
  if (childList)
469
- parent.dirty |= 2 /* Node */;
470
- if (parent.dirty & 1 /* Child */)
471
+ parent.flags |= 2 /* NodeDirty */;
472
+ if (parent.flags & 1 /* ChildDirty */)
471
473
  return;
472
- parent.dirty |= 1 /* Child */;
474
+ parent.flags |= 1 /* ChildDirty */;
473
475
  childList = false;
474
476
  }
475
477
  }
476
478
  setParent(parent) {
477
479
  if (this.parent != parent) {
478
480
  this.parent = parent;
479
- if (this.dirty)
481
+ if (this.flags & 7 /* Dirty */)
480
482
  this.markParentsDirty(true);
481
483
  }
482
484
  }
@@ -527,7 +529,9 @@ class ContentView {
527
529
  return false;
528
530
  }
529
531
  become(other) { return false; }
530
- canReuseDOM(other) { return other.constructor == this.constructor; }
532
+ canReuseDOM(other) {
533
+ return other.constructor == this.constructor && !((this.flags | other.flags) & 8 /* Composition */);
534
+ }
531
535
  // When this is a zero-length view with a side, this should return a
532
536
  // number <= 0 to indicate it is before its position, or a
533
537
  // number > 0 when after its position.
@@ -651,6 +655,113 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
651
655
  replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
652
656
  }
653
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
+
654
765
  let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
655
766
  let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
656
767
  const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent);
@@ -704,7 +815,10 @@ class TextView extends ContentView {
704
815
  this.createDOM(dom);
705
816
  }
706
817
  merge(from, to, source) {
707
- 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 */)))
708
822
  return false;
709
823
  this.text = this.text.slice(0, from) + (source ? source.text : "") + this.text.slice(to);
710
824
  this.markDirty();
@@ -714,6 +828,7 @@ class TextView extends ContentView {
714
828
  let result = new TextView(this.text.slice(from));
715
829
  this.text = this.text.slice(0, from);
716
830
  this.markDirty();
831
+ result.flags |= this.flags & 8 /* Composition */;
717
832
  return result;
718
833
  }
719
834
  localPosFromDOM(node, offset) {
@@ -745,16 +860,19 @@ class MarkView extends ContentView {
745
860
  dom.setAttribute(name, this.mark.attrs[name]);
746
861
  return dom;
747
862
  }
863
+ canReuseDOM(other) {
864
+ return super.canReuseDOM(other) && !((this.flags | other.flags) & 8 /* Composition */);
865
+ }
748
866
  reuseDOM(node) {
749
867
  if (node.nodeName == this.mark.tagName.toUpperCase()) {
750
868
  this.setDOM(node);
751
- this.dirty |= 4 /* Attrs */ | 2 /* Node */;
869
+ this.flags |= 4 /* AttrsDirty */ | 2 /* NodeDirty */;
752
870
  }
753
871
  }
754
872
  sync(view, track) {
755
873
  if (!this.dom)
756
874
  this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
757
- else if (this.dirty & 4 /* Attrs */)
875
+ else if (this.flags & 4 /* AttrsDirty */)
758
876
  this.setAttrs(this.dom);
759
877
  super.sync(view, track);
760
878
  }
@@ -833,7 +951,7 @@ class WidgetView extends ContentView {
833
951
  this.prevWidget = null;
834
952
  }
835
953
  static create(widget, length, side) {
836
- return new (widget.customView || WidgetView)(widget, length, side);
954
+ return new WidgetView(widget, length, side);
837
955
  }
838
956
  split(from) {
839
957
  let result = WidgetView.create(this.widget, this.length - from, this.side);
@@ -911,129 +1029,6 @@ class WidgetView extends ContentView {
911
1029
  this.widget.destroy(this.dom);
912
1030
  }
913
1031
  }
914
- class CompositionView extends WidgetView {
915
- domAtPos(pos) {
916
- let { topView, text } = this.widget;
917
- if (!topView)
918
- return new DOMPos(text, Math.min(pos, text.nodeValue.length));
919
- 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)));
920
- }
921
- sync() { this.setDOM(this.widget.toDOM()); }
922
- localPosFromDOM(node, offset) {
923
- let { topView, text } = this.widget;
924
- if (!topView)
925
- return Math.min(offset, this.length);
926
- return posFromDOMInCompositionTree(node, offset, topView, text, this.length - topView.length);
927
- }
928
- ignoreMutation() { return false; }
929
- get overrideDOMText() { return null; }
930
- coordsAt(pos, side) {
931
- let { topView, text } = this.widget;
932
- if (!topView)
933
- return textCoords(text, pos, side);
934
- return scanCompositionTree(pos, side, topView, text, this.length - topView.length, (v, pos, side) => v.coordsAt(pos, side), (text, pos, side) => textCoords(text, pos, side));
935
- }
936
- destroy() {
937
- var _a;
938
- super.destroy();
939
- (_a = this.widget.topView) === null || _a === void 0 ? void 0 : _a.destroy();
940
- }
941
- get isEditable() { return true; }
942
- canReuseDOM() { return true; }
943
- }
944
- // Uses the old structure of a chunk of content view frozen for
945
- // composition to try and find a reasonable DOM location for the given
946
- // offset.
947
- function scanCompositionTree(pos, side, view, text, dLen, enterView, fromText) {
948
- if (view instanceof MarkView) {
949
- for (let child = view.dom.firstChild; child; child = child.nextSibling) {
950
- let desc = ContentView.get(child);
951
- if (!desc) {
952
- let inner = scanCompositionNode(pos, side, child, fromText);
953
- if (typeof inner != "number")
954
- return inner;
955
- pos = inner;
956
- }
957
- else {
958
- let hasComp = contains(child, text);
959
- let len = desc.length + (hasComp ? dLen : 0);
960
- if (pos < len || pos == len && desc.getSide() <= 0)
961
- return hasComp ? scanCompositionTree(pos, side, desc, text, dLen, enterView, fromText) : enterView(desc, pos, side);
962
- pos -= len;
963
- }
964
- }
965
- return enterView(view, view.length, -1);
966
- }
967
- else if (view.dom == text) {
968
- return fromText(text, pos, side);
969
- }
970
- else {
971
- return enterView(view, pos, side);
972
- }
973
- }
974
- function scanCompositionNode(pos, side, node, fromText) {
975
- if (node.nodeType == 3) {
976
- let len = node.nodeValue.length;
977
- if (pos <= len)
978
- return fromText(node, pos, side);
979
- pos -= len;
980
- }
981
- else if (node.nodeType == 1 && node.contentEditable != "false") {
982
- for (let child = node.firstChild; child; child = child.nextSibling) {
983
- let inner = scanCompositionNode(pos, side, child, fromText);
984
- if (typeof inner != "number")
985
- return inner;
986
- pos = inner;
987
- }
988
- }
989
- return pos;
990
- }
991
- function posFromDOMInCompositionTree(node, offset, view, text, dLen) {
992
- if (view instanceof MarkView) {
993
- let pos = 0;
994
- for (let child = view.dom.firstChild; child; child = child.nextSibling) {
995
- let childView = ContentView.get(child);
996
- if (childView) {
997
- let hasComp = contains(child, text);
998
- if (contains(child, node))
999
- return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, childView, text, dLen)
1000
- : childView.localPosFromDOM(node, offset));
1001
- pos += childView.length + (hasComp ? dLen : 0);
1002
- }
1003
- else {
1004
- let inner = posFromDOMInOpaqueNode(node, offset, child);
1005
- if (inner.result != null)
1006
- return pos + inner.result;
1007
- pos += inner.size;
1008
- }
1009
- }
1010
- }
1011
- else if (view.dom == text) {
1012
- return Math.min(offset, text.nodeValue.length);
1013
- }
1014
- return view.localPosFromDOM(node, offset);
1015
- }
1016
- function posFromDOMInOpaqueNode(node, offset, target) {
1017
- if (target.nodeType == 3) {
1018
- return node == target ? { result: offset } : { size: target.nodeValue.length };
1019
- }
1020
- else if (target.nodeType == 1 && target.contentEditable != "false") {
1021
- let pos = 0;
1022
- for (let child = target.firstChild, i = 0;; child = child.nextSibling, i++) {
1023
- if (node == target && i == offset)
1024
- return { result: pos };
1025
- if (!child)
1026
- return { size: pos };
1027
- let inner = posFromDOMInOpaqueNode(node, offset, child);
1028
- if (inner.result != null)
1029
- return { result: offset + inner.result };
1030
- pos += inner.size;
1031
- }
1032
- }
1033
- else {
1034
- return target.contains(node) ? { result: 0 } : { size: 0 };
1035
- }
1036
- }
1037
1032
  // These are drawn around uneditable widgets to avoid a number of
1038
1033
  // browser bugs that show up when the cursor is directly next to
1039
1034
  // uneditable inline content.
@@ -1153,16 +1148,20 @@ function combineAttrs(source, target) {
1153
1148
  }
1154
1149
  return target;
1155
1150
  }
1156
- function attrsEq(a, b) {
1151
+ const noAttrs = Object.create(null);
1152
+ function attrsEq(a, b, ignore) {
1157
1153
  if (a == b)
1158
1154
  return true;
1159
- if (!a || !b)
1160
- return false;
1155
+ if (!a)
1156
+ a = noAttrs;
1157
+ if (!b)
1158
+ b = noAttrs;
1161
1159
  let keysA = Object.keys(a), keysB = Object.keys(b);
1162
- 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))
1163
1162
  return false;
1164
1163
  for (let key of keysA) {
1165
- if (keysB.indexOf(key) == -1 || a[key] !== b[key])
1164
+ if (key != ignore && (keysB.indexOf(key) == -1 || a[key] !== b[key]))
1166
1165
  return false;
1167
1166
  }
1168
1167
  return true;
@@ -1179,6 +1178,14 @@ function updateAttrs(dom, prev, attrs) {
1179
1178
  dom.setAttribute(changed = name, attrs[name]);
1180
1179
  return !!changed;
1181
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
+ }
1182
1189
 
1183
1190
  /**
1184
1191
  Widgets added to the content are described by subclasses of this
@@ -1243,10 +1250,6 @@ class WidgetType {
1243
1250
  /**
1244
1251
  @internal
1245
1252
  */
1246
- get customView() { return null; }
1247
- /**
1248
- @internal
1249
- */
1250
1253
  get isHidden() { return false; }
1251
1254
  /**
1252
1255
  This is called when the an instance of the widget is removed
@@ -1385,11 +1388,12 @@ class MarkDecoration extends Decoration {
1385
1388
  this.attrs = spec.attributes || null;
1386
1389
  }
1387
1390
  eq(other) {
1391
+ var _a, _b;
1388
1392
  return this == other ||
1389
1393
  other instanceof MarkDecoration &&
1390
1394
  this.tagName == other.tagName &&
1391
- this.class == other.class &&
1392
- 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");
1393
1397
  }
1394
1398
  range(from, to = from) {
1395
1399
  if (from >= to)
@@ -1541,7 +1545,7 @@ class LineView extends ContentView {
1541
1545
  reuseDOM(node) {
1542
1546
  if (node.nodeName == "DIV") {
1543
1547
  this.setDOM(node);
1544
- this.dirty |= 4 /* Attrs */ | 2 /* Node */;
1548
+ this.flags |= 4 /* AttrsDirty */ | 2 /* NodeDirty */;
1545
1549
  }
1546
1550
  }
1547
1551
  sync(view, track) {
@@ -1551,7 +1555,7 @@ class LineView extends ContentView {
1551
1555
  this.dom.className = "cm-line";
1552
1556
  this.prevAttrs = this.attrs ? null : undefined;
1553
1557
  }
1554
- else if (this.dirty & 4 /* Attrs */) {
1558
+ else if (this.flags & 4 /* AttrsDirty */) {
1555
1559
  clearAttributes(this.dom);
1556
1560
  this.dom.className = "cm-line";
1557
1561
  this.prevAttrs = this.attrs ? null : undefined;
@@ -2492,121 +2496,14 @@ function moveVisually(line, order, dir, start, forward) {
2492
2496
  return state.EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
2493
2497
  }
2494
2498
 
2495
- const LineBreakPlaceholder = "\uffff";
2496
- class DOMReader {
2497
- constructor(points, state$1) {
2498
- this.points = points;
2499
- this.text = "";
2500
- this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
2501
- }
2502
- append(text) {
2503
- this.text += text;
2504
- }
2505
- lineBreak() {
2506
- this.text += LineBreakPlaceholder;
2507
- }
2508
- readRange(start, end) {
2509
- if (!start)
2510
- return this;
2511
- let parent = start.parentNode;
2512
- for (let cur = start;;) {
2513
- this.findPointBefore(parent, cur);
2514
- let oldLen = this.text.length;
2515
- this.readNode(cur);
2516
- let next = cur.nextSibling;
2517
- if (next == end)
2518
- break;
2519
- let view = ContentView.get(cur), nextView = ContentView.get(next);
2520
- if (view && nextView ? view.breakAfter :
2521
- (view ? view.breakAfter : isBlockElement(cur)) ||
2522
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
2523
- this.lineBreak();
2524
- cur = next;
2525
- }
2526
- this.findPointBefore(parent, end);
2527
- return this;
2528
- }
2529
- readTextNode(node) {
2530
- let text = node.nodeValue;
2531
- for (let point of this.points)
2532
- if (point.node == node)
2533
- point.pos = this.text.length + Math.min(point.offset, text.length);
2534
- for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
2535
- let nextBreak = -1, breakSize = 1, m;
2536
- if (this.lineSeparator) {
2537
- nextBreak = text.indexOf(this.lineSeparator, off);
2538
- breakSize = this.lineSeparator.length;
2539
- }
2540
- else if (m = re.exec(text)) {
2541
- nextBreak = m.index;
2542
- breakSize = m[0].length;
2543
- }
2544
- this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
2545
- if (nextBreak < 0)
2546
- break;
2547
- this.lineBreak();
2548
- if (breakSize > 1)
2549
- for (let point of this.points)
2550
- if (point.node == node && point.pos > this.text.length)
2551
- point.pos -= breakSize - 1;
2552
- off = nextBreak + breakSize;
2553
- }
2554
- }
2555
- readNode(node) {
2556
- if (node.cmIgnore)
2557
- return;
2558
- let view = ContentView.get(node);
2559
- let fromView = view && view.overrideDOMText;
2560
- if (fromView != null) {
2561
- this.findPointInside(node, fromView.length);
2562
- for (let i = fromView.iter(); !i.next().done;) {
2563
- if (i.lineBreak)
2564
- this.lineBreak();
2565
- else
2566
- this.append(i.value);
2567
- }
2568
- }
2569
- else if (node.nodeType == 3) {
2570
- this.readTextNode(node);
2571
- }
2572
- else if (node.nodeName == "BR") {
2573
- if (node.nextSibling)
2574
- this.lineBreak();
2575
- }
2576
- else if (node.nodeType == 1) {
2577
- this.readRange(node.firstChild, null);
2578
- }
2579
- }
2580
- findPointBefore(node, next) {
2581
- for (let point of this.points)
2582
- if (point.node == node && node.childNodes[point.offset] == next)
2583
- point.pos = this.text.length;
2584
- }
2585
- findPointInside(node, maxLen) {
2586
- for (let point of this.points)
2587
- if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
2588
- point.pos = this.text.length + Math.min(maxLen, point.offset);
2589
- }
2590
- }
2591
- function isBlockElement(node) {
2592
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
2593
- }
2594
- class DOMPoint {
2595
- constructor(node, offset) {
2596
- this.node = node;
2597
- this.offset = offset;
2598
- this.pos = -1;
2599
- }
2600
- }
2601
-
2602
2499
  class DocView extends ContentView {
2603
2500
  constructor(view) {
2604
2501
  super();
2605
2502
  this.view = view;
2606
- this.compositionDeco = Decoration.none;
2607
- this.compositionNode = null;
2608
2503
  this.decorations = [];
2609
2504
  this.dynamicDecorationMap = [];
2505
+ this.hasComposition = null;
2506
+ this.markedForComposition = new Set;
2610
2507
  // Track a minimum width for the editor. When measuring sizes in
2611
2508
  // measureVisibleLineHeights, this is updated to point at the width
2612
2509
  // of a given element and its extent in the document. When a change
@@ -2629,7 +2526,7 @@ class DocView extends ContentView {
2629
2526
  this.children = [new LineView];
2630
2527
  this.children[0].setParent(this);
2631
2528
  this.updateDeco();
2632
- 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);
2633
2530
  }
2634
2531
  get length() { return this.view.state.doc.length; }
2635
2532
  // Update the document view to a given state.
@@ -2644,24 +2541,30 @@ class DocView extends ContentView {
2644
2541
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2645
2542
  }
2646
2543
  }
2647
- ({ deco: this.compositionDeco, node: this.compositionNode } =
2648
- this.view.inputState.composing < 0 ? noComp : 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
+ let { from, to } = this.hasComposition;
2548
+ changedRanges = new ChangedRange(from, to, update.changes.mapPos(from, -1), update.changes.mapPos(to, 1))
2549
+ .addToSet(changedRanges.slice());
2550
+ }
2551
+ this.hasComposition = composition ? { from: composition.range.fromB, to: composition.range.toB } : null;
2649
2552
  // When the DOM nodes around the selection are moved to another
2650
2553
  // parent, Chrome sometimes reports a different selection through
2651
2554
  // getSelection than the one that it actually shows to the user.
2652
2555
  // This forces a selection update when lines are joined to work
2653
2556
  // around that. Issue #54
2654
- if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
2557
+ if ((browser.ie || browser.chrome) && !composition && update &&
2655
2558
  update.state.doc.lines != update.startState.doc.lines)
2656
2559
  this.forceSelection = true;
2657
2560
  let prevDeco = this.decorations, deco = this.updateDeco();
2658
2561
  let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
2659
2562
  changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
2660
- if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
2563
+ if (!(this.flags & 7 /* Dirty */) && changedRanges.length == 0) {
2661
2564
  return false;
2662
2565
  }
2663
2566
  else {
2664
- this.updateInner(changedRanges, update.startState.doc.length);
2567
+ this.updateInner(changedRanges, update.startState.doc.length, composition);
2665
2568
  if (update.transactions.length)
2666
2569
  this.lastUpdate = Date.now();
2667
2570
  return true;
@@ -2669,9 +2572,9 @@ class DocView extends ContentView {
2669
2572
  }
2670
2573
  // Used by update and the constructor do perform the actual DOM
2671
2574
  // update
2672
- updateInner(changes, oldLength) {
2575
+ updateInner(changes, oldLength, composition) {
2673
2576
  this.view.viewState.mustMeasureContent = true;
2674
- this.updateChildren(changes, oldLength);
2577
+ this.updateChildren(changes, oldLength, composition);
2675
2578
  let { observer } = this.view;
2676
2579
  observer.ignore(() => {
2677
2580
  // Lock the height during redrawing, since Chrome sometimes
@@ -2686,11 +2589,12 @@ class DocView extends ContentView {
2686
2589
  // to detect that situation.
2687
2590
  let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2688
2591
  this.sync(this.view, track);
2689
- this.dirty = 0 /* Not */;
2592
+ this.flags &= ~7 /* Dirty */;
2690
2593
  if (track && (track.written || observer.selectionRange.focusNode != track.node))
2691
2594
  this.forceSelection = true;
2692
2595
  this.dom.style.height = "";
2693
2596
  });
2597
+ this.markedForComposition.forEach(cView => cView.flags &= ~8 /* Composition */);
2694
2598
  let gaps = [];
2695
2599
  if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2696
2600
  for (let child of this.children)
@@ -2698,18 +2602,71 @@ class DocView extends ContentView {
2698
2602
  gaps.push(child.dom);
2699
2603
  observer.updateGaps(gaps);
2700
2604
  }
2701
- updateChildren(changes, oldLength) {
2605
+ updateChildren(changes, oldLength, composition) {
2606
+ let ranges = composition ? composition.range.addToSet(changes.slice()) : changes;
2702
2607
  let cursor = this.childCursor(oldLength);
2703
- for (let i = changes.length - 1;; i--) {
2704
- let next = i >= 0 ? changes[i] : null;
2608
+ for (let i = ranges.length - 1;; i--) {
2609
+ let next = i >= 0 ? ranges[i] : null;
2705
2610
  if (!next)
2706
2611
  break;
2707
- let { fromA, toA, fromB, toB } = next;
2708
- let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap);
2612
+ let { fromA, toA, fromB, toB } = next, content, breakAtStart, openStart, openEnd;
2613
+ if (composition && composition.range.fromB < toB && composition.range.toB > fromB) {
2614
+ let before = ContentBuilder.build(this.view.state.doc, fromB, composition.range.fromB, this.decorations, this.dynamicDecorationMap);
2615
+ let after = ContentBuilder.build(this.view.state.doc, composition.range.toB, toB, this.decorations, this.dynamicDecorationMap);
2616
+ breakAtStart = before.breakAtStart;
2617
+ openStart = before.openStart;
2618
+ openEnd = after.openEnd;
2619
+ let compLine = this.compositionView(composition);
2620
+ if (after.content.length) {
2621
+ compLine.breakAfter = after.content[0].breakAfter;
2622
+ if (compLine.merge(compLine.length, compLine.length, after.content[0], false, after.openStart, 0))
2623
+ after.content.shift();
2624
+ }
2625
+ if (before.content.length) {
2626
+ if (compLine.merge(0, 0, before.content[before.content.length - 1], true, 0, before.openEnd))
2627
+ before.content.pop();
2628
+ }
2629
+ content = before.content.concat(compLine).concat(after.content);
2630
+ }
2631
+ else {
2632
+ ({ content, breakAtStart, openStart, openEnd } =
2633
+ ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap));
2634
+ }
2709
2635
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2710
2636
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2711
2637
  replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2712
2638
  }
2639
+ if (composition)
2640
+ this.fixCompositionDOM(composition);
2641
+ }
2642
+ compositionView(composition) {
2643
+ let cur = new TextView(composition.text.nodeValue);
2644
+ cur.flags |= 8 /* Composition */;
2645
+ for (let { deco } of composition.marks)
2646
+ cur = new MarkView(deco, [cur], cur.length);
2647
+ let line = new LineView;
2648
+ line.append(cur, 0);
2649
+ return line;
2650
+ }
2651
+ fixCompositionDOM(composition) {
2652
+ let fix = (dom, cView) => {
2653
+ cView.flags |= 8 /* Composition */;
2654
+ this.markedForComposition.add(cView);
2655
+ let prev = ContentView.get(dom);
2656
+ if (prev != cView) {
2657
+ if (prev)
2658
+ prev.dom = null;
2659
+ cView.setDOM(dom);
2660
+ }
2661
+ };
2662
+ let pos = this.childPos(composition.range.fromB, 1);
2663
+ let cView = this.children[pos.i];
2664
+ fix(composition.line, cView);
2665
+ for (let i = composition.marks.length - 1; i >= -1; i--) {
2666
+ pos = cView.childPos(pos.off, 1);
2667
+ cView = cView.children[pos.i];
2668
+ fix(i >= 0 ? composition.marks[i].node : composition.text, cView);
2669
+ }
2713
2670
  }
2714
2671
  // Sync the DOM selection to this.state.selection
2715
2672
  updateSelection(mustRead = false, fromPointer = false) {
@@ -2728,7 +2685,7 @@ class DocView extends ContentView {
2728
2685
  let head = main.empty ? anchor : this.domAtPos(main.head);
2729
2686
  // Always reset on Firefox when next to an uneditable node to
2730
2687
  // avoid invisible cursor bugs (#111)
2731
- if (browser.gecko && main.empty && !this.compositionDeco.size && betweenUneditable(anchor)) {
2688
+ if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
2732
2689
  let dummy = document.createTextNode("");
2733
2690
  this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null));
2734
2691
  anchor = head = new DOMPos(dummy, 0);
@@ -2800,7 +2757,7 @@ class DocView extends ContentView {
2800
2757
  this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
2801
2758
  }
2802
2759
  enforceCursorAssoc() {
2803
- if (this.compositionDeco.size)
2760
+ if (this.hasComposition)
2804
2761
  return;
2805
2762
  let { view } = this, cursor = view.state.selection.main;
2806
2763
  let sel = getSelection(view.root);
@@ -2910,6 +2867,7 @@ class DocView extends ContentView {
2910
2867
  let dummy = document.createElement("div"), lineHeight, charWidth, textHeight;
2911
2868
  dummy.className = "cm-line";
2912
2869
  dummy.style.width = "99999px";
2870
+ dummy.style.position = "absolute";
2913
2871
  dummy.textContent = "abc def ghi jkl mno pqr stu";
2914
2872
  this.view.observer.ignore(() => {
2915
2873
  this.dom.appendChild(dummy);
@@ -2959,7 +2917,6 @@ class DocView extends ContentView {
2959
2917
  this.dynamicDecorationMap[i] = false;
2960
2918
  return this.decorations = [
2961
2919
  ...allDeco,
2962
- this.compositionDeco,
2963
2920
  this.computeBlockGapDeco(),
2964
2921
  this.view.viewState.lineGapDeco
2965
2922
  ];
@@ -3002,83 +2959,86 @@ class BlockGapWidget extends WidgetType {
3002
2959
  }
3003
2960
  get estimatedHeight() { return this.height; }
3004
2961
  }
3005
- function compositionSurroundingNode(view) {
2962
+ function findCompositionNode(view) {
3006
2963
  let sel = view.observer.selectionRange;
3007
2964
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
3008
2965
  if (!textNode)
3009
2966
  return null;
3010
- let cView = view.docView.nearest(textNode);
3011
- if (!cView)
3012
- return null;
3013
- if (cView instanceof LineView) {
3014
- let topNode = textNode;
3015
- while (topNode.parentNode != cView.dom)
3016
- topNode = topNode.parentNode;
3017
- let prev = topNode.previousSibling;
3018
- while (prev && !ContentView.get(prev))
3019
- prev = prev.previousSibling;
3020
- let pos = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
3021
- return { from: pos, to: pos, node: topNode, text: textNode };
2967
+ let cView = ContentView.get(textNode);
2968
+ let from, to;
2969
+ if (cView instanceof TextView) {
2970
+ from = cView.posAtStart;
2971
+ to = from + cView.length;
3022
2972
  }
3023
2973
  else {
3024
- for (;;) {
3025
- let { parent } = cView;
3026
- if (!parent)
2974
+ up: for (let offset = 0, node = textNode;;) {
2975
+ for (let sibling = node.previousSibling, cView; sibling; sibling = sibling.previousSibling) {
2976
+ if (cView = ContentView.get(sibling)) {
2977
+ from = to = cView.posAtEnd + offset;
2978
+ break up;
2979
+ }
2980
+ let reader = new DOMReader([], view.state);
2981
+ reader.readNode(sibling);
2982
+ if (reader.text.indexOf(LineBreakPlaceholder) > -1)
2983
+ return null;
2984
+ offset += reader.text.length;
2985
+ }
2986
+ node = node.parentNode;
2987
+ if (!node)
3027
2988
  return null;
3028
- if (parent instanceof LineView)
2989
+ let parentView = ContentView.get(node);
2990
+ if (parentView) {
2991
+ from = to = parentView.posAtStart + offset;
3029
2992
  break;
3030
- cView = parent;
3031
- }
3032
- let from = cView.posAtStart;
3033
- return { from, to: from + cView.length, node: cView.dom, text: textNode };
3034
- }
3035
- }
3036
- const noComp = { deco: Decoration.none, node: null };
3037
- function computeCompositionDeco(view, changes) {
3038
- let surrounding = compositionSurroundingNode(view);
3039
- if (!surrounding)
3040
- return noComp;
3041
- let { from, to, node, text: textNode } = surrounding;
3042
- let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
3043
- let { state } = view, reader = new DOMReader([], state);
3044
- if (node.nodeType == 3)
3045
- reader.readTextNode(node);
3046
- else
3047
- reader.readRange(node.firstChild, null);
3048
- let { text } = reader;
3049
- if (text.indexOf(LineBreakPlaceholder) > -1)
3050
- return noComp; // Don't try to preserve multi-line compositions
3051
- if (newTo - newFrom < text.length) {
3052
- if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
3053
- newTo = newFrom + text.length;
3054
- else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo) == text)
3055
- newFrom = newTo - text.length;
2993
+ }
2994
+ }
2995
+ }
2996
+ let { main } = view.state.selection;
2997
+ return from > main.head || to < main.head ? null : { from, to, node: textNode };
2998
+ }
2999
+ function findCompositionRange(view, changes) {
3000
+ let found = findCompositionNode(view);
3001
+ if (!found)
3002
+ return null;
3003
+ let { from: fromA, to: toA, node: textNode } = found;
3004
+ let fromB = changes.mapPos(fromA, -1), toB = changes.mapPos(toA, 1);
3005
+ let text = textNode.nodeValue;
3006
+ // Don't try to preserve multi-line compositions
3007
+ if (/[\n\r]/.test(text))
3008
+ return null;
3009
+ if (toB - fromB != text.length) {
3010
+ // If there is a length mismatch, see if mapping non-inclusively helps
3011
+ let fromB2 = changes.mapPos(fromA, 1), toB2 = changes.mapPos(toA, -1);
3012
+ if (toB2 - fromB2 == text.length)
3013
+ fromB = fromB2, toB = toB2;
3014
+ // See if we can find an instance of the text at either side
3015
+ else if (view.state.doc.sliceString(toB - text.length, toB) == text)
3016
+ fromB = toB - text.length;
3017
+ else if (view.state.doc.sliceString(fromB, fromB + text.length) == text)
3018
+ toB = fromB + text.length;
3019
+ // Not found
3056
3020
  else
3057
- return noComp;
3058
- }
3059
- else if (state.doc.sliceString(newFrom, newTo) != text) {
3060
- return noComp;
3061
- }
3062
- let topView = ContentView.get(node);
3063
- if (topView instanceof CompositionView)
3064
- topView = topView.widget.topView;
3065
- else if (topView)
3066
- topView.parent = null;
3067
- let deco = Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
3068
- .range(newFrom, newTo));
3069
- return { deco, node };
3070
- }
3071
- class CompositionWidget extends WidgetType {
3072
- constructor(top, text, topView) {
3073
- super();
3074
- this.top = top;
3075
- this.text = text;
3076
- this.topView = topView;
3021
+ return null;
3022
+ }
3023
+ if (view.state.doc.sliceString(fromB, toB) != text)
3024
+ return null;
3025
+ let marks = [];
3026
+ let range = new ChangedRange(fromA, toA, fromB, toB);
3027
+ for (let parent = textNode.parentNode;; parent = parent.parentNode) {
3028
+ let parentView = ContentView.get(parent);
3029
+ if (parentView instanceof MarkView)
3030
+ marks.push({ node: parent, deco: parentView.mark });
3031
+ else if (parentView instanceof LineView || parent.nodeName == "DIV" && parent.parentNode == view.contentDOM)
3032
+ return { range, text: textNode, marks, line: parent };
3033
+ else if (parent != view.contentDOM)
3034
+ marks.push({ node: parent, deco: new MarkDecoration({
3035
+ inclusive: true,
3036
+ attributes: getAttrs(parent),
3037
+ tagName: parent.tagName.toLowerCase()
3038
+ }) });
3039
+ else
3040
+ return null;
3077
3041
  }
3078
- eq(other) { return this.top == other.top && this.text == other.text; }
3079
- toDOM() { return this.top; }
3080
- ignoreEvent() { return false; }
3081
- get customView() { return CompositionView; }
3082
3042
  }
3083
3043
  function nearbyTextNode(startNode, startOffset, side) {
3084
3044
  if (side <= 0)
@@ -3739,9 +3699,13 @@ const dragScrollMargin = 6;
3739
3699
  function dragScrollSpeed(dist) {
3740
3700
  return Math.max(0, dist) * 0.7 + 8;
3741
3701
  }
3702
+ function dist(a, b) {
3703
+ return Math.max(Math.abs(a.clientX - b.clientX), Math.abs(a.clientY - b.clientY));
3704
+ }
3742
3705
  class MouseSelection {
3743
3706
  constructor(view, startEvent, style, mustSelect) {
3744
3707
  this.view = view;
3708
+ this.startEvent = startEvent;
3745
3709
  this.style = style;
3746
3710
  this.mustSelect = mustSelect;
3747
3711
  this.scrollSpeed = { x: 0, y: 0 };
@@ -3768,7 +3732,7 @@ class MouseSelection {
3768
3732
  var _a;
3769
3733
  if (event.buttons == 0)
3770
3734
  return this.destroy();
3771
- if (this.dragging !== false)
3735
+ if (this.dragging || this.dragging == null && dist(this.startEvent, event) < 10)
3772
3736
  return;
3773
3737
  this.select(this.lastEvent = event);
3774
3738
  let sx = 0, sy = 0;
@@ -3847,7 +3811,7 @@ class MouseSelection {
3847
3811
  select(event) {
3848
3812
  let { view } = this, selection = this.skipAtoms(this.style.get(event, this.extend, this.multiple));
3849
3813
  if (this.mustSelect || !selection.eq(view.state.selection) ||
3850
- selection.main.assoc != view.state.selection.main.assoc)
3814
+ selection.main.assoc != view.state.selection.main.assoc && this.dragging === false)
3851
3815
  this.view.dispatch({
3852
3816
  selection,
3853
3817
  userEvent: "select.pointer"
@@ -4266,7 +4230,7 @@ handlers.compositionend = view => {
4266
4230
  // Otherwise, make sure that, if no changes come in soon, the
4267
4231
  // composition view is cleared.
4268
4232
  setTimeout(() => {
4269
- if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
4233
+ if (view.inputState.composing < 0 && view.docView.hasComposition)
4270
4234
  view.update([]);
4271
4235
  }, 50);
4272
4236
  }
@@ -5983,7 +5947,7 @@ function applyDOMChange(view, domChange) {
5983
5947
  if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
5984
5948
  change.to <= sel.to && change.to >= sel.to - 10) {
5985
5949
  let replaced = view.state.sliceDoc(change.from, change.to);
5986
- let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
5950
+ let composition = findCompositionNode(view) || view.state.doc.lineAt(sel.head);
5987
5951
  let offset = sel.to - change.to, size = sel.to - sel.from;
5988
5952
  tr = startState.changeByRange(range => {
5989
5953
  if (range.from == sel.from && range.to == sel.to)
@@ -5994,7 +5958,7 @@ function applyDOMChange(view, domChange) {
5994
5958
  // changes in the same node work without aborting
5995
5959
  // composition, so cursors in the composition range are
5996
5960
  // ignored.
5997
- compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
5961
+ composition && range.to >= composition.from && range.from <= composition.to)
5998
5962
  return { range };
5999
5963
  let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
6000
5964
  return {
@@ -6455,7 +6419,7 @@ class DOMObserver {
6455
6419
  return null;
6456
6420
  cView.markDirty(rec.type == "attributes");
6457
6421
  if (rec.type == "attributes")
6458
- cView.dirty |= 4 /* Attrs */;
6422
+ cView.flags |= 4 /* AttrsDirty */;
6459
6423
  if (rec.type == "childList") {
6460
6424
  let childBefore = findChild(cView, rec.previousSibling || rec.target.previousSibling, -1);
6461
6425
  let childAfter = findChild(cView, rec.nextSibling || rec.target.nextSibling, 1);
@@ -7629,7 +7593,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7629
7593
  else if (current != is)
7630
7594
  throw new Error("Key binding " + name + " is used both as a regular binding and as a multi-stroke prefix");
7631
7595
  };
7632
- let add = (scope, key, command, preventDefault) => {
7596
+ let add = (scope, key, command, preventDefault, stopPropagation) => {
7633
7597
  var _a, _b;
7634
7598
  let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
7635
7599
  let parts = key.split(/ (?!$)/).map(k => normalizeKeyName(k, platform));
@@ -7639,6 +7603,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7639
7603
  if (!scopeObj[prefix])
7640
7604
  scopeObj[prefix] = {
7641
7605
  preventDefault: true,
7606
+ stopPropagation: false,
7642
7607
  run: [(view) => {
7643
7608
  let ourObj = storedPrefix = { view, prefix, scope };
7644
7609
  setTimeout(() => { if (storedPrefix == ourObj)
@@ -7649,11 +7614,17 @@ function buildKeymap(bindings, platform = currentPlatform) {
7649
7614
  }
7650
7615
  let full = parts.join(" ");
7651
7616
  checkPrefix(full, false);
7652
- 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()) || [] });
7617
+ let binding = scopeObj[full] || (scopeObj[full] = {
7618
+ preventDefault: false,
7619
+ stopPropagation: false,
7620
+ run: ((_b = (_a = scopeObj._any) === null || _a === void 0 ? void 0 : _a.run) === null || _b === void 0 ? void 0 : _b.slice()) || []
7621
+ });
7653
7622
  if (command)
7654
7623
  binding.run.push(command);
7655
7624
  if (preventDefault)
7656
7625
  binding.preventDefault = true;
7626
+ if (stopPropagation)
7627
+ binding.stopPropagation = true;
7657
7628
  };
7658
7629
  for (let b of bindings) {
7659
7630
  let scopes = b.scope ? b.scope.split(" ") : ["editor"];
@@ -7661,7 +7632,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7661
7632
  for (let scope of scopes) {
7662
7633
  let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
7663
7634
  if (!scopeObj._any)
7664
- scopeObj._any = { preventDefault: false, run: [] };
7635
+ scopeObj._any = { preventDefault: false, stopPropagation: false, run: [] };
7665
7636
  for (let key in scopeObj)
7666
7637
  scopeObj[key].run.push(b.any);
7667
7638
  }
@@ -7669,9 +7640,9 @@ function buildKeymap(bindings, platform = currentPlatform) {
7669
7640
  if (!name)
7670
7641
  continue;
7671
7642
  for (let scope of scopes) {
7672
- add(scope, name, b.run, b.preventDefault);
7643
+ add(scope, name, b.run, b.preventDefault, b.stopPropagation);
7673
7644
  if (b.shift)
7674
- add(scope, "Shift-" + name, b.shift, b.preventDefault);
7645
+ add(scope, "Shift-" + name, b.shift, b.preventDefault, b.stopPropagation);
7675
7646
  }
7676
7647
  }
7677
7648
  return bound;
@@ -7679,11 +7650,13 @@ function buildKeymap(bindings, platform = currentPlatform) {
7679
7650
  function runHandlers(map, event, view, scope) {
7680
7651
  let name = w3cKeyname.keyName(event);
7681
7652
  let charCode = state.codePointAt(name, 0), isChar = state.codePointSize(charCode) == name.length && name != " ";
7682
- let prefix = "", fallthrough = false;
7653
+ let prefix = "", handled = false, prevented = false, stopPropagation = false;
7683
7654
  if (storedPrefix && storedPrefix.view == view && storedPrefix.scope == scope) {
7684
7655
  prefix = storedPrefix.prefix + " ";
7685
- if (fallthrough = modifierCodes.indexOf(event.keyCode) < 0)
7656
+ if (modifierCodes.indexOf(event.keyCode) < 0) {
7657
+ prevented = true;
7686
7658
  storedPrefix = null;
7659
+ }
7687
7660
  }
7688
7661
  let ran = new Set;
7689
7662
  let runFor = (binding) => {
@@ -7691,36 +7664,49 @@ function runHandlers(map, event, view, scope) {
7691
7664
  for (let cmd of binding.run)
7692
7665
  if (!ran.has(cmd)) {
7693
7666
  ran.add(cmd);
7694
- if (cmd(view, event))
7667
+ if (cmd(view, event)) {
7668
+ if (binding.stopPropagation)
7669
+ stopPropagation = true;
7695
7670
  return true;
7671
+ }
7696
7672
  }
7697
- if (binding.preventDefault)
7698
- fallthrough = true;
7673
+ if (binding.preventDefault) {
7674
+ if (binding.stopPropagation)
7675
+ stopPropagation = true;
7676
+ prevented = true;
7677
+ }
7699
7678
  }
7700
7679
  return false;
7701
7680
  };
7702
7681
  let scopeObj = map[scope], baseName, shiftName;
7703
7682
  if (scopeObj) {
7704
- if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)]))
7705
- return true;
7706
- if (isChar && (event.altKey || event.metaKey || event.ctrlKey) &&
7683
+ if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)])) {
7684
+ handled = true;
7685
+ }
7686
+ else if (isChar && (event.altKey || event.metaKey || event.ctrlKey) &&
7707
7687
  // Ctrl-Alt may be used for AltGr on Windows
7708
7688
  !(browser.windows && event.ctrlKey && event.altKey) &&
7709
7689
  (baseName = w3cKeyname.base[event.keyCode]) && baseName != name) {
7710
- if (runFor(scopeObj[prefix + modifiers(baseName, event, true)]))
7711
- return true;
7690
+ if (runFor(scopeObj[prefix + modifiers(baseName, event, true)])) {
7691
+ handled = true;
7692
+ }
7712
7693
  else if (event.shiftKey && (shiftName = w3cKeyname.shift[event.keyCode]) != name && shiftName != baseName &&
7713
- runFor(scopeObj[prefix + modifiers(shiftName, event, false)]))
7714
- return true;
7694
+ runFor(scopeObj[prefix + modifiers(shiftName, event, false)])) {
7695
+ handled = true;
7696
+ }
7715
7697
  }
7716
- else if (isChar && event.shiftKey) {
7717
- if (runFor(scopeObj[prefix + modifiers(name, event, true)]))
7718
- return true;
7698
+ else if (isChar && event.shiftKey &&
7699
+ runFor(scopeObj[prefix + modifiers(name, event, true)])) {
7700
+ handled = true;
7719
7701
  }
7720
- if (runFor(scopeObj._any))
7721
- return true;
7702
+ if (!handled && runFor(scopeObj._any))
7703
+ handled = true;
7722
7704
  }
7723
- return fallthrough;
7705
+ if (prevented)
7706
+ handled = true;
7707
+ if (handled && stopPropagation)
7708
+ event.stopPropagation();
7709
+ return handled;
7724
7710
  }
7725
7711
 
7726
7712
  /**
@@ -8507,7 +8493,9 @@ function placeholder(content) {
8507
8493
  return ViewPlugin.fromClass(class {
8508
8494
  constructor(view) {
8509
8495
  this.view = view;
8510
- this.placeholder = Decoration.set([Decoration.widget({ widget: new Placeholder(content), side: 1 }).range(0)]);
8496
+ this.placeholder = content
8497
+ ? Decoration.set([Decoration.widget({ widget: new Placeholder(content), side: 1 }).range(0)])
8498
+ : Decoration.none;
8511
8499
  }
8512
8500
  get decorations() { return this.view.state.doc.length ? Decoration.none : this.placeholder; }
8513
8501
  }, { decorations: v => v.decorations });