@codemirror/view 6.14.0 → 6.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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;
@@ -316,6 +318,9 @@ function atElementStart(doc, selection) {
316
318
  }
317
319
  }
318
320
  }
321
+ function isScrolledToBottom(elt) {
322
+ return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
323
+ }
319
324
 
320
325
  class DOMPos {
321
326
  constructor(node, offset, precise = true) {
@@ -331,7 +336,7 @@ class ContentView {
331
336
  constructor() {
332
337
  this.parent = null;
333
338
  this.dom = null;
334
- this.dirty = 2 /* Node */;
339
+ this.flags = 2 /* NodeDirty */;
335
340
  }
336
341
  get overrideDOMText() { return null; }
337
342
  get posAtStart() {
@@ -353,18 +358,18 @@ class ContentView {
353
358
  return this.posBefore(view) + view.length;
354
359
  }
355
360
  sync(view, track) {
356
- if (this.dirty & 2 /* Node */) {
361
+ if (this.flags & 2 /* NodeDirty */) {
357
362
  let parent = this.dom;
358
363
  let prev = null, next;
359
364
  for (let child of this.children) {
360
- if (child.dirty) {
365
+ if (child.flags & 7 /* Dirty */) {
361
366
  if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
362
367
  let contentView = ContentView.get(next);
363
368
  if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
364
369
  child.reuseDOM(next);
365
370
  }
366
371
  child.sync(view, track);
367
- child.dirty = 0 /* Not */;
372
+ child.flags &= ~7 /* Dirty */;
368
373
  }
369
374
  next = prev ? prev.nextSibling : parent.firstChild;
370
375
  if (track && !track.written && track.node == parent && next != child.dom)
@@ -384,11 +389,11 @@ class ContentView {
384
389
  while (next)
385
390
  next = rm$1(next);
386
391
  }
387
- else if (this.dirty & 1 /* Child */) {
392
+ else if (this.flags & 1 /* ChildDirty */) {
388
393
  for (let child of this.children)
389
- if (child.dirty) {
394
+ if (child.flags & 7 /* Dirty */) {
390
395
  child.sync(view, track);
391
- child.dirty = 0 /* Not */;
396
+ child.flags &= ~7 /* Dirty */;
392
397
  }
393
398
  }
394
399
  }
@@ -453,23 +458,23 @@ class ContentView {
453
458
  endDOM: toI < this.children.length && toI >= 0 ? this.children[toI].dom : null };
454
459
  }
455
460
  markDirty(andParent = false) {
456
- this.dirty |= 2 /* Node */;
461
+ this.flags |= 2 /* NodeDirty */;
457
462
  this.markParentsDirty(andParent);
458
463
  }
459
464
  markParentsDirty(childList) {
460
465
  for (let parent = this.parent; parent; parent = parent.parent) {
461
466
  if (childList)
462
- parent.dirty |= 2 /* Node */;
463
- if (parent.dirty & 1 /* Child */)
467
+ parent.flags |= 2 /* NodeDirty */;
468
+ if (parent.flags & 1 /* ChildDirty */)
464
469
  return;
465
- parent.dirty |= 1 /* Child */;
470
+ parent.flags |= 1 /* ChildDirty */;
466
471
  childList = false;
467
472
  }
468
473
  }
469
474
  setParent(parent) {
470
475
  if (this.parent != parent) {
471
476
  this.parent = parent;
472
- if (this.dirty)
477
+ if (this.flags & 7 /* Dirty */)
473
478
  this.markParentsDirty(true);
474
479
  }
475
480
  }
@@ -520,7 +525,9 @@ class ContentView {
520
525
  return false;
521
526
  }
522
527
  become(other) { return false; }
523
- canReuseDOM(other) { return other.constructor == this.constructor; }
528
+ canReuseDOM(other) {
529
+ return other.constructor == this.constructor && !((this.flags | other.flags) & 8 /* Composition */);
530
+ }
524
531
  // When this is a zero-length view with a side, this should return a
525
532
  // number <= 0 to indicate it is before its position, or a
526
533
  // number > 0 when after its position.
@@ -644,6 +651,113 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
644
651
  replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
645
652
  }
646
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
+
647
761
  let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
648
762
  let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
649
763
  const ie_edge = /*@__PURE__*//Edge\/(\d+)/.exec(nav.userAgent);
@@ -697,7 +811,10 @@ class TextView extends ContentView {
697
811
  this.createDOM(dom);
698
812
  }
699
813
  merge(from, to, source) {
700
- 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 */)))
701
818
  return false;
702
819
  this.text = this.text.slice(0, from) + (source ? source.text : "") + this.text.slice(to);
703
820
  this.markDirty();
@@ -707,6 +824,7 @@ class TextView extends ContentView {
707
824
  let result = new TextView(this.text.slice(from));
708
825
  this.text = this.text.slice(0, from);
709
826
  this.markDirty();
827
+ result.flags |= this.flags & 8 /* Composition */;
710
828
  return result;
711
829
  }
712
830
  localPosFromDOM(node, offset) {
@@ -738,16 +856,19 @@ class MarkView extends ContentView {
738
856
  dom.setAttribute(name, this.mark.attrs[name]);
739
857
  return dom;
740
858
  }
859
+ canReuseDOM(other) {
860
+ return super.canReuseDOM(other) && !((this.flags | other.flags) & 8 /* Composition */);
861
+ }
741
862
  reuseDOM(node) {
742
863
  if (node.nodeName == this.mark.tagName.toUpperCase()) {
743
864
  this.setDOM(node);
744
- this.dirty |= 4 /* Attrs */ | 2 /* Node */;
865
+ this.flags |= 4 /* AttrsDirty */ | 2 /* NodeDirty */;
745
866
  }
746
867
  }
747
868
  sync(view, track) {
748
869
  if (!this.dom)
749
870
  this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
750
- else if (this.dirty & 4 /* Attrs */)
871
+ else if (this.flags & 4 /* AttrsDirty */)
751
872
  this.setAttrs(this.dom);
752
873
  super.sync(view, track);
753
874
  }
@@ -826,7 +947,7 @@ class WidgetView extends ContentView {
826
947
  this.prevWidget = null;
827
948
  }
828
949
  static create(widget, length, side) {
829
- return new (widget.customView || WidgetView)(widget, length, side);
950
+ return new WidgetView(widget, length, side);
830
951
  }
831
952
  split(from) {
832
953
  let result = WidgetView.create(this.widget, this.length - from, this.side);
@@ -904,129 +1025,6 @@ class WidgetView extends ContentView {
904
1025
  this.widget.destroy(this.dom);
905
1026
  }
906
1027
  }
907
- class CompositionView extends WidgetView {
908
- domAtPos(pos) {
909
- let { topView, text } = this.widget;
910
- if (!topView)
911
- return new DOMPos(text, Math.min(pos, text.nodeValue.length));
912
- 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)));
913
- }
914
- sync() { this.setDOM(this.widget.toDOM()); }
915
- localPosFromDOM(node, offset) {
916
- let { topView, text } = this.widget;
917
- if (!topView)
918
- return Math.min(offset, this.length);
919
- return posFromDOMInCompositionTree(node, offset, topView, text, this.length - topView.length);
920
- }
921
- ignoreMutation() { return false; }
922
- get overrideDOMText() { return null; }
923
- coordsAt(pos, side) {
924
- let { topView, text } = this.widget;
925
- if (!topView)
926
- return textCoords(text, pos, side);
927
- return scanCompositionTree(pos, side, topView, text, this.length - topView.length, (v, pos, side) => v.coordsAt(pos, side), (text, pos, side) => textCoords(text, pos, side));
928
- }
929
- destroy() {
930
- var _a;
931
- super.destroy();
932
- (_a = this.widget.topView) === null || _a === void 0 ? void 0 : _a.destroy();
933
- }
934
- get isEditable() { return true; }
935
- canReuseDOM() { return true; }
936
- }
937
- // Uses the old structure of a chunk of content view frozen for
938
- // composition to try and find a reasonable DOM location for the given
939
- // offset.
940
- function scanCompositionTree(pos, side, view, text, dLen, enterView, fromText) {
941
- if (view instanceof MarkView) {
942
- for (let child = view.dom.firstChild; child; child = child.nextSibling) {
943
- let desc = ContentView.get(child);
944
- if (!desc) {
945
- let inner = scanCompositionNode(pos, side, child, fromText);
946
- if (typeof inner != "number")
947
- return inner;
948
- pos = inner;
949
- }
950
- else {
951
- let hasComp = contains(child, text);
952
- let len = desc.length + (hasComp ? dLen : 0);
953
- if (pos < len || pos == len && desc.getSide() <= 0)
954
- return hasComp ? scanCompositionTree(pos, side, desc, text, dLen, enterView, fromText) : enterView(desc, pos, side);
955
- pos -= len;
956
- }
957
- }
958
- return enterView(view, view.length, -1);
959
- }
960
- else if (view.dom == text) {
961
- return fromText(text, pos, side);
962
- }
963
- else {
964
- return enterView(view, pos, side);
965
- }
966
- }
967
- function scanCompositionNode(pos, side, node, fromText) {
968
- if (node.nodeType == 3) {
969
- let len = node.nodeValue.length;
970
- if (pos <= len)
971
- return fromText(node, pos, side);
972
- pos -= len;
973
- }
974
- else if (node.nodeType == 1 && node.contentEditable != "false") {
975
- for (let child = node.firstChild; child; child = child.nextSibling) {
976
- let inner = scanCompositionNode(pos, side, child, fromText);
977
- if (typeof inner != "number")
978
- return inner;
979
- pos = inner;
980
- }
981
- }
982
- return pos;
983
- }
984
- function posFromDOMInCompositionTree(node, offset, view, text, dLen) {
985
- if (view instanceof MarkView) {
986
- let pos = 0;
987
- for (let child = view.dom.firstChild; child; child = child.nextSibling) {
988
- let childView = ContentView.get(child);
989
- if (childView) {
990
- let hasComp = contains(child, text);
991
- if (contains(child, node))
992
- return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, childView, text, dLen)
993
- : childView.localPosFromDOM(node, offset));
994
- pos += childView.length + (hasComp ? dLen : 0);
995
- }
996
- else {
997
- let inner = posFromDOMInOpaqueNode(node, offset, child);
998
- if (inner.result != null)
999
- return pos + inner.result;
1000
- pos += inner.size;
1001
- }
1002
- }
1003
- }
1004
- else if (view.dom == text) {
1005
- return Math.min(offset, text.nodeValue.length);
1006
- }
1007
- return view.localPosFromDOM(node, offset);
1008
- }
1009
- function posFromDOMInOpaqueNode(node, offset, target) {
1010
- if (target.nodeType == 3) {
1011
- return node == target ? { result: offset } : { size: target.nodeValue.length };
1012
- }
1013
- else if (target.nodeType == 1 && target.contentEditable != "false") {
1014
- let pos = 0;
1015
- for (let child = target.firstChild, i = 0;; child = child.nextSibling, i++) {
1016
- if (node == target && i == offset)
1017
- return { result: pos };
1018
- if (!child)
1019
- return { size: pos };
1020
- let inner = posFromDOMInOpaqueNode(node, offset, child);
1021
- if (inner.result != null)
1022
- return { result: offset + inner.result };
1023
- pos += inner.size;
1024
- }
1025
- }
1026
- else {
1027
- return target.contains(node) ? { result: 0 } : { size: 0 };
1028
- }
1029
- }
1030
1028
  // These are drawn around uneditable widgets to avoid a number of
1031
1029
  // browser bugs that show up when the cursor is directly next to
1032
1030
  // uneditable inline content.
@@ -1146,16 +1144,20 @@ function combineAttrs(source, target) {
1146
1144
  }
1147
1145
  return target;
1148
1146
  }
