@codemirror/view 6.14.1 → 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.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, EditorState, findColumn, CharCategory, Annotation, Prec, Transaction, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
1
+ import { EditorState, Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, findColumn, CharCategory, Annotation, Prec, Transaction, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
2
2
  import { StyleModule } from 'style-mod';
3
3
  import { keyName, base, shift } from 'w3c-keyname';
4
4
 
@@ -96,13 +96,15 @@ function windowRect(win) {
96
96
  }
97
97
  function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
98
98
  let doc = dom.ownerDocument, win = doc.defaultView || window;
99
- for (let cur = dom; cur;) {
99
+ for (let cur = dom, stop = false; cur && !stop;) {
100
100
  if (cur.nodeType == 1) { // Element
101
101
  let bounding, top = cur == doc.body;
102
102
  if (top) {
103
103
  bounding = windowRect(win);
104
104
  }
105
105
  else {
106
+ if (/^(fixed|sticky)$/.test(getComputedStyle(cur).position))
107
+ stop = true;
106
108
  if (cur.scrollHeight <= cur.clientHeight && cur.scrollWidth <= cur.clientWidth) {
107
109
  cur = cur.assignedSlot || cur.parentNode;
108
110
  continue;
@@ -334,7 +336,7 @@ class ContentView {
334
336
  constructor() {
335
337
  this.parent = null;
336
338
  this.dom = null;
337
- this.dirty = 2 /* Node */;
339
+ this.flags = 2 /* NodeDirty */;
338
340
  }
339
341
  get overrideDOMText() { return null; }
340
342
  get posAtStart() {
@@ -356,18 +358,18 @@ class ContentView {
356
358
  return this.posBefore(view) + view.length;
357
359
  }
358
360
  sync(view, track) {
359
- if (this.dirty & 2 /* Node */) {
361
+ if (this.flags & 2 /* NodeDirty */) {
360
362
  let parent = this.dom;
361
363
  let prev = null, next;
362
364
  for (let child of this.children) {
363
- if (child.dirty) {
364
- if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild) && next != view.docView.compositionNode) {
365
+ if (child.flags & 7 /* Dirty */) {
366
+ if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
365
367
  let contentView = ContentView.get(next);
366
368
  if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
367
369
  child.reuseDOM(next);
368
370
  }
369
371
  child.sync(view, track);
370
- child.dirty = 0 /* Not */;
372
+ child.flags &= ~7 /* Dirty */;
371
373
  }
372
374
  next = prev ? prev.nextSibling : parent.firstChild;
373
375
  if (track && !track.written && track.node == parent && next != child.dom)
@@ -387,11 +389,11 @@ class ContentView {
387
389
  while (next)
388
390
  next = rm$1(next);
389
391
  }
390
- else if (this.dirty & 1 /* Child */) {
392
+ else if (this.flags & 1 /* ChildDirty */) {
391
393
  for (let child of this.children)
392
- if (child.dirty) {
394
+ if (child.flags & 7 /* Dirty */) {
393
395
  child.sync(view, track);
394
- child.dirty = 0 /* Not */;
396
+ child.flags &= ~7 /* Dirty */;
395
397
  }
396
398
  }
397
399
  }
@@ -456,23 +458,23 @@ class ContentView {
456
458
  endDOM: toI < this.children.length && toI >= 0 ? this.children[toI].dom : null };
457
459
  }
458
460
  markDirty(andParent = false) {
459
- this.dirty |= 2 /* Node */;
461
+ this.flags |= 2 /* NodeDirty */;
460
462
  this.markParentsDirty(andParent);
461
463
  }
462
464
  markParentsDirty(childList) {
463
465
  for (let parent = this.parent; parent; parent = parent.parent) {
464
466
  if (childList)
465
- parent.dirty |= 2 /* Node */;
466
- if (parent.dirty & 1 /* Child */)
467
+ parent.flags |= 2 /* NodeDirty */;
468
+ if (parent.flags & 1 /* ChildDirty */)
467
469
  return;
468
- parent.dirty |= 1 /* Child */;
470
+ parent.flags |= 1 /* ChildDirty */;
469
471
  childList = false;
470
472
  }
471
473
  }
472
474
  setParent(parent) {
473
475
  if (this.parent != parent) {
474
476
  this.parent = parent;
475
- if (this.dirty)
477
+ if (this.flags & 7 /* Dirty */)
476
478
  this.markParentsDirty(true);
477
479
  }
478
480
  }
@@ -523,7 +525,9 @@ class ContentView {
523
525
  return false;
524
526
  }
525
527
  become(other) { return false; }
526
- canReuseDOM(other) { return other.constructor == this.constructor; }
528
+ canReuseDOM(other) {
529
+ return other.constructor == this.constructor && !((this.flags | other.flags) & 8 /* Composition */);
530
+ }
527
531
  // When this is a zero-length view with a side, this should return a
528
532
  // number <= 0 to indicate it is before its position, or a
529
533
  // number > 0 when after its position.
@@ -647,6 +651,113 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
647
651
  replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
648
652
  }
649
653
 
654
+ const LineBreakPlaceholder = "\uffff";
655
+ class DOMReader {
656
+ constructor(points, state) {
657
+ this.points = points;
658
+ this.text = "";
659
+ this.lineSeparator = state.facet(EditorState.lineSeparator);
660
+ }
661
+ append(text) {
662
+ this.text += text;
663
+ }
664
+ lineBreak() {
665
+ this.text += LineBreakPlaceholder;
666
+ }
667
+ readRange(start, end) {
668
+ if (!start)
669
+ return this;
670
+ let parent = start.parentNode;
671
+ for (let cur = start;;) {
672
+ this.findPointBefore(parent, cur);
673
+ let oldLen = this.text.length;
674
+ this.readNode(cur);
675
+ let next = cur.nextSibling;
676
+ if (next == end)
677
+ break;
678
+ let view = ContentView.get(cur), nextView = ContentView.get(next);
679
+ if (view && nextView ? view.breakAfter :
680
+ (view ? view.breakAfter : isBlockElement(cur)) ||
681
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
682
+ this.lineBreak();
683
+ cur = next;
684
+ }
685
+ this.findPointBefore(parent, end);
686
+ return this;
687
+ }
688
+ readTextNode(node) {
689
+ let text = node.nodeValue;
690
+ for (let point of this.points)
691
+ if (point.node == node)
692
+ point.pos = this.text.length + Math.min(point.offset, text.length);
693
+ for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
694
+ let nextBreak = -1, breakSize = 1, m;
695
+ if (this.lineSeparator) {
696
+ nextBreak = text.indexOf(this.lineSeparator, off);
697
+ breakSize = this.lineSeparator.length;
698
+ }
699
+ else if (m = re.exec(text)) {
700
+ nextBreak = m.index;
701
+ breakSize = m[0].length;
702
+ }
703
+ this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
704
+ if (nextBreak < 0)
705
+ break;
706
+ this.lineBreak();
707
+ if (breakSize > 1)
708
+ for (let point of this.points)
709
+ if (point.node == node && point.pos > this.text.length)
710
+ point.pos -= breakSize - 1;
711
+ off = nextBreak + breakSize;
712
+ }
713
+ }
714
+ readNode(node) {
715
+ if (node.cmIgnore)
716
+ return;
717
+ let view = ContentView.get(node);
718
+ let fromView = view && view.overrideDOMText;
719
+ if (fromView != null) {
720
+ this.findPointInside(node, fromView.length);
721
+ for (let i = fromView.iter(); !i.next().done;) {
722
+ if (i.lineBreak)
723
+ this.lineBreak();
724
+ else
725
+ this.append(i.value);
726
+ }
727
+ }
728
+ else if (node.nodeType == 3) {
729
+ this.readTextNode(node);
730
+ }
731
+ else if (node.nodeName == "BR") {
732
+ if (node.nextSibling)
733
+ this.lineBreak();
734
+ }
735
+ else if (node.nodeType == 1) {
736
+ this.readRange(node.firstChild, null);
737
+ }
738
+ }
739
+ findPointBefore(node, next) {
740
+ for (let point of this.points)
741
+ if (point.node == node && node.childNodes[point.offset] == next)
742
+ point.pos = this.text.length;
743
+ }
744
+ findPointInside(node, maxLen) {
745
+ for (let point of this.points)
746
+ if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
747
+ point.pos = this.text.length + Math.min(maxLen, point.offset);
748
+ }
749
+ }
750
+ function isBlockElement(node) {
751
+ return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
752
+ }
753
+ class DOMPoint {
754
+ constructor(node, offset) {
755
+ this.node = node;
756
+ this.offset = offset;
757
+ this.pos = -1;
758
+ }
759
+ }
760
+
650
761
  let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
651
762
  let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
652
763
  const ie_edge = /*@__PURE__*//Edge\/(\d+)/.exec(nav.userAgent);
@@ -700,7 +811,10 @@ class TextView extends ContentView {
700
811
  this.createDOM(dom);
701
812
  }
702
813
  merge(from, to, source) {
703
- if (source && (!(source instanceof TextView) || this.length - (to - from) + source.length > MaxJoinLen))
814
+ if ((this.flags & 8 /* Composition */) ||
815
+ source && (!(source instanceof TextView) ||
816
+ this.length - (to - from) + source.length > MaxJoinLen ||
817
+ (source.flags & 8 /* Composition */)))
704
818
  return false;
705
819
  this.text = this.text.slice(0, from) + (source ? source.text : "") + this.text.slice(to);
706
820
  this.markDirty();
@@ -710,6 +824,7 @@ class TextView extends ContentView {
710
824
  let result = new TextView(this.text.slice(from));
711
825
  this.text = this.text.slice(0, from);
712
826
  this.markDirty();
827
+ result.flags |= this.flags & 8 /* Composition */;
713
828
  return result;
714
829
  }
715
830
  localPosFromDOM(node, offset) {
@@ -741,16 +856,19 @@ class MarkView extends ContentView {
741
856
  dom.setAttribute(name, this.mark.attrs[name]);
742
857
  return dom;
743
858
  }
859
+ canReuseDOM(other) {
860
+ return super.canReuseDOM(other) && !((this.flags | other.flags) & 8 /* Composition */);
861
+ }
744
862
  reuseDOM(node) {
745
863
  if (node.nodeName == this.mark.tagName.toUpperCase()) {
746
864
  this.setDOM(node);
747
- this.dirty |= 4 /* Attrs */ | 2 /* Node */;
865
+ this.flags |= 4 /* AttrsDirty */ | 2 /* NodeDirty */;
748
866
  }
749
867
  }
750
868
  sync(view, track) {
751
869
  if (!this.dom)
752
870
  this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
753
- else if (this.dirty & 4 /* Attrs */)
871
+ else if (this.flags & 4 /* AttrsDirty */)
754
872
  this.setAttrs(this.dom);
755
873
  super.sync(view, track);
756
874
  }
@@ -829,7 +947,7 @@ class WidgetView extends ContentView {
829
947
  this.prevWidget = null;
830
948
  }
831
949
  static create(widget, length, side) {
832
- return new (widget.customView || WidgetView)(widget, length, side);
950
+ return new WidgetView(widget, length, side);
833
951
  }
834
952
  split(from) {
835
953
  let result = WidgetView.create(this.widget, this.length - from, this.side);
@@ -907,129 +1025,6 @@ class WidgetView extends ContentView {
907
1025
  this.widget.destroy(this.dom);
908
1026
  }
909
1027
  }
910
- class CompositionView extends WidgetView {
911
- domAtPos(pos) {
912
- let { topView, text } = this.widget;
913
- if (!topView)
914
- return new DOMPos(text, Math.min(pos, text.nodeValue.length));
915
- 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)));
916
- }
917
- sync() { this.setDOM(this.widget.toDOM()); }
918
- localPosFromDOM(node, offset) {
919
- let { topView, text } = this.widget;
920
- if (!topView)
921
- return Math.min(offset, this.length);
922
- return posFromDOMInCompositionTree(node, offset, topView, text, this.length - topView.length);
923
- }
924
- ignoreMutation() { return false; }
925
- get overrideDOMText() { return null; }
926
- coordsAt(pos, side) {
927
- let { topView, text } = this.widget;
928
- if (!topView)
929
- return textCoords(text, pos, side);
930
- return scanCompositionTree(pos, side, topView, text, this.length - topView.length, (v, pos, side) => v.coordsAt(pos, side), (text, pos, side) => textCoords(text, pos, side));
931
- }
932
- destroy() {
933
- var _a;
934
- super.destroy();
935
- (_a = this.widget.topView) === null || _a === void 0 ? void 0 : _a.destroy();
936
- }
937
- get isEditable() { return true; }
938
- canReuseDOM() { return true; }
939
- }
940
- // Uses the old structure of a chunk of content view frozen for
941
- // composition to try and find a reasonable DOM location for the given
942
- // offset.
943
- function scanCompositionTree(pos, side, view, text, dLen, enterView, fromText) {
944
- if (view instanceof MarkView) {
945
- for (let child = view.dom.firstChild; child; child = child.nextSibling) {
946
- let desc = ContentView.get(child);
947
- if (!desc) {
948
- let inner = scanCompositionNode(pos, side, child, fromText);
949
- if (typeof inner != "number")
950
- return inner;
951
- pos = inner;
952
- }
953
- else {
954
- let hasComp = contains(child, text);
955
- let len = desc.length + (hasComp ? dLen : 0);
956
- if (pos < len || pos == len && desc.getSide() <= 0)
957
- return hasComp ? scanCompositionTree(pos, side, desc, text, dLen, enterView, fromText) : enterView(desc, pos, side);
958
- pos -= len;
959
- }
960
- }
961
- return enterView(view, view.length, -1);
962
- }
963
- else if (view.dom == text) {
964
- return fromText(text, pos, side);
965
- }
966
- else {
967
- return enterView(view, pos, side);
968
- }
969
- }
970
- function scanCompositionNode(pos, side, node, fromText) {
971
- if (node.nodeType == 3) {
972
- let len = node.nodeValue.length;
973
- if (pos <= len)
974
- return fromText(node, pos, side);
975
- pos -= len;
976
- }
977
- else if (node.nodeType == 1 && node.contentEditable != "false") {
978
- for (let child = node.firstChild; child; child = child.nextSibling) {
979
- let inner = scanCompositionNode(pos, side, child, fromText);
980
- if (typeof inner != "number")
981
- return inner;
982
- pos = inner;
983
- }
984
- }
985
- return pos;
986
- }
987
- function posFromDOMInCompositionTree(node, offset, view, text, dLen) {
988
- if (view instanceof MarkView) {
989
- let pos = 0;
990
- for (let child = view.dom.firstChild; child; child = child.nextSibling) {
991
- let childView = ContentView.get(child);
992
- if (childView) {
993
- let hasComp = contains(child, text);
994
- if (contains(child, node))
995
- return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, childView, text, dLen)
996
- : childView.localPosFromDOM(node, offset));
997
- pos += childView.length + (hasComp ? dLen : 0);
998
- }
999
- else {
1000
- let inner = posFromDOMInOpaqueNode(node, offset, child);
1001
- if (inner.result != null)
1002
- return pos + inner.result;
1003
- pos += inner.size;
1004
- }
1005
- }
1006
- }
1007
- else if (view.dom == text) {
1008
- return Math.min(offset, text.nodeValue.length);
1009
- }
1010
- return view.localPosFromDOM(node, offset);
1011
- }
1012
- function posFromDOMInOpaqueNode(node, offset, target) {
1013
- if (target.nodeType == 3) {
1014
- return node == target ? { result: offset } : { size: target.nodeValue.length };
1015
- }
1016
- else if (target.nodeType == 1 && target.contentEditable != "false") {
1017
- let pos = 0;
1018
- for (let child = target.firstChild, i = 0;; child = child.nextSibling, i++) {
1019
- if (node == target && i == offset)
1020
- return { result: pos };
1021
- if (!child)
1022
- return { size: pos };
1023
- let inner = posFromDOMInOpaqueNode(node, offset, child);
1024
- if (inner.result != null)
1025
- return { result: offset + inner.result };
1026
- pos += inner.size;
1027
- }
1028
- }
1029
- else {
1030
- return target.contains(node) ? { result: 0 } : { size: 0 };
1031
- }
1032
- }
1033
1028
  // These are drawn around uneditable widgets to avoid a number of
1034
1029
  // browser bugs that show up when the cursor is directly next to
1035
1030
  // uneditable inline content.
@@ -1149,16 +1144,20 @@ function combineAttrs(source, target) {
1149
1144
  }
1150
1145
  return target;
1151
1146
  }
1152
- function attrsEq(a, b) {
1147
+ const noAttrs = /*@__PURE__*/Object.create(null);
1148
+ function attrsEq(a, b, ignore) {
1153
1149
  if (a == b)
1154
1150
  return true;
1155
- if (!a || !b)
1156
- return false;
1151
+ if (!a)
1152
+ a = noAttrs;
1153
+ if (!b)
1154
+ b = noAttrs;
1157
1155
  let keysA = Object.keys(a), keysB = Object.keys(b);
1158
- if (keysA.length != keysB.length)
1156
+ if (keysA.length - (ignore && keysA.indexOf(ignore) > -1 ? 1 : 0) !=
1157
+ keysB.length - (ignore && keysB.indexOf(ignore) > -1 ? 1 : 0))
1159
1158
  return false;
1160
1159
  for (let key of keysA) {
1161
- if (keysB.indexOf(key) == -1 || a[key] !== b[key])
1160
+ if (key != ignore && (keysB.indexOf(key) == -1 || a[key] !== b[key]))
1162
1161
  return false;
1163
1162
  }
1164
1163
  return true;
@@ -1175,6 +1174,14 @@ function updateAttrs(dom, prev, attrs) {
1175
1174
  dom.setAttribute(changed = name, attrs[name]);
1176
1175
  return !!changed;
1177
1176
  }
1177
+ function getAttrs(dom) {
1178
+ let attrs = Object.create(null);
1179
+ for (let i = 0; i < dom.attributes.length; i++) {
1180
+ let attr = dom.attributes[i];
1181
+ attrs[attr.name] = attr.value;
1182
+ }
1183
+ return attrs;
1184
+ }
1178
1185
 
1179
1186
  /**
1180
1187
  Widgets added to the content are described by subclasses of this
@@ -1239,10 +1246,6 @@ class WidgetType {
1239
1246
  /**
1240
1247
  @internal
1241
1248
  */
1242
- get customView() { return null; }
1243
- /**
1244
- @internal
1245
- */
1246
1249
  get isHidden() { return false; }
1247
1250
  /**
1248
1251
  This is called when the an instance of the widget is removed
@@ -1380,11 +1383,12 @@ class MarkDecoration extends Decoration {
1380
1383
  this.attrs = spec.attributes || null;
1381
1384
  }
1382
1385
  eq(other) {
1386
+ var _a, _b;
1383
1387
  return this == other ||
1384
1388
  other instanceof MarkDecoration &&
1385
1389
  this.tagName == other.tagName &&
1386
- this.class == other.class &&
1387
- attrsEq(this.attrs, other.attrs);
1390
+ (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)) &&
1391
+ attrsEq(this.attrs, other.attrs, "class");
1388
1392
  }
1389
1393
  range(from, to = from) {
1390
1394
  if (from >= to)
@@ -1536,7 +1540,7 @@ class LineView extends ContentView {
1536
1540
  reuseDOM(node) {
1537
1541
  if (node.nodeName == "DIV") {
1538
1542
  this.setDOM(node);
1539
- this.dirty |= 4 /* Attrs */ | 2 /* Node */;
1543
+ this.flags |= 4 /* AttrsDirty */ | 2 /* NodeDirty */;
1540
1544
  }
1541
1545
  }
1542
1546
  sync(view, track) {
@@ -1546,7 +1550,7 @@ class LineView extends ContentView {
1546
1550
  this.dom.className = "cm-line";
1547
1551
  this.prevAttrs = this.attrs ? null : undefined;
1548
1552
  }
1549
- else if (this.dirty & 4 /* Attrs */) {
1553
+ else if (this.flags & 4 /* AttrsDirty */) {
1550
1554
  clearAttributes(this.dom);
1551
1555
  this.dom.className = "cm-line";
1552
1556
  this.prevAttrs = this.attrs ? null : undefined;
@@ -2486,121 +2490,14 @@ function moveVisually(line, order, dir, start, forward) {
2486
2490
  return EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
2487
2491
  }
2488
2492
 
2489
- const LineBreakPlaceholder = "\uffff";
2490
- class DOMReader {
2491
- constructor(points, state) {
2492
- this.points = points;
2493
- this.text = "";
2494
- this.lineSeparator = state.facet(EditorState.lineSeparator);
2495
- }
2496
- append(text) {
2497
- this.text += text;
2498
- }
2499
- lineBreak() {
2500
- this.text += LineBreakPlaceholder;
2501
- }
2502
- readRange(start, end) {
2503
- if (!start)
2504
- return this;
2505
- let parent = start.parentNode;
2506
- for (let cur = start;;) {
2507
- this.findPointBefore(parent, cur);
2508
- let oldLen = this.text.length;
2509
- this.readNode(cur);
2510
- let next = cur.nextSibling;
2511
- if (next == end)
2512
- break;
2513
- let view = ContentView.get(cur), nextView = ContentView.get(next);
2514
- if (view && nextView ? view.breakAfter :
2515
- (view ? view.breakAfter : isBlockElement(cur)) ||
2516
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
2517
- this.lineBreak();
2518
- cur = next;
2519
- }
2520
- this.findPointBefore(parent, end);
2521
- return this;
2522
- }
2523
- readTextNode(node) {
2524
- let text = node.nodeValue;
2525
- for (let point of this.points)
2526
- if (point.node == node)
2527
- point.pos = this.text.length + Math.min(point.offset, text.length);
2528
- for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
2529
- let nextBreak = -1, breakSize = 1, m;
2530
- if (this.lineSeparator) {
2531
- nextBreak = text.indexOf(this.lineSeparator, off);
2532
- breakSize = this.lineSeparator.length;
2533
- }
2534
- else if (m = re.exec(text)) {
2535
- nextBreak = m.index;
2536
- breakSize = m[0].length;
2537
- }
2538
- this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
2539
- if (nextBreak < 0)
2540
- break;
2541
- this.lineBreak();
2542
- if (breakSize > 1)
2543
- for (let point of this.points)
2544
- if (point.node == node && point.pos > this.text.length)
2545
- point.pos -= breakSize - 1;
2546
- off = nextBreak + breakSize;
2547
- }
2548
- }
2549
- readNode(node) {
2550
- if (node.cmIgnore)
2551
- return;
2552
- let view = ContentView.get(node);
2553
- let fromView = view && view.overrideDOMText;
2554
- if (fromView != null) {
2555
- this.findPointInside(node, fromView.length);
2556
- for (let i = fromView.iter(); !i.next().done;) {
2557
- if (i.lineBreak)
2558
- this.lineBreak();
2559
- else
2560
- this.append(i.value);
2561
- }
2562
- }
2563
- else if (node.nodeType == 3) {
2564
- this.readTextNode(node);
2565
- }
2566
- else if (node.nodeName == "BR") {
2567
- if (node.nextSibling)
2568
- this.lineBreak();
2569
- }
2570
- else if (node.nodeType == 1) {
2571
- this.readRange(node.firstChild, null);
2572
- }
2573
- }
2574
- findPointBefore(node, next) {
2575
- for (let point of this.points)
2576
- if (point.node == node && node.childNodes[point.offset] == next)
2577
- point.pos = this.text.length;
2578
- }
2579
- findPointInside(node, maxLen) {
2580
- for (let point of this.points)
2581
- if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
2582
- point.pos = this.text.length + Math.min(maxLen, point.offset);
2583
- }
2584
- }
2585
- function isBlockElement(node) {
2586
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
2587
- }
2588
- class DOMPoint {
2589
- constructor(node, offset) {
2590
- this.node = node;
2591
- this.offset = offset;
2592
- this.pos = -1;
2593
- }
2594
- }
2595
-
2596
2493
  class DocView extends ContentView {
2597
2494
  constructor(view) {
2598
2495
  super();
2599
2496
  this.view = view;
2600
- this.compositionDeco = Decoration.none;
2601
- this.compositionNode = null;
2602
2497
  this.decorations = [];
2603
2498
  this.dynamicDecorationMap = [];
2499
+ this.hasComposition = false;
2500
+ this.markedForComposition = new Set;
2604
2501
  // Track a minimum width for the editor. When measuring sizes in
2605
2502
  // measureVisibleLineHeights, this is updated to point at the width
2606
2503
  // of a given element and its extent in the document. When a change
@@ -2623,7 +2520,7 @@ class DocView extends ContentView {
2623
2520
  this.children = [new LineView];
2624
2521
  this.children[0].setParent(this);
2625
2522
  this.updateDeco();
2626
- this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0);
2523
+ this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0, null);
2627
2524
  }
2628
2525
  get length() { return this.view.state.doc.length; }
2629
2526
  // Update the document view to a given state.
@@ -2638,24 +2535,26 @@ class DocView extends ContentView {
2638
2535
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2639
2536
  }
2640
2537
  }
2641
- ({ deco: this.compositionDeco, node: this.compositionNode } =
2642
- this.view.inputState.composing < 0 ? noComp : computeCompositionDeco(this.view, update.changes));
2538
+ let composition = this.view.inputState.composing < 0 ? null : findCompositionRange(this.view, update.changes);
2539
+ if (this.hasComposition)
2540
+ this.markedForComposition.clear();
2541
+ this.hasComposition = !!composition;
2643
2542
  // When the DOM nodes around the selection are moved to another
2644
2543
  // parent, Chrome sometimes reports a different selection through
2645
2544
  // getSelection than the one that it actually shows to the user.
2646
2545
  // This forces a selection update when lines are joined to work
2647
2546
  // around that. Issue #54
2648
- if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
2547
+ if ((browser.ie || browser.chrome) && !composition && update &&
2649
2548
  update.state.doc.lines != update.startState.doc.lines)
2650
2549
  this.forceSelection = true;
2651
2550
  let prevDeco = this.decorations, deco = this.updateDeco();
2652
2551
  let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
2653
2552
  changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
2654
- if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
2553
+ if (!(this.flags & 7 /* Dirty */) && changedRanges.length == 0) {
2655
2554
  return false;
2656
2555
  }
2657
2556
  else {
2658
- this.updateInner(changedRanges, update.startState.doc.length);
2557
+ this.updateInner(changedRanges, update.startState.doc.length, composition);
2659
2558
  if (update.transactions.length)
2660
2559
  this.lastUpdate = Date.now();
2661
2560
  return true;
@@ -2663,9 +2562,9 @@ class DocView extends ContentView {
2663
2562
  }
2664
2563
  // Used by update and the constructor do perform the actual DOM
2665
2564
  // update
2666
- updateInner(changes, oldLength) {
2565
+ updateInner(changes, oldLength, composition) {
2667
2566
  this.view.viewState.mustMeasureContent = true;
2668
- this.updateChildren(changes, oldLength);
2567
+ this.updateChildren(changes, oldLength, composition);
2669
2568
  let { observer } = this.view;
2670
2569
  observer.ignore(() => {
2671
2570
  // Lock the height during redrawing, since Chrome sometimes
@@ -2680,11 +2579,12 @@ class DocView extends ContentView {
2680
2579
  // to detect that situation.
2681
2580
  let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2682
2581
  this.sync(this.view, track);
2683
- this.dirty = 0 /* Not */;
2582
+ this.flags &= ~7 /* Dirty */;
2684
2583
  if (track && (track.written || observer.selectionRange.focusNode != track.node))
2685
2584
  this.forceSelection = true;
2686
2585
  this.dom.style.height = "";
2687
2586
  });
2587
+ this.markedForComposition.forEach(cView => cView.flags &= ~8 /* Composition */);
2688
2588
  let gaps = [];
2689
2589
  if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2690
2590
  for (let child of this.children)
@@ -2692,18 +2592,62 @@ class DocView extends ContentView {
2692
2592
  gaps.push(child.dom);
2693
2593
  observer.updateGaps(gaps);
2694
2594
  }
2695
- updateChildren(changes, oldLength) {
2595
+ updateChildren(changes, oldLength, composition) {
2596
+ let ranges = composition ? composition.range.addToSet(changes.slice()) : changes;
2696
2597
  let cursor = this.childCursor(oldLength);
2697
- for (let i = changes.length - 1;; i--) {
2698
- let next = i >= 0 ? changes[i] : null;
2598
+ for (let i = ranges.length - 1;; i--) {
2599
+ let next = i >= 0 ? ranges[i] : null;
2699
2600
  if (!next)
2700
2601
  break;
2701
- let { fromA, toA, fromB, toB } = next;
2702
- let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap);
2602
+ let { fromA, toA, fromB, toB } = next, content, breakAtStart, openStart, openEnd;
2603
+ if (composition && composition.range.fromB < toB && composition.range.toB > fromB) {
2604
+ let before = ContentBuilder.build(this.view.state.doc, fromB, composition.range.fromB, this.decorations, this.dynamicDecorationMap);
2605
+ let after = ContentBuilder.build(this.view.state.doc, composition.range.toB, toB, this.decorations, this.dynamicDecorationMap);
2606
+ breakAtStart = before.breakAtStart;
2607
+ openStart = before.openStart;
2608
+ openEnd = after.openEnd;
2609
+ let compLine = this.compositionView(composition);
2610
+ compLine.merge(compLine.length, compLine.length, after.content[0], false, after.openStart, 0);
2611
+ compLine.merge(0, 0, before.content[before.content.length - 1], true, 0, before.openEnd);
2612
+ content = before.content.slice(0, before.content.length - 1).concat(compLine).concat(after.content.slice(1));
2613
+ }
2614
+ else {
2615
+ ({ content, breakAtStart, openStart, openEnd } =
2616
+ ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap));
2617
+ }
2703
2618
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2704
2619
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2705
2620
  replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2706
2621
  }
2622
+ if (composition)
2623
+ this.fixCompositionDOM(composition);
2624
+ }
2625
+ compositionView(composition) {
2626
+ let cur = new TextView(composition.text.nodeValue);
2627
+ cur.flags |= 8 /* Composition */;
2628
+ for (let { deco } of composition.marks)
2629
+ cur = new MarkView(deco, [cur], cur.length);
2630
+ let line = new LineView;
2631
+ line.append(cur, 0);
2632
+ return line;
2633
+ }
2634
+ fixCompositionDOM(composition) {
2635
+ let fix = (dom, cView) => {
2636
+ cView.flags |= 8 /* Composition */;
2637
+ this.markedForComposition.add(cView);
2638
+ let prev = ContentView.get(dom);
2639
+ if (prev)
2640
+ prev.dom = null;
2641
+ cView.setDOM(dom);
2642
+ };
2643
+ let pos = this.childPos(composition.range.fromB, 1);
2644
+ let cView = this.children[pos.i];
2645
+ fix(composition.line, cView);
2646
+ for (let i = composition.marks.length - 1; i >= -1; i--) {
2647
+ pos = cView.childPos(pos.off, 1);
2648
+ cView = cView.children[pos.i];
2649
+ fix(i >= 0 ? composition.marks[i].node : composition.text, cView);
2650
+ }
2707
2651
  }
2708
2652
  // Sync the DOM selection to this.state.selection
2709
2653
  updateSelection(mustRead = false, fromPointer = false) {
@@ -2722,7 +2666,7 @@ class DocView extends ContentView {
2722
2666
  let head = main.empty ? anchor : this.domAtPos(main.head);
2723
2667
  // Always reset on Firefox when next to an uneditable node to
2724
2668
  // avoid invisible cursor bugs (#111)
2725
- if (browser.gecko && main.empty && !this.compositionDeco.size && betweenUneditable(anchor)) {
2669
+ if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
2726
2670
  let dummy = document.createTextNode("");
2727
2671
  this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null));
2728
2672
  anchor = head = new DOMPos(dummy, 0);
@@ -2794,7 +2738,7 @@ class DocView extends ContentView {
2794
2738
  this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
2795
2739
  }
2796
2740
  enforceCursorAssoc() {
2797
- if (this.compositionDeco.size)
2741
+ if (this.hasComposition)
2798
2742
  return;
2799
2743
  let { view } = this, cursor = view.state.selection.main;
2800
2744
  let sel = getSelection(view.root);
@@ -2904,6 +2848,7 @@ class DocView extends ContentView {
2904
2848
  let dummy = document.createElement("div"), lineHeight, charWidth, textHeight;
2905
2849
  dummy.className = "cm-line";
2906
2850
  dummy.style.width = "99999px";
2851
+ dummy.style.position = "absolute";
2907
2852
  dummy.textContent = "abc def ghi jkl mno pqr stu";
2908
2853
  this.view.observer.ignore(() => {
2909
2854
  this.dom.appendChild(dummy);
@@ -2953,7 +2898,6 @@ class DocView extends ContentView {
2953
2898
  this.dynamicDecorationMap[i] = false;
2954
2899
  return this.decorations = [
2955
2900
  ...allDeco,
2956
- this.compositionDeco,
2957
2901
  this.computeBlockGapDeco(),
2958
2902
  this.view.viewState.lineGapDeco
2959
2903
  ];
@@ -2996,83 +2940,85 @@ class BlockGapWidget extends WidgetType {
2996
2940
  }
2997
2941
  get estimatedHeight() { return this.height; }
2998
2942
  }
2999
- function compositionSurroundingNode(view) {
2943
+ function findCompositionNode(view) {
3000
2944
  let sel = view.observer.selectionRange;
3001
2945
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
3002
2946
  if (!textNode)
3003
2947
  return null;
3004
- let cView = view.docView.nearest(textNode);
3005
- if (!cView)
3006
- return null;
3007
- if (cView instanceof LineView) {
3008
- let topNode = textNode;
3009
- while (topNode.parentNode != cView.dom)
3010
- topNode = topNode.parentNode;
3011
- let prev = topNode.previousSibling;
3012
- while (prev && !ContentView.get(prev))
3013
- prev = prev.previousSibling;
3014
- let pos = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
3015
- return { from: pos, to: pos, node: topNode, text: textNode };
2948
+ let cView = ContentView.get(textNode);
2949
+ let from, to;
2950
+ if (cView instanceof TextView) {
2951
+ from = cView.posAtStart;
2952
+ to = from + cView.length;
3016
2953
  }
3017
2954
  else {
3018
- for (;;) {
3019
- let { parent } = cView;
3020
- if (!parent)
2955
+ up: for (let offset = 0, node = textNode;;) {
2956
+ for (let sibling = node.previousSibling, cView; sibling; sibling = sibling.previousSibling) {
2957
+ if (cView = ContentView.get(sibling)) {
2958
+ from = to = cView.posAtEnd + offset;
2959
+ break up;
2960
+ }
2961
+ let reader = new DOMReader([], view.state);
2962
+ reader.readNode(sibling);
2963
+ if (reader.text.indexOf(LineBreakPlaceholder) > -1)
2964
+ return null;
2965
+ offset += reader.text.length;
2966
+ }
2967
+ node = node.parentNode;
2968
+ if (!node)
3021
2969
  return null;
3022
- if (parent instanceof LineView)
2970
+ let parentView = ContentView.get(node);
2971
+ if (parentView) {
2972
+ from = to = parentView.posAtStart + offset;
3023
2973
  break;
3024
- cView = parent;
3025
- }
3026
- let from = cView.posAtStart;
3027
- return { from, to: from + cView.length, node: cView.dom, text: textNode };
3028
- }
3029
- }
3030
- const noComp = { deco: Decoration.none, node: null };
3031
- function computeCompositionDeco(view, changes) {
3032
- let surrounding = compositionSurroundingNode(view);
3033
- if (!surrounding)
3034
- return noComp;
3035
- let { from, to, node, text: textNode } = surrounding;
3036
- let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
3037
- let { state } = view, reader = new DOMReader([], state);
3038
- if (node.nodeType == 3)
3039
- reader.readTextNode(node);
3040
- else
3041
- reader.readRange(node.firstChild, null);
3042
- let { text } = reader;
3043
- if (text.indexOf(LineBreakPlaceholder) > -1)
3044
- return noComp; // Don't try to preserve multi-line compositions
3045
- if (newTo - newFrom < text.length) {
3046
- if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
3047
- newTo = newFrom + text.length;
3048
- else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo) == text)
3049
- newFrom = newTo - text.length;
2974
+ }
2975
+ }
2976
+ }
2977
+ return { from, to, node: textNode };
2978
+ }
2979
+ function findCompositionRange(view, changes) {
2980
+ let found = findCompositionNode(view);
2981
+ if (!found)
2982
+ return null;
2983
+ let { from: fromA, to: toA, node: textNode } = found;
2984
+ let fromB = changes.mapPos(fromA, -1), toB = changes.mapPos(toA, 1);
2985
+ let text = textNode.nodeValue;
2986
+ // Don't try to preserve multi-line compositions
2987
+ if (/[\n\r]/.test(text))
2988
+ return null;
2989
+ if (toB - fromB != text.length) {
2990
+ // If there is a length mismatch, see if mapping non-inclusively helps
2991
+ let fromB2 = changes.mapPos(fromA, 1), toB2 = changes.mapPos(toA, -1);
2992
+ if (toB2 - fromB2 == text.length)
2993
+ fromB = fromB2, toB = toB2;
2994
+ // See if we can find an instance of the text at either side
2995
+ else if (view.state.doc.sliceString(toB - text.length, toB) == text)
2996
+ fromB = toB - text.length;
2997
+ else if (view.state.doc.sliceString(fromB, fromB + text.length) == text)
2998
+ toB = fromB + text.length;
2999
+ // Not found
3050
3000
  else
3051
- return noComp;
3052
- }
3053
- else if (state.doc.sliceString(newFrom, newTo) != text) {
3054
- return noComp;
3055
- }
3056
- let topView = ContentView.get(node);
3057
- if (topView instanceof CompositionView)
3058
- topView = topView.widget.topView;
3059
- else if (topView)
3060
- topView.parent = null;
3061
- let deco = Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
3062
- .range(newFrom, newTo));
3063
- return { deco, node };
3064
- }
3065
- class CompositionWidget extends WidgetType {
3066
- constructor(top, text, topView) {
3067
- super();
3068
- this.top = top;
3069
- this.text = text;
3070
- this.topView = topView;
3001
+ return null;
3002
+ }
3003
+ if (view.state.doc.sliceString(fromB, toB) != text)
3004
+ return null;
3005
+ let marks = [];
3006
+ let range = new ChangedRange(fromA, toA, fromB, toB);
3007
+ for (let parent = textNode.parentNode;; parent = parent.parentNode) {
3008
+ let parentView = ContentView.get(parent);
3009
+ if (parentView instanceof MarkView)
3010
+ marks.push({ node: parent, deco: parentView.mark });
3011
+ else if (parentView instanceof LineView || parent.nodeName == "DIV" && parent.parentNode == view.contentDOM)
3012
+ return { range, text: textNode, marks, line: parent };
3013
+ else if (parent != view.contentDOM)
3014
+ marks.push({ node: parent, deco: new MarkDecoration({
3015
+ inclusive: true,
3016
+ attributes: getAttrs(parent),
3017
+ tagName: parent.tagName.toLowerCase()
3018
+ }) });
3019
+ else
3020
+ return null;
3071
3021
  }
3072
- eq(other) { return this.top == other.top && this.text == other.text; }
3073
- toDOM() { return this.top; }
3074
- ignoreEvent() { return false; }
3075
- get customView() { return CompositionView; }
3076
3022
  }
3077
3023
  function nearbyTextNode(startNode, startOffset, side) {
3078
3024
  if (side <= 0)
@@ -3733,9 +3679,13 @@ const dragScrollMargin = 6;
3733
3679
  function dragScrollSpeed(dist) {
3734
3680
  return Math.max(0, dist) * 0.7 + 8;
3735
3681
  }
3682
+ function dist(a, b) {
3683
+ return Math.max(Math.abs(a.clientX - b.clientX), Math.abs(a.clientY - b.clientY));
3684
+ }
3736
3685
  class MouseSelection {
3737
3686
  constructor(view, startEvent, style, mustSelect) {
3738
3687
  this.view = view;
3688
+ this.startEvent = startEvent;
3739
3689
  this.style = style;
3740
3690
  this.mustSelect = mustSelect;
3741
3691
  this.scrollSpeed = { x: 0, y: 0 };
@@ -3762,7 +3712,7 @@ class MouseSelection {
3762
3712
  var _a;
3763
3713
  if (event.buttons == 0)
3764
3714
  return this.destroy();
3765
- if (this.dragging !== false)
3715
+ if (this.dragging || this.dragging == null && dist(this.startEvent, event) < 10)
3766
3716
  return;
3767
3717
  this.select(this.lastEvent = event);
3768
3718
  let sx = 0, sy = 0;
@@ -3841,7 +3791,7 @@ class MouseSelection {
3841
3791
  select(event) {
3842
3792
  let { view } = this, selection = this.skipAtoms(this.style.get(event, this.extend, this.multiple));
3843
3793
  if (this.mustSelect || !selection.eq(view.state.selection) ||
3844
- selection.main.assoc != view.state.selection.main.assoc)
3794
+ selection.main.assoc != view.state.selection.main.assoc && this.dragging === false)
3845
3795
  this.view.dispatch({
3846
3796
  selection,
3847
3797
  userEvent: "select.pointer"
@@ -4260,7 +4210,7 @@ handlers.compositionend = view => {
4260
4210
  // Otherwise, make sure that, if no changes come in soon, the
4261
4211
  // composition view is cleared.
4262
4212
  setTimeout(() => {
4263
- if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
4213
+ if (view.inputState.composing < 0 && view.docView.hasComposition)
4264
4214
  view.update([]);
4265
4215
  }, 50);
4266
4216
  }
@@ -5976,7 +5926,7 @@ function applyDOMChange(view, domChange) {
5976
5926
  if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
5977
5927
  change.to <= sel.to && change.to >= sel.to - 10) {
5978
5928
  let replaced = view.state.sliceDoc(change.from, change.to);
5979
- let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
5929
+ let composition = findCompositionNode(view) || view.state.doc.lineAt(sel.head);
5980
5930
  let offset = sel.to - change.to, size = sel.to - sel.from;
5981
5931
  tr = startState.changeByRange(range => {
5982
5932
  if (range.from == sel.from && range.to == sel.to)
@@ -5987,7 +5937,7 @@ function applyDOMChange(view, domChange) {
5987
5937
  // changes in the same node work without aborting
5988
5938
  // composition, so cursors in the composition range are
5989
5939
  // ignored.
5990
- compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
5940
+ composition && range.to >= composition.from && range.from <= composition.to)
5991
5941
  return { range };
5992
5942
  let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
5993
5943
  return {
@@ -6448,7 +6398,7 @@ class DOMObserver {
6448
6398
  return null;
6449
6399
  cView.markDirty(rec.type == "attributes");
6450
6400
  if (rec.type == "attributes")
6451
- cView.dirty |= 4 /* Attrs */;
6401
+ cView.flags |= 4 /* AttrsDirty */;
6452
6402
  if (rec.type == "childList") {
6453
6403
  let childBefore = findChild(cView, rec.previousSibling || rec.target.previousSibling, -1);
6454
6404
  let childAfter = findChild(cView, rec.nextSibling || rec.target.nextSibling, 1);
@@ -7622,7 +7572,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7622
7572
  else if (current != is)
7623
7573
  throw new Error("Key binding " + name + " is used both as a regular binding and as a multi-stroke prefix");
7624
7574
  };
7625
- let add = (scope, key, command, preventDefault) => {
7575
+ let add = (scope, key, command, preventDefault, stopPropagation) => {
7626
7576
  var _a, _b;
7627
7577
  let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
7628
7578
  let parts = key.split(/ (?!$)/).map(k => normalizeKeyName(k, platform));
@@ -7632,6 +7582,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7632
7582
  if (!scopeObj[prefix])
7633
7583
  scopeObj[prefix] = {
7634
7584
  preventDefault: true,
7585
+ stopPropagation: false,
7635
7586
  run: [(view) => {
7636
7587
  let ourObj = storedPrefix = { view, prefix, scope };
7637
7588
  setTimeout(() => { if (storedPrefix == ourObj)
@@ -7642,11 +7593,17 @@ function buildKeymap(bindings, platform = currentPlatform) {
7642
7593
  }
7643
7594
  let full = parts.join(" ");
7644
7595
  checkPrefix(full, false);
7645
- 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()) || [] });
7596
+ let binding = scopeObj[full] || (scopeObj[full] = {
7597
+ preventDefault: false,
7598
+ stopPropagation: false,
7599
+ run: ((_b = (_a = scopeObj._any) === null || _a === void 0 ? void 0 : _a.run) === null || _b === void 0 ? void 0 : _b.slice()) || []
7600
+ });
7646
7601
  if (command)
7647
7602
  binding.run.push(command);
7648
7603
  if (preventDefault)
7649
7604
  binding.preventDefault = true;
7605
+ if (stopPropagation)
7606
+ binding.stopPropagation = true;
7650
7607
  };
7651
7608
  for (let b of bindings) {
7652
7609
  let scopes = b.scope ? b.scope.split(" ") : ["editor"];
@@ -7654,7 +7611,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7654
7611
  for (let scope of scopes) {
7655
7612
  let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
7656
7613
  if (!scopeObj._any)
7657
- scopeObj._any = { preventDefault: false, run: [] };
7614
+ scopeObj._any = { preventDefault: false, stopPropagation: false, run: [] };
7658
7615
  for (let key in scopeObj)
7659
7616
  scopeObj[key].run.push(b.any);
7660
7617
  }
@@ -7662,9 +7619,9 @@ function buildKeymap(bindings, platform = currentPlatform) {
7662
7619
  if (!name)
7663
7620
  continue;
7664
7621
  for (let scope of scopes) {
7665
- add(scope, name, b.run, b.preventDefault);
7622
+ add(scope, name, b.run, b.preventDefault, b.stopPropagation);
7666
7623
  if (b.shift)
7667
- add(scope, "Shift-" + name, b.shift, b.preventDefault);
7624
+ add(scope, "Shift-" + name, b.shift, b.preventDefault, b.stopPropagation);
7668
7625
  }
7669
7626
  }
7670
7627
  return bound;
@@ -7672,11 +7629,13 @@ function buildKeymap(bindings, platform = currentPlatform) {
7672
7629
  function runHandlers(map, event, view, scope) {
7673
7630
  let name = keyName(event);
7674
7631
  let charCode = codePointAt(name, 0), isChar = codePointSize(charCode) == name.length && name != " ";
7675
- let prefix = "", fallthrough = false;
7632
+ let prefix = "", handled = false, prevented = false, stopPropagation = false;
7676
7633
  if (storedPrefix && storedPrefix.view == view && storedPrefix.scope == scope) {
7677
7634
  prefix = storedPrefix.prefix + " ";
7678
- if (fallthrough = modifierCodes.indexOf(event.keyCode) < 0)
7635
+ if (modifierCodes.indexOf(event.keyCode) < 0) {
7636
+ prevented = true;
7679
7637
  storedPrefix = null;
7638
+ }
7680
7639
  }
7681
7640
  let ran = new Set;
7682
7641
  let runFor = (binding) => {
@@ -7684,36 +7643,49 @@ function runHandlers(map, event, view, scope) {
7684
7643
  for (let cmd of binding.run)
7685
7644
  if (!ran.has(cmd)) {
7686
7645
  ran.add(cmd);
7687
- if (cmd(view, event))
7646
+ if (cmd(view, event)) {
7647
+ if (binding.stopPropagation)
7648
+ stopPropagation = true;
7688
7649
  return true;
7650
+ }
7689
7651
  }
7690
- if (binding.preventDefault)
7691
- fallthrough = true;
7652
+ if (binding.preventDefault) {
7653
+ if (binding.stopPropagation)
7654
+ stopPropagation = true;
7655
+ prevented = true;
7656
+ }
7692
7657
  }
7693
7658
  return false;
7694
7659
  };
7695
7660
  let scopeObj = map[scope], baseName, shiftName;
7696
7661
  if (scopeObj) {
7697
- if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)]))
7698
- return true;
7699
- if (isChar && (event.altKey || event.metaKey || event.ctrlKey) &&
7662
+ if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)])) {
7663
+ handled = true;
7664
+ }
7665
+ else if (isChar && (event.altKey || event.metaKey || event.ctrlKey) &&
7700
7666
  // Ctrl-Alt may be used for AltGr on Windows
7701
7667
  !(browser.windows && event.ctrlKey && event.altKey) &&
7702
7668
  (baseName = base[event.keyCode]) && baseName != name) {
7703
- if (runFor(scopeObj[prefix + modifiers(baseName, event, true)]))
7704
- return true;
7669
+ if (runFor(scopeObj[prefix + modifiers(baseName, event, true)])) {
7670
+ handled = true;
7671
+ }
7705
7672
  else if (event.shiftKey && (shiftName = shift[event.keyCode]) != name && shiftName != baseName &&
7706
- runFor(scopeObj[prefix + modifiers(shiftName, event, false)]))
7707
- return true;
7673
+ runFor(scopeObj[prefix + modifiers(shiftName, event, false)])) {
7674
+ handled = true;
7675
+ }
7708
7676
  }
7709
- else if (isChar && event.shiftKey) {
7710
- if (runFor(scopeObj[prefix + modifiers(name, event, true)]))
7711
- return true;
7677
+ else if (isChar && event.shiftKey &&
7678
+ runFor(scopeObj[prefix + modifiers(name, event, true)])) {
7679
+ handled = true;
7712
7680
  }
7713
- if (runFor(scopeObj._any))
7714
- return true;
7681
+ if (!handled && runFor(scopeObj._any))
7682
+ handled = true;
7715
7683
  }
7716
- return fallthrough;
7684
+ if (prevented)
7685
+ handled = true;
7686
+ if (handled && stopPropagation)
7687
+ event.stopPropagation();
7688
+ return handled;
7717
7689
  }
7718
7690
 
7719
7691
  /**
@@ -8500,7 +8472,9 @@ function placeholder(content) {
8500
8472
  return ViewPlugin.fromClass(class {
8501
8473
  constructor(view) {
8502
8474
  this.view = view;
8503
- this.placeholder = Decoration.set([Decoration.widget({ widget: new Placeholder(content), side: 1 }).range(0)]);
8475
+ this.placeholder = content
8476
+ ? Decoration.set([Decoration.widget({ widget: new Placeholder(content), side: 1 }).range(0)])
8477
+ : Decoration.none;
8504
8478
  }
8505
8479
  get decorations() { return this.view.state.doc.length ? Decoration.none : this.placeholder; }
8506
8480
  }, { decorations: v => v.decorations });