@codemirror/view 6.18.0 → 6.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 6.18.1 (2023-09-11)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where the editor duplicated text when the browser moved content into the focused text node on composition.
6
+
7
+ Make sure `widgetMarker` is called for gutters on lines covered by a block replace decoration.
8
+
9
+ Fix an issue where the cursor could be shown in a position that doesn't allow a cursor when the selection is in a block widget.
10
+
1
11
  ## 6.18.0 (2023-09-05)
2
12
 
3
13
  ### New features
package/dist/index.cjs CHANGED
@@ -484,6 +484,8 @@ class ContentView {
484
484
  }
485
485
  }
486
486
  setDOM(dom) {
487
+ if (this.dom == dom)
488
+ return;
487
489
  if (this.dom)
488
490
  this.dom.cmView = null;
489
491
  this.dom = dom;
@@ -656,113 +658,6 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
656
658
  replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
657
659
  }
658
660
 
659
- const LineBreakPlaceholder = "\uffff";
660
- class DOMReader {
661
- constructor(points, state$1) {
662
- this.points = points;
663
- this.text = "";
664
- this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
665
- }
666
- append(text) {
667
- this.text += text;
668
- }
669
- lineBreak() {
670
- this.text += LineBreakPlaceholder;
671
- }
672
- readRange(start, end) {
673
- if (!start)
674
- return this;
675
- let parent = start.parentNode;
676
- for (let cur = start;;) {
677
- this.findPointBefore(parent, cur);
678
- let oldLen = this.text.length;
679
- this.readNode(cur);
680
- let next = cur.nextSibling;
681
- if (next == end)
682
- break;
683
- let view = ContentView.get(cur), nextView = ContentView.get(next);
684
- if (view && nextView ? view.breakAfter :
685
- (view ? view.breakAfter : isBlockElement(cur)) ||
686
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
687
- this.lineBreak();
688
- cur = next;
689
- }
690
- this.findPointBefore(parent, end);
691
- return this;
692
- }
693
- readTextNode(node) {
694
- let text = node.nodeValue;
695
- for (let point of this.points)
696
- if (point.node == node)
697
- point.pos = this.text.length + Math.min(point.offset, text.length);
698
- for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
699
- let nextBreak = -1, breakSize = 1, m;
700
- if (this.lineSeparator) {
701
- nextBreak = text.indexOf(this.lineSeparator, off);
702
- breakSize = this.lineSeparator.length;
703
- }
704
- else if (m = re.exec(text)) {
705
- nextBreak = m.index;
706
- breakSize = m[0].length;
707
- }
708
- this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
709
- if (nextBreak < 0)
710
- break;
711
- this.lineBreak();
712
- if (breakSize > 1)
713
- for (let point of this.points)
714
- if (point.node == node && point.pos > this.text.length)
715
- point.pos -= breakSize - 1;
716
- off = nextBreak + breakSize;
717
- }
718
- }
719
- readNode(node) {
720
- if (node.cmIgnore)
721
- return;
722
- let view = ContentView.get(node);
723
- let fromView = view && view.overrideDOMText;
724
- if (fromView != null) {
725
- this.findPointInside(node, fromView.length);
726
- for (let i = fromView.iter(); !i.next().done;) {
727
- if (i.lineBreak)
728
- this.lineBreak();
729
- else
730
- this.append(i.value);
731
- }
732
- }
733
- else if (node.nodeType == 3) {
734
- this.readTextNode(node);
735
- }
736
- else if (node.nodeName == "BR") {
737
- if (node.nextSibling)
738
- this.lineBreak();
739
- }
740
- else if (node.nodeType == 1) {
741
- this.readRange(node.firstChild, null);
742
- }
743
- }
744
- findPointBefore(node, next) {
745
- for (let point of this.points)
746
- if (point.node == node && node.childNodes[point.offset] == next)
747
- point.pos = this.text.length;
748
- }
749
- findPointInside(node, maxLen) {
750
- for (let point of this.points)
751
- if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
752
- point.pos = this.text.length + Math.min(maxLen, point.offset);
753
- }
754
- }
755
- function isBlockElement(node) {
756
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
757
- }
758
- class DOMPoint {
759
- constructor(node, offset) {
760
- this.node = node;
761
- this.offset = offset;
762
- this.pos = -1;
763
- }
764
- }
765
-
766
661
  let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
767
662
  let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
768
663
  const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent);
@@ -2706,6 +2601,7 @@ class DocView extends ContentView {
2706
2601
  this.view = view;
2707
2602
  this.decorations = [];
2708
2603
  this.dynamicDecorationMap = [];
2604
+ this.domChanged = null;
2709
2605
  this.hasComposition = null;
2710
2606
  this.markedForComposition = new Set;
2711
2607
  // Track a minimum width for the editor. When measuring sizes in
@@ -2734,6 +2630,7 @@ class DocView extends ContentView {
2734
2630
  }
2735
2631
  // Update the document view to a given state.
2736
2632
  update(update) {
2633
+ var _a;
2737
2634
  let changedRanges = update.changedRanges;
2738
2635
  if (this.minWidth > 0 && changedRanges.length) {
2739
2636
  if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
@@ -2744,7 +2641,15 @@ class DocView extends ContentView {
2744
2641
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2745
2642
  }
2746
2643
  }
2747
- let composition = this.view.inputState.composing < 0 ? null : findCompositionRange(this.view, update.changes);
2644
+ let readCompositionAt = -1;
2645
+ if (this.view.inputState.composing >= 0) {
2646
+ if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel)
2647
+ readCompositionAt = this.domChanged.newSel.head;
2648
+ else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet)
2649
+ readCompositionAt = update.state.selection.main.head;
2650
+ }
2651
+ let composition = readCompositionAt > -1 ? findCompositionRange(this.view, update.changes, readCompositionAt) : null;
2652
+ this.domChanged = null;
2748
2653
  if (this.hasComposition) {
2749
2654
  this.markedForComposition.clear();
2750
2655
  let { from, to } = this.hasComposition;
@@ -2859,11 +2764,9 @@ class DocView extends ContentView {
2859
2764
  cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0);
2860
2765
  this.markedForComposition.add(cView);
2861
2766
  let prev = ContentView.get(dom);
2862
- if (prev != cView) {
2863
- if (prev)
2864
- prev.dom = null;
2865
- cView.setDOM(dom);
2866
- }
2767
+ if (prev && prev != cView)
2768
+ prev.dom = null;
2769
+ cView.setDOM(dom);
2867
2770
  };