1149
- function attrsEq(a, b) {
1147
+ const noAttrs = /*@__PURE__*/Object.create(null);
1148
+ function attrsEq(a, b, ignore) {
1150
1149
  if (a == b)
1151
1150
  return true;
1152
- if (!a || !b)
1153
- return false;
1151
+ if (!a)
1152
+ a = noAttrs;
1153
+ if (!b)
1154
+ b = noAttrs;
1154
1155
  let keysA = Object.keys(a), keysB = Object.keys(b);
1155
- 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))
1156
1158
  return false;
1157
1159
  for (let key of keysA) {
1158
- if (keysB.indexOf(key) == -1 || a[key] !== b[key])
1160
+ if (key != ignore && (keysB.indexOf(key) == -1 || a[key] !== b[key]))
1159
1161
  return false;
1160
1162
  }
1161
1163
  return true;
@@ -1172,6 +1174,14 @@ function updateAttrs(dom, prev, attrs) {
1172
1174
  dom.setAttribute(changed = name, attrs[name]);
1173
1175
  return !!changed;
1174
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
+ }
1175
1185
 
1176
1186
  /**
1177
1187
  Widgets added to the content are described by subclasses of this
@@ -1236,10 +1246,6 @@ class WidgetType {
1236
1246
  /**
1237
1247
  @internal
1238
1248
  */