2868
2771
  let pos = this.childPos(composition.range.fromB, 1);
2869
2772
  let cView = this.children[pos.i];
@@ -2886,9 +2789,8 @@ class DocView extends ContentView {
2886
2789
  let force = this.forceSelection;
2887
2790
  this.forceSelection = false;
2888
2791
  let main = this.view.state.selection.main;
2889
- // FIXME need to handle the case where the selection falls inside a block range
2890
- let anchor = this.domAtPos(main.anchor);
2891
- let head = main.empty ? anchor : this.domAtPos(main.head);
2792
+ let anchor = this.moveToLine(this.domAtPos(main.anchor));
2793
+ let head = main.empty ? anchor : this.moveToLine(this.domAtPos(main.head));
2892
2794
  // Always reset on Firefox when next to an uneditable node to
2893
2795
  // avoid invisible cursor bugs (#111)
2894
2796
  if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
@@ -2921,12 +2823,12 @@ class DocView extends ContentView {
2921
2823
  if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) {
2922
2824
  let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* NextTo.Before */ ? 1 : -1);
2923
2825
  if (text)
2924
- anchor = new DOMPos(text, nextTo == 1 /* NextTo.Before */ ? 0 : text.nodeValue.length);
2826
+ anchor = new DOMPos(text.node, text.offset);
2925
2827
  }
2926
2828
  }
2927
2829
  rawSel.collapse(anchor.node, anchor.offset);
2928
- if (main.bidiLevel != null && domSel.caretBidiLevel != null)
2929
- domSel.caretBidiLevel = main.bidiLevel;
2830
+ if (main.bidiLevel != null && rawSel.caretBidiLevel !== undefined)
2831
+ rawSel.caretBidiLevel = main.bidiLevel;
2930
2832
  }
2931
2833
  else if (rawSel.extend) {
2932
2834
  // Selection.extend can be used to create an 'inverted' selection
@@ -2989,6 +2891,26 @@ class DocView extends ContentView {
2989
2891
  if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
2990
2892
  sel.collapse(anchorNode, anchorOffset);
2991
2893
  }
2894
+ // If a position is in/near a block widget, move it to a nearby text
2895
+ // line, since we don't want the cursor inside a block widget.
2896
+ moveToLine(pos) {
2897
+ // Block widgets will return positions before/after them, which
2898
+ // are thus directly in the document DOM element.
2899
+ let dom = this.dom, newPos;
2900
+ if (pos.node != dom)
2901
+ return pos;
2902
+ for (let i = pos.offset; !newPos && i < dom.childNodes.length; i++) {
2903
+ let view = ContentView.get(dom.childNodes[i]);
2904
+ if (view instanceof LineView)
2905
+ newPos = view.domAtPos(0);
2906
+ }
2907
+ for (let i = pos.offset - 1; !newPos && i >= 0; i--) {
2908
+ let view = ContentView.get(dom.childNodes[i]);
2909
+ if (view instanceof LineView)
2910
+ newPos = view.domAtPos(view.length);
2911
+ }
2912
+ return newPos ? new DOMPos(newPos.node, newPos.offset, true) : pos;
2913
+ }
2992
2914
  nearest(dom) {
2993
2915
  for (let cur = dom; cur;) {
2994
2916
  let domView = ContentView.get(cur);
@@ -3187,74 +3109,27 @@ class BlockGapWidget extends WidgetType {
3187
3109
  }
3188
3110
  get estimatedHeight() { return this.height; }
3189
3111
  }
3190
- function findCompositionNode(view, dLen) {
3112
+ function findCompositionNode(view, headPos) {
3191
3113
  let sel = view.observer.selectionRange;
3192
3114
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
3193
3115
  if (!textNode)
3194
3116
  return null;
3195
- let cView = ContentView.get(textNode);
3196
- let from, to;
3197
- if (cView instanceof TextView) {
3198
- from = cView.posAtStart;
3199
- to = from + cView.length;
3200
- }
3201
- else {
3202
- let oldLen = Math.max(0, textNode.nodeValue.length - dLen);
3203
- up: for (let offset = 0, node = textNode;;) {
3204
- for (let sibling = node.previousSibling, cView; sibling; sibling = sibling.previousSibling) {
3205
- if (cView = ContentView.get(sibling)) {
3206
- to = cView.posAtEnd + offset;
3207
- from = Math.max(0, to - oldLen);
3208
- break up;
3209
- }
3210
- let reader = new DOMReader([], view.state);
3211
- reader.readNode(sibling);
3212
- if (reader.text.indexOf(LineBreakPlaceholder) > -1)
3213
- return null;
3214
- offset += reader.text.length;
3215
- }
3216
- node = node.parentNode;
3217
- if (!node)
3218
- return null;
3219
- let parentView = ContentView.get(node);
3220
- if (parentView) {
3221
- from = parentView.posAtStart + offset;
3222
- to = from + oldLen;
3223
- break;
3224
- }
3225
- }
3226
- }
3227
- return { from, to: to, node: textNode };
3117
+ let from = headPos - textNode.offset;
3118
+ return { from, to: from + textNode.node.nodeValue.length, node: textNode.node };
3228
3119
  }
3229
- function findCompositionRange(view, changes) {
3230
- let found = findCompositionNode(view, changes.newLength - changes.length);
3120
+ function findCompositionRange(view, changes, headPos) {
3121
+ let found = findCompositionNode(view, headPos);
3231
3122
  if (!found)
3232
3123
  return null;
3233
- let { from: fromA, to: toA, node: textNode } = found;
3234
- let fromB = changes.mapPos(fromA, -1), toB = changes.mapPos(toA, 1);
3235
- let text = textNode.nodeValue;
3124
+ let { node: textNode, from, to } = found, text = textNode.nodeValue;
3236
3125
  // Don't try to preserve multi-line compositions
3237
3126
  if (/[\n\r]/.test(text))
3238
3127
  return null;
3239
- if (toB - fromB != text.length) {
3240
- // If there is a length mismatch, see if mapping non-inclusively helps
3241
- let fromB2 = changes.mapPos(fromA, 1), toB2 = changes.mapPos(toA, -1);
3242
- if (toB2 - fromB2 == text.length)
3243
- fromB = fromB2, toB = toB2;
3244
- // See if we can find an instance of the text at either side
3245
- else if (view.state.doc.sliceString(toB - text.length, toB) == text)
3246
- fromB = toB - text.length;
3247
- else if (view.state.doc.sliceString(fromB, fromB + text.length) == text)
3248
- toB = fromB + text.length;
3249
- // Not found
3250
- else
3251
- return null;
3252
- }
3253
- let { main } = view.state.selection;
3254
- if (view.state.doc.sliceString(fromB, toB) != text || fromB > main.head || toB < main.head)
3128
+ if (view.state.doc.sliceString(found.from, found.to) != text)
3255
3129
  return null;
3130
+ let inv = changes.invertedDesc;
3131
+ let range = new ChangedRange(inv.mapPos(from), inv.mapPos(to), from, to);
3256
3132
  let marks = [];
3257
- let range = new ChangedRange(fromA, toA, fromB, toB);
3258
3133
  for (let parent = textNode.parentNode;; parent = parent.parentNode) {
3259
3134
  let parentView = ContentView.get(parent);
3260
3135
  if (parentView instanceof MarkView)
@@ -3275,7 +3150,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3275
3150
  if (side <= 0)
3276
3151
  for (let node = startNode, offset = startOffset;;) {
3277
3152
  if (node.nodeType == 3)
3278
- return node;
3153
+ return { node: node, offset: offset };
3279
3154
  if (node.nodeType == 1 && offset > 0) {
3280
3155
  node = node.childNodes[offset - 1];
3281
3156
  offset = maxOffset(node);
@@ -3287,7 +3162,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3287
3162
  if (side >= 0)
3288
3163
  for (let node = startNode, offset = startOffset;;) {
3289
3164
  if (node.nodeType == 3)
3290
- return node;
3165
+ return { node: node, offset: offset };
3291
3166
  if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
3292
3167
  node = node.childNodes[offset];
3293
3168
  offset = 0;
@@ -3324,6 +3199,15 @@ function inUneditable(node, inside) {
3324
3199
  }
3325
3200
  return false;
3326
3201
  }
3202
+ function touchesComposition(changes, composition) {
3203
+ let touched = false;
3204
+ if (composition)
3205
+ changes.iterChangedRanges((from, to) => {
3206
+ if (from < composition.to && to > composition.from)
3207
+ touched = true;
3208
+ });
3209
+ return touched;
3210
+ }
3327
3211
 
3328
3212
  function groupAt(state$1, pos, bias = 1) {
3329
3213
  let categorize = state$1.charCategorizer(pos);
@@ -6082,6 +5966,113 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
6082
5966
  }
6083
5967
  }, lightDarkIDs);
6084
5968
 
5969
+ const LineBreakPlaceholder = "\uffff";
5970
+ class DOMReader {
5971
+ constructor(points, state$1) {
5972
+ this.points = points;
5973
+ this.text = "";
5974
+ this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
5975
+ }
5976
+ append(text) {
5977
+ this.text += text;
5978
+ }
5979
+ lineBreak() {
5980
+ this.text += LineBreakPlaceholder;
5981
+ }
5982
+ readRange(start, end) {
5983
+ if (!start)
5984
+ return this;
5985
+ let parent = start.parentNode;
5986
+ for (let cur = start;;) {
5987
+ this.findPointBefore(parent, cur);
5988
+ let oldLen = this.text.length;
5989
+ this.readNode(cur);
5990
+ let next = cur.nextSibling;
5991
+ if (next == end)
5992
+ break;
5993
+ let view = ContentView.get(cur), nextView = ContentView.get(next);
5994
+ if (view && nextView ? view.breakAfter :
5995
+ (view ? view.breakAfter : isBlockElement(cur)) ||
5996
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
5997
+ this.lineBreak();
5998
+ cur = next;
5999
+ }
6000
+ this.findPointBefore(parent, end);
6001
+ return this;
6002
+ }
6003
+ readTextNode(node) {
6004
+ let text = node.nodeValue;
6005
+ for (let point of this.points)
6006
+ if (point.node == node)
6007
+ point.pos = this.text.length + Math.min(point.offset, text.length);
6008
+ for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
6009
+ let nextBreak = -1, breakSize = 1, m;
6010
+ if (this.lineSeparator) {
6011
+ nextBreak = text.indexOf(this.lineSeparator, off);
6012
+ breakSize = this.lineSeparator.length;
6013
+ }
6014
+ else if (m = re.exec(text)) {
6015
+ nextBreak = m.index;
6016
+ breakSize = m[0].length;
6017
+ }
6018
+ this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
6019
+ if (nextBreak < 0)
6020
+ break;
6021
+ this.lineBreak();
6022
+ if (breakSize > 1)
6023
+ for (let point of this.points)
6024
+ if (point.node == node && point.pos > this.text.length)
6025
+ point.pos -= breakSize - 1;
6026
+ off = nextBreak + breakSize;
6027
+ }
6028
+ }
6029
+ readNode(node) {
6030
+ if (node.cmIgnore)
6031
+ return;
6032
+ let view = ContentView.get(node);
6033
+ let fromView = view && view.overrideDOMText;
6034
+ if (fromView != null) {
6035
+ this.findPointInside(node, fromView.length);
6036
+ for (let i = fromView.iter(); !i.next().done;) {
6037
+ if (i.lineBreak)
6038
+ this.lineBreak();
6039
+ else
6040
+ this.append(i.value);
6041
+ }
6042
+ }
6043
+ else if (node.nodeType == 3) {
6044
+ this.readTextNode(node);
6045
+ }
6046
+ else if (node.nodeName == "BR") {
6047
+ if (node.nextSibling)
6048
+ this.lineBreak();
6049
+ }
6050
+ else if (node.nodeType == 1) {
6051
+ this.readRange(node.firstChild, null);
6052
+ }
6053
+ }
6054
+ findPointBefore(node, next) {
6055
+ for (let point of this.points)
6056
+ if (point.node == node && node.childNodes[point.offset] == next)
6057
+ point.pos = this.text.length;
6058
+ }
6059
+ findPointInside(node, maxLen) {
6060
+ for (let point of this.points)
6061
+ if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
6062
+ point.pos = this.text.length + Math.min(maxLen, point.offset);
6063
+ }
6064
+ }
6065
+ function isBlockElement(node) {
6066
+ return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
6067
+ }
6068
+ class DOMPoint {
6069
+ constructor(node, offset) {
6070
+ this.node = node;
6071
+ this.offset = offset;
6072
+ this.pos = -1;
6073
+ }
6074
+ }
6075
+
6085
6076
  class DOMChange {
6086
6077
  constructor(view, start, end, typeOver) {
6087
6078
  this.typeOver = typeOver;
@@ -6232,8 +6223,14 @@ function applyDefaultInsert(view, change, newSel) {
6232
6223
  if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
6233
6224
  change.to <= sel.to && change.to >= sel.to - 10) {
6234
6225
  let replaced = view.state.sliceDoc(change.from, change.to);
6235
- let composition = findCompositionNode(view, change.insert.length - (change.to - change.from)) ||
6236
- view.state.doc.lineAt(sel.head);
6226
+ let compositionRange, composition = newSel && findCompositionNode(view, newSel.main.head);
6227
+ if (composition) {
6228
+ let dLen = change.insert.length - (change.to - change.from);
6229
+ compositionRange = { from: composition.from, to: composition.to - dLen };
6230
+ }
6231
+ else {
6232
+ compositionRange = view.state.doc.lineAt(sel.head);
6233
+ }
6237
6234
  let offset = sel.to - change.to, size = sel.to - sel.from;
6238
6235
  tr = startState.changeByRange(range => {
6239
6236
  if (range.from == sel.from && range.to == sel.to)
@@ -6244,7 +6241,7 @@ function applyDefaultInsert(view, change, newSel) {
6244
6241
  // changes in the same node work without aborting
6245
6242
  // composition, so cursors in the composition range are
6246
6243
  // ignored.
6247
- composition && range.to >= composition.from && range.from <= composition.to)
6244
+ range.to >= compositionRange.from && range.from <= compositionRange.to)
6248
6245
  return { range };
6249
6246
  let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
6250
6247
  return {
@@ -6663,7 +6660,9 @@ class DOMObserver {
6663
6660
  this.lastChange = Date.now();
6664
6661
  this.view.inputState.lastFocusTime = 0;
6665
6662
  this.selectionChanged = false;
6666
- return new DOMChange(this.view, from, to, typeOver);
6663
+ let change = new DOMChange(this.view, from, to, typeOver);
6664
+ this.view.docView.domChanged = { newSel: change.newSel ? change.newSel.main : null };
6665
+ return change;
6667
6666
  }
6668
6667
  // Apply pending changes, if any
6669
6668
  flush(readSelection = true) {
@@ -9908,6 +9907,10 @@ const gutterView = ViewPlugin.fromClass(class {
9908
9907
  for (let cx of contexts)
9909
9908
  cx.line(this.view, line, classSet);
9910
9909
  }
9910
+ else if (line.widget) {
9911
+ for (let cx of contexts)
9912
+ cx.widget(this.view, line);
9913
+ }
9911
9914
  }
9912
9915
  for (let cx of contexts)
9913
9916
  cx.finish();
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { EditorState, Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, findColumn, CharCategory, Annotation, Transaction, Prec, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
1
+ import { Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, findColumn, CharCategory, Annotation, EditorState, Transaction, Prec, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
2
2
  import { StyleModule } from 'style-mod';
3
3
  import { keyName, base, shift } from 'w3c-keyname';
4
4
 
@@ -482,6 +482,8 @@ class ContentView {
482
482
  }
483
483
  }
484
484
  setDOM(dom) {
485
+ if (this.dom == dom)
486
+ return;
485
487
  if (this.dom)
486
488
  this.dom.cmView = null;
487
489
  this.dom = dom;
@@ -654,113 +656,6 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
654
656
  replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
655
657
  }
656
658
 
657
- const LineBreakPlaceholder = "\uffff";
658
- class DOMReader {
659
- constructor(points, state) {
660
- this.points = points;
661
- this.text = "";
662
- this.lineSeparator = state.facet(EditorState.lineSeparator);
663
- }
664
- append(text) {
665
- this.text += text;
666
- }
667
- lineBreak() {
668
- this.text += LineBreakPlaceholder;
669
- }
670
- readRange(start, end) {
671
- if (!start)
672
- return this;
673
- let parent = start.parentNode;
674
- for (let cur = start;;) {
675
- this.findPointBefore(parent, cur);
676
- let oldLen = this.text.length;
677
- this.readNode(cur);
678
- let next = cur.nextSibling;
679
- if (next == end)
680
- break;
681
- let view = ContentView.get(cur), nextView = ContentView.get(next);
682
- if (view && nextView ? view.breakAfter :
683
- (view ? view.breakAfter : isBlockElement(cur)) ||
684
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
685
- this.lineBreak();
686
- cur = next;
687
- }
688
- this.findPointBefore(parent, end);
689
- return this;
690
- }
691
- readTextNode(node) {
692
- let text = node.nodeValue;
693
- for (let point of this.points)
694
- if (point.node == node)
695
- point.pos = this.text.length + Math.min(point.offset, text.length);
696
- for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
697
- let nextBreak = -1, breakSize = 1, m;
698
- if (this.lineSeparator) {
699
- nextBreak = text.indexOf(this.lineSeparator, off);
700
- breakSize = this.lineSeparator.length;
701
- }
702
- else if (m = re.exec(text)) {
703
- nextBreak = m.index;
704
- breakSize = m[0].length;
705
- }
706
- this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
707
- if (nextBreak < 0)
708
- break;
709
- this.lineBreak();
710
- if (breakSize > 1)
711
- for (let point of this.points)
712
- if (point.node == node && point.pos > this.text.length)
713
- point.pos -= breakSize - 1;
714
- off = nextBreak + breakSize;
715
- }
716
- }
717
- readNode(node) {
718
- if (node.cmIgnore)
719
- return;
720
- let view = ContentView.get(node);
721
- let fromView = view && view.overrideDOMText;
722
- if (fromView != null) {
723
- this.findPointInside(node, fromView.length);
724
- for (let i = fromView.iter(); !i.next().done;) {
725
- if (i.lineBreak)
726
- this.lineBreak();
727
- else
728
- this.append(i.value);
729
- }
730
- }
731
- else if (node.nodeType == 3) {
732
- this.readTextNode(node);
733
- }
734
- else if (node.nodeName == "BR") {
735
- if (node.nextSibling)
736
- this.lineBreak();
737
- }
738
- else if (node.nodeType == 1) {
739
- this.readRange(node.firstChild, null);
740
- }
741
- }
742
- findPointBefore(node, next) {
743
- for (let point of this.points)
744
- if (point.node == node && node.childNodes[point.offset] == next)
745
- point.pos = this.text.length;
746
- }
747
- findPointInside(node, maxLen) {
748
- for (let point of this.points)
749
- if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
750
- point.pos = this.text.length + Math.min(maxLen, point.offset);
751
- }
752
- }
753
- function isBlockElement(node) {
754
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
755
- }
756
- class DOMPoint {
757
- constructor(node, offset) {
758
- this.node = node;
759
- this.offset = offset;
760
- this.pos = -1;
761
- }
762
- }
763
-
764
659
  let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
765
660
  let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
766
661
  const ie_edge = /*@__PURE__*//Edge\/(\d+)/.exec(nav.userAgent);
@@ -2702,6 +2597,7 @@ class DocView extends ContentView {
2702
2597
  this.view = view;
2703
2598
  this.decorations = [];
2704
2599
  this.dynamicDecorationMap = [];
2600
+ this.domChanged = null;
2705
2601
  this.hasComposition = null;
2706
2602
  this.markedForComposition = new Set;
2707
2603
  // Track a minimum width for the editor. When measuring sizes in
@@ -2730,6 +2626,7 @@ class DocView extends ContentView {
2730
2626
  }
2731
2627
  // Update the document view to a given state.
2732
2628
  update(update) {
2629
+ var _a;
2733
2630
  let changedRanges = update.changedRanges;
2734
2631
  if (this.minWidth > 0 && changedRanges.length) {
2735
2632
  if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
@@ -2740,7 +2637,15 @@ class DocView extends ContentView {
2740
2637
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2741
2638
  }
2742
2639
  }
2743
- let composition = this.view.inputState.composing < 0 ? null : findCompositionRange(this.view, update.changes);
2640
+ let readCompositionAt = -1;
2641
+ if (this.view.inputState.composing >= 0) {
2642
+ if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel)
2643
+ readCompositionAt = this.domChanged.newSel.head;
2644
+ else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet)
2645
+ readCompositionAt = update.state.selection.main.head;
2646
+ }
2647
+ let composition = readCompositionAt > -1 ? findCompositionRange(this.view, update.changes, readCompositionAt) : null;
2648
+ this.domChanged = null;
2744
2649
  if (this.hasComposition) {
2745
2650
  this.markedForComposition.clear();
2746
2651
  let { from, to } = this.hasComposition;
@@ -2855,11 +2760,9 @@ class DocView extends ContentView {
2855
2760
  cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0);
2856
2761
  this.markedForComposition.add(cView);
2857
2762
  let prev = ContentView.get(dom);
2858
- if (prev != cView) {
2859
- if (prev)
2860
- prev.dom = null;
2861
- cView.setDOM(dom);
2862
- }
2763
+ if (prev && prev != cView)
2764
+ prev.dom = null;
2765
+ cView.setDOM(dom);
2863
2766
  };
2864
2767
  let pos = this.childPos(composition.range.fromB, 1);
2865
2768
  let cView = this.children[pos.i];
@@ -2882,9 +2785,8 @@ class DocView extends ContentView {
2882
2785
  let force = this.forceSelection;
2883
2786
  this.forceSelection = false;
2884
2787
  let main = this.view.state.selection.main;
2885
- // FIXME need to handle the case where the selection falls inside a block range
2886
- let anchor = this.domAtPos(main.anchor);
2887
- let head = main.empty ? anchor : this.domAtPos(main.head);
2788
+ let anchor = this.moveToLine(this.domAtPos(main.anchor));
2789
+ let head = main.empty ? anchor : this.moveToLine(this.domAtPos(main.head));
2888
2790
  // Always reset on Firefox when next to an uneditable node to
2889
2791
  // avoid invisible cursor bugs (#111)
2890
2792
  if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
@@ -2917,12 +2819,12 @@ class DocView extends ContentView {
2917
2819
  if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) {
2918
2820
  let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* NextTo.Before */ ? 1 : -1);
2919
2821
  if (text)
2920
- anchor = new DOMPos(text, nextTo == 1 /* NextTo.Before */ ? 0 : text.nodeValue.length);
2822
+ anchor = new DOMPos(text.node, text.offset);
2921
2823
  }
2922
2824
  }
2923
2825
  rawSel.collapse(anchor.node, anchor.offset);
2924
- if (main.bidiLevel != null && domSel.caretBidiLevel != null)
2925
- domSel.caretBidiLevel = main.bidiLevel;
2826
+ if (main.bidiLevel != null && rawSel.caretBidiLevel !== undefined)
2827
+ rawSel.caretBidiLevel = main.bidiLevel;
2926
2828
  }
2927
2829
  else if (rawSel.extend) {
2928
2830
  // Selection.extend can be used to create an 'inverted' selection
@@ -2985,6 +2887,26 @@ class DocView extends ContentView {
2985
2887
  if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
2986
2888
  sel.collapse(anchorNode, anchorOffset);
2987
2889
  }
2890
+ // If a position is in/near a block widget, move it to a nearby text
2891
+ // line, since we don't want the cursor inside a block widget.
2892
+ moveToLine(pos) {
2893
+ // Block widgets will return positions before/after them, which
2894
+ // are thus directly in the document DOM element.
2895
+ let dom = this.dom, newPos;
2896
+ if (pos.node != dom)
2897
+ return pos;
2898
+ for (let i = pos.offset; !newPos && i < dom.childNodes.length; i++) {
2899
+ let view = ContentView.get(dom.childNodes[i]);
2900
+ if (view instanceof LineView)
2901
+ newPos = view.domAtPos(0);
2902
+ }
2903
+ for (let i = pos.offset - 1; !newPos && i >= 0; i--) {
2904
+ let view = ContentView.get(dom.childNodes[i]);
2905
+ if (view instanceof LineView)
2906
+ newPos = view.domAtPos(view.length);
2907
+ }
2908
+ return newPos ? new DOMPos(newPos.node, newPos.offset, true) : pos;
2909
+ }
2988
2910
  nearest(dom) {
2989
2911
  for (let cur = dom; cur;) {
2990
2912
  let domView = ContentView.get(cur);
@@ -3183,74 +3105,27 @@ class BlockGapWidget extends WidgetType {
3183
3105
  }
3184
3106
  get estimatedHeight() { return this.height; }
3185
3107
  }
3186
- function findCompositionNode(view, dLen) {
3108
+ function findCompositionNode(view, headPos) {
3187
3109
  let sel = view.observer.selectionRange;
3188
3110
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
3189
3111
  if (!textNode)
3190
3112
  return null;
3191
- let cView = ContentView.get(textNode);
3192
- let from, to;
3193
- if (cView instanceof TextView) {
3194
- from = cView.posAtStart;
3195
- to = from + cView.length;
3196
- }
3197
- else {
3198
- let oldLen = Math.max(0, textNode.nodeValue.length - dLen);
3199
- up: for (let offset = 0, node = textNode;;) {
3200
- for (let sibling = node.previousSibling, cView; sibling; sibling = sibling.previousSibling) {
3201
- if (cView = ContentView.get(sibling)) {
3202
- to = cView.posAtEnd + offset;
3203
- from = Math.max(0, to - oldLen);
3204
- break up;
3205
- }
3206
- let reader = new DOMReader([], view.state);
3207
- reader.readNode(sibling);
3208
- if (reader.text.indexOf(LineBreakPlaceholder) > -1)
3209
- return null;
3210
- offset += reader.text.length;
3211
- }
3212
- node = node.parentNode;
3213
- if (!node)
3214
- return null;
3215
- let parentView = ContentView.get(node);
3216
- if (parentView) {
3217
- from = parentView.posAtStart + offset;
3218
- to = from + oldLen;
3219
- break;
3220
- }
3221
- }
3222
- }
3223
- return { from, to: to, node: textNode };
3113
+ let from = headPos - textNode.offset;
3114
+ return { from, to: from + textNode.node.nodeValue.length, node: textNode.node };
3224
3115
  }
3225
- function findCompositionRange(view, changes) {
3226
- let found = findCompositionNode(view, changes.newLength - changes.length);
3116
+ function findCompositionRange(view, changes, headPos) {
3117
+ let found = findCompositionNode(view, headPos);
3227
3118
  if (!found)
3228
3119
  return null;
3229
- let { from: fromA, to: toA, node: textNode } = found;
3230
- let fromB = changes.mapPos(fromA, -1), toB = changes.mapPos(toA, 1);
3231
- let text = textNode.nodeValue;
3120
+ let { node: textNode, from, to } = found, text = textNode.nodeValue;
3232
3121
  // Don't try to preserve multi-line compositions
3233
3122
  if (/[\n\r]/.test(text))
3234
3123
  return null;
3235
- if (toB - fromB != text.length) {
3236
- // If there is a length mismatch, see if mapping non-inclusively helps
3237
- let fromB2 = changes.mapPos(fromA, 1), toB2 = changes.mapPos(toA, -1);
3238
- if (toB2 - fromB2 == text.length)
3239
- fromB = fromB2, toB = toB2;
3240
- // See if we can find an instance of the text at either side
3241
- else if (view.state.doc.sliceString(toB - text.length, toB) == text)
3242
- fromB = toB - text.length;
3243
- else if (view.state.doc.sliceString(fromB, fromB + text.length) == text)
3244
- toB = fromB + text.length;
3245
- // Not found
3246
- else
3247
- return null;
3248
- }
3249
- let { main } = view.state.selection;
3250
- if (view.state.doc.sliceString(fromB, toB) != text || fromB > main.head || toB < main.head)
3124
+ if (view.state.doc.sliceString(found.from, found.to) != text)
3251
3125
  return null;
3126
+ let inv = changes.invertedDesc;
3127
+ let range = new ChangedRange(inv.mapPos(from), inv.mapPos(to), from, to);
3252
3128
  let marks = [];
3253
- let range = new ChangedRange(fromA, toA, fromB, toB);
3254
3129
  for (let parent = textNode.parentNode;; parent = parent.parentNode) {
3255
3130
  let parentView = ContentView.get(parent);
3256
3131
  if (parentView instanceof MarkView)
@@ -3271,7 +3146,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3271
3146
  if (side <= 0)
3272
3147
  for (let node = startNode, offset = startOffset;;) {
3273
3148
  if (node.nodeType == 3)
3274
- return node;
3149
+ return { node: node, offset: offset };
3275
3150
  if (node.nodeType == 1 && offset > 0) {
3276
3151
  node = node.childNodes[offset - 1];
3277
3152
  offset = maxOffset(node);
@@ -3283,7 +3158,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3283
3158
  if (side >= 0)
3284
3159
  for (let node = startNode, offset = startOffset;;) {
3285
3160
  if (node.nodeType == 3)
3286
- return node;
3161
+ return { node: node, offset: offset };
3287
3162
  if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
3288
3163
  node = node.childNodes[offset];
3289
3164
  offset = 0;
@@ -3320,6 +3195,15 @@ function inUneditable(node, inside) {
3320
3195
  }
3321
3196
  return false;
3322
3197
  }
3198
+ function touchesComposition(changes, composition) {
3199
+ let touched = false;
3200
+ if (composition)
3201
+ changes.iterChangedRanges((from, to) => {
3202
+ if (from < composition.to && to > composition.from)
3203
+ touched = true;
3204
+ });
3205
+ return touched;
3206
+ }
3323
3207
 
3324
3208
  function groupAt(state, pos, bias = 1) {
3325
3209
  let categorize = state.charCategorizer(pos);
@@ -6077,6 +5961,113 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
6077
5961
  }
6078
5962
  }, lightDarkIDs);
6079
5963
 
5964
+ const LineBreakPlaceholder = "\uffff";
5965
+ class DOMReader {
5966
+ constructor(points, state) {
5967
+ this.points = points;
5968
+ this.text = "";
5969
+ this.lineSeparator = state.facet(EditorState.lineSeparator);
5970
+ }
5971
+ append(text) {
5972
+ this.text += text;
5973
+ }
5974
+ lineBreak() {
5975
+ this.text += LineBreakPlaceholder;
5976
+ }
5977
+ readRange(start, end) {
5978
+ if (!start)
5979
+ return this;
5980
+ let parent = start.parentNode;
5981
+ for (let cur = start;;) {
5982
+ this.findPointBefore(parent, cur);
5983
+ let oldLen = this.text.length;
5984
+ this.readNode(cur);
5985
+ let next = cur.nextSibling;
5986
+ if (next == end)
5987
+ break;
5988
+ let view = ContentView.get(cur), nextView = ContentView.get(next);
5989
+ if (view && nextView ? view.breakAfter :
5990
+ (view ? view.breakAfter : isBlockElement(cur)) ||
5991
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
5992
+ this.lineBreak();
5993
+ cur = next;
5994
+ }
5995
+ this.findPointBefore(parent, end);
5996
+ return this;
5997
+ }
5998
+ readTextNode(node) {
5999
+ let text = node.nodeValue;
6000
+ for (let point of this.points)
6001
+ if (point.node == node)
6002
+ point.pos = this.text.length + Math.min(point.offset, text.length);
6003
+ for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
6004
+ let nextBreak = -1, breakSize = 1, m;
6005
+ if (this.lineSeparator) {
6006
+ nextBreak = text.indexOf(this.lineSeparator, off);
6007
+ breakSize = this.lineSeparator.length;
6008
+ }
6009
+ else if (m = re.exec(text)) {
6010
+ nextBreak = m.index;
6011
+ breakSize = m[0].length;
6012
+ }
6013
+ this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
6014
+ if (nextBreak < 0)
6015
+ break;
6016
+ this.lineBreak();
6017
+ if (breakSize > 1)
6018
+ for (let point of this.points)
6019
+ if (point.node == node && point.pos > this.text.length)
6020
+ point.pos -= breakSize - 1;
6021
+ off = nextBreak + breakSize;
6022
+ }
6023
+ }
6024
+ readNode(node) {
6025
+ if (node.cmIgnore)
6026
+ return;
6027
+ let view = ContentView.get(node);
6028
+ let fromView = view && view.overrideDOMText;
6029
+ if (fromView != null) {
6030
+ this.findPointInside(node, fromView.length);
6031
+ for (let i = fromView.iter(); !i.next().done;) {
6032
+ if (i.lineBreak)
6033
+ this.lineBreak();
6034
+ else
6035
+ this.append(i.value);
6036
+ }
6037
+ }
6038
+ else if (node.nodeType == 3) {
6039
+ this.readTextNode(node);
6040
+ }
6041
+ else if (node.nodeName == "BR") {
6042
+ if (node.nextSibling)
6043
+ this.lineBreak();
6044
+ }
6045
+ else if (node.nodeType == 1) {
6046
+ this.readRange(node.firstChild, null);
6047
+ }
6048
+ }
6049
+ findPointBefore(node, next) {
6050
+ for (let point of this.points)
6051
+ if (point.node == node && node.childNodes[point.offset] == next)
6052
+ point.pos = this.text.length;
6053
+ }
6054
+ findPointInside(node, maxLen) {
6055
+ for (let point of this.points)
6056
+ if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
6057
+ point.pos = this.text.length + Math.min(maxLen, point.offset);
6058
+ }
6059
+ }
6060
+ function isBlockElement(node) {
6061
+ return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
6062
+ }
6063
+ class DOMPoint {
6064
+ constructor(node, offset) {
6065
+ this.node = node;
6066
+ this.offset = offset;
6067
+ this.pos = -1;
6068
+ }
6069
+ }
6070
+
6080
6071
  class DOMChange {
6081
6072
  constructor(view, start, end, typeOver) {
6082
6073
  this.typeOver = typeOver;
@@ -6227,8 +6218,14 @@ function applyDefaultInsert(view, change, newSel) {
6227
6218
  if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
6228
6219
  change.to <= sel.to && change.to >= sel.to - 10) {
6229
6220
  let replaced = view.state.sliceDoc(change.from, change.to);
6230
- let composition = findCompositionNode(view, change.insert.length - (change.to - change.from)) ||
6231
- view.state.doc.lineAt(sel.head);
6221
+ let compositionRange, composition = newSel && findCompositionNode(view, newSel.main.head);
6222
+ if (composition) {
6223
+ let dLen = change.insert.length - (change.to - change.from);
6224
+ compositionRange = { from: composition.from, to: composition.to - dLen };
6225
+ }
6226
+ else {
6227
+ compositionRange = view.state.doc.lineAt(sel.head);
6228
+ }
6232
6229
  let offset = sel.to - change.to, size = sel.to - sel.from;
6233
6230
  tr = startState.changeByRange(range => {
6234
6231
  if (range.from == sel.from && range.to == sel.to)
@@ -6239,7 +6236,7 @@ function applyDefaultInsert(view, change, newSel) {
6239
6236
  // changes in the same node work without aborting
6240
6237
  // composition, so cursors in the composition range are
6241
6238
  // ignored.
6242
- composition && range.to >= composition.from && range.from <= composition.to)
6239
+ range.to >= compositionRange.from && range.from <= compositionRange.to)
6243
6240
  return { range };
6244
6241
  let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
6245
6242
  return {
@@ -6658,7 +6655,9 @@ class DOMObserver {
6658
6655
  this.lastChange = Date.now();
6659
6656
  this.view.inputState.lastFocusTime = 0;
6660
6657
  this.selectionChanged = false;
6661
- return new DOMChange(this.view, from, to, typeOver);
6658
+ let change = new DOMChange(this.view, from, to, typeOver);
6659
+ this.view.docView.domChanged = { newSel: change.newSel ? change.newSel.main : null };
6660
+ return change;
6662
6661
  }
6663
6662
  // Apply pending changes, if any
6664
6663
  flush(readSelection = true) {
@@ -9903,6 +9902,10 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
9903
9902
  for (let cx of contexts)
9904
9903
  cx.line(this.view, line, classSet);
9905
9904
  }
9905
+ else if (line.widget) {
9906
+ for (let cx of contexts)
9907
+ cx.widget(this.view, line);
9908
+ }
9906
9909
  }
9907
9910
  for (let cx of contexts)
9908
9911
  cx.finish();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.18.0",
3
+ "version": "6.18.1",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",