1239
- get customView() { return null; }
1240
- /**
1241
- @internal
1242
- */
1243
1249
  get isHidden() { return false; }
1244
1250
  /**
1245
1251
  This is called when the an instance of the widget is removed
@@ -1377,11 +1383,12 @@ class MarkDecoration extends Decoration {
1377
1383
  this.attrs = spec.attributes || null;
1378
1384
  }
1379
1385
  eq(other) {
1386
+ var _a, _b;
1380
1387
  return this == other ||
1381
1388
  other instanceof MarkDecoration &&
1382
1389
  this.tagName == other.tagName &&
1383
- this.class == other.class &&
1384
- 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");
1385
1392
  }
1386
1393
  range(from, to = from) {
1387
1394
  if (from >= to)
@@ -1533,7 +1540,7 @@ class LineView extends ContentView {
1533
1540
  reuseDOM(node) {
1534
1541
  if (node.nodeName == "DIV") {
1535
1542
  this.setDOM(node);
1536
- this.dirty |= 4 /* Attrs */ | 2 /* Node */;
1543
+ this.flags |= 4 /* AttrsDirty */ | 2 /* NodeDirty */;
1537
1544
  }
1538
1545
  }
1539
1546
  sync(view, track) {
@@ -1543,7 +1550,7 @@ class LineView extends ContentView {
1543
1550
  this.dom.className = "cm-line";
1544
1551
  this.prevAttrs = this.attrs ? null : undefined;
1545
1552
  }
1546
- else if (this.dirty & 4 /* Attrs */) {
1553
+ else if (this.flags & 4 /* AttrsDirty */) {
1547
1554
  clearAttributes(this.dom);
1548
1555
  this.dom.className = "cm-line";
1549
1556
  this.prevAttrs = this.attrs ? null : undefined;
@@ -2483,120 +2490,14 @@ function moveVisually(line, order, dir, start, forward) {
2483
2490
  return EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
2484
2491
  }
2485
2492
 
2486
- const LineBreakPlaceholder = "\uffff";
2487
- class DOMReader {
2488
- constructor(points, state) {
2489
- this.points = points;
2490
- this.text = "";
2491
- this.lineSeparator = state.facet(EditorState.lineSeparator);
2492
- }
2493
- append(text) {
2494
- this.text += text;
2495
- }
2496
- lineBreak() {
2497
- this.text += LineBreakPlaceholder;
2498
- }
2499
- readRange(start, end) {
2500
- if (!start)
2501
- return this;
2502
- let parent = start.parentNode;
2503
- for (let cur = start;;) {
2504
- this.findPointBefore(parent, cur);
2505
- let oldLen = this.text.length;
2506
- this.readNode(cur);
2507
- let next = cur.nextSibling;
2508
- if (next == end)
2509
- break;
2510
- let view = ContentView.get(cur), nextView = ContentView.get(next);
2511
- if (view && nextView ? view.breakAfter :
2512
- (view ? view.breakAfter : isBlockElement(cur)) ||
2513
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
2514
- this.lineBreak();
2515
- cur = next;
2516
- }
2517
- this.findPointBefore(parent, end);
2518
- return this;
2519
- }
2520
- readTextNode(node) {
2521
- let text = node.nodeValue;
2522
- for (let point of this.points)
2523
- if (point.node == node)
2524
- point.pos = this.text.length + Math.min(point.offset, text.length);
2525
- for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
2526
- let nextBreak = -1, breakSize = 1, m;
2527
- if (this.lineSeparator) {
2528
- nextBreak = text.indexOf(this.lineSeparator, off);
2529
- breakSize = this.lineSeparator.length;
2530
- }
2531
- else if (m = re.exec(text)) {
2532
- nextBreak = m.index;
2533
- breakSize = m[0].length;
2534
- }
2535
- this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
2536
- if (nextBreak < 0)
2537
- break;
2538
- this.lineBreak();
2539
- if (breakSize > 1)
2540
- for (let point of this.points)
2541
- if (point.node == node && point.pos > this.text.length)
2542
- point.pos -= breakSize - 1;
2543
- off = nextBreak + breakSize;
2544
- }
2545
- }
2546
- readNode(node) {
2547
- if (node.cmIgnore)
2548
- return;
2549
- let view = ContentView.get(node);
2550
- let fromView = view && view.overrideDOMText;
2551
- if (fromView != null) {
2552
- this.findPointInside(node, fromView.length);
2553
- for (let i = fromView.iter(); !i.next().done;) {
2554
- if (i.lineBreak)
2555
- this.lineBreak();
2556
- else
2557
- this.append(i.value);
2558
- }
2559
- }
2560
- else if (node.nodeType == 3) {
2561
- this.readTextNode(node);
2562
- }
2563
- else if (node.nodeName == "BR") {
2564
- if (node.nextSibling)
2565
- this.lineBreak();
2566
- }
2567
- else if (node.nodeType == 1) {
2568
- this.readRange(node.firstChild, null);
2569
- }
2570
- }
2571
- findPointBefore(node, next) {
2572
- for (let point of this.points)
2573
- if (point.node == node && node.childNodes[point.offset] == next)
2574
- point.pos = this.text.length;
2575
- }
2576
- findPointInside(node, maxLen) {
2577
- for (let point of this.points)
2578
- if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
2579
- point.pos = this.text.length + Math.min(maxLen, point.offset);
2580
- }
2581
- }
2582
- function isBlockElement(node) {
2583
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
2584
- }
2585
- class DOMPoint {
2586
- constructor(node, offset) {
2587
- this.node = node;
2588
- this.offset = offset;
2589
- this.pos = -1;
2590
- }
2591
- }
2592
-
2593
2493
  class DocView extends ContentView {
2594
2494
  constructor(view) {
2595
2495
  super();
2596
2496
  this.view = view;
2597
- this.compositionDeco = Decoration.none;
2598
2497
  this.decorations = [];
2599
2498
  this.dynamicDecorationMap = [];
2499
+ this.hasComposition = false;
2500
+ this.markedForComposition = new Set;
2600
2501
  // Track a minimum width for the editor. When measuring sizes in
2601
2502
  // measureVisibleLineHeights, this is updated to point at the width
2602
2503
  // of a given element and its extent in the document. When a change
@@ -2619,7 +2520,7 @@ class DocView extends ContentView {
2619
2520
  this.children = [new LineView];
2620
2521
  this.children[0].setParent(this);
2621
2522
  this.updateDeco();
2622
- 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);
2623
2524
  }
2624
2525
  get length() { return this.view.state.doc.length; }
2625
2526
  // Update the document view to a given state.
@@ -2634,26 +2535,26 @@ class DocView extends ContentView {
2634
2535
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2635
2536
  }
2636
2537
  }
2637
- if (this.view.inputState.composing < 0)
2638
- this.compositionDeco = Decoration.none;
2639
- else if (update.transactions.length || this.dirty)
2640
- this.compositionDeco = 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;
2641
2542
  // When the DOM nodes around the selection are moved to another
2642
2543
  // parent, Chrome sometimes reports a different selection through
2643
2544
  // getSelection than the one that it actually shows to the user.
2644
2545
  // This forces a selection update when lines are joined to work
2645
2546
  // around that. Issue #54
2646
- if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
2547
+ if ((browser.ie || browser.chrome) && !composition && update &&
2647
2548
  update.state.doc.lines != update.startState.doc.lines)
2648
2549
  this.forceSelection = true;
2649
2550
  let prevDeco = this.decorations, deco = this.updateDeco();
2650
2551
  let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
2651
2552
  changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
2652
- if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
2553
+ if (!(this.flags & 7 /* Dirty */) && changedRanges.length == 0) {
2653
2554
  return false;
2654
2555
  }
2655
2556
  else {
2656
- this.updateInner(changedRanges, update.startState.doc.length);
2557
+ this.updateInner(changedRanges, update.startState.doc.length, composition);
2657
2558
  if (update.transactions.length)
2658
2559
  this.lastUpdate = Date.now();
2659
2560
  return true;
@@ -2661,9 +2562,9 @@ class DocView extends ContentView {
2661
2562
  }
2662
2563
  // Used by update and the constructor do perform the actual DOM
2663
2564
  // update
2664
- updateInner(changes, oldLength) {
2565
+ updateInner(changes, oldLength, composition) {
2665
2566
  this.view.viewState.mustMeasureContent = true;
2666
- this.updateChildren(changes, oldLength);
2567
+ this.updateChildren(changes, oldLength, composition);
2667
2568
  let { observer } = this.view;
2668
2569
  observer.ignore(() => {
2669
2570
  // Lock the height during redrawing, since Chrome sometimes
@@ -2678,11 +2579,12 @@ class DocView extends ContentView {
2678
2579
  // to detect that situation.
2679
2580
  let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
2680
2581
  this.sync(this.view, track);
2681
- this.dirty = 0 /* Not */;
2582
+ this.flags &= ~7 /* Dirty */;
2682
2583
  if (track && (track.written || observer.selectionRange.focusNode != track.node))
2683
2584
  this.forceSelection = true;
2684
2585
  this.dom.style.height = "";
2685
2586
  });
2587
+ this.markedForComposition.forEach(cView => cView.flags &= ~8 /* Composition */);
2686
2588
  let gaps = [];
2687
2589
  if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
2688
2590
  for (let child of this.children)
@@ -2690,18 +2592,62 @@ class DocView extends ContentView {
2690
2592
  gaps.push(child.dom);
2691
2593
  observer.updateGaps(gaps);
2692
2594
  }
2693
- updateChildren(changes, oldLength) {
2595
+ updateChildren(changes, oldLength, composition) {
2596
+ let ranges = composition ? composition.range.addToSet(changes.slice()) : changes;
2694
2597
  let cursor = this.childCursor(oldLength);
2695
- for (let i = changes.length - 1;; i--) {
2696
- let next = i >= 0 ? changes[i] : null;
2598
+ for (let i = ranges.length - 1;; i--) {
2599
+ let next = i >= 0 ? ranges[i] : null;
2697
2600
  if (!next)
2698
2601
  break;
2699
- let { fromA, toA, fromB, toB } = next;
2700
- 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
+ }
2701
2618
  let { i: toI, off: toOff } = cursor.findPos(toA, 1);
2702
2619
  let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
2703
2620
  replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
2704
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
+ }
2705
2651
  }
2706
2652
  // Sync the DOM selection to this.state.selection
2707
2653
  updateSelection(mustRead = false, fromPointer = false) {
@@ -2720,7 +2666,7 @@ class DocView extends ContentView {
2720
2666
  let head = main.empty ? anchor : this.domAtPos(main.head);
2721
2667
  // Always reset on Firefox when next to an uneditable node to
2722
2668
  // avoid invisible cursor bugs (#111)
2723
- if (browser.gecko && main.empty && !this.compositionDeco.size && betweenUneditable(anchor)) {
2669
+ if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
2724
2670
  let dummy = document.createTextNode("");
2725
2671
  this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null));
2726
2672
  anchor = head = new DOMPos(dummy, 0);
@@ -2792,7 +2738,7 @@ class DocView extends ContentView {
2792
2738
  this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
2793
2739
  }
2794
2740
  enforceCursorAssoc() {
2795
- if (this.compositionDeco.size)
2741
+ if (this.hasComposition)
2796
2742
  return;
2797
2743
  let { view } = this, cursor = view.state.selection.main;
2798
2744
  let sel = getSelection(view.root);
@@ -2902,6 +2848,7 @@ class DocView extends ContentView {
2902
2848
  let dummy = document.createElement("div"), lineHeight, charWidth, textHeight;
2903
2849
  dummy.className = "cm-line";
2904
2850
  dummy.style.width = "99999px";
2851
+ dummy.style.position = "absolute";
2905
2852
  dummy.textContent = "abc def ghi jkl mno pqr stu";
2906
2853
  this.view.observer.ignore(() => {
2907
2854
  this.dom.appendChild(dummy);
@@ -2951,7 +2898,6 @@ class DocView extends ContentView {
2951
2898
  this.dynamicDecorationMap[i] = false;
2952
2899
  return this.decorations = [
2953
2900
  ...allDeco,
2954
- this.compositionDeco,
2955
2901
  this.computeBlockGapDeco(),
2956
2902
  this.view.viewState.lineGapDeco
2957
2903
  ];
@@ -2994,81 +2940,85 @@ class BlockGapWidget extends WidgetType {
2994
2940
  }
2995
2941
  get estimatedHeight() { return this.height; }
2996
2942
  }
2997
- function compositionSurroundingNode(view) {
2943
+ function findCompositionNode(view) {
2998
2944
  let sel = view.observer.selectionRange;
2999
2945
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
3000
2946
  if (!textNode)
3001
2947
  return null;
3002
- let cView = view.docView.nearest(textNode);
3003
- if (!cView)
3004
- return null;
3005
- if (cView instanceof LineView) {
3006
- let topNode = textNode;
3007
- while (topNode.parentNode != cView.dom)
3008
- topNode = topNode.parentNode;
3009
- let prev = topNode.previousSibling;
3010
- while (prev && !ContentView.get(prev))
3011
- prev = prev.previousSibling;
3012
- let pos = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
3013
- 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;
3014
2953
  }
3015
2954
  else {
3016
- for (;;) {
3017
- let { parent } = cView;
3018
- 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)
3019
2969
  return null;
3020
- if (parent instanceof LineView)
2970
+ let parentView = ContentView.get(node);
2971
+ if (parentView) {
2972
+ from = to = parentView.posAtStart + offset;
3021
2973
  break;
3022
- cView = parent;
2974
+ }
3023
2975
  }
3024
- let from = cView.posAtStart;
3025
- return { from, to: from + cView.length, node: cView.dom, text: textNode };
3026
2976
  }
2977
+ return { from, to, node: textNode };
3027
2978
  }
3028
- function computeCompositionDeco(view, changes) {
3029
- let surrounding = compositionSurroundingNode(view);
3030
- if (!surrounding)
3031
- return Decoration.none;
3032
- let { from, to, node, text: textNode } = surrounding;
3033
- let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
3034
- let { state } = view, reader = new DOMReader([], state);
3035
- if (node.nodeType == 3)
3036
- reader.readTextNode(node);
3037
- else
3038
- reader.readRange(node.firstChild, null);
3039
- let { text } = reader;
3040
- if (text.indexOf(LineBreakPlaceholder) > -1)
3041
- return Decoration.none; // Don't try to preserve multi-line compositions
3042
- if (newTo - newFrom < text.length) {
3043
- if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
3044
- newTo = newFrom + text.length;
3045
- else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo) == text)
3046
- newFrom = newTo - text.length;
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
3047
3000
  else
3048
- return Decoration.none;
3049
- }
3050
- else if (state.doc.sliceString(newFrom, newTo) != text) {
3051
- return Decoration.none;
3001
+ return null;
3052
3002
  }
3053
- let topView = ContentView.get(node);
3054
- if (topView instanceof CompositionView)
3055
- topView = topView.widget.topView;
3056
- else if (topView)
3057
- topView.parent = null;
3058
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
3059
- .range(newFrom, newTo));
3060
- }
3061
- class CompositionWidget extends WidgetType {
3062
- constructor(top, text, topView) {
3063
- super();
3064
- this.top = top;
3065
- this.text = text;
3066
- this.topView = topView;
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;
3067
3021
  }
3068
- eq(other) { return this.top == other.top && this.text == other.text; }
3069
- toDOM() { return this.top; }
3070
- ignoreEvent() { return false; }
3071
- get customView() { return CompositionView; }
3072
3022
  }
3073
3023
  function nearbyTextNode(startNode, startOffset, side) {
3074
3024
  if (side <= 0)
@@ -3719,6 +3669,7 @@ class InputState {
3719
3669
  const PendingKeys = [
3720
3670
  { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3721
3671
  { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
3672
+ { key: "Enter", keyCode: 13, inputType: "insertLineBreak" },
3722
3673
  { key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
3723
3674
  ];
3724
3675
  const EmacsyPendingKeys = "dthko";
@@ -3728,9 +3679,13 @@ const dragScrollMargin = 6;
3728
3679
  function dragScrollSpeed(dist) {
3729
3680
  return Math.max(0, dist) * 0.7 + 8;
3730
3681
  }
3682
+ function dist(a, b) {
3683
+ return Math.max(Math.abs(a.clientX - b.clientX), Math.abs(a.clientY - b.clientY));
3684
+ }
3731
3685
  class MouseSelection {
3732
3686
  constructor(view, startEvent, style, mustSelect) {
3733
3687
  this.view = view;
3688
+ this.startEvent = startEvent;
3734
3689
  this.style = style;
3735
3690
  this.mustSelect = mustSelect;
3736
3691
  this.scrollSpeed = { x: 0, y: 0 };
@@ -3757,7 +3712,7 @@ class MouseSelection {
3757
3712
  var _a;
3758
3713
  if (event.buttons == 0)
3759
3714
  return this.destroy();
3760
- if (this.dragging !== false)
3715
+ if (this.dragging || this.dragging == null && dist(this.startEvent, event) < 10)
3761
3716
  return;
3762
3717
  this.select(this.lastEvent = event);
3763
3718
  let sx = 0, sy = 0;
@@ -3836,7 +3791,7 @@ class MouseSelection {
3836
3791
  select(event) {
3837
3792
  let { view } = this, selection = this.skipAtoms(this.style.get(event, this.extend, this.multiple));
3838
3793
  if (this.mustSelect || !selection.eq(view.state.selection) ||
3839
- selection.main.assoc != view.state.selection.main.assoc)
3794
+ selection.main.assoc != view.state.selection.main.assoc && this.dragging === false)
3840
3795
  this.view.dispatch({
3841
3796
  selection,
3842
3797
  userEvent: "select.pointer"
@@ -3963,7 +3918,7 @@ handlers.mousedown = (view, event) => {
3963
3918
  if (!style && event.button == 0)
3964
3919
  style = basicMouseSelection(view, event);
3965
3920
  if (style) {
3966
- let mustFocus = view.root.activeElement != view.contentDOM;
3921
+ let mustFocus = !view.hasFocus;
3967
3922
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
3968
3923
  if (mustFocus)
3969
3924
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
@@ -4255,7 +4210,7 @@ handlers.compositionend = view => {
4255
4210
  // Otherwise, make sure that, if no changes come in soon, the
4256
4211
  // composition view is cleared.
4257
4212
  setTimeout(() => {
4258
- if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
4213
+ if (view.inputState.composing < 0 && view.docView.hasComposition)
4259
4214
  view.update([]);
4260
4215
  }, 50);
4261
4216
  }
@@ -5159,7 +5114,7 @@ class ViewState {
5159
5114
  let contentChanges = update.changedRanges;
5160
5115
  let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
5161
5116
  let prevHeight = this.heightMap.height;
5162
- let scrollAnchor = this.scrolledToBottom ? null : this.lineBlockAtHeight(this.scrollTop);
5117
+ let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop);
5163
5118
  this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
5164
5119
  if (this.heightMap.height != prevHeight)
5165
5120
  update.flags |= 2 /* Height */;
@@ -5219,7 +5174,7 @@ class ViewState {
5219
5174
  this.scrollAnchorHeight = -1;
5220
5175
  this.scrollTop = view.scrollDOM.scrollTop;
5221
5176
  }
5222
- this.scrolledToBottom = this.scrollTop > view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight - 4;
5177
+ this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
5223
5178
  // Pixel viewport
5224
5179
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
5225
5180
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -5462,6 +5417,10 @@ class ViewState {
5462
5417
  lineBlockAtHeight(height) {
5463
5418
  return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5464
5419
  }
5420
+ scrollAnchorAt(scrollTop) {
5421
+ let block = this.lineBlockAtHeight(scrollTop + 8);
5422
+ return block.from >= this.viewport.from || this.viewportLines[0].top - scrollTop > 200 ? block : this.viewportLines[0];
5423
+ }
5465
5424
  elementAtHeight(height) {
5466
5425
  return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
5467
5426
  }
@@ -5967,7 +5926,7 @@ function applyDOMChange(view, domChange) {
5967
5926
  if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
5968
5927
  change.to <= sel.to && change.to >= sel.to - 10) {
5969
5928
  let replaced = view.state.sliceDoc(change.from, change.to);
5970
- let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
5929
+ let composition = findCompositionNode(view) || view.state.doc.lineAt(sel.head);
5971
5930
  let offset = sel.to - change.to, size = sel.to - sel.from;
5972
5931
  tr = startState.changeByRange(range => {
5973
5932
  if (range.from == sel.from && range.to == sel.to)
@@ -5978,7 +5937,7 @@ function applyDOMChange(view, domChange) {
5978
5937
  // changes in the same node work without aborting
5979
5938
  // composition, so cursors in the composition range are
5980
5939
  // ignored.
5981
- compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
5940
+ composition && range.to >= composition.from && range.from <= composition.to)
5982
5941
  return { range };
5983
5942
  let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
5984
5943
  return {
@@ -6439,7 +6398,7 @@ class DOMObserver {
6439
6398
  return null;
6440
6399
  cView.markDirty(rec.type == "attributes");
6441
6400
  if (rec.type == "attributes")
6442
- cView.dirty |= 4 /* Attrs */;
6401
+ cView.flags |= 4 /* AttrsDirty */;
6443
6402
  if (rec.type == "childList") {
6444
6403
  let childBefore = findChild(cView, rec.previousSibling || rec.target.previousSibling, -1);
6445
6404
  let childAfter = findChild(cView, rec.nextSibling || rec.target.nextSibling, 1);
@@ -6834,22 +6793,23 @@ class EditorView {
6834
6793
  let updated = null;
6835
6794
  let sDOM = this.scrollDOM, { scrollTop } = sDOM;
6836
6795
  let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
6796
+ if (scrollTop != this.viewState.scrollTop)
6797
+ scrollAnchorHeight = -1;
6837
6798
  this.viewState.scrollAnchorHeight = -1;
6838
- if (scrollAnchorHeight < 0 || scrollTop != this.viewState.scrollTop) {
6839
- if (scrollTop > sDOM.scrollHeight - sDOM.clientHeight - 4) {
6840
- scrollAnchorPos = -1;
6841
- scrollAnchorHeight = this.viewState.heightMap.height;
6842
- }
6843
- else {
6844
- let block = this.viewState.lineBlockAtHeight(scrollTop);
6845
- scrollAnchorPos = block.from;
6846
- scrollAnchorHeight = block.top;
6847
- }
6848
- }
6849
6799
  try {
6850
6800
  for (let i = 0;; i++) {
6801
+ if (scrollAnchorHeight < 0) {
6802
+ if (isScrolledToBottom(sDOM)) {
6803
+ scrollAnchorPos = -1;
6804
+ scrollAnchorHeight = this.viewState.heightMap.height;
6805
+ }
6806
+ else {
6807
+ let block = this.viewState.scrollAnchorAt(scrollTop);
6808
+ scrollAnchorPos = block.from;
6809
+ scrollAnchorHeight = block.top;
6810
+ }
6811
+ }
6851
6812
  this.updateState = 1 /* Measuring */;
6852
- let oldViewport = this.viewport;
6853
6813
  let changed = this.viewState.measure(this);
6854
6814
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6855
6815
  break;
@@ -6872,7 +6832,7 @@ class EditorView {
6872
6832
  return BadMeasure;
6873
6833
  }
6874
6834
  });
6875
- let update = ViewUpdate.create(this, this.state, []), redrawn = false, scrolled = false;
6835
+ let update = ViewUpdate.create(this, this.state, []), redrawn = false;
6876
6836
  update.flags |= changed;
6877
6837
  if (!updated)
6878
6838
  updated = update;
@@ -6896,28 +6856,28 @@ class EditorView {
6896
6856
  logException(this.state, e);
6897
6857
  }
6898
6858
  }
6899
- if (this.viewState.editorHeight) {
6900
- if (this.viewState.scrollTarget) {
6901
- this.docView.scrollIntoView(this.viewState.scrollTarget);
6902
- this.viewState.scrollTarget = null;
6903
- scrolled = true;
6904
- }
6905
- else if (scrollAnchorHeight > -1) {
6906
- let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
6907
- this.viewState.lineBlockAt(scrollAnchorPos).top;
6908
- let diff = newAnchorHeight - scrollAnchorHeight;
6909
- if (diff > 1 || diff < -1) {
6910
- sDOM.scrollTop = scrollTop + diff;
6911
- scrolled = true;
6912
- }
6913
- }
6914
- }
6915
6859
  if (redrawn)
6916
6860
  this.docView.updateSelection(true);
6917
- if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
6918
- !scrolled && this.measureRequests.length == 0)
6861
+ if (!update.viewportChanged && this.measureRequests.length == 0) {
6862
+ if (this.viewState.editorHeight) {
6863
+ if (this.viewState.scrollTarget) {
6864
+ this.docView.scrollIntoView(this.viewState.scrollTarget);
6865
+ this.viewState.scrollTarget = null;
6866
+ continue;
6867
+ }
6868
+ else {
6869
+ let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
6870
+ this.viewState.lineBlockAt(scrollAnchorPos).top;
6871
+ let diff = newAnchorHeight - scrollAnchorHeight;
6872
+ if (diff > 1 || diff < -1) {
6873
+ scrollTop = sDOM.scrollTop = scrollTop + diff;
6874
+ scrollAnchorHeight = -1;
6875
+ continue;
6876
+ }
6877
+ }
6878
+ }
6919
6879
  break;
6920
- scrollAnchorHeight = -1;
6880
+ }
6921
6881
  }
6922
6882
  }
6923
6883
  finally {
@@ -7612,7 +7572,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7612
7572
  else if (current != is)
7613
7573
  throw new Error("Key binding " + name + " is used both as a regular binding and as a multi-stroke prefix");
7614
7574
  };
7615
- let add = (scope, key, command, preventDefault) => {
7575
+ let add = (scope, key, command, preventDefault, stopPropagation) => {
7616
7576
  var _a, _b;
7617
7577
  let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
7618
7578
  let parts = key.split(/ (?!$)/).map(k => normalizeKeyName(k, platform));
@@ -7622,6 +7582,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7622
7582
  if (!scopeObj[prefix])
7623
7583
  scopeObj[prefix] = {
7624
7584
  preventDefault: true,
7585
+ stopPropagation: false,
7625
7586
  run: [(view) => {
7626
7587
  let ourObj = storedPrefix = { view, prefix, scope };
7627
7588
  setTimeout(() => { if (storedPrefix == ourObj)
@@ -7632,11 +7593,17 @@ function buildKeymap(bindings, platform = currentPlatform) {
7632
7593
  }
7633
7594
  let full = parts.join(" ");
7634
7595
  checkPrefix(full, false);
7635
- 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
+ });
7636
7601
  if (command)
7637
7602
  binding.run.push(command);
7638
7603
  if (preventDefault)
7639
7604
  binding.preventDefault = true;
7605
+ if (stopPropagation)
7606
+ binding.stopPropagation = true;
7640
7607
  };
7641
7608
  for (let b of bindings) {
7642
7609
  let scopes = b.scope ? b.scope.split(" ") : ["editor"];
@@ -7644,7 +7611,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
7644
7611
  for (let scope of scopes) {
7645
7612
  let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
7646
7613
  if (!scopeObj._any)
7647
- scopeObj._any = { preventDefault: false, run: [] };
7614
+ scopeObj._any = { preventDefault: false, stopPropagation: false, run: [] };
7648
7615
  for (let key in scopeObj)
7649
7616
  scopeObj[key].run.push(b.any);
7650
7617
  }
@@ -7652,9 +7619,9 @@ function buildKeymap(bindings, platform = currentPlatform) {
7652
7619
  if (!name)
7653
7620
  continue;
7654
7621
  for (let scope of scopes) {
7655
- add(scope, name, b.run, b.preventDefault);
7622
+ add(scope, name, b.run, b.preventDefault, b.stopPropagation);
7656
7623
  if (b.shift)
7657
- add(scope, "Shift-" + name, b.shift, b.preventDefault);
7624
+ add(scope, "Shift-" + name, b.shift, b.preventDefault, b.stopPropagation);
7658
7625
  }
7659
7626
  }
7660
7627
  return bound;
@@ -7662,11 +7629,13 @@ function buildKeymap(bindings, platform = currentPlatform) {
7662
7629
  function runHandlers(map, event, view, scope) {
7663
7630
  let name = keyName(event);
7664
7631
  let charCode = codePointAt(name, 0), isChar = codePointSize(charCode) == name.length && name != " ";
7665
- let prefix = "", fallthrough = false;
7632
+ let prefix = "", handled = false, prevented = false, stopPropagation = false;
7666
7633
  if (storedPrefix && storedPrefix.view == view && storedPrefix.scope == scope) {
7667
7634
  prefix = storedPrefix.prefix + " ";
7668
- if (fallthrough = modifierCodes.indexOf(event.keyCode) < 0)
7635
+ if (modifierCodes.indexOf(event.keyCode) < 0) {
7636
+ prevented = true;
7669
7637
  storedPrefix = null;
7638
+ }
7670
7639
  }
7671
7640
  let ran = new Set;
7672
7641
  let runFor = (binding) => {
@@ -7674,36 +7643,49 @@ function runHandlers(map, event, view, scope) {
7674
7643
  for (let cmd of binding.run)
7675
7644
  if (!ran.has(cmd)) {
7676
7645
  ran.add(cmd);
7677
- if (cmd(view, event))
7646
+ if (cmd(view, event)) {
7647
+ if (binding.stopPropagation)
7648
+ stopPropagation = true;
7678
7649
  return true;
7650
+ }
7679
7651
  }
7680
- if (binding.preventDefault)
7681
- fallthrough = true;
7652
+ if (binding.preventDefault) {
7653
+ if (binding.stopPropagation)
7654
+ stopPropagation = true;
7655
+ prevented = true;
7656
+ }
7682
7657
  }
7683
7658
  return false;
7684
7659
  };
7685
7660
  let scopeObj = map[scope], baseName, shiftName;
7686
7661
  if (scopeObj) {
7687
- if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)]))
7688
- return true;
7689
- 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) &&
7690
7666
  // Ctrl-Alt may be used for AltGr on Windows
7691
7667
  !(browser.windows && event.ctrlKey && event.altKey) &&
7692
7668
  (baseName = base[event.keyCode]) && baseName != name) {
7693
- if (runFor(scopeObj[prefix + modifiers(baseName, event, true)]))
7694
- return true;
7669
+ if (runFor(scopeObj[prefix + modifiers(baseName, event, true)])) {
7670
+ handled = true;
7671
+ }
7695
7672
  else if (event.shiftKey && (shiftName = shift[event.keyCode]) != name && shiftName != baseName &&
7696
- runFor(scopeObj[prefix + modifiers(shiftName, event, false)]))
7697
- return true;
7673
+ runFor(scopeObj[prefix + modifiers(shiftName, event, false)])) {
7674
+ handled = true;
7675
+ }
7698
7676
  }
7699
- else if (isChar && event.shiftKey) {
7700
- if (runFor(scopeObj[prefix + modifiers(name, event, true)]))
7701
- return true;
7677
+ else if (isChar && event.shiftKey &&
7678
+ runFor(scopeObj[prefix + modifiers(name, event, true)])) {
7679
+ handled = true;
7702
7680
  }
7703
- if (runFor(scopeObj._any))
7704
- return true;
7681
+ if (!handled && runFor(scopeObj._any))
7682
+ handled = true;
7705
7683
  }
7706
- return fallthrough;
7684
+ if (prevented)
7685
+ handled = true;
7686
+ if (handled && stopPropagation)
7687
+ event.stopPropagation();
7688
+ return handled;
7707
7689
  }
7708
7690
 
7709
7691
  /**
@@ -8490,7 +8472,9 @@ function placeholder(content) {
8490
8472
  return ViewPlugin.fromClass(class {
8491
8473
  constructor(view) {
8492
8474
  this.view = view;
8493
- 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;
8494
8478
  }
8495
8479
  get decorations() { return this.view.state.doc.length ? Decoration.none : this.placeholder; }
8496
8480
  }, { decorations: v => v.decorations });